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