replace APIENTRY with GLUTAPIENTRY to be consistant with glut.h
[mesa.git] / src / glut / glx / glut_gamemode.c
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
32 int __glutDisplaySettingsChanged = 0;
33 static DisplayMode *dmodes, *currentDm = NULL;
34 static int ndmodes = -1;
35 GLUTwindow *__glutGameModeWindow = NULL;
36
37 #ifdef TEST
38 static char *compstr[] =
39 {
40 "none", "=", "!=", "<=", ">=", ">", "<", "~"
41 };
42 static char *capstr[] =
43 {
44 "width", "height", "bpp", "hertz", "num"
45 };
46 #endif
47
48 void
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
62 void GLUTAPIENTRY
63 glutLeaveGameMode(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
81 static void
82 initGameModeSupport(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. */
143 static void
144 initGameModeSupport(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 */
159 static DisplayMode *
160 findMatch(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 */
289 static int
290 specialCaseParse(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 */
385 static int
386 parseCriteria(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 */
507 static Criterion *
508 parseDisplayString(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
551 void GLUTAPIENTRY
552 glutGameModeString(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
563 int GLUTAPIENTRY
564 glutEnterGameMode(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(&currentDm->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
657 int GLUTAPIENTRY
658 glutGameModeGet(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 }