glut_win.c revision c041511d
1
2/* Copyright (c) Mark J. Kilgard, 1994, 1997.  */
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 <stdlib.h>
13#include <stdio.h>
14#include <string.h>
15#include <assert.h>
16#if !defined(_WIN32)
17#include <X11/Xlib.h>
18#include <X11/Xatom.h>
19#endif
20
21#include "glutint.h"
22
23GLUTwindow *__glutCurrentWindow = NULL;
24GLUTwindow **__glutWindowList = NULL;
25int __glutWindowListSize = 0;
26#if !defined(_WIN32)
27GLUTstale *__glutStaleWindowList = NULL;
28#endif
29GLUTwindow *__glutMenuWindow = NULL;
30
31void (*__glutFreeOverlayFunc) (GLUToverlay *);
32XVisualInfo *(*__glutDetermineVisualFromString) (char *string, Bool * treatAsSingle,
33  Criterion * requiredCriteria, int nRequired, int requiredMask, void **fbc) = NULL;
34
35static Criterion requiredWindowCriteria[] =
36{
37  {LEVEL, EQ, 0},
38  {TRANSPARENT, EQ, 0}
39};
40static int numRequiredWindowCriteria = sizeof(requiredWindowCriteria) / sizeof(Criterion);
41static int requiredWindowCriteriaMask = (1 << LEVEL) | (1 << TRANSPARENT);
42
43static void
44cleanWindowWorkList(GLUTwindow * window)
45{
46  GLUTwindow **pEntry = &__glutWindowWorkList;
47  GLUTwindow *entry = __glutWindowWorkList;
48
49  /* Tranverse singly-linked window work list look for the
50     window. */
51  while (entry) {
52    if (entry == window) {
53      /* Found it; delete it. */
54      *pEntry = entry->prevWorkWin;
55      return;
56    } else {
57      pEntry = &entry->prevWorkWin;
58      entry = *pEntry;
59    }
60  }
61}
62
63#if !defined(_WIN32)
64
65static void
66cleanStaleWindowList(GLUTwindow * window)
67{
68  GLUTstale **pEntry = &__glutStaleWindowList;
69  GLUTstale *entry = __glutStaleWindowList;
70
71  /* Tranverse singly-linked stale window list look for the
72     window ID. */
73  while (entry) {
74    if (entry->window == window) {
75      /* Found it; delete it. */
76      *pEntry = entry->next;
77      free(entry);
78      return;
79    } else {
80      pEntry = &entry->next;
81      entry = *pEntry;
82    }
83  }
84}
85
86#endif
87
88static GLUTwindow *__glutWindowCache = NULL;
89
90GLUTwindow *
91__glutGetWindow(Window win)
92{
93  int i;
94
95  /* Does win belong to the last window ID looked up? */
96  if (__glutWindowCache && (win == __glutWindowCache->win ||
97      (__glutWindowCache->overlay && win ==
98        __glutWindowCache->overlay->win))) {
99    return
100      __glutWindowCache;
101  }
102  /* Otherwise scan the window list looking for the window ID. */
103  for (i = 0; i < __glutWindowListSize; i++) {
104    if (__glutWindowList[i]) {
105      if (win == __glutWindowList[i]->win) {
106        __glutWindowCache = __glutWindowList[i];
107        return __glutWindowCache;
108      }
109      if (__glutWindowList[i]->overlay) {
110        if (win == __glutWindowList[i]->overlay->win) {
111          __glutWindowCache = __glutWindowList[i];
112          return __glutWindowCache;
113        }
114      }
115    }
116  }
117#if !defined(_WIN32)
118  {
119    GLUTstale *entry;
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  }
128#endif
129  return NULL;
130}
131
132/* CENTRY */
133int GLUTAPIENTRY
134glutGetWindow(void)
135{
136  if (__glutCurrentWindow) {
137    return __glutCurrentWindow->num + 1;
138  } else {
139    return 0;
140  }
141}
142/* ENDCENTRY */
143
144void
145__glutSetWindow(GLUTwindow * window)
146{
147  /* It is tempting to try to short-circuit the call to
148     glXMakeCurrent if we "know" we are going to make current
149     to a window we are already current to.  In fact, this
150     assumption breaks when GLUT is expected to integrated with
151     other OpenGL windowing APIs that also make current to
152     OpenGL contexts.  Since glXMakeCurrent short-circuits the
153     "already bound" case, GLUT avoids the temptation to do so
154     too. */
155  __glutCurrentWindow = window;
156
157  MAKE_CURRENT_LAYER(__glutCurrentWindow);
158
159#if !defined(_WIN32)
160  /* We should be careful to force a finish between each
161     iteration through the GLUT main loop if indirect OpenGL
162     contexts are in use; indirect contexts tend to have  much
163     longer latency because lots of OpenGL extension requests
164     can queue up in the X protocol stream.  We accomplish this
165     by posting GLUT_FINISH_WORK to be done. */
166  if (!__glutCurrentWindow->isDirect)
167    __glutPutOnWorkList(__glutCurrentWindow, GLUT_FINISH_WORK);
168#endif
169
170  /* If debugging is enabled, we'll want to check this window
171     for any OpenGL errors every iteration through the GLUT
172     main loop.  To accomplish this, we post the
173     GLUT_DEBUG_WORK to be done on this window. */
174  if (__glutDebug) {
175    __glutPutOnWorkList(__glutCurrentWindow, GLUT_DEBUG_WORK);
176  }
177}
178
179/* CENTRY */
180void GLUTAPIENTRY
181glutSetWindow(int win)
182{
183  GLUTwindow *window;
184
185  if (win < 1 || win > __glutWindowListSize) {
186    __glutWarning("glutSetWindow attempted on bogus window.");
187    return;
188  }
189  window = __glutWindowList[win - 1];
190  if (!window) {
191    __glutWarning("glutSetWindow attempted on bogus window.");
192    return;
193  }
194  __glutSetWindow(window);
195}
196/* ENDCENTRY */
197
198static int
199getUnusedWindowSlot(void)
200{
201  int i;
202
203  /* Look for allocated, unused slot. */
204  for (i = 0; i < __glutWindowListSize; i++) {
205    if (!__glutWindowList[i]) {
206      return i;
207    }
208  }
209  /* Allocate a new slot. */
210  __glutWindowListSize++;
211  if (__glutWindowList) {
212    __glutWindowList = (GLUTwindow **)
213      realloc(__glutWindowList,
214      __glutWindowListSize * sizeof(GLUTwindow *));
215  } else {
216    /* XXX Some realloc's do not correctly perform a malloc
217       when asked to perform a realloc on a NULL pointer,
218       though the ANSI C library spec requires this. */
219    __glutWindowList = (GLUTwindow **)
220      malloc(sizeof(GLUTwindow *));
221  }
222  if (!__glutWindowList)
223    __glutFatalError("out of memory.");
224  __glutWindowList[__glutWindowListSize - 1] = NULL;
225  return __glutWindowListSize - 1;
226}
227
228static XVisualInfo *
229getVisualInfoCI(unsigned int mode)
230{
231  static int bufSizeList[] =
232  {16, 12, 8, 4, 2, 1, 0};
233  XVisualInfo *vi;
234  int list[32];
235  int i, n = 0;
236
237  /* Should not be looking at display mode mask if
238     __glutDisplayString is non-NULL. */
239  assert(!__glutDisplayString);
240
241  list[n++] = GLX_BUFFER_SIZE;
242  list[n++] = 1;
243  if (GLUT_WIND_IS_DOUBLE(mode)) {
244    list[n++] = GLX_DOUBLEBUFFER;
245  }
246  if (GLUT_WIND_IS_STEREO(mode)) {
247    list[n++] = GLX_STEREO;
248  }
249  if (GLUT_WIND_HAS_DEPTH(mode)) {
250    list[n++] = GLX_DEPTH_SIZE;
251    list[n++] = 1;
252  }
253  if (GLUT_WIND_HAS_STENCIL(mode)) {
254    list[n++] = GLX_STENCIL_SIZE;
255    list[n++] = 1;
256  }
257  list[n] = (int) None; /* terminate list */
258
259  /* glXChooseVisual specify GLX_BUFFER_SIZE prefers the
260     "smallest index buffer of at least the specified size".
261     This would be reasonable if GLUT allowed the user to
262     specify the required buffe size, but GLUT's display mode
263     is too simplistic (easy to use?). GLUT should try to find
264     the "largest".  So start with a large buffer size and
265     shrink until we find a matching one that exists. */
266
267  for (i = 0; bufSizeList[i]; i++) {
268    /* XXX Assumes list[1] is where GLX_BUFFER_SIZE parameter
269       is. */
270    list[1] = bufSizeList[i];
271    vi = glXChooseVisual(__glutDisplay,
272      __glutScreen, list);
273    if (vi)
274      return vi;
275  }
276  return NULL;
277}
278
279static XVisualInfo *
280getVisualInfoRGB(unsigned int mode)
281{
282  int list[32];
283  int n = 0;
284
285  /* Should not be looking at display mode mask if
286     __glutDisplayString is non-NULL. */
287  assert(!__glutDisplayString);
288
289  /* XXX Would a caching mechanism to minize the calls to
290     glXChooseVisual? You'd have to reference count
291     XVisualInfo* pointers.  Would also have to properly
292     interact with glutInitDisplayString. */
293
294  list[n++] = GLX_RGBA;
295  list[n++] = GLX_RED_SIZE;
296  list[n++] = 1;
297  list[n++] = GLX_GREEN_SIZE;
298  list[n++] = 1;
299  list[n++] = GLX_BLUE_SIZE;
300  list[n++] = 1;
301  if (GLUT_WIND_HAS_ALPHA(mode)) {
302    list[n++] = GLX_ALPHA_SIZE;
303    list[n++] = 1;
304  }
305  if (GLUT_WIND_IS_DOUBLE(mode)) {
306    list[n++] = GLX_DOUBLEBUFFER;
307  }
308  if (GLUT_WIND_IS_STEREO(mode)) {
309    list[n++] = GLX_STEREO;
310  }
311  if (GLUT_WIND_HAS_DEPTH(mode)) {
312    list[n++] = GLX_DEPTH_SIZE;
313    list[n++] = 1;
314  }
315  if (GLUT_WIND_HAS_STENCIL(mode)) {
316    list[n++] = GLX_STENCIL_SIZE;
317    list[n++] = 1;
318  }
319  if (GLUT_WIND_HAS_ACCUM(mode)) {
320    list[n++] = GLX_ACCUM_RED_SIZE;
321    list[n++] = 1;
322    list[n++] = GLX_ACCUM_GREEN_SIZE;
323    list[n++] = 1;
324    list[n++] = GLX_ACCUM_BLUE_SIZE;
325    list[n++] = 1;
326    if (GLUT_WIND_HAS_ALPHA(mode)) {
327      list[n++] = GLX_ACCUM_ALPHA_SIZE;
328      list[n++] = 1;
329    }
330  }
331#if defined(GLX_VERSION_1_1) && (defined(GLX_SGIS_multisample) || defined(GLX_ARB_multisample))
332  if (GLUT_WIND_IS_MULTISAMPLE(mode)) {
333    if (!__glutIsSupportedByGLX("GLX_SGIS_multisample") &&
334        !__glutIsSupportedByGLX("GLX_ARB_multisample"))
335      return NULL;
336#if defined(GLX_ARB_multisample)
337    list[n++] = GLX_SAMPLES_ARB;
338#elif defined(GLX_SGIS_multisample)
339    list[n++] = GLX_SAMPLES_SGIS;
340#endif
341    /* XXX Is 4 a reasonable minimum acceptable number of
342       samples? */
343    list[n++] = 4;
344  }
345#endif
346  list[n] = (int) None; /* terminate list */
347
348  return glXChooseVisual(__glutDisplay,
349    __glutScreen, list);
350}
351
352#ifndef VisualIDMask
353#define VisualIDMask 0
354#endif
355
356static XVisualInfo *
357getVisualInfoID(int id)
358{
359   XVisualInfo temp;
360   int count;
361#if !defined(_WIN32)
362   temp.visualid = id;
363#endif
364   return XGetVisualInfo(__glutDisplay, VisualIDMask, &temp, &count);
365}
366
367
368XVisualInfo *
369__glutGetVisualInfo(unsigned int mode)
370{
371  char *visStr;
372  /* XXX GLUT_LUMINANCE not implemented for GLUT 3.0. */
373  if (GLUT_WIND_IS_LUMINANCE(mode))
374    return NULL;
375
376  visStr = getenv("GLUT_FORCE_VISUAL");
377  if (visStr) {
378     int id = atoi(visStr);
379     return getVisualInfoID(id);
380  }
381
382  if (GLUT_WIND_IS_RGB(mode))
383    return getVisualInfoRGB(mode);
384  else
385    return getVisualInfoCI(mode);
386}
387
388XVisualInfo *
389__glutDetermineVisual(
390  unsigned int displayMode,
391  Bool * treatAsSingle,
392  XVisualInfo * (getVisualInfo) (unsigned int))
393{
394  XVisualInfo *vis;
395
396  /* Should not be looking at display mode mask if
397     __glutDisplayString is non-NULL. */
398  assert(!__glutDisplayString);
399
400  *treatAsSingle = GLUT_WIND_IS_SINGLE(displayMode);
401  vis = getVisualInfo(displayMode);
402  if (!vis) {
403    /* Fallback cases when can't get exactly what was asked
404       for... */
405    if (GLUT_WIND_IS_SINGLE(displayMode)) {
406      /* If we can't find a single buffered visual, try looking
407         for a double buffered visual.  We can treat a double
408         buffered visual as a single buffer visual by changing
409         the draw buffer to GL_FRONT and treating any swap
410         buffers as no-ops. */
411      displayMode |= GLUT_DOUBLE;
412      vis = getVisualInfo(displayMode);
413      *treatAsSingle = True;
414    }
415    if (!vis && GLUT_WIND_IS_MULTISAMPLE(displayMode)) {
416      /* If we can't seem to get multisampling (ie, not Reality
417         Engine class graphics!), go without multisampling.  It
418         is up to the application to query how many multisamples
419         were allocated (0 equals no multisampling) if the
420         application is going to use multisampling for more than
421         just antialiasing. */
422      displayMode &= ~GLUT_MULTISAMPLE;
423      vis = getVisualInfo(displayMode);
424    }
425  }
426  return vis;
427}
428
429static void GLUTCALLBACK
430__glutDefaultDisplay(void)
431{
432  /* XXX Remove the warning after GLUT 3.0. */
433  __glutWarning("The following is a new check for GLUT 3.0; update your code.");
434  __glutFatalError(
435    "redisplay needed for window %d, but no display callback.",
436    __glutCurrentWindow->num + 1);
437}
438
439void GLUTCALLBACK
440__glutDefaultReshape(int width, int height)
441{
442  GLUToverlay *overlay;
443
444  /* Adjust the viewport of the window (and overlay if one
445     exists). */
446  MAKE_CURRENT_WINDOW(__glutCurrentWindow);
447  glViewport(0, 0, (GLsizei) width, (GLsizei) height);
448  overlay = __glutCurrentWindow->overlay;
449  if (overlay) {
450    MAKE_CURRENT_OVERLAY(overlay);
451    glViewport(0, 0, (GLsizei) width, (GLsizei) height);
452  }
453  /* Make sure we are current to the current layer (application
454     should be able to count on the current layer not changing
455     unless the application explicitly calls glutUseLayer). */
456  MAKE_CURRENT_LAYER(__glutCurrentWindow);
457}
458
459XVisualInfo *
460__glutDetermineWindowVisual(Bool * treatAsSingle, Bool * visAlloced, void **fbc)
461{
462  if (__glutDisplayString) {
463
464    /* __glutDisplayString should be NULL except if
465       glutInitDisplayString has been called to register a
466       different display string.  Calling glutInitDisplayString
467       means using a string instead of an integer mask determine
468       the visual to use. Using the function pointer variable
469       __glutDetermineVisualFromString below avoids linking in
470       the code for implementing glutInitDisplayString (ie,
471       glut_dstr.o) unless glutInitDisplayString gets called by
472       the application. */
473
474    assert(__glutDetermineVisualFromString);
475    *visAlloced = False;
476    *fbc = NULL;
477    return __glutDetermineVisualFromString(__glutDisplayString, treatAsSingle,
478      requiredWindowCriteria, numRequiredWindowCriteria, requiredWindowCriteriaMask, fbc);
479  } else {
480    *visAlloced = True;
481    *fbc = NULL;
482    return __glutDetermineVisual(__glutDisplayMode,
483      treatAsSingle, __glutGetVisualInfo);
484  }
485}
486
487/* ARGSUSED5 */  /* Only Win32 uses gameMode parameter. */
488GLUTwindow *
489__glutCreateWindow(GLUTwindow * parent,
490  int x, int y, int width, int height, int gameMode)
491{
492  GLUTwindow *window;
493  XSetWindowAttributes wa;
494  unsigned long attribMask;
495  int winnum;
496  int i;
497  void *fbc;
498
499#if defined(_WIN32)
500  WNDCLASS wc;
501  int style;
502
503  if (!GetClassInfo(GetModuleHandle(NULL), "GLUT", &wc)) {
504    __glutOpenWin32Connection(NULL);
505  }
506#else
507  if (!__glutDisplay) {
508    __glutOpenXConnection(NULL);
509  }
510#endif
511  if (__glutGameModeWindow) {
512    __glutFatalError("cannot create windows in game mode.");
513  }
514  winnum = getUnusedWindowSlot();
515  window = (GLUTwindow *) malloc(sizeof(GLUTwindow));
516  if (!window) {
517    __glutFatalError("out of memory.");
518  }
519  window->num = winnum;
520
521#if !defined(_WIN32)
522  window->vis = __glutDetermineWindowVisual(&window->treatAsSingle,
523    &window->visAlloced, &fbc);
524  if (!window->vis) {
525    __glutFatalError(
526      "visual with necessary capabilities not found.");
527  }
528  __glutSetupColormap(window->vis, &window->colormap, &window->cmap);
529#endif
530  window->eventMask = StructureNotifyMask | ExposureMask;
531
532  attribMask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask;
533  wa.background_pixmap = None;
534  wa.border_pixel = 0;
535  wa.colormap = window->cmap;
536  wa.event_mask = window->eventMask;
537  if (parent) {
538    if (parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK)
539      wa.event_mask |= GLUT_HACK_STOP_PROPAGATE_MASK;
540    attribMask |= CWDontPropagate;
541    wa.do_not_propagate_mask = parent->eventMask & GLUT_DONT_PROPAGATE_FILTER_MASK;
542  } else {
543    wa.do_not_propagate_mask = 0;
544  }
545
546  /* Stash width and height before Win32's __glutAdjustCoords
547     possibly overwrites the values. */
548  window->width = width;
549  window->height = height;
550  window->forceReshape = True;
551  window->ignoreKeyRepeat = False;
552
553#if defined(_WIN32)
554  __glutAdjustCoords(parent ? parent->win : NULL,
555    &x, &y, &width, &height);
556  if (parent) {
557    style = WS_CHILD;
558  } else {
559    if (gameMode) {
560      /* Game mode window should be a WS_POPUP window to
561         ensure that the taskbar is hidden by it.  A standard
562         WS_OVERLAPPEDWINDOW does not hide the task bar. */
563      style = WS_POPUP | WS_MAXIMIZE;
564    } else {
565      /* A standard toplevel window with borders and such. */
566      style = WS_OVERLAPPEDWINDOW;
567    }
568  }
569  window->win = CreateWindow("GLUT", "GLUT",
570    WS_CLIPSIBLINGS | WS_CLIPCHILDREN | style,
571    x, y, width, height, parent ? parent->win : __glutRoot,
572    NULL, GetModuleHandle(NULL), 0);
573  window->hdc = GetDC(window->win);
574  /* Must set the XHDC for fake glXChooseVisual & fake
575     glXCreateContext & fake XAllocColorCells. */
576  XHDC = window->hdc;
577  window->vis = __glutDetermineWindowVisual(&window->treatAsSingle,
578    &window->visAlloced, &fbc);
579  if (!window->vis) {
580    __glutFatalError(
581      "pixel format with necessary capabilities not found.");
582  }
583  if (!SetPixelFormat(window->hdc,
584      ChoosePixelFormat(window->hdc, window->vis),
585      window->vis)) {
586    __glutFatalError("SetPixelFormat failed during window create.");
587  }
588  __glutSetupColormap(window->vis, &window->colormap, &window->cmap);
589  /* Make sure subwindows get a windowStatus callback. */
590  if (parent) {
591    PostMessage(parent->win, WM_ACTIVATE, 0, 0);
592  }
593  window->renderDc = window->hdc;
594#else
595  window->win = XCreateWindow(__glutDisplay,
596    parent == NULL ? __glutRoot : parent->win,
597    x, y, width, height, 0,
598    window->vis->depth, InputOutput, window->vis->visual,
599    attribMask, &wa);
600#endif
601  window->renderWin = window->win;
602#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_fbconfig)
603  if (fbc) {
604    window->ctx = __glut_glXCreateContextWithConfigSGIX(__glutDisplay, fbc,
605      GLX_RGBA_TYPE_SGIX, None, __glutTryDirect);
606  } else
607#endif
608  {
609    window->ctx = glXCreateContext(__glutDisplay, window->vis,
610      None, __glutTryDirect);
611  }
612  if (!window->ctx) {
613    __glutFatalError(
614      "failed to create OpenGL rendering context.");
615  }
616  window->renderCtx = window->ctx;
617#if !defined(_WIN32)
618  window->isDirect = glXIsDirect(__glutDisplay, window->ctx);
619  if (__glutForceDirect) {
620    if (!window->isDirect)
621      __glutFatalError("direct rendering not possible.");
622  }
623#endif
624
625  window->parent = parent;
626  if (parent) {
627    window->siblings = parent->children;
628    parent->children = window;
629  } else {
630    window->siblings = NULL;
631  }
632  window->overlay = NULL;
633  window->children = NULL;
634  window->display = __glutDefaultDisplay;
635  window->reshape = __glutDefaultReshape;
636  window->mouse = NULL;
637  window->motion = NULL;
638  window->passive = NULL;
639  window->entry = NULL;
640  window->keyboard = NULL;
641  window->keyboardUp = NULL;
642  window->windowStatus = NULL;
643  window->visibility = NULL;
644  window->special = NULL;
645  window->specialUp = NULL;
646  window->buttonBox = NULL;
647  window->dials = NULL;
648  window->spaceMotion = NULL;
649  window->spaceRotate = NULL;
650  window->spaceButton = NULL;
651  window->tabletMotion = NULL;
652  window->tabletButton = NULL;
653#ifdef _WIN32
654  window->joystick = NULL;
655  window->joyPollInterval = 0;
656#endif
657  window->tabletPos[0] = -1;
658  window->tabletPos[1] = -1;
659  window->shownState = 0;
660  window->visState = -1;  /* not VisibilityUnobscured,
661                             VisibilityPartiallyObscured, or
662                             VisibilityFullyObscured */
663  window->entryState = -1;  /* not EnterNotify or LeaveNotify */
664
665  window->desiredConfMask = 0;
666  window->buttonUses = 0;
667  window->cursor = GLUT_CURSOR_INHERIT;
668
669  /* Setup window to be mapped when glutMainLoop starts. */
670  window->workMask = GLUT_MAP_WORK;
671#ifdef _WIN32
672  if (gameMode) {
673    /* When mapping a game mode window, just show
674       the window.  We have already created the game
675       mode window with a maximize flag at creation
676       time.  Doing a ShowWindow(window->win, SW_SHOWNORMAL)
677       would be wrong for a game mode window since it
678       would unmaximize the window. */
679    window->desiredMapState = GameModeState;
680  } else {
681    window->desiredMapState = NormalState;
682  }
683#else
684  window->desiredMapState = NormalState;
685#endif
686  window->prevWorkWin = __glutWindowWorkList;
687  __glutWindowWorkList = window;
688
689  /* Initially, no menus attached. */
690  for (i = 0; i < GLUT_MAX_MENUS; i++) {
691    window->menu[i] = 0;
692  }
693
694  /* Add this new window to the window list. */
695  __glutWindowList[winnum] = window;
696
697  /* Make the new window the current window. */
698  __glutSetWindow(window);
699
700  __glutDetermineMesaSwapHackSupport();
701
702  if (window->treatAsSingle) {
703    /* We do this because either the window really is single
704       buffered (in which case this is redundant, but harmless,
705       because this is the initial single-buffered context
706       state); or we are treating a double buffered window as a
707       single-buffered window because the system does not appear
708       to export any suitable single- buffered visuals (in which
709       the following are necessary). */
710    glDrawBuffer(GL_FRONT);
711    glReadBuffer(GL_FRONT);
712  }
713  return window;
714}
715
716/* CENTRY */
717int GLUTAPIENTRY
718glutCreateWindow(const char *title)
719{
720  static int firstWindow = 1;
721  GLUTwindow *window;
722#if !defined(_WIN32)
723  XWMHints *wmHints;
724#endif
725  Window win;
726  XTextProperty textprop;
727
728  if (__glutGameModeWindow) {
729    __glutFatalError("cannot create windows in game mode.");
730  }
731  window = __glutCreateWindow(NULL,
732    __glutSizeHints.x, __glutSizeHints.y,
733    __glutInitWidth, __glutInitHeight,
734    /* not game mode */ 0);
735  win = window->win;
736  /* Setup ICCCM properties. */
737  textprop.value = (unsigned char *) title;
738  textprop.encoding = XA_STRING;
739  textprop.format = 8;
740  textprop.nitems = strlen(title);
741#if defined(_WIN32)
742  SetWindowText(win, title);
743  if (__glutIconic) {
744    window->desiredMapState = IconicState;
745  }
746#else
747  wmHints = XAllocWMHints();
748  wmHints->initial_state =
749    __glutIconic ? IconicState : NormalState;
750  wmHints->flags = StateHint;
751  XSetWMProperties(__glutDisplay, win, &textprop, &textprop,
752  /* Only put WM_COMMAND property on first window. */
753    firstWindow ? __glutArgv : NULL,
754    firstWindow ? __glutArgc : 0,
755    &__glutSizeHints, wmHints, NULL);
756  XFree(wmHints);
757  XSetWMProtocols(__glutDisplay, win, &__glutWMDeleteWindow, 1);
758#endif
759  firstWindow = 0;
760  return window->num + 1;
761}
762
763#ifdef _WIN32
764int GLUTAPIENTRY
765__glutCreateWindowWithExit(const char *title, void (__cdecl *exitfunc)(int))
766{
767  __glutExitFunc = exitfunc;
768  return glutCreateWindow(title);
769}
770#endif
771
772int GLUTAPIENTRY
773glutCreateSubWindow(int win, int x, int y, int width, int height)
774{
775  GLUTwindow *window;
776
777  window = __glutCreateWindow(__glutWindowList[win - 1],
778    x, y, width, height, /* not game mode */ 0);
779#if !defined(_WIN32)
780  {
781    GLUTwindow *toplevel;
782
783    toplevel = __glutToplevelOf(window);
784    if (toplevel->cmap != window->cmap) {
785      __glutPutOnWorkList(toplevel, GLUT_COLORMAP_WORK);
786    }
787  }
788#endif
789  return window->num + 1;
790}
791/* ENDCENTRY */
792
793void
794__glutDestroyWindow(GLUTwindow * window,
795  GLUTwindow * initialWindow)
796{
797  GLUTwindow **prev, *cur, *parent, *siblings;
798
799  /* Recursively destroy any children. */
800  cur = window->children;
801  while (cur) {
802    siblings = cur->siblings;
803    __glutDestroyWindow(cur, initialWindow);
804    cur = siblings;
805  }
806  /* Remove from parent's children list (only necessary for
807     non-initial windows and subwindows!). */
808  parent = window->parent;
809  if (parent && parent == initialWindow->parent) {
810    prev = &parent->children;
811    cur = parent->children;
812    while (cur) {
813      if (cur == window) {
814        *prev = cur->siblings;
815        break;
816      }
817      prev = &(cur->siblings);
818      cur = cur->siblings;
819    }
820  }
821  /* Unbind if bound to this window. */
822  if (window == __glutCurrentWindow) {
823    UNMAKE_CURRENT();
824    __glutCurrentWindow = NULL;
825  }
826  /* Begin tearing down window itself. */
827  if (window->overlay) {
828    __glutFreeOverlayFunc(window->overlay);
829  }
830  XDestroyWindow(__glutDisplay, window->win);
831  glXDestroyContext(__glutDisplay, window->ctx);
832  if (window->colormap) {
833    /* Only color index windows have colormap data structure. */
834    __glutFreeColormap(window->colormap);
835  }
836  /* NULLing the __glutWindowList helps detect is a window
837     instance has been destroyed, given a window number. */
838  __glutWindowList[window->num] = NULL;
839
840  /* Cleanup data structures that might contain window. */
841  cleanWindowWorkList(window);
842#if !defined(_WIN32)
843  cleanStaleWindowList(window);
844#endif
845  /* Remove window from the "get window cache" if it is there. */
846  if (__glutWindowCache == window)
847    __glutWindowCache = NULL;
848
849  if (window->visAlloced) {
850    /* Only free XVisualInfo* gotten from glXChooseVisual. */
851    XFree(window->vis);
852  }
853
854  if (window == __glutGameModeWindow) {
855    /* Destroying the game mode window should implicitly
856       have GLUT leave game mode. */
857    __glutCloseDownGameMode();
858  }
859
860  free(window);
861}
862
863/* CENTRY */
864void GLUTAPIENTRY
865glutDestroyWindow(int win)
866{
867  GLUTwindow *window = __glutWindowList[win - 1];
868
869  if (__glutMappedMenu && __glutMenuWindow == window) {
870    __glutFatalUsage("destroying menu window not allowed while menus in use");
871  }
872#if !defined(_WIN32)
873  /* If not a toplevel window... */
874  if (window->parent) {
875    /* Destroying subwindows may change colormap requirements;
876       recalculate toplevel window's WM_COLORMAP_WINDOWS
877       property. */
878    __glutPutOnWorkList(__glutToplevelOf(window->parent),
879      GLUT_COLORMAP_WORK);
880  }
881#endif
882  __glutDestroyWindow(window, window);
883  XFlush(__glutDisplay);
884}
885/* ENDCENTRY */
886
887void
888__glutChangeWindowEventMask(long eventMask, Bool add)
889{
890  if (add) {
891    /* Add eventMask to window's event mask. */
892    if ((__glutCurrentWindow->eventMask & eventMask) !=
893      eventMask) {
894      __glutCurrentWindow->eventMask |= eventMask;
895      __glutPutOnWorkList(__glutCurrentWindow,
896        GLUT_EVENT_MASK_WORK);
897    }
898  } else {
899    /* Remove eventMask from window's event mask. */
900    if (__glutCurrentWindow->eventMask & eventMask) {
901      __glutCurrentWindow->eventMask &= ~eventMask;
902      __glutPutOnWorkList(__glutCurrentWindow,
903        GLUT_EVENT_MASK_WORK);
904    }
905  }
906}
907
908void GLUTAPIENTRY
909glutDisplayFunc(GLUTdisplayCB displayFunc)
910{
911  /* XXX Remove the warning after GLUT 3.0. */
912  if (!displayFunc)
913    __glutFatalError("NULL display callback not allowed in GLUT 3.0; update your code.");
914  __glutCurrentWindow->display = displayFunc;
915}
916
917void GLUTAPIENTRY
918glutMouseFunc(GLUTmouseCB mouseFunc)
919{
920  if (__glutCurrentWindow->mouse) {
921    if (!mouseFunc) {
922      /* Previous mouseFunc being disabled. */
923      __glutCurrentWindow->buttonUses--;
924      __glutChangeWindowEventMask(
925        ButtonPressMask | ButtonReleaseMask,
926        __glutCurrentWindow->buttonUses > 0);
927    }
928  } else {
929    if (mouseFunc) {
930      /* Previously no mouseFunc, new one being installed. */
931      __glutCurrentWindow->buttonUses++;
932      __glutChangeWindowEventMask(
933        ButtonPressMask | ButtonReleaseMask, True);
934    }
935  }
936  __glutCurrentWindow->mouse = mouseFunc;
937}
938
939void GLUTAPIENTRY
940glutMotionFunc(GLUTmotionCB motionFunc)
941{
942  /* Hack.  Some window managers (4Dwm by default) will mask
943     motion events if the client is not selecting for button
944     press and release events. So we select for press and
945     release events too (being careful to use reference
946     counting).  */
947  if (__glutCurrentWindow->motion) {
948    if (!motionFunc) {
949      /* previous mouseFunc being disabled */
950      __glutCurrentWindow->buttonUses--;
951      __glutChangeWindowEventMask(
952        ButtonPressMask | ButtonReleaseMask,
953        __glutCurrentWindow->buttonUses > 0);
954    }
955  } else {
956    if (motionFunc) {
957      /* Previously no mouseFunc, new one being installed. */
958      __glutCurrentWindow->buttonUses++;
959      __glutChangeWindowEventMask(
960        ButtonPressMask | ButtonReleaseMask, True);
961    }
962  }
963  /* Real work of selecting for passive mouse motion.  */
964  __glutChangeWindowEventMask(
965    Button1MotionMask | Button2MotionMask | Button3MotionMask,
966    motionFunc != NULL);
967  __glutCurrentWindow->motion = motionFunc;
968}
969
970void GLUTAPIENTRY
971glutPassiveMotionFunc(GLUTpassiveCB passiveMotionFunc)
972{
973  __glutChangeWindowEventMask(PointerMotionMask,
974    passiveMotionFunc != NULL);
975
976  /* Passive motion also requires watching enters and leaves so
977     that a fake passive motion event can be generated on an
978     enter. */
979  __glutChangeWindowEventMask(EnterWindowMask | LeaveWindowMask,
980    __glutCurrentWindow->entry != NULL || passiveMotionFunc != NULL);
981
982  __glutCurrentWindow->passive = passiveMotionFunc;
983}
984
985void GLUTAPIENTRY
986glutEntryFunc(GLUTentryCB entryFunc)
987{
988  __glutChangeWindowEventMask(EnterWindowMask | LeaveWindowMask,
989    entryFunc != NULL || __glutCurrentWindow->passive);
990  __glutCurrentWindow->entry = entryFunc;
991  if (!entryFunc) {
992    __glutCurrentWindow->entryState = -1;
993  }
994}
995
996void GLUTAPIENTRY
997glutWindowStatusFunc(GLUTwindowStatusCB windowStatusFunc)
998{
999  __glutChangeWindowEventMask(VisibilityChangeMask,
1000    windowStatusFunc != NULL);
1001  __glutCurrentWindow->windowStatus = windowStatusFunc;
1002  if (!windowStatusFunc) {
1003    /* Make state invalid. */
1004    __glutCurrentWindow->visState = -1;
1005  }
1006}
1007
1008static void GLUTCALLBACK
1009visibilityHelper(int status)
1010{
1011  if (status == GLUT_HIDDEN || status == GLUT_FULLY_COVERED)
1012    __glutCurrentWindow->visibility(GLUT_NOT_VISIBLE);
1013  else
1014    __glutCurrentWindow->visibility(GLUT_VISIBLE);
1015}
1016
1017void GLUTAPIENTRY
1018glutVisibilityFunc(GLUTvisibilityCB visibilityFunc)
1019{
1020  __glutCurrentWindow->visibility = visibilityFunc;
1021  if (visibilityFunc)
1022    glutWindowStatusFunc(visibilityHelper);
1023  else
1024    glutWindowStatusFunc(NULL);
1025}
1026
1027void GLUTAPIENTRY
1028glutReshapeFunc(GLUTreshapeCB reshapeFunc)
1029{
1030  if (reshapeFunc) {
1031    __glutCurrentWindow->reshape = reshapeFunc;
1032  } else {
1033    __glutCurrentWindow->reshape = __glutDefaultReshape;
1034  }
1035}
1036