1 | /* $Id: glut_event.c,v 1.10 2000-05-20 13:48:22 jeroen Exp $ */
|
---|
2 | /* Copyright (c) Mark J. Kilgard, 1994, 1995, 1996, 1997, 1998. */
|
---|
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 | #include <stdlib.h>
|
---|
9 | #include <stdio.h>
|
---|
10 | #include <errno.h>
|
---|
11 | #include <assert.h>
|
---|
12 | #include <string.h> /* Some FD_ZERO macros use memset without
|
---|
13 | prototyping memset. */
|
---|
14 |
|
---|
15 | /* Much of the following #ifdef logic to include the proper
|
---|
16 | prototypes for the select system call is based on logic
|
---|
17 | from the X11R6.3 version of <X11/Xpoll.h>. */
|
---|
18 |
|
---|
19 | #if !defined(_WIN32) && !defined(__WIN32OS2__)
|
---|
20 | # ifdef __sgi
|
---|
21 | # include <bstring.h> /* prototype for bzero used by FD_ZERO */
|
---|
22 | # endif
|
---|
23 | # if (defined(SVR4) || defined(CRAY) || defined(AIXV3)) && !defined(FD_SETSIZE)
|
---|
24 | # include <sys/select.h> /* select system call interface */
|
---|
25 | # ifdef luna
|
---|
26 | # include <sysent.h>
|
---|
27 | # endif
|
---|
28 | # endif
|
---|
29 | /* AIX 4.2 fubar-ed <sys/select.h>, so go to heroic measures to get it */
|
---|
30 | # if defined(AIXV4) && !defined(NFDBITS)
|
---|
31 | # include <sys/select.h>
|
---|
32 | # endif
|
---|
33 | #endif /* !_WIN32 */
|
---|
34 |
|
---|
35 | #include <sys/types.h>
|
---|
36 |
|
---|
37 | #if !defined(_WIN32) && !defined(__WIN32OS2__)
|
---|
38 | # if defined(__vms) && ( __VMS_VER < 70000000 )
|
---|
39 | # include <sys/time.h>
|
---|
40 | # else
|
---|
41 | # ifndef __vms
|
---|
42 | # include <sys/time.h>
|
---|
43 | # endif
|
---|
44 | # endif
|
---|
45 | # include <unistd.h>
|
---|
46 | # include <X11/Xlib.h>
|
---|
47 | # include <X11/keysym.h>
|
---|
48 | #else
|
---|
49 | # ifdef __CYGWIN32__
|
---|
50 | # include <sys/time.h>
|
---|
51 | # else
|
---|
52 | # include <sys/timeb.h>
|
---|
53 | # endif
|
---|
54 | # ifdef __hpux
|
---|
55 | /* XXX Bert Gijsbers <bert@mc.bio.uva.nl> reports that HP-UX
|
---|
56 | needs different keysyms for the End, Insert, and Delete keys
|
---|
57 | to work on an HP 715. It would be better if HP generated
|
---|
58 | standard keysyms for standard keys. */
|
---|
59 | # include <X11/HPkeysym.h>
|
---|
60 | # endif
|
---|
61 | #endif /* !_WIN32 */
|
---|
62 |
|
---|
63 | #if defined(__vms) && ( __VMS_VER < 70000000 )
|
---|
64 | #include <ssdef.h>
|
---|
65 | #include <psldef.h>
|
---|
66 | extern int SYS$CLREF(int efn);
|
---|
67 | extern int SYS$SETIMR(unsigned int efn, struct timeval *timeout, void *ast,
|
---|
68 | unsigned int request_id, unsigned int flags);
|
---|
69 | extern int SYS$WFLOR(unsigned int efn, unsigned int mask);
|
---|
70 | extern int SYS$CANTIM(unsigned int request_id, unsigned int mode);
|
---|
71 | #endif /* __vms, VMs 6.2 or earlier */
|
---|
72 |
|
---|
73 | #include "glutint.h"
|
---|
74 |
|
---|
75 | static GLUTtimer *freeTimerList = NULL;
|
---|
76 |
|
---|
77 | GLUTidleCB __glutIdleFunc = NULL;
|
---|
78 | GLUTtimer *__glutTimerList = NULL;
|
---|
79 | #ifdef SUPPORT_FORTRAN
|
---|
80 | GLUTtimer *__glutNewTimer;
|
---|
81 | #endif
|
---|
82 | GLUTwindow *__glutWindowWorkList = NULL;
|
---|
83 | GLUTmenu *__glutMappedMenu;
|
---|
84 | GLUTmenu *__glutCurrentMenu = NULL;
|
---|
85 |
|
---|
86 | void (*__glutUpdateInputDeviceMaskFunc) (GLUTwindow *);
|
---|
87 | #if !defined(_WIN32) && !defined(__WIN32OS2__)
|
---|
88 | void (*__glutMenuItemEnterOrLeave)(GLUTmenuItem * item, int num, int type) = NULL;
|
---|
89 | void (*__glutFinishMenu)(Window win, int x, int y);
|
---|
90 | void (*__glutPaintMenu)(GLUTmenu * menu);
|
---|
91 | void (*__glutStartMenu)(GLUTmenu * menu, GLUTwindow * window, int x, int y, int x_win, int y_win);
|
---|
92 | GLUTmenu * (*__glutGetMenuByNum)(int menunum);
|
---|
93 | GLUTmenuItem * (*__glutGetMenuItem)(GLUTmenu * menu, Window win, int *which);
|
---|
94 | GLUTmenu * (*__glutGetMenu)(Window win);
|
---|
95 | #endif
|
---|
96 |
|
---|
97 | Atom __glutMotifHints = None;
|
---|
98 | /* Modifier mask of ~0 implies not in core input callback. */
|
---|
99 | unsigned int __glutModifierMask = (unsigned int) ~0;
|
---|
100 | int __glutWindowDamaged = 0;
|
---|
101 |
|
---|
102 | void GLAPIENTRY
|
---|
103 | glutIdleFunc(GLUTidleCB idleFunc)
|
---|
104 | {
|
---|
105 | __glutIdleFunc = idleFunc;
|
---|
106 | }
|
---|
107 |
|
---|
108 | void GLAPIENTRY
|
---|
109 | glutTimerFunc(unsigned int interval, GLUTtimerCB timerFunc, int value)
|
---|
110 | {
|
---|
111 | GLUTtimer *timer, *other;
|
---|
112 | GLUTtimer **prevptr;
|
---|
113 | struct timeval now;
|
---|
114 |
|
---|
115 | if (!timerFunc)
|
---|
116 | return;
|
---|
117 |
|
---|
118 | if (freeTimerList) {
|
---|
119 | timer = freeTimerList;
|
---|
120 | freeTimerList = timer->next;
|
---|
121 | } else {
|
---|
122 | timer = (GLUTtimer *) malloc(sizeof(GLUTtimer));
|
---|
123 | if (!timer)
|
---|
124 | __glutFatalError("out of memory.");
|
---|
125 | }
|
---|
126 |
|
---|
127 | timer->func = timerFunc;
|
---|
128 | #if defined(__vms) && ( __VMS_VER < 70000000 )
|
---|
129 | /* VMS time is expressed in units of 100 ns */
|
---|
130 | timer->timeout.val = interval * TICKS_PER_MILLISECOND;
|
---|
131 | #else
|
---|
132 | timer->timeout.tv_sec = (int) interval / 1000;
|
---|
133 | timer->timeout.tv_usec = (int) (interval % 1000) * 1000;
|
---|
134 | #endif
|
---|
135 | timer->value = value;
|
---|
136 | timer->next = NULL;
|
---|
137 | GETTIMEOFDAY(&now);
|
---|
138 | ADD_TIME(timer->timeout, timer->timeout, now);
|
---|
139 | prevptr = &__glutTimerList;
|
---|
140 | other = *prevptr;
|
---|
141 | while (other && IS_AFTER(other->timeout, timer->timeout)) {
|
---|
142 | prevptr = &other->next;
|
---|
143 | other = *prevptr;
|
---|
144 | }
|
---|
145 | timer->next = other;
|
---|
146 | #ifdef SUPPORT_FORTRAN
|
---|
147 | __glutNewTimer = timer; /* for Fortran binding! */
|
---|
148 | #endif
|
---|
149 | *prevptr = timer;
|
---|
150 | }
|
---|
151 |
|
---|
152 | void
|
---|
153 | handleTimeouts(void)
|
---|
154 | {
|
---|
155 | struct timeval now;
|
---|
156 | GLUTtimer *timer;
|
---|
157 |
|
---|
158 | /* Assumption is that __glutTimerList is already determined
|
---|
159 | to be non-NULL. */
|
---|
160 | GETTIMEOFDAY(&now);
|
---|
161 | while (IS_AT_OR_AFTER(__glutTimerList->timeout, now)) {
|
---|
162 | timer = __glutTimerList;
|
---|
163 | ((GLUTtimerCB)(timer->func))(timer->value);
|
---|
164 | __glutTimerList = timer->next;
|
---|
165 | timer->next = freeTimerList;
|
---|
166 | freeTimerList = timer;
|
---|
167 | if (!__glutTimerList)
|
---|
168 | break;
|
---|
169 | }
|
---|
170 | }
|
---|
171 |
|
---|
172 | void
|
---|
173 | __glutPutOnWorkList(GLUTwindow * window, int workMask)
|
---|
174 | {
|
---|
175 | if (window->workMask) {
|
---|
176 | /* Already on list; just OR in new workMask. */
|
---|
177 | window->workMask |= workMask;
|
---|
178 | } else {
|
---|
179 | /* Update work mask and add to window work list. */
|
---|
180 | window->workMask = workMask;
|
---|
181 | /* Assert that if the window does not have a
|
---|
182 | workMask already, the window should definitely
|
---|
183 | not be the head of the work list. */
|
---|
184 | assert(window != __glutWindowWorkList);
|
---|
185 | window->prevWorkWin = __glutWindowWorkList;
|
---|
186 | __glutWindowWorkList = window;
|
---|
187 | }
|
---|
188 | }
|
---|
189 |
|
---|
190 | void
|
---|
191 | __glutPostRedisplay(GLUTwindow * window, int layerMask)
|
---|
192 | {
|
---|
193 | int shown = (layerMask & (GLUT_REDISPLAY_WORK | GLUT_REPAIR_WORK)) ?
|
---|
194 | window->shownState : window->overlay->shownState;
|
---|
195 |
|
---|
196 | /* Post a redisplay if the window is visible (or the
|
---|
197 | visibility of the window is unknown, ie. window->visState
|
---|
198 | == -1) _and_ the layer is known to be shown. */
|
---|
199 | if (window->visState != GLUT_HIDDEN
|
---|
200 | && window->visState != GLUT_FULLY_COVERED && shown) {
|
---|
201 | __glutPutOnWorkList(window, layerMask);
|
---|
202 | }
|
---|
203 | }
|
---|
204 |
|
---|
205 | /* CENTRY */
|
---|
206 | void GLAPIENTRY
|
---|
207 | glutPostRedisplay(void)
|
---|
208 | {
|
---|
209 | __glutPostRedisplay(__glutCurrentWindow, GLUT_REDISPLAY_WORK);
|
---|
210 | }
|
---|
211 |
|
---|
212 | /* The advantage of this routine is that it saves the cost of a
|
---|
213 | glutSetWindow call (entailing an expensive OpenGL context switch),
|
---|
214 | particularly useful when multiple windows need redisplays posted at
|
---|
215 | the same times. See also glutPostWindowOverlayRedisplay. */
|
---|
216 | void GLAPIENTRY
|
---|
217 | glutPostWindowRedisplay(int win)
|
---|
218 | {
|
---|
219 | __glutPostRedisplay(__glutWindowList[win - 1], GLUT_REDISPLAY_WORK);
|
---|
220 | }
|
---|
221 |
|
---|
222 | /* ENDCENTRY */
|
---|
223 | static GLUTeventParser *eventParserList = NULL;
|
---|
224 |
|
---|
225 | /* __glutRegisterEventParser allows another module to register
|
---|
226 | to intercept X events types not otherwise acted on by the
|
---|
227 | GLUT processEventsAndTimeouts routine. The X Input
|
---|
228 | extension support code uses an event parser for handling X
|
---|
229 | Input extension events. */
|
---|
230 |
|
---|
231 | void
|
---|
232 | __glutRegisterEventParser(GLUTeventParser * parser)
|
---|
233 | {
|
---|
234 | parser->next = eventParserList;
|
---|
235 | eventParserList = parser;
|
---|
236 | }
|
---|
237 |
|
---|
238 | static void
|
---|
239 | markWindowHidden(GLUTwindow * window)
|
---|
240 | {
|
---|
241 | if (GLUT_HIDDEN != window->visState) {
|
---|
242 | GLUTwindow *child;
|
---|
243 |
|
---|
244 | if (window->windowStatus) {
|
---|
245 | window->visState = GLUT_HIDDEN;
|
---|
246 | __glutSetWindow(window);
|
---|
247 | ((GLUTwindowStatusCB)(window->windowStatus))(GLUT_HIDDEN);
|
---|
248 | }
|
---|
249 | /* An unmap is only reported on a single window; its
|
---|
250 | descendents need to know they are no longer visible. */
|
---|
251 | child = window->children;
|
---|
252 | while (child) {
|
---|
253 | markWindowHidden(child);
|
---|
254 | child = child->siblings;
|
---|
255 | }
|
---|
256 | }
|
---|
257 | }
|
---|
258 |
|
---|
259 | #if !defined(_WIN32) && !defined(__WIN32OS2__)
|
---|
260 |
|
---|
261 | static void
|
---|
262 | purgeStaleWindow(Window win)
|
---|
263 | {
|
---|
264 | GLUTstale **pEntry = &__glutStaleWindowList;
|
---|
265 | GLUTstale *entry = __glutStaleWindowList;
|
---|
266 |
|
---|
267 | /* Tranverse singly-linked stale window list look for the
|
---|
268 | window ID. */
|
---|
269 | while (entry) {
|
---|
270 | if (entry->win == win) {
|
---|
271 | /* Found it; delete it. */
|
---|
272 | *pEntry = entry->next;
|
---|
273 | free(entry);
|
---|
274 | return;
|
---|
275 | } else {
|
---|
276 | pEntry = &entry->next;
|
---|
277 | entry = *pEntry;
|
---|
278 | }
|
---|
279 | }
|
---|
280 | }
|
---|
281 |
|
---|
282 | /* Unlike XNextEvent, if a signal arrives,
|
---|
283 | interruptibleXNextEvent will return (with a zero return
|
---|
284 | value). This helps GLUT drop out of XNextEvent if a signal
|
---|
285 | is delivered. The intent is so that a GLUT program can call
|
---|
286 | glutIdleFunc in a signal handler to register an idle func
|
---|
287 | and then immediately get dropped into the idle func (after
|
---|
288 | returning from the signal handler). The idea is to make
|
---|
289 | GLUT's main loop reliably interruptible by signals. */
|
---|
290 | static int
|
---|
291 | interruptibleXNextEvent(Display * dpy, XEvent * event)
|
---|
292 | {
|
---|
293 | fd_set fds;
|
---|
294 | int rc;
|
---|
295 |
|
---|
296 | /* Flush X protocol since XPending does not do this
|
---|
297 | implicitly. */
|
---|
298 | XFlush(__glutDisplay);
|
---|
299 | for (;;) {
|
---|
300 | if (XPending(__glutDisplay)) {
|
---|
301 | XNextEvent(dpy, event);
|
---|
302 | return 1;
|
---|
303 | }
|
---|
304 | FD_ZERO(&fds);
|
---|
305 | FD_SET(__glutConnectionFD, &fds);
|
---|
306 | rc = select(__glutConnectionFD + 1, &fds,
|
---|
307 | NULL, NULL, NULL);
|
---|
308 | if (rc < 0) {
|
---|
309 | if (errno == EINTR) {
|
---|
310 | return 0;
|
---|
311 | } else {
|
---|
312 | __glutFatalError("select error.");
|
---|
313 | }
|
---|
314 | }
|
---|
315 | }
|
---|
316 | }
|
---|
317 |
|
---|
318 | #endif
|
---|
319 |
|
---|
320 | static void
|
---|
321 | processEventsAndTimeouts(void)
|
---|
322 | {
|
---|
323 | do {
|
---|
324 | #if defined(_WIN32) || defined(__WIN32OS2__)
|
---|
325 | MSG event;
|
---|
326 |
|
---|
327 | if(!GetMessage(&event, NULL, 0, 0)) /* bail if no more messages*/
|
---|
328 | exit(0);
|
---|
329 | TranslateMessage(&event); /* translate virtual-key messages*/
|
---|
330 | DispatchMessage(&event); /* call the window proc*/
|
---|
331 | /* see win32_event.c for event (message) processing procedures */
|
---|
332 | #else
|
---|
333 | static int mappedMenuButton;
|
---|
334 | GLUTeventParser *parser;
|
---|
335 | XEvent event, ahead;
|
---|
336 | GLUTwindow *window;
|
---|
337 | GLUTkeyboardCB keyboard;
|
---|
338 | GLUTspecialCB special;
|
---|
339 | int gotEvent, width, height;
|
---|
340 |
|
---|
341 | gotEvent = interruptibleXNextEvent(__glutDisplay, &event);
|
---|
342 | if (gotEvent) {
|
---|
343 | switch (event.type) {
|
---|
344 | case MappingNotify:
|
---|
345 | XRefreshKeyboardMapping((XMappingEvent *) & event);
|
---|
346 | break;
|
---|
347 | case ConfigureNotify:
|
---|
348 | window = __glutGetWindow(event.xconfigure.window);
|
---|
349 | if (window) {
|
---|
350 | if (window->win != event.xconfigure.window) {
|
---|
351 | /* Ignore ConfigureNotify sent to the overlay
|
---|
352 | planes. GLUT could get here because overlays
|
---|
353 | select for StructureNotify events to receive
|
---|
354 | DestroyNotify. */
|
---|
355 | break;
|
---|
356 | }
|
---|
357 | width = event.xconfigure.width;
|
---|
358 | height = event.xconfigure.height;
|
---|
359 | if (width != window->width || height != window->height) {
|
---|
360 | if (window->overlay) {
|
---|
361 | XResizeWindow(__glutDisplay, window->overlay->win, width, height);
|
---|
362 | }
|
---|
363 | window->width = width;
|
---|
364 | window->height = height;
|
---|
365 | __glutSetWindow(window);
|
---|
366 | /* Do not execute OpenGL out of sequence with
|
---|
367 | respect to the XResizeWindow request! */
|
---|
368 | glXWaitX();
|
---|
369 | ((GLUTreshapeCB)(window->reshape))(width, height);
|
---|
370 | window->forceReshape = False;
|
---|
371 | /* A reshape should be considered like posting a
|
---|
372 | repair; this is necessary for the "Mesa
|
---|
373 | glXSwapBuffers to repair damage" hack to operate
|
---|
374 | correctly. Without it, there's not an initial
|
---|
375 | back buffer render from which to blit from when
|
---|
376 | damage happens to the window. */
|
---|
377 | __glutPostRedisplay(window, GLUT_REPAIR_WORK);
|
---|
378 | }
|
---|
379 | }
|
---|
380 | break;
|
---|
381 | case Expose:
|
---|
382 | /* compress expose events */
|
---|
383 | while (XEventsQueued(__glutDisplay, QueuedAfterReading)
|
---|
384 | > 0) {
|
---|
385 | XPeekEvent(__glutDisplay, &ahead);
|
---|
386 | if (ahead.type != Expose ||
|
---|
387 | ahead.xexpose.window != event.xexpose.window) {
|
---|
388 | break;
|
---|
389 | }
|
---|
390 | XNextEvent(__glutDisplay, &event);
|
---|
391 | }
|
---|
392 | if (event.xexpose.count == 0) {
|
---|
393 | GLUTmenu *menu;
|
---|
394 |
|
---|
395 | if (__glutMappedMenu &&
|
---|
396 | (menu = __glutGetMenu(event.xexpose.window))) {
|
---|
397 | __glutPaintMenu(menu);
|
---|
398 | } else {
|
---|
399 | window = __glutGetWindow(event.xexpose.window);
|
---|
400 | if (window) {
|
---|
401 | if (window->win == event.xexpose.window) {
|
---|
402 | __glutPostRedisplay(window, GLUT_REPAIR_WORK);
|
---|
403 | } else if (window->overlay && window->overlay->win == event.xexpose.window) {
|
---|
404 | __glutPostRedisplay(window, GLUT_OVERLAY_REPAIR_WORK);
|
---|
405 | }
|
---|
406 | }
|
---|
407 | }
|
---|
408 | } else {
|
---|
409 | /* there are more exposes to read; wait to redisplay */
|
---|
410 | }
|
---|
411 | break;
|
---|
412 | case ButtonPress:
|
---|
413 | case ButtonRelease:
|
---|
414 | if (__glutMappedMenu && event.type == ButtonRelease
|
---|
415 | && mappedMenuButton == event.xbutton.button) {
|
---|
416 | /* Menu is currently popped up and its button is
|
---|
417 | released. */
|
---|
418 | __glutFinishMenu(event.xbutton.window, event.xbutton.x, event.xbutton.y);
|
---|
419 | } else {
|
---|
420 | window = __glutGetWindow(event.xbutton.window);
|
---|
421 | if (window) {
|
---|
422 | GLUTmenu *menu;
|
---|
423 | int menuNum;
|
---|
424 |
|
---|
425 | menuNum = window->menu[event.xbutton.button - 1];
|
---|
426 | /* Make sure that __glutGetMenuByNum is only called if there
|
---|
427 | really is a menu present. */
|
---|
428 | if ((menuNum > 0) && (menu = __glutGetMenuByNum(menuNum))) {
|
---|
429 | if (event.type == ButtonPress && !__glutMappedMenu) {
|
---|
430 | __glutStartMenu(menu, window,
|
---|
431 | event.xbutton.x_root, event.xbutton.y_root,
|
---|
432 | event.xbutton.x, event.xbutton.y);
|
---|
433 | mappedMenuButton = event.xbutton.button;
|
---|
434 | } else {
|
---|
435 | /* Ignore a release of a button with a menu
|
---|
436 | attatched to it when no menu is popped up,
|
---|
437 | or ignore a press when another menu is
|
---|
438 | already popped up. */
|
---|
439 | }
|
---|
440 | } else if (window->mouse) {
|
---|
441 | __glutSetWindow(window);
|
---|
442 | __glutModifierMask = event.xbutton.state;
|
---|
443 | ((GLUTmouseCB)(window->mouse))(event.xbutton.button - 1,
|
---|
444 | event.type == ButtonRelease ?
|
---|
445 | GLUT_UP : GLUT_DOWN,
|
---|
446 | event.xbutton.x, event.xbutton.y);
|
---|
447 | __glutModifierMask = ~0;
|
---|
448 | } else {
|
---|
449 | /* Stray mouse events. Ignore. */
|
---|
450 | }
|
---|
451 | } else {
|
---|
452 | /* Window might have been destroyed and all the
|
---|
453 | events for the window may not yet be received. */
|
---|
454 | }
|
---|
455 | }
|
---|
456 | break;
|
---|
457 | case MotionNotify:
|
---|
458 | if (!__glutMappedMenu) {
|
---|
459 | window = __glutGetWindow(event.xmotion.window);
|
---|
460 | if (window) {
|
---|
461 | /* If motion function registered _and_ buttons held
|
---|
462 | * down, call motion function... */
|
---|
463 | if (window->motion && event.xmotion.state &
|
---|
464 | (Button1Mask | Button2Mask | Button3Mask)) {
|
---|
465 | __glutSetWindow(window);
|
---|
466 | ((GLUTmotionCB)(window->motion))(event.xmotion.x, event.xmotion.y);
|
---|
467 | }
|
---|
468 | /* If passive motion function registered _and_
|
---|
469 | buttons not held down, call passive motion
|
---|
470 | function... */
|
---|
471 | else if (window->passive &&
|
---|
472 | ((event.xmotion.state &
|
---|
473 | (Button1Mask | Button2Mask | Button3Mask)) ==
|
---|
474 | 0)) {
|
---|
475 | __glutSetWindow(window);
|
---|
476 | ((GLUTpassiveCB)(window->passive))(event.xmotion.x,
|
---|
477 | event.xmotion.y);
|
---|
478 | }
|
---|
479 | }
|
---|
480 | } else {
|
---|
481 | /* Motion events are thrown away when a pop up menu
|
---|
482 | is active. */
|
---|
483 | }
|
---|
484 | break;
|
---|
485 | case KeyPress:
|
---|
486 | case KeyRelease:
|
---|
487 | window = __glutGetWindow(event.xkey.window);
|
---|
488 | if (!window) {
|
---|
489 | break;
|
---|
490 | }
|
---|
491 | if (event.type == KeyPress) {
|
---|
492 | keyboard = window->keyboard;
|
---|
493 | } else {
|
---|
494 |
|
---|
495 | /* If we are ignoring auto repeated keys for this window,
|
---|
496 | check if the next event in the X event queue is a KeyPress
|
---|
497 | for the exact same key (and at the exact same time) as the
|
---|
498 | key being released. The X11 protocol will send auto
|
---|
499 | repeated keys as such KeyRelease/KeyPress pairs. */
|
---|
500 |
|
---|
501 | if (window->ignoreKeyRepeat) {
|
---|
502 | if (XEventsQueued(__glutDisplay, QueuedAfterReading)) {
|
---|
503 | XPeekEvent(__glutDisplay, &ahead);
|
---|
504 | if (ahead.type == KeyPress
|
---|
505 | && ahead.xkey.window == event.xkey.window
|
---|
506 | && ahead.xkey.keycode == event.xkey.keycode
|
---|
507 | && ahead.xkey.time == event.xkey.time) {
|
---|
508 | /* Pop off the repeated KeyPress and ignore
|
---|
509 | the auto repeated KeyRelease/KeyPress pair. */
|
---|
510 | XNextEvent(__glutDisplay, &event);
|
---|
511 | break;
|
---|
512 | }
|
---|
513 | }
|
---|
514 | }
|
---|
515 | keyboard = window->keyboardUp;
|
---|
516 | }
|
---|
517 | if (keyboard) {
|
---|
518 | char tmp[1];
|
---|
519 | int rc;
|
---|
520 |
|
---|
521 | rc = XLookupString(&event.xkey, tmp, sizeof(tmp),
|
---|
522 | NULL, NULL);
|
---|
523 | if (rc) {
|
---|
524 | __glutSetWindow(window);
|
---|
525 | __glutModifierMask = event.xkey.state;
|
---|
526 | ((GLUTkeyboardCB)(keyboard))(tmp[0],
|
---|
527 | event.xkey.x, event.xkey.y);
|
---|
528 | __glutModifierMask = ~0;
|
---|
529 | break;
|
---|
530 | }
|
---|
531 | }
|
---|
532 | if (event.type == KeyPress) {
|
---|
533 | special = window->special;
|
---|
534 | } else {
|
---|
535 | special = window->specialUp;
|
---|
536 | }
|
---|
537 | if (special) {
|
---|
538 | KeySym ks;
|
---|
539 | int key;
|
---|
540 |
|
---|
541 | /* Introduced in X11R6: (Partial list of) Keypad Functions. Define
|
---|
542 | in place in case compiling against an older pre-X11R6
|
---|
543 | X11/keysymdef.h file. */
|
---|
544 | #ifndef XK_KP_Home
|
---|
545 | #define XK_KP_Home 0xFF95
|
---|
546 | #endif
|
---|
547 | #ifndef XK_KP_Left
|
---|
548 | #define XK_KP_Left 0xFF96
|
---|
549 | #endif
|
---|
550 | #ifndef XK_KP_Up
|
---|
551 | #define XK_KP_Up 0xFF97
|
---|
552 | #endif
|
---|
553 | #ifndef XK_KP_Right
|
---|
554 | #define XK_KP_Right 0xFF98
|
---|
555 | #endif
|
---|
556 | #ifndef XK_KP_Down
|
---|
557 | #define XK_KP_Down 0xFF99
|
---|
558 | #endif
|
---|
559 | #ifndef XK_KP_Prior
|
---|
560 | #define XK_KP_Prior 0xFF9A
|
---|
561 | #endif
|
---|
562 | #ifndef XK_KP_Next
|
---|
563 | #define XK_KP_Next 0xFF9B
|
---|
564 | #endif
|
---|
565 | #ifndef XK_KP_End
|
---|
566 | #define XK_KP_End 0xFF9C
|
---|
567 | #endif
|
---|
568 | #ifndef XK_KP_Insert
|
---|
569 | #define XK_KP_Insert 0xFF9E
|
---|
570 | #endif
|
---|
571 | #ifndef XK_KP_Delete
|
---|
572 | #define XK_KP_Delete 0xFF9F
|
---|
573 | #endif
|
---|
574 |
|
---|
575 | ks = XLookupKeysym((XKeyEvent *) & event, 0);
|
---|
576 | /* XXX Verbose, but makes no assumptions about keysym
|
---|
577 | layout. */
|
---|
578 | switch (ks) {
|
---|
579 | /* *INDENT-OFF* */
|
---|
580 | /* function keys */
|
---|
581 | case XK_F1: key = GLUT_KEY_F1; break;
|
---|
582 | case XK_F2: key = GLUT_KEY_F2; break;
|
---|
583 | case XK_F3: key = GLUT_KEY_F3; break;
|
---|
584 | case XK_F4: key = GLUT_KEY_F4; break;
|
---|
585 | case XK_F5: key = GLUT_KEY_F5; break;
|
---|
586 | case XK_F6: key = GLUT_KEY_F6; break;
|
---|
587 | case XK_F7: key = GLUT_KEY_F7; break;
|
---|
588 | case XK_F8: key = GLUT_KEY_F8; break;
|
---|
589 | case XK_F9: key = GLUT_KEY_F9; break;
|
---|
590 | case XK_F10: key = GLUT_KEY_F10; break;
|
---|
591 | case XK_F11: key = GLUT_KEY_F11; break;
|
---|
592 | case XK_F12: key = GLUT_KEY_F12; break;
|
---|
593 | /* directional keys */
|
---|
594 | case XK_KP_Left:
|
---|
595 | case XK_Left: key = GLUT_KEY_LEFT; break;
|
---|
596 | case XK_KP_Up: /* Introduced in X11R6. */
|
---|
597 | case XK_Up: key = GLUT_KEY_UP; break;
|
---|
598 | case XK_KP_Right: /* Introduced in X11R6. */
|
---|
599 | case XK_Right: key = GLUT_KEY_RIGHT; break;
|
---|
600 | case XK_KP_Down: /* Introduced in X11R6. */
|
---|
601 | case XK_Down: key = GLUT_KEY_DOWN; break;
|
---|
602 | /* *INDENT-ON* */
|
---|
603 |
|
---|
604 | case XK_KP_Prior: /* Introduced in X11R6. */
|
---|
605 | case XK_Prior:
|
---|
606 | /* XK_Prior same as X11R6's XK_Page_Up */
|
---|
607 | key = GLUT_KEY_PAGE_UP;
|
---|
608 | break;
|
---|
609 | case XK_KP_Next: /* Introduced in X11R6. */
|
---|
610 | case XK_Next:
|
---|
611 | /* XK_Next same as X11R6's XK_Page_Down */
|
---|
612 | key = GLUT_KEY_PAGE_DOWN;
|
---|
613 | break;
|
---|
614 | case XK_KP_Home: /* Introduced in X11R6. */
|
---|
615 | case XK_Home:
|
---|
616 | key = GLUT_KEY_HOME;
|
---|
617 | break;
|
---|
618 | #ifdef __hpux
|
---|
619 | case XK_Select:
|
---|
620 | #endif
|
---|
621 | case XK_KP_End: /* Introduced in X11R6. */
|
---|
622 | case XK_End:
|
---|
623 | key = GLUT_KEY_END;
|
---|
624 | break;
|
---|
625 | #ifdef __hpux
|
---|
626 | case XK_InsertChar:
|
---|
627 | #endif
|
---|
628 | case XK_KP_Insert: /* Introduced in X11R6. */
|
---|
629 | case XK_Insert:
|
---|
630 | key = GLUT_KEY_INSERT;
|
---|
631 | break;
|
---|
632 | #ifdef __hpux
|
---|
633 | case XK_DeleteChar:
|
---|
634 | #endif
|
---|
635 | case XK_KP_Delete: /* Introduced in X11R6. */
|
---|
636 | /* The Delete character is really an ASCII key. */
|
---|
637 | __glutSetWindow(window);
|
---|
638 | ((GLUTkeyboardCB)(keyboard))(127, /* ASCII Delete character. */
|
---|
639 | event.xkey.x, event.xkey.y);
|
---|
640 | goto skip;
|
---|
641 | default:
|
---|
642 | goto skip;
|
---|
643 | }
|
---|
644 | __glutSetWindow(window);
|
---|
645 | __glutModifierMask = event.xkey.state;
|
---|
646 | special(key, event.xkey.x, event.xkey.y);
|
---|
647 | __glutModifierMask = ~0;
|
---|
648 | skip:;
|
---|
649 | }
|
---|
650 | break;
|
---|
651 | case EnterNotify:
|
---|
652 | case LeaveNotify:
|
---|
653 | if (event.xcrossing.mode != NotifyNormal ||
|
---|
654 | event.xcrossing.detail == NotifyNonlinearVirtual ||
|
---|
655 | event.xcrossing.detail == NotifyVirtual) {
|
---|
656 |
|
---|
657 | /* Careful to ignore Enter/LeaveNotify events that
|
---|
658 | come from the pop-up menu pointer grab and ungrab.
|
---|
659 | Also, ignore "virtual" Enter/LeaveNotify events
|
---|
660 | since they represent the pointer passing through
|
---|
661 | the window hierarchy without actually entering or
|
---|
662 | leaving the actual real estate of a window. */
|
---|
663 |
|
---|
664 | break;
|
---|
665 | }
|
---|
666 | if (__glutMappedMenu) {
|
---|
667 | GLUTmenuItem *item;
|
---|
668 | int num;
|
---|
669 |
|
---|
670 | item = __glutGetMenuItem(__glutMappedMenu,
|
---|
671 | event.xcrossing.window, &num);
|
---|
672 | if (item) {
|
---|
673 | __glutMenuItemEnterOrLeave(item, num, event.type);
|
---|
674 | break;
|
---|
675 | }
|
---|
676 | }
|
---|
677 | window = __glutGetWindow(event.xcrossing.window);
|
---|
678 | if (window) {
|
---|
679 | if (window->entry) {
|
---|
680 | if (event.type == EnterNotify) {
|
---|
681 |
|
---|
682 | /* With overlays established, X can report two
|
---|
683 | enter events for both the overlay and normal
|
---|
684 | plane window. Do not generate a second enter
|
---|
685 | callback if we reported one without an
|
---|
686 | intervening leave. */
|
---|
687 |
|
---|
688 | if (window->entryState != EnterNotify) {
|
---|
689 | int num = window->num;
|
---|
690 | Window xid = window->win;
|
---|
691 |
|
---|
692 | window->entryState = EnterNotify;
|
---|
693 | __glutSetWindow(window);
|
---|
694 | ((GLUTentryCB)(window->entry))(GLUT_ENTERED);
|
---|
695 |
|
---|
696 | if (__glutMappedMenu) {
|
---|
697 |
|
---|
698 | /* Do not generate any passive motion events
|
---|
699 | when menus are in use. */
|
---|
700 |
|
---|
701 | } else {
|
---|
702 |
|
---|
703 | /* An EnterNotify event can result in a
|
---|
704 | "compound" callback if a passive motion
|
---|
705 | callback is also registered. In this case,
|
---|
706 | be a little paranoid about the possibility
|
---|
707 | the window could have been destroyed in the
|
---|
708 | entry callback. */
|
---|
709 |
|
---|
710 | window = __glutWindowList[num];
|
---|
711 | if (window && window->passive && window->win == xid) {
|
---|
712 | __glutSetWindow(window);
|
---|
713 | ((GLUTpassiveCB)(window->passive))(event.xcrossing.x, event.xcrossing.y);
|
---|
714 | }
|
---|
715 | }
|
---|
716 | }
|
---|
717 | } else {
|
---|
718 | if (window->entryState != LeaveNotify) {
|
---|
719 |
|
---|
720 | /* When an overlay is established for a window
|
---|
721 | already mapped and with the pointer in it,
|
---|
722 | the X server will generate a leave/enter
|
---|
723 | event pair as the pointer leaves (without
|
---|
724 | moving) from the normal plane X window to
|
---|
725 | the newly mapped overlay X window (or vice
|
---|
726 | versa). This enter/leave pair should not be
|
---|
727 | reported to the GLUT program since the pair
|
---|
728 | is a consequence of creating (or destroying)
|
---|
729 | the overlay, not an actual leave from the
|
---|
730 | GLUT window. */
|
---|
731 |
|
---|
732 | if (XEventsQueued(__glutDisplay, QueuedAfterReading)) {
|
---|
733 | XPeekEvent(__glutDisplay, &ahead);
|
---|
734 | if (ahead.type == EnterNotify &&
|
---|
735 | __glutGetWindow(ahead.xcrossing.window) == window) {
|
---|
736 | XNextEvent(__glutDisplay, &event);
|
---|
737 | break;
|
---|
738 | }
|
---|
739 | }
|
---|
740 | window->entryState = LeaveNotify;
|
---|
741 | __glutSetWindow(window);
|
---|
742 | ((GLUTentryCB)(window->entry))(GLUT_LEFT);
|
---|
743 | }
|
---|
744 | }
|
---|
745 | } else if (window->passive) {
|
---|
746 | __glutSetWindow(window);
|
---|
747 | ((GLUTpassiveCB)(window->passive))(event.xcrossing.x, event.xcrossing.y);
|
---|
748 | }
|
---|
749 | }
|
---|
750 | break;
|
---|
751 | case UnmapNotify:
|
---|
752 | /* MapNotify events are not needed to maintain
|
---|
753 | visibility state since VisibilityNotify events will
|
---|
754 | be delivered when a window becomes visible from
|
---|
755 | mapping. However, VisibilityNotify events are not
|
---|
756 | delivered when a window is unmapped (for the window
|
---|
757 | or its children). */
|
---|
758 | window = __glutGetWindow(event.xunmap.window);
|
---|
759 | if (window) {
|
---|
760 | if (window->win != event.xconfigure.window) {
|
---|
761 | /* Ignore UnmapNotify sent to the overlay planes.
|
---|
762 | GLUT could get here because overlays select for
|
---|
763 | StructureNotify events to receive DestroyNotify.
|
---|
764 | */
|
---|
765 | break;
|
---|
766 | }
|
---|
767 | markWindowHidden(window);
|
---|
768 | }
|
---|
769 | break;
|
---|
770 | case VisibilityNotify:
|
---|
771 | window = __glutGetWindow(event.xvisibility.window);
|
---|
772 | if (window) {
|
---|
773 | /* VisibilityUnobscured+1 = GLUT_FULLY_RETAINED,
|
---|
774 | VisibilityPartiallyObscured+1 =
|
---|
775 | GLUT_PARTIALLY_RETAINED, VisibilityFullyObscured+1
|
---|
776 | = GLUT_FULLY_COVERED. */
|
---|
777 | int visState = event.xvisibility.state + 1;
|
---|
778 |
|
---|
779 | if (visState != window->visState) {
|
---|
780 | if (window->windowStatus) {
|
---|
781 | window->visState = visState;
|
---|
782 | __glutSetWindow(window);
|
---|
783 | ((GLUTwindowStatusCB)(window->windowStatus))(visState);
|
---|
784 | }
|
---|
785 | }
|
---|
786 | }
|
---|
787 | break;
|
---|
788 | case ClientMessage:
|
---|
789 | if (event.xclient.data.l[0] == __glutWMDeleteWindow)
|
---|
790 | exit(0);
|
---|
791 | break;
|
---|
792 | case DestroyNotify:
|
---|
793 | purgeStaleWindow(event.xdestroywindow.window);
|
---|
794 | break;
|
---|
795 | case CirculateNotify:
|
---|
796 | case CreateNotify:
|
---|
797 | case GravityNotify:
|
---|
798 | case ReparentNotify:
|
---|
799 | /* Uninteresting to GLUT (but possible for GLUT to
|
---|
800 | receive). */
|
---|
801 | break;
|
---|
802 | default:
|
---|
803 | /* Pass events not directly handled by the GLUT main
|
---|
804 | event loop to any event parsers that have been
|
---|
805 | registered. In this way, X Input extension events
|
---|
806 | are passed to the correct handler without forcing
|
---|
807 | all GLUT programs to support X Input event handling.
|
---|
808 | */
|
---|
809 | parser = eventParserList;
|
---|
810 | while (parser) {
|
---|
811 | if (parser->func(&event))
|
---|
812 | break;
|
---|
813 | parser = parser->next;
|
---|
814 | }
|
---|
815 | break;
|
---|
816 | }
|
---|
817 | }
|
---|
818 | #endif /* _WIN32 */
|
---|
819 | if (__glutTimerList) {
|
---|
820 | handleTimeouts();
|
---|
821 | }
|
---|
822 | }
|
---|
823 | while (XPending(__glutDisplay));
|
---|
824 | }
|
---|
825 |
|
---|
826 | static void
|
---|
827 | waitForSomething(void)
|
---|
828 | {
|
---|
829 | #if defined(__vms) && ( __VMS_VER < 70000000 )
|
---|
830 | static struct timeval zerotime =
|
---|
831 | {0};
|
---|
832 | unsigned int timer_efn;
|
---|
833 | #define timer_id 'glut' /* random :-) number */
|
---|
834 | unsigned int wait_mask;
|
---|
835 | #else
|
---|
836 | static struct timeval zerotime =
|
---|
837 | {0, 0};
|
---|
838 | #if !defined(_WIN32) && !defined(__WIN32OS2__)
|
---|
839 | fd_set fds;
|
---|
840 | #endif
|
---|
841 | #endif
|
---|
842 | struct timeval now, timeout, waittime;
|
---|
843 | #if !defined(_WIN32) && !defined(__WIN32OS2__)
|
---|
844 | int rc;
|
---|
845 | #endif
|
---|
846 |
|
---|
847 | /* Flush X protocol since XPending does not do this
|
---|
848 | implicitly. */
|
---|
849 | XFlush(__glutDisplay);
|
---|
850 | if (XPending(__glutDisplay)) {
|
---|
851 | /* It is possible (but quite rare) that XFlush may have
|
---|
852 | needed to wait for a writable X connection file
|
---|
853 | descriptor, and in the process, may have had to read off
|
---|
854 | X protocol from the file descriptor. If XPending is true,
|
---|
855 | this case occured and we should avoid waiting in select
|
---|
856 | since X protocol buffered within Xlib is due to be
|
---|
857 | processed and potentially no more X protocol is on the
|
---|
858 | file descriptor, so we would risk waiting improperly in
|
---|
859 | select. */
|
---|
860 | goto immediatelyHandleXinput;
|
---|
861 | }
|
---|
862 | #if defined(__vms) && ( __VMS_VER < 70000000 )
|
---|
863 | timeout = __glutTimerList->timeout;
|
---|
864 | GETTIMEOFDAY(&now);
|
---|
865 | wait_mask = 1 << (__glutConnectionFD & 31);
|
---|
866 | if (IS_AFTER(now, timeout)) {
|
---|
867 | /* We need an event flag for the timer. */
|
---|
868 | /* XXX The `right' way to do this is to use LIB$GET_EF, but
|
---|
869 | since it needs to be in the same cluster as the EFN for
|
---|
870 | the display, we will have hack it. */
|
---|
871 | timer_efn = __glutConnectionFD - 1;
|
---|
872 | if ((timer_efn / 32) != (__glutConnectionFD / 32)) {
|
---|
873 | timer_efn = __glutConnectionFD + 1;
|
---|
874 | }
|
---|
875 | rc = SYS$CLREF(timer_efn);
|
---|
876 | rc = SYS$SETIMR(timer_efn, &timeout, NULL, timer_id, 0);
|
---|
877 | wait_mask |= 1 << (timer_efn & 31);
|
---|
878 | } else {
|
---|
879 | timer_efn = 0;
|
---|
880 | }
|
---|
881 | rc = SYS$WFLOR(__glutConnectionFD, wait_mask);
|
---|
882 | if (timer_efn != 0 && SYS$CLREF(timer_efn) == SS$_WASCLR) {
|
---|
883 | rc = SYS$CANTIM(timer_id, PSL$C_USER);
|
---|
884 | }
|
---|
885 | /* XXX There does not seem to be checking of "rc" in the code
|
---|
886 | above. Can any of the SYS$ routines above fail? */
|
---|
887 | #else /* not vms6.2 or lower */
|
---|
888 | #if !defined(_WIN32) && !defined(__WIN32OS2__)
|
---|
889 | FD_ZERO(&fds);
|
---|
890 | FD_SET(__glutConnectionFD, &fds);
|
---|
891 | #endif
|
---|
892 | timeout = __glutTimerList->timeout;
|
---|
893 | GETTIMEOFDAY(&now);
|
---|
894 | if (IS_AFTER(now, timeout)) {
|
---|
895 | TIMEDELTA(waittime, timeout, now);
|
---|
896 | } else {
|
---|
897 | waittime = zerotime;
|
---|
898 | }
|
---|
899 | #if !defined(_WIN32) && !defined(__WIN32OS2__)
|
---|
900 | rc = select(__glutConnectionFD + 1, &fds,
|
---|
901 | NULL, NULL, &waittime);
|
---|
902 | if (rc < 0 && errno != EINTR)
|
---|
903 | __glutFatalError("select error.");
|
---|
904 | #else
|
---|
905 | #if 0 /* XXX Nate, what is this junk? */
|
---|
906 | /* Set up a timer to fire in at least a millisecond, then wait for
|
---|
907 | the message. This should act like a select. */
|
---|
908 | SetTimer(NULL, 2, waittime.tv_usec, NULL);
|
---|
909 | WaitMessage();
|
---|
910 | KillTimer(NULL, 2);
|
---|
911 | #endif
|
---|
912 |
|
---|
913 | /* Actually, a sleep seems to do the trick -- do we even need this? */
|
---|
914 | Sleep(0);
|
---|
915 | #endif
|
---|
916 | #endif /* not vms6.2 or lower */
|
---|
917 | /* Without considering the cause of select unblocking, check
|
---|
918 | for pending X events and handle any timeouts (by calling
|
---|
919 | processEventsAndTimeouts). We always look for X events
|
---|
920 | even if select returned with 0 (indicating a timeout);
|
---|
921 | otherwise we risk starving X event processing by continous
|
---|
922 | timeouts. */
|
---|
923 | if (XPending(__glutDisplay)) {
|
---|
924 | immediatelyHandleXinput:
|
---|
925 | processEventsAndTimeouts();
|
---|
926 | } else {
|
---|
927 | if (__glutTimerList)
|
---|
928 | handleTimeouts();
|
---|
929 | }
|
---|
930 | }
|
---|
931 |
|
---|
932 | static void
|
---|
933 | idleWait(void)
|
---|
934 | {
|
---|
935 | if (XPending(__glutDisplay)) {
|
---|
936 | processEventsAndTimeouts();
|
---|
937 | } else {
|
---|
938 | if (__glutTimerList) {
|
---|
939 | handleTimeouts();
|
---|
940 | }
|
---|
941 | }
|
---|
942 | /* Make sure idle func still exists! */
|
---|
943 | if (__glutIdleFunc) {
|
---|
944 | ((GLUTidleCB)(__glutIdleFunc))();
|
---|
945 | }
|
---|
946 | }
|
---|
947 |
|
---|
948 | static GLUTwindow **beforeEnd;
|
---|
949 |
|
---|
950 | static GLUTwindow *
|
---|
951 | processWindowWorkList(GLUTwindow * window)
|
---|
952 | {
|
---|
953 | int workMask;
|
---|
954 |
|
---|
955 | if (window->prevWorkWin) {
|
---|
956 | window->prevWorkWin = processWindowWorkList(window->prevWorkWin);
|
---|
957 | } else {
|
---|
958 | beforeEnd = &window->prevWorkWin;
|
---|
959 | }
|
---|
960 |
|
---|
961 | /* Capture work mask for work that needs to be done to this
|
---|
962 | window, then clear the window's work mask (excepting the
|
---|
963 | dummy work bit, see below). Then, process the captured
|
---|
964 | work mask. This allows callbacks in the processing the
|
---|
965 | captured work mask to set the window's work mask for
|
---|
966 | subsequent processing. */
|
---|
967 |
|
---|
968 | workMask = window->workMask;
|
---|
969 | assert((workMask & GLUT_DUMMY_WORK) == 0);
|
---|
970 |
|
---|
971 | /* Set the dummy work bit, clearing all other bits, to
|
---|
972 | indicate that the window is currently on the window work
|
---|
973 | list _and_ that the window's work mask is currently being
|
---|
974 | processed. This convinces __glutPutOnWorkList that this
|
---|
975 | window is on the work list still. */
|
---|
976 |
|
---|
977 | window->workMask = GLUT_DUMMY_WORK;
|
---|
978 |
|
---|
979 | /* Optimization: most of the time, the work to do is a
|
---|
980 | redisplay and not these other types of work. Check for
|
---|
981 | the following cases as a group to before checking each one
|
---|
982 | individually one by one. This saves about 25 MIPS
|
---|
983 | instructions in the common redisplay only case. */
|
---|
984 |
|
---|
985 | if (workMask & (GLUT_EVENT_MASK_WORK | GLUT_DEVICE_MASK_WORK |
|
---|
986 | GLUT_CONFIGURE_WORK | GLUT_COLORMAP_WORK | GLUT_MAP_WORK)) {
|
---|
987 |
|
---|
988 | #if !defined(_WIN32) && !defined(__WIN32OS2__)
|
---|
989 | /* Be sure to set event mask BEFORE map window is done. */
|
---|
990 | if (workMask & GLUT_EVENT_MASK_WORK) {
|
---|
991 | long eventMask;
|
---|
992 |
|
---|
993 | /* Make sure children are not propogating events this
|
---|
994 | window is selecting for. Be sure to do this before
|
---|
995 | enabling events on the children's parent. */
|
---|
996 | if (window->children) {
|
---|
997 | GLUTwindow *child = window->children;
|
---|
998 | unsigned long attribMask = CWDontPropagate;
|
---|
999 | XSetWindowAttributes wa;
|
---|
1000 |
|
---|
1001 | wa.do_not_propagate_mask = window->eventMask & GLUT_DONT_PROPAGATE_FILTER_MASK;
|
---|
1002 | if (window->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK) {
|
---|
1003 | wa.event_mask = child->eventMask | (window->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK);
|
---|
1004 | attribMask |= CWEventMask;
|
---|
1005 | }
|
---|
1006 | do {
|
---|
1007 | XChangeWindowAttributes(__glutDisplay, child->win,
|
---|
1008 | attribMask, &wa);
|
---|
1009 | child = child->siblings;
|
---|
1010 | } while (child);
|
---|
1011 | }
|
---|
1012 | eventMask = window->eventMask;
|
---|
1013 | if (window->parent && window->parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK)
|
---|
1014 | eventMask |= (window->parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK);
|
---|
1015 | XSelectInput(__glutDisplay, window->win, eventMask);
|
---|
1016 | if (window->overlay)
|
---|
1017 | XSelectInput(__glutDisplay, window->overlay->win,
|
---|
1018 | window->eventMask & GLUT_OVERLAY_EVENT_FILTER_MASK);
|
---|
1019 | }
|
---|
1020 | #endif /* !_WIN32 */
|
---|
1021 | /* Be sure to set device mask BEFORE map window is done. */
|
---|
1022 |
|
---|
1023 | if (workMask & GLUT_DEVICE_MASK_WORK) {
|
---|
1024 | __glutUpdateInputDeviceMaskFunc(window);
|
---|
1025 | }
|
---|
1026 | /* Be sure to configure window BEFORE map window is done. */
|
---|
1027 | if (workMask & GLUT_CONFIGURE_WORK) {
|
---|
1028 | #if defined(_WIN32) || defined(__WIN32OS2__)
|
---|
1029 | RECT changes;
|
---|
1030 | POINT point;
|
---|
1031 | UINT flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER
|
---|
1032 | | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER;
|
---|
1033 |
|
---|
1034 | GetClientRect(window->win, &changes);
|
---|
1035 |
|
---|
1036 | /* If this window is a toplevel window, translate the 0,0 client
|
---|
1037 | coordinate into a screen coordinate for proper placement. */
|
---|
1038 | if (!window->parent) {
|
---|
1039 | point.x = 0;
|
---|
1040 | point.y = 0;
|
---|
1041 | ClientToScreen(window->win, &point);
|
---|
1042 | changes.left = point.x;
|
---|
1043 | changes.top = point.y;
|
---|
1044 | }
|
---|
1045 | if (window->desiredConfMask & (CWX | CWY)) {
|
---|
1046 | changes.left = window->desiredX;
|
---|
1047 | changes.top = window->desiredY;
|
---|
1048 | flags &= ~SWP_NOMOVE;
|
---|
1049 | }
|
---|
1050 | if (window->desiredConfMask & (CWWidth | CWHeight)) {
|
---|
1051 | changes.right = changes.left + window->desiredWidth;
|
---|
1052 | changes.bottom = changes.top + window->desiredHeight;
|
---|
1053 | flags &= ~SWP_NOSIZE;
|
---|
1054 | /* XXX If overlay exists, resize the overlay here, ie.
|
---|
1055 | if (window->overlay) ... */
|
---|
1056 | }
|
---|
1057 | if (window->desiredConfMask & CWStackMode) {
|
---|
1058 | flags &= ~SWP_NOZORDER;
|
---|
1059 | /* XXX Overlay support might require something special here. */
|
---|
1060 | }
|
---|
1061 |
|
---|
1062 | /* Adjust the window rectangle because Win32 thinks that the x, y,
|
---|
1063 | width & height are the WHOLE window (including decorations),
|
---|
1064 | whereas GLUT treats the x, y, width & height as only the CLIENT
|
---|
1065 | area of the window. Only do this to top level windows
|
---|
1066 | that are not in game mode (since game mode windows do
|
---|
1067 | not have any decorations). */
|
---|
1068 | if (!window->parent && window != __glutGameModeWindow) {
|
---|
1069 | AdjustWindowRect(&changes,
|
---|
1070 | WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
|
---|
1071 | FALSE);
|
---|
1072 | }
|
---|
1073 |
|
---|
1074 | /* Do the repositioning, moving, and push/pop. */
|
---|
1075 |
|
---|
1076 | SetWindowPos(window->win,
|
---|
1077 | window->desiredStack == Above ? HWND_TOP : HWND_NOTOPMOST,
|
---|
1078 | changes.left, changes.top,
|
---|
1079 | changes.right - changes.left, changes.bottom - changes.top,
|
---|
1080 | flags & ~SWP_NOSENDCHANGING);
|
---|
1081 |
|
---|
1082 | /* Zero out the mask. */
|
---|
1083 | window->desiredConfMask = 0;
|
---|
1084 |
|
---|
1085 | /* This hack causes the window to go back to the right position
|
---|
1086 | when it is taken out of fullscreen mode. */
|
---|
1087 | if (workMask & GLUT_FULL_SCREEN_WORK) {
|
---|
1088 | window->desiredConfMask |= CWX | CWY;
|
---|
1089 | window->desiredX = point.x;
|
---|
1090 | window->desiredY = point.y;
|
---|
1091 | }
|
---|
1092 | #else /* !_WIN32 */
|
---|
1093 | XWindowChanges changes;
|
---|
1094 |
|
---|
1095 | changes.x = window->desiredX;
|
---|
1096 | changes.y = window->desiredY;
|
---|
1097 | if (window->desiredConfMask & (CWWidth | CWHeight)) {
|
---|
1098 | changes.width = window->desiredWidth;
|
---|
1099 | changes.height = window->desiredHeight;
|
---|
1100 | if (window->overlay)
|
---|
1101 | XResizeWindow(__glutDisplay, window->overlay->win,
|
---|
1102 | window->desiredWidth, window->desiredHeight);
|
---|
1103 | if (__glutMotifHints != None) {
|
---|
1104 | if (workMask & GLUT_FULL_SCREEN_WORK) {
|
---|
1105 | MotifWmHints hints;
|
---|
1106 |
|
---|
1107 | hints.flags = MWM_HINTS_DECORATIONS;
|
---|
1108 | hints.decorations = 0; /* Absolutely no*/
|
---|
1109 | decorations. */
|
---|
1110 | XChangeProperty(__glutDisplay, window->win,
|
---|
1111 | __glutMotifHints, __glutMotifHints, 32,
|
---|
1112 | PropModeReplace, (unsigned char *) &hints, 4);
|
---|
1113 | if (workMask & GLUT_MAP_WORK) {
|
---|
1114 | /* Handle case where glutFullScreen is called
|
---|
1115 | before the first time that the window is
|
---|
1116 | mapped. Some window managers will randomly or
|
---|
1117 | interactively position the window the first
|
---|
1118 | time it is mapped if the window's
|
---|
1119 | WM_NORMAL_HINTS property does not request an
|
---|
1120 | explicit position. We don't want any such
|
---|
1121 | window manager interaction when going
|
---|
1122 | fullscreen. Overwrite the WM_NORMAL_HINTS
|
---|
1123 | property installed by glutCreateWindow's
|
---|
1124 | XSetWMProperties property with one explicitly
|
---|
1125 | requesting a fullscreen window. */
|
---|
1126 | XSizeHints hints;
|
---|
1127 |
|
---|
1128 | hints.flags = USPosition | USSize;
|
---|
1129 | hints.x = 0;
|
---|
1130 | hints.y = 0;
|
---|
1131 | hints.width = window->desiredWidth;
|
---|
1132 | hints.height = window->desiredHeight;
|
---|
1133 | XSetWMNormalHints(__glutDisplay, window->win, &hints);
|
---|
1134 | }
|
---|
1135 | } else {
|
---|
1136 | XDeleteProperty(__glutDisplay, window->win, __glutMotifHints);
|
---|
1137 | }
|
---|
1138 | }
|
---|
1139 | }
|
---|
1140 | if (window->desiredConfMask & CWStackMode) {
|
---|
1141 | changes.stack_mode = window->desiredStack;
|
---|
1142 | /* Do not let glutPushWindow push window beneath the
|
---|
1143 | underlay. */
|
---|
1144 | if (window->parent && window->parent->overlay
|
---|
1145 | && window->desiredStack == Below) {
|
---|
1146 | changes.stack_mode = Above;
|
---|
1147 | changes.sibling = window->parent->overlay->win;
|
---|
1148 | window->desiredConfMask |= CWSibling;
|
---|
1149 | }
|
---|
1150 | }
|
---|
1151 | XConfigureWindow(__glutDisplay, window->win,
|
---|
1152 | window->desiredConfMask, &changes);
|
---|
1153 | window->desiredConfMask = 0;
|
---|
1154 | #endif
|
---|
1155 | }
|
---|
1156 |
|
---|
1157 | #if !defined(_WIN32) && !defined(__WIN32OS2__)
|
---|
1158 | /* Be sure to establish the colormaps BEFORE map window is
|
---|
1159 | done. */
|
---|
1160 | if (workMask & GLUT_COLORMAP_WORK) {
|
---|
1161 | __glutEstablishColormapsProperty(window);
|
---|
1162 | }
|
---|
1163 | #endif
|
---|
1164 | if (workMask & GLUT_MAP_WORK) {
|
---|
1165 | switch (window->desiredMapState) {
|
---|
1166 | case WithdrawnState:
|
---|
1167 | if (window->parent) {
|
---|
1168 | XUnmapWindow(__glutDisplay, window->win);
|
---|
1169 | } else {
|
---|
1170 | XWithdrawWindow(__glutDisplay, window->win,
|
---|
1171 | __glutScreen);
|
---|
1172 | }
|
---|
1173 | window->shownState = 0;
|
---|
1174 | break;
|
---|
1175 | case NormalState:
|
---|
1176 | XMapWindow(__glutDisplay, window->win);
|
---|
1177 | window->shownState = 1;
|
---|
1178 | break;
|
---|
1179 | #if defined(_WIN32) || defined(__WIN32OS2__)
|
---|
1180 | case GameModeState: /* Not an Xlib value.*/
|
---|
1181 | ShowWindow(window->win, SW_SHOW);
|
---|
1182 | window->shownState = 1;
|
---|
1183 | break;
|
---|
1184 | #endif
|
---|
1185 | case IconicState:
|
---|
1186 | XIconifyWindow(__glutDisplay, window->win, __glutScreen);
|
---|
1187 | window->shownState = 0;
|
---|
1188 | break;
|
---|
1189 | }
|
---|
1190 | }
|
---|
1191 | }
|
---|
1192 |
|
---|
1193 | if (workMask & (GLUT_REDISPLAY_WORK | GLUT_OVERLAY_REDISPLAY_WORK | GLUT_REPAIR_WORK | GLUT_OVERLAY_REPAIR_WORK)) {
|
---|
1194 | if (window->forceReshape) {
|
---|
1195 | /* Guarantee that before a display callback is generated
|
---|
1196 | for a window, a reshape callback must be generated. */
|
---|
1197 |
|
---|
1198 | __glutSetWindow(window);
|
---|
1199 |
|
---|
1200 | ((GLUTreshapeCB)(window->reshape))(window->width, window->height);
|
---|
1201 |
|
---|
1202 | window->forceReshape = False;
|
---|
1203 |
|
---|
1204 | /* Setting the redisplay bit on the first reshape is
|
---|
1205 | necessary to make the "Mesa glXSwapBuffers to repair
|
---|
1206 | damage" hack operate correctly. Without indicating a
|
---|
1207 | redisplay is necessary, there's not an initial back
|
---|
1208 | buffer render from which to blit from when damage
|
---|
1209 | happens to the window. */
|
---|
1210 | workMask |= GLUT_REDISPLAY_WORK;
|
---|
1211 |
|
---|
1212 | }
|
---|
1213 |
|
---|
1214 | /* The code below is more involved than otherwise necessary
|
---|
1215 | because it is paranoid about the overlay or entire window
|
---|
1216 | being removed or destroyed in the course of the callbacks.
|
---|
1217 | Notice how the global __glutWindowDamaged is used to record
|
---|
1218 | the layers' damage status. See the code in glutLayerGet for
|
---|
1219 | how __glutWindowDamaged is used. The point is to not have to
|
---|
1220 | update the "damaged" field after the callback since the
|
---|
1221 | window (or overlay) may be destroyed (or removed) when the
|
---|
1222 | callback returns. */
|
---|
1223 |
|
---|
1224 | if (window->overlay && window->overlay->display) {
|
---|
1225 | int num = window->num;
|
---|
1226 | Window xid = window->overlay ? window->overlay->win : None;
|
---|
1227 |
|
---|
1228 | /* If an overlay display callback is registered, we
|
---|
1229 | differentiate between a redisplay needed for the
|
---|
1230 | overlay and/or normal plane. If there is no overlay
|
---|
1231 | display callback registered, we simply use the
|
---|
1232 | standard display callback. */
|
---|
1233 |
|
---|
1234 | if (workMask & (GLUT_REDISPLAY_WORK | GLUT_REPAIR_WORK)) {
|
---|
1235 | if (__glutMesaSwapHackSupport) {
|
---|
1236 | if (window->usedSwapBuffers) {
|
---|
1237 |
|
---|
1238 | if ((workMask & (GLUT_REPAIR_WORK | GLUT_REDISPLAY_WORK)) == GLUT_REPAIR_WORK) {
|
---|
1239 | SWAP_BUFFERS_WINDOW(window);
|
---|
1240 | goto skippedDisplayCallback1;
|
---|
1241 | }
|
---|
1242 | }
|
---|
1243 | }
|
---|
1244 |
|
---|
1245 | /* Render to normal plane. */
|
---|
1246 | #if defined(_WIN32) || defined(__WIN32OS2__)
|
---|
1247 | window->renderDc = window->hdc;
|
---|
1248 | #endif
|
---|
1249 | window->renderWin = window->win;
|
---|
1250 | window->renderCtx = window->ctx;
|
---|
1251 | __glutWindowDamaged = (workMask & GLUT_REPAIR_WORK);
|
---|
1252 | __glutSetWindow(window);
|
---|
1253 | window->usedSwapBuffers = 0;
|
---|
1254 | ((GLUTdisplayCB)(window->display))();
|
---|
1255 | __glutWindowDamaged = 0;
|
---|
1256 |
|
---|
1257 | skippedDisplayCallback1:;
|
---|
1258 | }
|
---|
1259 | if (workMask & (GLUT_OVERLAY_REDISPLAY_WORK | GLUT_OVERLAY_REPAIR_WORK)) {
|
---|
1260 |
|
---|
1261 | window = __glutWindowList[num];
|
---|
1262 | if (window && window->overlay &&
|
---|
1263 | window->overlay->win == xid && window->overlay->display) {
|
---|
1264 |
|
---|
1265 | /* Render to overlay. */
|
---|
1266 | #if defined(_WIN32) || defined(__WIN32OS2__)
|
---|
1267 | window->renderDc = window->overlay->hdc;
|
---|
1268 | #endif
|
---|
1269 | window->renderWin = window->overlay->win;
|
---|
1270 | window->renderCtx = window->overlay->ctx;
|
---|
1271 | __glutWindowDamaged = (workMask & GLUT_OVERLAY_REPAIR_WORK);
|
---|
1272 | __glutSetWindow(window);
|
---|
1273 | ((GLUTdisplayCB)(window->overlay->display))();
|
---|
1274 | __glutWindowDamaged = 0;
|
---|
1275 | } else {
|
---|
1276 | /* Overlay may have since been destroyed or the
|
---|
1277 | overlay callback may have been disabled during
|
---|
1278 | normal display callback. */
|
---|
1279 | }
|
---|
1280 | }
|
---|
1281 | } else {
|
---|
1282 | if (__glutMesaSwapHackSupport) {
|
---|
1283 | if (!window->overlay && window->usedSwapBuffers) {
|
---|
1284 | if ((workMask & (GLUT_REPAIR_WORK | GLUT_REDISPLAY_WORK)) == GLUT_REPAIR_WORK) {
|
---|
1285 |
|
---|
1286 | SWAP_BUFFERS_WINDOW(window);
|
---|
1287 | goto skippedDisplayCallback2;
|
---|
1288 | }
|
---|
1289 | }
|
---|
1290 | }
|
---|
1291 | /* Render to normal plane (and possibly overlay). */
|
---|
1292 |
|
---|
1293 | __glutWindowDamaged = (workMask & (GLUT_OVERLAY_REPAIR_WORK | GLUT_REPAIR_WORK));
|
---|
1294 | __glutSetWindow(window);
|
---|
1295 | window->usedSwapBuffers = 0;
|
---|
1296 | ((GLUTdisplayCB)(window->display))();
|
---|
1297 | __glutWindowDamaged = 0;
|
---|
1298 |
|
---|
1299 | skippedDisplayCallback2:;
|
---|
1300 | }
|
---|
1301 | }
|
---|
1302 | /* Combine workMask with window->workMask to determine what
|
---|
1303 | finish and debug work there is. */
|
---|
1304 |
|
---|
1305 | workMask |= window->workMask;
|
---|
1306 |
|
---|
1307 | if (workMask & GLUT_FINISH_WORK) {
|
---|
1308 | /* Finish work makes sure a glFinish gets done to indirect
|
---|
1309 | rendering contexts. Indirect contexts tend to have much
|
---|
1310 | longer latency because lots of OpenGL extension requests
|
---|
1311 | can queue up in the X protocol stream. __glutSetWindow
|
---|
1312 | is where the finish works gets queued for indirect
|
---|
1313 | contexts. */
|
---|
1314 | __glutSetWindow(window);
|
---|
1315 | glFinish();
|
---|
1316 | }
|
---|
1317 | if (workMask & GLUT_DEBUG_WORK) {
|
---|
1318 | __glutSetWindow(window);
|
---|
1319 | glutReportErrors();
|
---|
1320 | }
|
---|
1321 | /* Strip out dummy, finish, and debug work bits. */
|
---|
1322 | window->workMask &= ~(GLUT_DUMMY_WORK | GLUT_FINISH_WORK | GLUT_DEBUG_WORK);
|
---|
1323 |
|
---|
1324 | if (window->workMask) {
|
---|
1325 | /* Leave on work list. */
|
---|
1326 | return window;
|
---|
1327 | } else {
|
---|
1328 | /* Remove current window from work list. */
|
---|
1329 | return window->prevWorkWin;
|
---|
1330 | }
|
---|
1331 | }
|
---|
1332 |
|
---|
1333 | /* CENTRY */
|
---|
1334 | void GLAPIENTRY
|
---|
1335 | glutMainLoop(void)
|
---|
1336 | {
|
---|
1337 | #if !defined(_WIN32) && !defined(__WIN32OS2__)
|
---|
1338 | if (!__glutDisplay)
|
---|
1339 | __glutFatalUsage("main loop entered with out proper initialization.");
|
---|
1340 | #endif
|
---|
1341 | if (!__glutWindowListSize)
|
---|
1342 | __glutFatalUsage(
|
---|
1343 | "main loop entered with no windows created.");
|
---|
1344 | for (;;) {
|
---|
1345 | if (__glutWindowWorkList) {
|
---|
1346 | GLUTwindow *remainder, *work;
|
---|
1347 |
|
---|
1348 | work = __glutWindowWorkList;
|
---|
1349 | __glutWindowWorkList = NULL;
|
---|
1350 | if (work) {
|
---|
1351 | remainder = processWindowWorkList(work);
|
---|
1352 | if (remainder) {
|
---|
1353 | *beforeEnd = __glutWindowWorkList;
|
---|
1354 | __glutWindowWorkList = remainder;
|
---|
1355 | }
|
---|
1356 | }
|
---|
1357 | }
|
---|
1358 | if (__glutIdleFunc || __glutWindowWorkList) {
|
---|
1359 | idleWait();
|
---|
1360 | } else {
|
---|
1361 | if (__glutTimerList) {
|
---|
1362 | waitForSomething();
|
---|
1363 | } else {
|
---|
1364 | processEventsAndTimeouts();
|
---|
1365 | }
|
---|
1366 | }
|
---|
1367 | }
|
---|
1368 | }
|
---|
1369 | /* ENDCENTRY */
|
---|