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