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