Home | History | Annotate | Line # | Download | only in glx
      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 }
    678