1 | /* |
2 | * fg_main_x11.c |
3 | * |
4 | * The X11-specific windows message processing methods. |
5 | * |
6 | * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved. |
7 | * Written by Pawel W. Olszta, <olszta@sourceforge.net> |
8 | * Copied for Platform code by Evan Felix <karcaw at gmail.com> |
9 | * Creation date: Thur Feb 2 2012 |
10 | * |
11 | * Permission is hereby granted, free of charge, to any person obtaining a |
12 | * copy of this software and associated documentation files (the "Software"), |
13 | * to deal in the Software without restriction, including without limitation |
14 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
15 | * and/or sell copies of the Software, and to permit persons to whom the |
16 | * Software is furnished to do so, subject to the following conditions: |
17 | * |
18 | * The above copyright notice and this permission notice shall be included |
19 | * in all copies or substantial portions of the Software. |
20 | * |
21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
22 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
24 | * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
25 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
27 | */ |
28 | |
29 | #include <GL/freeglut.h> |
30 | #include "../fg_internal.h" |
31 | #include <errno.h> |
32 | #include <limits.h> |
33 | #include <stdarg.h> |
34 | |
35 | /* |
36 | * Try to get the maximum value allowed for ints, falling back to the minimum |
37 | * guaranteed by ISO C99 if there is no suitable header. |
38 | */ |
39 | #ifdef HAVE_LIMITS_H |
40 | # include <limits.h> |
41 | #endif |
42 | #ifndef INT_MAX |
43 | # define INT_MAX 32767 |
44 | #endif |
45 | |
46 | #ifndef MIN |
47 | # define MIN(a,b) (((a)<(b)) ? (a) : (b)) |
48 | #endif |
49 | |
50 | extern void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify); |
51 | extern void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify); |
52 | extern void fgPlatformFullScreenToggle( SFG_Window *win ); |
53 | extern void fgPlatformPositionWindow( SFG_Window *window, int x, int y ); |
54 | extern void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height ); |
55 | extern void fgPlatformPushWindow( SFG_Window *window ); |
56 | extern void fgPlatformPopWindow( SFG_Window *window ); |
57 | extern void fgPlatformHideWindow( SFG_Window *window ); |
58 | extern void fgPlatformIconifyWindow( SFG_Window *window ); |
59 | extern void fgPlatformShowWindow( SFG_Window *window ); |
60 | |
61 | /* used in the event handling code to match and discard stale mouse motion events */ |
62 | static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg); |
63 | |
64 | /* |
65 | * TODO BEFORE THE STABLE RELEASE: |
66 | * |
67 | * There are some issues concerning window redrawing under X11, and maybe |
68 | * some events are not handled. |
69 | * |
70 | * Need to investigate why the X11 version breaks out with an error when |
71 | * closing a window (using the window manager, not glutDestroyWindow)... |
72 | */ |
73 | |
74 | |
75 | |
76 | fg_time_t fgPlatformSystemTime ( void ) |
77 | { |
78 | #ifdef CLOCK_MONOTONIC |
79 | struct timespec now; |
80 | clock_gettime(CLOCK_MONOTONIC, &now); |
81 | return now.tv_nsec/1000000 + now.tv_sec*1000; |
82 | #elif defined(HAVE_GETTIMEOFDAY) |
83 | struct timeval now; |
84 | gettimeofday( &now, NULL ); |
85 | return now.tv_usec/1000 + now.tv_sec*1000; |
86 | #endif |
87 | } |
88 | |
89 | /* |
90 | * Does the magic required to relinquish the CPU until something interesting |
91 | * happens. |
92 | */ |
93 | |
94 | void fgPlatformSleepForEvents( fg_time_t msec ) |
95 | { |
96 | /* |
97 | * Possibly due to aggressive use of XFlush() and friends, |
98 | * it is possible to have our socket drained but still have |
99 | * unprocessed events. (Or, this may just be normal with |
100 | * X, anyway?) We do non-trivial processing of X events |
101 | * after the event-reading loop, in any case, so we |
102 | * need to allow that we may have an empty socket but non- |
103 | * empty event queue. |
104 | */ |
105 | if( ! XPending( fgDisplay.pDisplay.Display ) ) |
106 | { |
107 | fd_set fdset; |
108 | int err; |
109 | int socket; |
110 | struct timeval wait; |
111 | |
112 | socket = ConnectionNumber( fgDisplay.pDisplay.Display ); |
113 | FD_ZERO( &fdset ); |
114 | FD_SET( socket, &fdset ); |
115 | wait.tv_sec = msec / 1000; |
116 | wait.tv_usec = (msec % 1000) * 1000; |
117 | err = select( socket+1, &fdset, NULL, NULL, &wait ); |
118 | |
119 | if( ( -1 == err ) && ( errno != EINTR ) ) |
120 | fgWarning ( "freeglut select() error: %d" , errno ); |
121 | } |
122 | } |
123 | |
124 | |
125 | /* |
126 | * Returns GLUT modifier mask for the state field of an X11 event. |
127 | */ |
128 | int fgPlatformGetModifiers( int state ) |
129 | { |
130 | int ret = 0; |
131 | |
132 | if( state & ( ShiftMask | LockMask ) ) |
133 | ret |= GLUT_ACTIVE_SHIFT; |
134 | if( state & ControlMask ) |
135 | ret |= GLUT_ACTIVE_CTRL; |
136 | if( state & Mod1Mask ) |
137 | ret |= GLUT_ACTIVE_ALT; |
138 | |
139 | return ret; |
140 | } |
141 | |
142 | static const char* fghTypeToString( int type ) |
143 | { |
144 | switch( type ) { |
145 | case KeyPress: return "KeyPress" ; |
146 | case KeyRelease: return "KeyRelease" ; |
147 | case ButtonPress: return "ButtonPress" ; |
148 | case ButtonRelease: return "ButtonRelease" ; |
149 | case MotionNotify: return "MotionNotify" ; |
150 | case EnterNotify: return "EnterNotify" ; |
151 | case LeaveNotify: return "LeaveNotify" ; |
152 | case FocusIn: return "FocusIn" ; |
153 | case FocusOut: return "FocusOut" ; |
154 | case KeymapNotify: return "KeymapNotify" ; |
155 | case Expose: return "Expose" ; |
156 | case GraphicsExpose: return "GraphicsExpose" ; |
157 | case NoExpose: return "NoExpose" ; |
158 | case VisibilityNotify: return "VisibilityNotify" ; |
159 | case CreateNotify: return "CreateNotify" ; |
160 | case DestroyNotify: return "DestroyNotify" ; |
161 | case UnmapNotify: return "UnmapNotify" ; |
162 | case MapNotify: return "MapNotify" ; |
163 | case MapRequest: return "MapRequest" ; |
164 | case ReparentNotify: return "ReparentNotify" ; |
165 | case ConfigureNotify: return "ConfigureNotify" ; |
166 | case ConfigureRequest: return "ConfigureRequest" ; |
167 | case GravityNotify: return "GravityNotify" ; |
168 | case ResizeRequest: return "ResizeRequest" ; |
169 | case CirculateNotify: return "CirculateNotify" ; |
170 | case CirculateRequest: return "CirculateRequest" ; |
171 | case PropertyNotify: return "PropertyNotify" ; |
172 | case SelectionClear: return "SelectionClear" ; |
173 | case SelectionRequest: return "SelectionRequest" ; |
174 | case SelectionNotify: return "SelectionNotify" ; |
175 | case ColormapNotify: return "ColormapNotify" ; |
176 | case ClientMessage: return "ClientMessage" ; |
177 | case MappingNotify: return "MappingNotify" ; |
178 | default: return "UNKNOWN" ; |
179 | } |
180 | } |
181 | |
182 | static const char* fghBoolToString( Bool b ) |
183 | { |
184 | return b == False ? "False" : "True" ; |
185 | } |
186 | |
187 | static const char* fghNotifyHintToString( char is_hint ) |
188 | { |
189 | switch( is_hint ) { |
190 | case NotifyNormal: return "NotifyNormal" ; |
191 | case NotifyHint: return "NotifyHint" ; |
192 | default: return "UNKNOWN" ; |
193 | } |
194 | } |
195 | |
196 | static const char* fghNotifyModeToString( int mode ) |
197 | { |
198 | switch( mode ) { |
199 | case NotifyNormal: return "NotifyNormal" ; |
200 | case NotifyGrab: return "NotifyGrab" ; |
201 | case NotifyUngrab: return "NotifyUngrab" ; |
202 | case NotifyWhileGrabbed: return "NotifyWhileGrabbed" ; |
203 | default: return "UNKNOWN" ; |
204 | } |
205 | } |
206 | |
207 | static const char* fghNotifyDetailToString( int detail ) |
208 | { |
209 | switch( detail ) { |
210 | case NotifyAncestor: return "NotifyAncestor" ; |
211 | case NotifyVirtual: return "NotifyVirtual" ; |
212 | case NotifyInferior: return "NotifyInferior" ; |
213 | case NotifyNonlinear: return "NotifyNonlinear" ; |
214 | case NotifyNonlinearVirtual: return "NotifyNonlinearVirtual" ; |
215 | case NotifyPointer: return "NotifyPointer" ; |
216 | case NotifyPointerRoot: return "NotifyPointerRoot" ; |
217 | case NotifyDetailNone: return "NotifyDetailNone" ; |
218 | default: return "UNKNOWN" ; |
219 | } |
220 | } |
221 | |
222 | static const char* fghVisibilityToString( int state ) { |
223 | switch( state ) { |
224 | case VisibilityUnobscured: return "VisibilityUnobscured" ; |
225 | case VisibilityPartiallyObscured: return "VisibilityPartiallyObscured" ; |
226 | case VisibilityFullyObscured: return "VisibilityFullyObscured" ; |
227 | default: return "UNKNOWN" ; |
228 | } |
229 | } |
230 | |
231 | static const char* fghConfigureDetailToString( int detail ) |
232 | { |
233 | switch( detail ) { |
234 | case Above: return "Above" ; |
235 | case Below: return "Below" ; |
236 | case TopIf: return "TopIf" ; |
237 | case BottomIf: return "BottomIf" ; |
238 | case Opposite: return "Opposite" ; |
239 | default: return "UNKNOWN" ; |
240 | } |
241 | } |
242 | |
243 | static const char* fghPlaceToString( int place ) |
244 | { |
245 | switch( place ) { |
246 | case PlaceOnTop: return "PlaceOnTop" ; |
247 | case PlaceOnBottom: return "PlaceOnBottom" ; |
248 | default: return "UNKNOWN" ; |
249 | } |
250 | } |
251 | |
252 | static const char* fghMappingRequestToString( int request ) |
253 | { |
254 | switch( request ) { |
255 | case MappingModifier: return "MappingModifier" ; |
256 | case MappingKeyboard: return "MappingKeyboard" ; |
257 | case MappingPointer: return "MappingPointer" ; |
258 | default: return "UNKNOWN" ; |
259 | } |
260 | } |
261 | |
262 | static const char* fghPropertyStateToString( int state ) |
263 | { |
264 | switch( state ) { |
265 | case PropertyNewValue: return "PropertyNewValue" ; |
266 | case PropertyDelete: return "PropertyDelete" ; |
267 | default: return "UNKNOWN" ; |
268 | } |
269 | } |
270 | |
271 | static const char* fghColormapStateToString( int state ) |
272 | { |
273 | switch( state ) { |
274 | case ColormapUninstalled: return "ColormapUninstalled" ; |
275 | case ColormapInstalled: return "ColormapInstalled" ; |
276 | default: return "UNKNOWN" ; |
277 | } |
278 | } |
279 | |
280 | __fg_unused static void fghPrintEvent( XEvent *event ) |
281 | { |
282 | switch( event->type ) { |
283 | |
284 | case KeyPress: |
285 | case KeyRelease: { |
286 | XKeyEvent *e = &event->xkey; |
287 | fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, " |
288 | "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, " |
289 | "keycode=%u, same_screen=%s" , fghTypeToString( e->type ), |
290 | e->window, e->root, e->subwindow, (unsigned long)e->time, |
291 | e->x, e->y, e->x_root, e->y_root, e->state, e->keycode, |
292 | fghBoolToString( e->same_screen ) ); |
293 | break; |
294 | } |
295 | |
296 | case ButtonPress: |
297 | case ButtonRelease: { |
298 | XButtonEvent *e = &event->xbutton; |
299 | fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, " |
300 | "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, " |
301 | "button=%u, same_screen=%d" , fghTypeToString( e->type ), |
302 | e->window, e->root, e->subwindow, (unsigned long)e->time, |
303 | e->x, e->y, e->x_root, e->y_root, e->state, e->button, |
304 | fghBoolToString( e->same_screen ) ); |
305 | break; |
306 | } |
307 | |
308 | case MotionNotify: { |
309 | XMotionEvent *e = &event->xmotion; |
310 | fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, " |
311 | "(x,y)=(%d,%d), (x_root,y_root)=(%d,%d), state=0x%x, " |
312 | "is_hint=%s, same_screen=%d" , fghTypeToString( e->type ), |
313 | e->window, e->root, e->subwindow, (unsigned long)e->time, |
314 | e->x, e->y, e->x_root, e->y_root, e->state, |
315 | fghNotifyHintToString( e->is_hint ), |
316 | fghBoolToString( e->same_screen ) ); |
317 | break; |
318 | } |
319 | |
320 | case EnterNotify: |
321 | case LeaveNotify: { |
322 | XCrossingEvent *e = &event->xcrossing; |
323 | fgWarning( "%s: window=0x%x, root=0x%x, subwindow=0x%x, time=%lu, " |
324 | "(x,y)=(%d,%d), mode=%s, detail=%s, same_screen=%d, " |
325 | "focus=%d, state=0x%x" , fghTypeToString( e->type ), |
326 | e->window, e->root, e->subwindow, (unsigned long)e->time, |
327 | e->x, e->y, fghNotifyModeToString( e->mode ), |
328 | fghNotifyDetailToString( e->detail ), (int)e->same_screen, |
329 | (int)e->focus, e->state ); |
330 | break; |
331 | } |
332 | |
333 | case FocusIn: |
334 | case FocusOut: { |
335 | XFocusChangeEvent *e = &event->xfocus; |
336 | fgWarning( "%s: window=0x%x, mode=%s, detail=%s" , |
337 | fghTypeToString( e->type ), e->window, |
338 | fghNotifyModeToString( e->mode ), |
339 | fghNotifyDetailToString( e->detail ) ); |
340 | break; |
341 | } |
342 | |
343 | case KeymapNotify: { |
344 | XKeymapEvent *e = &event->xkeymap; |
345 | char buf[32 * 2 + 1]; |
346 | int i; |
347 | for ( i = 0; i < 32; i++ ) { |
348 | snprintf( &buf[ i * 2 ], sizeof( buf ) - i * 2, |
349 | "%02x" , e->key_vector[ i ] ); |
350 | } |
351 | buf[ i ] = '\0'; |
352 | fgWarning( "%s: window=0x%x, %s" , fghTypeToString( e->type ), e->window, |
353 | buf ); |
354 | break; |
355 | } |
356 | |
357 | case Expose: { |
358 | XExposeEvent *e = &event->xexpose; |
359 | fgWarning( "%s: window=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), " |
360 | "count=%d" , fghTypeToString( e->type ), e->window, e->x, |
361 | e->y, e->width, e->height, e->count ); |
362 | break; |
363 | } |
364 | |
365 | case GraphicsExpose: { |
366 | XGraphicsExposeEvent *e = &event->xgraphicsexpose; |
367 | fgWarning( "%s: drawable=0x%x, (x,y)=(%d,%d), (width,height)=(%d,%d), " |
368 | "count=%d, (major_code,minor_code)=(%d,%d)" , |
369 | fghTypeToString( e->type ), e->drawable, e->x, e->y, |
370 | e->width, e->height, e->count, e->major_code, |
371 | e->minor_code ); |
372 | break; |
373 | } |
374 | |
375 | case NoExpose: { |
376 | XNoExposeEvent *e = &event->xnoexpose; |
377 | fgWarning( "%s: drawable=0x%x, (major_code,minor_code)=(%d,%d)" , |
378 | fghTypeToString( e->type ), e->drawable, e->major_code, |
379 | e->minor_code ); |
380 | break; |
381 | } |
382 | |
383 | case VisibilityNotify: { |
384 | XVisibilityEvent *e = &event->xvisibility; |
385 | fgWarning( "%s: window=0x%x, state=%s" , fghTypeToString( e->type ), |
386 | e->window, fghVisibilityToString( e->state) ); |
387 | break; |
388 | } |
389 | |
390 | case CreateNotify: { |
391 | XCreateWindowEvent *e = &event->xcreatewindow; |
392 | fgWarning( "%s: (x,y)=(%d,%d), (width,height)=(%d,%d), border_width=%d, " |
393 | "window=0x%x, override_redirect=%s" , |
394 | fghTypeToString( e->type ), e->x, e->y, e->width, e->height, |
395 | e->border_width, e->window, |
396 | fghBoolToString( e->override_redirect ) ); |
397 | break; |
398 | } |
399 | |
400 | case DestroyNotify: { |
401 | XDestroyWindowEvent *e = &event->xdestroywindow; |
402 | fgWarning( "%s: event=0x%x, window=0x%x" , |
403 | fghTypeToString( e->type ), e->event, e->window ); |
404 | break; |
405 | } |
406 | |
407 | case UnmapNotify: { |
408 | XUnmapEvent *e = &event->xunmap; |
409 | fgWarning( "%s: event=0x%x, window=0x%x, from_configure=%s" , |
410 | fghTypeToString( e->type ), e->event, e->window, |
411 | fghBoolToString( e->from_configure ) ); |
412 | break; |
413 | } |
414 | |
415 | case MapNotify: { |
416 | XMapEvent *e = &event->xmap; |
417 | fgWarning( "%s: event=0x%x, window=0x%x, override_redirect=%s" , |
418 | fghTypeToString( e->type ), e->event, e->window, |
419 | fghBoolToString( e->override_redirect ) ); |
420 | break; |
421 | } |
422 | |
423 | case MapRequest: { |
424 | XMapRequestEvent *e = &event->xmaprequest; |
425 | fgWarning( "%s: parent=0x%x, window=0x%x" , |
426 | fghTypeToString( event->type ), e->parent, e->window ); |
427 | break; |
428 | } |
429 | |
430 | case ReparentNotify: { |
431 | XReparentEvent *e = &event->xreparent; |
432 | fgWarning( "%s: event=0x%x, window=0x%x, parent=0x%x, (x,y)=(%d,%d), " |
433 | "override_redirect=%s" , fghTypeToString( e->type ), |
434 | e->event, e->window, e->parent, e->x, e->y, |
435 | fghBoolToString( e->override_redirect ) ); |
436 | break; |
437 | } |
438 | |
439 | case ConfigureNotify: { |
440 | XConfigureEvent *e = &event->xconfigure; |
441 | fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d), " |
442 | "(width,height)=(%d,%d), border_width=%d, above=0x%x, " |
443 | "override_redirect=%s" , fghTypeToString( e->type ), e->event, |
444 | e->window, e->x, e->y, e->width, e->height, e->border_width, |
445 | e->above, fghBoolToString( e->override_redirect ) ); |
446 | break; |
447 | } |
448 | |
449 | case ConfigureRequest: { |
450 | XConfigureRequestEvent *e = &event->xconfigurerequest; |
451 | fgWarning( "%s: parent=0x%x, window=0x%x, (x,y)=(%d,%d), " |
452 | "(width,height)=(%d,%d), border_width=%d, above=0x%x, " |
453 | "detail=%s, value_mask=%lx" , fghTypeToString( e->type ), |
454 | e->parent, e->window, e->x, e->y, e->width, e->height, |
455 | e->border_width, e->above, |
456 | fghConfigureDetailToString( e->detail ), e->value_mask ); |
457 | break; |
458 | } |
459 | |
460 | case GravityNotify: { |
461 | XGravityEvent *e = &event->xgravity; |
462 | fgWarning( "%s: event=0x%x, window=0x%x, (x,y)=(%d,%d)" , |
463 | fghTypeToString( e->type ), e->event, e->window, e->x, e->y ); |
464 | break; |
465 | } |
466 | |
467 | case ResizeRequest: { |
468 | XResizeRequestEvent *e = &event->xresizerequest; |
469 | fgWarning( "%s: window=0x%x, (width,height)=(%d,%d)" , |
470 | fghTypeToString( e->type ), e->window, e->width, e->height ); |
471 | break; |
472 | } |
473 | |
474 | case CirculateNotify: { |
475 | XCirculateEvent *e = &event->xcirculate; |
476 | fgWarning( "%s: event=0x%x, window=0x%x, place=%s" , |
477 | fghTypeToString( e->type ), e->event, e->window, |
478 | fghPlaceToString( e->place ) ); |
479 | break; |
480 | } |
481 | |
482 | case CirculateRequest: { |
483 | XCirculateRequestEvent *e = &event->xcirculaterequest; |
484 | fgWarning( "%s: parent=0x%x, window=0x%x, place=%s" , |
485 | fghTypeToString( e->type ), e->parent, e->window, |
486 | fghPlaceToString( e->place ) ); |
487 | break; |
488 | } |
489 | |
490 | case PropertyNotify: { |
491 | XPropertyEvent *e = &event->xproperty; |
492 | fgWarning( "%s: window=0x%x, atom=%lu, time=%lu, state=%s" , |
493 | fghTypeToString( e->type ), e->window, |
494 | (unsigned long)e->atom, (unsigned long)e->time, |
495 | fghPropertyStateToString( e->state ) ); |
496 | break; |
497 | } |
498 | |
499 | case SelectionClear: { |
500 | XSelectionClearEvent *e = &event->xselectionclear; |
501 | fgWarning( "%s: window=0x%x, selection=%lu, time=%lu" , |
502 | fghTypeToString( e->type ), e->window, |
503 | (unsigned long)e->selection, (unsigned long)e->time ); |
504 | break; |
505 | } |
506 | |
507 | case SelectionRequest: { |
508 | XSelectionRequestEvent *e = &event->xselectionrequest; |
509 | fgWarning( "%s: owner=0x%x, requestor=0x%x, selection=0x%x, " |
510 | "target=0x%x, property=%lu, time=%lu" , |
511 | fghTypeToString( e->type ), e->owner, e->requestor, |
512 | (unsigned long)e->selection, (unsigned long)e->target, |
513 | (unsigned long)e->property, (unsigned long)e->time ); |
514 | break; |
515 | } |
516 | |
517 | case SelectionNotify: { |
518 | XSelectionEvent *e = &event->xselection; |
519 | fgWarning( "%s: requestor=0x%x, selection=0x%x, target=0x%x, " |
520 | "property=%lu, time=%lu" , fghTypeToString( e->type ), |
521 | e->requestor, (unsigned long)e->selection, |
522 | (unsigned long)e->target, (unsigned long)e->property, |
523 | (unsigned long)e->time ); |
524 | break; |
525 | } |
526 | |
527 | case ColormapNotify: { |
528 | XColormapEvent *e = &event->xcolormap; |
529 | fgWarning( "%s: window=0x%x, colormap=%lu, new=%s, state=%s" , |
530 | fghTypeToString( e->type ), e->window, |
531 | (unsigned long)e->colormap, fghBoolToString( e->new ), |
532 | fghColormapStateToString( e->state ) ); |
533 | break; |
534 | } |
535 | |
536 | case ClientMessage: { |
537 | XClientMessageEvent *e = &event->xclient; |
538 | char buf[ 61 ]; |
539 | char* p = buf; |
540 | char* end = buf + sizeof( buf ); |
541 | int i; |
542 | switch( e->format ) { |
543 | case 8: |
544 | for ( i = 0; i < 20; i++, p += 3 ) { |
545 | snprintf( p, end - p, " %02x" , e->data.b[ i ] ); |
546 | } |
547 | break; |
548 | case 16: |
549 | for ( i = 0; i < 10; i++, p += 5 ) { |
550 | snprintf( p, end - p, " %04x" , e->data.s[ i ] ); |
551 | } |
552 | break; |
553 | case 32: |
554 | for ( i = 0; i < 5; i++, p += 9 ) { |
555 | snprintf( p, end - p, " %08lx" , e->data.l[ i ] ); |
556 | } |
557 | break; |
558 | } |
559 | *p = '\0'; |
560 | fgWarning( "%s: window=0x%x, message_type=%lu, format=%d, data=(%s )" , |
561 | fghTypeToString( e->type ), e->window, |
562 | (unsigned long)e->message_type, e->format, buf ); |
563 | break; |
564 | } |
565 | |
566 | case MappingNotify: { |
567 | XMappingEvent *e = &event->xmapping; |
568 | fgWarning( "%s: window=0x%x, request=%s, first_keycode=%d, count=%d" , |
569 | fghTypeToString( e->type ), e->window, |
570 | fghMappingRequestToString( e->request ), e->first_keycode, |
571 | e->count ); |
572 | break; |
573 | } |
574 | |
575 | default: { |
576 | fgWarning( "%s" , fghTypeToString( event->type ) ); |
577 | break; |
578 | } |
579 | } |
580 | } |
581 | |
582 | /* UTF-8 decoding routine */ |
583 | enum |
584 | { |
585 | Runeerror = 0xFFFD, /* decoding error in UTF */ |
586 | |
587 | Bit1 = 7, |
588 | Bitx = 6, |
589 | Bit2 = 5, |
590 | Bit3 = 4, |
591 | Bit4 = 3, |
592 | Bit5 = 2, |
593 | |
594 | T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */ |
595 | Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */ |
596 | T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */ |
597 | T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */ |
598 | T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */ |
599 | T5 = ((1<<(Bit5+1))-1) ^ 0xFF, /* 1111 1000 */ |
600 | |
601 | Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */ |
602 | Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */ |
603 | Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */ |
604 | Rune4 = (1<<(Bit4+3*Bitx))-1, /* 0001 1111 1111 1111 1111 1111 */ |
605 | |
606 | Maskx = (1<<Bitx)-1, /* 0011 1111 */ |
607 | Testx = Maskx ^ 0xFF, /* 1100 0000 */ |
608 | |
609 | Bad = Runeerror, |
610 | }; |
611 | |
612 | static int chartorune(int *rune, const char *str) |
613 | { |
614 | int c, c1, c2, c3; |
615 | int l; |
616 | |
617 | /* |
618 | * one character sequence |
619 | * 00000-0007F => T1 |
620 | */ |
621 | c = *(const unsigned char*)str; |
622 | if(c < Tx) { |
623 | *rune = c; |
624 | return 1; |
625 | } |
626 | |
627 | /* |
628 | * two character sequence |
629 | * 0080-07FF => T2 Tx |
630 | */ |
631 | c1 = *(const unsigned char*)(str+1) ^ Tx; |
632 | if(c1 & Testx) |
633 | goto bad; |
634 | if(c < T3) { |
635 | if(c < T2) |
636 | goto bad; |
637 | l = ((c << Bitx) | c1) & Rune2; |
638 | if(l <= Rune1) |
639 | goto bad; |
640 | *rune = l; |
641 | return 2; |
642 | } |
643 | |
644 | /* |
645 | * three character sequence |
646 | * 0800-FFFF => T3 Tx Tx |
647 | */ |
648 | c2 = *(const unsigned char*)(str+2) ^ Tx; |
649 | if(c2 & Testx) |
650 | goto bad; |
651 | if(c < T4) { |
652 | l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3; |
653 | if(l <= Rune2) |
654 | goto bad; |
655 | *rune = l; |
656 | return 3; |
657 | } |
658 | |
659 | /* |
660 | * four character sequence (21-bit value) |
661 | * 10000-1FFFFF => T4 Tx Tx Tx |
662 | */ |
663 | c3 = *(const unsigned char*)(str+3) ^ Tx; |
664 | if (c3 & Testx) |
665 | goto bad; |
666 | if (c < T5) { |
667 | l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4; |
668 | if (l <= Rune3) |
669 | goto bad; |
670 | *rune = l; |
671 | return 4; |
672 | } |
673 | /* |
674 | * Support for 5-byte or longer UTF-8 would go here, but |
675 | * since we don't have that, we'll just fall through to bad. |
676 | */ |
677 | |
678 | /* |
679 | * bad decoding |
680 | */ |
681 | bad: |
682 | *rune = Bad; |
683 | return 1; |
684 | } |
685 | |
686 | extern char *fgClipboardBuffer[3]; |
687 | |
688 | static Atom fghGetAtom(const char *name) |
689 | { |
690 | return XInternAtom(fgDisplay.pDisplay.Display, name, False); |
691 | } |
692 | |
693 | static void fgHandleSelectionNotify(XEvent *event) |
694 | { |
695 | Display *dpy = fgDisplay.pDisplay.Display; |
696 | Atom actual_type; |
697 | int actual_format; |
698 | unsigned long item_count; |
699 | unsigned long bytes_after; |
700 | unsigned char *prop; |
701 | |
702 | if (event->xselection.property == None) |
703 | { |
704 | fgWarning("Couldn't convert selection to UTF-8 string." ); |
705 | return; |
706 | } |
707 | |
708 | XGetWindowProperty(dpy, event->xselection.requestor, event->xselection.property, |
709 | 0, LONG_MAX, True, AnyPropertyType, |
710 | &actual_type, &actual_format, &item_count, &bytes_after, &prop); |
711 | |
712 | if (actual_type == fghGetAtom("UTF8_STRING" )) |
713 | { |
714 | if (event->xselection.selection == fghGetAtom("CLIPBOARD" )) |
715 | { |
716 | free(fgClipboardBuffer[GLUT_CLIPBOARD]); |
717 | fgClipboardBuffer[GLUT_CLIPBOARD] = strdup((char*)prop); |
718 | } |
719 | if (event->xselection.selection == XA_PRIMARY) |
720 | { |
721 | free(fgClipboardBuffer[GLUT_PRIMARY]); |
722 | fgClipboardBuffer[GLUT_PRIMARY] = strdup((char*)prop); |
723 | } |
724 | if (event->xselection.selection == XA_SECONDARY) |
725 | { |
726 | free(fgClipboardBuffer[GLUT_SECONDARY]); |
727 | fgClipboardBuffer[GLUT_SECONDARY] = strdup((char*)prop); |
728 | } |
729 | } |
730 | |
731 | XFree(prop); |
732 | } |
733 | |
734 | static void fgHandleSelectionClear(XEvent *event) |
735 | { |
736 | if (event->xselectionclear.selection == fghGetAtom("CLIPBOARD" )) |
737 | { |
738 | free(fgClipboardBuffer[GLUT_CLIPBOARD]); |
739 | fgClipboardBuffer[GLUT_CLIPBOARD] = NULL; |
740 | } |
741 | else if (event->xselectionclear.selection == XA_PRIMARY) |
742 | { |
743 | free(fgClipboardBuffer[GLUT_PRIMARY]); |
744 | fgClipboardBuffer[GLUT_PRIMARY] = NULL; |
745 | } |
746 | else if (event->xselectionclear.selection == XA_SECONDARY) |
747 | { |
748 | free(fgClipboardBuffer[GLUT_SECONDARY]); |
749 | fgClipboardBuffer[GLUT_SECONDARY] = NULL; |
750 | } |
751 | } |
752 | |
753 | static void fgHandleSelectionRequest(XEvent *event) |
754 | { |
755 | Display *dpy = fgDisplay.pDisplay.Display; |
756 | Window requestor = event->xselectionrequest.requestor; |
757 | Atom selection = event->xselectionrequest.selection; |
758 | Atom target = event->xselectionrequest.target; |
759 | Atom property = event->xselectionrequest.property; |
760 | Atom time = event->xselectionrequest.time; |
761 | XEvent response; |
762 | char *text; |
763 | |
764 | if (property == None) |
765 | property = target; |
766 | |
767 | response.xselection.type = SelectionNotify; |
768 | response.xselection.send_event = True; |
769 | response.xselection.display = dpy; |
770 | response.xselection.requestor = requestor; |
771 | response.xselection.selection = selection; |
772 | response.xselection.target = target; |
773 | response.xselection.property = property; |
774 | response.xselection.time = time; |
775 | |
776 | if (selection == fghGetAtom("CLIPBOARD" )) |
777 | text = fgClipboardBuffer[GLUT_CLIPBOARD]; |
778 | else if (selection == XA_PRIMARY) |
779 | text = fgClipboardBuffer[GLUT_PRIMARY]; |
780 | else if (selection == XA_SECONDARY) |
781 | text = fgClipboardBuffer[GLUT_SECONDARY]; |
782 | else |
783 | return; |
784 | if (!text) |
785 | return; |
786 | |
787 | if (target == fghGetAtom("TARGETS" )) |
788 | { |
789 | Atom list[4] = { |
790 | fghGetAtom("TARGETS" ), |
791 | fghGetAtom("TIMESTAMP" ), |
792 | XA_STRING, |
793 | fghGetAtom("UTF8_STRING" ) |
794 | }; |
795 | XChangeProperty(dpy, requestor, property, target, |
796 | 32, PropModeReplace, (unsigned char *)list, sizeof(list)/sizeof(Atom)); |
797 | } |
798 | else if (target == XA_STRING || target == fghGetAtom("UTF8_STRING" )) |
799 | { |
800 | XChangeProperty(dpy, requestor, property, target, |
801 | 8, PropModeReplace, (unsigned char *)text, strlen(text)); |
802 | } |
803 | |
804 | XSendEvent(dpy, requestor, False, 0, &response); |
805 | } |
806 | |
807 | void fgPlatformSetClipboard(int selection, const char *text) |
808 | { |
809 | Display *dpy = fgDisplay.pDisplay.Display; |
810 | Window window = fgStructure.CurrentWindow->Window.Handle; |
811 | Atom xselection; |
812 | if (selection == GLUT_CLIPBOARD) |
813 | xselection = fghGetAtom("CLIPBOARD" ); |
814 | else if (selection == GLUT_PRIMARY) |
815 | xselection = XA_PRIMARY; |
816 | else if (selection == GLUT_SECONDARY) |
817 | xselection = XA_SECONDARY; |
818 | else |
819 | return; |
820 | |
821 | free(fgClipboardBuffer[selection]); |
822 | fgClipboardBuffer[selection] = strdup(text); |
823 | |
824 | XSetSelectionOwner(dpy, xselection, window, CurrentTime); |
825 | } |
826 | |
827 | static Bool isSelectionNotify(Display *dpi, XEvent *event, XPointer arg) |
828 | { |
829 | return (event->type == SelectionNotify); |
830 | } |
831 | |
832 | const char *fgPlatformGetClipboard(int selection) |
833 | { |
834 | Display *dpy = fgDisplay.pDisplay.Display; |
835 | Window window = fgStructure.CurrentWindow->Window.Handle; |
836 | Atom xselection; |
837 | Window owner; |
838 | XEvent event; |
839 | |
840 | if (selection == GLUT_CLIPBOARD) |
841 | xselection = fghGetAtom("CLIPBOARD" ); |
842 | else if (selection == GLUT_PRIMARY) |
843 | xselection = XA_PRIMARY; |
844 | else if (selection == GLUT_SECONDARY) |
845 | xselection = XA_SECONDARY; |
846 | else |
847 | return NULL; |
848 | |
849 | owner = XGetSelectionOwner(dpy, xselection); |
850 | if (!owner) |
851 | return NULL; |
852 | if (owner != window) |
853 | { |
854 | XConvertSelection(dpy, xselection, fghGetAtom("UTF8_STRING" ), xselection, window, CurrentTime); |
855 | XIfEvent(dpy, &event, isSelectionNotify, NULL); |
856 | fgHandleSelectionNotify(&event); |
857 | } |
858 | |
859 | return fgClipboardBuffer[selection]; |
860 | } |
861 | |
862 | void fgPlatformProcessSingleEvent ( void ) |
863 | { |
864 | SFG_Window* window; |
865 | XEvent event; |
866 | |
867 | /* This code was repeated constantly, so here it goes into a definition: */ |
868 | #define GETWINDOW(a) \ |
869 | window = fgWindowByHandle( event.a.window ); \ |
870 | if( window == NULL ) \ |
871 | break; |
872 | |
873 | #define GETMOUSE(a) \ |
874 | window->State.MouseX = event.a.x; \ |
875 | window->State.MouseY = event.a.y; |
876 | |
877 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" ); |
878 | |
879 | while( XPending( fgDisplay.pDisplay.Display ) ) |
880 | { |
881 | XNextEvent( fgDisplay.pDisplay.Display, &event ); |
882 | #if _DEBUG |
883 | fghPrintEvent( &event ); |
884 | #endif |
885 | if (XFilterEvent(&event, None)) |
886 | continue; |
887 | |
888 | switch( event.type ) |
889 | { |
890 | case ClientMessage: |
891 | if (fgStructure.CurrentWindow) |
892 | if(fgIsSpaceballXEvent(&event)) { |
893 | fgSpaceballHandleXEvent(&event); |
894 | break; |
895 | } |
896 | /* Destroy the window when the WM_DELETE_WINDOW message arrives */ |
897 | if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.pDisplay.DeleteWindow ) |
898 | { |
899 | GETWINDOW( xclient ); |
900 | |
901 | fgDestroyWindow ( window ); |
902 | |
903 | if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT ) |
904 | { |
905 | fgDeinitialize( ); |
906 | exit( 0 ); |
907 | } |
908 | else if( fgState.ActionOnWindowClose == GLUT_ACTION_GLUTMAINLOOP_RETURNS ) |
909 | fgState.ExecState = GLUT_EXEC_STATE_STOP; |
910 | |
911 | return; |
912 | } |
913 | break; |
914 | |
915 | case SelectionClear: |
916 | fgHandleSelectionClear(&event); |
917 | break; |
918 | case SelectionRequest: |
919 | fgHandleSelectionRequest(&event); |
920 | break; |
921 | case SelectionNotify: |
922 | fgHandleSelectionNotify(&event); |
923 | break; |
924 | |
925 | /* |
926 | * CreateNotify causes a configure-event so that sub-windows are |
927 | * handled compatibly with GLUT. Otherwise, your sub-windows |
928 | * (in freeglut only) will not get an initial reshape event, |
929 | * which can break things. |
930 | * |
931 | * GLUT presumably does this because it generally tries to treat |
932 | * sub-windows the same as windows. |
933 | */ |
934 | case CreateNotify: |
935 | case ConfigureNotify: |
936 | { |
937 | int width, height, x, y; |
938 | if( event.type == CreateNotify ) { |
939 | GETWINDOW( xcreatewindow ); |
940 | width = event.xcreatewindow.width; |
941 | height = event.xcreatewindow.height; |
942 | x = event.xcreatewindow.x; |
943 | y = event.xcreatewindow.y; |
944 | } else { |
945 | GETWINDOW( xconfigure ); |
946 | width = event.xconfigure.width; |
947 | height = event.xconfigure.height; |
948 | x = event.xconfigure.x; |
949 | y = event.xconfigure.y; |
950 | } |
951 | |
952 | /* Update state and call callback, if there was a change */ |
953 | fghOnPositionNotify(window, x, y, GL_FALSE); |
954 | /* Update state and call callback, if there was a change */ |
955 | fghOnReshapeNotify(window, width, height, GL_FALSE); |
956 | } |
957 | break; |
958 | |
959 | case DestroyNotify: |
960 | /* |
961 | * This is sent to confirm the XDestroyWindow call. |
962 | * |
963 | * XXX WHY is this commented out? Should we re-enable it? |
964 | */ |
965 | /* fgAddToWindowDestroyList ( window ); */ |
966 | break; |
967 | |
968 | case Expose: |
969 | /* |
970 | * We are too dumb to process partial exposes... |
971 | * |
972 | * XXX Well, we could do it. However, it seems to only |
973 | * XXX be potentially useful for single-buffered (since |
974 | * XXX double-buffered does not respect viewport when we |
975 | * XXX do a buffer-swap). |
976 | * |
977 | */ |
978 | if( event.xexpose.count == 0 ) |
979 | { |
980 | GETWINDOW( xexpose ); |
981 | window->State.WorkMask |= GLUT_DISPLAY_WORK; |
982 | } |
983 | break; |
984 | |
985 | case MapNotify: |
986 | break; |
987 | |
988 | case UnmapNotify: |
989 | /* We get this when iconifying a window. */ |
990 | GETWINDOW( xunmap ); |
991 | INVOKE_WCB( *window, WindowStatus, ( GLUT_HIDDEN ) ); |
992 | window->State.Visible = GL_FALSE; |
993 | break; |
994 | |
995 | case MappingNotify: |
996 | /* |
997 | * Have the client's keyboard knowledge updated (xlib.ps, |
998 | * page 206, says that's a good thing to do) |
999 | */ |
1000 | XRefreshKeyboardMapping( (XMappingEvent *) &event ); |
1001 | break; |
1002 | |
1003 | case VisibilityNotify: |
1004 | { |
1005 | /* |
1006 | * Sending this event, the X server can notify us that the window |
1007 | * has just acquired one of the three possible visibility states: |
1008 | * VisibilityUnobscured, VisibilityPartiallyObscured or |
1009 | * VisibilityFullyObscured. Note that we DO NOT receive a |
1010 | * VisibilityNotify event when iconifying a window, we only get an |
1011 | * UnmapNotify then. |
1012 | */ |
1013 | GETWINDOW( xvisibility ); |
1014 | switch( event.xvisibility.state ) |
1015 | { |
1016 | case VisibilityUnobscured: |
1017 | INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_RETAINED ) ); |
1018 | window->State.Visible = GL_TRUE; |
1019 | break; |
1020 | |
1021 | case VisibilityPartiallyObscured: |
1022 | INVOKE_WCB( *window, WindowStatus, |
1023 | ( GLUT_PARTIALLY_RETAINED ) ); |
1024 | window->State.Visible = GL_TRUE; |
1025 | break; |
1026 | |
1027 | case VisibilityFullyObscured: |
1028 | INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_COVERED ) ); |
1029 | window->State.Visible = GL_FALSE; |
1030 | break; |
1031 | |
1032 | default: |
1033 | fgWarning( "Unknown X visibility state: %d" , |
1034 | event.xvisibility.state ); |
1035 | break; |
1036 | } |
1037 | } |
1038 | break; |
1039 | |
1040 | case EnterNotify: |
1041 | case LeaveNotify: |
1042 | GETWINDOW( xcrossing ); |
1043 | GETMOUSE( xcrossing ); |
1044 | if( ( event.type == LeaveNotify ) && window->IsMenu && |
1045 | window->ActiveMenu && window->ActiveMenu->IsActive ) |
1046 | fgUpdateMenuHighlight( window->ActiveMenu ); |
1047 | |
1048 | INVOKE_WCB( *window, Entry, ( ( EnterNotify == event.type ) ? |
1049 | GLUT_ENTERED : |
1050 | GLUT_LEFT ) ); |
1051 | break; |
1052 | |
1053 | case MotionNotify: |
1054 | { |
1055 | /* if GLUT_SKIP_STALE_MOTION_EVENTS is true, then discard all but |
1056 | * the last motion event from the queue |
1057 | */ |
1058 | if(fgState.SkipStaleMotion) { |
1059 | while(XCheckIfEvent(fgDisplay.pDisplay.Display, &event, match_motion, 0)); |
1060 | } |
1061 | |
1062 | GETWINDOW( xmotion ); |
1063 | GETMOUSE( xmotion ); |
1064 | |
1065 | if( window->ActiveMenu ) |
1066 | { |
1067 | if( window == window->ActiveMenu->ParentWindow ) |
1068 | { |
1069 | window->ActiveMenu->Window->State.MouseX = |
1070 | event.xmotion.x_root - window->ActiveMenu->X; |
1071 | window->ActiveMenu->Window->State.MouseY = |
1072 | event.xmotion.y_root - window->ActiveMenu->Y; |
1073 | } |
1074 | |
1075 | fgUpdateMenuHighlight( window->ActiveMenu ); |
1076 | |
1077 | break; |
1078 | } |
1079 | |
1080 | /* |
1081 | * XXX For more than 5 buttons, just check {event.xmotion.state}, |
1082 | * XXX rather than a host of bit-masks? Or maybe we need to |
1083 | * XXX track ButtonPress/ButtonRelease events in our own |
1084 | * XXX bit-mask? |
1085 | */ |
1086 | fgState.Modifiers = fgPlatformGetModifiers( event.xmotion.state ); |
1087 | if ( event.xmotion.state & ( Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask ) ) { |
1088 | INVOKE_WCB( *window, Motion, ( event.xmotion.x, |
1089 | event.xmotion.y ) ); |
1090 | } else { |
1091 | INVOKE_WCB( *window, Passive, ( event.xmotion.x, |
1092 | event.xmotion.y ) ); |
1093 | } |
1094 | fgState.Modifiers = INVALID_MODIFIERS; |
1095 | } |
1096 | break; |
1097 | |
1098 | case ButtonRelease: |
1099 | case ButtonPress: |
1100 | { |
1101 | GLboolean pressed = GL_TRUE; |
1102 | int button; |
1103 | |
1104 | if( event.type == ButtonRelease ) |
1105 | pressed = GL_FALSE ; |
1106 | |
1107 | /* |
1108 | * A mouse button has been pressed or released. Traditionally, |
1109 | * break if the window was found within the freeglut structures. |
1110 | */ |
1111 | GETWINDOW( xbutton ); |
1112 | GETMOUSE( xbutton ); |
1113 | |
1114 | /* |
1115 | * An X button (at least in XFree86) is numbered from 1. |
1116 | * A GLUT button is numbered from 0. |
1117 | * Old GLUT passed through buttons other than just the first |
1118 | * three, though it only gave symbolic names and official |
1119 | * support to the first three. |
1120 | */ |
1121 | button = event.xbutton.button - 1; |
1122 | |
1123 | /* |
1124 | * Do not execute the application's mouse callback if a menu |
1125 | * is hooked to this button. In that case an appropriate |
1126 | * private call should be generated. |
1127 | */ |
1128 | if( fgCheckActiveMenu( window, button, pressed, |
1129 | event.xbutton.x, event.xbutton.y ) ) |
1130 | break; |
1131 | |
1132 | /* |
1133 | * Check if there is a mouse or mouse wheel callback hooked to the |
1134 | * window |
1135 | */ |
1136 | if( ! FETCH_WCB( *window, Mouse ) && |
1137 | ! FETCH_WCB( *window, MouseWheel ) ) |
1138 | break; |
1139 | |
1140 | fgState.Modifiers = fgPlatformGetModifiers( event.xbutton.state ); |
1141 | |
1142 | /* Finally execute the mouse or mouse wheel callback */ |
1143 | if( ( button < glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS ) ) || ( ! FETCH_WCB( *window, MouseWheel ) ) ) |
1144 | INVOKE_WCB( *window, Mouse, ( button, |
1145 | pressed ? GLUT_DOWN : GLUT_UP, |
1146 | event.xbutton.x, |
1147 | event.xbutton.y ) |
1148 | ); |
1149 | else |
1150 | { |
1151 | /* |
1152 | * Map 4 and 5 to wheel zero; EVEN to +1, ODD to -1 |
1153 | * " 6 and 7 " " one; ... |
1154 | * |
1155 | * XXX This *should* be behind some variables/macros, |
1156 | * XXX since the order and numbering isn't certain |
1157 | * XXX See XFree86 configuration docs (even back in the |
1158 | * XXX 3.x days, and especially with 4.x). |
1159 | * |
1160 | * XXX Note that {button} has already been decremented |
1161 | * XXX in mapping from X button numbering to GLUT. |
1162 | * |
1163 | * XXX Should add support for partial wheel turns as Windows does -- 5/27/11 |
1164 | */ |
1165 | int wheel_number = (button - glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS )) / 2; |
1166 | int direction = -1; |
1167 | if( button % 2 ) |
1168 | direction = 1; |
1169 | |
1170 | if( pressed ) |
1171 | INVOKE_WCB( *window, MouseWheel, ( wheel_number, |
1172 | direction, |
1173 | event.xbutton.x, |
1174 | event.xbutton.y ) |
1175 | ); |
1176 | } |
1177 | fgState.Modifiers = INVALID_MODIFIERS; |
1178 | } |
1179 | break; |
1180 | |
1181 | case KeyRelease: |
1182 | case KeyPress: |
1183 | { |
1184 | FGCBKeyboardExt keyboard_ext_cb; |
1185 | FGCBKeyboard keyboard_cb, keyboard_low_cb; |
1186 | FGCBSpecial special_cb; |
1187 | int did_keyboard_cb = 0; |
1188 | |
1189 | GETWINDOW( xkey ); |
1190 | GETMOUSE( xkey ); |
1191 | |
1192 | /* Detect auto repeated keys, if configured globally or per-window */ |
1193 | |
1194 | if ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) |
1195 | { |
1196 | if (event.type==KeyRelease) |
1197 | { |
1198 | /* |
1199 | * Look at X11 keystate to detect repeat mode. |
1200 | * While X11 says the key is actually held down, we'll ignore KeyRelease/KeyPress pairs. |
1201 | */ |
1202 | |
1203 | char keys[32]; |
1204 | XQueryKeymap( fgDisplay.pDisplay.Display, keys ); /* Look at X11 keystate to detect repeat mode */ |
1205 | |
1206 | if ( event.xkey.keycode<256 ) /* XQueryKeymap is limited to 256 keycodes */ |
1207 | { |
1208 | if ( keys[event.xkey.keycode>>3] & (1<<(event.xkey.keycode%8)) ) |
1209 | window->State.pWState.KeyRepeating = GL_TRUE; |
1210 | else |
1211 | window->State.pWState.KeyRepeating = GL_FALSE; |
1212 | } |
1213 | } |
1214 | } |
1215 | else |
1216 | window->State.pWState.KeyRepeating = GL_FALSE; |
1217 | |
1218 | /* Cease processing this event if it is auto repeated */ |
1219 | |
1220 | if (window->State.pWState.KeyRepeating) |
1221 | { |
1222 | if (event.type == KeyPress) window->State.pWState.KeyRepeating = GL_FALSE; |
1223 | break; |
1224 | } |
1225 | |
1226 | if( event.type == KeyPress ) |
1227 | { |
1228 | keyboard_ext_cb = (FGCBKeyboardExt)( FETCH_WCB( *window, KeyboardExt )); |
1229 | keyboard_low_cb = (FGCBKeyboard)( FETCH_WCB( *window, KeyboardDown )); |
1230 | keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, Keyboard )); |
1231 | special_cb = (FGCBSpecial) ( FETCH_WCB( *window, Special )); |
1232 | } |
1233 | else |
1234 | { |
1235 | keyboard_ext_cb = NULL; |
1236 | keyboard_low_cb = (FGCBKeyboard)( FETCH_WCB( *window, KeyboardUp )); |
1237 | keyboard_cb = NULL; |
1238 | special_cb = (FGCBSpecial) ( FETCH_WCB( *window, SpecialUp )); |
1239 | } |
1240 | |
1241 | /* Is there a character keyboard callback hooked for this window? */ |
1242 | if (keyboard_ext_cb || keyboard_cb) |
1243 | { |
1244 | static XComposeStatus composeStatus = { 0 }; /* keep state across invocations */ |
1245 | XIC ic = window->Window.pContext.IC; |
1246 | Status status; |
1247 | char buf[32], *utf8 = buf; |
1248 | KeySym keySym; |
1249 | int i, c, len; |
1250 | |
1251 | /* Check for the Unicode text associated with the event: */ |
1252 | if (ic) |
1253 | { |
1254 | len = Xutf8LookupString(ic, &event.xkey, buf, sizeof buf, &keySym, &status); |
1255 | if (status == XBufferOverflow) |
1256 | { |
1257 | utf8 = malloc(len); |
1258 | len = Xutf8LookupString(ic, &event.xkey, utf8, len, &keySym, &status); |
1259 | } |
1260 | } |
1261 | else |
1262 | { |
1263 | len = XLookupString(&event.xkey, buf, sizeof buf, &keySym, &composeStatus); |
1264 | } |
1265 | |
1266 | if (len > 0) |
1267 | { |
1268 | fgSetWindow(window); |
1269 | fgState.Modifiers = fgPlatformGetModifiers(event.xkey.state); |
1270 | |
1271 | i = 0; |
1272 | while (i < len) |
1273 | { |
1274 | i += chartorune(&c, utf8 + i); |
1275 | |
1276 | /* ...for the Unicode translateable keypresses... */ |
1277 | if (keyboard_ext_cb) |
1278 | keyboard_ext_cb(c, event.xkey.x, event.xkey.y); |
1279 | |
1280 | /* ...for the Latin-1 translateable keypresses... */ |
1281 | if (keyboard_cb) |
1282 | if (c < 256) |
1283 | keyboard_cb(c, event.xkey.x, event.xkey.y); |
1284 | } |
1285 | |
1286 | fgState.Modifiers = INVALID_MODIFIERS; |
1287 | |
1288 | did_keyboard_cb = 1; |
1289 | } |
1290 | |
1291 | if (utf8 != buf) |
1292 | free(utf8); |
1293 | } |
1294 | |
1295 | /* Is there a low-level keyboard callback hooked for this window? */ |
1296 | if (keyboard_low_cb || special_cb) |
1297 | { |
1298 | int special = -1; |
1299 | int ascii = 0; |
1300 | |
1301 | KeySym keySym = XLookupKeysym(&event.xkey, 0); |
1302 | |
1303 | /* ...for low-level keys, which need to be |
1304 | * translated to GLUT_KEY_Xs or ASCII values... |
1305 | */ |
1306 | switch( keySym ) |
1307 | { |
1308 | case XK_F1: special = GLUT_KEY_F1; break; |
1309 | case XK_F2: special = GLUT_KEY_F2; break; |
1310 | case XK_F3: special = GLUT_KEY_F3; break; |
1311 | case XK_F4: special = GLUT_KEY_F4; break; |
1312 | case XK_F5: special = GLUT_KEY_F5; break; |
1313 | case XK_F6: special = GLUT_KEY_F6; break; |
1314 | case XK_F7: special = GLUT_KEY_F7; break; |
1315 | case XK_F8: special = GLUT_KEY_F8; break; |
1316 | case XK_F9: special = GLUT_KEY_F9; break; |
1317 | case XK_F10: special = GLUT_KEY_F10; break; |
1318 | case XK_F11: special = GLUT_KEY_F11; break; |
1319 | case XK_F12: special = GLUT_KEY_F12; break; |
1320 | |
1321 | case XK_KP_Left: |
1322 | case XK_Left: special = GLUT_KEY_LEFT; break; |
1323 | case XK_KP_Right: |
1324 | case XK_Right: special = GLUT_KEY_RIGHT; break; |
1325 | case XK_KP_Up: |
1326 | case XK_Up: special = GLUT_KEY_UP; break; |
1327 | case XK_KP_Down: |
1328 | case XK_Down: special = GLUT_KEY_DOWN; break; |
1329 | |
1330 | case XK_KP_Prior: |
1331 | case XK_Prior: special = GLUT_KEY_PAGE_UP; break; |
1332 | case XK_KP_Next: |
1333 | case XK_Next: special = GLUT_KEY_PAGE_DOWN; break; |
1334 | case XK_KP_Home: |
1335 | case XK_Home: special = GLUT_KEY_HOME; break; |
1336 | case XK_KP_End: |
1337 | case XK_End: special = GLUT_KEY_END; break; |
1338 | case XK_KP_Insert: |
1339 | case XK_Insert: special = GLUT_KEY_INSERT; break; |
1340 | |
1341 | case XK_Num_Lock : special = GLUT_KEY_NUM_LOCK; break; |
1342 | case XK_KP_Begin : special = GLUT_KEY_BEGIN; break; |
1343 | case XK_KP_Delete: special = GLUT_KEY_DELETE; break; |
1344 | |
1345 | case XK_Shift_L: special = GLUT_KEY_SHIFT_L; break; |
1346 | case XK_Shift_R: special = GLUT_KEY_SHIFT_R; break; |
1347 | case XK_Control_L: special = GLUT_KEY_CTRL_L; break; |
1348 | case XK_Control_R: special = GLUT_KEY_CTRL_R; break; |
1349 | case XK_Alt_L: special = GLUT_KEY_ALT_L; break; |
1350 | case XK_Alt_R: special = GLUT_KEY_ALT_R; break; |
1351 | default: |
1352 | if( keySym >= XK_space && keySym <= XK_ydiaeresis ) |
1353 | ascii = keySym; |
1354 | break; |
1355 | } |
1356 | |
1357 | /* |
1358 | * Execute the callback (if one has been specified), |
1359 | * given that the special code seems to be valid... |
1360 | * But only if we haven't already sent translated text for it, |
1361 | * such as numeric keypad keys with numlock on. |
1362 | */ |
1363 | if( special_cb && (special != -1) && !did_keyboard_cb ) |
1364 | { |
1365 | fgSetWindow( window ); |
1366 | fgState.Modifiers = fgPlatformGetModifiers( event.xkey.state ); |
1367 | special_cb( special, event.xkey.x, event.xkey.y ); |
1368 | fgState.Modifiers = INVALID_MODIFIERS; |
1369 | } |
1370 | else if( keyboard_low_cb && (ascii >= 32 && ascii < 256) ) |
1371 | { |
1372 | fgSetWindow( window ); |
1373 | fgState.Modifiers = fgPlatformGetModifiers( event.xkey.state ); |
1374 | keyboard_low_cb( ascii, event.xkey.x, event.xkey.y ); |
1375 | fgState.Modifiers = INVALID_MODIFIERS; |
1376 | } |
1377 | } |
1378 | } |
1379 | break; |
1380 | |
1381 | case ReparentNotify: |
1382 | break; /* XXX Should disable this event */ |
1383 | |
1384 | /* Not handled */ |
1385 | case GravityNotify: |
1386 | break; |
1387 | |
1388 | default: |
1389 | /* enter handling of Extension Events here */ |
1390 | #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H |
1391 | fgHandleExtensionEvents( &event ); |
1392 | #endif |
1393 | break; |
1394 | } |
1395 | } |
1396 | } |
1397 | |
1398 | |
1399 | static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg) |
1400 | { |
1401 | return xev->type == MotionNotify; |
1402 | } |
1403 | |
1404 | void fgPlatformMainLoopPreliminaryWork ( void ) |
1405 | { |
1406 | } |
1407 | |
1408 | |
1409 | /* deal with work list items */ |
1410 | void fgPlatformInitWork(SFG_Window* window) |
1411 | { |
1412 | /* Notify windowStatus/visibility, position and size get notified on window creation with message handlers above |
1413 | * XXX CHECK: do the messages happen too early like on windows, so client code cannot have registered |
1414 | * a callback yet and the message is thus never received by client? |
1415 | * -> this is a no-op |
1416 | */ |
1417 | return; |
1418 | } |
1419 | |
1420 | void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask) |
1421 | { |
1422 | if (workMask & GLUT_FULL_SCREEN_WORK) |
1423 | fgPlatformFullScreenToggle( window ); |
1424 | if (workMask & GLUT_POSITION_WORK) |
1425 | fgPlatformPositionWindow( window, window->State.DesiredXpos, window->State.DesiredYpos ); |
1426 | if (workMask & GLUT_SIZE_WORK) |
1427 | fgPlatformReshapeWindow ( window, window->State.DesiredWidth, window->State.DesiredHeight ); |
1428 | if (workMask & GLUT_ZORDER_WORK) |
1429 | { |
1430 | if (window->State.DesiredZOrder < 0) |
1431 | fgPlatformPushWindow( window ); |
1432 | else |
1433 | fgPlatformPopWindow( window ); |
1434 | } |
1435 | } |
1436 | |
1437 | void fgPlatformVisibilityWork(SFG_Window* window) |
1438 | { |
1439 | /* Visibility status of window gets updated in the window message handlers above |
1440 | * XXX: is this really the case? check |
1441 | */ |
1442 | SFG_Window *win = window; |
1443 | switch (window->State.DesiredVisibility) |
1444 | { |
1445 | case DesireHiddenState: |
1446 | fgPlatformHideWindow( window ); |
1447 | break; |
1448 | case DesireIconicState: |
1449 | /* Call on top-level window */ |
1450 | while (win->Parent) |
1451 | win = win->Parent; |
1452 | fgPlatformIconifyWindow( win ); |
1453 | break; |
1454 | case DesireNormalState: |
1455 | fgPlatformShowWindow( window ); |
1456 | break; |
1457 | } |
1458 | } |
1459 | |
1460 | |