configure: allow C{,XX}FLAGS override
[mesa.git] / src / glut / glx / glut_win.c
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
23 GLUTwindow *__glutCurrentWindow = NULL;
24 GLUTwindow **__glutWindowList = NULL;
25 int __glutWindowListSize = 0;
26 #if !defined(_WIN32)
27 GLUTstale *__glutStaleWindowList = NULL;
28 #endif
29 GLUTwindow *__glutMenuWindow = NULL;
30
31 void (*__glutFreeOverlayFunc) (GLUToverlay *);
32 XVisualInfo *(*__glutDetermineVisualFromString) (char *string, Bool * treatAsSingle,
33 Criterion * requiredCriteria, int nRequired, int requiredMask, void **fbc) = NULL;
34
35 static Criterion requiredWindowCriteria[] =
36 {
37 {LEVEL, EQ, 0},
38 {TRANSPARENT, EQ, 0}
39 };
40 static int numRequiredWindowCriteria = sizeof(requiredWindowCriteria) / sizeof(Criterion);
41 static int requiredWindowCriteriaMask = (1 << LEVEL) | (1 << TRANSPARENT);
42
43 static void
44 cleanWindowWorkList(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
65 static void
66 cleanStaleWindowList(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
88 static GLUTwindow *__glutWindowCache = NULL;
89
90 GLUTwindow *
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 */
133 int GLUTAPIENTRY
134 glutGetWindow(void)
135 {
136 if (__glutCurrentWindow) {
137 return __glutCurrentWindow->num + 1;
138 } else {
139 return 0;
140 }
141 }
142 /* ENDCENTRY */
143
144 void
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 */
180 void GLUTAPIENTRY
181 glutSetWindow(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
198 static int
199 getUnusedWindowSlot(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
228 static XVisualInfo *
229 getVisualInfoCI(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
279 static XVisualInfo *
280 getVisualInfoRGB(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
356 static XVisualInfo *
357 getVisualInfoID(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
368 XVisualInfo *
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
388 XVisualInfo *
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
429 static 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
439 void 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
459 XVisualInfo *
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. */
488 GLUTwindow *
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 */
717 int GLUTAPIENTRY
718 glutCreateWindow(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
764 int GLUTAPIENTRY
765 __glutCreateWindowWithExit(const char *title, void (__cdecl *exitfunc)(int))
766 {
767 __glutExitFunc = exitfunc;
768 return glutCreateWindow(title);
769 }
770 #endif
771
772 int GLUTAPIENTRY
773 glutCreateSubWindow(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
793 void
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 */
864 void GLUTAPIENTRY
865 glutDestroyWindow(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
887 void
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
908 void GLUTAPIENTRY
909 glutDisplayFunc(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
917 void GLUTAPIENTRY
918 glutMouseFunc(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
939 void GLUTAPIENTRY
940 glutMotionFunc(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
970 void GLUTAPIENTRY
971 glutPassiveMotionFunc(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
985 void GLUTAPIENTRY
986 glutEntryFunc(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
996 void GLUTAPIENTRY
997 glutWindowStatusFunc(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
1008 static void GLUTCALLBACK
1009 visibilityHelper(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
1017 void GLUTAPIENTRY
1018 glutVisibilityFunc(GLUTvisibilityCB visibilityFunc)
1019 {
1020 __glutCurrentWindow->visibility = visibilityFunc;
1021 if (visibilityFunc)
1022 glutWindowStatusFunc(visibilityHelper);
1023 else
1024 glutWindowStatusFunc(NULL);
1025 }
1026
1027 void GLUTAPIENTRY
1028 glutReshapeFunc(GLUTreshapeCB reshapeFunc)
1029 {
1030 if (reshapeFunc) {
1031 __glutCurrentWindow->reshape = reshapeFunc;
1032 } else {
1033 __glutCurrentWindow->reshape = __glutDefaultReshape;
1034 }
1035 }