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