home *** CD-ROM | disk | FTP | other *** search
/ Magazyn Amiga 5 / MA_Cover_5.iso / ppc / mesa / src-glut / glut_win.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-01-31  |  34.7 KB  |  1,187 lines

  1. /* Copyright (c) Mark J. Kilgard, 1994.  */
  2.  
  3. /* This program is freely distributable without licensing fees
  4.    and is provided without guarantee or warrantee expressed or
  5.    implied. This program is -not- in the public domain. */
  6.  
  7. #include <stdlib.h>
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <assert.h>
  11. #if !defined(WIN32)
  12. #include <X11/Xlib.h>
  13. #include <X11/Xutil.h>
  14. #include <X11/Xatom.h>  /* for XA_RGB_DEFAULT_MAP atom */
  15. #if defined(__vms)
  16. #include <X11/StdCmap.h>  /* for XmuLookupStandardColormap */
  17. #else
  18. #include <X11/Xmu/StdCmap.h>  /* for XmuLookupStandardColormap */
  19. #endif
  20. #endif
  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.  
  30. #include <GL/glut.h>
  31. #include "glutint.h"
  32.  
  33. GLUTwindow *__glutCurrentWindow = NULL;
  34. GLUTwindow **__glutWindowList = NULL;
  35. int __glutWindowListSize = 0;
  36. GLUTstale *__glutStaleWindowList = NULL;
  37.  
  38. void (*__glutFreeOverlayFunc) (GLUToverlay *);
  39. XVisualInfo *(*__glutDetermineVisualFromString) (char *string, Bool * treatAsSingle,
  40.   Criterion * requiredCriteria, int nRequired, int requiredMask) = NULL;
  41.  
  42. static Criterion requiredWindowCriteria[] =
  43. {
  44.   {LEVEL, EQ, 0},
  45.   {TRANSPARENT, EQ, 0}
  46. };
  47. static int numRequiredWindowCriteria = sizeof(requiredWindowCriteria) / sizeof(Criterion);
  48. static int requiredWindowCriteriaMask = (1 << LEVEL) | (1 << TRANSPARENT);
  49.  
  50. static void
  51. cleanWindowWorkList(GLUTwindow * window)
  52. {
  53.   GLUTwindow **pEntry = &__glutWindowWorkList;
  54.   GLUTwindow *entry = __glutWindowWorkList;
  55.  
  56.   /* Tranverse singly-linked window work list look for the
  57.      window. */
  58.   while (entry) {
  59.     if (entry == window) {
  60.       /* Found it; delete it. */
  61.       *pEntry = entry->prevWorkWin;
  62.       return;
  63.     } else {
  64.       pEntry = &entry->prevWorkWin;
  65.       entry = *pEntry;
  66.     }
  67.   }
  68. }
  69.  
  70. static void
  71. cleanStaleWindowList(GLUTwindow * window)
  72. {
  73.   GLUTstale **pEntry = &__glutStaleWindowList;
  74.   GLUTstale *entry = __glutStaleWindowList;
  75.  
  76.   /* Tranverse singly-linked stale window list look for the
  77.      window ID. */
  78.   while (entry) {
  79.     if (entry->window == window) {
  80.       /* Found it; delete it. */
  81.       *pEntry = entry->next;
  82.       free(entry);
  83.       return;
  84.     } else {
  85.       pEntry = &entry->next;
  86.       entry = *pEntry;
  87.     }
  88.   }
  89. }
  90.  
  91. static GLUTwindow *__glutWindowCache = NULL;
  92.  
  93. GLUTwindow *
  94. __glutGetWindow(Window win)
  95. {
  96.   GLUTstale *entry;
  97.   int i;
  98.  
  99.   /* Does win belong to the last window ID looked up? */
  100.   if (__glutWindowCache && (win == __glutWindowCache->win ||
  101.       (__glutWindowCache->overlay && win ==
  102.         __glutWindowCache->overlay->win))) {
  103.     return
  104.       __glutWindowCache;
  105.   }
  106.   /* Otherwise scan the window list looking for the window ID. */
  107.   for (i = 0; i < __glutWindowListSize; i++) {
  108.     if (__glutWindowList[i]) {
  109.       if (win == __glutWindowList[i]->win) {
  110.         __glutWindowCache = __glutWindowList[i];
  111.         return __glutWindowCache;
  112.       }
  113.       if (__glutWindowList[i]->overlay) {
  114.         if (win == __glutWindowList[i]->overlay->win) {
  115.           __glutWindowCache = __glutWindowList[i];
  116.           return __glutWindowCache;
  117.         }
  118.       }
  119.     }
  120.   }
  121.   /* Scan through destroyed overlay window IDs for which no
  122.      DestroyNotify has yet been received. */
  123.   for (entry = __glutStaleWindowList; entry; entry = entry->next) {
  124.     if (entry->win == win)
  125.       return entry->window;
  126.   }
  127.   return NULL;
  128. }
  129.  
  130. /* CENTRY */
  131. int APIENTRY 
  132. glutGetWindow(void)
  133. {
  134.   if (__glutCurrentWindow) {
  135.     return __glutCurrentWindow->num + 1;
  136.   } else {
  137.     return 0;
  138.   }
  139. }
  140. /* ENDCENTRY */
  141.  
  142. void
  143. __glutSetWindow(GLUTwindow * window)
  144. {
  145.   /* It is tempting to try to short-circuit the call to
  146.      glXMakeCurrent if we "know" we are going to make current
  147.      to a window we are already current to.  In fact, this
  148.      assumption breaks when GLUT is expected to integrated with
  149.      other OpenGL windowing APIs that also make current to
  150.      OpenGL contexts.  Since glXMakeCurrent short-circuits the
  151.      "already bound" case, GLUT avoids the temptation to do so
  152.      too. */
  153.   __glutCurrentWindow = window;
  154.  
  155.   glXMakeCurrent(__glutDisplay, __glutCurrentWindow->renderWin,
  156.     __glutCurrentWindow->renderCtx);
  157.  
  158.   /* We should be careful to force a finish between each
  159.      iteration through the GLUT main loop if indirect OpenGL 
  160.      contexts are in use; indirect contexts tend to have  much
  161.      longer latency because lots of OpenGL extension requests
  162.      can queue up in the X protocol stream.  We accomplish this
  163.      by posting GLUT_FINISH_WORK to be done. */
  164.   if (!__glutCurrentWindow->isDirect)
  165.     __glutPutOnWorkList(__glutCurrentWindow, GLUT_FINISH_WORK);
  166.  
  167.   /* If debugging is enabled, we'll want to check this window
  168.      for any OpenGL errors every iteration through the GLUT
  169.      main loop.  To accomplish this, we post the
  170.      GLUT_DEBUG_WORK to be done on this window. */
  171.   if (__glutDebug)
  172.     __glutPutOnWorkList(__glutCurrentWindow, GLUT_DEBUG_WORK);
  173. }
  174.  
  175. /* CENTRY */
  176. void APIENTRY 
  177. glutSetWindow(int win)
  178. {
  179.   GLUTwindow *window;
  180.  
  181.   if (win < 1 || win > __glutWindowListSize) {
  182.     __glutWarning("glutSetWindow attempted on bogus window.");
  183.     return;
  184.   }
  185.   window = __glutWindowList[win - 1];
  186.   if (!window) {
  187.     __glutWarning("glutSetWindow attempted on bogus window.");
  188.     return;
  189.   }
  190.   __glutSetWindow(window);
  191. }
  192. /* ENDCENTRY */
  193.  
  194. static int
  195. getUnusedWindowSlot(void)
  196. {
  197.   int i;
  198.  
  199.   /* Look for allocated, unused slot. */
  200.   for (i = 0; i < __glutWindowListSize; i++) {
  201.     if (!__glutWindowList[i]) {
  202.       return i;
  203.     }
  204.   }
  205.   /* Allocate a new slot. */
  206.   __glutWindowListSize++;
  207.   if (__glutWindowList) {
  208.     __glutWindowList = (GLUTwindow **)
  209.       realloc(__glutWindowList,
  210.       __glutWindowListSize * sizeof(GLUTwindow *));
  211.   } else {
  212.     /* XXX Some realloc's do not correctly perform a malloc
  213.        when asked to perform a realloc on a NULL pointer,
  214.        though the ANSI C library spec requires this. */
  215.     __glutWindowList = (GLUTwindow **)
  216.       malloc(sizeof(GLUTwindow *));
  217.   }
  218.   if (!__glutWindowList)
  219.     __glutFatalError("out of memory.");
  220.   __glutWindowList[__glutWindowListSize - 1] = NULL;
  221.   return __glutWindowListSize - 1;
  222. }
  223.  
  224. static XVisualInfo *
  225. getVisualInfoCI(unsigned int mode)
  226. {
  227.   static int bufSizeList[] =
  228.   {16, 12, 8, 4, 2, 1, 0};
  229.   XVisualInfo *vi;
  230.   int list[32];
  231.   int i, n = 0;
  232.  
  233.   list[n++] = GLX_BUFFER_SIZE;
  234.   list[n++] = 1;
  235.   if (GLUT_WIND_IS_DOUBLE(mode)) {
  236.     list[n++] = GLX_DOUBLEBUFFER;
  237.   }
  238.   if (GLUT_WIND_IS_STEREO(mode)) {
  239.     list[n++] = GLX_STEREO;
  240.   }
  241.   if (GLUT_WIND_HAS_DEPTH(mode)) {
  242.     list[n++] = GLX_DEPTH_SIZE;
  243.     list[n++] = 1;
  244.   }
  245.   if (GLUT_WIND_HAS_STENCIL(mode)) {
  246.     list[n++] = GLX_STENCIL_SIZE;
  247.     list[n++] = 1;
  248.   }
  249.   list[n] = (int) None; /* terminate list */
  250.  
  251.   /* glXChooseVisual specify GLX_BUFFER_SIZE prefers the
  252.      "smallest index buffer of at least the specified size".
  253.      This would be reasonable if GLUT allowed the user to
  254.      specify the required buffe size, but GLUT's display mode
  255.      is too simplistic (easy to use?). GLUT should try to find
  256.      the "largest".  So start with a large buffer size and
  257.      shrink until we find a matching one that exists. */
  258.  
  259.   for (i = 0; bufSizeList[i]; i++) {
  260.     /* XXX Assumes list[1] is where GLX_BUFFER_SIZE parameter
  261.        is. */
  262.     list[1] = bufSizeList[i];
  263.     vi = glXChooseVisual(__glutDisplay,
  264.       __glutScreen, list);
  265.     if (vi)
  266.       return vi;
  267.   }
  268.   return NULL;
  269. }
  270.  
  271. static XVisualInfo *
  272. getVisualInfoRGB(unsigned int mode)
  273. {
  274.   int list[32];
  275.   int n = 0;
  276.  
  277.   /* XXX Would a caching mechanism to minize the calls to
  278.      glXChooseVisual? You'd have to reference count
  279.      XVisualInfo* pointers. */
  280.  
  281.   list[n++] = GLX_RGBA;
  282.   list[n++] = GLX_RED_SIZE;
  283.   list[n++] = 1;
  284.   list[n++] = GLX_GREEN_SIZE;
  285.   list[n++] = 1;
  286.   list[n++] = GLX_BLUE_SIZE;
  287.   list[n++] = 1;
  288.   if (GLUT_WIND_HAS_ALPHA(mode)) {
  289.     list[n++] = GLX_ALPHA_SIZE;
  290.     list[n++] = 1;
  291.   }
  292.   if (GLUT_WIND_IS_DOUBLE(mode)) {
  293.     list[n++] = GLX_DOUBLEBUFFER;
  294.   }
  295.   if (GLUT_WIND_IS_STEREO(mode)) {
  296.     list[n++] = GLX_STEREO;
  297.   }
  298.   if (GLUT_WIND_HAS_DEPTH(mode)) {
  299.     list[n++] = GLX_DEPTH_SIZE;
  300.     list[n++] = 1;
  301.   }
  302.   if (GLUT_WIND_HAS_STENCIL(mode)) {
  303.     list[n++] = GLX_STENCIL_SIZE;
  304.     list[n++] = 1;
  305.   }
  306.   if (GLUT_WIND_HAS_ACCUM(mode)) {
  307.     list[n++] = GLX_ACCUM_RED_SIZE;
  308.     list[n++] = 1;
  309.     list[n++] = GLX_ACCUM_GREEN_SIZE;
  310.     list[n++] = 1;
  311.     list[n++] = GLX_ACCUM_BLUE_SIZE;
  312.     list[n++] = 1;
  313.     if (GLUT_WIND_HAS_ALPHA(mode)) {
  314.       list[n++] = GLX_ACCUM_ALPHA_SIZE;
  315.       list[n++] = 1;
  316.     }
  317.   }
  318. #if defined(GLX_VERSION_1_1) && defined(GLX_SGIS_multisample)
  319.   if (GLUT_WIND_IS_MULTISAMPLE(mode)) {
  320.     if (!__glutIsSupportedByGLX("GLX_SGIS_multisample"))
  321.       return NULL;
  322.     list[n++] = GLX_SAMPLES_SGIS;
  323.     /* XXX Is 4 a reasonable minimum acceptable number of
  324.        samples? */
  325.     list[n++] = 4;
  326.   }
  327. #endif
  328.   list[n] = (int) None; /* terminate list */
  329.  
  330.   return glXChooseVisual(__glutDisplay,
  331.     __glutScreen, list);
  332. }
  333.  
  334. XVisualInfo *
  335. __glutGetVisualInfo(unsigned int mode)
  336. {
  337.   /* XXX GLUT_LUMINANCE not implemented for GLUT 3.0. */
  338.   if (GLUT_WIND_IS_LUMINANCE(mode))
  339.     return NULL;
  340.  
  341.   if (GLUT_WIND_IS_RGB(mode))
  342.     return getVisualInfoRGB(mode);
  343.   else
  344.     return getVisualInfoCI(mode);
  345. }
  346.  
  347. XVisualInfo *
  348. __glutDetermineVisual(
  349.   unsigned int displayMode,
  350.   Bool * treatAsSingle,
  351.   XVisualInfo * (getVisualInfo) (unsigned int))
  352. {
  353.   XVisualInfo *vis;
  354.  
  355.   *treatAsSingle = GLUT_WIND_IS_SINGLE(displayMode);
  356.   vis = getVisualInfo(displayMode);
  357.   if (!vis) {
  358.     /* Fallback cases when can't get exactly what was asked
  359.        for... */
  360.     if (GLUT_WIND_IS_SINGLE(displayMode)) {
  361.       /* If we can't find a single buffered visual, try looking
  362.          for a double buffered visual.  We can treat a double
  363.          buffered visual as a single buffer visual by changing
  364.          the draw buffer to GL_FRONT and treating any swap
  365.          buffers as no-ops. */
  366.       displayMode |= GLUT_DOUBLE;
  367.       vis = getVisualInfo(displayMode);
  368.       *treatAsSingle = True;
  369.     }
  370.     if (!vis && GLUT_WIND_IS_MULTISAMPLE(displayMode)) {
  371.       /* If we can't seem to get multisampling (ie, not Reality
  372.          Engine class graphics!), go without multisampling.  It
  373.          is up to the application to query how many multisamples
  374.          were allocated (0 equals no multisampling) if the
  375.          application is going to use multisampling for more than
  376.          just antialiasing. */
  377.       displayMode &= ~GLUT_MULTISAMPLE;
  378.       vis = getVisualInfo(displayMode);
  379.     }
  380.   }
  381.   return vis;
  382. }
  383.  
  384. void
  385. __glutSetupColormap(XVisualInfo * vi, GLUTcolormap ** colormap, Colormap * cmap, int isRGB)
  386. {
  387. #if defined(WIN32)
  388.   if (vi->dwFlags & PFD_NEED_PALETTE || vi->iPixelType==PFD_TYPE_COLORINDEX) {
  389.     *colormap = __glutAssociateColormap(vi);
  390.     *cmap = (*colormap)->cmap;
  391.   } else {
  392.     *colormap = NULL;
  393.     *cmap = 0;
  394.   }
  395. #else
  396.   Status status;
  397.   XStandardColormap *standardCmaps;
  398.   int i, numCmaps;
  399.   static Atom hpColorRecoveryAtom = -1;
  400.  
  401.   switch (vi->class) {
  402.   case PseudoColor:
  403.     if (isRGB) {
  404.       /* Mesa might return a PseudoColor visual for RGB mode. */
  405.       *colormap = NULL;
  406.       if (MaxCmapsOfScreen(DefaultScreenOfDisplay(__glutDisplay)) == 1
  407.         && vi->visual == DefaultVisual(__glutDisplay, __glutScreen)) {
  408.         char *private = getenv("MESA_PRIVATE_CMAP");
  409.  
  410.         if (private) {
  411.           /* User doesn't want to share colormaps. */
  412.           *cmap = XCreateColormap(__glutDisplay, __glutRoot,
  413.             vi->visual, AllocNone);
  414.         } else {
  415.           /* Share the root colormap. */
  416.           *cmap = DefaultColormap(__glutDisplay, __glutScreen);
  417.         }
  418.       } else {
  419.         /* Get our own PseudoColor colormap. */
  420.         *cmap = XCreateColormap(__glutDisplay, __glutRoot,
  421.           vi->visual, AllocNone);
  422.       }
  423.     } else {
  424.       /* CI mode, real GLX never returns a PseudoColor visual
  425.          for RGB mode. */
  426.       *colormap = __glutAssociateColormap(vi);
  427.       *cmap = (*colormap)->cmap;
  428.     }
  429.     break;
  430.   case TrueColor:
  431.   case DirectColor:
  432.     *colormap = NULL;   /* NULL if RGBA */
  433.  
  434.     /* Hewlett-Packard supports a feature called "HP Color
  435.        Recovery". Mesa has code to use HP Color Recovery.  For
  436.        Mesa to use this feature, the atom
  437.        _HP_RGB_SMOOTH_MAP_LIST must be defined on the root
  438.        window AND the colormap obtainable by XGetRGBColormaps
  439.        for that atom must be set on the window.  If that
  440.        colormap is not set, the output will look stripy. */
  441.  
  442.     if (hpColorRecoveryAtom == -1) {
  443.       char *xvendor;
  444.  
  445. #define VENDOR_HP "Hewlett-Packard"
  446.  
  447.       /* Only makes sense to make XInternAtom round-trip if we
  448.          know that we are connected to an HP X server. */
  449.       xvendor = ServerVendor(__glutDisplay);
  450.       if (!strncmp(xvendor, VENDOR_HP, sizeof(VENDOR_HP) - 1)) {
  451.         hpColorRecoveryAtom = XInternAtom(__glutDisplay, "_HP_RGB_SMOOTH_MAP_LIST", True);
  452.       } else {
  453.         hpColorRecoveryAtom = None;
  454.       }
  455.     }
  456.     if (hpColorRecoveryAtom != None) {
  457.       status = XGetRGBColormaps(__glutDisplay, __glutRoot,
  458.         &standardCmaps, &numCmaps, hpColorRecoveryAtom);
  459.       if (status == 1) {
  460.         for (i = 0; i < numCmaps; i++) {
  461.           if (standardCmaps[i].visualid == vi->visualid) {
  462.             *cmap = standardCmaps[i].colormap;
  463.             XFree(standardCmaps);
  464.             return;
  465.           }
  466.         }
  467.         XFree(standardCmaps);
  468.       }
  469.     }
  470. #ifndef SOLARIS_2_4_BUG
  471.     /* Solaris 2.4 has a bug in its XmuLookupStandardColormap
  472.        implementation.  Please compile your Solaris 2.4 version
  473.        of GLUT with -DSOLARIS_2_4_BUG to work around this bug.
  474.        The symptom of the bug is that programs will get a
  475.        BadMatch error from X_CreateWindow when creating a GLUT
  476.        window because Solaris 2.4 creates a  corrupted
  477.        RGB_DEFAULT_MAP property.  Note that this workaround
  478.        prevents Colormap sharing between applications, perhaps
  479.        leading unnecessary colormap installations or colormap
  480.        flashing. */
  481.     status = XmuLookupStandardColormap(__glutDisplay,
  482.       vi->screen, vi->visualid, vi->depth, XA_RGB_DEFAULT_MAP,
  483.       /* replace */ False, /* retain */ True);
  484.     if (status == 1) {
  485.       status = XGetRGBColormaps(__glutDisplay, __glutRoot,
  486.         &standardCmaps, &numCmaps, XA_RGB_DEFAULT_MAP);
  487.       if (status == 1) {
  488.         for (i = 0; i < numCmaps; i++) {
  489.           if (standardCmaps[i].visualid == vi->visualid) {
  490.             *cmap = standardCmaps[i].colormap;
  491.             XFree(standardCmaps);
  492.             return;
  493.           }
  494.         }
  495.         XFree(standardCmaps);
  496.       }
  497.     }
  498. #endif
  499.     /* If no standard colormap but TrueColor, just make a
  500.        private one. */
  501.     /* XXX Should do a better job of internal sharing for
  502.        privately allocated TrueColor colormaps. */
  503.     /* XXX DirectColor probably needs ramps hand initialized! */
  504.     *cmap = XCreateColormap(__glutDisplay, __glutRoot,
  505.       vi->visual, AllocNone);
  506.     break;
  507.   case StaticColor:
  508.   case StaticGray:
  509.   case GrayScale:
  510.     /* Mesa supports these visuals */
  511.     *colormap = NULL;
  512.     *cmap = XCreateColormap(__glutDisplay, __glutRoot,
  513.       vi->visual, AllocNone);
  514.     break;
  515.   default:
  516.     __glutFatalError(
  517.       "could not allocate colormap for visual type: %d.",
  518.       vi->class);
  519.   }
  520.   return;
  521. #endif
  522. }
  523.  
  524. void
  525. __glutDefaultDisplay(void)
  526. {
  527.   /* XXX Remove the warning after GLUT 3.0. */
  528.   __glutWarning("The following is a new check for GLUT 3.0; update your code.");
  529.   __glutFatalError(
  530.     "redisplay needed for window %d, but no display callback.",
  531.     __glutCurrentWindow->num + 1);
  532. }
  533.  
  534. void
  535. __glutDefaultReshape(int width, int height)
  536. {
  537.   GLUToverlay *overlay;
  538.  
  539.   /* Adjust the viewport of the window (and overlay if one
  540.      exists). */
  541.   glXMakeCurrent(__glutDisplay, __glutCurrentWindow->win,
  542.     __glutCurrentWindow->ctx);
  543.   glViewport(0, 0, (GLsizei) width, (GLsizei) height);
  544.   overlay = __glutCurrentWindow->overlay;
  545.   if (overlay) {
  546.     glXMakeCurrent(__glutDisplay, overlay->win, overlay->ctx);
  547.     glViewport(0, 0, (GLsizei) width, (GLsizei) height);
  548.   }
  549.   /* Make sure we are current to the current layer (application
  550.      should be able to count on the current layer not changing
  551.      unless the application explicitly calls glutUseLayer). */
  552.   glXMakeCurrent(__glutDisplay, __glutCurrentWindow->renderWin,
  553.     __glutCurrentWindow->renderCtx);
  554. }
  555.  
  556. XVisualInfo *
  557. __glutDetermineWindowVisual(Bool * treatAsSingle, Bool * visAlloced)
  558. {
  559.   if (__glutDisplayString) {
  560.  
  561.     /* __glutDisplayString should be NULL except if
  562.        glutInitDisplayString has been called to register a
  563.        different display string.  Calling glutInitDisplayString
  564.        means using a string instead of an integer mask determine 
  565.        the visual to use. Using the function pointer variable
  566.        __glutDetermineVisualFromString below avoids linking in
  567.        the code for implementing glutInitDisplayString (ie,
  568.        glut_dstr.o) unless glutInitDisplayString gets called by
  569.        the application. */
  570.  
  571.     assert(__glutDetermineVisualFromString);
  572.     *visAlloced = False;
  573.     return __glutDetermineVisualFromString(__glutDisplayString, treatAsSingle,
  574.       requiredWindowCriteria, numRequiredWindowCriteria, requiredWindowCriteriaMask);
  575.   } else {
  576.     *visAlloced = True;
  577.     return __glutDetermineVisual(__glutDisplayMode,
  578.       treatAsSingle, __glutGetVisualInfo);
  579.   }
  580. }
  581.  
  582. GLUTwindow *
  583. __glutCreateWindow(GLUTwindow * parent,
  584.   int x, int y, int width, int height)
  585. {
  586.   GLUTwindow *window;
  587.   XSetWindowAttributes wa;
  588.   unsigned long attribMask;
  589.   int winnum;
  590.   int i;
  591.  
  592. #if defined(WIN32)
  593.   WNDCLASS wc;
  594.   if (!GetClassInfo(GetModuleHandle(NULL), "GLUT", &wc))
  595.     __glutOpenWin32Connection(NULL);
  596. #else
  597.   if (!__glutDisplay)
  598.     __glutOpenXConnection(NULL);
  599. #endif
  600.   winnum = getUnusedWindowSlot();
  601.   window = (GLUTwindow *) malloc(sizeof(GLUTwindow));
  602.   if (!window)
  603.     __glutFatalError("out of memory.");
  604.   window->num = winnum;
  605.  
  606. #if !defined(WIN32)
  607.   window->vis = __glutDetermineWindowVisual(&window->treatAsSingle,
  608.     &window->visAlloced);
  609.   if (!window->vis) {
  610.     __glutFatalError(
  611.       "visual with necessary capabilities not found.");
  612.   }
  613.   __glutSetupColormap(window->vis, &window->colormap, &window->cmap,
  614.     GLUT_WIND_IS_RGB(__glutDisplayMode));
  615. #endif
  616.   window->eventMask = StructureNotifyMask | ExposureMask;
  617.  
  618.   attribMask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask;
  619.   wa.background_pixmap = None;
  620.   wa.border_pixel = 0;
  621.   wa.colormap = window->cmap;
  622.   wa.event_mask = window->eventMask;
  623.   if (parent) {
  624.     if (parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK)
  625.       wa.event_mask |= GLUT_HACK_STOP_PROPAGATE_MASK;
  626.     attribMask |= CWDontPropagate;
  627.     wa.do_not_propagate_mask = parent->eventMask & GLUT_DONT_PROPAGATE_FILTER_MASK;
  628.   } else {
  629.     wa.do_not_propagate_mask = 0;
  630.   }
  631.  
  632.   /* Stash width and height before Win32's __glutAdjustCoords possibly
  633.      overwrites the values. */
  634.   window->width = width;
  635.   window->height = height;
  636.   window->forceReshape = True;
  637.  
  638. #if defined(WIN32)
  639.   __glutAdjustCoords(parent ? parent->win : NULL, &x, &y, &width, &height);
  640.   window->win = XCreateWindow(__glutDisplay,
  641.     parent == NULL ? __glutRoot : parent->win,
  642.     x, y, width, height, 0,
  643.     0, InputOutput, 0,
  644.     attribMask, &wa);
  645.   window->hdc = GetDC(window->win);
  646.   /* must set the XHDC for fake glXChooseVisual & fake
  647.      glXCreateContext & fake XAllocColorCells. */
  648.   XHDC = window->hdc;
  649.   window->vis = __glutDetermineWindowVisual(&window->treatAsSingle,
  650.     &window->visAlloced);
  651.   if (!window->vis) {
  652.     __glutFatalError(
  653.       "visual with necessary capabilities not found.");
  654.   }
  655.   if (__glutDebug)
  656.     glXPrintPixelFormat(ChoosePixelFormat(window->hdc, window->vis), 
  657.             window->vis);
  658.   if (!SetPixelFormat(window->hdc,
  659.               ChoosePixelFormat(window->hdc, window->vis),
  660.               window->vis))
  661.     __glutFatalError("SetPixelFormat() failed in glutCreateWindow().");
  662.   __glutSetupColormap(window->vis, &window->colormap, &window->cmap,
  663.               GLUT_WIND_IS_RGB(__glutDisplayMode));
  664.   /* make sure subwindows get a windowStatus callback */
  665.   if (parent)
  666.     PostMessage(parent->win, WM_ACTIVATE, 0, 0);
  667. #else
  668.   window->win = XCreateWindow(__glutDisplay,
  669.     parent == NULL ? __glutRoot : parent->win,
  670.     x, y, width, height, 0,
  671.     window->vis->depth, InputOutput, window->vis->visual,
  672.     attribMask, &wa);
  673. #endif
  674.   window->renderWin = window->win;
  675.   window->ctx = glXCreateContext(__glutDisplay, window->vis,
  676.     None, __glutTryDirect);
  677.   window->renderCtx = window->ctx;
  678.   window->isDirect = glXIsDirect(__glutDisplay, window->ctx);
  679.   if (__glutForceDirect) {
  680.     if (!window->isDirect)
  681.       __glutFatalError("direct rendering not possible.");
  682.   }
  683.  
  684.   window->parent = parent;
  685.   if (parent) {
  686.     window->siblings = parent->children;
  687.     parent->children = window;
  688.   } else {
  689.     window->siblings = NULL;
  690.   }
  691.   window->overlay = NULL;
  692.   window->children = NULL;
  693.   window->display = __glutDefaultDisplay;
  694.   window->reshape = __glutDefaultReshape;
  695.   window->mouse = NULL;
  696.   window->motion = NULL;
  697.   window->windowStatus = NULL;
  698.   window->visibility = NULL;
  699.   window->passive = NULL;
  700.   window->entry = NULL;
  701.   window->special = NULL;
  702.   window->buttonBox = NULL;
  703.   window->dials = NULL;
  704.   window->spaceMotion = NULL;
  705.   window->spaceRotate = NULL;
  706.   window->spaceButton = NULL;
  707.   window->tabletMotion = NULL;
  708.   window->tabletButton = NULL;
  709.   window->tabletPos[0] = -1;
  710.   window->tabletPos[1] = -1;
  711.   window->keyboard = NULL;
  712.   window->shownState = 0;
  713.   window->visState = -1;  /* not VisibilityUnobscured,
  714.                              VisibilityPartiallyObscured, or
  715.                              VisibilityFullyObscured */
  716.   window->entryState = -1;  /* not EnterNotify or LeaveNotify */
  717.   window->workMask = GLUT_MAP_WORK;
  718.   window->desiredMapState = NormalState;
  719.   window->desiredConfMask = 0;
  720.   window->buttonUses = 0;
  721.   window->cursor = GLUT_CURSOR_INHERIT;
  722.   window->prevWorkWin = __glutWindowWorkList;
  723.   __glutWindowWorkList = window;
  724.   for (i = 0; i < GLUT_MAX_MENUS; i++) {
  725.     window->menu[i] = 0;
  726.   }
  727.   __glutWindowList[winnum] = window;
  728.   __glutSetWindow(window);
  729.  
  730.   __glutDetermineMesaSwapHackSupport();
  731.  
  732.   if (window->treatAsSingle) {
  733.     /* We do this because either the window really is single
  734.        buffered (in which case this is redundant, but harmless,
  735.        because this is the initial single-buffered context
  736.        state); or we are treating a double buffered window as a
  737.        single-buffered window because the system does not appear
  738.        to export any suitable single- buffered visuals (in which
  739.        the following are necessary). */
  740.     glDrawBuffer(GL_FRONT);
  741.     glReadBuffer(GL_FRONT);
  742.   }
  743.   return window;
  744. }
  745.  
  746. static int
  747. findColormaps(GLUTwindow * window,
  748.   Window * winlist, Colormap * cmaplist, int num, int max)
  749. {
  750.   GLUTwindow *child;
  751.   int i;
  752.  
  753.   /* Do not allow more entries that maximum number of
  754.      colormaps! */
  755.   if (num >= max)
  756.     return num;
  757.   /* Is cmap for this window already on the list? */
  758.   for (i = 0; i < num; i++) {
  759.     if (cmaplist[i] == window->cmap)
  760.       goto normalColormapAlreadyListed;
  761.   }
  762.   /* Not found on the list; add colormap and window. */
  763.   winlist[num] = window->win;
  764.   cmaplist[num] = window->cmap;
  765.   num++;
  766.  
  767. normalColormapAlreadyListed:
  768.  
  769.   /* Repeat above but for the overlay colormap if there one. */
  770.   if (window->overlay) {
  771.     if (num >= max)
  772.       return num;
  773.     for (i = 0; i < num; i++) {
  774.       if (cmaplist[i] == window->overlay->cmap)
  775.         goto overlayColormapAlreadyListed;
  776.     }
  777.     winlist[num] = window->overlay->win;
  778.     cmaplist[num] = window->overlay->cmap;
  779.     num++;
  780.   }
  781. overlayColormapAlreadyListed:
  782.  
  783.   /* Recursively search children. */
  784.   child = window->children;
  785.   while (child) {
  786.     num = findColormaps(child, winlist, cmaplist, num, max);
  787.     child = child->siblings;
  788.   }
  789.   return num;
  790. }
  791.  
  792. void
  793. __glutEstablishColormapsProperty(GLUTwindow * window)
  794. {
  795. #if !defined(WIN32)
  796.   /* this routine is strictly X.  Win32 doesn't need to do anything of
  797.      this sort (but has to do other wacky stuff later). */
  798.   static Atom wmColormapWindows = None;
  799.   Window *winlist;
  800.   Colormap *cmaplist;
  801.   Status status;
  802.   int maxcmaps, num;
  803.  
  804.   assert(!window->parent);
  805.   maxcmaps = MaxCmapsOfScreen(ScreenOfDisplay(__glutDisplay,
  806.       __glutScreen));
  807.   /* For portability reasons we don't use alloca for winlist
  808.      and cmaplist, but we could. */
  809.   winlist = (Window *) malloc(maxcmaps * sizeof(Window));
  810.   cmaplist = (Colormap *) malloc(maxcmaps * sizeof(Colormap));
  811.   num = findColormaps(window, winlist, cmaplist, 0, maxcmaps);
  812.   if (num < 2) {
  813.     /* Property no longer needed; remove it. */
  814.     wmColormapWindows = XSGIFastInternAtom(__glutDisplay,
  815.       "WM_COLORMAP_WINDOWS", SGI_XA_WM_COLORMAP_WINDOWS, False);
  816.     if (wmColormapWindows == None) {
  817.       __glutWarning("Could not intern X atom for WM_COLORMAP_WINDOWS.");
  818.       return;
  819.     }
  820.     XDeleteProperty(__glutDisplay, window->win, wmColormapWindows);
  821.   } else {
  822.     status = XSetWMColormapWindows(__glutDisplay, window->win,
  823.       winlist, num);
  824.     /* XSetWMColormapWindows should always work unless the
  825.        WM_COLORMAP_WINDOWS property cannot be intern'ed.  We
  826.        check to be safe. */
  827.     if (status == False)
  828.       __glutFatalError("XSetWMColormapWindows returned False.");
  829.   }
  830.   /* For portability reasons we don't use alloca for winlist
  831.      and cmaplist, but we could. */
  832.   free(winlist);
  833.   free(cmaplist);
  834. #endif
  835. }
  836.  
  837. GLUTwindow *
  838. __glutToplevelOf(GLUTwindow * window)
  839. {
  840.   while (window->parent) {
  841.     window = window->parent;
  842.   }
  843.   return window;
  844. }
  845.  
  846. /* CENTRY */
  847. int APIENTRY 
  848. glutCreateWindow(const char *title)
  849. {
  850.   static int firstWindow = 1;
  851.   GLUTwindow *window;
  852. #if !defined(WIN32)
  853.   XWMHints *wmHints;
  854. #endif
  855.   Window win;
  856.   XTextProperty textprop;
  857.  
  858.   window = __glutCreateWindow(NULL,
  859.     __glutSizeHints.x, __glutSizeHints.y,
  860.     __glutInitWidth, __glutInitHeight);
  861.   win = window->win;
  862.   /* Setup ICCCM properties. */
  863.   textprop.value = (unsigned char *) title;
  864.   textprop.encoding = XA_STRING;
  865.   textprop.format = 8;
  866.   textprop.nitems = strlen(title);
  867. #if defined(WIN32)
  868.   SetWindowText(win, title);
  869.   if (__glutIconic)
  870.     ShowWindow(win, SW_MINIMIZE);
  871. #else
  872.   wmHints = XAllocWMHints();
  873.   wmHints->initial_state =
  874.     __glutIconic ? IconicState : NormalState;
  875.   wmHints->flags = StateHint;
  876.   XSetWMProperties(__glutDisplay, win, &textprop, &textprop,
  877.   /* Only put WM_COMMAND property on first window. */
  878.     firstWindow ? __glutArgv : NULL,
  879.     firstWindow ? __glutArgc : 0,
  880.     &__glutSizeHints, wmHints, NULL);
  881.   XFree(wmHints);
  882.   XSetWMProtocols(__glutDisplay, win, &__glutWMDeleteWindow, 1);
  883. #endif
  884.   firstWindow = 0;
  885.   return window->num + 1;
  886. }
  887.  
  888. int APIENTRY 
  889. glutCreateSubWindow(int win, int x, int y, int width, int height)
  890. {
  891.   GLUTwindow *window, *toplevel;
  892.  
  893.   window = __glutCreateWindow(__glutWindowList[win - 1],
  894.     x, y, width, height);
  895.   toplevel = __glutToplevelOf(window);
  896.   if (toplevel->cmap != window->cmap) {
  897.     __glutPutOnWorkList(toplevel, GLUT_COLORMAP_WORK);
  898.   }
  899.   return window->num + 1;
  900. }
  901. /* ENDCENTRY */
  902.  
  903. void
  904. __glutDestroyWindow(GLUTwindow * window,
  905.   GLUTwindow * initialWindow)
  906. {
  907.   GLUTwindow **prev, *cur, *parent, *siblings;
  908.  
  909.   /* Recursively destroy any children. */
  910.   cur = window->children;
  911.   while (cur) {
  912.     siblings = cur->siblings;
  913.     __glutDestroyWindow(cur, initialWindow);
  914.     cur = siblings;
  915.   }
  916.   /* Remove from parent's children list (only necessary for
  917.      non-initial windows and subwindows!). */
  918.   parent = window->parent;
  919.   if (parent && parent == initialWindow->parent) {
  920.     prev = &parent->children;
  921.     cur = parent->children;
  922.     while (cur) {
  923.       if (cur == window) {
  924.         *prev = cur->siblings;
  925.         break;
  926.       }
  927.       prev = &(cur->siblings);
  928.       cur = cur->siblings;
  929.     }
  930.   }
  931.   /* Unbind if bound to this window. */
  932.   if (window == __glutCurrentWindow) {
  933.     glXMakeCurrent(__glutDisplay, None, NULL);
  934.     __glutCurrentWindow = NULL;
  935.   }
  936.   /* Begin tearing down window itself. */
  937.   if (window->overlay) {
  938.     __glutFreeOverlayFunc(window->overlay);
  939.   }
  940.   XDestroyWindow(__glutDisplay, window->win);
  941.   glXDestroyContext(__glutDisplay, window->ctx);
  942.   if (window->colormap) {
  943.     /* Only color index windows have colormap data structure. */
  944.     __glutFreeColormap(window->colormap);
  945.   }
  946.   /* NULLing the __glutWindowList helps detect is a window
  947.      instance has been destroyed, given a window number. */
  948.   __glutWindowList[window->num] = NULL;
  949.  
  950.   /* Cleanup data structures that might contain window. */
  951.   cleanWindowWorkList(window);
  952.   cleanStaleWindowList(window);
  953.   /* Remove window from the "get window cache" if it is there. */
  954.   if (__glutWindowCache == window)
  955.     __glutWindowCache = NULL;
  956.  
  957.   if (window->visAlloced) {
  958.     /* Only free XVisualInfo* gotten from glXChooseVisual. */
  959.     XFree(window->vis);
  960.   }
  961.   free(window);
  962. }
  963.  
  964. /* CENTRY */
  965. void APIENTRY 
  966. glutDestroyWindow(int win)
  967. {
  968.   GLUTwindow *window = __glutWindowList[win - 1];
  969.  
  970.   if (__glutMappedMenu && __glutMenuWindow == window) {
  971.     __glutFatalUsage("destroying menu window not allowed while menus in use");
  972.   }
  973.   /* if not a toplevel window... */
  974.   if (window->parent) {
  975.     /* Destroying subwindows may change colormap requirements;
  976.        recalculate toplevel window's WM_COLORMAP_WINDOWS
  977.        property. */
  978.     __glutPutOnWorkList(__glutToplevelOf(window->parent),
  979.       GLUT_COLORMAP_WORK);
  980.   }
  981.   __glutDestroyWindow(window, window);
  982. }
  983.  
  984. void APIENTRY 
  985. glutSwapBuffers(void)
  986. {
  987.   GLUTwindow *window = __glutCurrentWindow;
  988.  
  989.   if (window->renderWin == window->win) {
  990.     if (__glutCurrentWindow->treatAsSingle) {
  991.       /* Pretend the double buffered window is single buffered,
  992.          so treat glutSwapBuffers as a no-op. */
  993.       return;
  994.     }
  995.   } else {
  996.     if (__glutCurrentWindow->overlay->treatAsSingle) {
  997.       /* Pretend the double buffered overlay is single
  998.          buffered, so treat glutSwapBuffers as a no-op. */
  999.       return;
  1000.     }
  1001.   }
  1002.  
  1003.   /* For the MESA_SWAP_HACK. */
  1004.   window->usedSwapBuffers = 1;
  1005.  
  1006.   glXSwapBuffers(__glutDisplay, __glutCurrentWindow->renderWin);
  1007.  
  1008.   /* I considered putting the window being swapped on the
  1009.      GLUT_FINISH_WORK work list because you could call
  1010.      glutSwapBuffers from an idle callback which doesn't call
  1011.      __glutSetWindow which normally adds indirect rendering
  1012.      windows to the GLUT_FINISH_WORK work list.  Not being put
  1013.      on the list could lead to the buffering up of multiple
  1014.      redisplays and buffer swaps and hamper interactivity.  I
  1015.      consider this an application bug due to not using
  1016.      glutPostRedisplay to trigger redraws.  If
  1017.      glutPostRedisplay were used, __glutSetWindow would be
  1018.      called and a glFinish to throttle buffering would occur. */
  1019. }
  1020. /* ENDCENTRY */
  1021.  
  1022. void
  1023. __glutChangeWindowEventMask(long eventMask, Bool add)
  1024. {
  1025.   if (add) {
  1026.     /* Add eventMask to window's event mask. */
  1027.     if ((__glutCurrentWindow->eventMask & eventMask) !=
  1028.       eventMask) {
  1029.       __glutCurrentWindow->eventMask |= eventMask;
  1030.       __glutPutOnWorkList(__glutCurrentWindow,
  1031.         GLUT_EVENT_MASK_WORK);
  1032.     }
  1033.   } else {
  1034.     /* Remove eventMask from window's event mask. */
  1035.     if (__glutCurrentWindow->eventMask & eventMask) {
  1036.       __glutCurrentWindow->eventMask &= ~eventMask;
  1037.       __glutPutOnWorkList(__glutCurrentWindow,
  1038.         GLUT_EVENT_MASK_WORK);
  1039.     }
  1040.   }
  1041. }
  1042.  
  1043. void APIENTRY 
  1044. glutDisplayFunc(GLUTdisplayCB displayFunc)
  1045. {
  1046.   /* XXX Remove the warning after GLUT 3.0. */
  1047.   if (!displayFunc)
  1048.     __glutFatalError("NULL display callback not allowed in GLUT 3.0; update your code.");
  1049.   __glutCurrentWindow->display = displayFunc;
  1050. }
  1051.  
  1052. void APIENTRY 
  1053. glutKeyboardFunc(GLUTkeyboardCB keyboardFunc)
  1054. {
  1055.   __glutChangeWindowEventMask(KeyPressMask,
  1056.     keyboardFunc != NULL || __glutCurrentWindow->special != NULL);
  1057.   __glutCurrentWindow->keyboard = keyboardFunc;
  1058. }
  1059.  
  1060. void APIENTRY 
  1061. glutSpecialFunc(GLUTspecialCB specialFunc)
  1062. {
  1063.   __glutChangeWindowEventMask(KeyPressMask,
  1064.     specialFunc != NULL || __glutCurrentWindow->keyboard != NULL);
  1065.   __glutCurrentWindow->special = specialFunc;
  1066. }
  1067.  
  1068. void APIENTRY 
  1069. glutMouseFunc(GLUTmouseCB mouseFunc)
  1070. {
  1071.   if (__glutCurrentWindow->mouse) {
  1072.     if (!mouseFunc) {
  1073.       /* Previous mouseFunc being disabled. */
  1074.       __glutCurrentWindow->buttonUses--;
  1075.       __glutChangeWindowEventMask(
  1076.         ButtonPressMask | ButtonReleaseMask,
  1077.         __glutCurrentWindow->buttonUses > 0);
  1078.     }
  1079.   } else {
  1080.     if (mouseFunc) {
  1081.       /* Previously no mouseFunc, new one being installed. */
  1082.       __glutCurrentWindow->buttonUses++;
  1083.       __glutChangeWindowEventMask(
  1084.         ButtonPressMask | ButtonReleaseMask, True);
  1085.     }
  1086.   }
  1087.   __glutCurrentWindow->mouse = mouseFunc;
  1088. }
  1089.  
  1090. void APIENTRY 
  1091. glutMotionFunc(GLUTmotionCB motionFunc)
  1092. {
  1093.   /* Hack.  Some window managers (4Dwm by default) will mask
  1094.      motion events if the client is not selecting for button
  1095.      press and release events. So we select for press and
  1096.      release events too (being careful to use reference
  1097.      counting).  */
  1098.   if (__glutCurrentWindow->motion) {
  1099.     if (!motionFunc) {
  1100.       /* previous mouseFunc being disabled */
  1101.       __glutCurrentWindow->buttonUses--;
  1102.       __glutChangeWindowEventMask(
  1103.         ButtonPressMask | ButtonReleaseMask,
  1104.         __glutCurrentWindow->buttonUses > 0);
  1105.     }
  1106.   } else {
  1107.     if (motionFunc) {
  1108.       /* Previously no mouseFunc, new one being installed. */
  1109.       __glutCurrentWindow->buttonUses++;
  1110.       __glutChangeWindowEventMask(
  1111.         ButtonPressMask | ButtonReleaseMask, True);
  1112.     }
  1113.   }
  1114.   /* Real work of selecting for passive mouse motion.  */
  1115.   __glutChangeWindowEventMask(
  1116.     Button1MotionMask | Button2MotionMask | Button3MotionMask,
  1117.     motionFunc != NULL);
  1118.   __glutCurrentWindow->motion = motionFunc;
  1119. }
  1120.  
  1121. void APIENTRY 
  1122. glutPassiveMotionFunc(GLUTpassiveCB passiveMotionFunc)
  1123. {
  1124.   __glutChangeWindowEventMask(PointerMotionMask,
  1125.     passiveMotionFunc != NULL);
  1126.  
  1127.   /* Passive motion also requires watching enters and leaves so
  1128.      that a fake passive motion event can be generated on an
  1129.      enter. */
  1130.   __glutChangeWindowEventMask(EnterWindowMask | LeaveWindowMask,
  1131.     __glutCurrentWindow->entry != NULL || passiveMotionFunc != NULL);
  1132.  
  1133.   __glutCurrentWindow->passive = passiveMotionFunc;
  1134. }
  1135.  
  1136. void APIENTRY 
  1137. glutEntryFunc(GLUTentryCB entryFunc)
  1138. {
  1139.   __glutChangeWindowEventMask(EnterWindowMask | LeaveWindowMask,
  1140.     entryFunc != NULL || __glutCurrentWindow->passive);
  1141.   __glutCurrentWindow->entry = entryFunc;
  1142.   if (!entryFunc) {
  1143.     __glutCurrentWindow->entryState = -1;
  1144.   }
  1145. }
  1146.  
  1147. void APIENTRY 
  1148. glutWindowStatusFunc(GLUTwindowStatusCB windowStatusFunc)
  1149. {
  1150.   __glutChangeWindowEventMask(VisibilityChangeMask,
  1151.     windowStatusFunc != NULL);
  1152.   __glutCurrentWindow->windowStatus = windowStatusFunc;
  1153.   if (!windowStatusFunc) {
  1154.     /* Make state invalid. */
  1155.     __glutCurrentWindow->visState = -1;
  1156.   }
  1157. }
  1158.  
  1159. static void
  1160. visibilityHelper(int status)
  1161. {
  1162.   if (status == GLUT_HIDDEN || status == GLUT_FULLY_COVERED)
  1163.     __glutCurrentWindow->visibility(GLUT_NOT_VISIBLE);
  1164.   else
  1165.     __glutCurrentWindow->visibility(GLUT_VISIBLE);
  1166. }
  1167.  
  1168. void APIENTRY 
  1169. glutVisibilityFunc(GLUTvisibilityCB visibilityFunc)
  1170. {
  1171.   __glutCurrentWindow->visibility = visibilityFunc;
  1172.   if (visibilityFunc)
  1173.     glutWindowStatusFunc(visibilityHelper);
  1174.   else
  1175.     glutWindowStatusFunc(NULL);
  1176. }
  1177.  
  1178. void APIENTRY 
  1179. glutReshapeFunc(GLUTreshapeCB reshapeFunc)
  1180. {
  1181.   if (reshapeFunc) {
  1182.     __glutCurrentWindow->reshape = reshapeFunc;
  1183.   } else {
  1184.     __glutCurrentWindow->reshape = __glutDefaultReshape;
  1185.   }
  1186. }
  1187.