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