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
50extern void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify);
51extern void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify);
52extern void fgPlatformFullScreenToggle( SFG_Window *win );
53extern void fgPlatformPositionWindow( SFG_Window *window, int x, int y );
54extern void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height );
55extern void fgPlatformPushWindow( SFG_Window *window );
56extern void fgPlatformPopWindow( SFG_Window *window );
57extern void fgPlatformHideWindow( SFG_Window *window );
58extern void fgPlatformIconifyWindow( SFG_Window *window );
59extern void fgPlatformShowWindow( SFG_Window *window );
60
61/* used in the event handling code to match and discard stale mouse motion events */
62static 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
76fg_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
94void 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 */
128int 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
142static 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
182static const char* fghBoolToString( Bool b )
183{
184 return b == False ? "False" : "True";
185}
186
187static 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
196static 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
207static 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
222static 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
231static 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
243static 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
252static 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
262static 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
271static 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 */
583enum
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
612static 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 */
681bad:
682 *rune = Bad;
683 return 1;
684}
685
686extern char *fgClipboardBuffer[3];
687
688static Atom fghGetAtom(const char *name)
689{
690 return XInternAtom(fgDisplay.pDisplay.Display, name, False);
691}
692
693static 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
734static 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
753static 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
807void 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
827static Bool isSelectionNotify(Display *dpi, XEvent *event, XPointer arg)
828{
829 return (event->type == SelectionNotify);
830}
831
832const 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
862void 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
1399static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg)
1400{
1401 return xev->type == MotionNotify;
1402}
1403
1404void fgPlatformMainLoopPreliminaryWork ( void )
1405{
1406}
1407
1408
1409/* deal with work list items */
1410void 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
1420void 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
1437void 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