SDL_x11events.c (55955B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20 */ 21 #include "../../SDL_internal.h" 22 23 #if SDL_VIDEO_DRIVER_X11 24 25 #include <sys/types.h> 26 #include <sys/time.h> 27 #include <signal.h> 28 #include <unistd.h> 29 #include <limits.h> /* For INT_MAX */ 30 31 #include "SDL_x11video.h" 32 #include "SDL_x11touch.h" 33 #include "SDL_x11xinput2.h" 34 #include "../../core/unix/SDL_poll.h" 35 #include "../../events/SDL_events_c.h" 36 #include "../../events/SDL_mouse_c.h" 37 #include "../../events/SDL_touch_c.h" 38 39 #include "SDL_hints.h" 40 #include "SDL_timer.h" 41 #include "SDL_syswm.h" 42 43 #include <stdio.h> 44 45 /*#define DEBUG_XEVENTS*/ 46 47 #ifndef _NET_WM_MOVERESIZE_SIZE_TOPLEFT 48 #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 49 #endif 50 51 #ifndef _NET_WM_MOVERESIZE_SIZE_TOP 52 #define _NET_WM_MOVERESIZE_SIZE_TOP 1 53 #endif 54 55 #ifndef _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 56 #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 57 #endif 58 59 #ifndef _NET_WM_MOVERESIZE_SIZE_RIGHT 60 #define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 61 #endif 62 63 #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 64 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 65 #endif 66 67 #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOM 68 #define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 69 #endif 70 71 #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 72 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 73 #endif 74 75 #ifndef _NET_WM_MOVERESIZE_SIZE_LEFT 76 #define _NET_WM_MOVERESIZE_SIZE_LEFT 7 77 #endif 78 79 #ifndef _NET_WM_MOVERESIZE_MOVE 80 #define _NET_WM_MOVERESIZE_MOVE 8 81 #endif 82 83 typedef struct { 84 unsigned char *data; 85 int format, count; 86 Atom type; 87 } SDL_x11Prop; 88 89 /* Reads property 90 Must call X11_XFree on results 91 */ 92 static void X11_ReadProperty(SDL_x11Prop *p, Display *disp, Window w, Atom prop) 93 { 94 unsigned char *ret=NULL; 95 Atom type; 96 int fmt; 97 unsigned long count; 98 unsigned long bytes_left; 99 int bytes_fetch = 0; 100 101 do { 102 if (ret != 0) X11_XFree(ret); 103 X11_XGetWindowProperty(disp, w, prop, 0, bytes_fetch, False, AnyPropertyType, &type, &fmt, &count, &bytes_left, &ret); 104 bytes_fetch += bytes_left; 105 } while (bytes_left != 0); 106 107 p->data=ret; 108 p->format=fmt; 109 p->count=count; 110 p->type=type; 111 } 112 113 /* Find text-uri-list in a list of targets and return it's atom 114 if available, else return None */ 115 static Atom X11_PickTarget(Display *disp, Atom list[], int list_count) 116 { 117 Atom request = None; 118 char *name; 119 int i; 120 for (i=0; i < list_count && request == None; i++) { 121 name = X11_XGetAtomName(disp, list[i]); 122 if ((SDL_strcmp("text/uri-list", name) == 0) || (SDL_strcmp("text/plain", name) == 0)) { 123 request = list[i]; 124 } 125 X11_XFree(name); 126 } 127 return request; 128 } 129 130 /* Wrapper for X11_PickTarget for a maximum of three targets, a special 131 case in the Xdnd protocol */ 132 static Atom X11_PickTargetFromAtoms(Display *disp, Atom a0, Atom a1, Atom a2) 133 { 134 int count=0; 135 Atom atom[3]; 136 if (a0 != None) atom[count++] = a0; 137 if (a1 != None) atom[count++] = a1; 138 if (a2 != None) atom[count++] = a2; 139 return X11_PickTarget(disp, atom, count); 140 } 141 142 struct KeyRepeatCheckData 143 { 144 XEvent *event; 145 SDL_bool found; 146 }; 147 148 static Bool X11_KeyRepeatCheckIfEvent(Display *display, XEvent *chkev, 149 XPointer arg) 150 { 151 struct KeyRepeatCheckData *d = (struct KeyRepeatCheckData *) arg; 152 if (chkev->type == KeyPress && 153 chkev->xkey.keycode == d->event->xkey.keycode && 154 chkev->xkey.time - d->event->xkey.time < 2) 155 d->found = SDL_TRUE; 156 return False; 157 } 158 159 /* Check to see if this is a repeated key. 160 (idea shamelessly lifted from GII -- thanks guys! :) 161 */ 162 static SDL_bool X11_KeyRepeat(Display *display, XEvent *event) 163 { 164 XEvent dummyev; 165 struct KeyRepeatCheckData d; 166 d.event = event; 167 d.found = SDL_FALSE; 168 if (X11_XPending(display)) 169 X11_XCheckIfEvent(display, &dummyev, X11_KeyRepeatCheckIfEvent, 170 (XPointer) &d); 171 return d.found; 172 } 173 174 static SDL_bool 175 X11_IsWheelEvent(Display * display,XEvent * event,int * xticks,int * yticks) 176 { 177 /* according to the xlib docs, no specific mouse wheel events exist. 178 However, the defacto standard is that the vertical wheel is X buttons 179 4 (up) and 5 (down) and a horizontal wheel is 6 (left) and 7 (right). */ 180 181 /* Xlib defines "Button1" through 5, so we just use literals here. */ 182 switch (event->xbutton.button) { 183 case 4: *yticks = 1; return SDL_TRUE; 184 case 5: *yticks = -1; return SDL_TRUE; 185 case 6: *xticks = 1; return SDL_TRUE; 186 case 7: *xticks = -1; return SDL_TRUE; 187 default: break; 188 } 189 return SDL_FALSE; 190 } 191 192 /* Decodes URI escape sequences in string buf of len bytes 193 (excluding the terminating NULL byte) in-place. Since 194 URI-encoded characters take three times the space of 195 normal characters, this should not be an issue. 196 197 Returns the number of decoded bytes that wound up in 198 the buffer, excluding the terminating NULL byte. 199 200 The buffer is guaranteed to be NULL-terminated but 201 may contain embedded NULL bytes. 202 203 On error, -1 is returned. 204 */ 205 static int X11_URIDecode(char *buf, int len) { 206 int ri, wi, di; 207 char decode = '\0'; 208 if (buf == NULL || len < 0) { 209 errno = EINVAL; 210 return -1; 211 } 212 if (len == 0) { 213 len = SDL_strlen(buf); 214 } 215 for (ri = 0, wi = 0, di = 0; ri < len && wi < len; ri += 1) { 216 if (di == 0) { 217 /* start decoding */ 218 if (buf[ri] == '%') { 219 decode = '\0'; 220 di += 1; 221 continue; 222 } 223 /* normal write */ 224 buf[wi] = buf[ri]; 225 wi += 1; 226 continue; 227 } else if (di == 1 || di == 2) { 228 char off = '\0'; 229 char isa = buf[ri] >= 'a' && buf[ri] <= 'f'; 230 char isA = buf[ri] >= 'A' && buf[ri] <= 'F'; 231 char isn = buf[ri] >= '0' && buf[ri] <= '9'; 232 if (!(isa || isA || isn)) { 233 /* not a hexadecimal */ 234 int sri; 235 for (sri = ri - di; sri <= ri; sri += 1) { 236 buf[wi] = buf[sri]; 237 wi += 1; 238 } 239 di = 0; 240 continue; 241 } 242 /* itsy bitsy magicsy */ 243 if (isn) { 244 off = 0 - '0'; 245 } else if (isa) { 246 off = 10 - 'a'; 247 } else if (isA) { 248 off = 10 - 'A'; 249 } 250 decode |= (buf[ri] + off) << (2 - di) * 4; 251 if (di == 2) { 252 buf[wi] = decode; 253 wi += 1; 254 di = 0; 255 } else { 256 di += 1; 257 } 258 continue; 259 } 260 } 261 buf[wi] = '\0'; 262 return wi; 263 } 264 265 /* Convert URI to local filename 266 return filename if possible, else NULL 267 */ 268 static char* X11_URIToLocal(char* uri) { 269 char *file = NULL; 270 SDL_bool local; 271 272 if (memcmp(uri,"file:/",6) == 0) uri += 6; /* local file? */ 273 else if (strstr(uri,":/") != NULL) return file; /* wrong scheme */ 274 275 local = uri[0] != '/' || (uri[0] != '\0' && uri[1] == '/'); 276 277 /* got a hostname? */ 278 if (!local && uri[0] == '/' && uri[2] != '/') { 279 char* hostname_end = strchr(uri+1, '/'); 280 if (hostname_end != NULL) { 281 char hostname[ 257 ]; 282 if (gethostname(hostname, 255) == 0) { 283 hostname[ 256 ] = '\0'; 284 if (memcmp(uri+1, hostname, hostname_end - (uri+1)) == 0) { 285 uri = hostname_end + 1; 286 local = SDL_TRUE; 287 } 288 } 289 } 290 } 291 if (local) { 292 file = uri; 293 /* Convert URI escape sequences to real characters */ 294 X11_URIDecode(file, 0); 295 if (uri[1] == '/') { 296 file++; 297 } else { 298 file--; 299 } 300 } 301 return file; 302 } 303 304 #if SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS 305 static void X11_HandleGenericEvent(SDL_VideoData *videodata, XEvent *xev) 306 { 307 /* event is a union, so cookie == &event, but this is type safe. */ 308 XGenericEventCookie *cookie = &xev->xcookie; 309 if (X11_XGetEventData(videodata->display, cookie)) { 310 X11_HandleXinput2Event(videodata, cookie); 311 312 /* Send a SDL_SYSWMEVENT if the application wants them. 313 * Since event data is only available until XFreeEventData is called, 314 * the *only* way for an application to access it is to register an event filter/watcher 315 * and do all the processing on the SDL_SYSWMEVENT inside the callback. */ 316 if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) { 317 SDL_SysWMmsg wmmsg; 318 319 SDL_VERSION(&wmmsg.version); 320 wmmsg.subsystem = SDL_SYSWM_X11; 321 wmmsg.msg.x11.event = *xev; 322 SDL_SendSysWMEvent(&wmmsg); 323 } 324 325 X11_XFreeEventData(videodata->display, cookie); 326 } 327 } 328 #endif /* SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS */ 329 330 static unsigned 331 X11_GetNumLockModifierMask(_THIS) 332 { 333 SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata; 334 Display *display = viddata->display; 335 unsigned num_mask = 0; 336 int i, j; 337 XModifierKeymap *xmods; 338 unsigned n; 339 340 xmods = X11_XGetModifierMapping(display); 341 n = xmods->max_keypermod; 342 for(i = 3; i < 8; i++) { 343 for(j = 0; j < n; j++) { 344 KeyCode kc = xmods->modifiermap[i * n + j]; 345 if (viddata->key_layout[kc] == SDL_SCANCODE_NUMLOCKCLEAR) { 346 num_mask = 1 << i; 347 break; 348 } 349 } 350 } 351 X11_XFreeModifiermap(xmods); 352 353 return num_mask; 354 } 355 356 static void 357 X11_ReconcileKeyboardState(_THIS) 358 { 359 SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata; 360 Display *display = viddata->display; 361 char keys[32]; 362 int keycode; 363 Window junk_window; 364 int x, y; 365 unsigned int mask; 366 const Uint8 *keyboardState; 367 368 X11_XQueryKeymap(display, keys); 369 370 /* Sync up the keyboard modifier state */ 371 if (X11_XQueryPointer(display, DefaultRootWindow(display), &junk_window, &junk_window, &x, &y, &x, &y, &mask)) { 372 SDL_ToggleModState(KMOD_CAPS, (mask & LockMask) != 0); 373 SDL_ToggleModState(KMOD_NUM, (mask & X11_GetNumLockModifierMask(_this)) != 0); 374 } 375 376 keyboardState = SDL_GetKeyboardState(0); 377 for (keycode = 0; keycode < 256; ++keycode) { 378 SDL_Scancode scancode = viddata->key_layout[keycode]; 379 SDL_bool x11KeyPressed = (keys[keycode / 8] & (1 << (keycode % 8))) != 0; 380 SDL_bool sdlKeyPressed = keyboardState[scancode] == SDL_PRESSED; 381 382 if (x11KeyPressed && !sdlKeyPressed) { 383 SDL_SendKeyboardKey(SDL_PRESSED, scancode); 384 } else if (!x11KeyPressed && sdlKeyPressed) { 385 SDL_SendKeyboardKey(SDL_RELEASED, scancode); 386 } 387 } 388 } 389 390 391 static void 392 X11_DispatchFocusIn(_THIS, SDL_WindowData *data) 393 { 394 #ifdef DEBUG_XEVENTS 395 printf("window %p: Dispatching FocusIn\n", data); 396 #endif 397 SDL_SetKeyboardFocus(data->window); 398 X11_ReconcileKeyboardState(_this); 399 #ifdef X_HAVE_UTF8_STRING 400 if (data->ic) { 401 X11_XSetICFocus(data->ic); 402 } 403 #endif 404 #ifdef SDL_USE_IME 405 SDL_IME_SetFocus(SDL_TRUE); 406 #endif 407 } 408 409 static void 410 X11_DispatchFocusOut(_THIS, SDL_WindowData *data) 411 { 412 #ifdef DEBUG_XEVENTS 413 printf("window %p: Dispatching FocusOut\n", data); 414 #endif 415 /* If another window has already processed a focus in, then don't try to 416 * remove focus here. Doing so will incorrectly remove focus from that 417 * window, and the focus lost event for this window will have already 418 * been dispatched anyway. */ 419 if (data->window == SDL_GetKeyboardFocus()) { 420 SDL_SetKeyboardFocus(NULL); 421 } 422 #ifdef X_HAVE_UTF8_STRING 423 if (data->ic) { 424 X11_XUnsetICFocus(data->ic); 425 } 426 #endif 427 #ifdef SDL_USE_IME 428 SDL_IME_SetFocus(SDL_FALSE); 429 #endif 430 } 431 432 static void 433 X11_DispatchMapNotify(SDL_WindowData *data) 434 { 435 SDL_Window *window = data->window; 436 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0); 437 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_SHOWN, 0, 0); 438 if (!(window->flags & SDL_WINDOW_HIDDEN) && (window->flags & SDL_WINDOW_INPUT_FOCUS)) { 439 SDL_UpdateWindowGrab(window); 440 } 441 } 442 443 static void 444 X11_DispatchUnmapNotify(SDL_WindowData *data) 445 { 446 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0); 447 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MINIMIZED, 0, 0); 448 } 449 450 static void 451 InitiateWindowMove(_THIS, const SDL_WindowData *data, const SDL_Point *point) 452 { 453 SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata; 454 SDL_Window* window = data->window; 455 Display *display = viddata->display; 456 XEvent evt; 457 458 /* !!! FIXME: we need to regrab this if necessary when the drag is done. */ 459 X11_XUngrabPointer(display, 0L); 460 X11_XFlush(display); 461 462 evt.xclient.type = ClientMessage; 463 evt.xclient.window = data->xwindow; 464 evt.xclient.message_type = X11_XInternAtom(display, "_NET_WM_MOVERESIZE", True); 465 evt.xclient.format = 32; 466 evt.xclient.data.l[0] = window->x + point->x; 467 evt.xclient.data.l[1] = window->y + point->y; 468 evt.xclient.data.l[2] = _NET_WM_MOVERESIZE_MOVE; 469 evt.xclient.data.l[3] = Button1; 470 evt.xclient.data.l[4] = 0; 471 X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt); 472 473 X11_XSync(display, 0); 474 } 475 476 static void 477 InitiateWindowResize(_THIS, const SDL_WindowData *data, const SDL_Point *point, int direction) 478 { 479 SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata; 480 SDL_Window* window = data->window; 481 Display *display = viddata->display; 482 XEvent evt; 483 484 if (direction < _NET_WM_MOVERESIZE_SIZE_TOPLEFT || direction > _NET_WM_MOVERESIZE_SIZE_LEFT) 485 return; 486 487 /* !!! FIXME: we need to regrab this if necessary when the drag is done. */ 488 X11_XUngrabPointer(display, 0L); 489 X11_XFlush(display); 490 491 evt.xclient.type = ClientMessage; 492 evt.xclient.window = data->xwindow; 493 evt.xclient.message_type = X11_XInternAtom(display, "_NET_WM_MOVERESIZE", True); 494 evt.xclient.format = 32; 495 evt.xclient.data.l[0] = window->x + point->x; 496 evt.xclient.data.l[1] = window->y + point->y; 497 evt.xclient.data.l[2] = direction; 498 evt.xclient.data.l[3] = Button1; 499 evt.xclient.data.l[4] = 0; 500 X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt); 501 502 X11_XSync(display, 0); 503 } 504 505 static SDL_bool 506 ProcessHitTest(_THIS, const SDL_WindowData *data, const XEvent *xev) 507 { 508 SDL_Window *window = data->window; 509 510 if (window->hit_test) { 511 const SDL_Point point = { xev->xbutton.x, xev->xbutton.y }; 512 const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data); 513 static const int directions[] = { 514 _NET_WM_MOVERESIZE_SIZE_TOPLEFT, _NET_WM_MOVERESIZE_SIZE_TOP, 515 _NET_WM_MOVERESIZE_SIZE_TOPRIGHT, _NET_WM_MOVERESIZE_SIZE_RIGHT, 516 _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT, _NET_WM_MOVERESIZE_SIZE_BOTTOM, 517 _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT, _NET_WM_MOVERESIZE_SIZE_LEFT 518 }; 519 520 switch (rc) { 521 case SDL_HITTEST_DRAGGABLE: 522 InitiateWindowMove(_this, data, &point); 523 return SDL_TRUE; 524 525 case SDL_HITTEST_RESIZE_TOPLEFT: 526 case SDL_HITTEST_RESIZE_TOP: 527 case SDL_HITTEST_RESIZE_TOPRIGHT: 528 case SDL_HITTEST_RESIZE_RIGHT: 529 case SDL_HITTEST_RESIZE_BOTTOMRIGHT: 530 case SDL_HITTEST_RESIZE_BOTTOM: 531 case SDL_HITTEST_RESIZE_BOTTOMLEFT: 532 case SDL_HITTEST_RESIZE_LEFT: 533 InitiateWindowResize(_this, data, &point, directions[rc - SDL_HITTEST_RESIZE_TOPLEFT]); 534 return SDL_TRUE; 535 536 default: return SDL_FALSE; 537 } 538 } 539 540 return SDL_FALSE; 541 } 542 543 static void 544 X11_UpdateUserTime(SDL_WindowData *data, const unsigned long latest) 545 { 546 if (latest && (latest != data->user_time)) { 547 SDL_VideoData *videodata = data->videodata; 548 Display *display = videodata->display; 549 X11_XChangeProperty(display, data->xwindow, videodata->_NET_WM_USER_TIME, 550 XA_CARDINAL, 32, PropModeReplace, 551 (const unsigned char *) &latest, 1); 552 #ifdef DEBUG_XEVENTS 553 printf("window %p: updating _NET_WM_USER_TIME to %lu\n", data, latest); 554 #endif 555 data->user_time = latest; 556 } 557 } 558 559 static void 560 X11_HandleClipboardEvent(_THIS, const XEvent *xevent) 561 { 562 SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; 563 Display *display = videodata->display; 564 565 SDL_assert(videodata->clipboard_window != None); 566 SDL_assert(xevent->xany.window == videodata->clipboard_window); 567 568 switch (xevent->type) { 569 /* Copy the selection from our own CUTBUFFER to the requested property */ 570 case SelectionRequest: { 571 const XSelectionRequestEvent *req = &xevent->xselectionrequest; 572 XEvent sevent; 573 int seln_format; 574 unsigned long nbytes; 575 unsigned long overflow; 576 unsigned char *seln_data; 577 578 #ifdef DEBUG_XEVENTS 579 printf("window CLIPBOARD: SelectionRequest (requestor = %ld, target = %ld)\n", 580 req->requestor, req->target); 581 #endif 582 583 SDL_zero(sevent); 584 sevent.xany.type = SelectionNotify; 585 sevent.xselection.selection = req->selection; 586 sevent.xselection.target = None; 587 sevent.xselection.property = None; /* tell them no by default */ 588 sevent.xselection.requestor = req->requestor; 589 sevent.xselection.time = req->time; 590 591 /* !!! FIXME: We were probably storing this on the root window 592 because an SDL window might go away...? but we don't have to do 593 this now (or ever, really). */ 594 if (X11_XGetWindowProperty(display, DefaultRootWindow(display), 595 X11_GetSDLCutBufferClipboardType(display), 0, INT_MAX/4, False, req->target, 596 &sevent.xselection.target, &seln_format, &nbytes, 597 &overflow, &seln_data) == Success) { 598 /* !!! FIXME: cache atoms */ 599 Atom XA_TARGETS = X11_XInternAtom(display, "TARGETS", 0); 600 if (sevent.xselection.target == req->target) { 601 X11_XChangeProperty(display, req->requestor, req->property, 602 sevent.xselection.target, seln_format, PropModeReplace, 603 seln_data, nbytes); 604 sevent.xselection.property = req->property; 605 } else if (XA_TARGETS == req->target) { 606 Atom SupportedFormats[] = { XA_TARGETS, sevent.xselection.target }; 607 X11_XChangeProperty(display, req->requestor, req->property, 608 XA_ATOM, 32, PropModeReplace, 609 (unsigned char*)SupportedFormats, 610 SDL_arraysize(SupportedFormats)); 611 sevent.xselection.property = req->property; 612 sevent.xselection.target = XA_TARGETS; 613 } 614 X11_XFree(seln_data); 615 } 616 X11_XSendEvent(display, req->requestor, False, 0, &sevent); 617 X11_XSync(display, False); 618 } 619 break; 620 621 case SelectionNotify: { 622 #ifdef DEBUG_XEVENTS 623 printf("window CLIPBOARD: SelectionNotify (requestor = %ld, target = %ld)\n", 624 xevent->xselection.requestor, xevent->xselection.target); 625 #endif 626 videodata->selection_waiting = SDL_FALSE; 627 } 628 break; 629 630 case SelectionClear: { 631 /* !!! FIXME: cache atoms */ 632 Atom XA_CLIPBOARD = X11_XInternAtom(display, "CLIPBOARD", 0); 633 634 #ifdef DEBUG_XEVENTS 635 printf("window CLIPBOARD: SelectionClear (requestor = %ld, target = %ld)\n", 636 xevent->xselection.requestor, xevent->xselection.target); 637 #endif 638 639 if (xevent->xselectionclear.selection == XA_PRIMARY || 640 (XA_CLIPBOARD != None && xevent->xselectionclear.selection == XA_CLIPBOARD)) { 641 SDL_SendClipboardUpdate(); 642 } 643 } 644 break; 645 } 646 } 647 648 static Bool 649 isMapNotify(Display *display, XEvent *ev, XPointer arg) 650 { 651 XUnmapEvent *unmap; 652 653 unmap = (XUnmapEvent*) arg; 654 655 return ev->type == MapNotify && 656 ev->xmap.window == unmap->window && 657 ev->xmap.serial == unmap->serial; 658 } 659 660 static Bool 661 isReparentNotify(Display *display, XEvent *ev, XPointer arg) 662 { 663 XUnmapEvent *unmap; 664 665 unmap = (XUnmapEvent*) arg; 666 667 return ev->type == ReparentNotify && 668 ev->xreparent.window == unmap->window && 669 ev->xreparent.serial == unmap->serial; 670 } 671 672 static void 673 X11_DispatchEvent(_THIS) 674 { 675 SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; 676 Display *display; 677 SDL_WindowData *data; 678 XEvent xevent; 679 int orig_event_type; 680 KeyCode orig_keycode; 681 XClientMessageEvent m; 682 int i; 683 684 if (!videodata) { 685 return; 686 } 687 display = videodata->display; 688 689 SDL_zero(xevent); /* valgrind fix. --ryan. */ 690 X11_XNextEvent(display, &xevent); 691 692 /* Save the original keycode for dead keys, which are filtered out by 693 the XFilterEvent() call below. 694 */ 695 orig_event_type = xevent.type; 696 if (orig_event_type == KeyPress || orig_event_type == KeyRelease) { 697 orig_keycode = xevent.xkey.keycode; 698 } else { 699 orig_keycode = 0; 700 } 701 702 /* filter events catchs XIM events and sends them to the correct handler */ 703 if (X11_XFilterEvent(&xevent, None) == True) { 704 #if 0 705 printf("Filtered event type = %d display = %d window = %d\n", 706 xevent.type, xevent.xany.display, xevent.xany.window); 707 #endif 708 /* Make sure dead key press/release events are sent */ 709 /* But only if we're using one of the DBus IMEs, otherwise 710 some XIM IMEs will generate duplicate events */ 711 if (orig_keycode) { 712 #if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX) 713 SDL_Scancode scancode = videodata->key_layout[orig_keycode]; 714 videodata->filter_code = orig_keycode; 715 videodata->filter_time = xevent.xkey.time; 716 717 if (orig_event_type == KeyPress) { 718 SDL_SendKeyboardKey(SDL_PRESSED, scancode); 719 } else { 720 SDL_SendKeyboardKey(SDL_RELEASED, scancode); 721 } 722 #endif 723 } 724 return; 725 } 726 727 #if SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS 728 if(xevent.type == GenericEvent) { 729 X11_HandleGenericEvent(videodata, &xevent); 730 return; 731 } 732 #endif 733 734 /* Send a SDL_SYSWMEVENT if the application wants them */ 735 if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) { 736 SDL_SysWMmsg wmmsg; 737 738 SDL_VERSION(&wmmsg.version); 739 wmmsg.subsystem = SDL_SYSWM_X11; 740 wmmsg.msg.x11.event = xevent; 741 SDL_SendSysWMEvent(&wmmsg); 742 } 743 744 #if 0 745 printf("type = %d display = %d window = %d\n", 746 xevent.type, xevent.xany.display, xevent.xany.window); 747 #endif 748 749 if ((videodata->clipboard_window != None) && 750 (videodata->clipboard_window == xevent.xany.window)) { 751 X11_HandleClipboardEvent(_this, &xevent); 752 return; 753 } 754 755 data = NULL; 756 if (videodata && videodata->windowlist) { 757 for (i = 0; i < videodata->numwindows; ++i) { 758 if ((videodata->windowlist[i] != NULL) && 759 (videodata->windowlist[i]->xwindow == xevent.xany.window)) { 760 data = videodata->windowlist[i]; 761 break; 762 } 763 } 764 } 765 if (!data) { 766 /* The window for KeymapNotify, etc events is 0 */ 767 if (xevent.type == KeymapNotify) { 768 if (SDL_GetKeyboardFocus() != NULL) { 769 X11_ReconcileKeyboardState(_this); 770 } 771 } else if (xevent.type == MappingNotify) { 772 /* Has the keyboard layout changed? */ 773 const int request = xevent.xmapping.request; 774 775 #ifdef DEBUG_XEVENTS 776 printf("window %p: MappingNotify!\n", data); 777 #endif 778 if ((request == MappingKeyboard) || (request == MappingModifier)) { 779 X11_XRefreshKeyboardMapping(&xevent.xmapping); 780 } 781 782 X11_UpdateKeymap(_this); 783 SDL_SendKeymapChangedEvent(); 784 } 785 return; 786 } 787 788 switch (xevent.type) { 789 790 /* Gaining mouse coverage? */ 791 case EnterNotify:{ 792 SDL_Mouse *mouse = SDL_GetMouse(); 793 #ifdef DEBUG_XEVENTS 794 printf("window %p: EnterNotify! (%d,%d,%d)\n", data, 795 xevent.xcrossing.x, 796 xevent.xcrossing.y, 797 xevent.xcrossing.mode); 798 if (xevent.xcrossing.mode == NotifyGrab) 799 printf("Mode: NotifyGrab\n"); 800 if (xevent.xcrossing.mode == NotifyUngrab) 801 printf("Mode: NotifyUngrab\n"); 802 #endif 803 SDL_SetMouseFocus(data->window); 804 805 mouse->last_x = xevent.xcrossing.x; 806 mouse->last_y = xevent.xcrossing.y; 807 808 if (!mouse->relative_mode) { 809 SDL_SendMouseMotion(data->window, 0, 0, xevent.xcrossing.x, xevent.xcrossing.y); 810 } 811 } 812 break; 813 /* Losing mouse coverage? */ 814 case LeaveNotify:{ 815 #ifdef DEBUG_XEVENTS 816 printf("window %p: LeaveNotify! (%d,%d,%d)\n", data, 817 xevent.xcrossing.x, 818 xevent.xcrossing.y, 819 xevent.xcrossing.mode); 820 if (xevent.xcrossing.mode == NotifyGrab) 821 printf("Mode: NotifyGrab\n"); 822 if (xevent.xcrossing.mode == NotifyUngrab) 823 printf("Mode: NotifyUngrab\n"); 824 #endif 825 if (!SDL_GetMouse()->relative_mode) { 826 SDL_SendMouseMotion(data->window, 0, 0, xevent.xcrossing.x, xevent.xcrossing.y); 827 } 828 829 if (xevent.xcrossing.mode != NotifyGrab && 830 xevent.xcrossing.mode != NotifyUngrab && 831 xevent.xcrossing.detail != NotifyInferior) { 832 SDL_SetMouseFocus(NULL); 833 } 834 } 835 break; 836 837 /* Gaining input focus? */ 838 case FocusIn:{ 839 if (xevent.xfocus.mode == NotifyGrab || xevent.xfocus.mode == NotifyUngrab) { 840 /* Someone is handling a global hotkey, ignore it */ 841 #ifdef DEBUG_XEVENTS 842 printf("window %p: FocusIn (NotifyGrab/NotifyUngrab, ignoring)\n", data); 843 #endif 844 break; 845 } 846 847 if (xevent.xfocus.detail == NotifyInferior || xevent.xfocus.detail == NotifyPointer) { 848 #ifdef DEBUG_XEVENTS 849 printf("window %p: FocusIn (NotifyInferior/NotifyPointer, ignoring)\n", data); 850 #endif 851 break; 852 } 853 #ifdef DEBUG_XEVENTS 854 printf("window %p: FocusIn!\n", data); 855 #endif 856 if (!videodata->last_mode_change_deadline) /* no recent mode changes */ 857 { 858 data->pending_focus = PENDING_FOCUS_NONE; 859 data->pending_focus_time = 0; 860 X11_DispatchFocusIn(_this, data); 861 } 862 else 863 { 864 data->pending_focus = PENDING_FOCUS_IN; 865 data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME; 866 } 867 data->last_focus_event_time = SDL_GetTicks(); 868 } 869 break; 870 871 /* Losing input focus? */ 872 case FocusOut:{ 873 if (xevent.xfocus.mode == NotifyGrab || xevent.xfocus.mode == NotifyUngrab) { 874 /* Someone is handling a global hotkey, ignore it */ 875 #ifdef DEBUG_XEVENTS 876 printf("window %p: FocusOut (NotifyGrab/NotifyUngrab, ignoring)\n", data); 877 #endif 878 break; 879 } 880 if (xevent.xfocus.detail == NotifyInferior || xevent.xfocus.detail == NotifyPointer) { 881 /* We still have focus if a child gets focus. We also don't 882 care about the position of the pointer when the keyboard 883 focus changed. */ 884 #ifdef DEBUG_XEVENTS 885 printf("window %p: FocusOut (NotifyInferior/NotifyPointer, ignoring)\n", data); 886 #endif 887 break; 888 } 889 #ifdef DEBUG_XEVENTS 890 printf("window %p: FocusOut!\n", data); 891 #endif 892 if (!videodata->last_mode_change_deadline) /* no recent mode changes */ 893 { 894 data->pending_focus = PENDING_FOCUS_NONE; 895 data->pending_focus_time = 0; 896 X11_DispatchFocusOut(_this, data); 897 } 898 else 899 { 900 data->pending_focus = PENDING_FOCUS_OUT; 901 data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME; 902 } 903 } 904 break; 905 906 /* Key press? */ 907 case KeyPress:{ 908 KeyCode keycode = xevent.xkey.keycode; 909 KeySym keysym = NoSymbol; 910 char text[SDL_TEXTINPUTEVENT_TEXT_SIZE]; 911 Status status = 0; 912 SDL_bool handled_by_ime = SDL_FALSE; 913 914 #ifdef DEBUG_XEVENTS 915 printf("window %p: KeyPress (X11 keycode = 0x%X)\n", data, xevent.xkey.keycode); 916 #endif 917 #if 1 918 if (videodata->key_layout[keycode] == SDL_SCANCODE_UNKNOWN && keycode) { 919 int min_keycode, max_keycode; 920 X11_XDisplayKeycodes(display, &min_keycode, &max_keycode); 921 keysym = X11_KeyCodeToSym(_this, keycode, xevent.xkey.state >> 13); 922 fprintf(stderr, 923 "The key you just pressed is not recognized by SDL. To help get this fixed, please report this to the SDL forums/mailing list <https://discourse.libsdl.org/> X11 KeyCode %d (%d), X11 KeySym 0x%lX (%s).\n", 924 keycode, keycode - min_keycode, keysym, 925 X11_XKeysymToString(keysym)); 926 } 927 #endif 928 /* */ 929 SDL_zeroa(text); 930 #ifdef X_HAVE_UTF8_STRING 931 if (data->ic) { 932 X11_Xutf8LookupString(data->ic, &xevent.xkey, text, sizeof(text), 933 &keysym, &status); 934 } else { 935 X11_XLookupString(&xevent.xkey, text, sizeof(text), &keysym, NULL); 936 } 937 #else 938 X11_XLookupString(&xevent.xkey, text, sizeof(text), &keysym, NULL); 939 #endif 940 941 #ifdef SDL_USE_IME 942 if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){ 943 handled_by_ime = SDL_IME_ProcessKeyEvent(keysym, keycode); 944 } 945 #endif 946 if (!handled_by_ime) { 947 /* Don't send the key if it looks like a duplicate of a filtered key sent by an IME */ 948 if (xevent.xkey.keycode != videodata->filter_code || xevent.xkey.time != videodata->filter_time) { 949 SDL_SendKeyboardKey(SDL_PRESSED, videodata->key_layout[keycode]); 950 } 951 if(*text) { 952 SDL_SendKeyboardText(text); 953 } 954 } 955 956 X11_UpdateUserTime(data, xevent.xkey.time); 957 } 958 break; 959 960 /* Key release? */ 961 case KeyRelease:{ 962 KeyCode keycode = xevent.xkey.keycode; 963 964 #ifdef DEBUG_XEVENTS 965 printf("window %p: KeyRelease (X11 keycode = 0x%X)\n", data, xevent.xkey.keycode); 966 #endif 967 if (X11_KeyRepeat(display, &xevent)) { 968 /* We're about to get a repeated key down, ignore the key up */ 969 break; 970 } 971 SDL_SendKeyboardKey(SDL_RELEASED, videodata->key_layout[keycode]); 972 } 973 break; 974 975 /* Have we been iconified? */ 976 case UnmapNotify:{ 977 XEvent ev; 978 979 #ifdef DEBUG_XEVENTS 980 printf("window %p: UnmapNotify!\n", data); 981 #endif 982 983 if (X11_XCheckIfEvent(display, &ev, &isReparentNotify, (XPointer)&xevent.xunmap)) { 984 X11_XCheckIfEvent(display, &ev, &isMapNotify, (XPointer)&xevent.xunmap); 985 } else { 986 X11_DispatchUnmapNotify(data); 987 } 988 } 989 break; 990 991 /* Have we been restored? */ 992 case MapNotify:{ 993 #ifdef DEBUG_XEVENTS 994 printf("window %p: MapNotify!\n", data); 995 #endif 996 X11_DispatchMapNotify(data); 997 } 998 break; 999 1000 /* Have we been resized or moved? */ 1001 case ConfigureNotify:{ 1002 #ifdef DEBUG_XEVENTS 1003 printf("window %p: ConfigureNotify! (position: %d,%d, size: %dx%d)\n", data, 1004 xevent.xconfigure.x, xevent.xconfigure.y, 1005 xevent.xconfigure.width, xevent.xconfigure.height); 1006 #endif 1007 /* Real configure notify events are relative to the parent, synthetic events are absolute. */ 1008 if (!xevent.xconfigure.send_event) { 1009 unsigned int NumChildren; 1010 Window ChildReturn, Root, Parent; 1011 Window * Children; 1012 /* Translate these coodinates back to relative to root */ 1013 X11_XQueryTree(data->videodata->display, xevent.xconfigure.window, &Root, &Parent, &Children, &NumChildren); 1014 X11_XTranslateCoordinates(xevent.xconfigure.display, 1015 Parent, DefaultRootWindow(xevent.xconfigure.display), 1016 xevent.xconfigure.x, xevent.xconfigure.y, 1017 &xevent.xconfigure.x, &xevent.xconfigure.y, 1018 &ChildReturn); 1019 } 1020 1021 if (xevent.xconfigure.x != data->last_xconfigure.x || 1022 xevent.xconfigure.y != data->last_xconfigure.y) { 1023 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED, 1024 xevent.xconfigure.x, xevent.xconfigure.y); 1025 #ifdef SDL_USE_IME 1026 if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){ 1027 /* Update IME candidate list position */ 1028 SDL_IME_UpdateTextRect(NULL); 1029 } 1030 #endif 1031 } 1032 if (xevent.xconfigure.width != data->last_xconfigure.width || 1033 xevent.xconfigure.height != data->last_xconfigure.height) { 1034 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED, 1035 xevent.xconfigure.width, 1036 xevent.xconfigure.height); 1037 } 1038 data->last_xconfigure = xevent.xconfigure; 1039 } 1040 break; 1041 1042 /* Have we been requested to quit (or another client message?) */ 1043 case ClientMessage:{ 1044 1045 static int xdnd_version=0; 1046 1047 if (xevent.xclient.message_type == videodata->XdndEnter) { 1048 1049 SDL_bool use_list = xevent.xclient.data.l[1] & 1; 1050 data->xdnd_source = xevent.xclient.data.l[0]; 1051 xdnd_version = (xevent.xclient.data.l[1] >> 24); 1052 #ifdef DEBUG_XEVENTS 1053 printf("XID of source window : %ld\n", data->xdnd_source); 1054 printf("Protocol version to use : %d\n", xdnd_version); 1055 printf("More then 3 data types : %d\n", (int) use_list); 1056 #endif 1057 1058 if (use_list) { 1059 /* fetch conversion targets */ 1060 SDL_x11Prop p; 1061 X11_ReadProperty(&p, display, data->xdnd_source, videodata->XdndTypeList); 1062 /* pick one */ 1063 data->xdnd_req = X11_PickTarget(display, (Atom*)p.data, p.count); 1064 X11_XFree(p.data); 1065 } else { 1066 /* pick from list of three */ 1067 data->xdnd_req = X11_PickTargetFromAtoms(display, xevent.xclient.data.l[2], xevent.xclient.data.l[3], xevent.xclient.data.l[4]); 1068 } 1069 } 1070 else if (xevent.xclient.message_type == videodata->XdndPosition) { 1071 1072 #ifdef DEBUG_XEVENTS 1073 Atom act= videodata->XdndActionCopy; 1074 if(xdnd_version >= 2) { 1075 act = xevent.xclient.data.l[4]; 1076 } 1077 printf("Action requested by user is : %s\n", X11_XGetAtomName(display , act)); 1078 #endif 1079 1080 1081 /* reply with status */ 1082 memset(&m, 0, sizeof(XClientMessageEvent)); 1083 m.type = ClientMessage; 1084 m.display = xevent.xclient.display; 1085 m.window = xevent.xclient.data.l[0]; 1086 m.message_type = videodata->XdndStatus; 1087 m.format=32; 1088 m.data.l[0] = data->xwindow; 1089 m.data.l[1] = (data->xdnd_req != None); 1090 m.data.l[2] = 0; /* specify an empty rectangle */ 1091 m.data.l[3] = 0; 1092 m.data.l[4] = videodata->XdndActionCopy; /* we only accept copying anyway */ 1093 1094 X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m); 1095 X11_XFlush(display); 1096 } 1097 else if(xevent.xclient.message_type == videodata->XdndDrop) { 1098 if (data->xdnd_req == None) { 1099 /* say again - not interested! */ 1100 memset(&m, 0, sizeof(XClientMessageEvent)); 1101 m.type = ClientMessage; 1102 m.display = xevent.xclient.display; 1103 m.window = xevent.xclient.data.l[0]; 1104 m.message_type = videodata->XdndFinished; 1105 m.format=32; 1106 m.data.l[0] = data->xwindow; 1107 m.data.l[1] = 0; 1108 m.data.l[2] = None; /* fail! */ 1109 X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m); 1110 } else { 1111 /* convert */ 1112 if(xdnd_version >= 1) { 1113 X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, xevent.xclient.data.l[2]); 1114 } else { 1115 X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, CurrentTime); 1116 } 1117 } 1118 } 1119 else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) && 1120 (xevent.xclient.format == 32) && 1121 (xevent.xclient.data.l[0] == videodata->_NET_WM_PING)) { 1122 Window root = DefaultRootWindow(display); 1123 1124 #ifdef DEBUG_XEVENTS 1125 printf("window %p: _NET_WM_PING\n", data); 1126 #endif 1127 xevent.xclient.window = root; 1128 X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, &xevent); 1129 break; 1130 } 1131 1132 else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) && 1133 (xevent.xclient.format == 32) && 1134 (xevent.xclient.data.l[0] == videodata->WM_DELETE_WINDOW)) { 1135 1136 #ifdef DEBUG_XEVENTS 1137 printf("window %p: WM_DELETE_WINDOW\n", data); 1138 #endif 1139 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0); 1140 break; 1141 } 1142 else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) && 1143 (xevent.xclient.format == 32) && 1144 (xevent.xclient.data.l[0] == videodata->WM_TAKE_FOCUS)) { 1145 1146 #ifdef DEBUG_XEVENTS 1147 printf("window %p: WM_TAKE_FOCUS\n", data); 1148 #endif 1149 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_TAKE_FOCUS, 0, 0); 1150 break; 1151 } 1152 } 1153 break; 1154 1155 /* Do we need to refresh ourselves? */ 1156 case Expose:{ 1157 #ifdef DEBUG_XEVENTS 1158 printf("window %p: Expose (count = %d)\n", data, xevent.xexpose.count); 1159 #endif 1160 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0); 1161 } 1162 break; 1163 1164 case MotionNotify:{ 1165 SDL_Mouse *mouse = SDL_GetMouse(); 1166 if(!mouse->relative_mode || mouse->relative_mode_warp) { 1167 #ifdef DEBUG_MOTION 1168 printf("window %p: X11 motion: %d,%d\n", data, xevent.xmotion.x, xevent.xmotion.y); 1169 #endif 1170 1171 SDL_SendMouseMotion(data->window, 0, 0, xevent.xmotion.x, xevent.xmotion.y); 1172 } 1173 } 1174 break; 1175 1176 case ButtonPress:{ 1177 int xticks = 0, yticks = 0; 1178 #ifdef DEBUG_XEVENTS 1179 printf("window %p: ButtonPress (X11 button = %d)\n", data, xevent.xbutton.button); 1180 #endif 1181 if (X11_IsWheelEvent(display,&xevent,&xticks, &yticks)) { 1182 SDL_SendMouseWheel(data->window, 0, (float) xticks, (float) yticks, SDL_MOUSEWHEEL_NORMAL); 1183 } else { 1184 SDL_bool ignore_click = SDL_FALSE; 1185 int button = xevent.xbutton.button; 1186 if(button == Button1) { 1187 if (ProcessHitTest(_this, data, &xevent)) { 1188 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIT_TEST, 0, 0); 1189 break; /* don't pass this event on to app. */ 1190 } 1191 } 1192 else if(button > 7) { 1193 /* X button values 4-7 are used for scrolling, so X1 is 8, X2 is 9, ... 1194 => subtract (8-SDL_BUTTON_X1) to get value SDL expects */ 1195 button -= (8-SDL_BUTTON_X1); 1196 } 1197 if (data->last_focus_event_time) { 1198 const int X11_FOCUS_CLICK_TIMEOUT = 10; 1199 if (!SDL_TICKS_PASSED(SDL_GetTicks(), data->last_focus_event_time + X11_FOCUS_CLICK_TIMEOUT)) { 1200 ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, SDL_FALSE); 1201 } 1202 data->last_focus_event_time = 0; 1203 } 1204 if (!ignore_click) { 1205 SDL_SendMouseButton(data->window, 0, SDL_PRESSED, button); 1206 } 1207 } 1208 X11_UpdateUserTime(data, xevent.xbutton.time); 1209 } 1210 break; 1211 1212 case ButtonRelease:{ 1213 int button = xevent.xbutton.button; 1214 /* The X server sends a Release event for each Press for wheels. Ignore them. */ 1215 int xticks = 0, yticks = 0; 1216 #ifdef DEBUG_XEVENTS 1217 printf("window %p: ButtonRelease (X11 button = %d)\n", data, xevent.xbutton.button); 1218 #endif 1219 if (!X11_IsWheelEvent(display, &xevent, &xticks, &yticks)) { 1220 if (button > 7) { 1221 /* see explanation at case ButtonPress */ 1222 button -= (8-SDL_BUTTON_X1); 1223 } 1224 SDL_SendMouseButton(data->window, 0, SDL_RELEASED, button); 1225 } 1226 } 1227 break; 1228 1229 case PropertyNotify:{ 1230 #ifdef DEBUG_XEVENTS 1231 unsigned char *propdata; 1232 int status, real_format; 1233 Atom real_type; 1234 unsigned long items_read, items_left; 1235 1236 char *name = X11_XGetAtomName(display, xevent.xproperty.atom); 1237 if (name) { 1238 printf("window %p: PropertyNotify: %s %s time=%lu\n", data, name, (xevent.xproperty.state == PropertyDelete) ? "deleted" : "changed", xevent.xproperty.time); 1239 X11_XFree(name); 1240 } 1241 1242 status = X11_XGetWindowProperty(display, data->xwindow, xevent.xproperty.atom, 0L, 8192L, False, AnyPropertyType, &real_type, &real_format, &items_read, &items_left, &propdata); 1243 if (status == Success && items_read > 0) { 1244 if (real_type == XA_INTEGER) { 1245 int *values = (int *)propdata; 1246 1247 printf("{"); 1248 for (i = 0; i < items_read; i++) { 1249 printf(" %d", values[i]); 1250 } 1251 printf(" }\n"); 1252 } else if (real_type == XA_CARDINAL) { 1253 if (real_format == 32) { 1254 Uint32 *values = (Uint32 *)propdata; 1255 1256 printf("{"); 1257 for (i = 0; i < items_read; i++) { 1258 printf(" %d", values[i]); 1259 } 1260 printf(" }\n"); 1261 } else if (real_format == 16) { 1262 Uint16 *values = (Uint16 *)propdata; 1263 1264 printf("{"); 1265 for (i = 0; i < items_read; i++) { 1266 printf(" %d", values[i]); 1267 } 1268 printf(" }\n"); 1269 } else if (real_format == 8) { 1270 Uint8 *values = (Uint8 *)propdata; 1271 1272 printf("{"); 1273 for (i = 0; i < items_read; i++) { 1274 printf(" %d", values[i]); 1275 } 1276 printf(" }\n"); 1277 } 1278 } else if (real_type == XA_STRING || 1279 real_type == videodata->UTF8_STRING) { 1280 printf("{ \"%s\" }\n", propdata); 1281 } else if (real_type == XA_ATOM) { 1282 Atom *atoms = (Atom *)propdata; 1283 1284 printf("{"); 1285 for (i = 0; i < items_read; i++) { 1286 char *atomname = X11_XGetAtomName(display, atoms[i]); 1287 if (atomname) { 1288 printf(" %s", atomname); 1289 X11_XFree(atomname); 1290 } 1291 } 1292 printf(" }\n"); 1293 } else { 1294 char *atomname = X11_XGetAtomName(display, real_type); 1295 printf("Unknown type: %ld (%s)\n", real_type, atomname ? atomname : "UNKNOWN"); 1296 if (atomname) { 1297 X11_XFree(atomname); 1298 } 1299 } 1300 } 1301 if (status == Success) { 1302 X11_XFree(propdata); 1303 } 1304 #endif /* DEBUG_XEVENTS */ 1305 1306 /* Take advantage of this moment to make sure user_time has a 1307 valid timestamp from the X server, so if we later try to 1308 raise/restore this window, _NET_ACTIVE_WINDOW can have a 1309 non-zero timestamp, even if there's never been a mouse or 1310 key press to this window so far. Note that we don't try to 1311 set _NET_WM_USER_TIME here, though. That's only for legit 1312 user interaction with the window. */ 1313 if (!data->user_time) { 1314 data->user_time = xevent.xproperty.time; 1315 } 1316 1317 if (xevent.xproperty.atom == data->videodata->_NET_WM_STATE) { 1318 /* Get the new state from the window manager. 1319 Compositing window managers can alter visibility of windows 1320 without ever mapping / unmapping them, so we handle that here, 1321 because they use the NETWM protocol to notify us of changes. 1322 */ 1323 const Uint32 flags = X11_GetNetWMState(_this, xevent.xproperty.window); 1324 const Uint32 changed = flags ^ data->window->flags; 1325 1326 if ((changed & SDL_WINDOW_HIDDEN) || (changed & SDL_WINDOW_FULLSCREEN)) { 1327 if (flags & SDL_WINDOW_HIDDEN) { 1328 X11_DispatchUnmapNotify(data); 1329 } else { 1330 X11_DispatchMapNotify(data); 1331 } 1332 } 1333 1334 if (changed & SDL_WINDOW_MAXIMIZED) { 1335 if (flags & SDL_WINDOW_MAXIMIZED) { 1336 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MAXIMIZED, 0, 0); 1337 } else { 1338 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESTORED, 0, 0); 1339 } 1340 } 1341 } else if (xevent.xproperty.atom == videodata->XKLAVIER_STATE) { 1342 /* Hack for Ubuntu 12.04 (etc) that doesn't send MappingNotify 1343 events when the keyboard layout changes (for example, 1344 changing from English to French on the menubar's keyboard 1345 icon). Since it changes the XKLAVIER_STATE property, we 1346 notice and reinit our keymap here. This might not be the 1347 right approach, but it seems to work. */ 1348 X11_UpdateKeymap(_this); 1349 SDL_SendKeymapChangedEvent(); 1350 } else if (xevent.xproperty.atom == videodata->_NET_FRAME_EXTENTS) { 1351 Atom type; 1352 int format; 1353 unsigned long nitems, bytes_after; 1354 unsigned char *property; 1355 if (X11_XGetWindowProperty(display, data->xwindow, videodata->_NET_FRAME_EXTENTS, 0, 16, 0, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &property) == Success) { 1356 if (type != None && nitems == 4) { 1357 data->border_left = (int) ((long*)property)[0]; 1358 data->border_right = (int) ((long*)property)[1]; 1359 data->border_top = (int) ((long*)property)[2]; 1360 data->border_bottom = (int) ((long*)property)[3]; 1361 } 1362 X11_XFree(property); 1363 1364 #ifdef DEBUG_XEVENTS 1365 printf("New _NET_FRAME_EXTENTS: left=%d right=%d, top=%d, bottom=%d\n", data->border_left, data->border_right, data->border_top, data->border_bottom); 1366 #endif 1367 } 1368 } 1369 } 1370 break; 1371 1372 case SelectionNotify: { 1373 Atom target = xevent.xselection.target; 1374 #ifdef DEBUG_XEVENTS 1375 printf("window %p: SelectionNotify (requestor = %ld, target = %ld)\n", data, 1376 xevent.xselection.requestor, xevent.xselection.target); 1377 #endif 1378 if (target == data->xdnd_req) { 1379 /* read data */ 1380 SDL_x11Prop p; 1381 X11_ReadProperty(&p, display, data->xwindow, videodata->PRIMARY); 1382 1383 if (p.format == 8) { 1384 char *saveptr = NULL; 1385 char *name = X11_XGetAtomName(display, target); 1386 if (name) { 1387 char *token = SDL_strtokr((char *) p.data, "\r\n", &saveptr); 1388 while (token != NULL) { 1389 if (SDL_strcmp("text/plain", name) == 0) { 1390 SDL_SendDropText(data->window, token); 1391 } else if (SDL_strcmp("text/uri-list", name) == 0) { 1392 char *fn = X11_URIToLocal(token); 1393 if (fn) { 1394 SDL_SendDropFile(data->window, fn); 1395 } 1396 } 1397 token = SDL_strtokr(NULL, "\r\n", &saveptr); 1398 } 1399 X11_XFree(name); 1400 } 1401 SDL_SendDropComplete(data->window); 1402 } 1403 X11_XFree(p.data); 1404 1405 /* send reply */ 1406 SDL_memset(&m, 0, sizeof(XClientMessageEvent)); 1407 m.type = ClientMessage; 1408 m.display = display; 1409 m.window = data->xdnd_source; 1410 m.message_type = videodata->XdndFinished; 1411 m.format = 32; 1412 m.data.l[0] = data->xwindow; 1413 m.data.l[1] = 1; 1414 m.data.l[2] = videodata->XdndActionCopy; 1415 X11_XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent*)&m); 1416 1417 X11_XSync(display, False); 1418 } 1419 } 1420 break; 1421 1422 default:{ 1423 #ifdef DEBUG_XEVENTS 1424 printf("window %p: Unhandled event %d\n", data, xevent.type); 1425 #endif 1426 } 1427 break; 1428 } 1429 } 1430 1431 static void 1432 X11_HandleFocusChanges(_THIS) 1433 { 1434 SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; 1435 int i; 1436 1437 if (videodata && videodata->windowlist) { 1438 for (i = 0; i < videodata->numwindows; ++i) { 1439 SDL_WindowData *data = videodata->windowlist[i]; 1440 if (data && data->pending_focus != PENDING_FOCUS_NONE) { 1441 Uint32 now = SDL_GetTicks(); 1442 if (SDL_TICKS_PASSED(now, data->pending_focus_time)) { 1443 if (data->pending_focus == PENDING_FOCUS_IN) { 1444 X11_DispatchFocusIn(_this, data); 1445 } else { 1446 X11_DispatchFocusOut(_this, data); 1447 } 1448 data->pending_focus = PENDING_FOCUS_NONE; 1449 } 1450 } 1451 } 1452 } 1453 } 1454 /* Ack! X11_XPending() actually performs a blocking read if no events available */ 1455 static int 1456 X11_Pending(Display * display) 1457 { 1458 /* Flush the display connection and look to see if events are queued */ 1459 X11_XFlush(display); 1460 if (X11_XEventsQueued(display, QueuedAlready)) { 1461 return (1); 1462 } 1463 1464 /* More drastic measures are required -- see if X is ready to talk */ 1465 if (SDL_IOReady(ConnectionNumber(display), SDL_FALSE, 0)) { 1466 return (X11_XPending(display)); 1467 } 1468 1469 /* Oh well, nothing is ready .. */ 1470 return (0); 1471 } 1472 1473 void 1474 X11_PumpEvents(_THIS) 1475 { 1476 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; 1477 1478 if (data->last_mode_change_deadline) { 1479 if (SDL_TICKS_PASSED(SDL_GetTicks(), data->last_mode_change_deadline)) { 1480 data->last_mode_change_deadline = 0; /* assume we're done. */ 1481 } 1482 } 1483 1484 /* Update activity every 30 seconds to prevent screensaver */ 1485 if (_this->suspend_screensaver) { 1486 const Uint32 now = SDL_GetTicks(); 1487 if (!data->screensaver_activity || 1488 SDL_TICKS_PASSED(now, data->screensaver_activity + 30000)) { 1489 X11_XResetScreenSaver(data->display); 1490 1491 #if SDL_USE_LIBDBUS 1492 SDL_DBus_ScreensaverTickle(); 1493 #endif 1494 1495 data->screensaver_activity = now; 1496 } 1497 } 1498 1499 /* Keep processing pending events */ 1500 while (X11_Pending(data->display)) { 1501 X11_DispatchEvent(_this); 1502 } 1503 1504 #ifdef SDL_USE_IME 1505 if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){ 1506 SDL_IME_PumpEvents(); 1507 } 1508 #endif 1509 1510 /* FIXME: Only need to do this when there are pending focus changes */ 1511 X11_HandleFocusChanges(_this); 1512 } 1513 1514 1515 void 1516 X11_SuspendScreenSaver(_THIS) 1517 { 1518 #if SDL_VIDEO_DRIVER_X11_XSCRNSAVER 1519 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; 1520 int dummy; 1521 int major_version, minor_version; 1522 #endif /* SDL_VIDEO_DRIVER_X11_XSCRNSAVER */ 1523 1524 #if SDL_USE_LIBDBUS 1525 if (SDL_DBus_ScreensaverInhibit(_this->suspend_screensaver)) { 1526 return; 1527 } 1528 1529 if (_this->suspend_screensaver) { 1530 SDL_DBus_ScreensaverTickle(); 1531 } 1532 #endif 1533 1534 #if SDL_VIDEO_DRIVER_X11_XSCRNSAVER 1535 if (SDL_X11_HAVE_XSS) { 1536 /* X11_XScreenSaverSuspend was introduced in MIT-SCREEN-SAVER 1.1 */ 1537 if (!X11_XScreenSaverQueryExtension(data->display, &dummy, &dummy) || 1538 !X11_XScreenSaverQueryVersion(data->display, 1539 &major_version, &minor_version) || 1540 major_version < 1 || (major_version == 1 && minor_version < 1)) { 1541 return; 1542 } 1543 1544 X11_XScreenSaverSuspend(data->display, _this->suspend_screensaver); 1545 X11_XResetScreenSaver(data->display); 1546 } 1547 #endif 1548 } 1549 1550 #endif /* SDL_VIDEO_DRIVER_X11 */ 1551 1552 /* vi: set ts=4 sw=4 expandtab: */