Atlas - SDL_x11events.c
Home / ext / SDL / src / video / x11 Lines: 4 | Size: 95262 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 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#ifdef 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_x11pen.h" 33#include "SDL_x11touch.h" 34#include "SDL_x11xinput2.h" 35#include "SDL_x11xfixes.h" 36#include "SDL_x11settings.h" 37#include "../SDL_clipboard_c.h" 38#include "SDL_x11xsync.h" 39#include "../../core/unix/SDL_poll.h" 40#include "../../events/SDL_events_c.h" 41#include "../../events/SDL_mouse_c.h" 42#include "../../events/SDL_touch_c.h" 43#include "../../core/linux/SDL_system_theme.h" 44#include "../SDL_sysvideo.h" 45 46#include <stdio.h> 47 48#if 0 49#define DEBUG_XEVENTS 50#endif 51 52#ifndef _NET_WM_MOVERESIZE_SIZE_TOPLEFT 53#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 54#endif 55 56#ifndef _NET_WM_MOVERESIZE_SIZE_TOP 57#define _NET_WM_MOVERESIZE_SIZE_TOP 1 58#endif 59 60#ifndef _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 61#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 62#endif 63 64#ifndef _NET_WM_MOVERESIZE_SIZE_RIGHT 65#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 66#endif 67 68#ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 69#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 70#endif 71 72#ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOM 73#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 74#endif 75 76#ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 77#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 78#endif 79 80#ifndef _NET_WM_MOVERESIZE_SIZE_LEFT 81#define _NET_WM_MOVERESIZE_SIZE_LEFT 7 82#endif 83 84#ifndef _NET_WM_MOVERESIZE_MOVE 85#define _NET_WM_MOVERESIZE_MOVE 8 86#endif 87 88typedef struct 89{ 90 unsigned char *data; 91 int format, count; 92 Atom type; 93} SDL_x11Prop; 94 95/* Reads property 96 Must call X11_XFree on results 97 */ 98static void X11_ReadProperty(SDL_x11Prop *p, Display *disp, Window w, Atom prop) 99{ 100 unsigned char *ret = NULL; 101 Atom type; 102 int fmt; 103 unsigned long count; 104 unsigned long bytes_left; 105 int bytes_fetch = 0; 106 107 do { 108 if (ret) { 109 X11_XFree(ret); 110 } 111 X11_XGetWindowProperty(disp, w, prop, 0, bytes_fetch, False, AnyPropertyType, &type, &fmt, &count, &bytes_left, &ret); 112 bytes_fetch += bytes_left; 113 } while (bytes_left != 0); 114 115 p->data = ret; 116 p->format = fmt; 117 p->count = count; 118 p->type = type; 119} 120 121/* Find text-uri-list in a list of targets and return it's atom 122 if available, else return None */ 123static Atom X11_PickTarget(Display *disp, Atom list[], int list_count) 124{ 125 Atom request = None; 126 char *name; 127 int i; 128 for (i = 0; i < list_count && request == None; i++) { 129 name = X11_XGetAtomName(disp, list[i]); 130 // Preferred MIME targets 131 if ((SDL_strcmp("text/uri-list", name) == 0) || 132 (SDL_strcmp("text/plain;charset=utf-8", name) == 0) || 133 (SDL_strcmp("UTF8_STRING", name) == 0)) { 134 request = list[i]; 135 } 136 // Fallback MIME targets 137 if ((SDL_strcmp("text/plain", name) == 0) || 138 (SDL_strcmp("TEXT", name) == 0)) { 139 if (request == None) { 140 request = list[i]; 141 } 142 } 143 X11_XFree(name); 144 } 145 return request; 146} 147 148/* Wrapper for X11_PickTarget for a maximum of three targets, a special 149 case in the Xdnd protocol */ 150static Atom X11_PickTargetFromAtoms(Display *disp, Atom a0, Atom a1, Atom a2) 151{ 152 int count = 0; 153 Atom atom[3]; 154 if (a0 != None) { 155 atom[count++] = a0; 156 } 157 if (a1 != None) { 158 atom[count++] = a1; 159 } 160 if (a2 != None) { 161 atom[count++] = a2; 162 } 163 return X11_PickTarget(disp, atom, count); 164} 165 166struct KeyRepeatCheckData 167{ 168 XEvent *event; 169 bool found; 170}; 171 172static Bool X11_KeyRepeatCheckIfEvent(Display *display, XEvent *chkev, 173 XPointer arg) 174{ 175 struct KeyRepeatCheckData *d = (struct KeyRepeatCheckData *)arg; 176 if (chkev->type == KeyPress && chkev->xkey.keycode == d->event->xkey.keycode && chkev->xkey.time - d->event->xkey.time < 2) { 177 d->found = true; 178 } 179 return False; 180} 181 182/* Check to see if this is a repeated key. 183 (idea shamelessly lifted from GII -- thanks guys! :) 184 */ 185static bool X11_KeyRepeat(Display *display, XEvent *event) 186{ 187 XEvent dummyev; 188 struct KeyRepeatCheckData d; 189 d.event = event; 190 d.found = false; 191 if (X11_XPending(display)) { 192 X11_XCheckIfEvent(display, &dummyev, X11_KeyRepeatCheckIfEvent, (XPointer)&d); 193 } 194 return d.found; 195} 196 197bool X11_IsWheelEvent(int button, int *xticks, int *yticks) 198{ 199 /* according to the xlib docs, no specific mouse wheel events exist. 200 However, the defacto standard is that the vertical wheel is X buttons 201 4 (up) and 5 (down) and a horizontal wheel is 6 (left) and 7 (right). */ 202 203 // Xlib defines "Button1" through 5, so we just use literals here. 204 switch (button) { 205 case 4: 206 *yticks = 1; 207 return true; 208 case 5: 209 *yticks = -1; 210 return true; 211 case 6: 212 *xticks = 1; 213 return true; 214 case 7: 215 *xticks = -1; 216 return true; 217 default: 218 break; 219 } 220 return false; 221} 222 223// An X11 event hook 224static SDL_X11EventHook g_X11EventHook = NULL; 225static void *g_X11EventHookData = NULL; 226 227void SDL_SetX11EventHook(SDL_X11EventHook callback, void *userdata) 228{ 229 g_X11EventHook = callback; 230 g_X11EventHookData = userdata; 231} 232 233#ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS 234static void X11_HandleGenericEvent(SDL_VideoDevice *_this, XEvent *xev) 235{ 236 SDL_VideoData *videodata = _this->internal; 237 238 // event is a union, so cookie == &event, but this is type safe. 239 XGenericEventCookie *cookie = &xev->xcookie; 240 if (X11_XGetEventData(videodata->display, cookie)) { 241 if (!g_X11EventHook || g_X11EventHook(g_X11EventHookData, xev)) { 242 X11_HandleXinput2Event(_this, cookie); 243 } 244 X11_XFreeEventData(videodata->display, cookie); 245 } 246} 247#endif // SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS 248 249static void X11_UpdateSystemKeyModifiers(SDL_VideoData *viddata) 250{ 251#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB 252 if (viddata->keyboard.xkb_enabled) { 253 XkbStateRec xkb_state; 254 if (X11_XkbGetState(viddata->display, XkbUseCoreKbd, &xkb_state) == Success) { 255 viddata->keyboard.pressed_modifiers = xkb_state.base_mods; 256 viddata->keyboard.locked_modifiers = xkb_state.latched_mods | xkb_state.locked_mods; 257 } 258 } else 259#endif 260 { 261 Window junk_window; 262 int x, y; 263 unsigned int mod_mask; 264 265 X11_XQueryPointer(viddata->display, DefaultRootWindow(viddata->display), &junk_window, &junk_window, &x, &y, &x, &y, &mod_mask); 266 viddata->keyboard.pressed_modifiers = mod_mask & (ShiftMask | ControlMask | Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask); 267 viddata->keyboard.locked_modifiers = mod_mask & (LockMask | viddata->keyboard.numlock_mask | viddata->keyboard.scrolllock_mask); 268 } 269} 270 271static void X11_ReconcileModifiers(SDL_VideoData *viddata, bool key_pressed) 272{ 273 /* Handle explicit pressed modifier state. This will correct the modifier state 274 * if common modifier keys were remapped and the modifiers presumed to be set 275 * during a key press event were incorrect, if the modifier was set to the 276 * pressed state via means other than pressing the physical key, or if the 277 * modifier state was set by a keypress before the corresponding key event 278 * was received. 279 */ 280 if (key_pressed) { 281 if (viddata->keyboard.pressed_modifiers & ShiftMask) { 282 if (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_SHIFT) { 283 viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_SHIFT; 284 viddata->keyboard.sdl_pressed_modifiers |= (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_SHIFT); 285 } 286 } 287 288 if (viddata->keyboard.pressed_modifiers & ControlMask) { 289 if (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_CTRL) { 290 viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_CTRL; 291 viddata->keyboard.sdl_pressed_modifiers |= (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_CTRL); 292 } 293 } 294 295 if (viddata->keyboard.pressed_modifiers & viddata->keyboard.alt_mask) { 296 if (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_ALT) { 297 viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_ALT; 298 viddata->keyboard.sdl_pressed_modifiers |= (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_ALT); 299 } 300 } 301 302 if (viddata->keyboard.pressed_modifiers & viddata->keyboard.gui_mask) { 303 if (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_GUI) { 304 viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_GUI; 305 viddata->keyboard.sdl_pressed_modifiers |= (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_GUI); 306 } 307 } 308 } else { 309 if (viddata->keyboard.pressed_modifiers & ShiftMask) { 310 if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_SHIFT)) { 311 viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_SHIFT; 312 } 313 } else { 314 viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_SHIFT; 315 } 316 317 if (viddata->keyboard.pressed_modifiers & ControlMask) { 318 if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_CTRL)) { 319 viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_CTRL; 320 } 321 } else { 322 viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_CTRL; 323 } 324 325 if (viddata->keyboard.pressed_modifiers & viddata->keyboard.alt_mask) { 326 if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_ALT)) { 327 viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_ALT; 328 } 329 } else { 330 viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_ALT; 331 } 332 333 if (viddata->keyboard.pressed_modifiers & viddata->keyboard.gui_mask) { 334 if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_GUI)) { 335 viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_GUI; 336 } 337 } else { 338 viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_GUI; 339 } 340 341 if (viddata->keyboard.pressed_modifiers & viddata->keyboard.level3_mask) { 342 if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_MODE)) { 343 viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_MODE; 344 } 345 } else { 346 viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_MODE; 347 } 348 349 if (viddata->keyboard.pressed_modifiers & viddata->keyboard.level5_mask) { 350 if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_LEVEL5)) { 351 viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_LEVEL5; 352 } 353 } else { 354 viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_LEVEL5; 355 } 356 } 357 358 /* If a latch or lock was activated by a keypress, the latch/lock will 359 * be tied to the specific left/right key that initiated it. Otherwise, 360 * the ambiguous left/right combo is used. 361 * 362 * The modifier will remain active until the latch/lock is released by 363 * the system. 364 */ 365 if (viddata->keyboard.locked_modifiers & ShiftMask) { 366 if (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_SHIFT) { 367 viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_SHIFT; 368 viddata->keyboard.sdl_locked_modifiers |= (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_SHIFT); 369 } else if (!(viddata->keyboard.sdl_locked_modifiers & SDL_KMOD_SHIFT)) { 370 viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_SHIFT; 371 } 372 } else { 373 viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_SHIFT; 374 } 375 376 if (viddata->keyboard.locked_modifiers & ControlMask) { 377 if (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_CTRL) { 378 viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_CTRL; 379 viddata->keyboard.sdl_locked_modifiers |= (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_CTRL); 380 } else if (!(viddata->keyboard.sdl_locked_modifiers & SDL_KMOD_CTRL)) { 381 viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_CTRL; 382 } 383 } else { 384 viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_CTRL; 385 } 386 387 if (viddata->keyboard.locked_modifiers & viddata->keyboard.alt_mask) { 388 if (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_ALT) { 389 viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_ALT; 390 viddata->keyboard.sdl_locked_modifiers |= (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_ALT); 391 } else if (!(viddata->keyboard.sdl_locked_modifiers & SDL_KMOD_ALT)) { 392 viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_ALT; 393 } 394 } else { 395 viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_ALT; 396 } 397 398 if (viddata->keyboard.locked_modifiers & viddata->keyboard.gui_mask) { 399 if (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_GUI) { 400 viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_GUI; 401 viddata->keyboard.sdl_locked_modifiers |= (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_GUI); 402 } else if (!(viddata->keyboard.sdl_locked_modifiers & SDL_KMOD_GUI)) { 403 viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_GUI; 404 } 405 } else { 406 viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_GUI; 407 } 408 409 if (viddata->keyboard.locked_modifiers & viddata->keyboard.level3_mask) { 410 viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_MODE; 411 } else { 412 viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_MODE; 413 } 414 415 if (viddata->keyboard.locked_modifiers & viddata->keyboard.level5_mask) { 416 viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_LEVEL5; 417 } else { 418 viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_LEVEL5; 419 } 420 421 // Capslock, Numlock, and Scrolllock can only be locked, not pressed. 422 if (viddata->keyboard.locked_modifiers & LockMask) { 423 viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_CAPS; 424 } else { 425 viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_CAPS; 426 } 427 428 if (viddata->keyboard.locked_modifiers & viddata->keyboard.numlock_mask) { 429 viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_NUM; 430 } else { 431 viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_NUM; 432 } 433 434 if (viddata->keyboard.locked_modifiers & viddata->keyboard.scrolllock_mask) { 435 viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_SCROLL; 436 } else { 437 viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_SCROLL; 438 } 439 440 SDL_SetModState(viddata->keyboard.sdl_pressed_modifiers | viddata->keyboard.sdl_locked_modifiers); 441} 442 443static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode, bool pressed) 444{ 445 const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false); 446 SDL_Keymod mod = SDL_KMOD_NONE; 447 448 /* SDL clients expect modifier state to be activated at the same time as the 449 * source keypress, so we set pressed modifier state with the usual modifier 450 * keys here, as the explicit modifier event won't arrive until after the 451 * keypress event. If this is wrong, it will be corrected when the explicit 452 * modifier state is checked. 453 */ 454 switch (keycode) { 455 case SDLK_LSHIFT: 456 mod = SDL_KMOD_LSHIFT; 457 break; 458 case SDLK_RSHIFT: 459 mod = SDL_KMOD_RSHIFT; 460 break; 461 case SDLK_LCTRL: 462 mod = SDL_KMOD_LCTRL; 463 break; 464 case SDLK_RCTRL: 465 mod = SDL_KMOD_RCTRL; 466 break; 467 case SDLK_LALT: 468 mod = SDL_KMOD_LALT; 469 break; 470 case SDLK_RALT: 471 mod = SDL_KMOD_RALT; 472 break; 473 case SDLK_LGUI: 474 mod = SDL_KMOD_LGUI; 475 break; 476 case SDLK_RGUI: 477 mod = SDL_KMOD_RGUI; 478 break; 479 case SDLK_MODE: 480 mod = SDL_KMOD_MODE; 481 break; 482 case SDLK_LEVEL5_SHIFT: 483 mod = SDL_KMOD_LEVEL5; 484 break; 485 case SDLK_CAPSLOCK: 486 case SDLK_NUMLOCKCLEAR: 487 case SDLK_SCROLLLOCK: 488 { 489 // XKB provides the latched/locked state explicitly. 490 if (viddata->keyboard.xkb_enabled) { 491 /* For locking modifier keys, query the lock state directly, or we may have to wait until the next 492 * key press event to know if a lock was actually activated from the key event. 493 */ 494 unsigned int cur_mask = viddata->keyboard.locked_modifiers; 495 X11_UpdateSystemKeyModifiers(viddata); 496 497 if (viddata->keyboard.locked_modifiers & LockMask) { 498 cur_mask |= LockMask; 499 } else { 500 cur_mask &= ~LockMask; 501 } 502 if (viddata->keyboard.locked_modifiers & viddata->keyboard.numlock_mask) { 503 cur_mask |= viddata->keyboard.numlock_mask; 504 } else { 505 cur_mask &= ~viddata->keyboard.numlock_mask; 506 } 507 if (viddata->keyboard.locked_modifiers & viddata->keyboard.scrolllock_mask) { 508 cur_mask |= viddata->keyboard.scrolllock_mask; 509 } else { 510 cur_mask &= ~viddata->keyboard.scrolllock_mask; 511 } 512 513 viddata->keyboard.locked_modifiers = cur_mask; 514 } 515 } break; 516 default: 517 return; 518 } 519 520 if (pressed) { 521 viddata->keyboard.sdl_pressed_modifiers |= mod; 522 viddata->keyboard.sdl_physically_pressed_modifiers |= mod; 523 } else { 524 viddata->keyboard.sdl_pressed_modifiers &= ~mod; 525 viddata->keyboard.sdl_physically_pressed_modifiers &= ~mod; 526 } 527 528 X11_ReconcileModifiers(viddata, true); 529} 530 531void X11_ReconcileKeyboardState(SDL_VideoDevice *_this) 532{ 533 SDL_VideoData *videodata = _this->internal; 534 Display *display = videodata->display; 535 char keys[32]; 536 537 // Rebuild the modifier state in case it changed while focus was lost. 538 X11_UpdateSystemKeyModifiers(videodata); 539 X11_ReconcileModifiers(videodata, false); 540 541 // Keep caps, num, and scroll, but clear the others until we have updated key state. 542 videodata->keyboard.sdl_pressed_modifiers = 0; 543 videodata->keyboard.sdl_physically_pressed_modifiers = 0; 544 videodata->keyboard.sdl_locked_modifiers &= SDL_KMOD_CAPS | SDL_KMOD_NUM | SDL_KMOD_SCROLL; 545 videodata->keyboard.pressed_modifiers = 0; 546 videodata->keyboard.locked_modifiers &= LockMask | videodata->keyboard.numlock_mask| videodata->keyboard.scrolllock_mask; 547 548 X11_XQueryKeymap(display, keys); 549 550 const bool *keystate = SDL_GetKeyboardState(NULL); 551 for (Uint32 keycode = 0; keycode < SDL_arraysize(videodata->keyboard.key_layout); ++keycode) { 552 const SDL_Scancode scancode = videodata->keyboard.key_layout[keycode]; 553 const bool x11KeyPressed = (keys[keycode / 8] & (1 << (keycode % 8))) != 0; 554 const bool sdlKeyPressed = keystate[scancode]; 555 556 if (x11KeyPressed && !sdlKeyPressed) { 557 // Only update modifier state for keys that are pressed in another application 558 switch (SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false)) { 559 case SDLK_LCTRL: 560 case SDLK_RCTRL: 561 case SDLK_LSHIFT: 562 case SDLK_RSHIFT: 563 case SDLK_LALT: 564 case SDLK_RALT: 565 case SDLK_LGUI: 566 case SDLK_RGUI: 567 case SDLK_MODE: 568 case SDLK_LEVEL5_SHIFT: 569 X11_HandleModifierKeys(videodata, scancode, true); 570 SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, true); 571 break; 572 default: 573 break; 574 } 575 } else if (!x11KeyPressed && sdlKeyPressed) { 576 X11_HandleModifierKeys(videodata, scancode, false); 577 SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, false); 578 } 579 } 580 581 // Update the latched/locked state for modifiers other than Caps, Num, and Scroll lock. 582 X11_UpdateSystemKeyModifiers(videodata); 583 X11_ReconcileModifiers(videodata, true); 584} 585 586static void X11_DispatchFocusIn(SDL_VideoDevice *_this, SDL_WindowData *data) 587{ 588#ifdef DEBUG_XEVENTS 589 SDL_Log("window 0x%lx: Dispatching FocusIn", data->xwindow); 590#endif 591 SDL_SetKeyboardFocus(data->window); 592 X11_ReconcileKeyboardState(_this); 593#ifdef X_HAVE_UTF8_STRING 594 if (data->ic) { 595 X11_XSetICFocus(data->ic); 596 } 597#endif 598 if (data->flashing_window) { 599 X11_FlashWindow(_this, data->window, SDL_FLASH_CANCEL); 600 } 601} 602 603static void X11_DispatchFocusOut(SDL_VideoDevice *_this, SDL_WindowData *data) 604{ 605#ifdef DEBUG_XEVENTS 606 SDL_Log("window 0x%lx: Dispatching FocusOut", data->xwindow); 607#endif 608 /* If another window has already processed a focus in, then don't try to 609 * remove focus here. Doing so will incorrectly remove focus from that 610 * window, and the focus lost event for this window will have already 611 * been dispatched anyway. */ 612 if (data->window == SDL_GetKeyboardFocus()) { 613 SDL_SetKeyboardFocus(NULL); 614 } 615#ifdef X_HAVE_UTF8_STRING 616 if (data->ic) { 617 X11_XUnsetICFocus(data->ic); 618 } 619#endif 620} 621 622static void X11_DispatchMapNotify(SDL_WindowData *data) 623{ 624 SDL_Window *window = data->window; 625 626 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0); 627 data->was_shown = true; 628 629 // This may be sent when restoring a minimized window. 630 if (window->flags & SDL_WINDOW_MINIMIZED) { 631 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0); 632 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_EXPOSED, 0, 0); 633 } 634 635 if (window->flags & SDL_WINDOW_INPUT_FOCUS) { 636 SDL_UpdateWindowGrab(window); 637 } 638} 639 640static void X11_DispatchUnmapNotify(SDL_WindowData *data) 641{ 642 SDL_Window *window = data->window; 643 644 // This may be sent when minimizing a window. 645 if (!window->is_hiding) { 646 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0); 647 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_OCCLUDED, 0, 0); 648 } else { 649 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_HIDDEN, 0, 0); 650 } 651} 652 653static void DispatchWindowMove(SDL_VideoDevice *_this, const SDL_WindowData *data, const SDL_Point *point) 654{ 655 SDL_VideoData *videodata = _this->internal; 656 SDL_Window *window = data->window; 657 Display *display = videodata->display; 658 XEvent evt; 659 660 // !!! FIXME: we need to regrab this if necessary when the drag is done. 661 X11_XUngrabPointer(display, 0L); 662 X11_XFlush(display); 663 664 evt.xclient.type = ClientMessage; 665 evt.xclient.window = data->xwindow; 666 evt.xclient.message_type = videodata->atoms._NET_WM_MOVERESIZE; 667 evt.xclient.format = 32; 668 evt.xclient.data.l[0] = (size_t)window->x + point->x; 669 evt.xclient.data.l[1] = (size_t)window->y + point->y; 670 evt.xclient.data.l[2] = _NET_WM_MOVERESIZE_MOVE; 671 evt.xclient.data.l[3] = Button1; 672 evt.xclient.data.l[4] = 0; 673 X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt); 674 675 X11_XSync(display, 0); 676} 677 678static void ScheduleWindowMove(SDL_VideoDevice *_this, SDL_WindowData *data, const SDL_Point *point) 679{ 680 data->pending_move = true; 681 data->pending_move_point = *point; 682} 683 684static void InitiateWindowResize(SDL_VideoDevice *_this, const SDL_WindowData *data, const SDL_Point *point, int direction) 685{ 686 SDL_VideoData *videodata = _this->internal; 687 SDL_Window *window = data->window; 688 Display *display = videodata->display; 689 XEvent evt; 690 691 if (direction < _NET_WM_MOVERESIZE_SIZE_TOPLEFT || direction > _NET_WM_MOVERESIZE_SIZE_LEFT) { 692 return; 693 } 694 695 // !!! FIXME: we need to regrab this if necessary when the drag is done. 696 X11_XUngrabPointer(display, 0L); 697 X11_XFlush(display); 698 699 evt.xclient.type = ClientMessage; 700 evt.xclient.window = data->xwindow; 701 evt.xclient.message_type = videodata->atoms._NET_WM_MOVERESIZE; 702 evt.xclient.format = 32; 703 evt.xclient.data.l[0] = (size_t)window->x + point->x; 704 evt.xclient.data.l[1] = (size_t)window->y + point->y; 705 evt.xclient.data.l[2] = direction; 706 evt.xclient.data.l[3] = Button1; 707 evt.xclient.data.l[4] = 0; 708 X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt); 709 710 X11_XSync(display, 0); 711} 712 713bool X11_ProcessHitTest(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y, bool force_new_result) 714{ 715 SDL_Window *window = data->window; 716 if (!window->hit_test) return false; 717 const SDL_Point point = { (int)x, (int)y }; 718 SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data); 719 if (!force_new_result && rc == data->hit_test_result) { 720 return true; 721 } 722 X11_SetHitTestCursor(rc); 723 data->hit_test_result = rc; 724 return true; 725} 726 727bool X11_TriggerHitTestAction(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y) 728{ 729 SDL_Window *window = data->window; 730 731 if (window->hit_test) { 732 const SDL_Point point = { (int)x, (int)y }; 733 static const int directions[] = { 734 _NET_WM_MOVERESIZE_SIZE_TOPLEFT, _NET_WM_MOVERESIZE_SIZE_TOP, 735 _NET_WM_MOVERESIZE_SIZE_TOPRIGHT, _NET_WM_MOVERESIZE_SIZE_RIGHT, 736 _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT, _NET_WM_MOVERESIZE_SIZE_BOTTOM, 737 _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT, _NET_WM_MOVERESIZE_SIZE_LEFT 738 }; 739 740 switch (data->hit_test_result) { 741 case SDL_HITTEST_DRAGGABLE: 742 /* Some window managers get in a bad state when a move event starts while input is transitioning 743 to the SDL window. This can happen when clicking on a drag region of an unfocused window 744 where the same mouse down event will trigger a drag event and a window activate. */ 745 if (data->window->flags & SDL_WINDOW_INPUT_FOCUS) { 746 DispatchWindowMove(_this, data, &point); 747 } else { 748 ScheduleWindowMove(_this, data, &point); 749 } 750 return true; 751 752 case SDL_HITTEST_RESIZE_TOPLEFT: 753 case SDL_HITTEST_RESIZE_TOP: 754 case SDL_HITTEST_RESIZE_TOPRIGHT: 755 case SDL_HITTEST_RESIZE_RIGHT: 756 case SDL_HITTEST_RESIZE_BOTTOMRIGHT: 757 case SDL_HITTEST_RESIZE_BOTTOM: 758 case SDL_HITTEST_RESIZE_BOTTOMLEFT: 759 case SDL_HITTEST_RESIZE_LEFT: 760 InitiateWindowResize(_this, data, &point, directions[data->hit_test_result - SDL_HITTEST_RESIZE_TOPLEFT]); 761 return true; 762 763 default: 764 return false; 765 } 766 } 767 768 return false; 769} 770 771static void X11_UpdateUserTime(SDL_WindowData *data, const unsigned long latest) 772{ 773 if (latest && (latest != data->user_time)) { 774 SDL_VideoData *videodata = data->videodata; 775 Display *display = videodata->display; 776 X11_XChangeProperty(display, data->xwindow, videodata->atoms._NET_WM_USER_TIME, 777 XA_CARDINAL, 32, PropModeReplace, 778 (const unsigned char *)&latest, 1); 779#ifdef DEBUG_XEVENTS 780 SDL_Log("window 0x%lx: updating _NET_WM_USER_TIME to %lu", data->xwindow, latest); 781#endif 782 data->user_time = latest; 783 } 784} 785 786static void X11_HandleClipboardEvent(SDL_VideoDevice *_this, const XEvent *xevent) 787{ 788 int i; 789 SDL_VideoData *videodata = _this->internal; 790 Display *display = videodata->display; 791 792 SDL_assert(videodata->clipboard_window != None); 793 SDL_assert(xevent->xany.window == videodata->clipboard_window); 794 795 switch (xevent->type) { 796 // Copy the selection from our own CUTBUFFER to the requested property 797 case SelectionRequest: 798 { 799 const XSelectionRequestEvent *req = &xevent->xselectionrequest; 800 XEvent sevent; 801 int mime_formats; 802 unsigned char *seln_data; 803 size_t seln_length = 0; 804 Atom XA_TARGETS = videodata->atoms.TARGETS; 805 SDLX11_ClipboardData *clipboard; 806 807#ifdef DEBUG_XEVENTS 808 char *atom_name; 809 atom_name = X11_XGetAtomName(display, req->target); 810 SDL_Log("window CLIPBOARD: SelectionRequest (requestor = 0x%lx, target = 0x%lx, mime_type = %s)", 811 req->requestor, req->target, atom_name); 812 if (atom_name) { 813 X11_XFree(atom_name); 814 } 815#endif 816 817 if (req->selection == XA_PRIMARY) { 818 clipboard = &videodata->primary_selection; 819 } else { 820 clipboard = &videodata->clipboard; 821 } 822 823 SDL_zero(sevent); 824 sevent.xany.type = SelectionNotify; 825 sevent.xselection.selection = req->selection; 826 sevent.xselection.target = None; 827 sevent.xselection.property = None; // tell them no by default 828 sevent.xselection.requestor = req->requestor; 829 sevent.xselection.time = req->time; 830 831 /* !!! FIXME: We were probably storing this on the root window 832 because an SDL window might go away...? but we don't have to do 833 this now (or ever, really). */ 834 835 if (req->target == XA_TARGETS) { 836 Atom *supportedFormats; 837 supportedFormats = SDL_malloc((clipboard->mime_count + 1) * sizeof(Atom)); 838 supportedFormats[0] = XA_TARGETS; 839 mime_formats = 1; 840 for (i = 0; i < clipboard->mime_count; ++i) { 841 supportedFormats[mime_formats++] = X11_XInternAtom(display, clipboard->mime_types[i], False); 842 } 843 X11_XChangeProperty(display, req->requestor, req->property, 844 XA_ATOM, 32, PropModeReplace, 845 (unsigned char *)supportedFormats, 846 mime_formats); 847 sevent.xselection.property = req->property; 848 sevent.xselection.target = XA_TARGETS; 849 SDL_free(supportedFormats); 850 } else { 851 if (clipboard->callback) { 852 for (i = 0; i < clipboard->mime_count; ++i) { 853 const char *mime_type = clipboard->mime_types[i]; 854 if (X11_XInternAtom(display, mime_type, False) != req->target) { 855 continue; 856 } 857 858 // FIXME: We don't support the X11 INCR protocol for large clipboards. Do we want that? - Yes, yes we do. 859 // This is a safe cast, XChangeProperty() doesn't take a const value, but it doesn't modify the data 860 seln_data = (unsigned char *)clipboard->callback(clipboard->userdata, mime_type, &seln_length); 861 if (seln_data) { 862 X11_XChangeProperty(display, req->requestor, req->property, 863 req->target, 8, PropModeReplace, 864 seln_data, seln_length); 865 sevent.xselection.property = req->property; 866 sevent.xselection.target = req->target; 867 } 868 break; 869 } 870 } 871 } 872 X11_XSendEvent(display, req->requestor, False, 0, &sevent); 873 X11_XSync(display, False); 874 } break; 875 876 case SelectionNotify: 877 { 878 const XSelectionEvent *xsel = &xevent->xselection; 879#ifdef DEBUG_XEVENTS 880 const char *propName = xsel->property ? X11_XGetAtomName(display, xsel->property) : "None"; 881 const char *targetName = xsel->target ? X11_XGetAtomName(display, xsel->target) : "None"; 882 883 SDL_Log("window CLIPBOARD: SelectionNotify (requestor = 0x%lx, target = %s, property = %s)", 884 xsel->requestor, targetName, propName); 885#endif 886 if (xsel->target == videodata->atoms.TARGETS && xsel->property == videodata->atoms.SDL_FORMATS) { 887 /* the new mime formats are the SDL_FORMATS property as an array of Atoms */ 888 Atom atom = None; 889 Atom *patom; 890 unsigned char *data = NULL; 891 int format_property = 0; 892 unsigned long length = 0; 893 unsigned long bytes_left = 0; 894 int j; 895 896 X11_XGetWindowProperty(display, GetWindow(_this), videodata->atoms.SDL_FORMATS, 0, 200, 897 0, XA_ATOM, &atom, &format_property, &length, &bytes_left, &data); 898 899 int allocationsize = (length + 1) * sizeof(char *); 900 for (j = 0, patom = (Atom *)data; j < length; j++, patom++) { 901 char *atomStr = X11_XGetAtomName(display, *patom); 902 allocationsize += SDL_strlen(atomStr) + 1; 903 X11_XFree(atomStr); 904 } 905 906 char **new_mime_types = SDL_AllocateTemporaryMemory(allocationsize); 907 if (new_mime_types) { 908 char *strPtr = (char *)(new_mime_types + length + 1); 909 910 for (j = 0, patom = (Atom *)data; j < length; j++, patom++) { 911 char *atomStr = X11_XGetAtomName(display, *patom); 912 new_mime_types[j] = strPtr; 913 strPtr = stpcpy(strPtr, atomStr) + 1; 914 X11_XFree(atomStr); 915 } 916 new_mime_types[length] = NULL; 917 918 SDL_SendClipboardUpdate(false, new_mime_types, length); 919 } 920 921 if (data) { 922 X11_XFree(data); 923 } 924 } 925 926 videodata->selection_waiting = false; 927 } break; 928 929 case SelectionClear: 930 { 931 Atom XA_CLIPBOARD = videodata->atoms.CLIPBOARD; 932 SDLX11_ClipboardData *clipboard = NULL; 933 934#ifdef DEBUG_XEVENTS 935 SDL_Log("window CLIPBOARD: SelectionClear (requestor = 0x%lx, target = 0x%lx)", 936 xevent->xselection.requestor, xevent->xselection.target); 937#endif 938 939 if (xevent->xselectionclear.selection == XA_PRIMARY) { 940 clipboard = &videodata->primary_selection; 941 } else if (XA_CLIPBOARD != None && xevent->xselectionclear.selection == XA_CLIPBOARD) { 942 clipboard = &videodata->clipboard; 943 } 944 if (clipboard && clipboard->callback) { 945 if (clipboard->sequence) { 946 SDL_CancelClipboardData(clipboard->sequence); 947 } else { 948 SDL_free(clipboard->userdata); 949 } 950 SDL_zerop(clipboard); 951 } 952 } break; 953 954 case PropertyNotify: 955 { 956 char *name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom); 957 958 if (SDL_strncmp(name_of_atom, "SDL_SELECTION", sizeof("SDL_SELECTION") - 1) == 0 && xevent->xproperty.state == PropertyNewValue) { 959 videodata->selection_incr_waiting = false; 960 } 961 962 if (name_of_atom) { 963 X11_XFree(name_of_atom); 964 } 965 } break; 966 } 967} 968 969static Bool isMapNotify(Display *display, XEvent *ev, XPointer arg) 970{ 971 XUnmapEvent *unmap; 972 973 unmap = (XUnmapEvent *)arg; 974 975 return ev->type == MapNotify && 976 ev->xmap.window == unmap->window && 977 ev->xmap.serial == unmap->serial; 978} 979 980static Bool isReparentNotify(Display *display, XEvent *ev, XPointer arg) 981{ 982 XUnmapEvent *unmap; 983 984 unmap = (XUnmapEvent *)arg; 985 986 return ev->type == ReparentNotify && 987 ev->xreparent.window == unmap->window && 988 ev->xreparent.serial == unmap->serial; 989} 990 991static bool IsHighLatin1(const char *string, int length) 992{ 993 while (length-- > 0) { 994 Uint8 ch = (Uint8)*string; 995 if (ch >= 0x80) { 996 return true; 997 } 998 ++string; 999 } 1000 return false; 1001} 1002 1003static int XLookupStringAsUTF8(XKeyEvent *event_struct, char *buffer_return, int bytes_buffer, KeySym *keysym_return, XComposeStatus *status_in_out) 1004{ 1005 int result = X11_XLookupString(event_struct, buffer_return, bytes_buffer, keysym_return, status_in_out); 1006 if (IsHighLatin1(buffer_return, result)) { 1007 char *utf8_text = SDL_iconv_string("UTF-8", "ISO-8859-1", buffer_return, result + 1); 1008 if (utf8_text) { 1009 SDL_strlcpy(buffer_return, utf8_text, bytes_buffer); 1010 SDL_free(utf8_text); 1011 return SDL_strlen(buffer_return); 1012 } else { 1013 return 0; 1014 } 1015 } 1016 return result; 1017} 1018 1019SDL_WindowData *X11_FindWindow(SDL_VideoData *videodata, Window window) 1020{ 1021 if (videodata && videodata->windowlist) { 1022 for (int i = 0; i < videodata->numwindows; ++i) { 1023 if ((videodata->windowlist[i] != NULL) && 1024 (videodata->windowlist[i]->xwindow == window)) { 1025 return videodata->windowlist[i]; 1026 } 1027 } 1028 } 1029 return NULL; 1030} 1031 1032Uint64 X11_GetEventTimestamp(unsigned long time) 1033{ 1034 // FIXME: Get the event time in the SDL tick time base 1035 return SDL_GetTicksNS(); 1036} 1037 1038void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_KeyboardID keyboardID, XEvent *xevent) 1039{ 1040 SDL_VideoData *videodata = _this->internal; 1041 Display *display = videodata->display; 1042 KeyCode keycode = xevent->xkey.keycode; 1043 KeySym keysym = NoSymbol; 1044 int text_length = 0; 1045 char text[64]; 1046 Status status = 0; 1047 bool handled_by_ime = false; 1048 bool pressed = (xevent->type == KeyPress); 1049 SDL_Scancode scancode = videodata->keyboard.key_layout[keycode]; 1050 Uint64 timestamp = X11_GetEventTimestamp(xevent->xkey.time); 1051 1052#ifdef DEBUG_XEVENTS 1053 SDL_Log("window 0x%lx %s (X11 keycode = 0x%X)", xevent->xany.window, (xevent->type == KeyPress ? "KeyPress" : "KeyRelease"), xevent->xkey.keycode); 1054#endif 1055#ifdef DEBUG_SCANCODES 1056 if (scancode == SDL_SCANCODE_UNKNOWN && keycode) { 1057 int min_keycode, max_keycode; 1058 X11_XDisplayKeycodes(display, &min_keycode, &max_keycode); 1059 keysym = X11_KeyCodeToSym(_this, keycode, xevent->xkey.state >> 13); 1060 SDL_Log("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).", 1061 keycode, keycode - min_keycode, keysym, 1062 X11_XKeysymToString(keysym)); 1063 } 1064#endif // DEBUG SCANCODES 1065 1066 text[0] = '\0'; 1067 1068 // XKB updates the modifiers explicitly via a state event. 1069 if (!videodata->keyboard.xkb_enabled) { 1070 videodata->keyboard.pressed_modifiers = xevent->xkey.state & (ShiftMask | ControlMask | Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask); 1071 videodata->keyboard.locked_modifiers = xevent->xkey.state & (LockMask | videodata->keyboard.numlock_mask | videodata->keyboard.scrolllock_mask); 1072 } 1073 1074 if (SDL_TextInputActive(windowdata->window)) { 1075 // filter events catches XIM events and sends them to the correct handler 1076 if (X11_XFilterEvent(xevent, None)) { 1077#ifdef DEBUG_XEVENTS 1078 SDL_Log("Filtered event type = %d display = %p window = 0x%lx", 1079 xevent->type, xevent->xany.display, xevent->xany.window); 1080#endif 1081 handled_by_ime = true; 1082 } 1083 1084 if (!handled_by_ime) { 1085#ifdef X_HAVE_UTF8_STRING 1086 if (windowdata->ic && xevent->type == KeyPress) { 1087 text_length = X11_Xutf8LookupString(windowdata->ic, &xevent->xkey, text, sizeof(text) - 1, 1088 &keysym, &status); 1089 } else { 1090 text_length = XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text) - 1, &keysym, NULL); 1091 } 1092#else 1093 text_length = XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text) - 1, &keysym, NULL); 1094#endif 1095 } 1096 } 1097 1098 if (!handled_by_ime) { 1099 if (pressed) { 1100 X11_HandleModifierKeys(videodata, scancode, true); 1101 SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, true); 1102 1103 if (*text && !(SDL_GetModState() & (SDL_KMOD_CTRL | SDL_KMOD_ALT))) { 1104 text[text_length] = '\0'; 1105 X11_ClearComposition(windowdata); 1106 SDL_SendKeyboardText(text); 1107 } 1108 } else { 1109 if (X11_KeyRepeat(display, xevent)) { 1110 // We're about to get a repeated key down, ignore the key up 1111 return; 1112 } 1113 1114 X11_HandleModifierKeys(videodata, scancode, false); 1115 SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, false); 1116 } 1117 } 1118 1119 if (pressed) { 1120 X11_UpdateUserTime(windowdata, xevent->xkey.time); 1121 } 1122} 1123 1124void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time) 1125{ 1126 SDL_Window *window = windowdata->window; 1127 int xticks = 0, yticks = 0; 1128 Uint64 timestamp = X11_GetEventTimestamp(time); 1129 1130#ifdef DEBUG_XEVENTS 1131 SDL_Log("window 0x%lx: ButtonPress (X11 button = %d)", windowdata->xwindow, button); 1132#endif 1133 1134 SDL_Mouse *mouse = SDL_GetMouse(); 1135 if (!mouse->relative_mode && (x != mouse->x || y != mouse->y)) { 1136 X11_ProcessHitTest(_this, windowdata, x, y, false); 1137 SDL_SendMouseMotion(timestamp, window, mouseID, false, x, y); 1138 } 1139 1140 if (X11_IsWheelEvent(button, &xticks, &yticks)) { 1141 SDL_SendMouseWheel(timestamp, window, mouseID, (float)-xticks, (float)yticks, SDL_MOUSEWHEEL_NORMAL); 1142 } else { 1143 bool ignore_click = false; 1144 if (button > 7) { 1145 /* X button values 4-7 are used for scrolling, so X1 is 8, X2 is 9, ... 1146 => subtract (8-SDL_BUTTON_X1) to get value SDL expects */ 1147 button -= (8 - SDL_BUTTON_X1); 1148 } 1149 if (button == Button1) { 1150 if (X11_TriggerHitTestAction(_this, windowdata, x, y)) { 1151 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_HIT_TEST, 0, 0); 1152 return; // don't pass this event on to app. 1153 } 1154 } 1155 if (windowdata->last_focus_event_time) { 1156 const int X11_FOCUS_CLICK_TIMEOUT = 10; 1157 if (SDL_GetTicks() < (windowdata->last_focus_event_time + X11_FOCUS_CLICK_TIMEOUT)) { 1158 ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, false); 1159 } 1160 windowdata->last_focus_event_time = 0; 1161 } 1162 if (!ignore_click) { 1163 SDL_SendMouseButton(timestamp, window, mouseID, button, true); 1164 } 1165 } 1166 X11_UpdateUserTime(windowdata, time); 1167} 1168 1169void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, unsigned long time) 1170{ 1171 SDL_Window *window = windowdata->window; 1172 // The X server sends a Release event for each Press for wheels. Ignore them. 1173 int xticks = 0, yticks = 0; 1174 Uint64 timestamp = X11_GetEventTimestamp(time); 1175 1176#ifdef DEBUG_XEVENTS 1177 SDL_Log("window 0x%lx: ButtonRelease (X11 button = %d)", windowdata->xwindow, button); 1178#endif 1179 if (!X11_IsWheelEvent(button, &xticks, &yticks)) { 1180 if (button > 7) { 1181 // see explanation at case ButtonPress 1182 button -= (8 - SDL_BUTTON_X1); 1183 } 1184 SDL_SendMouseButton(timestamp, window, mouseID, button, false); 1185 1186 if (window->internal->pending_grab) { 1187 X11_SetWindowMouseGrab(_this, window, true); 1188 } 1189 } 1190} 1191 1192void X11_GetBorderValues(SDL_WindowData *data) 1193{ 1194 SDL_VideoData *videodata = data->videodata; 1195 Display *display = videodata->display; 1196 1197 Atom type; 1198 int format; 1199 unsigned long nitems, bytes_after; 1200 unsigned char *property; 1201 1202 // Some compositors will send extents even when the border hint is turned off. Ignore them in this case. 1203 if (!(data->window->flags & SDL_WINDOW_BORDERLESS)) { 1204 if (X11_XGetWindowProperty(display, data->xwindow, videodata->atoms._NET_FRAME_EXTENTS, 0, 16, 0, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &property) == Success) { 1205 if (type != None && nitems == 4) { 1206 data->border_left = (int)((long *)property)[0]; 1207 data->border_right = (int)((long *)property)[1]; 1208 data->border_top = (int)((long *)property)[2]; 1209 data->border_bottom = (int)((long *)property)[3]; 1210 } 1211 X11_XFree(property); 1212 1213#ifdef DEBUG_XEVENTS 1214 SDL_Log("New _NET_FRAME_EXTENTS: left=%d right=%d, top=%d, bottom=%d", data->border_left, data->border_right, data->border_top, data->border_bottom); 1215#endif 1216 } 1217 } else { 1218 data->border_left = data->border_top = data->border_right = data->border_bottom = 0; 1219 } 1220} 1221 1222void X11_EmitConfigureNotifyEvents(SDL_WindowData *data, XConfigureEvent *xevent) 1223{ 1224 if (!data->size_move_event_flags) { 1225 int x = xevent->x; 1226 int y = xevent->y; 1227 1228 if (xevent->x != data->last_xconfigure.x || 1229 xevent->y != data->last_xconfigure.y) { 1230 data->pending_operation &= ~X11_PENDING_OP_MOVE; 1231 } 1232 SDL_GlobalToRelativeForWindow(data->window, x, y, &x, &y); 1233 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED, x, y); 1234 1235 for (SDL_Window *w = data->window->first_child; w; w = w->next_sibling) { 1236 // Don't update hidden child popup windows, their relative position doesn't change 1237 if (SDL_WINDOW_IS_POPUP(w) && !(w->flags & SDL_WINDOW_HIDDEN)) { 1238 X11_UpdateWindowPosition(w, true); 1239 } 1240 } 1241 1242 if (xevent->width != data->last_xconfigure.width || 1243 xevent->height != data->last_xconfigure.height) { 1244 data->pending_operation &= ~X11_PENDING_OP_RESIZE; 1245 } 1246 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESIZED, xevent->width, xevent->height); 1247 } 1248 1249 SDL_copyp(&data->last_xconfigure, xevent); 1250} 1251 1252static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) 1253{ 1254 SDL_VideoData *videodata = _this->internal; 1255 Display *display; 1256 SDL_WindowData *data; 1257 XClientMessageEvent m; 1258 int i; 1259 1260 SDL_assert(videodata != NULL); 1261 display = videodata->display; 1262 1263 // filter events catches XIM events and sends them to the correct handler 1264 // Key press/release events are filtered in X11_HandleKeyEvent() 1265 if (xevent->type != KeyPress && xevent->type != KeyRelease) { 1266 if (X11_XFilterEvent(xevent, None)) { 1267#ifdef DEBUG_XEVENTS 1268 SDL_Log("Filtered event type = %d display = %p window = 0x%lx", 1269 xevent->type, xevent->xany.display, xevent->xany.window); 1270#endif 1271 return; 1272 } 1273 } 1274 1275#ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS 1276 if (xevent->type == GenericEvent) { 1277 X11_HandleGenericEvent(_this, xevent); 1278 return; 1279 } 1280#endif 1281 1282 // Calling the event hook for generic events happens in X11_HandleGenericEvent(), where the event data is available 1283 if (g_X11EventHook) { 1284 if (!g_X11EventHook(g_X11EventHookData, xevent)) { 1285 return; 1286 } 1287 } 1288 1289#ifdef SDL_VIDEO_DRIVER_X11_XRANDR 1290 if (videodata->xrandr_event_base && (xevent->type == (videodata->xrandr_event_base + RRNotify))) { 1291 X11_HandleXRandREvent(_this, xevent); 1292 } 1293#endif 1294 1295#ifdef DEBUG_XEVENTS 1296 SDL_Log("X11 event type = %d display = %p window = 0x%lx", 1297 xevent->type, xevent->xany.display, xevent->xany.window); 1298#endif 1299 1300#ifdef SDL_VIDEO_DRIVER_X11_XFIXES 1301 if (SDL_X11_HAVE_XFIXES && 1302 xevent->type == X11_GetXFixesSelectionNotifyEvent()) { 1303 XFixesSelectionNotifyEvent *ev = (XFixesSelectionNotifyEvent *)xevent; 1304 1305#ifdef DEBUG_XEVENTS 1306 SDL_Log("window CLIPBOARD: XFixesSelectionNotify (selection = %s)", 1307 X11_XGetAtomName(display, ev->selection)); 1308#endif 1309 1310 if (ev->subtype == XFixesSetSelectionOwnerNotify) 1311 { 1312 if (ev->selection != videodata->atoms.CLIPBOARD) 1313 return; 1314 1315 if (X11_XGetSelectionOwner(display, ev->selection) == videodata->clipboard_window) 1316 return; 1317 1318 /* when here we're notified that the clipboard had an external change, we request the 1319 * available mime types by asking for a conversion to the TARGETS format. We should get a 1320 * SelectionNotify event later, and when treating these results, we will push a ClipboardUpdated 1321 * event 1322 */ 1323 1324 X11_XConvertSelection(display, videodata->atoms.CLIPBOARD, videodata->atoms.TARGETS, 1325 videodata->atoms.SDL_FORMATS, GetWindow(_this), CurrentTime); 1326 } 1327 1328 return; 1329 } 1330#endif // SDL_VIDEO_DRIVER_X11_XFIXES 1331 1332 if ((videodata->clipboard_window != None) && 1333 (videodata->clipboard_window == xevent->xany.window)) { 1334 X11_HandleClipboardEvent(_this, xevent); 1335 return; 1336 } 1337 1338 // xsettings internally filters events for the windows it watches 1339 X11_HandleXsettingsEvent(_this, xevent); 1340 1341 data = X11_FindWindow(videodata, xevent->xany.window); 1342 1343 if (!data) { 1344 // The window for KeymapNotify, etc events is 0 1345#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB 1346 if (videodata->keyboard.xkb_enabled && xevent->type == videodata->keyboard.xkb.event) { 1347 XkbEvent *xkbevent = (XkbEvent *)xevent; 1348 switch (xkbevent->any.xkb_type) { 1349 case XkbStateNotify: 1350 { 1351#ifdef DEBUG_XEVENTS 1352 SDL_Log("window 0x%lx: XkbStateNotify!", xevent->xany.window); 1353#endif 1354 if ((xkbevent->state.changed & XkbGroupStateMask) && xkbevent->state.group != videodata->keyboard.xkb.current_group) { 1355 videodata->keyboard.xkb.current_group = xkbevent->state.group; 1356 SDL_SetKeymap(videodata->keyboard.xkb.keymaps[videodata->keyboard.xkb.current_group], true); 1357 } 1358 1359 if (xkbevent->state.changed & XkbModifierStateMask) { 1360 videodata->keyboard.pressed_modifiers = xkbevent->state.base_mods; 1361 videodata->keyboard.locked_modifiers = xkbevent->state.latched_mods | xkbevent->state.locked_mods; 1362 X11_ReconcileModifiers(videodata, false); 1363 } 1364 } break; 1365 1366 case XkbMapNotify: 1367#ifdef DEBUG_XEVENTS 1368 SDL_Log("window 0x%lx: XkbMapNotify!", xevent->xany.window); 1369 SDL_FALLTHROUGH; 1370#endif 1371 case XkbNewKeyboardNotify: 1372 { 1373#ifdef DEBUG_XEVENTS 1374 if (xkbevent->any.xkb_type == XkbNewKeyboardNotify) { 1375 SDL_Log("window 0x%lx: XkbNewKeyboardNotify!", xevent->xany.window); 1376 } 1377#endif 1378 X11_XkbRefreshKeyboardMapping(&xkbevent->map); 1379 1380 // Don't redundantly rebuild the keymap if this is a duplicate event. 1381 if (xkbevent->any.serial != videodata->keyboard.xkb.last_map_serial) { 1382 videodata->keyboard.xkb.last_map_serial = xkbevent->any.serial; 1383 X11_UpdateKeymap(_this, true); 1384 } 1385 } break; 1386 1387 default: 1388 break; 1389 } 1390 } else 1391#endif 1392 if (xevent->type == KeymapNotify) { 1393#ifdef DEBUG_XEVENTS 1394 SDL_Log("window 0x%lx: KeymapNotify!", xevent->xany.window); 1395#endif 1396 if (SDL_GetKeyboardFocus() != NULL) { 1397 if (!videodata->keyboard.xkb_enabled) { 1398 X11_UpdateKeymap(_this, true); 1399 } 1400 X11_ReconcileKeyboardState(_this); 1401 } 1402 } else if (xevent->type == MappingNotify) { 1403 const int request = xevent->xmapping.request; 1404 1405 if (request == MappingPointer) { 1406#ifdef DEBUG_XEVENTS 1407 SDL_Log("window 0x%lx: MappingNotify!", xevent->xany.window); 1408#endif 1409 X11_Xinput2UpdatePointerMapping(_this); 1410 } else if (!videodata->keyboard.xkb_enabled) { 1411 // Has the keyboard layout changed? 1412#ifdef DEBUG_XEVENTS 1413 SDL_Log("window 0x%lx: MappingNotify!", xevent->xany.window); 1414#endif 1415 if (request == MappingKeyboard || request == MappingModifier) { 1416 X11_XRefreshKeyboardMapping(&xevent->xmapping); 1417 } 1418 1419 X11_UpdateKeymap(_this, true); 1420 } 1421 } else if (xevent->type == PropertyNotify && videodata) { 1422 char *name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom); 1423 if (name_of_atom) { 1424 if (SDL_startswith(name_of_atom, "_ICC_PROFILE")) { 1425 XWindowAttributes attrib; 1426 int screennum; 1427 for (i = 0; i < videodata->numwindows; ++i) { 1428 if (videodata->windowlist[i] != NULL) { 1429 data = videodata->windowlist[i]; 1430 X11_XGetWindowAttributes(display, data->xwindow, &attrib); 1431 screennum = X11_XScreenNumberOfScreen(attrib.screen); 1432 if (screennum == 0 && SDL_strcmp(name_of_atom, "_ICC_PROFILE") == 0) { 1433 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0); 1434 } else if (SDL_strncmp(name_of_atom, "_ICC_PROFILE_", sizeof("_ICC_PROFILE_") - 1) == 0 && SDL_strlen(name_of_atom) > sizeof("_ICC_PROFILE_") - 1) { 1435 int iccscreennum = SDL_atoi(&name_of_atom[sizeof("_ICC_PROFILE_") - 1]); 1436 1437 if (screennum == iccscreennum) { 1438 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0); 1439 } 1440 } 1441 } 1442 } 1443 } else if (SDL_strcmp(name_of_atom, "_NET_WORKAREA") == 0) { 1444 for (i = 0; i < _this->num_displays; ++i) { 1445 SDL_SendDisplayEvent(_this->displays[i], SDL_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED, 0, 0); 1446 } 1447 } 1448 X11_XFree(name_of_atom); 1449 } 1450 } 1451 return; 1452 } 1453 1454 switch (xevent->type) { 1455 1456 // Gaining mouse coverage? 1457 case EnterNotify: 1458 { 1459 SDL_Mouse *mouse = SDL_GetMouse(); 1460#ifdef DEBUG_XEVENTS 1461 SDL_Log("window 0x%lx: EnterNotify! (%d,%d,%d)", xevent->xany.window, 1462 xevent->xcrossing.x, 1463 xevent->xcrossing.y, 1464 xevent->xcrossing.mode); 1465 if (xevent->xcrossing.mode == NotifyGrab) { 1466 SDL_Log("Mode: NotifyGrab"); 1467 } 1468 if (xevent->xcrossing.mode == NotifyUngrab) { 1469 SDL_Log("Mode: NotifyUngrab"); 1470 } 1471#endif 1472 SDL_SetMouseFocus(data->window); 1473 1474 mouse->last_x = xevent->xcrossing.x; 1475 mouse->last_y = xevent->xcrossing.y; 1476 1477#ifdef SDL_VIDEO_DRIVER_X11_XFIXES 1478 { 1479 // Only create the barriers if we have input focus 1480 SDL_WindowData *windowdata = data->window->internal; 1481 if ((data->pointer_barrier_active == true) && windowdata->window->flags & SDL_WINDOW_INPUT_FOCUS) { 1482 X11_ConfineCursorWithFlags(_this, windowdata->window, &windowdata->barrier_rect, X11_BARRIER_HANDLED_BY_EVENT); 1483 } 1484 } 1485#endif 1486 1487 if (!mouse->relative_mode) { 1488 SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xcrossing.x, (float)xevent->xcrossing.y); 1489 } 1490 1491 // We ungrab in LeaveNotify, so we may need to grab again here, but not if captured, as the capture can be lost. 1492 if (!(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) { 1493 SDL_UpdateWindowGrab(data->window); 1494 } 1495 1496 X11_ProcessHitTest(_this, data, mouse->last_x, mouse->last_y, true); 1497 } break; 1498 // Losing mouse coverage? 1499 case LeaveNotify: 1500 { 1501#ifdef DEBUG_XEVENTS 1502 SDL_Log("window 0x%lx: LeaveNotify! (%d,%d,%d)", xevent->xany.window, 1503 xevent->xcrossing.x, 1504 xevent->xcrossing.y, 1505 xevent->xcrossing.mode); 1506 if (xevent->xcrossing.mode == NotifyGrab) { 1507 SDL_Log("Mode: NotifyGrab"); 1508 } 1509 if (xevent->xcrossing.mode == NotifyUngrab) { 1510 SDL_Log("Mode: NotifyUngrab"); 1511 } 1512#endif 1513 if (!SDL_GetMouse()->relative_mode) { 1514 SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xcrossing.x, (float)xevent->xcrossing.y); 1515 } 1516 1517 if (xevent->xcrossing.mode != NotifyGrab && 1518 xevent->xcrossing.mode != NotifyUngrab && 1519 xevent->xcrossing.detail != NotifyInferior) { 1520 1521 /* In order for interaction with the window decorations and menu to work properly 1522 on Mutter, we need to ungrab the keyboard when the mouse leaves. */ 1523 if (!(data->window->flags & SDL_WINDOW_FULLSCREEN)) { 1524 X11_SetWindowKeyboardGrab(_this, data->window, false); 1525 } 1526 1527 SDL_SetMouseFocus(NULL); 1528 } 1529 } break; 1530 1531 // Gaining input focus? 1532 case FocusIn: 1533 { 1534 if (xevent->xfocus.mode == NotifyGrab || xevent->xfocus.mode == NotifyUngrab) { 1535 // Someone is handling a global hotkey, ignore it 1536#ifdef DEBUG_XEVENTS 1537 SDL_Log("window 0x%lx: FocusIn (NotifyGrab/NotifyUngrab, ignoring)", xevent->xany.window); 1538#endif 1539 break; 1540 } 1541 1542 if (xevent->xfocus.detail == NotifyInferior || xevent->xfocus.detail == NotifyPointer) { 1543#ifdef DEBUG_XEVENTS 1544 SDL_Log("window 0x%lx: FocusIn (NotifyInferior/NotifyPointer, ignoring)", xevent->xany.window); 1545#endif 1546 break; 1547 } 1548#ifdef DEBUG_XEVENTS 1549 SDL_Log("window 0x%lx: FocusIn!", xevent->xany.window); 1550#endif 1551 if (!videodata->last_mode_change_deadline) /* no recent mode changes */ { 1552 data->pending_focus = PENDING_FOCUS_NONE; 1553 data->pending_focus_time = 0; 1554 X11_DispatchFocusIn(_this, data); 1555 } else { 1556 data->pending_focus = PENDING_FOCUS_IN; 1557 data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME; 1558 } 1559 data->last_focus_event_time = SDL_GetTicks(); 1560 } break; 1561 1562 // Losing input focus? 1563 case FocusOut: 1564 { 1565 if (xevent->xfocus.mode == NotifyGrab || xevent->xfocus.mode == NotifyUngrab) { 1566 // Someone is handling a global hotkey, ignore it 1567#ifdef DEBUG_XEVENTS 1568 SDL_Log("window 0x%lx: FocusOut (NotifyGrab/NotifyUngrab, ignoring)", xevent->xany.window); 1569#endif 1570 break; 1571 } 1572 if (xevent->xfocus.detail == NotifyInferior || xevent->xfocus.detail == NotifyPointer) { 1573 /* We still have focus if a child gets focus. We also don't 1574 care about the position of the pointer when the keyboard 1575 focus changed. */ 1576#ifdef DEBUG_XEVENTS 1577 SDL_Log("window 0x%lx: FocusOut (NotifyInferior/NotifyPointer, ignoring)", xevent->xany.window); 1578#endif 1579 break; 1580 } 1581#ifdef DEBUG_XEVENTS 1582 SDL_Log("window 0x%lx: FocusOut!", xevent->xany.window); 1583#endif 1584 if (!videodata->last_mode_change_deadline) /* no recent mode changes */ { 1585 data->pending_focus = PENDING_FOCUS_NONE; 1586 data->pending_focus_time = 0; 1587 X11_DispatchFocusOut(_this, data); 1588 } else { 1589 data->pending_focus = PENDING_FOCUS_OUT; 1590 data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME; 1591 } 1592 1593#ifdef SDL_VIDEO_DRIVER_X11_XFIXES 1594 // Disable confinement if it is activated. 1595 if (data->pointer_barrier_active == true) { 1596 X11_ConfineCursorWithFlags(_this, data->window, NULL, X11_BARRIER_HANDLED_BY_EVENT); 1597 } 1598#endif // SDL_VIDEO_DRIVER_X11_XFIXES 1599 } break; 1600 1601 1602 // Have we been iconified? 1603 case UnmapNotify: 1604 { 1605 XEvent ev; 1606 1607#ifdef DEBUG_XEVENTS 1608 SDL_Log("window 0x%lx: UnmapNotify!", xevent->xany.window); 1609#endif 1610 1611 if (X11_XCheckIfEvent(display, &ev, &isReparentNotify, (XPointer)&xevent->xunmap)) { 1612 X11_XCheckIfEvent(display, &ev, &isMapNotify, (XPointer)&xevent->xunmap); 1613 } else { 1614 X11_DispatchUnmapNotify(data); 1615 } 1616 1617#ifdef SDL_VIDEO_DRIVER_X11_XFIXES 1618 // Disable confinement if the window gets hidden. 1619 if (data->pointer_barrier_active == true) { 1620 X11_ConfineCursorWithFlags(_this, data->window, NULL, X11_BARRIER_HANDLED_BY_EVENT); 1621 } 1622#endif // SDL_VIDEO_DRIVER_X11_XFIXES 1623 } break; 1624 1625 // Have we been restored? 1626 case MapNotify: 1627 { 1628#ifdef DEBUG_XEVENTS 1629 SDL_Log("window 0x%lx: MapNotify!", xevent->xany.window); 1630#endif 1631 X11_DispatchMapNotify(data); 1632 1633#ifdef SDL_VIDEO_DRIVER_X11_XFIXES 1634 // Enable confinement if it was activated. 1635 if (data->pointer_barrier_active == true) { 1636 X11_ConfineCursorWithFlags(_this, data->window, &data->barrier_rect, X11_BARRIER_HANDLED_BY_EVENT); 1637 } 1638#endif // SDL_VIDEO_DRIVER_X11_XFIXES 1639 } break; 1640 1641 // Have we been resized or moved? 1642 case ConfigureNotify: 1643 { 1644#ifdef DEBUG_XEVENTS 1645 SDL_Log("window 0x%lx: ConfigureNotify! (position: %d,%d, size: %dx%d)", xevent->xany.window, 1646 xevent->xconfigure.x, xevent->xconfigure.y, 1647 xevent->xconfigure.width, xevent->xconfigure.height); 1648#endif 1649 // Real configure notify events are relative to the parent, synthetic events are absolute. 1650 if (!xevent->xconfigure.send_event) { 1651 unsigned int NumChildren; 1652 Window ChildReturn, Root, Parent; 1653 Window *Children; 1654 // Translate these coordinates back to relative to root 1655 X11_XQueryTree(data->videodata->display, xevent->xconfigure.window, &Root, &Parent, &Children, &NumChildren); 1656 X11_XTranslateCoordinates(xevent->xconfigure.display, 1657 Parent, DefaultRootWindow(xevent->xconfigure.display), 1658 xevent->xconfigure.x, xevent->xconfigure.y, 1659 &xevent->xconfigure.x, &xevent->xconfigure.y, 1660 &ChildReturn); 1661 } 1662 1663 /* Some window managers send ConfigureNotify before PropertyNotify when changing state (Xfce and 1664 * fvwm are known to do this), which is backwards from other window managers, as well as what is 1665 * expected by SDL and its clients. Defer emitting the size/move events until the corresponding 1666 * PropertyNotify arrives for consistency. 1667 */ 1668 const SDL_WindowFlags changed = X11_GetNetWMState(_this, data->window, xevent->xproperty.window) ^ data->window->flags; 1669 if (changed & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) { 1670 SDL_copyp(&data->pending_xconfigure, &xevent->xconfigure); 1671 data->emit_size_move_after_property_notify = true; 1672 } 1673 1674 if (!data->emit_size_move_after_property_notify) { 1675 X11_EmitConfigureNotifyEvents(data, &xevent->xconfigure); 1676 } 1677 1678#ifdef SDL_VIDEO_DRIVER_X11_XSYNC 1679 X11_HandleConfigure(data->window, &xevent->xconfigure); 1680#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ 1681 } break; 1682 1683 // Have we been requested to quit (or another client message?) 1684 case ClientMessage: 1685 { 1686 static int xdnd_version = 0; 1687 1688 if (xevent->xclient.message_type == videodata->atoms.XdndEnter) { 1689 1690 bool use_list = xevent->xclient.data.l[1] & 1; 1691 data->xdnd_source = xevent->xclient.data.l[0]; 1692 xdnd_version = (xevent->xclient.data.l[1] >> 24); 1693#ifdef DEBUG_XEVENTS 1694 SDL_Log("XID of source window : 0x%lx", data->xdnd_source); 1695 SDL_Log("Protocol version to use : %d", xdnd_version); 1696 SDL_Log("More then 3 data types : %d", (int)use_list); 1697#endif 1698 1699 if (use_list) { 1700 // fetch conversion targets 1701 SDL_x11Prop p; 1702 X11_ReadProperty(&p, display, data->xdnd_source, videodata->atoms.XdndTypeList); 1703 // pick one 1704 data->xdnd_req = X11_PickTarget(display, (Atom *)p.data, p.count); 1705 X11_XFree(p.data); 1706 } else { 1707 // pick from list of three 1708 data->xdnd_req = X11_PickTargetFromAtoms(display, xevent->xclient.data.l[2], xevent->xclient.data.l[3], xevent->xclient.data.l[4]); 1709 } 1710 } else if (xevent->xclient.message_type == videodata->atoms.XdndLeave) { 1711#ifdef DEBUG_XEVENTS 1712 SDL_Log("XID of source window : 0x%lx", xevent->xclient.data.l[0]); 1713#endif 1714 SDL_SendDropComplete(data->window); 1715 } else if (xevent->xclient.message_type == videodata->atoms.XdndPosition) { 1716 1717#ifdef DEBUG_XEVENTS 1718 Atom act = videodata->atoms.XdndActionCopy; 1719 if (xdnd_version >= 2) { 1720 act = xevent->xclient.data.l[4]; 1721 } 1722 SDL_Log("Action requested by user is : %s", X11_XGetAtomName(display, act)); 1723#endif 1724 { 1725 // Drag and Drop position 1726 int root_x, root_y, window_x, window_y; 1727 Window ChildReturn; 1728 root_x = xevent->xclient.data.l[2] >> 16; 1729 root_y = xevent->xclient.data.l[2] & 0xffff; 1730 // Translate from root to current window position 1731 X11_XTranslateCoordinates(display, DefaultRootWindow(display), data->xwindow, 1732 root_x, root_y, &window_x, &window_y, &ChildReturn); 1733 1734 SDL_SendDropPosition(data->window, (float)window_x, (float)window_y); 1735 } 1736 1737 // reply with status 1738 SDL_zero(m); 1739 m.type = ClientMessage; 1740 m.display = xevent->xclient.display; 1741 m.window = xevent->xclient.data.l[0]; 1742 m.message_type = videodata->atoms.XdndStatus; 1743 m.format = 32; 1744 m.data.l[0] = data->xwindow; 1745 m.data.l[1] = (data->xdnd_req != None); 1746 m.data.l[2] = 0; // specify an empty rectangle 1747 m.data.l[3] = 0; 1748 m.data.l[4] = videodata->atoms.XdndActionCopy; // we only accept copying anyway 1749 1750 X11_XSendEvent(display, xevent->xclient.data.l[0], False, NoEventMask, (XEvent *)&m); 1751 X11_XFlush(display); 1752 } else if (xevent->xclient.message_type == videodata->atoms.XdndDrop) { 1753 if (data->xdnd_req == None) { 1754 // say again - not interested! 1755 SDL_zero(m); 1756 m.type = ClientMessage; 1757 m.display = xevent->xclient.display; 1758 m.window = xevent->xclient.data.l[0]; 1759 m.message_type = videodata->atoms.XdndFinished; 1760 m.format = 32; 1761 m.data.l[0] = data->xwindow; 1762 m.data.l[1] = 0; 1763 m.data.l[2] = None; // fail! 1764 X11_XSendEvent(display, xevent->xclient.data.l[0], False, NoEventMask, (XEvent *)&m); 1765 } else { 1766 // convert 1767 if (xdnd_version >= 1) { 1768 X11_XConvertSelection(display, videodata->atoms.XdndSelection, data->xdnd_req, videodata->atoms.PRIMARY, data->xwindow, xevent->xclient.data.l[2]); 1769 } else { 1770 X11_XConvertSelection(display, videodata->atoms.XdndSelection, data->xdnd_req, videodata->atoms.PRIMARY, data->xwindow, CurrentTime); 1771 } 1772 } 1773 } else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) && 1774 (xevent->xclient.format == 32) && 1775 (xevent->xclient.data.l[0] == videodata->atoms._NET_WM_PING)) { 1776 Window root = DefaultRootWindow(display); 1777 1778#ifdef DEBUG_XEVENTS 1779 SDL_Log("window 0x%lx: _NET_WM_PING", xevent->xany.window); 1780#endif 1781 xevent->xclient.window = root; 1782 X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, xevent); 1783 break; 1784 } 1785 1786 else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) && 1787 (xevent->xclient.format == 32) && 1788 (xevent->xclient.data.l[0] == videodata->atoms.WM_DELETE_WINDOW)) { 1789 1790#ifdef DEBUG_XEVENTS 1791 SDL_Log("window 0x%lx: WM_DELETE_WINDOW", xevent->xany.window); 1792#endif 1793 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_CLOSE_REQUESTED, 0, 0); 1794 break; 1795 } else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) && 1796 (xevent->xclient.format == 32) && 1797 (xevent->xclient.data.l[0] == videodata->atoms._NET_WM_SYNC_REQUEST)) { 1798 1799#ifdef DEBUG_XEVENTS 1800 printf("window %p: _NET_WM_SYNC_REQUEST\n", data); 1801#endif 1802#ifdef SDL_VIDEO_DRIVER_X11_XSYNC 1803 X11_HandleSyncRequest(data->window, &xevent->xclient); 1804#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ 1805 break; 1806 } 1807 } break; 1808 1809 // Do we need to refresh ourselves? 1810 case Expose: 1811 { 1812#ifdef DEBUG_XEVENTS 1813 SDL_Log("window 0x%lx: Expose (count = %d)", xevent->xany.window, xevent->xexpose.count); 1814#endif 1815 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_EXPOSED, 0, 0); 1816 } break; 1817 1818 /* Use XInput2 instead of the xevents API if possible, for: 1819 - KeyPress 1820 - KeyRelease 1821 - MotionNotify 1822 - ButtonPress 1823 - ButtonRelease 1824 XInput2 has more precise information, e.g., to distinguish different input devices. */ 1825 case KeyPress: 1826 case KeyRelease: 1827 { 1828 if (data->xinput2_keyboard_enabled) { 1829 // This input is being handled by XInput2 1830 break; 1831 } 1832 1833 X11_HandleKeyEvent(_this, data, SDL_GLOBAL_KEYBOARD_ID, xevent); 1834 } break; 1835 1836 case MotionNotify: 1837 { 1838 if (X11_Xinput2HandlesMotionForWindow(data)) { 1839 // This input is being handled by XInput2 1840 break; 1841 } 1842 1843 SDL_Mouse *mouse = SDL_GetMouse(); 1844 if (!mouse->relative_mode) { 1845#ifdef DEBUG_MOTION 1846 SDL_Log("window 0x%lx: X11 motion: %d,%d", xevent->xany.window, xevent->xmotion.x, xevent->xmotion.y); 1847#endif 1848 1849 X11_ProcessHitTest(_this, data, (float)xevent->xmotion.x, (float)xevent->xmotion.y, false); 1850 SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xmotion.x, (float)xevent->xmotion.y); 1851 } 1852 } break; 1853 1854 case ButtonPress: 1855 { 1856 if (data->xinput2_mouse_enabled) { 1857 // This input is being handled by XInput2 1858 break; 1859 } 1860 1861 X11_HandleButtonPress(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button, 1862 xevent->xbutton.x, xevent->xbutton.y, xevent->xbutton.time); 1863 } break; 1864 1865 case ButtonRelease: 1866 { 1867 if (data->xinput2_mouse_enabled) { 1868 // This input is being handled by XInput2 1869 break; 1870 } 1871 1872 X11_HandleButtonRelease(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button, xevent->xbutton.time); 1873 } break; 1874 1875 case PropertyNotify: 1876 { 1877#ifdef DEBUG_XEVENTS 1878 unsigned char *propdata; 1879 int status, real_format; 1880 Atom real_type; 1881 unsigned long items_read, items_left; 1882 1883 char *name = X11_XGetAtomName(display, xevent->xproperty.atom); 1884 if (name) { 1885 SDL_Log("window 0x%lx: PropertyNotify: %s %s time=%lu", xevent->xany.window, name, (xevent->xproperty.state == PropertyDelete) ? "deleted" : "changed", xevent->xproperty.time); 1886 X11_XFree(name); 1887 } 1888 1889 status = X11_XGetWindowProperty(display, data->xwindow, xevent->xproperty.atom, 0L, 8192L, False, AnyPropertyType, &real_type, &real_format, &items_read, &items_left, &propdata); 1890 if (status == Success && items_read > 0) { 1891 if (real_type == XA_INTEGER) { 1892 int *values = (int *)propdata; 1893 1894 SDL_Log("{"); 1895 for (i = 0; i < items_read; i++) { 1896 SDL_Log(" %d", values[i]); 1897 } 1898 SDL_Log(" }"); 1899 } else if (real_type == XA_CARDINAL) { 1900 if (real_format == 32) { 1901 Uint32 *values = (Uint32 *)propdata; 1902 1903 SDL_Log("{"); 1904 for (i = 0; i < items_read; i++) { 1905 SDL_Log(" %d", values[i]); 1906 } 1907 SDL_Log(" }"); 1908 } else if (real_format == 16) { 1909 Uint16 *values = (Uint16 *)propdata; 1910 1911 SDL_Log("{"); 1912 for (i = 0; i < items_read; i++) { 1913 SDL_Log(" %d", values[i]); 1914 } 1915 SDL_Log(" }"); 1916 } else if (real_format == 8) { 1917 Uint8 *values = (Uint8 *)propdata; 1918 1919 SDL_Log("{"); 1920 for (i = 0; i < items_read; i++) { 1921 SDL_Log(" %d", values[i]); 1922 } 1923 SDL_Log(" }"); 1924 } 1925 } else if (real_type == XA_STRING || 1926 real_type == videodata->atoms.UTF8_STRING) { 1927 SDL_Log("{ \"%s\" }", propdata); 1928 } else if (real_type == XA_ATOM) { 1929 Atom *atoms = (Atom *)propdata; 1930 1931 SDL_Log("{"); 1932 for (i = 0; i < items_read; i++) { 1933 char *atomname = X11_XGetAtomName(display, atoms[i]); 1934 if (atomname) { 1935 SDL_Log(" %s", atomname); 1936 X11_XFree(atomname); 1937 } 1938 } 1939 SDL_Log(" }"); 1940 } else { 1941 char *atomname = X11_XGetAtomName(display, real_type); 1942 SDL_Log("Unknown type: 0x%lx (%s)", real_type, atomname ? atomname : "UNKNOWN"); 1943 if (atomname) { 1944 X11_XFree(atomname); 1945 } 1946 } 1947 } 1948 if (status == Success) { 1949 X11_XFree(propdata); 1950 } 1951#endif // DEBUG_XEVENTS 1952 1953 /* Take advantage of this moment to make sure user_time has a 1954 valid timestamp from the X server, so if we later try to 1955 raise/restore this window, _NET_ACTIVE_WINDOW can have a 1956 non-zero timestamp, even if there's never been a mouse or 1957 key press to this window so far. Note that we don't try to 1958 set _NET_WM_USER_TIME here, though. That's only for legit 1959 user interaction with the window. */ 1960 if (!data->user_time) { 1961 data->user_time = xevent->xproperty.time; 1962 } 1963 1964 if (xevent->xproperty.atom == data->videodata->atoms._NET_WM_STATE) { 1965 /* Get the new state from the window manager. 1966 * Compositing window managers can alter visibility of windows 1967 * without ever mapping / unmapping them, so we handle that here, 1968 * because they use the NETWM protocol to notify us of changes. 1969 */ 1970 const SDL_WindowFlags flags = X11_GetNetWMState(_this, data->window, xevent->xproperty.window); 1971 const SDL_WindowFlags changed = flags ^ data->window->flags; 1972 1973 if ((changed & SDL_WINDOW_HIDDEN) && !(flags & SDL_WINDOW_HIDDEN)) { 1974 X11_DispatchMapNotify(data); 1975 } 1976 1977 if (!SDL_WINDOW_IS_POPUP(data->window)) { 1978 if (changed & SDL_WINDOW_FULLSCREEN) { 1979 data->pending_operation &= ~X11_PENDING_OP_FULLSCREEN; 1980 1981 if (flags & SDL_WINDOW_FULLSCREEN) { 1982 if (!(flags & SDL_WINDOW_MINIMIZED)) { 1983 const bool commit = SDL_memcmp(&data->window->current_fullscreen_mode, &data->requested_fullscreen_mode, sizeof(SDL_DisplayMode)) != 0; 1984 1985 // Ensure the maximized flag is cleared before entering fullscreen. 1986 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0); 1987 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0); 1988 if (commit) { 1989 /* This was initiated by the compositor, or the mode was changed between the request and the window 1990 * becoming fullscreen. Switch to the application requested mode if necessary. 1991 */ 1992 SDL_copyp(&data->window->current_fullscreen_mode, &data->window->requested_fullscreen_mode); 1993 SDL_UpdateFullscreenMode(data->window, SDL_FULLSCREEN_OP_UPDATE, true); 1994 } else { 1995 SDL_UpdateFullscreenMode(data->window, SDL_FULLSCREEN_OP_ENTER, false); 1996 } 1997 } 1998 } else { 1999 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0); 2000 SDL_UpdateFullscreenMode(data->window, false, false); 2001 2002 SDL_zero(data->requested_fullscreen_mode); 2003 2004 // Need to restore or update any limits changed while the window was fullscreen. 2005 X11_SetWindowMinMax(data->window, !!(flags & SDL_WINDOW_MAXIMIZED)); 2006 2007 // Toggle the borders if they were forced on while creating a borderless fullscreen window. 2008 if (data->fullscreen_borders_forced_on) { 2009 data->toggle_borders = true; 2010 data->fullscreen_borders_forced_on = false; 2011 } 2012 } 2013 2014 if ((flags & SDL_WINDOW_FULLSCREEN) && 2015 (data->border_top || data->border_left || data->border_bottom || data->border_right)) { 2016 /* If the window is entering fullscreen and the borders are 2017 * non-zero sized, turn off size events until the borders are 2018 * shut off to avoid bogus window sizes and positions, and 2019 * note that the old borders were non-zero for restoration. 2020 */ 2021 data->size_move_event_flags |= X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS; 2022 data->previous_borders_nonzero = true; 2023 } else if (!(flags & SDL_WINDOW_FULLSCREEN) && 2024 data->previous_borders_nonzero && 2025 (!data->border_top && !data->border_left && !data->border_bottom && !data->border_right)) { 2026 /* If the window is leaving fullscreen and the current borders 2027 * are zero sized, but weren't when entering fullscreen, turn 2028 * off size events until the borders come back to avoid bogus 2029 * window sizes and positions. 2030 */ 2031 data->size_move_event_flags |= X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS; 2032 data->previous_borders_nonzero = false; 2033 } else { 2034 data->size_move_event_flags = 0; 2035 data->previous_borders_nonzero = false; 2036 2037 if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) { 2038 data->toggle_borders = false; 2039 X11_SetWindowBordered(_this, data->window, !(data->window->flags & SDL_WINDOW_BORDERLESS)); 2040 } 2041 } 2042 } 2043 if ((changed & SDL_WINDOW_MAXIMIZED) && ((flags & SDL_WINDOW_MAXIMIZED) && !(flags & SDL_WINDOW_MINIMIZED))) { 2044 data->pending_operation &= ~X11_PENDING_OP_MAXIMIZE; 2045 if ((changed & SDL_WINDOW_MINIMIZED)) { 2046 data->pending_operation &= ~X11_PENDING_OP_RESTORE; 2047 // If coming out of minimized, send a restore event before sending maximized. 2048 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0); 2049 } 2050 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0); 2051 } 2052 if ((changed & SDL_WINDOW_MINIMIZED) && (flags & SDL_WINDOW_MINIMIZED)) { 2053 data->pending_operation &= ~X11_PENDING_OP_MINIMIZE; 2054 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0); 2055 } 2056 if (!(flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) { 2057 data->pending_operation &= ~X11_PENDING_OP_RESTORE; 2058 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0); 2059 2060 // Apply any pending state if restored. 2061 if (!(flags & SDL_WINDOW_FULLSCREEN)) { 2062 if (data->pending_position) { 2063 data->pending_position = false; 2064 data->pending_operation |= X11_PENDING_OP_MOVE; 2065 data->expected.x = data->window->pending.x - data->border_left; 2066 data->expected.y = data->window->pending.y - data->border_top; 2067 X11_XMoveWindow(display, data->xwindow, data->expected.x, data->expected.y); 2068 } 2069 if (data->pending_size) { 2070 data->pending_size = false; 2071 data->pending_operation |= X11_PENDING_OP_RESIZE; 2072 data->expected.w = data->window->pending.w; 2073 data->expected.h = data->window->pending.h; 2074 X11_XResizeWindow(display, data->xwindow, data->window->pending.w, data->window->pending.h); 2075 } 2076 } 2077 } 2078 if (data->emit_size_move_after_property_notify) { 2079 X11_EmitConfigureNotifyEvents(data, &data->pending_xconfigure); 2080 data->emit_size_move_after_property_notify = false; 2081 } 2082 if ((flags & SDL_WINDOW_INPUT_FOCUS)) { 2083 if (data->pending_move) { 2084 DispatchWindowMove(_this, data, &data->pending_move_point); 2085 data->pending_move = false; 2086 } 2087 } 2088 } 2089 if (changed & SDL_WINDOW_OCCLUDED) { 2090 SDL_SendWindowEvent(data->window, (flags & SDL_WINDOW_OCCLUDED) ? SDL_EVENT_WINDOW_OCCLUDED : SDL_EVENT_WINDOW_EXPOSED, 0, 0); 2091 } 2092 } else if (xevent->xproperty.atom == videodata->atoms.WM_STATE) { 2093 /* Support for ICCCM-compliant window managers (like i3) that change 2094 WM_STATE to WithdrawnState without sending UnmapNotify or updating 2095 _NET_WM_STATE when moving windows to invisible workspaces. */ 2096 Atom type; 2097 int format; 2098 unsigned long nitems, bytes_after; 2099 unsigned char *prop_data = NULL; 2100 2101 if (X11_XGetWindowProperty(display, data->xwindow, videodata->atoms.WM_STATE, 2102 0L, 2L, False, videodata->atoms.WM_STATE, 2103 &type, &format, &nitems, &bytes_after, &prop_data) == Success) { 2104 if (nitems > 0) { 2105 // WM_STATE: 0=Withdrawn, 1=Normal, 3=Iconic 2106 Uint32 state = *(Uint32 *)prop_data; 2107 2108 if (state == 0 || state == 3) { // Withdrawn or Iconic 2109 if (!(data->window->flags & SDL_WINDOW_MINIMIZED)) { 2110 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0); 2111 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_OCCLUDED, 0, 0); 2112 } 2113 } else if (state == 1) { // NormalState 2114 if (data->window->flags & SDL_WINDOW_MINIMIZED) { 2115 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0); 2116 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_EXPOSED, 0, 0); 2117 } 2118 } 2119 } 2120 X11_XFree(prop_data); 2121 } 2122 } else if (xevent->xproperty.atom == videodata->atoms.XKLAVIER_STATE) { 2123 /* Hack for Ubuntu 12.04 (etc) that doesn't send MappingNotify 2124 events when the keyboard layout changes (for example, 2125 changing from English to French on the menubar's keyboard 2126 icon). Since it changes the XKLAVIER_STATE property, we 2127 notice and reinit our keymap here. This might not be the 2128 right approach, but it seems to work. */ 2129 X11_UpdateKeymap(_this, true); 2130 } else if (xevent->xproperty.atom == videodata->atoms._NET_FRAME_EXTENTS) { 2131 X11_GetBorderValues(data); 2132 if (data->size_move_event_flags) { 2133 /* Events are disabled when leaving fullscreen until the borders appear to avoid 2134 * incorrect size/position events on compositing window managers. 2135 */ 2136 data->size_move_event_flags &= ~X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS; 2137 } 2138 if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) { 2139 data->toggle_borders = false; 2140 X11_SetWindowBordered(_this, data->window, !(data->window->flags & SDL_WINDOW_BORDERLESS)); 2141 } 2142 } 2143 } break; 2144 2145 case SelectionNotify: 2146 { 2147 Atom target = xevent->xselection.target; 2148#ifdef DEBUG_XEVENTS 2149 SDL_Log("window 0x%lx: SelectionNotify (requestor = 0x%lx, target = 0x%lx)", xevent->xany.window, 2150 xevent->xselection.requestor, xevent->xselection.target); 2151#endif 2152 if (target == data->xdnd_req) { 2153 // read data 2154 SDL_x11Prop p; 2155 X11_ReadProperty(&p, display, data->xwindow, videodata->atoms.PRIMARY); 2156 2157 if (p.format == 8) { 2158 char *saveptr = NULL; 2159 char *name = X11_XGetAtomName(display, target); 2160 if (name) { 2161 char *token = SDL_strtok_r((char *)p.data, "\r\n", &saveptr); 2162 while (token) { 2163 if ((SDL_strcmp("text/plain;charset=utf-8", name) == 0) || 2164 (SDL_strcmp("UTF8_STRING", name) == 0) || 2165 (SDL_strcmp("text/plain", name) == 0) || 2166 (SDL_strcmp("TEXT", name) == 0)) { 2167 SDL_SendDropText(data->window, token); 2168 } else if (SDL_strcmp("text/uri-list", name) == 0) { 2169 if (SDL_URIToLocal(token, token) >= 0) { 2170 SDL_SendDropFile(data->window, NULL, token); 2171 } 2172 } 2173 token = SDL_strtok_r(NULL, "\r\n", &saveptr); 2174 } 2175 X11_XFree(name); 2176 } 2177 SDL_SendDropComplete(data->window); 2178 } 2179 X11_XFree(p.data); 2180 2181 // send reply 2182 SDL_zero(m); 2183 m.type = ClientMessage; 2184 m.display = display; 2185 m.window = data->xdnd_source; 2186 m.message_type = videodata->atoms.XdndFinished; 2187 m.format = 32; 2188 m.data.l[0] = data->xwindow; 2189 m.data.l[1] = 1; 2190 m.data.l[2] = videodata->atoms.XdndActionCopy; 2191 X11_XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent *)&m); 2192 2193 X11_XSync(display, False); 2194 } 2195 } break; 2196 2197 default: 2198 { 2199#ifdef DEBUG_XEVENTS 2200 SDL_Log("window 0x%lx: Unhandled event %d", xevent->xany.window, xevent->type); 2201#endif 2202 } break; 2203 } 2204} 2205 2206static void X11_HandleFocusChanges(SDL_VideoDevice *_this) 2207{ 2208 SDL_VideoData *videodata = _this->internal; 2209 int i; 2210 2211 if (videodata && videodata->windowlist) { 2212 for (i = 0; i < videodata->numwindows; ++i) { 2213 SDL_WindowData *data = videodata->windowlist[i]; 2214 if (data && data->pending_focus != PENDING_FOCUS_NONE) { 2215 Uint64 now = SDL_GetTicks(); 2216 if (now >= data->pending_focus_time) { 2217 if (data->pending_focus == PENDING_FOCUS_IN) { 2218 X11_DispatchFocusIn(_this, data); 2219 } else { 2220 X11_DispatchFocusOut(_this, data); 2221 } 2222 data->pending_focus = PENDING_FOCUS_NONE; 2223 } 2224 } 2225 } 2226 } 2227} 2228 2229static Bool isAnyEvent(Display *display, XEvent *ev, XPointer arg) 2230{ 2231 return True; 2232} 2233 2234static bool X11_PollEvent(Display *display, XEvent *event) 2235{ 2236 if (!X11_XCheckIfEvent(display, event, isAnyEvent, NULL)) { 2237 return false; 2238 } 2239 2240 return true; 2241} 2242 2243void X11_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window) 2244{ 2245 SDL_VideoData *data = _this->internal; 2246 Display *req_display = data->request_display; 2247 Window xwindow = window->internal->xwindow; 2248 XClientMessageEvent event; 2249 2250 SDL_zero(event); 2251 event.type = ClientMessage; 2252 event.display = req_display; 2253 event.send_event = True; 2254 event.message_type = data->atoms._SDL_WAKEUP; 2255 event.format = 8; 2256 2257 X11_XSendEvent(req_display, xwindow, False, NoEventMask, (XEvent *)&event); 2258 /* XSendEvent returns a status and it could be BadValue or BadWindow. If an 2259 error happens it is an SDL's internal error and there is nothing we can do here. */ 2260 X11_XFlush(req_display); 2261} 2262 2263int X11_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS) 2264{ 2265 SDL_VideoData *videodata = _this->internal; 2266 Display *display; 2267 XEvent xevent; 2268 display = videodata->display; 2269 2270 SDL_zero(xevent); 2271 2272 // Flush and poll to grab any events already read and queued 2273 X11_XFlush(display); 2274 if (X11_PollEvent(display, &xevent)) { 2275 // Fall through 2276 } else if (timeoutNS == 0) { 2277 return 0; 2278 } else { 2279 // Use SDL_IOR_NO_RETRY to ensure SIGINT will break us out of our wait 2280 int err = SDL_IOReady(ConnectionNumber(display), SDL_IOR_READ | SDL_IOR_NO_RETRY, timeoutNS); 2281 if (err > 0) { 2282 if (!X11_PollEvent(display, &xevent)) { 2283 /* Someone may have beat us to reading the fd. Return 1 here to 2284 * trigger the normal spurious wakeup logic in the event core. */ 2285 return 1; 2286 } 2287 } else if (err == 0) { 2288 // Timeout 2289 return 0; 2290 } else { 2291 // Error returned from poll()/select() 2292 2293 if (errno == EINTR) { 2294 /* If the wait was interrupted by a signal, we may have generated a 2295 * SDL_EVENT_QUIT event. Let the caller know to call SDL_PumpEvents(). */ 2296 return 1; 2297 } else { 2298 return err; 2299 } 2300 } 2301 } 2302 2303 X11_DispatchEvent(_this, &xevent); 2304 return 1; 2305} 2306 2307void X11_PumpEvents(SDL_VideoDevice *_this) 2308{ 2309 SDL_VideoData *data = _this->internal; 2310 XEvent xevent; 2311 int i; 2312 2313 /* Check if a display had the mode changed and is waiting for a window to asynchronously become 2314 * fullscreen. If there is no fullscreen window past the elapsed timeout, revert the mode switch. 2315 */ 2316 for (i = 0; i < _this->num_displays; ++i) { 2317 if (_this->displays[i]->internal->mode_switch_deadline_ns) { 2318 if (_this->displays[i]->fullscreen_window) { 2319 _this->displays[i]->internal->mode_switch_deadline_ns = 0; 2320 } else if (SDL_GetTicksNS() >= _this->displays[i]->internal->mode_switch_deadline_ns) { 2321 SDL_LogError(SDL_LOG_CATEGORY_VIDEO, 2322 "Time out elapsed after mode switch on display %" SDL_PRIu32 " with no window becoming fullscreen; reverting", _this->displays[i]->id); 2323 SDL_SetDisplayModeForDisplay(_this->displays[i], NULL); 2324 _this->displays[i]->internal->mode_switch_deadline_ns = 0; 2325 } 2326 } 2327 } 2328 2329 if (data->last_mode_change_deadline) { 2330 if (SDL_GetTicks() >= data->last_mode_change_deadline) { 2331 data->last_mode_change_deadline = 0; // assume we're done. 2332 } 2333 } 2334 2335 // Update activity every 30 seconds to prevent screensaver 2336 if (_this->suspend_screensaver) { 2337 Uint64 now = SDL_GetTicks(); 2338 if (!data->screensaver_activity || now >= (data->screensaver_activity + 30000)) { 2339 X11_XResetScreenSaver(data->display); 2340 2341#ifdef SDL_USE_LIBDBUS 2342 SDL_DBus_ScreensaverTickle(); 2343#endif 2344 2345 data->screensaver_activity = now; 2346 } 2347 } 2348 2349 SDL_zero(xevent); 2350 2351 // Keep processing pending events 2352 while (X11_PollEvent(data->display, &xevent)) { 2353 X11_DispatchEvent(_this, &xevent); 2354 } 2355 2356 // FIXME: Only need to do this when there are pending focus changes 2357 X11_HandleFocusChanges(_this); 2358 2359 // FIXME: Only need to do this when there are flashing windows 2360 for (i = 0; i < data->numwindows; ++i) { 2361 if (data->windowlist[i] != NULL && 2362 data->windowlist[i]->flash_cancel_time && 2363 SDL_GetTicks() >= data->windowlist[i]->flash_cancel_time) { 2364 X11_FlashWindow(_this, data->windowlist[i]->window, SDL_FLASH_CANCEL); 2365 } 2366 } 2367 2368 if (data->xinput_hierarchy_changed) { 2369 X11_Xinput2UpdateDevices(_this); 2370 data->xinput_hierarchy_changed = false; 2371 } 2372} 2373 2374bool X11_SuspendScreenSaver(SDL_VideoDevice *_this) 2375{ 2376#ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER 2377 SDL_VideoData *data = _this->internal; 2378 int dummy; 2379 int major_version, minor_version; 2380#endif // SDL_VIDEO_DRIVER_X11_XSCRNSAVER 2381 2382#ifdef SDL_USE_LIBDBUS 2383 if (SDL_DBus_ScreensaverInhibit(_this->suspend_screensaver)) { 2384 return true; 2385 } 2386 2387 if (_this->suspend_screensaver) { 2388 SDL_DBus_ScreensaverTickle(); 2389 } 2390#endif 2391 2392#ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER 2393 if (SDL_X11_HAVE_XSS) { 2394 // X11_XScreenSaverSuspend was introduced in MIT-SCREEN-SAVER 1.1 2395 if (!X11_XScreenSaverQueryExtension(data->display, &dummy, &dummy) || 2396 !X11_XScreenSaverQueryVersion(data->display, 2397 &major_version, &minor_version) || 2398 major_version < 1 || (major_version == 1 && minor_version < 1)) { 2399 return SDL_Unsupported(); 2400 } 2401 2402 X11_XScreenSaverSuspend(data->display, _this->suspend_screensaver); 2403 X11_XResetScreenSaver(data->display); 2404 return true; 2405 } 2406#endif 2407 return SDL_Unsupported(); 2408} 2409 2410#endif // SDL_VIDEO_DRIVER_X11 2411[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.