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