added glutGetProcAddress() and GLUT_FPS env var option
[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 APIENTRY
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 APIENTRY
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)
332 if (GLUT_WIND_IS_MULTISAMPLE(mode)) {
333 if (!__glutIsSupportedByGLX("GLX_SGIS_multisample"))
334 return NULL;
335 list[n++] = GLX_SAMPLES_SGIS;
336 /* XXX Is 4 a reasonable minimum acceptable number of
337 samples? */
338 list[n++] = 4;
339 }
340 #endif
341 list[n] = (int) None; /* terminate list */
342
343 return glXChooseVisual(__glutDisplay,
344 __glutScreen, list);
345 }
346
347 XVisualInfo *
348 __glutGetVisualInfo(unsigned int mode)
349 {
350 /* XXX GLUT_LUMINANCE not implemented for GLUT 3.0. */
351 if (GLUT_WIND_IS_LUMINANCE(mode))
352 return NULL;
353
354 if (GLUT_WIND_IS_RGB(mode))
355 return getVisualInfoRGB(mode);
356 else
357 return getVisualInfoCI(mode);
358 }
359
360 XVisualInfo *
361 __glutDetermineVisual(
362 unsigned int displayMode,
363 Bool * treatAsSingle,
364 XVisualInfo * (getVisualInfo) (unsigned int))
365 {
366 XVisualInfo *vis;
367
368 /* Should not be looking at display mode mask if
369 __glutDisplayString is non-NULL. */
370 assert(!__glutDisplayString);
371
372 *treatAsSingle = GLUT_WIND_IS_SINGLE(displayMode);
373 vis = getVisualInfo(displayMode);
374 if (!vis) {
375 /* Fallback cases when can't get exactly what was asked
376 for... */
377 if (GLUT_WIND_IS_SINGLE(displayMode)) {
378 /* If we can't find a single buffered visual, try looking
379 for a double buffered visual. We can treat a double
380 buffered visual as a single buffer visual by changing
381 the draw buffer to GL_FRONT and treating any swap
382 buffers as no-ops. */
383 displayMode |= GLUT_DOUBLE;
384 vis = getVisualInfo(displayMode);
385 *treatAsSingle = True;
386 }
387 if (!vis && GLUT_WIND_IS_MULTISAMPLE(displayMode)) {
388 /* If we can't seem to get multisampling (ie, not Reality
389 Engine class graphics!), go without multisampling. It
390 is up to the application to query how many multisamples
391 were allocated (0 equals no multisampling) if the
392 application is going to use multisampling for more than
393 just antialiasing. */
394 displayMode &= ~GLUT_MULTISAMPLE;
395 vis = getVisualInfo(displayMode);
396 }
397 }
398 return vis;
399 }
400
401 static void GLUTCALLBACK
402 __glutDefaultDisplay(void)
403 {
404 /* XXX Remove the warning after GLUT 3.0. */
405 __glutWarning("The following is a new check for GLUT 3.0; update your code.");
406 __glutFatalError(
407 "redisplay needed for window %d, but no display callback.",
408 __glutCurrentWindow->num + 1);
409 }
410
411 void GLUTCALLBACK
412 __glutDefaultReshape(int width, int height)
413 {
414 GLUToverlay *overlay;
415
416 /* Adjust the viewport of the window (and overlay if one
417 exists). */
418 MAKE_CURRENT_WINDOW(__glutCurrentWindow);
419 glViewport(0, 0, (GLsizei) width, (GLsizei) height);
420 overlay = __glutCurrentWindow->overlay;
421 if (overlay) {
422 MAKE_CURRENT_OVERLAY(overlay);
423 glViewport(0, 0, (GLsizei) width, (GLsizei) height);
424 }
425 /* Make sure we are current to the current layer (application
426 should be able to count on the current layer not changing
427 unless the application explicitly calls glutUseLayer). */
428 MAKE_CURRENT_LAYER(__glutCurrentWindow);
429 }
430
431 XVisualInfo *
432 __glutDetermineWindowVisual(Bool * treatAsSingle, Bool * visAlloced, void **fbc)
433 {
434 if (__glutDisplayString) {
435
436 /* __glutDisplayString should be NULL except if
437 glutInitDisplayString has been called to register a
438 different display string. Calling glutInitDisplayString
439 means using a string instead of an integer mask determine
440 the visual to use. Using the function pointer variable
441 __glutDetermineVisualFromString below avoids linking in
442 the code for implementing glutInitDisplayString (ie,
443 glut_dstr.o) unless glutInitDisplayString gets called by
444 the application. */
445
446 assert(__glutDetermineVisualFromString);
447 *visAlloced = False;
448 *fbc = NULL;
449 return __glutDetermineVisualFromString(__glutDisplayString, treatAsSingle,
450 requiredWindowCriteria, numRequiredWindowCriteria, requiredWindowCriteriaMask, fbc);
451 } else {
452 *visAlloced = True;
453 *fbc = NULL;
454 return __glutDetermineVisual(__glutDisplayMode,
455 treatAsSingle, __glutGetVisualInfo);
456 }
457 }
458
459 /* ARGSUSED5 */ /* Only Win32 uses gameMode parameter. */
460 GLUTwindow *
461 __glutCreateWindow(GLUTwindow * parent,
462 int x, int y, int width, int height, int gameMode)
463 {
464 GLUTwindow *window;
465 XSetWindowAttributes wa;
466 unsigned long attribMask;
467 int winnum;
468 int i;
469 #if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_fbconfig)
470 GLXFBConfigSGIX fbc;
471 #else
472 void *fbc;
473 #endif
474
475 #if defined(_WIN32)
476 WNDCLASS wc;
477 int style;
478
479 if (!GetClassInfo(GetModuleHandle(NULL), "GLUT", &wc)) {
480 __glutOpenWin32Connection(NULL);
481 }
482 #else
483 if (!__glutDisplay) {
484 __glutOpenXConnection(NULL);
485 }
486 #endif
487 if (__glutGameModeWindow) {
488 __glutFatalError("cannot create windows in game mode.");
489 }
490 winnum = getUnusedWindowSlot();
491 window = (GLUTwindow *) malloc(sizeof(GLUTwindow));
492 if (!window) {
493 __glutFatalError("out of memory.");
494 }
495 window->num = winnum;
496
497 #if !defined(_WIN32)
498 window->vis = __glutDetermineWindowVisual(&window->treatAsSingle,
499 &window->visAlloced, (void**) &fbc);
500 if (!window->vis) {
501 __glutFatalError(
502 "visual with necessary capabilities not found.");
503 }
504 __glutSetupColormap(window->vis, &window->colormap, &window->cmap);
505 #endif
506 window->eventMask = StructureNotifyMask | ExposureMask;
507
508 attribMask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask;
509 wa.background_pixmap = None;
510 wa.border_pixel = 0;
511 wa.colormap = window->cmap;
512 wa.event_mask = window->eventMask;
513 if (parent) {
514 if (parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK)
515 wa.event_mask |= GLUT_HACK_STOP_PROPAGATE_MASK;
516 attribMask |= CWDontPropagate;
517 wa.do_not_propagate_mask = parent->eventMask & GLUT_DONT_PROPAGATE_FILTER_MASK;
518 } else {
519 wa.do_not_propagate_mask = 0;
520 }
521
522 /* Stash width and height before Win32's __glutAdjustCoords
523 possibly overwrites the values. */
524 window->width = width;
525 window->height = height;
526 window->forceReshape = True;
527 window->ignoreKeyRepeat = False;
528
529 #if defined(_WIN32)
530 __glutAdjustCoords(parent ? parent->win : NULL,
531 &x, &y, &width, &height);
532 if (parent) {
533 style = WS_CHILD;
534 } else {
535 if (gameMode) {
536 /* Game mode window should be a WS_POPUP window to
537 ensure that the taskbar is hidden by it. A standard
538 WS_OVERLAPPEDWINDOW does not hide the task bar. */
539 style = WS_POPUP | WS_MAXIMIZE;
540 } else {
541 /* A standard toplevel window with borders and such. */
542 style = WS_OVERLAPPEDWINDOW;
543 }
544 }
545 window->win = CreateWindow("GLUT", "GLUT",
546 WS_CLIPSIBLINGS | WS_CLIPCHILDREN | style,
547 x, y, width, height, parent ? parent->win : __glutRoot,
548 NULL, GetModuleHandle(NULL), 0);
549 window->hdc = GetDC(window->win);
550 /* Must set the XHDC for fake glXChooseVisual & fake
551 glXCreateContext & fake XAllocColorCells. */
552 XHDC = window->hdc;
553 window->vis = __glutDetermineWindowVisual(&window->treatAsSingle,
554 &window->visAlloced, &fbc);
555 if (!window->vis) {
556 __glutFatalError(
557 "pixel format with necessary capabilities not found.");
558 }
559 if (!SetPixelFormat(window->hdc,
560 ChoosePixelFormat(window->hdc, window->vis),
561 window->vis)) {
562 __glutFatalError("SetPixelFormat failed during window create.");
563 }
564 __glutSetupColormap(window->vis, &window->colormap, &window->cmap);
565 /* Make sure subwindows get a windowStatus callback. */
566 if (parent) {
567 PostMessage(parent->win, WM_ACTIVATE, 0, 0);
568 }
569 window->renderDc = window->hdc;
570 #else
571 window->win = XCreateWindow(__glutDisplay,
572 parent == NULL ? __glutRoot : parent->win,
573 x, y, width, height, 0,
574 window->vis->depth, InputOutput, window->vis->visual,
575 attribMask, &wa);
576 #endif
577 window->renderWin = window->win;
578 #if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_fbconfig)
579 if (fbc) {
580 window->ctx = __glut_glXCreateContextWithConfigSGIX(__glutDisplay, fbc,
581 GLX_RGBA_TYPE_SGIX, None, __glutTryDirect);
582 } else
583 #endif
584 {
585 window->ctx = glXCreateContext(__glutDisplay, window->vis,
586 None, __glutTryDirect);
587 }
588 if (!window->ctx) {
589 __glutFatalError(
590 "failed to create OpenGL rendering context.");
591 }
592 window->renderCtx = window->ctx;
593 #if !defined(_WIN32)
594 window->isDirect = glXIsDirect(__glutDisplay, window->ctx);
595 if (__glutForceDirect) {
596 if (!window->isDirect)
597 __glutFatalError("direct rendering not possible.");
598 }
599 #endif
600
601 window->parent = parent;
602 if (parent) {
603 window->siblings = parent->children;
604 parent->children = window;
605 } else {
606 window->siblings = NULL;
607 }
608 window->overlay = NULL;
609 window->children = NULL;
610 window->display = __glutDefaultDisplay;
611 window->reshape = __glutDefaultReshape;
612 window->mouse = NULL;
613 window->motion = NULL;
614 window->passive = NULL;
615 window->entry = NULL;
616 window->keyboard = NULL;
617 window->keyboardUp = NULL;
618 window->windowStatus = NULL;
619 window->visibility = NULL;
620 window->special = NULL;
621 window->specialUp = NULL;
622 window->buttonBox = NULL;
623 window->dials = NULL;
624 window->spaceMotion = NULL;
625 window->spaceRotate = NULL;
626 window->spaceButton = NULL;
627 window->tabletMotion = NULL;
628 window->tabletButton = NULL;
629 #ifdef _WIN32
630 window->joystick = NULL;
631 window->joyPollInterval = 0;
632 #endif
633 window->tabletPos[0] = -1;
634 window->tabletPos[1] = -1;
635 window->shownState = 0;
636 window->visState = -1; /* not VisibilityUnobscured,
637 VisibilityPartiallyObscured, or
638 VisibilityFullyObscured */
639 window->entryState = -1; /* not EnterNotify or LeaveNotify */
640
641 window->desiredConfMask = 0;
642 window->buttonUses = 0;
643 window->cursor = GLUT_CURSOR_INHERIT;
644
645 /* Setup window to be mapped when glutMainLoop starts. */
646 window->workMask = GLUT_MAP_WORK;
647 #ifdef _WIN32
648 if (gameMode) {
649 /* When mapping a game mode window, just show
650 the window. We have already created the game
651 mode window with a maximize flag at creation
652 time. Doing a ShowWindow(window->win, SW_SHOWNORMAL)
653 would be wrong for a game mode window since it
654 would unmaximize the window. */
655 window->desiredMapState = GameModeState;
656 } else {
657 window->desiredMapState = NormalState;
658 }
659 #else
660 window->desiredMapState = NormalState;
661 #endif
662 window->prevWorkWin = __glutWindowWorkList;
663 __glutWindowWorkList = window;
664
665 /* Initially, no menus attached. */
666 for (i = 0; i < GLUT_MAX_MENUS; i++) {
667 window->menu[i] = 0;
668 }
669
670 /* Add this new window to the window list. */
671 __glutWindowList[winnum] = window;
672
673 /* Make the new window the current window. */
674 __glutSetWindow(window);
675
676 __glutDetermineMesaSwapHackSupport();
677
678 if (window->treatAsSingle) {
679 /* We do this because either the window really is single
680 buffered (in which case this is redundant, but harmless,
681 because this is the initial single-buffered context
682 state); or we are treating a double buffered window as a
683 single-buffered window because the system does not appear
684 to export any suitable single- buffered visuals (in which
685 the following are necessary). */
686 glDrawBuffer(GL_FRONT);
687 glReadBuffer(GL_FRONT);
688 }
689 return window;
690 }
691
692 /* CENTRY */
693 int APIENTRY
694 glutCreateWindow(const char *title)
695 {
696 static int firstWindow = 1;
697 GLUTwindow *window;
698 #if !defined(_WIN32)
699 XWMHints *wmHints;
700 #endif
701 Window win;
702 XTextProperty textprop;
703
704 if (__glutGameModeWindow) {
705 __glutFatalError("cannot create windows in game mode.");
706 }
707 window = __glutCreateWindow(NULL,
708 __glutSizeHints.x, __glutSizeHints.y,
709 __glutInitWidth, __glutInitHeight,
710 /* not game mode */ 0);
711 win = window->win;
712 /* Setup ICCCM properties. */
713 textprop.value = (unsigned char *) title;
714 textprop.encoding = XA_STRING;
715 textprop.format = 8;
716 textprop.nitems = strlen(title);
717 #if defined(_WIN32)
718 SetWindowText(win, title);
719 if (__glutIconic) {
720 window->desiredMapState = IconicState;
721 }
722 #else
723 wmHints = XAllocWMHints();
724 wmHints->initial_state =
725 __glutIconic ? IconicState : NormalState;
726 wmHints->flags = StateHint;
727 XSetWMProperties(__glutDisplay, win, &textprop, &textprop,
728 /* Only put WM_COMMAND property on first window. */
729 firstWindow ? __glutArgv : NULL,
730 firstWindow ? __glutArgc : 0,
731 &__glutSizeHints, wmHints, NULL);
732 XFree(wmHints);
733 XSetWMProtocols(__glutDisplay, win, &__glutWMDeleteWindow, 1);
734 #endif
735 firstWindow = 0;
736 return window->num + 1;
737 }
738
739 #ifdef _WIN32
740 int APIENTRY
741 __glutCreateWindowWithExit(const char *title, void (__cdecl *exitfunc)(int))
742 {
743 __glutExitFunc = exitfunc;
744 return glutCreateWindow(title);
745 }
746 #endif
747
748 int APIENTRY
749 glutCreateSubWindow(int win, int x, int y, int width, int height)
750 {
751 GLUTwindow *window;
752
753 window = __glutCreateWindow(__glutWindowList[win - 1],
754 x, y, width, height, /* not game mode */ 0);
755 #if !defined(_WIN32)
756 {
757 GLUTwindow *toplevel;
758
759 toplevel = __glutToplevelOf(window);
760 if (toplevel->cmap != window->cmap) {
761 __glutPutOnWorkList(toplevel, GLUT_COLORMAP_WORK);
762 }
763 }
764 #endif
765 return window->num + 1;
766 }
767 /* ENDCENTRY */
768
769 void
770 __glutDestroyWindow(GLUTwindow * window,
771 GLUTwindow * initialWindow)
772 {
773 GLUTwindow **prev, *cur, *parent, *siblings;
774
775 /* Recursively destroy any children. */
776 cur = window->children;
777 while (cur) {
778 siblings = cur->siblings;
779 __glutDestroyWindow(cur, initialWindow);
780 cur = siblings;
781 }
782 /* Remove from parent's children list (only necessary for
783 non-initial windows and subwindows!). */
784 parent = window->parent;
785 if (parent && parent == initialWindow->parent) {
786 prev = &parent->children;
787 cur = parent->children;
788 while (cur) {
789 if (cur == window) {
790 *prev = cur->siblings;
791 break;
792 }
793 prev = &(cur->siblings);
794 cur = cur->siblings;
795 }
796 }
797 /* Unbind if bound to this window. */
798 if (window == __glutCurrentWindow) {
799 UNMAKE_CURRENT();
800 __glutCurrentWindow = NULL;
801 }
802 /* Begin tearing down window itself. */
803 if (window->overlay) {
804 __glutFreeOverlayFunc(window->overlay);
805 }
806 XDestroyWindow(__glutDisplay, window->win);
807 glXDestroyContext(__glutDisplay, window->ctx);
808 if (window->colormap) {
809 /* Only color index windows have colormap data structure. */
810 __glutFreeColormap(window->colormap);
811 }
812 /* NULLing the __glutWindowList helps detect is a window
813 instance has been destroyed, given a window number. */
814 __glutWindowList[window->num] = NULL;
815
816 /* Cleanup data structures that might contain window. */
817 cleanWindowWorkList(window);
818 #if !defined(_WIN32)
819 cleanStaleWindowList(window);
820 #endif
821 /* Remove window from the "get window cache" if it is there. */
822 if (__glutWindowCache == window)
823 __glutWindowCache = NULL;
824
825 if (window->visAlloced) {
826 /* Only free XVisualInfo* gotten from glXChooseVisual. */
827 XFree(window->vis);
828 }
829
830 if (window == __glutGameModeWindow) {
831 /* Destroying the game mode window should implicitly
832 have GLUT leave game mode. */
833 __glutCloseDownGameMode();
834 }
835
836 free(window);
837 }
838
839 /* CENTRY */
840 void APIENTRY
841 glutDestroyWindow(int win)
842 {
843 GLUTwindow *window = __glutWindowList[win - 1];
844
845 if (__glutMappedMenu && __glutMenuWindow == window) {
846 __glutFatalUsage("destroying menu window not allowed while menus in use");
847 }
848 #if !defined(_WIN32)
849 /* If not a toplevel window... */
850 if (window->parent) {
851 /* Destroying subwindows may change colormap requirements;
852 recalculate toplevel window's WM_COLORMAP_WINDOWS
853 property. */
854 __glutPutOnWorkList(__glutToplevelOf(window->parent),
855 GLUT_COLORMAP_WORK);
856 }
857 #endif
858 __glutDestroyWindow(window, window);
859 XFlush(__glutDisplay);
860 }
861 /* ENDCENTRY */
862
863 void
864 __glutChangeWindowEventMask(long eventMask, Bool add)
865 {
866 if (add) {
867 /* Add eventMask to window's event mask. */
868 if ((__glutCurrentWindow->eventMask & eventMask) !=
869 eventMask) {
870 __glutCurrentWindow->eventMask |= eventMask;
871 __glutPutOnWorkList(__glutCurrentWindow,
872 GLUT_EVENT_MASK_WORK);
873 }
874 } else {
875 /* Remove eventMask from window's event mask. */
876 if (__glutCurrentWindow->eventMask & eventMask) {
877 __glutCurrentWindow->eventMask &= ~eventMask;
878 __glutPutOnWorkList(__glutCurrentWindow,
879 GLUT_EVENT_MASK_WORK);
880 }
881 }
882 }
883
884 void APIENTRY
885 glutDisplayFunc(GLUTdisplayCB displayFunc)
886 {
887 /* XXX Remove the warning after GLUT 3.0. */
888 if (!displayFunc)
889 __glutFatalError("NULL display callback not allowed in GLUT 3.0; update your code.");
890 __glutCurrentWindow->display = displayFunc;
891 }
892
893 void APIENTRY
894 glutMouseFunc(GLUTmouseCB mouseFunc)
895 {
896 if (__glutCurrentWindow->mouse) {
897 if (!mouseFunc) {
898 /* Previous mouseFunc being disabled. */
899 __glutCurrentWindow->buttonUses--;
900 __glutChangeWindowEventMask(
901 ButtonPressMask | ButtonReleaseMask,
902 __glutCurrentWindow->buttonUses > 0);
903 }
904 } else {
905 if (mouseFunc) {
906 /* Previously no mouseFunc, new one being installed. */
907 __glutCurrentWindow->buttonUses++;
908 __glutChangeWindowEventMask(
909 ButtonPressMask | ButtonReleaseMask, True);
910 }
911 }
912 __glutCurrentWindow->mouse = mouseFunc;
913 }
914
915 void APIENTRY
916 glutMotionFunc(GLUTmotionCB motionFunc)
917 {
918 /* Hack. Some window managers (4Dwm by default) will mask
919 motion events if the client is not selecting for button
920 press and release events. So we select for press and
921 release events too (being careful to use reference
922 counting). */
923 if (__glutCurrentWindow->motion) {
924 if (!motionFunc) {
925 /* previous mouseFunc being disabled */
926 __glutCurrentWindow->buttonUses--;
927 __glutChangeWindowEventMask(
928 ButtonPressMask | ButtonReleaseMask,
929 __glutCurrentWindow->buttonUses > 0);
930 }
931 } else {
932 if (motionFunc) {
933 /* Previously no mouseFunc, new one being installed. */
934 __glutCurrentWindow->buttonUses++;
935 __glutChangeWindowEventMask(
936 ButtonPressMask | ButtonReleaseMask, True);
937 }
938 }
939 /* Real work of selecting for passive mouse motion. */
940 __glutChangeWindowEventMask(
941 Button1MotionMask | Button2MotionMask | Button3MotionMask,
942 motionFunc != NULL);
943 __glutCurrentWindow->motion = motionFunc;
944 }
945
946 void APIENTRY
947 glutPassiveMotionFunc(GLUTpassiveCB passiveMotionFunc)
948 {
949 __glutChangeWindowEventMask(PointerMotionMask,
950 passiveMotionFunc != NULL);
951
952 /* Passive motion also requires watching enters and leaves so
953 that a fake passive motion event can be generated on an
954 enter. */
955 __glutChangeWindowEventMask(EnterWindowMask | LeaveWindowMask,
956 __glutCurrentWindow->entry != NULL || passiveMotionFunc != NULL);
957
958 __glutCurrentWindow->passive = passiveMotionFunc;
959 }
960
961 void APIENTRY
962 glutEntryFunc(GLUTentryCB entryFunc)
963 {
964 __glutChangeWindowEventMask(EnterWindowMask | LeaveWindowMask,
965 entryFunc != NULL || __glutCurrentWindow->passive);
966 __glutCurrentWindow->entry = entryFunc;
967 if (!entryFunc) {
968 __glutCurrentWindow->entryState = -1;
969 }
970 }
971
972 void APIENTRY
973 glutWindowStatusFunc(GLUTwindowStatusCB windowStatusFunc)
974 {
975 __glutChangeWindowEventMask(VisibilityChangeMask,
976 windowStatusFunc != NULL);
977 __glutCurrentWindow->windowStatus = windowStatusFunc;
978 if (!windowStatusFunc) {
979 /* Make state invalid. */
980 __glutCurrentWindow->visState = -1;
981 }
982 }
983
984 static void GLUTCALLBACK
985 visibilityHelper(int status)
986 {
987 if (status == GLUT_HIDDEN || status == GLUT_FULLY_COVERED)
988 __glutCurrentWindow->visibility(GLUT_NOT_VISIBLE);
989 else
990 __glutCurrentWindow->visibility(GLUT_VISIBLE);
991 }
992
993 void APIENTRY
994 glutVisibilityFunc(GLUTvisibilityCB visibilityFunc)
995 {
996 __glutCurrentWindow->visibility = visibilityFunc;
997 if (visibilityFunc)
998 glutWindowStatusFunc(visibilityHelper);
999 else
1000 glutWindowStatusFunc(NULL);
1001 }
1002
1003 void APIENTRY
1004 glutReshapeFunc(GLUTreshapeCB reshapeFunc)
1005 {
1006 if (reshapeFunc) {
1007 __glutCurrentWindow->reshape = reshapeFunc;
1008 } else {
1009 __glutCurrentWindow->reshape = __glutDefaultReshape;
1010 }
1011 }