glut_gamemode.c revision c041511d
1 2/* Copyright (c) Mark J. Kilgard, 1998. */ 3 4/* This program is freely distributable without licensing fees 5 and is provided without guarantee or warrantee expressed or 6 implied. This program is -not- in the public domain. */ 7 8#ifdef __VMS 9#include <GL/vms_x_fix.h> 10#endif 11 12#include <assert.h> 13#include <stdio.h> 14#include <stdlib.h> 15#include <string.h> 16 17#include "glutint.h" 18 19#ifndef _WIN32 20#include <X11/Xlib.h> 21#include <X11/Xatom.h> 22 23/* SGI optimization introduced in IRIX 6.3 to avoid X server 24 round trips for interning common X atoms. */ 25#if defined(_SGI_EXTRA_PREDEFINES) && !defined(NO_FAST_ATOMS) 26#include <X11/SGIFastAtom.h> 27#else 28#define XSGIFastInternAtom(dpy,string,fast_name,how) XInternAtom(dpy,string,how) 29#endif 30#endif /* not _WIN32 */ 31 32int __glutDisplaySettingsChanged = 0; 33static DisplayMode *dmodes, *currentDm = NULL; 34static int ndmodes = -1; 35GLUTwindow *__glutGameModeWindow = NULL; 36 37#ifdef TEST 38static char *compstr[] = 39{ 40 "none", "=", "!=", "<=", ">=", ">", "<", "~" 41}; 42static char *capstr[] = 43{ 44 "width", "height", "bpp", "hertz", "num" 45}; 46#endif 47 48void 49__glutCloseDownGameMode(void) 50{ 51 if (__glutDisplaySettingsChanged) { 52#ifdef _WIN32 53 /* Assumes that display settings have been changed, that 54 is __glutDisplaySettingsChanged is true. */ 55 ChangeDisplaySettings(NULL, 0); 56#endif 57 __glutDisplaySettingsChanged = 0; 58 } 59 __glutGameModeWindow = NULL; 60} 61 62void GLUTAPIENTRY 63glutLeaveGameMode(void) 64{ 65 if (__glutGameModeWindow == NULL) { 66 __glutWarning("not in game mode so cannot leave game mode"); 67 return; 68 } 69 __glutDestroyWindow(__glutGameModeWindow, 70 __glutGameModeWindow); 71 XFlush(__glutDisplay); 72 __glutGameModeWindow = NULL; 73} 74 75#ifdef _WIN32 76 77/* Same values as from MSDN's SetDisp.c example. */ 78#define MIN_WIDTH 400 79#define MIN_FREQUENCY 60 80 81static void 82initGameModeSupport(void) 83{ 84 DEVMODE dm; 85 DWORD mode; 86 int i; 87 88 if (ndmodes >= 0) { 89 /* ndmodes is initially -1 to indicate no 90 dmodes allocated yet. */ 91 return; 92 } 93 94 /* Determine how many display modes there are. */ 95 ndmodes = 0; 96 mode = 0; 97 while (EnumDisplaySettings(NULL, mode, &dm)) { 98 if (dm.dmPelsWidth >= MIN_WIDTH && 99 (dm.dmDisplayFrequency == 0 || 100 dm.dmDisplayFrequency >= MIN_FREQUENCY)) { 101 ndmodes++; 102 } 103 mode++; 104 } 105 106 /* Allocate memory for a list of all the display modes. */ 107 dmodes = (DisplayMode*) 108 malloc(ndmodes * sizeof(DisplayMode)); 109 110 /* Now that we know how many display modes to expect, 111 enumerate them again and save the information in 112 the list we allocated above. */ 113 i = 0; 114 mode = 0; 115 while (EnumDisplaySettings(NULL, mode, &dm)) { 116 /* Try to reject any display settings that seem unplausible. */ 117 if (dm.dmPelsWidth >= MIN_WIDTH && 118 (dm.dmDisplayFrequency == 0 || 119 dm.dmDisplayFrequency >= MIN_FREQUENCY)) { 120 dmodes[i].devmode = dm; 121 dmodes[i].valid = 1; /* XXX Not used for now. */ 122 dmodes[i].cap[DM_WIDTH] = dm.dmPelsWidth; 123 dmodes[i].cap[DM_HEIGHT] = dm.dmPelsHeight; 124 dmodes[i].cap[DM_PIXEL_DEPTH] = dm.dmBitsPerPel; 125 if (dm.dmDisplayFrequency == 0) { 126 /* Guess a reasonable guess. */ 127 /* Lame Windows 95 version of EnumDisplaySettings. */ 128 dmodes[i].cap[DM_HERTZ] = 60; 129 } else { 130 dmodes[i].cap[DM_HERTZ] = dm.dmDisplayFrequency; 131 } 132 i++; 133 } 134 mode++; 135 } 136 137 assert(i == ndmodes); 138} 139 140#else 141 142/* X Windows version of initGameModeSupport. */ 143static void 144initGameModeSupport(void) 145{ 146 if (ndmodes >= 0) { 147 /* ndmodes is initially -1 to indicate no 148 dmodes allocated yet. */ 149 return; 150 } 151 152 /* Determine how many display modes there are. */ 153 ndmodes = 0; 154} 155 156#endif 157 158/* This routine is based on similiar code in glut_dstr.c */ 159static DisplayMode * 160findMatch(DisplayMode * dmodes, int ndmodes, 161 Criterion * criteria, int ncriteria) 162{ 163 DisplayMode *found; 164 int *bestScore, *thisScore; 165 int i, j, numok, result = 0, worse, better; 166 167 found = NULL; 168 numok = 1; /* "num" capability is indexed from 1, 169 not 0. */ 170 171 /* XXX alloca canidate. */ 172 bestScore = (int *) malloc(ncriteria * sizeof(int)); 173 if (!bestScore) { 174 __glutFatalError("out of memory."); 175 } 176 for (j = 0; j < ncriteria; j++) { 177 /* Very negative number. */ 178 bestScore[j] = -32768; 179 } 180 181 /* XXX alloca canidate. */ 182 thisScore = (int *) malloc(ncriteria * sizeof(int)); 183 if (!thisScore) { 184 __glutFatalError("out of memory."); 185 } 186 187 for (i = 0; i < ndmodes; i++) { 188 if (dmodes[i].valid) { 189 worse = 0; 190 better = 0; 191 192 for (j = 0; j < ncriteria; j++) { 193 int cap, cvalue, dvalue; 194 195 cap = criteria[j].capability; 196 cvalue = criteria[j].value; 197 if (cap == NUM) { 198 dvalue = numok; 199 } else { 200 dvalue = dmodes[i].cap[cap]; 201 } 202#ifdef TEST 203 if (verbose) 204 printf(" %s %s %d to %d\n", 205 capstr[cap], compstr[criteria[j].comparison], cvalue, dvalue); 206#endif 207 switch (criteria[j].comparison) { 208 case EQ: 209 result = cvalue == dvalue; 210 thisScore[j] = 1; 211 break; 212 case NEQ: 213 result = cvalue != dvalue; 214 thisScore[j] = 1; 215 break; 216 case LT: 217 result = dvalue < cvalue; 218 thisScore[j] = dvalue - cvalue; 219 break; 220 case GT: 221 result = dvalue > cvalue; 222 thisScore[j] = dvalue - cvalue; 223 break; 224 case LTE: 225 result = dvalue <= cvalue; 226 thisScore[j] = dvalue - cvalue; 227 break; 228 case GTE: 229 result = (dvalue >= cvalue); 230 thisScore[j] = dvalue - cvalue; 231 break; 232 case MIN: 233 result = dvalue >= cvalue; 234 thisScore[j] = cvalue - dvalue; 235 break; 236 } 237 238#ifdef TEST 239 if (verbose) 240 printf(" result=%d score=%d bestScore=%d\n", result, thisScore[j], bestScore[j]); 241#endif 242 243 if (result) { 244 if (better || thisScore[j] > bestScore[j]) { 245 better = 1; 246 } else if (thisScore[j] == bestScore[j]) { 247 /* Keep looking. */ 248 } else { 249 goto nextDM; 250 } 251 } else { 252 if (cap == NUM) { 253 worse = 1; 254 } else { 255 goto nextDM; 256 } 257 } 258 259 } 260 261 if (better && !worse) { 262 found = &dmodes[i]; 263 for (j = 0; j < ncriteria; j++) { 264 bestScore[j] = thisScore[j]; 265 } 266 } 267 numok++; 268 269 nextDM:; 270 271 } 272 } 273 free(bestScore); 274 free(thisScore); 275 return found; 276} 277 278/** 279 * Parses strings in the form of: 280 * 800x600 281 * 800x600:16 282 * 800x600@60 283 * 800x600:16@60 284 * @60 285 * :16 286 * :16@60 287 * NOTE that @ before : is not parsed. 288 */ 289static int 290specialCaseParse(char *word, Criterion * criterion, int mask) 291{ 292 char *xstr, *response; 293 int got; 294 int width, height, bpp, hertz; 295 296 switch(word[0]) { 297 case '0': 298 case '1': 299 case '2': 300 case '3': 301 case '4': 302 case '5': 303 case '6': 304 case '7': 305 case '8': 306 case '9': 307 /* The WWWxHHH case. */ 308 if (mask & (1 << DM_WIDTH)) { 309 return -1; 310 } 311 xstr = strpbrk(&word[1], "x"); 312 if (xstr) { 313 width = (int) strtol(word, &response, 0); 314 if (response == word || response[0] != 'x') { 315 /* Not a valid number OR needs to be followed by 'x'. */ 316 return -1; 317 } 318 height = (int) strtol(&xstr[1], &response, 0); 319 if (response == &xstr[1]) { 320 /* Not a valid number. */ 321 return -1; 322 } 323 criterion[0].capability = DM_WIDTH; 324 criterion[0].comparison = EQ; 325 criterion[0].value = width; 326 criterion[1].capability = DM_HEIGHT; 327 criterion[1].comparison = EQ; 328 criterion[1].value = height; 329 got = specialCaseParse(response, 330 &criterion[2], 1 << DM_WIDTH); 331 if (got >= 0) { 332 return got + 2; 333 } else { 334 return -1; 335 } 336 } 337 return -1; 338 case ':': 339 /* The :BPP case. */ 340 if (mask & (1 << DM_PIXEL_DEPTH)) { 341 return -1; 342 } 343 bpp = (int) strtol(&word[1], &response, 0); 344 if (response == &word[1]) { 345 /* Not a valid number. */ 346 return -1; 347 } 348 criterion[0].capability = DM_PIXEL_DEPTH; 349 criterion[0].comparison = EQ; 350 criterion[0].value = bpp; 351 got = specialCaseParse(response, 352 &criterion[1], 1 << DM_WIDTH | 1 << DM_PIXEL_DEPTH); 353 if (got >= 0) { 354 return got + 1; 355 } else { 356 return -1; 357 } 358 case '@': 359 /* The @HZ case. */ 360 if (mask & (1 << DM_HERTZ)) { 361 return -1; 362 } 363 hertz = (int) strtol(&word[1], &response, 0); 364 if (response == &word[1]) { 365 /* Not a valid number. */ 366 return -1; 367 } 368 criterion[0].capability = DM_HERTZ; 369 criterion[0].comparison = EQ; 370 criterion[0].value = hertz; 371 got = specialCaseParse(response, 372 &criterion[1], ~DM_HERTZ); 373 if (got >= 0) { 374 return got + 1; 375 } else { 376 return -1; 377 } 378 case '\0': 379 return 0; 380 } 381 return -1; 382} 383 384/* This routine is based on similiar code in glut_dstr.c */ 385static int 386parseCriteria(char *word, Criterion * criterion) 387{ 388 char *cstr, *vstr, *response; 389 int comparator, value = 0; 390 391 cstr = strpbrk(word, "=><!~"); 392 if (cstr) { 393 switch (cstr[0]) { 394 case '=': 395 comparator = EQ; 396 vstr = &cstr[1]; 397 break; 398 case '~': 399 comparator = MIN; 400 vstr = &cstr[1]; 401 break; 402 case '>': 403 if (cstr[1] == '=') { 404 comparator = GTE; 405 vstr = &cstr[2]; 406 } else { 407 comparator = GT; 408 vstr = &cstr[1]; 409 } 410 break; 411 case '<': 412 if (cstr[1] == '=') { 413 comparator = LTE; 414 vstr = &cstr[2]; 415 } else { 416 comparator = LT; 417 vstr = &cstr[1]; 418 } 419 break; 420 case '!': 421 if (cstr[1] == '=') { 422 comparator = NEQ; 423 vstr = &cstr[2]; 424 } else { 425 return -1; 426 } 427 break; 428 default: 429 return -1; 430 } 431 value = (int) strtol(vstr, &response, 0); 432 if (response == vstr) { 433 /* Not a valid number. */ 434 return -1; 435 } 436 *cstr = '\0'; 437 } else { 438 comparator = NONE; 439 } 440 switch (word[0]) { 441 case 'b': 442 if (!strcmp(word, "bpp")) { 443 criterion[0].capability = DM_PIXEL_DEPTH; 444 if (comparator == NONE) { 445 return -1; 446 } else { 447 criterion[0].comparison = comparator; 448 criterion[0].value = value; 449 return 1; 450 } 451 } 452 return -1; 453 case 'h': 454 if (!strcmp(word, "height")) { 455 criterion[0].capability = DM_HEIGHT; 456 if (comparator == NONE) { 457 return -1; 458 } else { 459 criterion[0].comparison = comparator; 460 criterion[0].value = value; 461 return 1; 462 } 463 } 464 if (!strcmp(word, "hertz")) { 465 criterion[0].capability = DM_HERTZ; 466 if (comparator == NONE) { 467 return -1; 468 } else { 469 criterion[0].comparison = comparator; 470 criterion[0].value = value; 471 return 1; 472 } 473 } 474 return -1; 475 case 'n': 476 if (!strcmp(word, "num")) { 477 criterion[0].capability = DM_NUM; 478 if (comparator == NONE) { 479 return -1; 480 } else { 481 criterion[0].comparison = comparator; 482 criterion[0].value = value; 483 return 1; 484 } 485 } 486 return -1; 487 case 'w': 488 if (!strcmp(word, "width")) { 489 criterion[0].capability = DM_WIDTH; 490 if (comparator == NONE) { 491 return -1; 492 } else { 493 criterion[0].comparison = comparator; 494 criterion[0].value = value; 495 return 1; 496 } 497 } 498 return -1; 499 } 500 if (comparator == NONE) { 501 return specialCaseParse(word, criterion, 0); 502 } 503 return -1; 504} 505 506/* This routine is based on similiar code in glut_dstr.c */ 507static Criterion * 508parseDisplayString(const char *display, int *ncriteria) 509{ 510 Criterion *criteria = NULL; 511 int n, parsed; 512 char *copy, *word; 513 514 copy = __glutStrdup(display); 515 /* Attempt to estimate how many criteria entries should be 516 needed. */ 517 n = 0; 518 word = strtok(copy, " \t"); 519 while (word) { 520 n++; 521 word = strtok(NULL, " \t"); 522 } 523 /* Allocate number of words of criteria. A word 524 could contain as many as four criteria in the 525 worst case. Example: 800x600:16@60 */ 526 criteria = (Criterion *) malloc(4 * n * sizeof(Criterion)); 527 if (!criteria) { 528 __glutFatalError("out of memory."); 529 } 530 531 /* Re-copy the copy of the display string. */ 532 strcpy(copy, display); 533 534 n = 0; 535 word = strtok(copy, " \t"); 536 while (word) { 537 parsed = parseCriteria(word, &criteria[n]); 538 if (parsed >= 0) { 539 n += parsed; 540 } else { 541 __glutWarning("Unrecognized game mode string word: %s (ignoring)\n", word); 542 } 543 word = strtok(NULL, " \t"); 544 } 545 546 free(copy); 547 *ncriteria = n; 548 return criteria; 549} 550 551void GLUTAPIENTRY 552glutGameModeString(const char *string) 553{ 554 Criterion *criteria; 555 int ncriteria; 556 557 initGameModeSupport(); 558 criteria = parseDisplayString(string, &ncriteria); 559 currentDm = findMatch(dmodes, ndmodes, criteria, ncriteria); 560 free(criteria); 561} 562 563int GLUTAPIENTRY 564glutEnterGameMode(void) 565{ 566 GLUTwindow *window; 567 int width, height; 568 Window win; 569 570 if (__glutMappedMenu) { 571 __glutFatalUsage("entering game mode not allowed while menus in use"); 572 } 573 if (__glutGameModeWindow) { 574 /* Already in game mode, so blow away game mode 575 window so apps can change resolutions. */ 576 window = __glutGameModeWindow; 577 /* Setting the game mode window to NULL tricks 578 the window destroy code into not undoing the 579 screen display change since we plan on immediately 580 doing another mode change. */ 581 __glutGameModeWindow = NULL; 582 __glutDestroyWindow(window, window); 583 } 584 585 /* Assume default screen size until we find out if we 586 can actually change the display settings. */ 587 width = __glutScreenWidth; 588 height = __glutScreenHeight; 589 590 if (currentDm) { 591#ifdef _WIN32 592 LONG status; 593 static int registered = 0; 594 595 status = ChangeDisplaySettings(¤tDm->devmode, 596 CDS_FULLSCREEN); 597 if (status == DISP_CHANGE_SUCCESSFUL) { 598 __glutDisplaySettingsChanged = 1; 599 width = currentDm->cap[DM_WIDTH]; 600 height = currentDm->cap[DM_HEIGHT]; 601 if (!registered) { 602 atexit(__glutCloseDownGameMode); 603 registered = 1; 604 } 605 } else { 606 /* Switch back to default resolution. */ 607 ChangeDisplaySettings(NULL, 0); 608 } 609#endif 610 } 611 612 window = __glutCreateWindow(NULL, 0, 0, 613 width, height, /* game mode */ 1); 614 win = window->win; 615 616#if !defined(_WIN32) 617 if (__glutMotifHints == None) { 618 __glutMotifHints = XSGIFastInternAtom(__glutDisplay, "_MOTIF_WM_HINTS", 619 SGI_XA__MOTIF_WM_HINTS, 0); 620 if (__glutMotifHints == None) { 621 __glutWarning("Could not intern X atom for _MOTIF_WM_HINTS."); 622 } 623 } 624 625 /* Game mode window is a toplevel window. */ 626 XSetWMProtocols(__glutDisplay, win, &__glutWMDeleteWindow, 1); 627#endif 628 629 /* Schedule the fullscreen property to be added and to 630 make sure the window is configured right. Win32 631 doesn't need this. */ 632 window->desiredX = 0; 633 window->desiredY = 0; 634 window->desiredWidth = width; 635 window->desiredHeight = height; 636 window->desiredConfMask |= CWX | CWY | CWWidth | CWHeight; 637#ifdef _WIN32 638 /* Win32 does not want to use GLUT_FULL_SCREEN_WORK 639 for game mode because we need to be maximizing 640 the window in game mode, not just sizing it to 641 take up the full screen. The Win32-ness of game 642 mode happens when you pass 1 in the gameMode parameter 643 to __glutCreateWindow above. A gameMode of creates 644 a WS_POPUP window, not a standard WS_OVERLAPPEDWINDOW 645 window. WS_POPUP ensures the taskbar is hidden. */ 646 __glutPutOnWorkList(window, 647 GLUT_CONFIGURE_WORK); 648#else 649 __glutPutOnWorkList(window, 650 GLUT_CONFIGURE_WORK | GLUT_FULL_SCREEN_WORK); 651#endif 652 653 __glutGameModeWindow = window; 654 return window->num + 1; 655} 656 657int GLUTAPIENTRY 658glutGameModeGet(GLenum mode) 659{ 660 switch (mode) { 661 case GLUT_GAME_MODE_ACTIVE: 662 return __glutGameModeWindow != NULL; 663 case GLUT_GAME_MODE_POSSIBLE: 664 return currentDm != NULL; 665 case GLUT_GAME_MODE_WIDTH: 666 return currentDm ? currentDm->cap[DM_WIDTH] : -1; 667 case GLUT_GAME_MODE_HEIGHT: 668 return currentDm ? currentDm->cap[DM_HEIGHT] : -1; 669 case GLUT_GAME_MODE_PIXEL_DEPTH: 670 return currentDm ? currentDm->cap[DM_PIXEL_DEPTH] : -1; 671 case GLUT_GAME_MODE_REFRESH_RATE: 672 return currentDm ? currentDm->cap[DM_HERTZ] : -1; 673 case GLUT_GAME_MODE_DISPLAY_CHANGED: 674 return __glutDisplaySettingsChanged; 675 default: 676 return -1; 677 } 678} 679