Atlas - SDL_x11events.c

Home / ext / SDL / src / video / x11 Lines: 4 | Size: 93514 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 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 for (Uint32 keycode = 0; keycode < SDL_arraysize(videodata->keyboard.key_layout); ++keycode) { 551 const SDL_Scancode scancode = videodata->keyboard.key_layout[keycode]; 552 const bool x11KeyPressed = (keys[keycode / 8] & (1 << (keycode % 8))) != 0; 553 554 if (x11KeyPressed) { 555 // Only update modifier state for keys that are pressed in another application 556 switch (SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false)) { 557 case SDLK_LCTRL: 558 case SDLK_RCTRL: 559 case SDLK_LSHIFT: 560 case SDLK_RSHIFT: 561 case SDLK_LALT: 562 case SDLK_RALT: 563 case SDLK_LGUI: 564 case SDLK_RGUI: 565 case SDLK_MODE: 566 case SDLK_LEVEL5_SHIFT: 567 X11_HandleModifierKeys(videodata, scancode, true); 568 SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, true); 569 break; 570 default: 571 break; 572 } 573 } 574 } 575 576 // Update the latched/locked state for modifiers other than Caps, Num, and Scroll lock. 577 X11_UpdateSystemKeyModifiers(videodata); 578 X11_ReconcileModifiers(videodata, false); 579} 580 581static void X11_DispatchFocusIn(SDL_VideoDevice *_this, SDL_WindowData *data) 582{ 583#ifdef DEBUG_XEVENTS 584 SDL_Log("window 0x%lx: Dispatching FocusIn", data->xwindow); 585#endif 586 SDL_SetKeyboardFocus(data->window); 587 X11_ReconcileKeyboardState(_this); 588#ifdef X_HAVE_UTF8_STRING 589 if (data->ic) { 590 X11_XSetICFocus(data->ic); 591 } 592#endif 593 if (data->flashing_window) { 594 X11_FlashWindow(_this, data->window, SDL_FLASH_CANCEL); 595 } 596} 597 598static void X11_DispatchFocusOut(SDL_VideoDevice *_this, SDL_WindowData *data) 599{ 600#ifdef DEBUG_XEVENTS 601 SDL_Log("window 0x%lx: Dispatching FocusOut", data->xwindow); 602#endif 603 /* If another window has already processed a focus in, then don't try to 604 * remove focus here. Doing so will incorrectly remove focus from that 605 * window, and the focus lost event for this window will have already 606 * been dispatched anyway. */ 607 if (data->window == SDL_GetKeyboardFocus()) { 608 SDL_SetKeyboardFocus(NULL); 609 } 610#ifdef X_HAVE_UTF8_STRING 611 if (data->ic) { 612 X11_XUnsetICFocus(data->ic); 613 } 614#endif 615} 616 617static void X11_DispatchMapNotify(SDL_WindowData *data) 618{ 619 SDL_Window *window = data->window; 620 621 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0); 622 data->was_shown = true; 623 624 // This may be sent when restoring a minimized window. 625 if (window->flags & SDL_WINDOW_MINIMIZED) { 626 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0); 627 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_EXPOSED, 0, 0); 628 } 629 630 if (window->flags & SDL_WINDOW_INPUT_FOCUS) { 631 SDL_UpdateWindowGrab(window); 632 } 633} 634 635static void X11_DispatchUnmapNotify(SDL_WindowData *data) 636{ 637 SDL_Window *window = data->window; 638 639 // This may be sent when minimizing a window. 640 if (!window->is_hiding) { 641 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0); 642 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_OCCLUDED, 0, 0); 643 } else { 644 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_HIDDEN, 0, 0); 645 } 646} 647 648static void DispatchWindowMove(SDL_VideoDevice *_this, const SDL_WindowData *data, const SDL_Point *point) 649{ 650 SDL_VideoData *videodata = _this->internal; 651 SDL_Window *window = data->window; 652 Display *display = videodata->display; 653 XEvent evt; 654 655 // !!! FIXME: we need to regrab this if necessary when the drag is done. 656 X11_XUngrabPointer(display, 0L); 657 X11_XFlush(display); 658 659 evt.xclient.type = ClientMessage; 660 evt.xclient.window = data->xwindow; 661 evt.xclient.message_type = videodata->atoms._NET_WM_MOVERESIZE; 662 evt.xclient.format = 32; 663 evt.xclient.data.l[0] = (size_t)window->x + point->x; 664 evt.xclient.data.l[1] = (size_t)window->y + point->y; 665 evt.xclient.data.l[2] = _NET_WM_MOVERESIZE_MOVE; 666 evt.xclient.data.l[3] = Button1; 667 evt.xclient.data.l[4] = 0; 668 X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt); 669 670 X11_XSync(display, 0); 671} 672 673static void ScheduleWindowMove(SDL_VideoDevice *_this, SDL_WindowData *data, const SDL_Point *point) 674{ 675 data->pending_move = true; 676 data->pending_move_point = *point; 677} 678 679static void InitiateWindowResize(SDL_VideoDevice *_this, const SDL_WindowData *data, const SDL_Point *point, int direction) 680{ 681 SDL_VideoData *videodata = _this->internal; 682 SDL_Window *window = data->window; 683 Display *display = videodata->display; 684 XEvent evt; 685 686 if (direction < _NET_WM_MOVERESIZE_SIZE_TOPLEFT || direction > _NET_WM_MOVERESIZE_SIZE_LEFT) { 687 return; 688 } 689 690 // !!! FIXME: we need to regrab this if necessary when the drag is done. 691 X11_XUngrabPointer(display, 0L); 692 X11_XFlush(display); 693 694 evt.xclient.type = ClientMessage; 695 evt.xclient.window = data->xwindow; 696 evt.xclient.message_type = videodata->atoms._NET_WM_MOVERESIZE; 697 evt.xclient.format = 32; 698 evt.xclient.data.l[0] = (size_t)window->x + point->x; 699 evt.xclient.data.l[1] = (size_t)window->y + point->y; 700 evt.xclient.data.l[2] = direction; 701 evt.xclient.data.l[3] = Button1; 702 evt.xclient.data.l[4] = 0; 703 X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt); 704 705 X11_XSync(display, 0); 706} 707 708bool X11_ProcessHitTest(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y, bool force_new_result) 709{ 710 SDL_Window *window = data->window; 711 if (!window->hit_test) return false; 712 const SDL_Point point = { (int)x, (int)y }; 713 SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data); 714 if (!force_new_result && rc == data->hit_test_result) { 715 return true; 716 } 717 X11_SetHitTestCursor(rc); 718 data->hit_test_result = rc; 719 return true; 720} 721 722bool X11_TriggerHitTestAction(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y) 723{ 724 SDL_Window *window = data->window; 725 726 if (window->hit_test) { 727 const SDL_Point point = { (int)x, (int)y }; 728 static const int directions[] = { 729 _NET_WM_MOVERESIZE_SIZE_TOPLEFT, _NET_WM_MOVERESIZE_SIZE_TOP, 730 _NET_WM_MOVERESIZE_SIZE_TOPRIGHT, _NET_WM_MOVERESIZE_SIZE_RIGHT, 731 _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT, _NET_WM_MOVERESIZE_SIZE_BOTTOM, 732 _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT, _NET_WM_MOVERESIZE_SIZE_LEFT 733 }; 734 735 switch (data->hit_test_result) { 736 case SDL_HITTEST_DRAGGABLE: 737 /* Some window managers get in a bad state when a move event starts while input is transitioning 738 to the SDL window. This can happen when clicking on a drag region of an unfocused window 739 where the same mouse down event will trigger a drag event and a window activate. */ 740 if (data->window->flags & SDL_WINDOW_INPUT_FOCUS) { 741 DispatchWindowMove(_this, data, &point); 742 } else { 743 ScheduleWindowMove(_this, data, &point); 744 } 745 return true; 746 747 case SDL_HITTEST_RESIZE_TOPLEFT: 748 case SDL_HITTEST_RESIZE_TOP: 749 case SDL_HITTEST_RESIZE_TOPRIGHT: 750 case SDL_HITTEST_RESIZE_RIGHT: 751 case SDL_HITTEST_RESIZE_BOTTOMRIGHT: 752 case SDL_HITTEST_RESIZE_BOTTOM: 753 case SDL_HITTEST_RESIZE_BOTTOMLEFT: 754 case SDL_HITTEST_RESIZE_LEFT: 755 InitiateWindowResize(_this, data, &point, directions[data->hit_test_result - SDL_HITTEST_RESIZE_TOPLEFT]); 756 return true; 757 758 default: 759 return false; 760 } 761 } 762 763 return false; 764} 765 766static void X11_UpdateUserTime(SDL_WindowData *data, const unsigned long latest) 767{ 768 if (latest && (latest != data->user_time)) { 769 SDL_VideoData *videodata = data->videodata; 770 Display *display = videodata->display; 771 X11_XChangeProperty(display, data->xwindow, videodata->atoms._NET_WM_USER_TIME, 772 XA_CARDINAL, 32, PropModeReplace, 773 (const unsigned char *)&latest, 1); 774#ifdef DEBUG_XEVENTS 775 SDL_Log("window 0x%lx: updating _NET_WM_USER_TIME to %lu", data->xwindow, latest); 776#endif 777 data->user_time = latest; 778 } 779} 780 781static void X11_HandleClipboardEvent(SDL_VideoDevice *_this, const XEvent *xevent) 782{ 783 int i; 784 SDL_VideoData *videodata = _this->internal; 785 Display *display = videodata->display; 786 787 SDL_assert(videodata->clipboard_window != None); 788 SDL_assert(xevent->xany.window == videodata->clipboard_window); 789 790 switch (xevent->type) { 791 // Copy the selection from our own CUTBUFFER to the requested property 792 case SelectionRequest: 793 { 794 const XSelectionRequestEvent *req = &xevent->xselectionrequest; 795 XEvent sevent; 796 int mime_formats; 797 unsigned char *seln_data; 798 size_t seln_length = 0; 799 Atom XA_TARGETS = videodata->atoms.TARGETS; 800 SDLX11_ClipboardData *clipboard; 801 802#ifdef DEBUG_XEVENTS 803 char *atom_name; 804 atom_name = X11_XGetAtomName(display, req->target); 805 SDL_Log("window CLIPBOARD: SelectionRequest (requestor = 0x%lx, target = 0x%lx, mime_type = %s)", 806 req->requestor, req->target, atom_name); 807 if (atom_name) { 808 X11_XFree(atom_name); 809 } 810#endif 811 812 if (req->selection == XA_PRIMARY) { 813 clipboard = &videodata->primary_selection; 814 } else { 815 clipboard = &videodata->clipboard; 816 } 817 818 SDL_zero(sevent); 819 sevent.xany.type = SelectionNotify; 820 sevent.xselection.selection = req->selection; 821 sevent.xselection.target = None; 822 sevent.xselection.property = None; // tell them no by default 823 sevent.xselection.requestor = req->requestor; 824 sevent.xselection.time = req->time; 825 826 /* !!! FIXME: We were probably storing this on the root window 827 because an SDL window might go away...? but we don't have to do 828 this now (or ever, really). */ 829 830 if (req->target == XA_TARGETS) { 831 Atom *supportedFormats; 832 supportedFormats = SDL_malloc((clipboard->mime_count + 1) * sizeof(Atom)); 833 supportedFormats[0] = XA_TARGETS; 834 mime_formats = 1; 835 for (i = 0; i < clipboard->mime_count; ++i) { 836 supportedFormats[mime_formats++] = X11_XInternAtom(display, clipboard->mime_types[i], False); 837 } 838 X11_XChangeProperty(display, req->requestor, req->property, 839 XA_ATOM, 32, PropModeReplace, 840 (unsigned char *)supportedFormats, 841 mime_formats); 842 sevent.xselection.property = req->property; 843 sevent.xselection.target = XA_TARGETS; 844 SDL_free(supportedFormats); 845 } else { 846 if (clipboard->callback) { 847 for (i = 0; i < clipboard->mime_count; ++i) { 848 const char *mime_type = clipboard->mime_types[i]; 849 if (X11_XInternAtom(display, mime_type, False) != req->target) { 850 continue; 851 } 852 853 // FIXME: We don't support the X11 INCR protocol for large clipboards. Do we want that? - Yes, yes we do. 854 // This is a safe cast, XChangeProperty() doesn't take a const value, but it doesn't modify the data 855 seln_data = (unsigned char *)clipboard->callback(clipboard->userdata, mime_type, &seln_length); 856 if (seln_data) { 857 X11_XChangeProperty(display, req->requestor, req->property, 858 req->target, 8, PropModeReplace, 859 seln_data, seln_length); 860 sevent.xselection.property = req->property; 861 sevent.xselection.target = req->target; 862 } 863 break; 864 } 865 } 866 } 867 X11_XSendEvent(display, req->requestor, False, 0, &sevent); 868 X11_XSync(display, False); 869 } break; 870 871 case SelectionNotify: 872 { 873 const XSelectionEvent *xsel = &xevent->xselection; 874#ifdef DEBUG_XEVENTS 875 const char *propName = xsel->property ? X11_XGetAtomName(display, xsel->property) : "None"; 876 const char *targetName = xsel->target ? X11_XGetAtomName(display, xsel->target) : "None"; 877 878 SDL_Log("window CLIPBOARD: SelectionNotify (requestor = 0x%lx, target = %s, property = %s)", 879 xsel->requestor, targetName, propName); 880#endif 881 if (xsel->target == videodata->atoms.TARGETS && xsel->property == videodata->atoms.SDL_FORMATS) { 882 /* the new mime formats are the SDL_FORMATS property as an array of Atoms */ 883 Atom atom = None; 884 Atom *patom; 885 unsigned char *data = NULL; 886 int format_property = 0; 887 unsigned long length = 0; 888 unsigned long bytes_left = 0; 889 int j; 890 891 X11_XGetWindowProperty(display, GetWindow(_this), videodata->atoms.SDL_FORMATS, 0, 200, 892 0, XA_ATOM, &atom, &format_property, &length, &bytes_left, &data); 893 894 int allocationsize = (length + 1) * sizeof(char *); 895 for (j = 0, patom = (Atom *)data; j < length; j++, patom++) { 896 char *atomStr = X11_XGetAtomName(display, *patom); 897 allocationsize += SDL_strlen(atomStr) + 1; 898 X11_XFree(atomStr); 899 } 900 901 char **new_mime_types = SDL_AllocateTemporaryMemory(allocationsize); 902 if (new_mime_types) { 903 char *strPtr = (char *)(new_mime_types + length + 1); 904 905 for (j = 0, patom = (Atom *)data; j < length; j++, patom++) { 906 char *atomStr = X11_XGetAtomName(display, *patom); 907 new_mime_types[j] = strPtr; 908 strPtr = stpcpy(strPtr, atomStr) + 1; 909 X11_XFree(atomStr); 910 } 911 new_mime_types[length] = NULL; 912 913 SDL_SendClipboardUpdate(false, new_mime_types, length); 914 } 915 916 if (data) { 917 X11_XFree(data); 918 } 919 } 920 921 videodata->selection_waiting = false; 922 } break; 923 924 case SelectionClear: 925 { 926 Atom XA_CLIPBOARD = videodata->atoms.CLIPBOARD; 927 SDLX11_ClipboardData *clipboard = NULL; 928 929#ifdef DEBUG_XEVENTS 930 SDL_Log("window CLIPBOARD: SelectionClear (requestor = 0x%lx, target = 0x%lx)", 931 xevent->xselection.requestor, xevent->xselection.target); 932#endif 933 934 if (xevent->xselectionclear.selection == XA_PRIMARY) { 935 clipboard = &videodata->primary_selection; 936 } else if (XA_CLIPBOARD != None && xevent->xselectionclear.selection == XA_CLIPBOARD) { 937 clipboard = &videodata->clipboard; 938 } 939 if (clipboard && clipboard->callback) { 940 if (clipboard->sequence) { 941 SDL_CancelClipboardData(clipboard->sequence); 942 } else { 943 SDL_free(clipboard->userdata); 944 } 945 SDL_zerop(clipboard); 946 } 947 } break; 948 949 case PropertyNotify: 950 { 951 char *name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom); 952 953 if (SDL_strncmp(name_of_atom, "SDL_SELECTION", sizeof("SDL_SELECTION") - 1) == 0 && xevent->xproperty.state == PropertyNewValue) { 954 videodata->selection_incr_waiting = false; 955 } 956 957 if (name_of_atom) { 958 X11_XFree(name_of_atom); 959 } 960 } break; 961 } 962} 963 964static Bool isMapNotify(Display *display, XEvent *ev, XPointer arg) 965{ 966 XUnmapEvent *unmap; 967 968 unmap = (XUnmapEvent *)arg; 969 970 return ev->type == MapNotify && 971 ev->xmap.window == unmap->window && 972 ev->xmap.serial == unmap->serial; 973} 974 975static Bool isReparentNotify(Display *display, XEvent *ev, XPointer arg) 976{ 977 XUnmapEvent *unmap; 978 979 unmap = (XUnmapEvent *)arg; 980 981 return ev->type == ReparentNotify && 982 ev->xreparent.window == unmap->window && 983 ev->xreparent.serial == unmap->serial; 984} 985 986static bool IsHighLatin1(const char *string, int length) 987{ 988 while (length-- > 0) { 989 Uint8 ch = (Uint8)*string; 990 if (ch >= 0x80) { 991 return true; 992 } 993 ++string; 994 } 995 return false; 996} 997 998static int XLookupStringAsUTF8(XKeyEvent *event_struct, char *buffer_return, int bytes_buffer, KeySym *keysym_return, XComposeStatus *status_in_out) 999{ 1000 int result = X11_XLookupString(event_struct, buffer_return, bytes_buffer, keysym_return, status_in_out); 1001 if (IsHighLatin1(buffer_return, result)) { 1002 char *utf8_text = SDL_iconv_string("UTF-8", "ISO-8859-1", buffer_return, result + 1); 1003 if (utf8_text) { 1004 SDL_strlcpy(buffer_return, utf8_text, bytes_buffer); 1005 SDL_free(utf8_text); 1006 return SDL_strlen(buffer_return); 1007 } else { 1008 return 0; 1009 } 1010 } 1011 return result; 1012} 1013 1014SDL_WindowData *X11_FindWindow(SDL_VideoData *videodata, Window window) 1015{ 1016 if (videodata && videodata->windowlist) { 1017 for (int i = 0; i < videodata->numwindows; ++i) { 1018 if ((videodata->windowlist[i] != NULL) && 1019 (videodata->windowlist[i]->xwindow == window)) { 1020 return videodata->windowlist[i]; 1021 } 1022 } 1023 } 1024 return NULL; 1025} 1026 1027Uint64 X11_GetEventTimestamp(unsigned long time) 1028{ 1029 // FIXME: Get the event time in the SDL tick time base 1030 return SDL_GetTicksNS(); 1031} 1032 1033void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_KeyboardID keyboardID, XEvent *xevent) 1034{ 1035 SDL_VideoData *videodata = _this->internal; 1036 Display *display = videodata->display; 1037 KeyCode keycode = xevent->xkey.keycode; 1038 KeySym keysym = NoSymbol; 1039 int text_length = 0; 1040 char text[64]; 1041 Status status = 0; 1042 bool handled_by_ime = false; 1043 bool pressed = (xevent->type == KeyPress); 1044 SDL_Scancode scancode = videodata->keyboard.key_layout[keycode]; 1045 Uint64 timestamp = X11_GetEventTimestamp(xevent->xkey.time); 1046 1047#ifdef DEBUG_XEVENTS 1048 SDL_Log("window 0x%lx %s (X11 keycode = 0x%X)", xevent->xany.window, (xevent->type == KeyPress ? "KeyPress" : "KeyRelease"), xevent->xkey.keycode); 1049#endif 1050#ifdef DEBUG_SCANCODES 1051 if (scancode == SDL_SCANCODE_UNKNOWN && keycode) { 1052 int min_keycode, max_keycode; 1053 X11_XDisplayKeycodes(display, &min_keycode, &max_keycode); 1054 keysym = X11_KeyCodeToSym(_this, keycode, xevent->xkey.state >> 13); 1055 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).", 1056 keycode, keycode - min_keycode, keysym, 1057 X11_XKeysymToString(keysym)); 1058 } 1059#endif // DEBUG SCANCODES 1060 1061 text[0] = '\0'; 1062 1063 // XKB updates the modifiers explicitly via a state event. 1064 if (!videodata->keyboard.xkb_enabled) { 1065 videodata->keyboard.pressed_modifiers = xevent->xkey.state & (ShiftMask | ControlMask | Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask); 1066 videodata->keyboard.locked_modifiers = xevent->xkey.state & (LockMask | videodata->keyboard.numlock_mask | videodata->keyboard.scrolllock_mask); 1067 } 1068 1069 if (SDL_TextInputActive(windowdata->window)) { 1070 // filter events catches XIM events and sends them to the correct handler 1071 if (X11_XFilterEvent(xevent, None)) { 1072#ifdef DEBUG_XEVENTS 1073 SDL_Log("Filtered event type = %d display = %p window = 0x%lx", 1074 xevent->type, xevent->xany.display, xevent->xany.window); 1075#endif 1076 handled_by_ime = true; 1077 } 1078 1079 if (!handled_by_ime) { 1080#ifdef X_HAVE_UTF8_STRING 1081 if (windowdata->ic && xevent->type == KeyPress) { 1082 text_length = X11_Xutf8LookupString(windowdata->ic, &xevent->xkey, text, sizeof(text) - 1, 1083 &keysym, &status); 1084 } else { 1085 text_length = XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text) - 1, &keysym, NULL); 1086 } 1087#else 1088 text_length = XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text) - 1, &keysym, NULL); 1089#endif 1090 } 1091 } 1092 1093 if (!handled_by_ime) { 1094 if (pressed) { 1095 X11_HandleModifierKeys(videodata, scancode, true); 1096 SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, true); 1097 1098 if (*text && !(SDL_GetModState() & (SDL_KMOD_CTRL | SDL_KMOD_ALT))) { 1099 text[text_length] = '\0'; 1100 X11_ClearComposition(windowdata); 1101 SDL_SendKeyboardText(text); 1102 } 1103 } else { 1104 if (X11_KeyRepeat(display, xevent)) { 1105 // We're about to get a repeated key down, ignore the key up 1106 return; 1107 } 1108 1109 X11_HandleModifierKeys(videodata, scancode, false); 1110 SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, false); 1111 } 1112 } 1113 1114 if (pressed) { 1115 X11_UpdateUserTime(windowdata, xevent->xkey.time); 1116 } 1117} 1118 1119void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time) 1120{ 1121 SDL_Window *window = windowdata->window; 1122 int xticks = 0, yticks = 0; 1123 Uint64 timestamp = X11_GetEventTimestamp(time); 1124 1125#ifdef DEBUG_XEVENTS 1126 SDL_Log("window 0x%lx: ButtonPress (X11 button = %d)", windowdata->xwindow, button); 1127#endif 1128 1129 SDL_Mouse *mouse = SDL_GetMouse(); 1130 if (!mouse->relative_mode && (x != mouse->x || y != mouse->y)) { 1131 X11_ProcessHitTest(_this, windowdata, x, y, false); 1132 SDL_SendMouseMotion(timestamp, window, mouseID, false, x, y); 1133 } 1134 1135 if (X11_IsWheelEvent(button, &xticks, &yticks)) { 1136 SDL_SendMouseWheel(timestamp, window, mouseID, (float)-xticks, (float)yticks, SDL_MOUSEWHEEL_NORMAL); 1137 } else { 1138 bool ignore_click = false; 1139 if (button > 7) { 1140 /* X button values 4-7 are used for scrolling, so X1 is 8, X2 is 9, ... 1141 => subtract (8-SDL_BUTTON_X1) to get value SDL expects */ 1142 button -= (8 - SDL_BUTTON_X1); 1143 } 1144 if (button == Button1) { 1145 if (X11_TriggerHitTestAction(_this, windowdata, x, y)) { 1146 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_HIT_TEST, 0, 0); 1147 return; // don't pass this event on to app. 1148 } 1149 } 1150 if (windowdata->last_focus_event_time) { 1151 const int X11_FOCUS_CLICK_TIMEOUT = 10; 1152 if (SDL_GetTicks() < (windowdata->last_focus_event_time + X11_FOCUS_CLICK_TIMEOUT)) { 1153 ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, false); 1154 } 1155 windowdata->last_focus_event_time = 0; 1156 } 1157 if (!ignore_click) { 1158 SDL_SendMouseButton(timestamp, window, mouseID, button, true); 1159 } 1160 } 1161 X11_UpdateUserTime(windowdata, time); 1162} 1163 1164void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, unsigned long time) 1165{ 1166 SDL_Window *window = windowdata->window; 1167 // The X server sends a Release event for each Press for wheels. Ignore them. 1168 int xticks = 0, yticks = 0; 1169 Uint64 timestamp = X11_GetEventTimestamp(time); 1170 1171#ifdef DEBUG_XEVENTS 1172 SDL_Log("window 0x%lx: ButtonRelease (X11 button = %d)", windowdata->xwindow, button); 1173#endif 1174 if (!X11_IsWheelEvent(button, &xticks, &yticks)) { 1175 if (button > 7) { 1176 // see explanation at case ButtonPress 1177 button -= (8 - SDL_BUTTON_X1); 1178 } 1179 SDL_SendMouseButton(timestamp, window, mouseID, button, false); 1180 1181 if (window->internal->pending_grab) { 1182 X11_SetWindowMouseGrab(_this, window, true); 1183 } 1184 } 1185} 1186 1187void X11_GetBorderValues(SDL_WindowData *data) 1188{ 1189 SDL_VideoData *videodata = data->videodata; 1190 Display *display = videodata->display; 1191 1192 Atom type; 1193 int format; 1194 unsigned long nitems, bytes_after; 1195 unsigned char *property; 1196 1197 // Some compositors will send extents even when the border hint is turned off. Ignore them in this case. 1198 if (!(data->window->flags & SDL_WINDOW_BORDERLESS)) { 1199 if (X11_XGetWindowProperty(display, data->xwindow, videodata->atoms._NET_FRAME_EXTENTS, 0, 16, 0, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &property) == Success) { 1200 if (type != None && nitems == 4) { 1201 data->border_left = (int)((long *)property)[0]; 1202 data->border_right = (int)((long *)property)[1]; 1203 data->border_top = (int)((long *)property)[2]; 1204 data->border_bottom = (int)((long *)property)[3]; 1205 } 1206 X11_XFree(property); 1207 1208#ifdef DEBUG_XEVENTS 1209 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); 1210#endif 1211 } 1212 } else { 1213 data->border_left = data->border_top = data->border_right = data->border_bottom = 0; 1214 } 1215} 1216 1217void X11_EmitConfigureNotifyEvents(SDL_WindowData *data, XConfigureEvent *xevent) 1218{ 1219 if (xevent->x != data->last_xconfigure.x || 1220 xevent->y != data->last_xconfigure.y) { 1221 if (!data->size_move_event_flags) { 1222 SDL_Window *w; 1223 int x = xevent->x; 1224 int y = xevent->y; 1225 1226 data->pending_operation &= ~X11_PENDING_OP_MOVE; 1227 SDL_GlobalToRelativeForWindow(data->window, x, y, &x, &y); 1228 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED, x, y); 1229 1230 for (w = data->window->first_child; w; w = w->next_sibling) { 1231 // Don't update hidden child popup windows, their relative position doesn't change 1232 if (SDL_WINDOW_IS_POPUP(w) && !(w->flags & SDL_WINDOW_HIDDEN)) { 1233 X11_UpdateWindowPosition(w, true); 1234 } 1235 } 1236 } 1237 } 1238 1239 if (xevent->width != data->last_xconfigure.width || 1240 xevent->height != data->last_xconfigure.height) { 1241 if (!data->size_move_event_flags) { 1242 data->pending_operation &= ~X11_PENDING_OP_RESIZE; 1243 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESIZED, 1244 xevent->width, 1245 xevent->height); 1246 } 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 (!videodata->keyboard.xkb_enabled) { 1397 if (SDL_GetKeyboardFocus() != NULL) { 1398 X11_UpdateKeymap(_this, true); 1399 } 1400 } 1401 1402 X11_ReconcileKeyboardState(_this); 1403 } else if (xevent->type == MappingNotify) { 1404 const int request = xevent->xmapping.request; 1405 1406 if (request == MappingPointer) { 1407#ifdef DEBUG_XEVENTS 1408 SDL_Log("window 0x%lx: MappingNotify!", xevent->xany.window); 1409#endif 1410 X11_Xinput2UpdatePointerMapping(_this); 1411 } else if (!videodata->keyboard.xkb_enabled) { 1412 // Has the keyboard layout changed? 1413#ifdef DEBUG_XEVENTS 1414 SDL_Log("window 0x%lx: MappingNotify!", xevent->xany.window); 1415#endif 1416 if (request == MappingKeyboard || request == MappingModifier) { 1417 X11_XRefreshKeyboardMapping(&xevent->xmapping); 1418 } 1419 1420 X11_UpdateKeymap(_this, true); 1421 } 1422 } else if (xevent->type == PropertyNotify && videodata) { 1423 char *name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom); 1424 if (name_of_atom) { 1425 if (SDL_startswith(name_of_atom, "_ICC_PROFILE")) { 1426 XWindowAttributes attrib; 1427 int screennum; 1428 for (i = 0; i < videodata->numwindows; ++i) { 1429 if (videodata->windowlist[i] != NULL) { 1430 data = videodata->windowlist[i]; 1431 X11_XGetWindowAttributes(display, data->xwindow, &attrib); 1432 screennum = X11_XScreenNumberOfScreen(attrib.screen); 1433 if (screennum == 0 && SDL_strcmp(name_of_atom, "_ICC_PROFILE") == 0) { 1434 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0); 1435 } else if (SDL_strncmp(name_of_atom, "_ICC_PROFILE_", sizeof("_ICC_PROFILE_") - 1) == 0 && SDL_strlen(name_of_atom) > sizeof("_ICC_PROFILE_") - 1) { 1436 int iccscreennum = SDL_atoi(&name_of_atom[sizeof("_ICC_PROFILE_") - 1]); 1437 1438 if (screennum == iccscreennum) { 1439 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0); 1440 } 1441 } 1442 } 1443 } 1444 } else if (SDL_strcmp(name_of_atom, "_NET_WORKAREA") == 0) { 1445 for (i = 0; i < _this->num_displays; ++i) { 1446 SDL_SendDisplayEvent(_this->displays[i], SDL_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED, 0, 0); 1447 } 1448 } 1449 X11_XFree(name_of_atom); 1450 } 1451 } 1452 return; 1453 } 1454 1455 switch (xevent->type) { 1456 1457 // Gaining mouse coverage? 1458 case EnterNotify: 1459 { 1460 SDL_Mouse *mouse = SDL_GetMouse(); 1461#ifdef DEBUG_XEVENTS 1462 SDL_Log("window 0x%lx: EnterNotify! (%d,%d,%d)", xevent->xany.window, 1463 xevent->xcrossing.x, 1464 xevent->xcrossing.y, 1465 xevent->xcrossing.mode); 1466 if (xevent->xcrossing.mode == NotifyGrab) { 1467 SDL_Log("Mode: NotifyGrab"); 1468 } 1469 if (xevent->xcrossing.mode == NotifyUngrab) { 1470 SDL_Log("Mode: NotifyUngrab"); 1471 } 1472#endif 1473 SDL_SetMouseFocus(data->window); 1474 1475 mouse->last_x = xevent->xcrossing.x; 1476 mouse->last_y = xevent->xcrossing.y; 1477 1478#ifdef SDL_VIDEO_DRIVER_X11_XFIXES 1479 { 1480 // Only create the barriers if we have input focus 1481 SDL_WindowData *windowdata = data->window->internal; 1482 if ((data->pointer_barrier_active == true) && windowdata->window->flags & SDL_WINDOW_INPUT_FOCUS) { 1483 X11_ConfineCursorWithFlags(_this, windowdata->window, &windowdata->barrier_rect, X11_BARRIER_HANDLED_BY_EVENT); 1484 } 1485 } 1486#endif 1487 1488 if (!mouse->relative_mode) { 1489 SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xcrossing.x, (float)xevent->xcrossing.y); 1490 } 1491 1492 // We ungrab in LeaveNotify, so we may need to grab again here, but not if captured, as the capture can be lost. 1493 if (!(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) { 1494 SDL_UpdateWindowGrab(data->window); 1495 } 1496 1497 X11_ProcessHitTest(_this, data, mouse->last_x, mouse->last_y, true); 1498 } break; 1499 // Losing mouse coverage? 1500 case LeaveNotify: 1501 { 1502#ifdef DEBUG_XEVENTS 1503 SDL_Log("window 0x%lx: LeaveNotify! (%d,%d,%d)", xevent->xany.window, 1504 xevent->xcrossing.x, 1505 xevent->xcrossing.y, 1506 xevent->xcrossing.mode); 1507 if (xevent->xcrossing.mode == NotifyGrab) { 1508 SDL_Log("Mode: NotifyGrab"); 1509 } 1510 if (xevent->xcrossing.mode == NotifyUngrab) { 1511 SDL_Log("Mode: NotifyUngrab"); 1512 } 1513#endif 1514 if (!SDL_GetMouse()->relative_mode) { 1515 SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xcrossing.x, (float)xevent->xcrossing.y); 1516 } 1517 1518 if (xevent->xcrossing.mode != NotifyGrab && 1519 xevent->xcrossing.mode != NotifyUngrab && 1520 xevent->xcrossing.detail != NotifyInferior) { 1521 1522 /* In order for interaction with the window decorations and menu to work properly 1523 on Mutter, we need to ungrab the keyboard when the mouse leaves. */ 1524 if (!(data->window->flags & SDL_WINDOW_FULLSCREEN)) { 1525 X11_SetWindowKeyboardGrab(_this, data->window, false); 1526 } 1527 1528 SDL_SetMouseFocus(NULL); 1529 } 1530 } break; 1531 1532 // Gaining input focus? 1533 case FocusIn: 1534 { 1535 if (xevent->xfocus.mode == NotifyGrab || xevent->xfocus.mode == NotifyUngrab) { 1536 // Someone is handling a global hotkey, ignore it 1537#ifdef DEBUG_XEVENTS 1538 SDL_Log("window 0x%lx: FocusIn (NotifyGrab/NotifyUngrab, ignoring)", xevent->xany.window); 1539#endif 1540 break; 1541 } 1542 1543 if (xevent->xfocus.detail == NotifyInferior || xevent->xfocus.detail == NotifyPointer) { 1544#ifdef DEBUG_XEVENTS 1545 SDL_Log("window 0x%lx: FocusIn (NotifyInferior/NotifyPointer, ignoring)", xevent->xany.window); 1546#endif 1547 break; 1548 } 1549#ifdef DEBUG_XEVENTS 1550 SDL_Log("window 0x%lx: FocusIn!", xevent->xany.window); 1551#endif 1552 if (!videodata->last_mode_change_deadline) /* no recent mode changes */ { 1553 data->pending_focus = PENDING_FOCUS_NONE; 1554 data->pending_focus_time = 0; 1555 X11_DispatchFocusIn(_this, data); 1556 } else { 1557 data->pending_focus = PENDING_FOCUS_IN; 1558 data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME; 1559 } 1560 data->last_focus_event_time = SDL_GetTicks(); 1561 } break; 1562 1563 // Losing input focus? 1564 case FocusOut: 1565 { 1566 if (xevent->xfocus.mode == NotifyGrab || xevent->xfocus.mode == NotifyUngrab) { 1567 // Someone is handling a global hotkey, ignore it 1568#ifdef DEBUG_XEVENTS 1569 SDL_Log("window 0x%lx: FocusOut (NotifyGrab/NotifyUngrab, ignoring)", xevent->xany.window); 1570#endif 1571 break; 1572 } 1573 if (xevent->xfocus.detail == NotifyInferior || xevent->xfocus.detail == NotifyPointer) { 1574 /* We still have focus if a child gets focus. We also don't 1575 care about the position of the pointer when the keyboard 1576 focus changed. */ 1577#ifdef DEBUG_XEVENTS 1578 SDL_Log("window 0x%lx: FocusOut (NotifyInferior/NotifyPointer, ignoring)", xevent->xany.window); 1579#endif 1580 break; 1581 } 1582#ifdef DEBUG_XEVENTS 1583 SDL_Log("window 0x%lx: FocusOut!", xevent->xany.window); 1584#endif 1585 if (!videodata->last_mode_change_deadline) /* no recent mode changes */ { 1586 data->pending_focus = PENDING_FOCUS_NONE; 1587 data->pending_focus_time = 0; 1588 X11_DispatchFocusOut(_this, data); 1589 } else { 1590 data->pending_focus = PENDING_FOCUS_OUT; 1591 data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME; 1592 } 1593 1594#ifdef SDL_VIDEO_DRIVER_X11_XFIXES 1595 // Disable confinement if it is activated. 1596 if (data->pointer_barrier_active == true) { 1597 X11_ConfineCursorWithFlags(_this, data->window, NULL, X11_BARRIER_HANDLED_BY_EVENT); 1598 } 1599#endif // SDL_VIDEO_DRIVER_X11_XFIXES 1600 } break; 1601 1602 1603 // Have we been iconified? 1604 case UnmapNotify: 1605 { 1606 XEvent ev; 1607 1608#ifdef DEBUG_XEVENTS 1609 SDL_Log("window 0x%lx: UnmapNotify!", xevent->xany.window); 1610#endif 1611 1612 if (X11_XCheckIfEvent(display, &ev, &isReparentNotify, (XPointer)&xevent->xunmap)) { 1613 X11_XCheckIfEvent(display, &ev, &isMapNotify, (XPointer)&xevent->xunmap); 1614 } else { 1615 X11_DispatchUnmapNotify(data); 1616 } 1617 1618#ifdef SDL_VIDEO_DRIVER_X11_XFIXES 1619 // Disable confinement if the window gets hidden. 1620 if (data->pointer_barrier_active == true) { 1621 X11_ConfineCursorWithFlags(_this, data->window, NULL, X11_BARRIER_HANDLED_BY_EVENT); 1622 } 1623#endif // SDL_VIDEO_DRIVER_X11_XFIXES 1624 } break; 1625 1626 // Have we been restored? 1627 case MapNotify: 1628 { 1629#ifdef DEBUG_XEVENTS 1630 SDL_Log("window 0x%lx: MapNotify!", xevent->xany.window); 1631#endif 1632 X11_DispatchMapNotify(data); 1633 1634#ifdef SDL_VIDEO_DRIVER_X11_XFIXES 1635 // Enable confinement if it was activated. 1636 if (data->pointer_barrier_active == true) { 1637 X11_ConfineCursorWithFlags(_this, data->window, &data->barrier_rect, X11_BARRIER_HANDLED_BY_EVENT); 1638 } 1639#endif // SDL_VIDEO_DRIVER_X11_XFIXES 1640 } break; 1641 1642 // Have we been resized or moved? 1643 case ConfigureNotify: 1644 { 1645#ifdef DEBUG_XEVENTS 1646 SDL_Log("window 0x%lx: ConfigureNotify! (position: %d,%d, size: %dx%d)", xevent->xany.window, 1647 xevent->xconfigure.x, xevent->xconfigure.y, 1648 xevent->xconfigure.width, xevent->xconfigure.height); 1649#endif 1650 // Real configure notify events are relative to the parent, synthetic events are absolute. 1651 if (!xevent->xconfigure.send_event) { 1652 unsigned int NumChildren; 1653 Window ChildReturn, Root, Parent; 1654 Window *Children; 1655 // Translate these coordinates back to relative to root 1656 X11_XQueryTree(data->videodata->display, xevent->xconfigure.window, &Root, &Parent, &Children, &NumChildren); 1657 X11_XTranslateCoordinates(xevent->xconfigure.display, 1658 Parent, DefaultRootWindow(xevent->xconfigure.display), 1659 xevent->xconfigure.x, xevent->xconfigure.y, 1660 &xevent->xconfigure.x, &xevent->xconfigure.y, 1661 &ChildReturn); 1662 } 1663 1664 /* Some window managers send ConfigureNotify before PropertyNotify when changing state (Xfce and 1665 * fvwm are known to do this), which is backwards from other window managers, as well as what is 1666 * expected by SDL and its clients. Defer emitting the size/move events until the corresponding 1667 * PropertyNotify arrives for consistency. 1668 */ 1669 const SDL_WindowFlags changed = X11_GetNetWMState(_this, data->window, xevent->xproperty.window) ^ data->window->flags; 1670 if (changed & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) { 1671 SDL_copyp(&data->pending_xconfigure, &xevent->xconfigure); 1672 data->emit_size_move_after_property_notify = true; 1673 } 1674 1675 if (!data->emit_size_move_after_property_notify) { 1676 X11_EmitConfigureNotifyEvents(data, &xevent->xconfigure); 1677 } 1678 1679#ifdef SDL_VIDEO_DRIVER_X11_XSYNC 1680 X11_HandleConfigure(data->window, &xevent->xconfigure); 1681#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ 1682 } break; 1683 1684 // Have we been requested to quit (or another client message?) 1685 case ClientMessage: 1686 { 1687 static int xdnd_version = 0; 1688 1689 if (xevent->xclient.message_type == videodata->atoms.XdndEnter) { 1690 1691 bool use_list = xevent->xclient.data.l[1] & 1; 1692 data->xdnd_source = xevent->xclient.data.l[0]; 1693 xdnd_version = (xevent->xclient.data.l[1] >> 24); 1694#ifdef DEBUG_XEVENTS 1695 SDL_Log("XID of source window : 0x%lx", data->xdnd_source); 1696 SDL_Log("Protocol version to use : %d", xdnd_version); 1697 SDL_Log("More then 3 data types : %d", (int)use_list); 1698#endif 1699 1700 if (use_list) { 1701 // fetch conversion targets 1702 SDL_x11Prop p; 1703 X11_ReadProperty(&p, display, data->xdnd_source, videodata->atoms.XdndTypeList); 1704 // pick one 1705 data->xdnd_req = X11_PickTarget(display, (Atom *)p.data, p.count); 1706 X11_XFree(p.data); 1707 } else { 1708 // pick from list of three 1709 data->xdnd_req = X11_PickTargetFromAtoms(display, xevent->xclient.data.l[2], xevent->xclient.data.l[3], xevent->xclient.data.l[4]); 1710 } 1711 } else if (xevent->xclient.message_type == videodata->atoms.XdndLeave) { 1712#ifdef DEBUG_XEVENTS 1713 SDL_Log("XID of source window : 0x%lx", xevent->xclient.data.l[0]); 1714#endif 1715 SDL_SendDropComplete(data->window); 1716 } else if (xevent->xclient.message_type == videodata->atoms.XdndPosition) { 1717 1718#ifdef DEBUG_XEVENTS 1719 Atom act = videodata->atoms.XdndActionCopy; 1720 if (xdnd_version >= 2) { 1721 act = xevent->xclient.data.l[4]; 1722 } 1723 SDL_Log("Action requested by user is : %s", X11_XGetAtomName(display, act)); 1724#endif 1725 { 1726 // Drag and Drop position 1727 int root_x, root_y, window_x, window_y; 1728 Window ChildReturn; 1729 root_x = xevent->xclient.data.l[2] >> 16; 1730 root_y = xevent->xclient.data.l[2] & 0xffff; 1731 // Translate from root to current window position 1732 X11_XTranslateCoordinates(display, DefaultRootWindow(display), data->xwindow, 1733 root_x, root_y, &window_x, &window_y, &ChildReturn); 1734 1735 SDL_SendDropPosition(data->window, (float)window_x, (float)window_y); 1736 } 1737 1738 // reply with status 1739 SDL_memset(&m, 0, sizeof(XClientMessageEvent)); 1740 m.type = ClientMessage; 1741 m.display = xevent->xclient.display; 1742 m.window = xevent->xclient.data.l[0]; 1743 m.message_type = videodata->atoms.XdndStatus; 1744 m.format = 32; 1745 m.data.l[0] = data->xwindow; 1746 m.data.l[1] = (data->xdnd_req != None); 1747 m.data.l[2] = 0; // specify an empty rectangle 1748 m.data.l[3] = 0; 1749 m.data.l[4] = videodata->atoms.XdndActionCopy; // we only accept copying anyway 1750 1751 X11_XSendEvent(display, xevent->xclient.data.l[0], False, NoEventMask, (XEvent *)&m); 1752 X11_XFlush(display); 1753 } else if (xevent->xclient.message_type == videodata->atoms.XdndDrop) { 1754 if (data->xdnd_req == None) { 1755 // say again - not interested! 1756 SDL_memset(&m, 0, sizeof(XClientMessageEvent)); 1757 m.type = ClientMessage; 1758 m.display = xevent->xclient.display; 1759 m.window = xevent->xclient.data.l[0]; 1760 m.message_type = videodata->atoms.XdndFinished; 1761 m.format = 32; 1762 m.data.l[0] = data->xwindow; 1763 m.data.l[1] = 0; 1764 m.data.l[2] = None; // fail! 1765 X11_XSendEvent(display, xevent->xclient.data.l[0], False, NoEventMask, (XEvent *)&m); 1766 } else { 1767 // convert 1768 if (xdnd_version >= 1) { 1769 X11_XConvertSelection(display, videodata->atoms.XdndSelection, data->xdnd_req, videodata->atoms.PRIMARY, data->xwindow, xevent->xclient.data.l[2]); 1770 } else { 1771 X11_XConvertSelection(display, videodata->atoms.XdndSelection, data->xdnd_req, videodata->atoms.PRIMARY, data->xwindow, CurrentTime); 1772 } 1773 } 1774 } else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) && 1775 (xevent->xclient.format == 32) && 1776 (xevent->xclient.data.l[0] == videodata->atoms._NET_WM_PING)) { 1777 Window root = DefaultRootWindow(display); 1778 1779#ifdef DEBUG_XEVENTS 1780 SDL_Log("window 0x%lx: _NET_WM_PING", xevent->xany.window); 1781#endif 1782 xevent->xclient.window = root; 1783 X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, xevent); 1784 break; 1785 } 1786 1787 else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) && 1788 (xevent->xclient.format == 32) && 1789 (xevent->xclient.data.l[0] == videodata->atoms.WM_DELETE_WINDOW)) { 1790 1791#ifdef DEBUG_XEVENTS 1792 SDL_Log("window 0x%lx: WM_DELETE_WINDOW", xevent->xany.window); 1793#endif 1794 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_CLOSE_REQUESTED, 0, 0); 1795 break; 1796 } else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) && 1797 (xevent->xclient.format == 32) && 1798 (xevent->xclient.data.l[0] == videodata->atoms._NET_WM_SYNC_REQUEST)) { 1799 1800#ifdef DEBUG_XEVENTS 1801 printf("window %p: _NET_WM_SYNC_REQUEST\n", data); 1802#endif 1803#ifdef SDL_VIDEO_DRIVER_X11_XSYNC 1804 X11_HandleSyncRequest(data->window, &xevent->xclient); 1805#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ 1806 break; 1807 } 1808 } break; 1809 1810 // Do we need to refresh ourselves? 1811 case Expose: 1812 { 1813#ifdef DEBUG_XEVENTS 1814 SDL_Log("window 0x%lx: Expose (count = %d)", xevent->xany.window, xevent->xexpose.count); 1815#endif 1816 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_EXPOSED, 0, 0); 1817 } break; 1818 1819 /* Use XInput2 instead of the xevents API if possible, for: 1820 - KeyPress 1821 - KeyRelease 1822 - MotionNotify 1823 - ButtonPress 1824 - ButtonRelease 1825 XInput2 has more precise information, e.g., to distinguish different input devices. */ 1826 case KeyPress: 1827 case KeyRelease: 1828 { 1829 if (data->xinput2_keyboard_enabled) { 1830 // This input is being handled by XInput2 1831 break; 1832 } 1833 1834 X11_HandleKeyEvent(_this, data, SDL_GLOBAL_KEYBOARD_ID, xevent); 1835 } break; 1836 1837 case MotionNotify: 1838 { 1839 if (X11_Xinput2HandlesMotionForWindow(data)) { 1840 // This input is being handled by XInput2 1841 break; 1842 } 1843 1844 SDL_Mouse *mouse = SDL_GetMouse(); 1845 if (!mouse->relative_mode) { 1846#ifdef DEBUG_MOTION 1847 SDL_Log("window 0x%lx: X11 motion: %d,%d", xevent->xany.window, xevent->xmotion.x, xevent->xmotion.y); 1848#endif 1849 1850 X11_ProcessHitTest(_this, data, (float)xevent->xmotion.x, (float)xevent->xmotion.y, false); 1851 SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xmotion.x, (float)xevent->xmotion.y); 1852 } 1853 } break; 1854 1855 case ButtonPress: 1856 { 1857 if (data->xinput2_mouse_enabled) { 1858 // This input is being handled by XInput2 1859 break; 1860 } 1861 1862 X11_HandleButtonPress(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button, 1863 xevent->xbutton.x, xevent->xbutton.y, xevent->xbutton.time); 1864 } break; 1865 1866 case ButtonRelease: 1867 { 1868 if (data->xinput2_mouse_enabled) { 1869 // This input is being handled by XInput2 1870 break; 1871 } 1872 1873 X11_HandleButtonRelease(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button, xevent->xbutton.time); 1874 } break; 1875 1876 case PropertyNotify: 1877 { 1878#ifdef DEBUG_XEVENTS 1879 unsigned char *propdata; 1880 int status, real_format; 1881 Atom real_type; 1882 unsigned long items_read, items_left; 1883 1884 char *name = X11_XGetAtomName(display, xevent->xproperty.atom); 1885 if (name) { 1886 SDL_Log("window 0x%lx: PropertyNotify: %s %s time=%lu", xevent->xany.window, name, (xevent->xproperty.state == PropertyDelete) ? "deleted" : "changed", xevent->xproperty.time); 1887 X11_XFree(name); 1888 } 1889 1890 status = X11_XGetWindowProperty(display, data->xwindow, xevent->xproperty.atom, 0L, 8192L, False, AnyPropertyType, &real_type, &real_format, &items_read, &items_left, &propdata); 1891 if (status == Success && items_read > 0) { 1892 if (real_type == XA_INTEGER) { 1893 int *values = (int *)propdata; 1894 1895 SDL_Log("{"); 1896 for (i = 0; i < items_read; i++) { 1897 SDL_Log(" %d", values[i]); 1898 } 1899 SDL_Log(" }"); 1900 } else if (real_type == XA_CARDINAL) { 1901 if (real_format == 32) { 1902 Uint32 *values = (Uint32 *)propdata; 1903 1904 SDL_Log("{"); 1905 for (i = 0; i < items_read; i++) { 1906 SDL_Log(" %d", values[i]); 1907 } 1908 SDL_Log(" }"); 1909 } else if (real_format == 16) { 1910 Uint16 *values = (Uint16 *)propdata; 1911 1912 SDL_Log("{"); 1913 for (i = 0; i < items_read; i++) { 1914 SDL_Log(" %d", values[i]); 1915 } 1916 SDL_Log(" }"); 1917 } else if (real_format == 8) { 1918 Uint8 *values = (Uint8 *)propdata; 1919 1920 SDL_Log("{"); 1921 for (i = 0; i < items_read; i++) { 1922 SDL_Log(" %d", values[i]); 1923 } 1924 SDL_Log(" }"); 1925 } 1926 } else if (real_type == XA_STRING || 1927 real_type == videodata->atoms.UTF8_STRING) { 1928 SDL_Log("{ \"%s\" }", propdata); 1929 } else if (real_type == XA_ATOM) { 1930 Atom *atoms = (Atom *)propdata; 1931 1932 SDL_Log("{"); 1933 for (i = 0; i < items_read; i++) { 1934 char *atomname = X11_XGetAtomName(display, atoms[i]); 1935 if (atomname) { 1936 SDL_Log(" %s", atomname); 1937 X11_XFree(atomname); 1938 } 1939 } 1940 SDL_Log(" }"); 1941 } else { 1942 char *atomname = X11_XGetAtomName(display, real_type); 1943 SDL_Log("Unknown type: 0x%lx (%s)", real_type, atomname ? atomname : "UNKNOWN"); 1944 if (atomname) { 1945 X11_XFree(atomname); 1946 } 1947 } 1948 } 1949 if (status == Success) { 1950 X11_XFree(propdata); 1951 } 1952#endif // DEBUG_XEVENTS 1953 1954 /* Take advantage of this moment to make sure user_time has a 1955 valid timestamp from the X server, so if we later try to 1956 raise/restore this window, _NET_ACTIVE_WINDOW can have a 1957 non-zero timestamp, even if there's never been a mouse or 1958 key press to this window so far. Note that we don't try to 1959 set _NET_WM_USER_TIME here, though. That's only for legit 1960 user interaction with the window. */ 1961 if (!data->user_time) { 1962 data->user_time = xevent->xproperty.time; 1963 } 1964 1965 if (xevent->xproperty.atom == data->videodata->atoms._NET_WM_STATE) { 1966 /* Get the new state from the window manager. 1967 * Compositing window managers can alter visibility of windows 1968 * without ever mapping / unmapping them, so we handle that here, 1969 * because they use the NETWM protocol to notify us of changes. 1970 */ 1971 const SDL_WindowFlags flags = X11_GetNetWMState(_this, data->window, xevent->xproperty.window); 1972 const SDL_WindowFlags changed = flags ^ data->window->flags; 1973 1974 if ((changed & SDL_WINDOW_HIDDEN) && !(flags & SDL_WINDOW_HIDDEN)) { 1975 X11_DispatchMapNotify(data); 1976 } 1977 1978 if (!SDL_WINDOW_IS_POPUP(data->window)) { 1979 if (changed & SDL_WINDOW_FULLSCREEN) { 1980 data->pending_operation &= ~X11_PENDING_OP_FULLSCREEN; 1981 1982 if (flags & SDL_WINDOW_FULLSCREEN) { 1983 if (!(flags & SDL_WINDOW_MINIMIZED)) { 1984 const bool commit = SDL_memcmp(&data->window->current_fullscreen_mode, &data->requested_fullscreen_mode, sizeof(SDL_DisplayMode)) != 0; 1985 1986 // Ensure the maximized flag is cleared before entering fullscreen. 1987 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0); 1988 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0); 1989 if (commit) { 1990 /* This was initiated by the compositor, or the mode was changed between the request and the window 1991 * becoming fullscreen. Switch to the application requested mode if necessary. 1992 */ 1993 SDL_copyp(&data->window->current_fullscreen_mode, &data->window->requested_fullscreen_mode); 1994 SDL_UpdateFullscreenMode(data->window, SDL_FULLSCREEN_OP_UPDATE, true); 1995 } else { 1996 SDL_UpdateFullscreenMode(data->window, SDL_FULLSCREEN_OP_ENTER, false); 1997 } 1998 } 1999 } else { 2000 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0); 2001 SDL_UpdateFullscreenMode(data->window, false, false); 2002 2003 SDL_zero(data->requested_fullscreen_mode); 2004 2005 // Need to restore or update any limits changed while the window was fullscreen. 2006 X11_SetWindowMinMax(data->window, !!(flags & SDL_WINDOW_MAXIMIZED)); 2007 2008 // Toggle the borders if they were forced on while creating a borderless fullscreen window. 2009 if (data->fullscreen_borders_forced_on) { 2010 data->toggle_borders = true; 2011 data->fullscreen_borders_forced_on = false; 2012 } 2013 } 2014 2015 if ((flags & SDL_WINDOW_FULLSCREEN) && 2016 (data->border_top || data->border_left || data->border_bottom || data->border_right)) { 2017 /* If the window is entering fullscreen and the borders are 2018 * non-zero sized, turn off size events until the borders are 2019 * shut off to avoid bogus window sizes and positions, and 2020 * note that the old borders were non-zero for restoration. 2021 */ 2022 data->size_move_event_flags |= X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS; 2023 data->previous_borders_nonzero = true; 2024 } else if (!(flags & SDL_WINDOW_FULLSCREEN) && 2025 data->previous_borders_nonzero && 2026 (!data->border_top && !data->border_left && !data->border_bottom && !data->border_right)) { 2027 /* If the window is leaving fullscreen and the current borders 2028 * are zero sized, but weren't when entering fullscreen, turn 2029 * off size events until the borders come back to avoid bogus 2030 * window sizes and positions. 2031 */ 2032 data->size_move_event_flags |= X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS; 2033 data->previous_borders_nonzero = false; 2034 } else { 2035 data->size_move_event_flags = 0; 2036 data->previous_borders_nonzero = false; 2037 2038 if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) { 2039 data->toggle_borders = false; 2040 X11_SetWindowBordered(_this, data->window, !(data->window->flags & SDL_WINDOW_BORDERLESS)); 2041 } 2042 } 2043 } 2044 if ((changed & SDL_WINDOW_MAXIMIZED) && ((flags & SDL_WINDOW_MAXIMIZED) && !(flags & SDL_WINDOW_MINIMIZED))) { 2045 data->pending_operation &= ~X11_PENDING_OP_MAXIMIZE; 2046 if ((changed & SDL_WINDOW_MINIMIZED)) { 2047 data->pending_operation &= ~X11_PENDING_OP_RESTORE; 2048 // If coming out of minimized, send a restore event before sending maximized. 2049 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0); 2050 } 2051 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0); 2052 } 2053 if ((changed & SDL_WINDOW_MINIMIZED) && (flags & SDL_WINDOW_MINIMIZED)) { 2054 data->pending_operation &= ~X11_PENDING_OP_MINIMIZE; 2055 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0); 2056 } 2057 if (!(flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) { 2058 data->pending_operation &= ~X11_PENDING_OP_RESTORE; 2059 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0); 2060 2061 // Apply any pending state if restored. 2062 if (!(flags & SDL_WINDOW_FULLSCREEN)) { 2063 if (data->pending_position) { 2064 data->pending_position = false; 2065 data->pending_operation |= X11_PENDING_OP_MOVE; 2066 data->expected.x = data->window->pending.x - data->border_left; 2067 data->expected.y = data->window->pending.y - data->border_top; 2068 X11_XMoveWindow(display, data->xwindow, data->expected.x, data->expected.y); 2069 } 2070 if (data->pending_size) { 2071 data->pending_size = false; 2072 data->pending_operation |= X11_PENDING_OP_RESIZE; 2073 data->expected.w = data->window->pending.w; 2074 data->expected.h = data->window->pending.h; 2075 X11_XResizeWindow(display, data->xwindow, data->window->pending.w, data->window->pending.h); 2076 } 2077 } 2078 } 2079 if (data->emit_size_move_after_property_notify) { 2080 X11_EmitConfigureNotifyEvents(data, &data->pending_xconfigure); 2081 data->emit_size_move_after_property_notify = false; 2082 } 2083 if ((flags & SDL_WINDOW_INPUT_FOCUS)) { 2084 if (data->pending_move) { 2085 DispatchWindowMove(_this, data, &data->pending_move_point); 2086 data->pending_move = false; 2087 } 2088 } 2089 } 2090 if (changed & SDL_WINDOW_OCCLUDED) { 2091 SDL_SendWindowEvent(data->window, (flags & SDL_WINDOW_OCCLUDED) ? SDL_EVENT_WINDOW_OCCLUDED : SDL_EVENT_WINDOW_EXPOSED, 0, 0); 2092 } 2093 } else if (xevent->xproperty.atom == videodata->atoms.XKLAVIER_STATE) { 2094 /* Hack for Ubuntu 12.04 (etc) that doesn't send MappingNotify 2095 events when the keyboard layout changes (for example, 2096 changing from English to French on the menubar's keyboard 2097 icon). Since it changes the XKLAVIER_STATE property, we 2098 notice and reinit our keymap here. This might not be the 2099 right approach, but it seems to work. */ 2100 X11_UpdateKeymap(_this, true); 2101 } else if (xevent->xproperty.atom == videodata->atoms._NET_FRAME_EXTENTS) { 2102 X11_GetBorderValues(data); 2103 if (data->size_move_event_flags) { 2104 /* Events are disabled when leaving fullscreen until the borders appear to avoid 2105 * incorrect size/position events on compositing window managers. 2106 */ 2107 data->size_move_event_flags &= ~X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS; 2108 } 2109 if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) { 2110 data->toggle_borders = false; 2111 X11_SetWindowBordered(_this, data->window, !(data->window->flags & SDL_WINDOW_BORDERLESS)); 2112 } 2113 } 2114 } break; 2115 2116 case SelectionNotify: 2117 { 2118 Atom target = xevent->xselection.target; 2119#ifdef DEBUG_XEVENTS 2120 SDL_Log("window 0x%lx: SelectionNotify (requestor = 0x%lx, target = 0x%lx)", xevent->xany.window, 2121 xevent->xselection.requestor, xevent->xselection.target); 2122#endif 2123 if (target == data->xdnd_req) { 2124 // read data 2125 SDL_x11Prop p; 2126 X11_ReadProperty(&p, display, data->xwindow, videodata->atoms.PRIMARY); 2127 2128 if (p.format == 8) { 2129 char *saveptr = NULL; 2130 char *name = X11_XGetAtomName(display, target); 2131 if (name) { 2132 char *token = SDL_strtok_r((char *)p.data, "\r\n", &saveptr); 2133 while (token) { 2134 if ((SDL_strcmp("text/plain;charset=utf-8", name) == 0) || 2135 (SDL_strcmp("UTF8_STRING", name) == 0) || 2136 (SDL_strcmp("text/plain", name) == 0) || 2137 (SDL_strcmp("TEXT", name) == 0)) { 2138 SDL_SendDropText(data->window, token); 2139 } else if (SDL_strcmp("text/uri-list", name) == 0) { 2140 if (SDL_URIToLocal(token, token) >= 0) { 2141 SDL_SendDropFile(data->window, NULL, token); 2142 } 2143 } 2144 token = SDL_strtok_r(NULL, "\r\n", &saveptr); 2145 } 2146 X11_XFree(name); 2147 } 2148 SDL_SendDropComplete(data->window); 2149 } 2150 X11_XFree(p.data); 2151 2152 // send reply 2153 SDL_memset(&m, 0, sizeof(XClientMessageEvent)); 2154 m.type = ClientMessage; 2155 m.display = display; 2156 m.window = data->xdnd_source; 2157 m.message_type = videodata->atoms.XdndFinished; 2158 m.format = 32; 2159 m.data.l[0] = data->xwindow; 2160 m.data.l[1] = 1; 2161 m.data.l[2] = videodata->atoms.XdndActionCopy; 2162 X11_XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent *)&m); 2163 2164 X11_XSync(display, False); 2165 } 2166 } break; 2167 2168 default: 2169 { 2170#ifdef DEBUG_XEVENTS 2171 SDL_Log("window 0x%lx: Unhandled event %d", xevent->xany.window, xevent->type); 2172#endif 2173 } break; 2174 } 2175} 2176 2177static void X11_HandleFocusChanges(SDL_VideoDevice *_this) 2178{ 2179 SDL_VideoData *videodata = _this->internal; 2180 int i; 2181 2182 if (videodata && videodata->windowlist) { 2183 for (i = 0; i < videodata->numwindows; ++i) { 2184 SDL_WindowData *data = videodata->windowlist[i]; 2185 if (data && data->pending_focus != PENDING_FOCUS_NONE) { 2186 Uint64 now = SDL_GetTicks(); 2187 if (now >= data->pending_focus_time) { 2188 if (data->pending_focus == PENDING_FOCUS_IN) { 2189 X11_DispatchFocusIn(_this, data); 2190 } else { 2191 X11_DispatchFocusOut(_this, data); 2192 } 2193 data->pending_focus = PENDING_FOCUS_NONE; 2194 } 2195 } 2196 } 2197 } 2198} 2199 2200static Bool isAnyEvent(Display *display, XEvent *ev, XPointer arg) 2201{ 2202 return True; 2203} 2204 2205static bool X11_PollEvent(Display *display, XEvent *event) 2206{ 2207 if (!X11_XCheckIfEvent(display, event, isAnyEvent, NULL)) { 2208 return false; 2209 } 2210 2211 return true; 2212} 2213 2214void X11_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window) 2215{ 2216 SDL_VideoData *data = _this->internal; 2217 Display *req_display = data->request_display; 2218 Window xwindow = window->internal->xwindow; 2219 XClientMessageEvent event; 2220 2221 SDL_memset(&event, 0, sizeof(XClientMessageEvent)); 2222 event.type = ClientMessage; 2223 event.display = req_display; 2224 event.send_event = True; 2225 event.message_type = data->atoms._SDL_WAKEUP; 2226 event.format = 8; 2227 2228 X11_XSendEvent(req_display, xwindow, False, NoEventMask, (XEvent *)&event); 2229 /* XSendEvent returns a status and it could be BadValue or BadWindow. If an 2230 error happens it is an SDL's internal error and there is nothing we can do here. */ 2231 X11_XFlush(req_display); 2232} 2233 2234int X11_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS) 2235{ 2236 SDL_VideoData *videodata = _this->internal; 2237 Display *display; 2238 XEvent xevent; 2239 display = videodata->display; 2240 2241 SDL_zero(xevent); 2242 2243 // Flush and poll to grab any events already read and queued 2244 X11_XFlush(display); 2245 if (X11_PollEvent(display, &xevent)) { 2246 // Fall through 2247 } else if (timeoutNS == 0) { 2248 return 0; 2249 } else { 2250 // Use SDL_IOR_NO_RETRY to ensure SIGINT will break us out of our wait 2251 int err = SDL_IOReady(ConnectionNumber(display), SDL_IOR_READ | SDL_IOR_NO_RETRY, timeoutNS); 2252 if (err > 0) { 2253 if (!X11_PollEvent(display, &xevent)) { 2254 /* Someone may have beat us to reading the fd. Return 1 here to 2255 * trigger the normal spurious wakeup logic in the event core. */ 2256 return 1; 2257 } 2258 } else if (err == 0) { 2259 // Timeout 2260 return 0; 2261 } else { 2262 // Error returned from poll()/select() 2263 2264 if (errno == EINTR) { 2265 /* If the wait was interrupted by a signal, we may have generated a 2266 * SDL_EVENT_QUIT event. Let the caller know to call SDL_PumpEvents(). */ 2267 return 1; 2268 } else { 2269 return err; 2270 } 2271 } 2272 } 2273 2274 X11_DispatchEvent(_this, &xevent); 2275 return 1; 2276} 2277 2278void X11_PumpEvents(SDL_VideoDevice *_this) 2279{ 2280 SDL_VideoData *data = _this->internal; 2281 XEvent xevent; 2282 int i; 2283 2284 /* Check if a display had the mode changed and is waiting for a window to asynchronously become 2285 * fullscreen. If there is no fullscreen window past the elapsed timeout, revert the mode switch. 2286 */ 2287 for (i = 0; i < _this->num_displays; ++i) { 2288 if (_this->displays[i]->internal->mode_switch_deadline_ns) { 2289 if (_this->displays[i]->fullscreen_window) { 2290 _this->displays[i]->internal->mode_switch_deadline_ns = 0; 2291 } else if (SDL_GetTicksNS() >= _this->displays[i]->internal->mode_switch_deadline_ns) { 2292 SDL_LogError(SDL_LOG_CATEGORY_VIDEO, 2293 "Time out elapsed after mode switch on display %" SDL_PRIu32 " with no window becoming fullscreen; reverting", _this->displays[i]->id); 2294 SDL_SetDisplayModeForDisplay(_this->displays[i], NULL); 2295 _this->displays[i]->internal->mode_switch_deadline_ns = 0; 2296 } 2297 } 2298 } 2299 2300 if (data->last_mode_change_deadline) { 2301 if (SDL_GetTicks() >= data->last_mode_change_deadline) { 2302 data->last_mode_change_deadline = 0; // assume we're done. 2303 } 2304 } 2305 2306 // Update activity every 30 seconds to prevent screensaver 2307 if (_this->suspend_screensaver) { 2308 Uint64 now = SDL_GetTicks(); 2309 if (!data->screensaver_activity || now >= (data->screensaver_activity + 30000)) { 2310 X11_XResetScreenSaver(data->display); 2311 2312#ifdef SDL_USE_LIBDBUS 2313 SDL_DBus_ScreensaverTickle(); 2314#endif 2315 2316 data->screensaver_activity = now; 2317 } 2318 } 2319 2320 SDL_zero(xevent); 2321 2322 // Keep processing pending events 2323 while (X11_PollEvent(data->display, &xevent)) { 2324 X11_DispatchEvent(_this, &xevent); 2325 } 2326 2327 // FIXME: Only need to do this when there are pending focus changes 2328 X11_HandleFocusChanges(_this); 2329 2330 // FIXME: Only need to do this when there are flashing windows 2331 for (i = 0; i < data->numwindows; ++i) { 2332 if (data->windowlist[i] != NULL && 2333 data->windowlist[i]->flash_cancel_time && 2334 SDL_GetTicks() >= data->windowlist[i]->flash_cancel_time) { 2335 X11_FlashWindow(_this, data->windowlist[i]->window, SDL_FLASH_CANCEL); 2336 } 2337 } 2338 2339 if (data->xinput_hierarchy_changed) { 2340 X11_Xinput2UpdateDevices(_this); 2341 data->xinput_hierarchy_changed = false; 2342 } 2343} 2344 2345bool X11_SuspendScreenSaver(SDL_VideoDevice *_this) 2346{ 2347#ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER 2348 SDL_VideoData *data = _this->internal; 2349 int dummy; 2350 int major_version, minor_version; 2351#endif // SDL_VIDEO_DRIVER_X11_XSCRNSAVER 2352 2353#ifdef SDL_USE_LIBDBUS 2354 if (SDL_DBus_ScreensaverInhibit(_this->suspend_screensaver)) { 2355 return true; 2356 } 2357 2358 if (_this->suspend_screensaver) { 2359 SDL_DBus_ScreensaverTickle(); 2360 } 2361#endif 2362 2363#ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER 2364 if (SDL_X11_HAVE_XSS) { 2365 // X11_XScreenSaverSuspend was introduced in MIT-SCREEN-SAVER 1.1 2366 if (!X11_XScreenSaverQueryExtension(data->display, &dummy, &dummy) || 2367 !X11_XScreenSaverQueryVersion(data->display, 2368 &major_version, &minor_version) || 2369 major_version < 1 || (major_version == 1 && minor_version < 1)) { 2370 return SDL_Unsupported(); 2371 } 2372 2373 X11_XScreenSaverSuspend(data->display, _this->suspend_screensaver); 2374 X11_XResetScreenSaver(data->display); 2375 return true; 2376 } 2377#endif 2378 return SDL_Unsupported(); 2379} 2380 2381#endif // SDL_VIDEO_DRIVER_X11 2382
[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.