Atlas - SDL_waylandevents.c
Home / ext / SDL / src / video / wayland Lines: 5 | Size: 159803 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 22#include "SDL_internal.h" 23 24#ifdef SDL_VIDEO_DRIVER_WAYLAND 25 26#include "../../core/unix/SDL_poll.h" 27#include "../../events/SDL_events_c.h" 28#include "../../events/SDL_scancode_tables_c.h" 29#include "../../events/SDL_keysym_to_keycode_c.h" 30#include "../../core/linux/SDL_system_theme.h" 31#include "../SDL_sysvideo.h" 32 33#include "SDL_waylandvideo.h" 34#include "SDL_waylandevents_c.h" 35#include "SDL_waylandwindow.h" 36#include "SDL_waylandmouse.h" 37#include "SDL_waylandclipboard.h" 38#include "SDL_waylandkeyboard.h" 39 40#include "pointer-constraints-unstable-v1-client-protocol.h" 41#include "relative-pointer-unstable-v1-client-protocol.h" 42#include "xdg-shell-client-protocol.h" 43#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h" 44#include "text-input-unstable-v3-client-protocol.h" 45#include "tablet-v2-client-protocol.h" 46#include "primary-selection-unstable-v1-client-protocol.h" 47#include "input-timestamps-unstable-v1-client-protocol.h" 48#include "pointer-gestures-unstable-v1-client-protocol.h" 49#include "cursor-shape-v1-client-protocol.h" 50#include "viewporter-client-protocol.h" 51 52#ifdef HAVE_LIBDECOR_H 53#include <libdecor.h> 54#endif 55 56// Per the spec, Wayland mouse and stylus buttons are defined as Linux event codes. 57#ifdef SDL_INPUT_LINUXEV 58#include <linux/input.h> 59#else 60#define BTN_LEFT (0x110) 61#define BTN_RIGHT (0x111) 62#define BTN_MIDDLE (0x112) 63#define BTN_SIDE (0x113) 64#define BTN_EXTRA (0x114) 65#define BTN_STYLUS (0x14b) 66#define BTN_STYLUS2 (0x14c) 67#define BTN_STYLUS3 (0x149) 68#endif 69#include "../../events/SDL_keysym_to_scancode_c.h" 70#include "../../events/imKStoUCS.h" 71#include <errno.h> 72#include <sys/mman.h> 73#include <unistd.h> 74#include <xkbcommon/xkbcommon-compose.h> 75#include <xkbcommon/xkbcommon.h> 76 77// Weston uses a ratio of 10 units per scroll tick 78#define WAYLAND_WHEEL_AXIS_UNIT 10 79 80#ifndef XKB_MOD_NAME_MOD3 81#define XKB_MOD_NAME_MOD3 "Mod3" 82#endif 83 84#ifndef XKB_MOD_NAME_MOD5 85#define XKB_MOD_NAME_MOD5 "Mod5" 86#endif 87 88// Keyboard and mouse names to match XWayland 89#define WAYLAND_DEFAULT_KEYBOARD_NAME "Virtual core keyboard" 90#define WAYLAND_DEFAULT_POINTER_NAME "Virtual core pointer" 91#define WAYLAND_DEFAULT_TOUCH_NAME "Virtual core touch" 92 93// Focus clickthrough timeout 94#define WAYLAND_FOCUS_CLICK_TIMEOUT_NS SDL_MS_TO_NS(10) 95 96// Timer rollover detection thresholds 97#define WAYLAND_TIMER_ROLLOVER_INTERVAL_LOW (SDL_MAX_UINT32 / 16U) 98#define WAYLAND_TIMER_ROLLOVER_INTERVAL_HIGH (WAYLAND_TIMER_ROLLOVER_INTERVAL_LOW * 15U) 99 100// Scoped function declarations 101static void Wayland_SeatUpdateKeyboardGrab(SDL_WaylandSeat *seat); 102 103typedef struct 104{ 105 SDL_TouchID id; 106 wl_fixed_t fx; 107 wl_fixed_t fy; 108 struct wl_surface *surface; 109 110 struct wl_list link; 111} SDL_WaylandTouchPoint; 112 113static void Wayland_SeatAddTouch(SDL_WaylandSeat *seat, SDL_TouchID id, wl_fixed_t fx, wl_fixed_t fy, struct wl_surface *surface) 114{ 115 SDL_WaylandTouchPoint *tp = SDL_malloc(sizeof(SDL_WaylandTouchPoint)); 116 117 SDL_zerop(tp); 118 tp->id = id; 119 tp->fx = fx; 120 tp->fy = fy; 121 tp->surface = surface; 122 123 WAYLAND_wl_list_insert(&seat->touch.points, &tp->link); 124} 125 126static void Wayland_SeatCancelTouch(SDL_WaylandSeat *seat, SDL_WaylandTouchPoint *tp) 127{ 128 if (tp->surface) { 129 SDL_WindowData *window_data = Wayland_GetWindowDataForOwnedSurface(tp->surface); 130 131 if (window_data) { 132 const float x = (float)(wl_fixed_to_double(tp->fx) / window_data->current.logical_width); 133 const float y = (float)(wl_fixed_to_double(tp->fy) / window_data->current.logical_height); 134 135 SDL_SendTouch(0, (SDL_TouchID)(uintptr_t)seat->touch.wl_touch, 136 (SDL_FingerID)(tp->id + 1), window_data->sdlwindow, SDL_EVENT_FINGER_CANCELED, x, y, 0.0f); 137 138 --window_data->active_touch_count; 139 140 /* If the window currently has mouse focus and has no currently active keyboards, pointers, 141 * or touch events, then consider mouse focus to be lost. 142 */ 143 if (SDL_GetMouseFocus() == window_data->sdlwindow && !window_data->keyboard_focus_count && 144 !window_data->pointer_focus_count && !window_data->active_touch_count) { 145 SDL_SetMouseFocus(NULL); 146 } 147 } 148 } 149 150 WAYLAND_wl_list_remove(&tp->link); 151 SDL_free(tp); 152} 153 154static void Wayland_SeatUpdateTouch(SDL_WaylandSeat *seat, SDL_TouchID id, wl_fixed_t fx, wl_fixed_t fy, struct wl_surface **surface) 155{ 156 SDL_WaylandTouchPoint *tp; 157 158 wl_list_for_each (tp, &seat->touch.points, link) { 159 if (tp->id == id) { 160 tp->fx = fx; 161 tp->fy = fy; 162 if (surface) { 163 *surface = tp->surface; 164 } 165 break; 166 } 167 } 168} 169 170static void Wayland_SeatRemoveTouch(SDL_WaylandSeat *seat, SDL_TouchID id, wl_fixed_t *fx, wl_fixed_t *fy, struct wl_surface **surface) 171{ 172 SDL_WaylandTouchPoint *tp; 173 174 wl_list_for_each (tp, &seat->touch.points, link) { 175 if (tp->id == id) { 176 if (fx) { 177 *fx = tp->fx; 178 } 179 if (fy) { 180 *fy = tp->fy; 181 } 182 if (surface) { 183 *surface = tp->surface; 184 } 185 186 WAYLAND_wl_list_remove(&tp->link); 187 SDL_free(tp); 188 break; 189 } 190 } 191} 192 193static void Wayland_GetScaledMouseRect(SDL_Window *window, SDL_Rect *scaled_rect) 194{ 195 SDL_WindowData *window_data = window->internal; 196 197 scaled_rect->x = (int)SDL_floor(window->mouse_rect.x / window_data->pointer_scale.x); 198 scaled_rect->y = (int)SDL_floor(window->mouse_rect.y / window_data->pointer_scale.y); 199 scaled_rect->w = (int)SDL_ceil(window->mouse_rect.w / window_data->pointer_scale.x); 200 scaled_rect->h = (int)SDL_ceil(window->mouse_rect.h / window_data->pointer_scale.y); 201} 202 203static Uint64 Wayland_AdjustEventTimestampBase(Uint64 nsTimestamp) 204{ 205 static Uint64 timestamp_offset = 0; 206 const Uint64 now = SDL_GetTicksNS(); 207 208 if (!timestamp_offset) { 209 timestamp_offset = (now - nsTimestamp); 210 } 211 nsTimestamp += timestamp_offset; 212 213 if (nsTimestamp > now) { 214 timestamp_offset -= (nsTimestamp - now); 215 nsTimestamp = now; 216 } 217 218 return nsTimestamp; 219} 220 221/* This should only be called with 32-bit millisecond timestamps received in Wayland events! 222 * No synthetic or high-res timestamps, as they can corrupt the rollover offset! 223 */ 224static Uint64 Wayland_EventTimestampMSToNS(Uint32 wl_timestamp_ms) 225{ 226 static Uint64 timestamp_offset = 0; 227 static Uint32 last = 0; 228 Uint64 timestamp = SDL_MS_TO_NS(wl_timestamp_ms) + timestamp_offset; 229 230 if (wl_timestamp_ms >= last) { 231 if (timestamp_offset && last < WAYLAND_TIMER_ROLLOVER_INTERVAL_LOW && wl_timestamp_ms > WAYLAND_TIMER_ROLLOVER_INTERVAL_HIGH) { 232 // A time that crossed backwards across zero was received. Subtract the increased time base offset. 233 timestamp -= SDL_MS_TO_NS(SDL_UINT64_C(0x100000000)); 234 } else { 235 last = wl_timestamp_ms; 236 } 237 } else { 238 /* Only increment the base time offset if the timer actually crossed forward across 0, 239 * and not if this is just a timestamp from a slightly older event. 240 */ 241 if (wl_timestamp_ms < WAYLAND_TIMER_ROLLOVER_INTERVAL_LOW && last > WAYLAND_TIMER_ROLLOVER_INTERVAL_HIGH) { 242 timestamp_offset += SDL_MS_TO_NS(SDL_UINT64_C(0x100000000)); 243 timestamp += SDL_MS_TO_NS(SDL_UINT64_C(0x100000000)); 244 last = wl_timestamp_ms; 245 } 246 } 247 248 return timestamp; 249} 250 251/* Even if high-res timestamps are available, the millisecond timestamps are still processed 252 * to accumulate the rollover offset if needed later. 253 */ 254 255static Uint64 Wayland_GetKeyboardTimestamp(SDL_WaylandSeat *seat, Uint32 wl_timestamp_ms) 256{ 257 const Uint64 adjustedTimestampNS = Wayland_EventTimestampMSToNS(wl_timestamp_ms); 258 return Wayland_AdjustEventTimestampBase(seat->keyboard.timestamps ? seat->keyboard.highres_timestamp_ns : adjustedTimestampNS); 259} 260 261static Uint64 Wayland_GetPointerTimestamp(SDL_WaylandSeat *seat, Uint32 wl_timestamp_ms) 262{ 263 const Uint64 adjustedTimestampNS = Wayland_EventTimestampMSToNS(wl_timestamp_ms); 264 return Wayland_AdjustEventTimestampBase(seat->pointer.timestamps ? seat->pointer.highres_timestamp_ns : adjustedTimestampNS); 265} 266 267Uint64 Wayland_GetTouchTimestamp(SDL_WaylandSeat *seat, Uint32 wl_timestamp_ms) 268{ 269 const Uint64 adjustedTimestampNS = Wayland_EventTimestampMSToNS(wl_timestamp_ms); 270 return Wayland_AdjustEventTimestampBase(seat->touch.timestamps ? seat->touch.highres_timestamp_ns : adjustedTimestampNS); 271} 272 273static void input_timestamp_listener(void *data, struct zwp_input_timestamps_v1 *zwp_input_timestamps_v1, 274 uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) 275{ 276 *((Uint64 *)data) = ((((Uint64)tv_sec_hi << 32) | (Uint64)tv_sec_lo) * SDL_NS_PER_SECOND) + tv_nsec; 277} 278 279static const struct zwp_input_timestamps_v1_listener timestamp_listener = { 280 input_timestamp_listener 281}; 282 283static void Wayland_SeatRegisterInputTimestampListeners(SDL_WaylandSeat *seat) 284{ 285 if (seat->display->input_timestamps_manager) { 286 if (seat->keyboard.wl_keyboard && !seat->keyboard.timestamps) { 287 seat->keyboard.timestamps = zwp_input_timestamps_manager_v1_get_keyboard_timestamps(seat->display->input_timestamps_manager, seat->keyboard.wl_keyboard); 288 zwp_input_timestamps_v1_add_listener(seat->keyboard.timestamps, ×tamp_listener, &seat->keyboard.highres_timestamp_ns); 289 } 290 291 if (seat->pointer.wl_pointer && !seat->pointer.timestamps) { 292 seat->pointer.timestamps = zwp_input_timestamps_manager_v1_get_pointer_timestamps(seat->display->input_timestamps_manager, seat->pointer.wl_pointer); 293 zwp_input_timestamps_v1_add_listener(seat->pointer.timestamps, ×tamp_listener, &seat->pointer.highres_timestamp_ns); 294 } 295 296 if (seat->touch.wl_touch && !seat->touch.timestamps) { 297 seat->touch.timestamps = zwp_input_timestamps_manager_v1_get_touch_timestamps(seat->display->input_timestamps_manager, seat->touch.wl_touch); 298 zwp_input_timestamps_v1_add_listener(seat->touch.timestamps, ×tamp_listener, &seat->touch.highres_timestamp_ns); 299 } 300 } 301} 302 303void Wayland_DisplayInitInputTimestampManager(SDL_VideoData *display) 304{ 305 if (display->input_timestamps_manager) { 306 SDL_WaylandSeat *seat; 307 wl_list_for_each (seat, &display->seat_list, link) { 308 Wayland_SeatRegisterInputTimestampListeners(seat); 309 } 310 } 311} 312 313static void handle_pinch_begin(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers) 314{ 315 if (!surface) { 316 return; 317 } 318 319 SDL_WindowData *wind = Wayland_GetWindowDataForOwnedSurface(surface); 320 if (wind) { 321 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 322 seat->pointer.gesture_focus = wind; 323 324 const Uint64 timestamp = Wayland_GetPointerTimestamp(seat, time); 325 SDL_SendPinch(SDL_EVENT_PINCH_BEGIN, timestamp, wind->sdlwindow, 0.0f); 326 } 327} 328 329static void handle_pinch_update(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t time, 330 wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation) 331{ 332 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 333 334 if (seat->pointer.gesture_focus) { 335 const Uint64 timestamp = Wayland_GetPointerTimestamp(seat, time); 336 const float s = (float)wl_fixed_to_double(scale); 337 SDL_SendPinch(SDL_EVENT_PINCH_UPDATE, timestamp, seat->pointer.gesture_focus->sdlwindow, s); 338 } 339} 340 341static void handle_pinch_end(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled) 342{ 343 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 344 345 if (seat->pointer.gesture_focus) { 346 const Uint64 timestamp = Wayland_GetPointerTimestamp(seat, time); 347 SDL_SendPinch(SDL_EVENT_PINCH_END, timestamp, seat->pointer.gesture_focus->sdlwindow, 0.0f); 348 349 seat->pointer.gesture_focus = NULL; 350 } 351} 352 353static const struct zwp_pointer_gesture_pinch_v1_listener gesture_pinch_listener = { 354 handle_pinch_begin, 355 handle_pinch_update, 356 handle_pinch_end 357}; 358 359static void Wayland_SeatCreatePointerGestures(SDL_WaylandSeat *seat) 360{ 361 if (seat->display->zwp_pointer_gestures) { 362 if (seat->pointer.wl_pointer && !seat->pointer.gesture_pinch) { 363 seat->pointer.gesture_pinch = zwp_pointer_gestures_v1_get_pinch_gesture(seat->display->zwp_pointer_gestures, seat->pointer.wl_pointer); 364 zwp_pointer_gesture_pinch_v1_set_user_data(seat->pointer.gesture_pinch, seat); 365 zwp_pointer_gesture_pinch_v1_add_listener(seat->pointer.gesture_pinch, &gesture_pinch_listener, seat); 366 } 367 } 368} 369 370void Wayland_DisplayInitPointerGestureManager(SDL_VideoData *display) 371{ 372 SDL_WaylandSeat *seat; 373 wl_list_for_each (seat, &display->seat_list, link) { 374 Wayland_SeatCreatePointerGestures(seat); 375 } 376} 377 378static void Wayland_SeatCreateCursorShape(SDL_WaylandSeat *seat) 379{ 380 if (seat->display->cursor_shape_manager) { 381 if (seat->pointer.wl_pointer && !seat->pointer.cursor_state.cursor_shape) { 382 seat->pointer.cursor_state.cursor_shape = wp_cursor_shape_manager_v1_get_pointer(seat->display->cursor_shape_manager, seat->pointer.wl_pointer); 383 } 384 385 SDL_WaylandPenTool *tool; 386 wl_list_for_each(tool, &seat->tablet.tool_list, link) { 387 if (!tool->cursor_state.cursor_shape) { 388 tool->cursor_state.cursor_shape = wp_cursor_shape_manager_v1_get_tablet_tool_v2(seat->display->cursor_shape_manager, tool->wltool); 389 } 390 } 391 } 392} 393 394void Wayland_DisplayInitCursorShapeManager(SDL_VideoData *display) 395{ 396 SDL_WaylandSeat *seat; 397 wl_list_for_each (seat, &display->seat_list, link) { 398 Wayland_SeatCreateCursorShape(seat); 399 } 400} 401 402static void Wayland_SeatSetKeymap(SDL_WaylandSeat *seat) 403{ 404 const bool send_event = !seat->display->initializing; 405 406 if (seat->keyboard.sdl_keymap && 407 seat->keyboard.xkb.current_layout < seat->keyboard.xkb.num_layouts && 408 seat->keyboard.sdl_keymap[seat->keyboard.xkb.current_layout] != SDL_GetCurrentKeymap(true)) { 409 SDL_SetKeymap(seat->keyboard.sdl_keymap[seat->keyboard.xkb.current_layout], send_event); 410 SDL_SetModState(seat->keyboard.pressed_modifiers | seat->keyboard.locked_modifiers); 411 } 412} 413 414// Returns true if a key repeat event was due 415static bool keyboard_repeat_handle(SDL_WaylandKeyboardRepeat *repeat_info, Uint64 elapsed) 416{ 417 bool ret = false; 418 419 while (elapsed >= repeat_info->next_repeat_ns) { 420 if (repeat_info->scancode != SDL_SCANCODE_UNKNOWN) { 421 const Uint64 timestamp = repeat_info->base_time_ns + repeat_info->next_repeat_ns; 422 SDL_SendKeyboardKeyIgnoreModifiers(Wayland_AdjustEventTimestampBase(timestamp), repeat_info->keyboard_id, repeat_info->key, repeat_info->scancode, true); 423 } 424 if (repeat_info->text[0] && !(SDL_GetModState() & (SDL_KMOD_CTRL | SDL_KMOD_ALT))) { 425 SDL_SendKeyboardText(repeat_info->text); 426 } 427 repeat_info->next_repeat_ns += SDL_NS_PER_SECOND / (Uint64)repeat_info->repeat_rate; 428 ret = true; 429 } 430 return ret; 431} 432 433static void keyboard_repeat_clear(SDL_WaylandKeyboardRepeat *repeat_info) 434{ 435 if (!repeat_info->is_initialized) { 436 return; 437 } 438 repeat_info->is_key_down = false; 439} 440 441static void keyboard_repeat_set(SDL_WaylandKeyboardRepeat *repeat_info, Uint32 keyboard_id, uint32_t key, Uint32 wl_press_time_ms, 442 Uint64 base_time_ns, uint32_t scancode, bool has_text, char text[8]) 443{ 444 if (!repeat_info->is_initialized || !repeat_info->repeat_rate) { 445 return; 446 } 447 repeat_info->is_key_down = true; 448 repeat_info->keyboard_id = keyboard_id; 449 repeat_info->key = key; 450 repeat_info->wl_press_time_ms = wl_press_time_ms; 451 repeat_info->base_time_ns = base_time_ns; 452 repeat_info->sdl_press_time_ns = SDL_GetTicksNS(); 453 repeat_info->next_repeat_ns = SDL_MS_TO_NS(repeat_info->repeat_delay_ms); 454 repeat_info->scancode = scancode; 455 if (has_text) { 456 SDL_memcpy(repeat_info->text, text, sizeof(repeat_info->text)); 457 } else { 458 repeat_info->text[0] = '\0'; 459 } 460} 461 462static uint32_t keyboard_repeat_get_key(SDL_WaylandKeyboardRepeat *repeat_info) 463{ 464 if (repeat_info->is_initialized && repeat_info->is_key_down) { 465 return repeat_info->key; 466 } 467 468 return 0; 469} 470 471static void keyboard_repeat_set_text(SDL_WaylandKeyboardRepeat *repeat_info, const char text[8]) 472{ 473 if (repeat_info->is_initialized) { 474 SDL_copyp(repeat_info->text, text); 475 } 476} 477 478static bool keyboard_repeat_is_set(SDL_WaylandKeyboardRepeat *repeat_info) 479{ 480 return repeat_info->is_initialized && repeat_info->is_key_down; 481} 482 483static bool keyboard_repeat_key_is_set(SDL_WaylandKeyboardRepeat *repeat_info, uint32_t key) 484{ 485 return repeat_info->is_initialized && repeat_info->is_key_down && key == repeat_info->key; 486} 487 488static void sync_done_handler(void *data, struct wl_callback *callback, uint32_t callback_data) 489{ 490 // Nothing to do, just destroy the callback 491 wl_callback_destroy(callback); 492} 493 494static struct wl_callback_listener sync_listener = { 495 sync_done_handler 496}; 497 498void Wayland_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window) 499{ 500 SDL_VideoData *d = _this->internal; 501 502 // Queue a sync event to unblock the main event queue if it's being waited on. 503 struct wl_callback *cb = wl_display_sync(d->display); 504 wl_callback_add_listener(cb, &sync_listener, NULL); 505 WAYLAND_wl_display_flush(d->display); 506} 507 508int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS) 509{ 510 SDL_VideoData *d = _this->internal; 511 SDL_WaylandSeat *seat; 512 Uint64 start = SDL_GetTicksNS(); 513 const int display_fd = WAYLAND_wl_display_get_fd(d->display); 514 int ret; 515 bool poll_alarm_set = false; 516 517#ifdef SDL_USE_IME 518 SDL_Window *keyboard_focus = SDL_GetKeyboardFocus(); 519 if (!d->text_input_manager && keyboard_focus && SDL_TextInputActive(keyboard_focus)) { 520 // If a DBus IME is active with no text input protocol, periodically wake to poll it. 521 if (timeoutNS < 0 || SDL_MS_TO_NS(200) <= timeoutNS) { 522 timeoutNS = SDL_MS_TO_NS(200); 523 poll_alarm_set = true; 524 } 525 } 526#endif 527 528 // If key repeat is active, we'll need to cap our maximum wait time to handle repeats 529 wl_list_for_each (seat, &d->seat_list, link) { 530 if (keyboard_repeat_is_set(&seat->keyboard.repeat)) { 531 const Uint64 elapsed = start - seat->keyboard.repeat.sdl_press_time_ns; 532 const Uint64 next_repeat_wait_time = (seat->keyboard.repeat.next_repeat_ns - elapsed) + 1; 533 if (timeoutNS < 0 || next_repeat_wait_time <= timeoutNS) { 534 timeoutNS = next_repeat_wait_time; 535 poll_alarm_set = true; 536 } 537 } 538 } 539 540 if (WAYLAND_wl_display_prepare_read(d->display) == 0) { 541 if (timeoutNS > 0) { 542 const Uint64 now = SDL_GetTicksNS(); 543 const Uint64 elapsed = now - start; 544 start = now; 545 timeoutNS = elapsed <= timeoutNS ? timeoutNS - elapsed : 0; 546 } 547 548 ret = WAYLAND_wl_display_flush(d->display); 549 550 if (ret == -1 && errno == EAGAIN) { 551 // Unable to write to the socket; poll until the socket can be written to, it times out, or is interrupted. 552 ret = SDL_IOReady(display_fd, SDL_IOR_WRITE | SDL_IOR_NO_RETRY, timeoutNS); 553 554 if (ret <= 0) { 555 // The poll operation timed out or experienced an error, so see if there are any events to read without waiting. 556 timeoutNS = 0; 557 } 558 } 559 560 if (ret < 0) { 561 // Pump events on an interrupt or broken pipe to handle the error. 562 WAYLAND_wl_display_cancel_read(d->display); 563 return errno == EINTR || errno == EPIPE ? 1 : ret; 564 } 565 566 if (timeoutNS > 0) { 567 const Uint64 now = SDL_GetTicksNS(); 568 const Uint64 elapsed = now - start; 569 start = now; 570 timeoutNS = elapsed <= timeoutNS ? timeoutNS - elapsed : 0; 571 } 572 573 // Use SDL_IOR_NO_RETRY to catch EINTR. 574 ret = SDL_IOReady(display_fd, SDL_IOR_READ | SDL_IOR_NO_RETRY, timeoutNS); 575 if (ret <= 0) { 576 // Timeout or error, cancel the read. 577 WAYLAND_wl_display_cancel_read(d->display); 578 579 // The poll timed out with no data to read, but signal the caller to pump events if polling is required. 580 if (ret == 0) { 581 return poll_alarm_set ? 1 : 0; 582 } else { 583 // Pump events on an interrupt or broken pipe to handle the error. 584 return errno == EINTR || errno == EPIPE ? 1 : ret; 585 } 586 } 587 588 ret = WAYLAND_wl_display_read_events(d->display); 589 if (ret == -1) { 590 return ret; 591 } 592 } 593 594 // Signal to the caller that there might be an event available. 595 return 1; 596} 597 598void Wayland_PumpEvents(SDL_VideoDevice *_this) 599{ 600 SDL_VideoData *d = _this->internal; 601 SDL_WaylandSeat *seat; 602 const int display_fd = WAYLAND_wl_display_get_fd(d->display); 603 int ret = 0; 604 605#ifdef SDL_USE_IME 606 SDL_Window *keyboard_focus = SDL_GetKeyboardFocus(); 607 if (!d->text_input_manager && keyboard_focus && SDL_TextInputActive(keyboard_focus)) { 608 SDL_IME_PumpEvents(); 609 } 610#endif 611 612#ifdef HAVE_LIBDECOR_H 613 if (d->shell.libdecor) { 614 libdecor_dispatch(d->shell.libdecor, 0); 615 } 616#endif 617 618 /* If the queue isn't empty, dispatch any old events, and try to prepare for reading again. 619 * If preparing to read returns -1 on the second try, wl_display_read_events() enqueued new 620 * events at some point between dispatching the old events and preparing for the read, 621 * probably from another thread, which means that the events in the queue are current. 622 */ 623 ret = WAYLAND_wl_display_prepare_read(d->display); 624 if (ret == -1) { 625 ret = WAYLAND_wl_display_dispatch_pending(d->display); 626 if (ret < 0) { 627 goto connection_error; 628 } 629 630 ret = WAYLAND_wl_display_prepare_read(d->display); 631 } 632 633 if (ret == 0) { 634 ret = WAYLAND_wl_display_flush(d->display); 635 636 if (ret == -1 && errno == EAGAIN) { 637 // Unable to write to the socket; wait a brief time to see if it becomes writable. 638 ret = SDL_IOReady(display_fd, SDL_IOR_WRITE, SDL_MS_TO_NS(4)); 639 if (ret > 0) { 640 ret = WAYLAND_wl_display_flush(d->display); 641 } 642 } 643 644 // If the compositor closed the socket, just jump to the error handler. 645 if (ret < 0 && errno == EPIPE) { 646 WAYLAND_wl_display_cancel_read(d->display); 647 goto connection_error; 648 } 649 650 ret = SDL_IOReady(display_fd, SDL_IOR_READ, 0); 651 if (ret > 0) { 652 ret = WAYLAND_wl_display_read_events(d->display); 653 if (ret == 0) { 654 ret = WAYLAND_wl_display_dispatch_pending(d->display); 655 } 656 } else { 657 WAYLAND_wl_display_cancel_read(d->display); 658 } 659 660 } else { 661 ret = WAYLAND_wl_display_dispatch_pending(d->display); 662 } 663 664 if (ret >= 0) { 665 // Synthesize key repeat events. 666 wl_list_for_each (seat, &d->seat_list, link) { 667 if (keyboard_repeat_is_set(&seat->keyboard.repeat)) { 668 Wayland_SeatSetKeymap(seat); 669 670 const Uint64 elapsed = SDL_GetTicksNS() - seat->keyboard.repeat.sdl_press_time_ns; 671 keyboard_repeat_handle(&seat->keyboard.repeat, elapsed); 672 } 673 } 674 } 675 676connection_error: 677 if (ret < 0) { 678 Wayland_HandleDisplayDisconnected(_this); 679 } 680} 681 682static void pointer_dispatch_absolute_motion(SDL_WaylandSeat *seat) 683{ 684 SDL_WindowData *window_data = seat->pointer.focus; 685 SDL_Window *window = window_data ? window_data->sdlwindow : NULL; 686 687 if (window_data) { 688 const float sx = (float)(wl_fixed_to_double(seat->pointer.pending_frame.absolute.sx) * window_data->pointer_scale.x); 689 const float sy = (float)(wl_fixed_to_double(seat->pointer.pending_frame.absolute.sy) * window_data->pointer_scale.y); 690 SDL_SendMouseMotion(seat->pointer.pending_frame.timestamp_ns, window_data->sdlwindow, seat->pointer.sdl_id, false, sx, sy); 691 692 seat->pointer.last_motion.x = (int)SDL_floorf(sx); 693 seat->pointer.last_motion.y = (int)SDL_floorf(sy); 694 695 // If the pointer should be confined, but wasn't for some reason, keep trying until it is. 696 if (!SDL_RectEmpty(&window->mouse_rect) && !seat->pointer.is_confined) { 697 Wayland_SeatUpdatePointerGrab(seat); 698 } 699 700 if (window->hit_test) { 701 SDL_HitTestResult rc = window->hit_test(window, &seat->pointer.last_motion, window->hit_test_data); 702 703 // Apply the toplevel constraints if the window isn't resizable from those directions. 704 switch (rc) { 705 case SDL_HITTEST_RESIZE_TOPLEFT: 706 if ((window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_TOP) && 707 (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_LEFT)) { 708 rc = SDL_HITTEST_NORMAL; 709 } else if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_TOP) { 710 rc = SDL_HITTEST_RESIZE_LEFT; 711 } else if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_LEFT) { 712 rc = SDL_HITTEST_RESIZE_TOP; 713 } 714 break; 715 case SDL_HITTEST_RESIZE_TOP: 716 if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_TOP) { 717 rc = SDL_HITTEST_NORMAL; 718 } 719 break; 720 case SDL_HITTEST_RESIZE_TOPRIGHT: 721 if ((window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_TOP) && 722 (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_RIGHT)) { 723 rc = SDL_HITTEST_NORMAL; 724 } else if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_TOP) { 725 rc = SDL_HITTEST_RESIZE_RIGHT; 726 } else if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_RIGHT) { 727 rc = SDL_HITTEST_RESIZE_TOP; 728 } 729 break; 730 case SDL_HITTEST_RESIZE_RIGHT: 731 if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_RIGHT) { 732 rc = SDL_HITTEST_NORMAL; 733 } 734 break; 735 case SDL_HITTEST_RESIZE_BOTTOMRIGHT: 736 if ((window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_BOTTOM) && 737 (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_RIGHT)) { 738 rc = SDL_HITTEST_NORMAL; 739 } else if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_BOTTOM) { 740 rc = SDL_HITTEST_RESIZE_RIGHT; 741 } else if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_RIGHT) { 742 rc = SDL_HITTEST_RESIZE_BOTTOM; 743 } 744 break; 745 case SDL_HITTEST_RESIZE_BOTTOM: 746 if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_BOTTOM) { 747 rc = SDL_HITTEST_NORMAL; 748 } 749 break; 750 case SDL_HITTEST_RESIZE_BOTTOMLEFT: 751 if ((window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_BOTTOM) && 752 (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_LEFT)) { 753 rc = SDL_HITTEST_NORMAL; 754 } else if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_BOTTOM) { 755 rc = SDL_HITTEST_RESIZE_LEFT; 756 } else if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_LEFT) { 757 rc = SDL_HITTEST_RESIZE_BOTTOM; 758 } 759 break; 760 case SDL_HITTEST_RESIZE_LEFT: 761 if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_LEFT) { 762 rc = SDL_HITTEST_NORMAL; 763 } 764 break; 765 default: 766 break; 767 } 768 769 if (rc != window_data->hit_test_result) { 770 window_data->hit_test_result = rc; 771 Wayland_SeatUpdatePointerCursor(seat); 772 } 773 } 774 } 775} 776 777static void pointer_handle_motion(void *data, struct wl_pointer *pointer, 778 uint32_t time, wl_fixed_t sx, wl_fixed_t sy) 779{ 780 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 781 const Uint64 timestamp = Wayland_GetPointerTimestamp(seat, time); 782 783 seat->pointer.pending_frame.absolute.sx = sx; 784 seat->pointer.pending_frame.absolute.sy = sy; 785 786 if (wl_pointer_get_version(seat->pointer.wl_pointer) >= WL_POINTER_FRAME_SINCE_VERSION) { 787 seat->pointer.pending_frame.have_absolute = true; 788 789 /* The relative pointer timestamp is higher resolution than the default millisecond timestamp, 790 * but lower than the highres timestamp. Use the best timer available for this frame, 791 */ 792 if (!seat->pointer.pending_frame.have_relative || seat->pointer.timestamps) { 793 seat->pointer.pending_frame.timestamp_ns = timestamp; 794 } 795 } else { 796 seat->pointer.pending_frame.timestamp_ns = timestamp; 797 pointer_dispatch_absolute_motion(seat); 798 } 799} 800 801static void pointer_dispatch_enter(SDL_WaylandSeat *seat) 802{ 803 SDL_WindowData *window = Wayland_GetWindowDataForOwnedSurface(seat->pointer.pending_frame.enter_surface); 804 if (!window) { 805 // Entering a surface not managed by SDL; just set the cursor reset flag. 806 Wayland_SeatResetCursor(seat); 807 return; 808 } 809 810 if (window->surface != seat->pointer.pending_frame.enter_surface) { 811 /* This surface is part of the window managed by SDL, but it is not the main content 812 * surface and doesn't get focus. Just set the default cursor and leave. 813 */ 814 Wayland_SeatSetDefaultCursor(seat); 815 return; 816 } 817 818 seat->pointer.focus = window; 819 ++window->pointer_focus_count; 820 SDL_SetMouseFocus(window->sdlwindow); 821 822 // Send the initial position. 823 pointer_dispatch_absolute_motion(seat); 824 825 // Update the pointer grab state. 826 Wayland_SeatUpdatePointerGrab(seat); 827 828 /* If the cursor was changed while our window didn't have pointer 829 * focus, we might need to trigger another call to 830 * wl_pointer_set_cursor() for the new cursor to be displayed. 831 * 832 * This will also update the cursor if a second pointer entered a 833 * window that already has focus, as the focus change sequence 834 * won't be run. 835 */ 836 Wayland_SeatUpdatePointerCursor(seat); 837} 838 839static void pointer_handle_enter(void *data, struct wl_pointer *pointer, 840 uint32_t serial, struct wl_surface *surface, 841 wl_fixed_t sx_w, wl_fixed_t sy_w) 842{ 843 if (!surface) { 844 // Enter event for a destroyed surface. 845 return; 846 } 847 848 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 849 seat->pointer.pending_frame.enter_surface = surface; 850 seat->pointer.enter_serial = serial; 851 852 /* In the case of e.g. a pointer confine warp, we may receive an enter 853 * event with no following motion event, but with the new coordinates 854 * as part of the enter event. 855 */ 856 seat->pointer.pending_frame.absolute.sx = sx_w; 857 seat->pointer.pending_frame.absolute.sy = sy_w; 858 859 if (wl_pointer_get_version(seat->pointer.wl_pointer) < WL_POINTER_FRAME_SINCE_VERSION) { 860 // Dispatching an enter event generates an absolute motion event, for which there is no timestamp. 861 seat->pointer.pending_frame.timestamp_ns = 0; 862 pointer_dispatch_enter(seat); 863 } 864} 865 866static void pointer_dispatch_leave(SDL_WaylandSeat *seat, bool update_pointer) 867{ 868 SDL_WindowData *window = Wayland_GetWindowDataForOwnedSurface(seat->pointer.pending_frame.leave_surface); 869 870 if (window) { 871 if (seat->pointer.focus) { 872 if (seat->pointer.focus->surface == seat->pointer.pending_frame.leave_surface) { 873 // Clear the capture flag and raise all buttons 874 window->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE; 875 876 seat->pointer.focus = NULL; 877 for (Uint8 i = 1; seat->pointer.buttons_pressed; ++i) { 878 if (seat->pointer.buttons_pressed & SDL_BUTTON_MASK(i)) { 879 SDL_SendMouseButton(0, window->sdlwindow, seat->pointer.sdl_id, i, false); 880 seat->pointer.buttons_pressed &= ~SDL_BUTTON_MASK(i); 881 } 882 } 883 884 /* A pointer leave event may be emitted if the compositor hides the pointer in response to receiving a touch event. 885 * Don't relinquish focus if the surface has active touches, as the compositor is just transitioning from mouse to touch mode. 886 */ 887 SDL_Window *mouse_focus = SDL_GetMouseFocus(); 888 const bool had_focus = mouse_focus && window->sdlwindow == mouse_focus; 889 if (!--window->pointer_focus_count && had_focus && !window->active_touch_count) { 890 SDL_SetMouseFocus(NULL); 891 } 892 893 if (update_pointer) { 894 Wayland_SeatUpdatePointerGrab(seat); 895 Wayland_SeatUpdatePointerCursor(seat); 896 } 897 } 898 } else if (update_pointer) { 899 // Leaving a non-content surface managed by SDL; just set the cursor reset flag. 900 Wayland_SeatResetCursor(seat); 901 } 902 } 903} 904 905static void pointer_handle_leave(void *data, struct wl_pointer *pointer, 906 uint32_t serial, struct wl_surface *surface) 907{ 908 if (!surface) { 909 // Leave event for a destroyed surface. 910 return; 911 } 912 913 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 914 seat->pointer.pending_frame.leave_surface = surface; 915 if (wl_pointer_get_version(seat->pointer.wl_pointer) < WL_POINTER_FRAME_SINCE_VERSION) { 916 pointer_dispatch_leave(seat, true); 917 } 918} 919 920static bool Wayland_ProcessHitTest(SDL_WaylandSeat *seat, Uint32 serial) 921{ 922 // Pointer is immobilized, do nothing. 923 if (seat->pointer.locked_pointer) { 924 return false; 925 } 926 927 SDL_WindowData *window_data = seat->pointer.focus; 928 SDL_Window *window = window_data->sdlwindow; 929 930 if (window->hit_test) { 931 static const uint32_t directions[] = { 932 XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT, XDG_TOPLEVEL_RESIZE_EDGE_TOP, 933 XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT, XDG_TOPLEVEL_RESIZE_EDGE_RIGHT, 934 XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT, XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM, 935 XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT, XDG_TOPLEVEL_RESIZE_EDGE_LEFT 936 }; 937 938#ifdef HAVE_LIBDECOR_H 939 static const uint32_t directions_libdecor[] = { 940 LIBDECOR_RESIZE_EDGE_TOP_LEFT, LIBDECOR_RESIZE_EDGE_TOP, 941 LIBDECOR_RESIZE_EDGE_TOP_RIGHT, LIBDECOR_RESIZE_EDGE_RIGHT, 942 LIBDECOR_RESIZE_EDGE_BOTTOM_RIGHT, LIBDECOR_RESIZE_EDGE_BOTTOM, 943 LIBDECOR_RESIZE_EDGE_BOTTOM_LEFT, LIBDECOR_RESIZE_EDGE_LEFT 944 }; 945#endif 946 947 switch (window_data->hit_test_result) { 948 case SDL_HITTEST_DRAGGABLE: 949#ifdef HAVE_LIBDECOR_H 950 if (window_data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) { 951 if (window_data->shell_surface.libdecor.frame) { 952 libdecor_frame_move(window_data->shell_surface.libdecor.frame, 953 seat->wl_seat, 954 serial); 955 } 956 } else 957#endif 958 if (window_data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) { 959 if (window_data->shell_surface.xdg.toplevel.xdg_toplevel) { 960 xdg_toplevel_move(window_data->shell_surface.xdg.toplevel.xdg_toplevel, 961 seat->wl_seat, 962 serial); 963 } 964 } 965 return true; 966 967 case SDL_HITTEST_RESIZE_TOPLEFT: 968 case SDL_HITTEST_RESIZE_TOP: 969 case SDL_HITTEST_RESIZE_TOPRIGHT: 970 case SDL_HITTEST_RESIZE_RIGHT: 971 case SDL_HITTEST_RESIZE_BOTTOMRIGHT: 972 case SDL_HITTEST_RESIZE_BOTTOM: 973 case SDL_HITTEST_RESIZE_BOTTOMLEFT: 974 case SDL_HITTEST_RESIZE_LEFT: 975#ifdef HAVE_LIBDECOR_H 976 if (window_data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) { 977 if (window_data->shell_surface.libdecor.frame) { 978 libdecor_frame_resize(window_data->shell_surface.libdecor.frame, 979 seat->wl_seat, 980 serial, 981 directions_libdecor[window_data->hit_test_result - SDL_HITTEST_RESIZE_TOPLEFT]); 982 } 983 } else 984#endif 985 if (window_data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) { 986 if (window_data->shell_surface.xdg.toplevel.xdg_toplevel) { 987 xdg_toplevel_resize(window_data->shell_surface.xdg.toplevel.xdg_toplevel, 988 seat->wl_seat, 989 serial, 990 directions[window_data->hit_test_result - SDL_HITTEST_RESIZE_TOPLEFT]); 991 } 992 } 993 return true; 994 995 default: 996 return false; 997 } 998 } 999 1000 return false; 1001} 1002 1003static void pointer_dispatch_button(SDL_WaylandSeat *seat, Uint8 sdl_button, bool down) 1004{ 1005 SDL_WindowData *window = seat->pointer.focus; 1006 1007 if (window) { 1008 bool ignore_click = false; 1009 1010 if (down) { 1011 seat->pointer.buttons_pressed |= SDL_BUTTON_MASK(sdl_button); 1012 } else { 1013 seat->pointer.buttons_pressed &= ~SDL_BUTTON_MASK(sdl_button); 1014 } 1015 1016 if (sdl_button == SDL_BUTTON_LEFT && Wayland_ProcessHitTest(seat, seat->last_implicit_grab_serial)) { 1017 return; // don't pass this event on to app. 1018 } 1019 1020 // Possibly ignore this click if it was to gain focus. 1021 if (window->last_focus_event_time_ns) { 1022 if (down && (SDL_GetTicksNS() - window->last_focus_event_time_ns) < WAYLAND_FOCUS_CLICK_TIMEOUT_NS) { 1023 ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, false); 1024 } 1025 1026 window->last_focus_event_time_ns = 0; 1027 } 1028 1029 /* Wayland won't let you "capture" the mouse, but it will automatically track 1030 * the mouse outside the window if you drag outside of it, until you let go 1031 * of all buttons (even if you add or remove presses outside the window, as 1032 * long as any button is still down, the capture remains). 1033 * 1034 * The mouse is not captured in relative mode. 1035 */ 1036 if (!seat->pointer.relative_pointer) { 1037 if (seat->pointer.buttons_pressed != 0) { 1038 window->sdlwindow->flags |= SDL_WINDOW_MOUSE_CAPTURE; 1039 } else { 1040 window->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE; 1041 } 1042 } 1043 1044 if (!ignore_click) { 1045 SDL_SendMouseButton(seat->pointer.pending_frame.timestamp_ns, window->sdlwindow, seat->pointer.sdl_id, sdl_button, down); 1046 } 1047 } 1048} 1049 1050static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial, 1051 uint32_t time, uint32_t button, uint32_t state_w) 1052{ 1053 SDL_WaylandSeat *seat = data; 1054 Uint8 sdl_button; 1055 1056 switch (button) { 1057 case BTN_LEFT: 1058 sdl_button = SDL_BUTTON_LEFT; 1059 break; 1060 case BTN_MIDDLE: 1061 sdl_button = SDL_BUTTON_MIDDLE; 1062 break; 1063 case BTN_RIGHT: 1064 sdl_button = SDL_BUTTON_RIGHT; 1065 break; 1066 default: 1067 sdl_button = SDL_BUTTON_X1 + (button - BTN_SIDE); 1068 break; 1069 } 1070 1071 if (state_w) { 1072 Wayland_UpdateImplicitGrabSerial(seat, serial); 1073 } 1074 1075 seat->pointer.pending_frame.timestamp_ns = Wayland_GetPointerTimestamp(seat, time); 1076 1077 if (wl_seat_get_version(seat->wl_seat) >= WL_POINTER_FRAME_SINCE_VERSION) { 1078 if (state_w) { 1079 seat->pointer.pending_frame.buttons_pressed |= SDL_BUTTON_MASK(sdl_button); 1080 } else { 1081 seat->pointer.pending_frame.buttons_released |= SDL_BUTTON_MASK(sdl_button); 1082 } 1083 } else { 1084 pointer_dispatch_button(seat, sdl_button, state_w != 0); 1085 } 1086} 1087 1088static void pointer_handle_axis_common_v1(SDL_WaylandSeat *seat, 1089 Uint64 nsTimestamp, uint32_t axis, wl_fixed_t value) 1090{ 1091 SDL_WindowData *window = seat->pointer.focus; 1092 const enum wl_pointer_axis a = axis; 1093 1094 if (seat->pointer.focus) { 1095 float x, y; 1096 1097 switch (a) { 1098 case WL_POINTER_AXIS_VERTICAL_SCROLL: 1099 x = 0; 1100 y = 0 - (float)wl_fixed_to_double(value); 1101 break; 1102 case WL_POINTER_AXIS_HORIZONTAL_SCROLL: 1103 x = (float)wl_fixed_to_double(value); 1104 y = 0; 1105 break; 1106 default: 1107 return; 1108 } 1109 1110 x /= WAYLAND_WHEEL_AXIS_UNIT; 1111 y /= WAYLAND_WHEEL_AXIS_UNIT; 1112 1113 SDL_SendMouseWheel(nsTimestamp, window->sdlwindow, seat->pointer.sdl_id, x, y, SDL_MOUSEWHEEL_NORMAL); 1114 } 1115} 1116 1117static void pointer_handle_axis_common(SDL_WaylandSeat *seat, enum SDL_WaylandAxisEvent type, 1118 uint32_t axis, wl_fixed_t value) 1119{ 1120 const enum wl_pointer_axis a = axis; 1121 1122 if (seat->pointer.focus) { 1123 seat->pointer.pending_frame.have_axis = true; 1124 1125 switch (a) { 1126 case WL_POINTER_AXIS_VERTICAL_SCROLL: 1127 switch (type) { 1128 case SDL_WAYLAND_AXIS_EVENT_VALUE120: 1129 /* 1130 * High resolution scroll event. The spec doesn't state that axis_value120 1131 * events are limited to one per frame, so the values are accumulated. 1132 */ 1133 if (seat->pointer.pending_frame.axis.y_axis_type != SDL_WAYLAND_AXIS_EVENT_VALUE120) { 1134 seat->pointer.pending_frame.axis.y_axis_type = SDL_WAYLAND_AXIS_EVENT_VALUE120; 1135 seat->pointer.pending_frame.axis.y = 0.0f; 1136 } 1137 seat->pointer.pending_frame.axis.y += 0 - (float)wl_fixed_to_double(value); 1138 break; 1139 case SDL_WAYLAND_AXIS_EVENT_DISCRETE: 1140 /* 1141 * This is a discrete axis event, so we process it and set the 1142 * flag to ignore future continuous axis events in this frame. 1143 */ 1144 if (seat->pointer.pending_frame.axis.y_axis_type != SDL_WAYLAND_AXIS_EVENT_DISCRETE) { 1145 seat->pointer.pending_frame.axis.y_axis_type = SDL_WAYLAND_AXIS_EVENT_DISCRETE; 1146 seat->pointer.pending_frame.axis.y = 0 - (float)wl_fixed_to_double(value); 1147 } 1148 break; 1149 case SDL_WAYLAND_AXIS_EVENT_CONTINUOUS: 1150 // Only process continuous events if no discrete events have been received. 1151 if (seat->pointer.pending_frame.axis.y_axis_type == SDL_WAYLAND_AXIS_EVENT_CONTINUOUS) { 1152 seat->pointer.pending_frame.axis.y = 0 - (float)wl_fixed_to_double(value); 1153 } 1154 break; 1155 } 1156 break; 1157 case WL_POINTER_AXIS_HORIZONTAL_SCROLL: 1158 switch (type) { 1159 case SDL_WAYLAND_AXIS_EVENT_VALUE120: 1160 /* 1161 * High resolution scroll event. The spec doesn't state that axis_value120 1162 * events are limited to one per frame, so the values are accumulated. 1163 */ 1164 if (seat->pointer.pending_frame.axis.x_axis_type != SDL_WAYLAND_AXIS_EVENT_VALUE120) { 1165 seat->pointer.pending_frame.axis.x_axis_type = SDL_WAYLAND_AXIS_EVENT_VALUE120; 1166 seat->pointer.pending_frame.axis.x = 0.0f; 1167 } 1168 seat->pointer.pending_frame.axis.x += (float)wl_fixed_to_double(value); 1169 break; 1170 case SDL_WAYLAND_AXIS_EVENT_DISCRETE: 1171 /* 1172 * This is a discrete axis event, so we process it and set the 1173 * flag to ignore future continuous axis events in this frame. 1174 */ 1175 if (seat->pointer.pending_frame.axis.x_axis_type != SDL_WAYLAND_AXIS_EVENT_DISCRETE) { 1176 seat->pointer.pending_frame.axis.x_axis_type = SDL_WAYLAND_AXIS_EVENT_DISCRETE; 1177 seat->pointer.pending_frame.axis.x = (float)wl_fixed_to_double(value); 1178 } 1179 break; 1180 case SDL_WAYLAND_AXIS_EVENT_CONTINUOUS: 1181 // Only process continuous events if no discrete events have been received. 1182 if (seat->pointer.pending_frame.axis.x_axis_type == SDL_WAYLAND_AXIS_EVENT_CONTINUOUS) { 1183 seat->pointer.pending_frame.axis.x = (float)wl_fixed_to_double(value); 1184 } 1185 break; 1186 } 1187 break; 1188 } 1189 } 1190} 1191 1192static void pointer_handle_axis(void *data, struct wl_pointer *pointer, 1193 uint32_t time, uint32_t axis, wl_fixed_t value) 1194{ 1195 SDL_WaylandSeat *seat = data; 1196 const Uint64 nsTimestamp = Wayland_GetPointerTimestamp(seat, time); 1197 1198 if (wl_seat_get_version(seat->wl_seat) >= WL_POINTER_FRAME_SINCE_VERSION) { 1199 seat->pointer.pending_frame.timestamp_ns = nsTimestamp; 1200 pointer_handle_axis_common(seat, SDL_WAYLAND_AXIS_EVENT_CONTINUOUS, axis, value); 1201 } else { 1202 pointer_handle_axis_common_v1(seat, nsTimestamp, axis, value); 1203 } 1204} 1205 1206static void pointer_handle_axis_relative_direction(void *data, struct wl_pointer *pointer, 1207 uint32_t axis, uint32_t axis_relative_direction) 1208{ 1209 SDL_WaylandSeat *seat = data; 1210 if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) { 1211 return; 1212 } 1213 switch (axis_relative_direction) { 1214 case WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL: 1215 seat->pointer.pending_frame.axis.direction = SDL_MOUSEWHEEL_NORMAL; 1216 break; 1217 case WL_POINTER_AXIS_RELATIVE_DIRECTION_INVERTED: 1218 seat->pointer.pending_frame.axis.direction = SDL_MOUSEWHEEL_FLIPPED; 1219 break; 1220 } 1221} 1222 1223static void pointer_dispatch_relative_motion(SDL_WaylandSeat *seat) 1224{ 1225 SDL_WindowData *window = seat->pointer.focus; 1226 1227 if (window) { 1228 SDL_Mouse *mouse = SDL_GetMouse(); 1229 double dx; 1230 double dy; 1231 if (mouse->InputTransform || !mouse->enable_relative_system_scale) { 1232 dx = wl_fixed_to_double(seat->pointer.pending_frame.relative.dx_unaccel); 1233 dy = wl_fixed_to_double(seat->pointer.pending_frame.relative.dy_unaccel); 1234 } else { 1235 dx = wl_fixed_to_double(seat->pointer.pending_frame.relative.dx) * window->pointer_scale.x; 1236 dy = wl_fixed_to_double(seat->pointer.pending_frame.relative.dy) * window->pointer_scale.y; 1237 } 1238 1239 SDL_SendMouseMotion(seat->pointer.pending_frame.timestamp_ns, window->sdlwindow, seat->pointer.sdl_id, true, (float)dx, (float)dy); 1240 } 1241} 1242 1243static void pointer_dispatch_axis(SDL_WaylandSeat *seat) 1244{ 1245 float x, y; 1246 SDL_MouseWheelDirection direction = seat->pointer.pending_frame.axis.direction; 1247 1248 switch (seat->pointer.pending_frame.axis.x_axis_type) { 1249 case SDL_WAYLAND_AXIS_EVENT_CONTINUOUS: 1250 x = seat->pointer.pending_frame.axis.x / WAYLAND_WHEEL_AXIS_UNIT; 1251 break; 1252 case SDL_WAYLAND_AXIS_EVENT_DISCRETE: 1253 x = seat->pointer.pending_frame.axis.x; 1254 break; 1255 case SDL_WAYLAND_AXIS_EVENT_VALUE120: 1256 x = seat->pointer.pending_frame.axis.x / 120.0f; 1257 break; 1258 default: 1259 x = 0.0f; 1260 break; 1261 } 1262 1263 switch (seat->pointer.pending_frame.axis.y_axis_type) { 1264 case SDL_WAYLAND_AXIS_EVENT_CONTINUOUS: 1265 y = seat->pointer.pending_frame.axis.y / WAYLAND_WHEEL_AXIS_UNIT; 1266 break; 1267 case SDL_WAYLAND_AXIS_EVENT_DISCRETE: 1268 y = seat->pointer.pending_frame.axis.y; 1269 break; 1270 case SDL_WAYLAND_AXIS_EVENT_VALUE120: 1271 y = seat->pointer.pending_frame.axis.y / 120.0f; 1272 break; 1273 default: 1274 y = 0.0f; 1275 break; 1276 } 1277 1278 SDL_SendMouseWheel(seat->pointer.pending_frame.timestamp_ns, 1279 seat->pointer.focus->sdlwindow, seat->pointer.sdl_id, x, y, direction); 1280} 1281 1282static void pointer_handle_frame(void *data, struct wl_pointer *pointer) 1283{ 1284 SDL_WaylandSeat *seat = data; 1285 1286 if (seat->pointer.pending_frame.enter_surface) { 1287 if (seat->pointer.pending_frame.leave_surface) { 1288 // Leaving the previous surface before entering a new surface. 1289 pointer_dispatch_leave(seat, false); 1290 seat->pointer.pending_frame.leave_surface = NULL; 1291 } 1292 1293 pointer_dispatch_enter(seat); 1294 } 1295 1296 if (seat->pointer.pending_frame.have_absolute) { 1297 pointer_dispatch_absolute_motion(seat); 1298 } 1299 1300 if (seat->pointer.pending_frame.have_relative) { 1301 pointer_dispatch_relative_motion(seat); 1302 } 1303 1304 for (Uint8 i = 1; seat->pointer.pending_frame.buttons_pressed || seat->pointer.pending_frame.buttons_released; ++i) { 1305 const Uint32 mask = SDL_BUTTON_MASK(i); 1306 if (seat->pointer.pending_frame.buttons_pressed & mask) { 1307 pointer_dispatch_button(seat, i, true); 1308 seat->pointer.pending_frame.buttons_pressed &= ~mask; 1309 } 1310 if (seat->pointer.pending_frame.buttons_released & mask) { 1311 pointer_dispatch_button(seat, i, false); 1312 seat->pointer.pending_frame.buttons_released &= ~mask; 1313 } 1314 } 1315 1316 if (seat->pointer.pending_frame.have_axis) { 1317 pointer_dispatch_axis(seat); 1318 } 1319 1320 if (seat->pointer.pending_frame.leave_surface) { 1321 pointer_dispatch_leave(seat, true); 1322 } 1323 1324 SDL_zero(seat->pointer.pending_frame); 1325} 1326 1327static void pointer_handle_axis_source(void *data, struct wl_pointer *pointer, 1328 uint32_t axis_source) 1329{ 1330 // unimplemented 1331} 1332 1333static void pointer_handle_axis_stop(void *data, struct wl_pointer *pointer, 1334 uint32_t time, uint32_t axis) 1335{ 1336 // unimplemented 1337} 1338 1339static void pointer_handle_axis_discrete(void *data, struct wl_pointer *pointer, 1340 uint32_t axis, int32_t discrete) 1341{ 1342 SDL_WaylandSeat *seat = data; 1343 pointer_handle_axis_common(seat, SDL_WAYLAND_AXIS_EVENT_DISCRETE, axis, wl_fixed_from_int(discrete)); 1344} 1345 1346static void pointer_handle_axis_value120(void *data, struct wl_pointer *pointer, 1347 uint32_t axis, int32_t value120) 1348{ 1349 SDL_WaylandSeat *seat = data; 1350 pointer_handle_axis_common(seat, SDL_WAYLAND_AXIS_EVENT_VALUE120, axis, wl_fixed_from_int(value120)); 1351} 1352 1353static const struct wl_pointer_listener pointer_listener = { 1354 pointer_handle_enter, 1355 pointer_handle_leave, 1356 pointer_handle_motion, 1357 pointer_handle_button, 1358 pointer_handle_axis, 1359 pointer_handle_frame, // Version 5 1360 pointer_handle_axis_source, // Version 5 1361 pointer_handle_axis_stop, // Version 5 1362 pointer_handle_axis_discrete, // Version 5 1363 pointer_handle_axis_value120, // Version 8 1364 pointer_handle_axis_relative_direction // Version 9 1365}; 1366 1367static void relative_pointer_handle_relative_motion(void *data, 1368 struct zwp_relative_pointer_v1 *pointer, 1369 uint32_t time_hi, 1370 uint32_t time_lo, 1371 wl_fixed_t dx, 1372 wl_fixed_t dy, 1373 wl_fixed_t dx_unaccel, 1374 wl_fixed_t dy_unaccel) 1375{ 1376 SDL_WaylandSeat *seat = data; 1377 1378 // Relative pointer event times are in microsecond granularity. 1379 seat->pointer.pending_frame.relative.dx = dx; 1380 seat->pointer.pending_frame.relative.dy = dy; 1381 seat->pointer.pending_frame.relative.dx_unaccel = dx_unaccel; 1382 seat->pointer.pending_frame.relative.dy_unaccel = dy_unaccel; 1383 seat->pointer.pending_frame.timestamp_ns = Wayland_AdjustEventTimestampBase(SDL_US_TO_NS(((Uint64)time_hi << 32) | (Uint64)time_lo)); 1384 1385 if (wl_pointer_get_version(seat->pointer.wl_pointer) >= WL_POINTER_FRAME_SINCE_VERSION) { 1386 seat->pointer.pending_frame.have_relative = true; 1387 } else { 1388 pointer_dispatch_relative_motion(seat); 1389 } 1390} 1391 1392static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = { 1393 relative_pointer_handle_relative_motion, 1394}; 1395 1396static void locked_pointer_locked(void *data, struct zwp_locked_pointer_v1 *locked_pointer) 1397{ 1398 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 1399 seat->pointer.is_confined = true; 1400} 1401 1402static void locked_pointer_unlocked(void *data, struct zwp_locked_pointer_v1 *locked_pointer) 1403{ 1404 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 1405 seat->pointer.is_confined = false; 1406} 1407 1408static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = { 1409 locked_pointer_locked, 1410 locked_pointer_unlocked, 1411}; 1412 1413static void confined_pointer_confined(void *data, struct zwp_confined_pointer_v1 *confined_pointer) 1414{ 1415 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 1416 seat->pointer.is_confined = true; 1417} 1418 1419static void confined_pointer_unconfined(void *data, struct zwp_confined_pointer_v1 *confined_pointer) 1420{ 1421 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 1422 seat->pointer.is_confined = false; 1423} 1424 1425static const struct zwp_confined_pointer_v1_listener confined_pointer_listener = { 1426 confined_pointer_confined, 1427 confined_pointer_unconfined, 1428}; 1429 1430static void touch_handler_down(void *data, struct wl_touch *touch, uint32_t serial, 1431 uint32_t timestamp, struct wl_surface *surface, 1432 int id, wl_fixed_t fx, wl_fixed_t fy) 1433{ 1434 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 1435 SDL_WindowData *window_data; 1436 1437 // Check that this surface is valid. 1438 if (!surface) { 1439 return; 1440 } 1441 1442 Wayland_SeatAddTouch(seat, id, fx, fy, surface); 1443 Wayland_UpdateImplicitGrabSerial(seat, serial); 1444 window_data = Wayland_GetWindowDataForOwnedSurface(surface); 1445 1446 if (window_data && window_data->surface == surface) { 1447 float x, y; 1448 1449 if (window_data->current.logical_width <= 1) { 1450 x = 0.5f; 1451 } else { 1452 x = (float)wl_fixed_to_double(fx) / (window_data->current.logical_width - 1); 1453 } 1454 if (window_data->current.logical_height <= 1) { 1455 y = 0.5f; 1456 } else { 1457 y = (float)wl_fixed_to_double(fy) / (window_data->current.logical_height - 1); 1458 } 1459 1460 ++window_data->active_touch_count; 1461 SDL_SetMouseFocus(window_data->sdlwindow); 1462 1463 SDL_SendTouch(Wayland_GetTouchTimestamp(seat, timestamp), (SDL_TouchID)(uintptr_t)touch, 1464 (SDL_FingerID)(id + 1), window_data->sdlwindow, SDL_EVENT_FINGER_DOWN, x, y, 1.0f); 1465 } 1466} 1467 1468static void touch_handler_up(void *data, struct wl_touch *touch, uint32_t serial, uint32_t timestamp, int id) 1469{ 1470 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 1471 wl_fixed_t fx = 0, fy = 0; 1472 struct wl_surface *surface = NULL; 1473 1474 Wayland_SeatRemoveTouch(seat, id, &fx, &fy, &surface); 1475 1476 if (surface) { 1477 SDL_WindowData *window_data = Wayland_GetWindowDataForOwnedSurface(surface); 1478 1479 if (window_data && window_data->surface == surface) { 1480 const float x = (float)wl_fixed_to_double(fx) / window_data->current.logical_width; 1481 const float y = (float)wl_fixed_to_double(fy) / window_data->current.logical_height; 1482 1483 SDL_SendTouch(Wayland_GetTouchTimestamp(seat, timestamp), (SDL_TouchID)(uintptr_t)touch, 1484 (SDL_FingerID)(id + 1), window_data->sdlwindow, SDL_EVENT_FINGER_UP, x, y, 0.0f); 1485 1486 --window_data->active_touch_count; 1487 1488 /* If the window currently has mouse focus and has no currently active keyboards, pointers, 1489 * or touch events, then consider mouse focus to be lost. 1490 */ 1491 if (SDL_GetMouseFocus() == window_data->sdlwindow && !window_data->keyboard_focus_count && 1492 !window_data->pointer_focus_count && !window_data->active_touch_count) { 1493 SDL_SetMouseFocus(NULL); 1494 } 1495 } 1496 } 1497} 1498 1499static void touch_handler_motion(void *data, struct wl_touch *touch, uint32_t timestamp, int id, wl_fixed_t fx, wl_fixed_t fy) 1500{ 1501 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 1502 struct wl_surface *surface = NULL; 1503 1504 Wayland_SeatUpdateTouch(seat, id, fx, fy, &surface); 1505 1506 if (surface) { 1507 SDL_WindowData *window_data = Wayland_GetWindowDataForOwnedSurface(surface); 1508 1509 if (window_data && window_data->surface == surface) { 1510 const float x = (float)wl_fixed_to_double(fx) / window_data->current.logical_width; 1511 const float y = (float)wl_fixed_to_double(fy) / window_data->current.logical_height; 1512 1513 SDL_SendTouchMotion(Wayland_GetTouchTimestamp(seat, timestamp), (SDL_TouchID)(uintptr_t)touch, 1514 (SDL_FingerID)(id + 1), window_data->sdlwindow, x, y, 1.0f); 1515 } 1516 } 1517} 1518 1519static void touch_handler_frame(void *data, struct wl_touch *touch) 1520{ 1521} 1522 1523static void touch_handler_cancel(void *data, struct wl_touch *touch) 1524{ 1525 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 1526 SDL_WaylandTouchPoint *tp, *temp; 1527 1528 // Need the safe loop variant here as cancelling a touch point removes it from the list. 1529 wl_list_for_each_safe (tp, temp, &seat->touch.points, link) { 1530 Wayland_SeatCancelTouch(seat, tp); 1531 } 1532} 1533 1534static void touch_handler_shape(void *data, struct wl_touch *wl_touch, int32_t id, wl_fixed_t major, wl_fixed_t minor) 1535{ 1536} 1537 1538static void touch_handler_orientation(void *data, struct wl_touch *wl_touch, int32_t id, wl_fixed_t orientation) 1539{ 1540} 1541 1542static const struct wl_touch_listener touch_listener = { 1543 touch_handler_down, 1544 touch_handler_up, 1545 touch_handler_motion, 1546 touch_handler_frame, 1547 touch_handler_cancel, 1548 touch_handler_shape, // Version 6 1549 touch_handler_orientation // Version 6 1550}; 1551 1552// Fallback for xkb_keymap_key_get_mods_for_level(), which is only available from 1.0.0, while the SDL minimum is 0.5.0. 1553#if !SDL_XKBCOMMON_CHECK_VERSION(1, 0, 0) 1554static size_t xkb_legacy_get_mods_for_level(SDL_WaylandSeat *seat, xkb_keycode_t key, xkb_layout_index_t layout, xkb_level_index_t level, xkb_mod_mask_t *masks_out, size_t masks_size) 1555{ 1556 if (!masks_out || !masks_size) { 1557 return 0; 1558 } 1559 1560 // Level 0 is always unmodified, so early out. 1561 if (level == 0) { 1562 *masks_out = 0; 1563 return 1; 1564 } 1565 1566 size_t mask_idx = 0; 1567 const xkb_mod_mask_t keymod_masks[] = { 1568 0, 1569 seat->keyboard.xkb.shift_mask, 1570 seat->keyboard.xkb.caps_mask, 1571 seat->keyboard.xkb.shift_mask | seat->keyboard.xkb.caps_mask, 1572 seat->keyboard.xkb.level3_mask, 1573 seat->keyboard.xkb.level3_mask | seat->keyboard.xkb.shift_mask, 1574 seat->keyboard.xkb.level3_mask | seat->keyboard.xkb.caps_mask, 1575 seat->keyboard.xkb.level3_mask | seat->keyboard.xkb.shift_mask | seat->keyboard.xkb.caps_mask, 1576 seat->keyboard.xkb.level5_mask, 1577 seat->keyboard.xkb.level5_mask | seat->keyboard.xkb.shift_mask, 1578 seat->keyboard.xkb.level5_mask | seat->keyboard.xkb.caps_mask, 1579 seat->keyboard.xkb.level5_mask | seat->keyboard.xkb.shift_mask | seat->keyboard.xkb.caps_mask 1580 }; 1581 const xkb_mod_mask_t pressed_mod_mask = seat->keyboard.xkb.shift_mask | seat->keyboard.xkb.level3_mask | seat->keyboard.xkb.level5_mask; 1582 const xkb_mod_mask_t locked_mod_mask = seat->keyboard.xkb.caps_mask; 1583 1584 for (size_t i = 0; i < SDL_arraysize(keymod_masks); ++i) { 1585 WAYLAND_xkb_state_update_mask(seat->keyboard.xkb.state, keymod_masks[i] & pressed_mod_mask, 0, keymod_masks[i] & locked_mod_mask, 0, 0, layout); 1586 if (WAYLAND_xkb_state_key_get_level(seat->keyboard.xkb.state, key, layout) == level) { 1587 masks_out[mask_idx] = keymod_masks[i]; 1588 1589 if (++mask_idx == masks_size) { 1590 break; 1591 } 1592 } 1593 } 1594 1595 return mask_idx; 1596} 1597#endif 1598 1599static void Wayland_KeymapIterator(struct xkb_keymap *keymap, xkb_keycode_t key, void *data) 1600{ 1601 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 1602 const xkb_keysym_t *syms; 1603 SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN; 1604 1605 // Only the shift, alt, level 3, level 5 and caps lock modifiers affect SDL keymaps. 1606 const xkb_mod_mask_t xkb_valid_mod_mask = seat->keyboard.xkb.shift_mask | 1607 seat->keyboard.xkb.alt_mask | 1608 seat->keyboard.xkb.level3_mask | 1609 seat->keyboard.xkb.level5_mask | 1610 seat->keyboard.xkb.caps_mask; 1611 1612 // Look up the scancode for hardware keyboards. Virtual keyboards get the scancode from the keysym. 1613 if (!seat->keyboard.is_virtual) { 1614 scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, (key - 8)); 1615 if (scancode == SDL_SCANCODE_UNKNOWN) { 1616 return; 1617 } 1618 } 1619 1620 for (xkb_layout_index_t layout = 0; layout < seat->keyboard.xkb.num_layouts; ++layout) { 1621 const xkb_level_index_t num_levels = WAYLAND_xkb_keymap_num_levels_for_key(seat->keyboard.xkb.keymap, key, layout); 1622 for (xkb_level_index_t level = 0; level < num_levels; ++level) { 1623 if (WAYLAND_xkb_keymap_key_get_syms_by_level(seat->keyboard.xkb.keymap, key, layout, level, &syms) > 0) { 1624 /* If the keyboard is virtual or the key didn't have a corresponding hardware scancode, try to 1625 * look it up from the keysym. If there is still no corresponding scancode, skip this mapping 1626 * for now, as it will be dynamically added with a reserved scancode on first use. 1627 */ 1628 if (scancode == SDL_SCANCODE_UNKNOWN) { 1629 scancode = SDL_GetScancodeFromKeySym(syms[0], key); 1630 if (scancode == SDL_SCANCODE_UNKNOWN) { 1631 continue; 1632 } 1633 } 1634 1635 xkb_mod_mask_t xkb_mod_masks[16]; 1636#if SDL_XKBCOMMON_CHECK_VERSION(1, 0, 0) 1637 const size_t num_masks = WAYLAND_xkb_keymap_key_get_mods_for_level(seat->keyboard.xkb.keymap, key, layout, level, xkb_mod_masks, SDL_arraysize(xkb_mod_masks)); 1638#else 1639 const size_t num_masks = xkb_legacy_get_mods_for_level(seat, key, layout, level, xkb_mod_masks, SDL_arraysize(xkb_mod_masks)); 1640#endif 1641 for (size_t mask = 0; mask < num_masks; ++mask) { 1642 // Ignore this modifier set if it uses unsupported modifier types. 1643 if ((xkb_mod_masks[mask] | xkb_valid_mod_mask) != xkb_valid_mod_mask) { 1644 continue; 1645 } 1646 1647 const SDL_Keymod sdl_mod = (xkb_mod_masks[mask] & seat->keyboard.xkb.shift_mask ? SDL_KMOD_SHIFT : 0) | 1648 (xkb_mod_masks[mask] & seat->keyboard.xkb.alt_mask ? SDL_KMOD_ALT : 0) | 1649 (xkb_mod_masks[mask] & seat->keyboard.xkb.level3_mask ? SDL_KMOD_MODE : 0) | 1650 (xkb_mod_masks[mask] & seat->keyboard.xkb.level5_mask ? SDL_KMOD_LEVEL5 : 0) | 1651 (xkb_mod_masks[mask] & seat->keyboard.xkb.caps_mask ? SDL_KMOD_CAPS : 0); 1652 1653 SDL_Keycode keycode = SDL_GetKeyCodeFromKeySym(syms[0], key, sdl_mod); 1654 1655 if (!keycode) { 1656 switch (scancode) { 1657 case SDL_SCANCODE_RETURN: 1658 keycode = SDLK_RETURN; 1659 break; 1660 case SDL_SCANCODE_ESCAPE: 1661 keycode = SDLK_ESCAPE; 1662 break; 1663 case SDL_SCANCODE_BACKSPACE: 1664 keycode = SDLK_BACKSPACE; 1665 break; 1666 case SDL_SCANCODE_DELETE: 1667 keycode = SDLK_DELETE; 1668 break; 1669 default: 1670 keycode = SDL_SCANCODE_TO_KEYCODE(scancode); 1671 break; 1672 } 1673 } 1674 1675 SDL_SetKeymapEntry(seat->keyboard.sdl_keymap[layout], scancode, sdl_mod, keycode); 1676 } 1677 } 1678 } 1679 } 1680} 1681 1682static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, 1683 uint32_t format, int fd, uint32_t size) 1684{ 1685 SDL_WaylandSeat *seat = data; 1686 char *map_str; 1687 1688 if (!data) { 1689 close(fd); 1690 return; 1691 } 1692 1693 if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { 1694 close(fd); 1695 return; 1696 } 1697 1698 map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); 1699 if (map_str == MAP_FAILED) { 1700 close(fd); 1701 return; 1702 } 1703 1704 if (seat->keyboard.xkb.keymap != NULL) { 1705 /* if there's already a keymap loaded, throw it away rather than leaking it before 1706 * parsing the new one 1707 */ 1708 WAYLAND_xkb_keymap_unref(seat->keyboard.xkb.keymap); 1709 seat->keyboard.xkb.keymap = NULL; 1710 } 1711 seat->keyboard.xkb.keymap = WAYLAND_xkb_keymap_new_from_string(seat->display->xkb_context, 1712 map_str, 1713 XKB_KEYMAP_FORMAT_TEXT_V1, 1714 0); 1715 munmap(map_str, size); 1716 close(fd); 1717 1718 if (!seat->keyboard.xkb.keymap) { 1719 SDL_SetError("failed to compile keymap"); 1720 return; 1721 } 1722 1723 // Clear the old layouts. 1724 for (xkb_layout_index_t i = 0; i < seat->keyboard.xkb.num_layouts; ++i) { 1725 SDL_DestroyKeymap(seat->keyboard.sdl_keymap[i]); 1726 } 1727 SDL_free(seat->keyboard.sdl_keymap); 1728 seat->keyboard.sdl_keymap = NULL; 1729 seat->keyboard.xkb.num_layouts = 0; 1730 1731#if SDL_XKBCOMMON_CHECK_VERSION(1, 10, 0) 1732 seat->keyboard.xkb.shift_mask = WAYLAND_xkb_keymap_mod_get_mask(seat->keyboard.xkb.keymap, XKB_MOD_NAME_SHIFT); 1733 seat->keyboard.xkb.ctrl_mask = WAYLAND_xkb_keymap_mod_get_mask(seat->keyboard.xkb.keymap, XKB_MOD_NAME_CTRL); 1734 seat->keyboard.xkb.alt_mask = WAYLAND_xkb_keymap_mod_get_mask(seat->keyboard.xkb.keymap, XKB_VMOD_NAME_ALT); 1735 seat->keyboard.xkb.gui_mask = WAYLAND_xkb_keymap_mod_get_mask(seat->keyboard.xkb.keymap, XKB_VMOD_NAME_SUPER); 1736 seat->keyboard.xkb.level3_mask = WAYLAND_xkb_keymap_mod_get_mask(seat->keyboard.xkb.keymap, XKB_VMOD_NAME_LEVEL3); 1737 seat->keyboard.xkb.level5_mask = WAYLAND_xkb_keymap_mod_get_mask(seat->keyboard.xkb.keymap, XKB_VMOD_NAME_LEVEL5); 1738 seat->keyboard.xkb.num_mask = WAYLAND_xkb_keymap_mod_get_mask(seat->keyboard.xkb.keymap, XKB_VMOD_NAME_NUM); 1739 seat->keyboard.xkb.caps_mask = WAYLAND_xkb_keymap_mod_get_mask(seat->keyboard.xkb.keymap, XKB_MOD_NAME_CAPS); 1740#else 1741#define GET_MOD_INDEX(mod) \ 1742 WAYLAND_xkb_keymap_mod_get_index(seat->keyboard.xkb.keymap, XKB_MOD_NAME_##mod) 1743 seat->keyboard.xkb.shift_mask = 1 << GET_MOD_INDEX(SHIFT); 1744 seat->keyboard.xkb.ctrl_mask = 1 << GET_MOD_INDEX(CTRL); 1745 seat->keyboard.xkb.alt_mask = 1 << GET_MOD_INDEX(ALT); 1746 seat->keyboard.xkb.gui_mask = 1 << GET_MOD_INDEX(LOGO); 1747 // Note: This is correct: Mod3 is typically level 5 shift, and Mod5 is typically level 3 shift. 1748 seat->keyboard.xkb.level3_mask = 1 << GET_MOD_INDEX(MOD5); 1749 seat->keyboard.xkb.level5_mask = 1 << GET_MOD_INDEX(MOD3); 1750 seat->keyboard.xkb.num_mask = 1 << GET_MOD_INDEX(NUM); 1751 seat->keyboard.xkb.caps_mask = 1 << GET_MOD_INDEX(CAPS); 1752#undef GET_MOD_INDEX 1753#endif 1754 1755 if (seat->keyboard.xkb.state != NULL) { 1756 /* if there's already a state, throw it away rather than leaking it before 1757 * trying to create a new one with the new keymap. 1758 */ 1759 WAYLAND_xkb_state_unref(seat->keyboard.xkb.state); 1760 seat->keyboard.xkb.state = NULL; 1761 } 1762 seat->keyboard.xkb.state = WAYLAND_xkb_state_new(seat->keyboard.xkb.keymap); 1763 if (!seat->keyboard.xkb.state) { 1764 SDL_SetError("failed to create XKB state"); 1765 WAYLAND_xkb_keymap_unref(seat->keyboard.xkb.keymap); 1766 seat->keyboard.xkb.keymap = NULL; 1767 return; 1768 } 1769 1770 /* 1771 * Assume that a nameless layout implies a virtual keyboard with an arbitrary layout. 1772 * TODO: Use a better method of detection? 1773 */ 1774 seat->keyboard.is_virtual = WAYLAND_xkb_keymap_layout_get_name(seat->keyboard.xkb.keymap, 0) == NULL; 1775 1776 // Allocate and populate the new layout maps. 1777 seat->keyboard.xkb.num_layouts = WAYLAND_xkb_keymap_num_layouts(seat->keyboard.xkb.keymap); 1778 if (seat->keyboard.xkb.num_layouts) { 1779 seat->keyboard.sdl_keymap = SDL_calloc(seat->keyboard.xkb.num_layouts, sizeof(SDL_Keymap *)); 1780 if (!seat->keyboard.sdl_keymap) { 1781 return; 1782 } 1783 1784 for (xkb_layout_index_t i = 0; i < seat->keyboard.xkb.num_layouts; ++i) { 1785 seat->keyboard.sdl_keymap[i] = SDL_CreateKeymap(false); 1786 if (!seat->keyboard.sdl_keymap[i]) { 1787 for (xkb_layout_index_t j = 0; j < i; ++j) { 1788 SDL_DestroyKeymap(seat->keyboard.sdl_keymap[j]); 1789 } 1790 SDL_free(seat->keyboard.sdl_keymap); 1791 seat->keyboard.sdl_keymap = NULL; 1792 return; 1793 } 1794 } 1795 1796 WAYLAND_xkb_keymap_key_for_each(seat->keyboard.xkb.keymap, Wayland_KeymapIterator, seat); 1797 1798 // Restore any previously set modifier/layout information, if valid. 1799 WAYLAND_xkb_state_update_mask(seat->keyboard.xkb.state, 1800 seat->keyboard.xkb.wl_pressed_modifiers, seat->keyboard.xkb.wl_latched_modifiers, seat->keyboard.xkb.wl_locked_modifiers, 1801 0, 0, seat->keyboard.xkb.current_layout < seat->keyboard.xkb.num_layouts ? seat->keyboard.xkb.current_layout : 0); 1802 Wayland_SeatSetKeymap(seat); 1803 } 1804 1805 /* 1806 * See https://blogs.s-osg.org/compose-key-support-weston/ 1807 * for further explanation on dead keys in Wayland. 1808 */ 1809 1810 // Look up the preferred locale, falling back to "C" as default 1811 const char *locale = SDL_getenv("LC_ALL"); 1812 if (!locale) { 1813 locale = SDL_getenv("LC_CTYPE"); 1814 if (!locale) { 1815 locale = SDL_getenv("LANG"); 1816 if (!locale) { 1817 locale = "C"; 1818 } 1819 } 1820 } 1821 1822 /* Set up the XKB compose table. 1823 * 1824 * This is a very slow operation, so it is only done during initialization, 1825 * or if the locale envvar changed during runtime. 1826 */ 1827 if (!seat->keyboard.current_locale || SDL_strcmp(seat->keyboard.current_locale, locale) != 0) { 1828 // Cache the current locale for later comparison. 1829 SDL_free(seat->keyboard.current_locale); 1830 seat->keyboard.current_locale = SDL_strdup(locale); 1831 1832 if (seat->keyboard.xkb.compose_table != NULL) { 1833 WAYLAND_xkb_compose_table_unref(seat->keyboard.xkb.compose_table); 1834 seat->keyboard.xkb.compose_table = NULL; 1835 } 1836 seat->keyboard.xkb.compose_table = WAYLAND_xkb_compose_table_new_from_locale(seat->display->xkb_context, 1837 locale, XKB_COMPOSE_COMPILE_NO_FLAGS); 1838 if (seat->keyboard.xkb.compose_table) { 1839 // Set up XKB compose state 1840 if (seat->keyboard.xkb.compose_state != NULL) { 1841 WAYLAND_xkb_compose_state_unref(seat->keyboard.xkb.compose_state); 1842 seat->keyboard.xkb.compose_state = NULL; 1843 } 1844 seat->keyboard.xkb.compose_state = WAYLAND_xkb_compose_state_new(seat->keyboard.xkb.compose_table, 1845 XKB_COMPOSE_STATE_NO_FLAGS); 1846 if (!seat->keyboard.xkb.compose_state) { 1847 SDL_SetError("could not create XKB compose state"); 1848 WAYLAND_xkb_compose_table_unref(seat->keyboard.xkb.compose_table); 1849 seat->keyboard.xkb.compose_table = NULL; 1850 } 1851 } 1852 } else if (seat->keyboard.xkb.compose_state) { 1853 WAYLAND_xkb_compose_state_reset(seat->keyboard.xkb.compose_state); 1854 } 1855} 1856 1857/* 1858 * Virtual keyboards can have arbitrary layouts, arbitrary scancodes/keycodes, etc... 1859 * Key presses from these devices must be looked up by their keysym value. 1860 */ 1861static SDL_Scancode Wayland_GetScancodeForKey(SDL_WaylandSeat *seat, uint32_t key, const xkb_keysym_t **syms) 1862{ 1863 SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN; 1864 1865 if (!seat->keyboard.is_virtual) { 1866 scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, key); 1867 } 1868 if (scancode == SDL_SCANCODE_UNKNOWN) { 1869 const xkb_keysym_t *keysym; 1870 if (WAYLAND_xkb_state_key_get_syms(seat->keyboard.xkb.state, key + 8, &keysym) > 0) { 1871 scancode = SDL_GetScancodeFromKeySym(keysym[0], key + 8); 1872 if (syms) { 1873 *syms = keysym; 1874 } 1875 } 1876 } 1877 1878 return scancode; 1879} 1880 1881static void Wayland_ReconcileModifiers(SDL_WaylandSeat *seat, bool key_pressed) 1882{ 1883 /* Handle explicit pressed modifier state. This will correct the modifier state 1884 * if common modifier keys were remapped and the modifiers presumed to be set 1885 * during a key press event were incorrect, or if the modifier was set to the 1886 * pressed state via means other than pressing the physical key. 1887 */ 1888 if (!key_pressed) { 1889 if (seat->keyboard.xkb.wl_pressed_modifiers & seat->keyboard.xkb.shift_mask) { 1890 if (!(seat->keyboard.pressed_modifiers & SDL_KMOD_SHIFT)) { 1891 seat->keyboard.pressed_modifiers |= SDL_KMOD_SHIFT; 1892 } 1893 } else { 1894 seat->keyboard.pressed_modifiers &= ~SDL_KMOD_SHIFT; 1895 } 1896 1897 if (seat->keyboard.xkb.wl_pressed_modifiers & seat->keyboard.xkb.ctrl_mask) { 1898 if (!(seat->keyboard.pressed_modifiers & SDL_KMOD_CTRL)) { 1899 seat->keyboard.pressed_modifiers |= SDL_KMOD_CTRL; 1900 } 1901 } else { 1902 seat->keyboard.pressed_modifiers &= ~SDL_KMOD_CTRL; 1903 } 1904 1905 if (seat->keyboard.xkb.wl_pressed_modifiers & seat->keyboard.xkb.alt_mask) { 1906 if (!(seat->keyboard.pressed_modifiers & SDL_KMOD_ALT)) { 1907 seat->keyboard.pressed_modifiers |= SDL_KMOD_ALT; 1908 } 1909 } else { 1910 seat->keyboard.pressed_modifiers &= ~SDL_KMOD_ALT; 1911 } 1912 1913 if (seat->keyboard.xkb.wl_pressed_modifiers & seat->keyboard.xkb.gui_mask) { 1914 if (!(seat->keyboard.pressed_modifiers & SDL_KMOD_GUI)) { 1915 seat->keyboard.pressed_modifiers |= SDL_KMOD_GUI; 1916 } 1917 } else { 1918 seat->keyboard.pressed_modifiers &= ~SDL_KMOD_GUI; 1919 } 1920 1921 if (seat->keyboard.xkb.wl_pressed_modifiers & seat->keyboard.xkb.level3_mask) { 1922 if (!(seat->keyboard.pressed_modifiers & SDL_KMOD_MODE)) { 1923 seat->keyboard.pressed_modifiers |= SDL_KMOD_MODE; 1924 } 1925 } else { 1926 seat->keyboard.pressed_modifiers &= ~SDL_KMOD_MODE; 1927 } 1928 1929 if (seat->keyboard.xkb.wl_pressed_modifiers & seat->keyboard.xkb.level5_mask) { 1930 if (!(seat->keyboard.pressed_modifiers & SDL_KMOD_LEVEL5)) { 1931 seat->keyboard.pressed_modifiers |= SDL_KMOD_LEVEL5; 1932 } 1933 } else { 1934 seat->keyboard.pressed_modifiers &= ~SDL_KMOD_LEVEL5; 1935 } 1936 } 1937 1938 /* If a latch or lock was activated by a keypress, the latch/lock will 1939 * be tied to the specific left/right key that initiated it. Otherwise, 1940 * the ambiguous left/right combo is used. 1941 * 1942 * The modifier will remain active until the latch/lock is released by 1943 * the system. 1944 */ 1945 const xkb_mod_mask_t xkb_locked_modifiers = seat->keyboard.xkb.wl_latched_modifiers | seat->keyboard.xkb.wl_locked_modifiers; 1946 1947 if (xkb_locked_modifiers & seat->keyboard.xkb.shift_mask) { 1948 if (seat->keyboard.pressed_modifiers & SDL_KMOD_SHIFT) { 1949 seat->keyboard.locked_modifiers &= ~SDL_KMOD_SHIFT; 1950 seat->keyboard.locked_modifiers |= (seat->keyboard.pressed_modifiers & SDL_KMOD_SHIFT); 1951 } else if (!(seat->keyboard.locked_modifiers & SDL_KMOD_SHIFT)) { 1952 seat->keyboard.locked_modifiers |= SDL_KMOD_SHIFT; 1953 } 1954 } else { 1955 seat->keyboard.locked_modifiers &= ~SDL_KMOD_SHIFT; 1956 } 1957 1958 if (xkb_locked_modifiers & seat->keyboard.xkb.ctrl_mask) { 1959 if (seat->keyboard.pressed_modifiers & SDL_KMOD_CTRL) { 1960 seat->keyboard.locked_modifiers &= ~SDL_KMOD_CTRL; 1961 seat->keyboard.locked_modifiers |= (seat->keyboard.pressed_modifiers & SDL_KMOD_CTRL); 1962 } else if (!(seat->keyboard.locked_modifiers & SDL_KMOD_CTRL)) { 1963 seat->keyboard.locked_modifiers |= SDL_KMOD_CTRL; 1964 } 1965 } else { 1966 seat->keyboard.locked_modifiers &= ~SDL_KMOD_CTRL; 1967 } 1968 1969 if (xkb_locked_modifiers & seat->keyboard.xkb.alt_mask) { 1970 if (seat->keyboard.pressed_modifiers & SDL_KMOD_ALT) { 1971 seat->keyboard.locked_modifiers &= ~SDL_KMOD_ALT; 1972 seat->keyboard.locked_modifiers |= (seat->keyboard.pressed_modifiers & SDL_KMOD_ALT); 1973 } else if (!(seat->keyboard.locked_modifiers & SDL_KMOD_ALT)) { 1974 seat->keyboard.locked_modifiers |= SDL_KMOD_ALT; 1975 } 1976 } else { 1977 seat->keyboard.locked_modifiers &= ~SDL_KMOD_ALT; 1978 } 1979 1980 if (xkb_locked_modifiers & seat->keyboard.xkb.gui_mask) { 1981 if (seat->keyboard.pressed_modifiers & SDL_KMOD_GUI) { 1982 seat->keyboard.locked_modifiers &= ~SDL_KMOD_GUI; 1983 seat->keyboard.locked_modifiers |= (seat->keyboard.pressed_modifiers & SDL_KMOD_GUI); 1984 } else if (!(seat->keyboard.locked_modifiers & SDL_KMOD_GUI)) { 1985 seat->keyboard.locked_modifiers |= SDL_KMOD_GUI; 1986 } 1987 } else { 1988 seat->keyboard.locked_modifiers &= ~SDL_KMOD_GUI; 1989 } 1990 1991 if (xkb_locked_modifiers & seat->keyboard.xkb.level3_mask) { 1992 seat->keyboard.locked_modifiers |= SDL_KMOD_MODE; 1993 } else { 1994 seat->keyboard.locked_modifiers &= ~SDL_KMOD_MODE; 1995 } 1996 1997 if (xkb_locked_modifiers & seat->keyboard.xkb.level5_mask) { 1998 seat->keyboard.locked_modifiers |= SDL_KMOD_LEVEL5; 1999 } else { 2000 seat->keyboard.locked_modifiers &= ~SDL_KMOD_LEVEL5; 2001 } 2002 2003 // Capslock and Numlock can only be locked, not pressed. 2004 if (xkb_locked_modifiers & seat->keyboard.xkb.caps_mask) { 2005 seat->keyboard.locked_modifiers |= SDL_KMOD_CAPS; 2006 } else { 2007 seat->keyboard.locked_modifiers &= ~SDL_KMOD_CAPS; 2008 } 2009 2010 if (xkb_locked_modifiers & seat->keyboard.xkb.num_mask) { 2011 seat->keyboard.locked_modifiers |= SDL_KMOD_NUM; 2012 } else { 2013 seat->keyboard.locked_modifiers &= ~SDL_KMOD_NUM; 2014 } 2015 2016 SDL_SetModState(seat->keyboard.pressed_modifiers | seat->keyboard.locked_modifiers); 2017} 2018 2019static void Wayland_HandleModifierKeys(SDL_WaylandSeat *seat, SDL_Scancode scancode, bool pressed) 2020{ 2021 const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false); 2022 SDL_Keymod mod; 2023 2024 /* SDL clients expect modifier state to be activated at the same time as the 2025 * source keypress, so we set pressed modifier state with the usual modifier 2026 * keys here, as the explicit modifier event won't arrive until after the 2027 * keypress event. If this is wrong, it will be corrected when the explicit 2028 * modifier state is sent at a later time. 2029 */ 2030 switch (keycode) { 2031 case SDLK_LSHIFT: 2032 mod = SDL_KMOD_LSHIFT; 2033 break; 2034 case SDLK_RSHIFT: 2035 mod = SDL_KMOD_RSHIFT; 2036 break; 2037 case SDLK_LCTRL: 2038 mod = SDL_KMOD_LCTRL; 2039 break; 2040 case SDLK_RCTRL: 2041 mod = SDL_KMOD_RCTRL; 2042 break; 2043 case SDLK_LALT: 2044 mod = SDL_KMOD_LALT; 2045 break; 2046 case SDLK_RALT: 2047 mod = SDL_KMOD_RALT; 2048 break; 2049 case SDLK_LGUI: 2050 mod = SDL_KMOD_LGUI; 2051 break; 2052 case SDLK_RGUI: 2053 mod = SDL_KMOD_RGUI; 2054 break; 2055 case SDLK_MODE: 2056 mod = SDL_KMOD_MODE; 2057 break; 2058 case SDLK_LEVEL5_SHIFT: 2059 mod = SDL_KMOD_LEVEL5; 2060 break; 2061 default: 2062 return; 2063 } 2064 2065 if (pressed) { 2066 seat->keyboard.pressed_modifiers |= mod; 2067 } else { 2068 seat->keyboard.pressed_modifiers &= ~mod; 2069 } 2070 2071 Wayland_ReconcileModifiers(seat, true); 2072} 2073 2074static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, 2075 uint32_t serial, struct wl_surface *surface, 2076 struct wl_array *keys) 2077{ 2078 SDL_WaylandSeat *seat = data; 2079 uint32_t *key; 2080 2081 if (!surface) { 2082 // Enter event for a destroyed surface. 2083 return; 2084 } 2085 2086 SDL_WindowData *window = Wayland_GetWindowDataForOwnedSurface(surface); 2087 if (!window) { 2088 // Not a surface owned by SDL. 2089 return; 2090 } 2091 2092 ++window->keyboard_focus_count; 2093 seat->keyboard.focus = window; 2094 2095 // Restore the keyboard focus to the child popup that was holding it 2096 SDL_SetKeyboardFocus(window->sdlwindow->keyboard_focus ? window->sdlwindow->keyboard_focus : window->sdlwindow); 2097 2098 // Update the keyboard grab and any relative pointer grabs related to this keyboard focus. 2099 Wayland_SeatUpdateKeyboardGrab(seat); 2100 Wayland_DisplayUpdatePointerGrabs(seat->display, window); 2101 2102 // Update text input and IME focus. 2103 Wayland_SeatUpdateTextInput(seat); 2104 2105#ifdef SDL_USE_IME 2106 if (!seat->text_input.zwp_text_input) { 2107 SDL_IME_SetFocus(true); 2108 } 2109#endif 2110 2111 Uint64 timestamp = SDL_GetTicksNS(); 2112 window->last_focus_event_time_ns = timestamp; 2113 2114 Wayland_SeatSetKeymap(seat); 2115 2116 wl_array_for_each (key, keys) { 2117 const SDL_Scancode scancode = Wayland_GetScancodeForKey(seat, *key, NULL); 2118 if (scancode != SDL_SCANCODE_UNKNOWN) { 2119 const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false); 2120 2121 switch (keycode) { 2122 case SDLK_LSHIFT: 2123 case SDLK_RSHIFT: 2124 case SDLK_LCTRL: 2125 case SDLK_RCTRL: 2126 case SDLK_LALT: 2127 case SDLK_RALT: 2128 case SDLK_LGUI: 2129 case SDLK_RGUI: 2130 case SDLK_MODE: 2131 case SDLK_LEVEL5_SHIFT: 2132 Wayland_HandleModifierKeys(seat, scancode, true); 2133 SDL_SendKeyboardKeyIgnoreModifiers(timestamp, seat->keyboard.sdl_id, *key, scancode, true); 2134 break; 2135 default: 2136 break; 2137 } 2138 } 2139 } 2140} 2141 2142static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, 2143 uint32_t serial, struct wl_surface *surface) 2144{ 2145 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 2146 2147 if (!surface) { 2148 // Leave event for a destroyed surface. 2149 return; 2150 } 2151 2152 SDL_WindowData *window = Wayland_GetWindowDataForOwnedSurface(surface); 2153 if (!window) { 2154 // Not a surface owned by SDL. 2155 return; 2156 } 2157 2158 // Stop key repeat before clearing keyboard focus 2159 keyboard_repeat_clear(&seat->keyboard.repeat); 2160 2161 SDL_Window *keyboard_focus = SDL_GetKeyboardFocus(); 2162 2163 // The keyboard focus may be a child popup 2164 while (keyboard_focus && SDL_WINDOW_IS_POPUP(keyboard_focus)) { 2165 keyboard_focus = keyboard_focus->parent; 2166 } 2167 2168 const bool had_focus = keyboard_focus && window->sdlwindow == keyboard_focus; 2169 seat->keyboard.focus = NULL; 2170 --window->keyboard_focus_count; 2171 2172 // Only relinquish focus if this window has the active focus, and no other keyboards have focus on the window. 2173 if (!window->keyboard_focus_count && had_focus) { 2174 SDL_SetKeyboardFocus(NULL); 2175 } 2176 2177 // Release the keyboard grab and any relative pointer grabs related to this keyboard focus. 2178 Wayland_SeatUpdateKeyboardGrab(seat); 2179 Wayland_DisplayUpdatePointerGrabs(seat->display, window); 2180 2181 // Clear the pressed modifiers. 2182 seat->keyboard.pressed_modifiers = SDL_KMOD_NONE; 2183 2184 // Update text input and IME focus. 2185 Wayland_SeatUpdateTextInput(seat); 2186 2187#ifdef SDL_USE_IME 2188 if (!seat->text_input.zwp_text_input && !window->keyboard_focus_count) { 2189 SDL_IME_SetFocus(false); 2190 } 2191#endif 2192 2193 /* If the window has mouse focus, has no pointers within it, and no active touches, consider 2194 * mouse focus to be lost. 2195 */ 2196 if (SDL_GetMouseFocus() == window->sdlwindow && !window->pointer_focus_count && !window->active_touch_count) { 2197 SDL_SetMouseFocus(NULL); 2198 } 2199} 2200 2201static bool keyboard_input_get_text(char text[8], const SDL_WaylandSeat *seat, uint32_t key, bool down, bool *handled_by_ime) 2202{ 2203 const xkb_keysym_t *syms; 2204 xkb_keysym_t sym; 2205 2206 if (!seat->keyboard.focus || !seat->keyboard.xkb.state) { 2207 return false; 2208 } 2209 2210 // TODO: Can this happen? 2211 if (WAYLAND_xkb_state_key_get_syms(seat->keyboard.xkb.state, key + 8, &syms) != 1) { 2212 return false; 2213 } 2214 sym = syms[0]; 2215 2216#ifdef SDL_USE_IME 2217 if (SDL_IME_ProcessKeyEvent(sym, key + 8, down)) { 2218 if (handled_by_ime) { 2219 *handled_by_ime = true; 2220 } 2221 return true; 2222 } 2223#endif 2224 2225 if (!down) { 2226 return false; 2227 } 2228 2229 if (seat->keyboard.xkb.compose_state && WAYLAND_xkb_compose_state_feed(seat->keyboard.xkb.compose_state, sym) == XKB_COMPOSE_FEED_ACCEPTED) { 2230 switch (WAYLAND_xkb_compose_state_get_status(seat->keyboard.xkb.compose_state)) { 2231 case XKB_COMPOSE_COMPOSING: 2232 if (handled_by_ime) { 2233 *handled_by_ime = true; 2234 } 2235 return true; 2236 case XKB_COMPOSE_CANCELLED: 2237 default: 2238 sym = XKB_KEY_NoSymbol; 2239 break; 2240 case XKB_COMPOSE_NOTHING: 2241 break; 2242 case XKB_COMPOSE_COMPOSED: 2243 sym = WAYLAND_xkb_compose_state_get_one_sym(seat->keyboard.xkb.compose_state); 2244 break; 2245 } 2246 } 2247 2248 return WAYLAND_xkb_keysym_to_utf8(sym, text, 8) > 0; 2249} 2250 2251static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard, 2252 uint32_t serial, uint32_t time, uint32_t key, 2253 uint32_t state_w) 2254{ 2255 SDL_WaylandSeat *seat = data; 2256 enum wl_keyboard_key_state state = state_w; 2257 char text[8]; 2258 bool has_text = false; 2259 bool handled_by_ime = false; 2260 const Uint64 timestamp_ns = Wayland_GetKeyboardTimestamp(seat, time); 2261 2262 Wayland_UpdateImplicitGrabSerial(seat, serial); 2263 2264 if (state == WL_KEYBOARD_KEY_STATE_REPEATED) { 2265 // If this key shouldn't be repeated, just return. 2266 if (seat->keyboard.xkb.keymap && !WAYLAND_xkb_keymap_key_repeats(seat->keyboard.xkb.keymap, key + 8)) { 2267 return; 2268 } 2269 2270 // SDL automatically handles key tracking and repeat status, so just map 'repeated' to 'pressed'. 2271 state = WL_KEYBOARD_KEY_STATE_PRESSED; 2272 } 2273 2274 Wayland_SeatSetKeymap(seat); 2275 2276 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { 2277 SDL_Window *keyboard_focus = SDL_GetKeyboardFocus(); 2278 if (keyboard_focus && SDL_TextInputActive(keyboard_focus)) { 2279 has_text = keyboard_input_get_text(text, seat, key, true, &handled_by_ime); 2280 } 2281 } else { 2282 if (keyboard_repeat_key_is_set(&seat->keyboard.repeat, key)) { 2283 /* Send any due key repeat events before stopping the repeat and generating the key up event. 2284 * Compute time based on the Wayland time, as it reports when the release event happened. 2285 * Using SDL_GetTicks would be wrong, as it would report when the release event is processed, 2286 * which may be off if the application hasn't pumped events for a while. 2287 */ 2288 const Uint64 elapsed = SDL_MS_TO_NS(time - seat->keyboard.repeat.wl_press_time_ms); 2289 keyboard_repeat_handle(&seat->keyboard.repeat, elapsed); 2290 keyboard_repeat_clear(&seat->keyboard.repeat); 2291 } 2292 keyboard_input_get_text(text, seat, key, false, &handled_by_ime); 2293 } 2294 2295 const xkb_keysym_t *syms = NULL; 2296 SDL_Scancode scancode = Wayland_GetScancodeForKey(seat, key, &syms); 2297 Wayland_HandleModifierKeys(seat, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED); 2298 2299 // If we have a key with unknown scancode, check if the keysym corresponds to a valid Unicode value, and assign it a reserved scancode. 2300 if (scancode == SDL_SCANCODE_UNKNOWN && syms && seat->keyboard.sdl_keymap) { 2301 const SDL_Keycode keycode = (SDL_Keycode)SDL_KeySymToUcs4(syms[0]); 2302 if (keycode != SDLK_UNKNOWN) { 2303 SDL_Keymod modstate = SDL_KMOD_NONE; 2304 2305 // Check if this keycode already exists in the keymap. 2306 scancode = SDL_GetKeymapScancode(seat->keyboard.sdl_keymap[seat->keyboard.xkb.current_layout], keycode, &modstate); 2307 2308 // Make sure we have this keycode in our keymap 2309 if (scancode == SDL_SCANCODE_UNKNOWN && keycode < SDLK_SCANCODE_MASK) { 2310 scancode = SDL_GetKeymapNextReservedScancode(seat->keyboard.sdl_keymap[seat->keyboard.xkb.current_layout]); 2311 SDL_SetKeymapEntry(seat->keyboard.sdl_keymap[seat->keyboard.xkb.current_layout], scancode, modstate, keycode); 2312 } 2313 } 2314 } 2315 2316 SDL_SendKeyboardKeyIgnoreModifiers(timestamp_ns, seat->keyboard.sdl_id, key, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED); 2317 2318 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { 2319 if (handled_by_ime) { 2320 has_text = false; 2321 } 2322 if (has_text && !(SDL_GetModState() & (SDL_KMOD_CTRL | SDL_KMOD_ALT))) { 2323 SDL_SendKeyboardText(text); 2324 } 2325 if (seat->keyboard.xkb.keymap && WAYLAND_xkb_keymap_key_repeats(seat->keyboard.xkb.keymap, key + 8)) { 2326 keyboard_repeat_set(&seat->keyboard.repeat, seat->keyboard.sdl_id, key, time, timestamp_ns, scancode, has_text, text); 2327 } 2328 } 2329} 2330 2331static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, 2332 uint32_t serial, uint32_t mods_depressed, 2333 uint32_t mods_latched, uint32_t mods_locked, 2334 uint32_t group) 2335{ 2336 SDL_WaylandSeat *seat = data; 2337 const uint32_t previous_layout = seat->keyboard.xkb.current_layout; 2338 2339 seat->keyboard.xkb.wl_pressed_modifiers = mods_depressed; 2340 seat->keyboard.xkb.wl_latched_modifiers = mods_latched; 2341 seat->keyboard.xkb.wl_locked_modifiers = mods_locked; 2342 seat->keyboard.xkb.current_layout = group; 2343 2344 Wayland_ReconcileModifiers(seat, false); 2345 2346 // If we get a modifier notification before the keymap, there's no further state to update yet. 2347 if (!seat->keyboard.xkb.state) { 2348 return; 2349 } 2350 2351 WAYLAND_xkb_state_update_mask(seat->keyboard.xkb.state, 2352 mods_depressed, mods_latched, mods_locked, 2353 0, 0, group); 2354 2355 // If a key is repeating, update the text to apply the modifier. 2356 if (keyboard_repeat_is_set(&seat->keyboard.repeat)) { 2357 char text[8]; 2358 const uint32_t key = keyboard_repeat_get_key(&seat->keyboard.repeat); 2359 2360 if (keyboard_input_get_text(text, seat, key, true, NULL)) { 2361 keyboard_repeat_set_text(&seat->keyboard.repeat, text); 2362 } 2363 } 2364 2365 if (group != previous_layout) { 2366 Wayland_SeatSetKeymap(seat); 2367 2368 if (seat->keyboard.xkb.compose_state) { 2369 // Reset the compose state so composite and dead keys don't carry over. 2370 WAYLAND_xkb_compose_state_reset(seat->keyboard.xkb.compose_state); 2371 } 2372 } 2373} 2374 2375static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard, 2376 int32_t rate, int32_t delay) 2377{ 2378 SDL_WaylandSeat *seat = data; 2379 seat->keyboard.repeat.repeat_rate = SDL_clamp(rate, 0, 1000); 2380 seat->keyboard.repeat.repeat_delay_ms = delay; 2381 seat->keyboard.repeat.is_initialized = true; 2382} 2383 2384static const struct wl_keyboard_listener keyboard_listener = { 2385 keyboard_handle_keymap, 2386 keyboard_handle_enter, 2387 keyboard_handle_leave, 2388 keyboard_handle_key, 2389 keyboard_handle_modifiers, 2390 keyboard_handle_repeat_info, // Version 4 2391}; 2392 2393static void Wayland_SeatDestroyPointer(SDL_WaylandSeat *seat) 2394{ 2395 Wayland_CursorStateRelease(&seat->pointer.cursor_state); 2396 2397 // End any active gestures. 2398 if (seat->pointer.gesture_focus) { 2399 SDL_SendPinch(SDL_EVENT_PINCH_END, 0, seat->pointer.gesture_focus->sdlwindow, 0.0f); 2400 } 2401 2402 // Make sure focus is removed from a surface before the pointer is destroyed. 2403 if (seat->pointer.focus) { 2404 seat->pointer.pending_frame.leave_surface = seat->pointer.focus->surface; 2405 pointer_dispatch_leave(seat, false); 2406 seat->pointer.pending_frame.leave_surface = NULL; 2407 } 2408 2409 SDL_RemoveMouse(seat->pointer.sdl_id); 2410 2411 if (seat->pointer.confined_pointer) { 2412 zwp_confined_pointer_v1_destroy(seat->pointer.confined_pointer); 2413 } 2414 2415 if (seat->pointer.locked_pointer) { 2416 zwp_locked_pointer_v1_destroy(seat->pointer.locked_pointer); 2417 } 2418 2419 if (seat->pointer.relative_pointer) { 2420 zwp_relative_pointer_v1_destroy(seat->pointer.relative_pointer); 2421 } 2422 2423 if (seat->pointer.timestamps) { 2424 zwp_input_timestamps_v1_destroy(seat->pointer.timestamps); 2425 } 2426 2427 if (seat->pointer.gesture_pinch) { 2428 zwp_pointer_gesture_pinch_v1_destroy(seat->pointer.gesture_pinch); 2429 } 2430 2431 if (seat->pointer.wl_pointer) { 2432 if (wl_pointer_get_version(seat->pointer.wl_pointer) >= WL_POINTER_RELEASE_SINCE_VERSION) { 2433 wl_pointer_release(seat->pointer.wl_pointer); 2434 } else { 2435 wl_pointer_destroy(seat->pointer.wl_pointer); 2436 } 2437 } 2438 2439 SDL_zero(seat->pointer); 2440} 2441 2442static void Wayland_SeatDestroyKeyboard(SDL_WaylandSeat *seat) 2443{ 2444 // Make sure focus is removed from a surface before the keyboard is destroyed. 2445 if (seat->keyboard.focus) { 2446 keyboard_handle_leave(seat, seat->keyboard.wl_keyboard, 0, seat->keyboard.focus->surface); 2447 } 2448 2449 SDL_RemoveKeyboard(seat->keyboard.sdl_id); 2450 2451 if (seat->keyboard.sdl_keymap) { 2452 if (seat->keyboard.xkb.current_layout < seat->keyboard.xkb.num_layouts && 2453 seat->keyboard.sdl_keymap[seat->keyboard.xkb.current_layout] == SDL_GetCurrentKeymap(true)) { 2454 SDL_SetModState(SDL_KMOD_NONE); 2455 } 2456 for (xkb_layout_index_t i = 0; i < seat->keyboard.xkb.num_layouts; ++i) { 2457 SDL_DestroyKeymap(seat->keyboard.sdl_keymap[i]); 2458 } 2459 SDL_free(seat->keyboard.sdl_keymap); 2460 seat->keyboard.sdl_keymap = NULL; 2461 } 2462 2463 if (seat->keyboard.key_inhibitor) { 2464 zwp_keyboard_shortcuts_inhibitor_v1_destroy(seat->keyboard.key_inhibitor); 2465 } 2466 2467 if (seat->keyboard.timestamps) { 2468 zwp_input_timestamps_v1_destroy(seat->keyboard.timestamps); 2469 } 2470 2471 if (seat->keyboard.wl_keyboard) { 2472 if (wl_keyboard_get_version(seat->keyboard.wl_keyboard) >= WL_KEYBOARD_RELEASE_SINCE_VERSION) { 2473 wl_keyboard_release(seat->keyboard.wl_keyboard); 2474 } else { 2475 wl_keyboard_destroy(seat->keyboard.wl_keyboard); 2476 } 2477 } 2478 2479 SDL_free(seat->keyboard.current_locale); 2480 2481 if (seat->keyboard.xkb.compose_state) { 2482 WAYLAND_xkb_compose_state_unref(seat->keyboard.xkb.compose_state); 2483 } 2484 2485 if (seat->keyboard.xkb.compose_table) { 2486 WAYLAND_xkb_compose_table_unref(seat->keyboard.xkb.compose_table); 2487 } 2488 2489 if (seat->keyboard.xkb.state) { 2490 WAYLAND_xkb_state_unref(seat->keyboard.xkb.state); 2491 } 2492 2493 if (seat->keyboard.xkb.keymap) { 2494 WAYLAND_xkb_keymap_unref(seat->keyboard.xkb.keymap); 2495 } 2496 2497 SDL_zero(seat->keyboard); 2498} 2499 2500static void Wayland_SeatDestroyTouch(SDL_WaylandSeat *seat) 2501{ 2502 // Cancel any active touches before the touch object is destroyed. 2503 if (seat->touch.wl_touch) { 2504 touch_handler_cancel(seat, seat->touch.wl_touch); 2505 } 2506 2507 SDL_DelTouch((SDL_TouchID)(uintptr_t)seat->touch.wl_touch); 2508 2509 if (seat->touch.timestamps) { 2510 zwp_input_timestamps_v1_destroy(seat->touch.timestamps); 2511 } 2512 2513 if (seat->touch.wl_touch) { 2514 if (wl_touch_get_version(seat->touch.wl_touch) >= WL_TOUCH_RELEASE_SINCE_VERSION) { 2515 wl_touch_release(seat->touch.wl_touch); 2516 } else { 2517 wl_touch_destroy(seat->touch.wl_touch); 2518 } 2519 } 2520 2521 SDL_zero(seat->touch); 2522 WAYLAND_wl_list_init(&seat->touch.points); 2523} 2524 2525static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability capabilities) 2526{ 2527 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 2528 char name_fmt[256]; 2529 2530 if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !seat->pointer.wl_pointer) { 2531 seat->pointer.wl_pointer = wl_seat_get_pointer(wl_seat); 2532 SDL_zero(seat->pointer.pending_frame.axis); 2533 2534 Wayland_SeatCreateCursorShape(seat); 2535 2536 wl_pointer_set_user_data(seat->pointer.wl_pointer, seat); 2537 wl_pointer_add_listener(seat->pointer.wl_pointer, &pointer_listener, seat); 2538 2539 // Pointer gestures 2540 Wayland_SeatCreatePointerGestures(seat); 2541 2542 seat->pointer.sdl_id = SDL_GetNextObjectID(); 2543 2544 if (seat->name) { 2545 SDL_snprintf(name_fmt, sizeof(name_fmt), "%s (%s)", WAYLAND_DEFAULT_POINTER_NAME, seat->name); 2546 } else { 2547 SDL_snprintf(name_fmt, sizeof(name_fmt), "%s %" SDL_PRIu32, WAYLAND_DEFAULT_POINTER_NAME, seat->pointer.sdl_id); 2548 } 2549 2550 SDL_AddMouse(seat->pointer.sdl_id, name_fmt); 2551 } else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && seat->pointer.wl_pointer) { 2552 Wayland_SeatDestroyPointer(seat); 2553 } 2554 2555 if ((capabilities & WL_SEAT_CAPABILITY_TOUCH) && !seat->touch.wl_touch) { 2556 seat->touch.wl_touch = wl_seat_get_touch(wl_seat); 2557 wl_touch_set_user_data(seat->touch.wl_touch, seat); 2558 wl_touch_add_listener(seat->touch.wl_touch, &touch_listener, seat); 2559 2560 if (seat->name) { 2561 SDL_snprintf(name_fmt, sizeof(name_fmt), "%s (%s)", WAYLAND_DEFAULT_TOUCH_NAME, seat->name); 2562 } else { 2563 SDL_snprintf(name_fmt, sizeof(name_fmt), "%s %" SDL_PRIu64, WAYLAND_DEFAULT_TOUCH_NAME, (SDL_TouchID)(uintptr_t)seat->touch.wl_touch); 2564 } 2565 2566 SDL_AddTouch((SDL_TouchID)(uintptr_t)seat->touch.wl_touch, SDL_TOUCH_DEVICE_DIRECT, name_fmt); 2567 } else if (!(capabilities & WL_SEAT_CAPABILITY_TOUCH) && seat->touch.wl_touch) { 2568 Wayland_SeatDestroyTouch(seat); 2569 } 2570 2571 if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && !seat->keyboard.wl_keyboard) { 2572 seat->keyboard.wl_keyboard = wl_seat_get_keyboard(wl_seat); 2573 wl_keyboard_set_user_data(seat->keyboard.wl_keyboard, seat); 2574 wl_keyboard_add_listener(seat->keyboard.wl_keyboard, &keyboard_listener, seat); 2575 2576 seat->keyboard.sdl_id = SDL_GetNextObjectID(); 2577 2578 if (seat->name) { 2579 SDL_snprintf(name_fmt, sizeof(name_fmt), "%s (%s)", WAYLAND_DEFAULT_KEYBOARD_NAME, seat->name); 2580 } else { 2581 SDL_snprintf(name_fmt, sizeof(name_fmt), "%s %" SDL_PRIu32, WAYLAND_DEFAULT_KEYBOARD_NAME, seat->keyboard.sdl_id); 2582 } 2583 2584 SDL_AddKeyboard(seat->keyboard.sdl_id, name_fmt); 2585 } else if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && seat->keyboard.wl_keyboard) { 2586 Wayland_SeatDestroyKeyboard(seat); 2587 } 2588 2589 Wayland_SeatRegisterInputTimestampListeners(seat); 2590} 2591 2592static void seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) 2593{ 2594 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 2595 2596 if (name && *name != '\0') { 2597 seat->name = SDL_strdup(name); 2598 } 2599} 2600 2601static const struct wl_seat_listener seat_listener = { 2602 seat_handle_capabilities, 2603 seat_handle_name, // Version 2 2604}; 2605 2606static void data_source_handle_target(void *data, struct wl_data_source *wl_data_source, 2607 const char *mime_type) 2608{ 2609} 2610 2611static void data_source_handle_send(void *data, struct wl_data_source *wl_data_source, 2612 const char *mime_type, int32_t fd) 2613{ 2614 Wayland_data_source_send((SDL_WaylandDataSource *)data, mime_type, fd); 2615} 2616 2617static void data_source_handle_cancelled(void *data, struct wl_data_source *wl_data_source) 2618{ 2619 SDL_WaylandDataSource *source = data; 2620 if (source) { 2621 Wayland_data_source_destroy(source); 2622 } 2623} 2624 2625static void data_source_handle_dnd_drop_performed(void *data, struct wl_data_source *wl_data_source) 2626{ 2627} 2628 2629static void data_source_handle_dnd_finished(void *data, struct wl_data_source *wl_data_source) 2630{ 2631} 2632 2633static void data_source_handle_action(void *data, struct wl_data_source *wl_data_source, 2634 uint32_t dnd_action) 2635{ 2636} 2637 2638static const struct wl_data_source_listener data_source_listener = { 2639 data_source_handle_target, 2640 data_source_handle_send, 2641 data_source_handle_cancelled, 2642 data_source_handle_dnd_drop_performed, // Version 3 2643 data_source_handle_dnd_finished, // Version 3 2644 data_source_handle_action, // Version 3 2645}; 2646 2647static void primary_selection_source_send(void *data, struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1, 2648 const char *mime_type, int32_t fd) 2649{ 2650 Wayland_primary_selection_source_send((SDL_WaylandPrimarySelectionSource *)data, 2651 mime_type, fd); 2652} 2653 2654static void primary_selection_source_cancelled(void *data, struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1) 2655{ 2656 Wayland_primary_selection_source_destroy(data); 2657} 2658 2659static const struct zwp_primary_selection_source_v1_listener primary_selection_source_listener = { 2660 primary_selection_source_send, 2661 primary_selection_source_cancelled, 2662}; 2663 2664SDL_WaylandDataSource *Wayland_data_source_create(SDL_VideoDevice *_this) 2665{ 2666 SDL_WaylandDataSource *data_source = NULL; 2667 SDL_VideoData *driver_data = NULL; 2668 struct wl_data_source *id = NULL; 2669 2670 if (!_this || !_this->internal) { 2671 SDL_SetError("Video driver uninitialized"); 2672 } else { 2673 driver_data = _this->internal; 2674 2675 if (driver_data->data_device_manager) { 2676 id = wl_data_device_manager_create_data_source( 2677 driver_data->data_device_manager); 2678 } 2679 2680 if (!id) { 2681 SDL_SetError("Wayland unable to create data source"); 2682 } else { 2683 data_source = SDL_calloc(1, sizeof(*data_source)); 2684 if (!data_source) { 2685 wl_data_source_destroy(id); 2686 } else { 2687 data_source->source = id; 2688 wl_data_source_set_user_data(id, data_source); 2689 wl_data_source_add_listener(id, &data_source_listener, 2690 data_source); 2691 } 2692 } 2693 } 2694 return data_source; 2695} 2696 2697SDL_WaylandPrimarySelectionSource *Wayland_primary_selection_source_create(SDL_VideoDevice *_this) 2698{ 2699 SDL_WaylandPrimarySelectionSource *primary_selection_source = NULL; 2700 SDL_VideoData *driver_data = NULL; 2701 struct zwp_primary_selection_source_v1 *id = NULL; 2702 2703 if (!_this || !_this->internal) { 2704 SDL_SetError("Video driver uninitialized"); 2705 } else { 2706 driver_data = _this->internal; 2707 2708 if (driver_data->primary_selection_device_manager) { 2709 id = zwp_primary_selection_device_manager_v1_create_source( 2710 driver_data->primary_selection_device_manager); 2711 } 2712 2713 if (!id) { 2714 SDL_SetError("Wayland unable to create primary selection source"); 2715 } else { 2716 primary_selection_source = SDL_calloc(1, sizeof(*primary_selection_source)); 2717 if (!primary_selection_source) { 2718 zwp_primary_selection_source_v1_destroy(id); 2719 } else { 2720 primary_selection_source->source = id; 2721 zwp_primary_selection_source_v1_add_listener(id, &primary_selection_source_listener, 2722 primary_selection_source); 2723 } 2724 } 2725 } 2726 return primary_selection_source; 2727} 2728 2729static void data_offer_handle_offer(void *data, struct wl_data_offer *wl_data_offer, 2730 const char *mime_type) 2731{ 2732 SDL_WaylandDataOffer *offer = data; 2733 Wayland_data_offer_add_mime(offer, mime_type); 2734 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2735 ". In wl_data_offer_listener . data_offer_handle_offer on data_offer 0x%08x for MIME '%s'", 2736 (wl_data_offer ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)wl_data_offer) : -1), 2737 mime_type); 2738} 2739 2740static void data_offer_handle_source_actions(void *data, struct wl_data_offer *wl_data_offer, 2741 uint32_t source_actions) 2742{ 2743 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2744 ". In wl_data_offer_listener . data_offer_handle_source_actions on data_offer 0x%08x for Source Actions '%d'", 2745 (wl_data_offer ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)wl_data_offer) : -1), 2746 source_actions); 2747} 2748 2749static void data_offer_handle_actions(void *data, struct wl_data_offer *wl_data_offer, 2750 uint32_t dnd_action) 2751{ 2752 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2753 ". In wl_data_offer_listener . data_offer_handle_actions on data_offer 0x%08x for DND Actions '%d'", 2754 (wl_data_offer ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)wl_data_offer) : -1), 2755 dnd_action); 2756} 2757 2758static const struct wl_data_offer_listener data_offer_listener = { 2759 data_offer_handle_offer, 2760 data_offer_handle_source_actions, // Version 3 2761 data_offer_handle_actions, // Version 3 2762}; 2763 2764static void primary_selection_offer_handle_offer(void *data, struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer_v1, 2765 const char *mime_type) 2766{ 2767 SDL_WaylandPrimarySelectionOffer *offer = data; 2768 Wayland_primary_selection_offer_add_mime(offer, mime_type); 2769 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2770 ". In zwp_primary_selection_offer_v1_listener . primary_selection_offer_handle_offer on primary_selection_offer 0x%08x for MIME '%s'", 2771 (zwp_primary_selection_offer_v1 ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)zwp_primary_selection_offer_v1) : -1), 2772 mime_type); 2773} 2774 2775static const struct zwp_primary_selection_offer_v1_listener primary_selection_offer_listener = { 2776 primary_selection_offer_handle_offer, 2777}; 2778 2779static void data_device_handle_data_offer(void *data, struct wl_data_device *wl_data_device, 2780 struct wl_data_offer *id) 2781{ 2782 SDL_WaylandDataOffer *data_offer = SDL_calloc(1, sizeof(*data_offer)); 2783 if (data_offer) { 2784 SDL_WaylandDataDevice *data_device = (SDL_WaylandDataDevice *)data; 2785 data_device->seat->display->last_incoming_data_offer_seat = data_device->seat; 2786 data_offer->offer = id; 2787 data_offer->data_device = data_device; 2788 data_offer->read_fd = -1; 2789 WAYLAND_wl_list_init(&(data_offer->mimes)); 2790 wl_data_offer_set_user_data(id, data_offer); 2791 wl_data_offer_add_listener(id, &data_offer_listener, data_offer); 2792 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2793 ". In wl_data_device_listener . data_device_handle_data_offer on data_offer 0x%08x", 2794 (id ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)id) : -1)); 2795 } 2796} 2797 2798static void data_device_handle_enter(void *data, struct wl_data_device *wl_data_device, 2799 uint32_t serial, struct wl_surface *surface, 2800 wl_fixed_t x, wl_fixed_t y, struct wl_data_offer *id) 2801{ 2802 SDL_WaylandDataDevice *data_device = data; 2803 data_device->has_mime_file = false; 2804 data_device->has_mime_text = false; 2805 uint32_t dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; 2806 2807 data_device->drag_serial = serial; 2808 2809 if (id) { 2810 data_device->drag_offer = wl_data_offer_get_user_data(id); 2811 2812 // TODO: SDL Support more mime types 2813#ifdef SDL_USE_LIBDBUS 2814 if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_PORTAL_MIME)) { 2815 data_device->has_mime_file = true; 2816 data_device->mime_type = FILE_PORTAL_MIME; 2817 wl_data_offer_accept(id, serial, FILE_PORTAL_MIME); 2818 } 2819#endif 2820 if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_MIME)) { 2821 data_device->has_mime_file = true; 2822 data_device->mime_type = FILE_MIME; 2823 wl_data_offer_accept(id, serial, FILE_MIME); 2824 } 2825 2826 size_t mime_count = 0; 2827 const char *const *text_mime_types = Wayland_GetTextMimeTypes(SDL_GetVideoDevice(), &mime_count); 2828 for (size_t i = 0; i < mime_count; ++i) { 2829 if (Wayland_data_offer_has_mime(data_device->drag_offer, text_mime_types[i])) { 2830 data_device->has_mime_text = true; 2831 data_device->mime_type = text_mime_types[i]; 2832 wl_data_offer_accept(id, serial, text_mime_types[i]); 2833 break; 2834 } 2835 } 2836 2837 // SDL only supports "copy" style drag and drop 2838 if (data_device->has_mime_file || data_device->has_mime_text) { 2839 dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; 2840 } else { 2841 // drag_mime is NULL this will decline the offer 2842 wl_data_offer_accept(id, serial, NULL); 2843 } 2844 if (wl_data_offer_get_version(data_device->drag_offer->offer) >= 2845 WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) { 2846 wl_data_offer_set_actions(data_device->drag_offer->offer, 2847 dnd_action, dnd_action); 2848 } 2849 2850 // find the current window 2851 if (surface) { 2852 SDL_WindowData *window = Wayland_GetWindowDataForOwnedSurface(surface); 2853 if (window) { 2854 data_device->dnd_window = window->sdlwindow; 2855 const float dx = (float)wl_fixed_to_double(x); 2856 const float dy = (float)wl_fixed_to_double(y); 2857 SDL_SendDropPosition(data_device->dnd_window, dx, dy); 2858 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2859 ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d into window %d for serial %d", 2860 WAYLAND_wl_proxy_get_id((struct wl_proxy *)id), 2861 wl_fixed_to_int(x), wl_fixed_to_int(y), SDL_GetWindowID(data_device->dnd_window), serial); 2862 } else { 2863 data_device->dnd_window = NULL; 2864 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2865 ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d for serial %d", 2866 WAYLAND_wl_proxy_get_id((struct wl_proxy *)id), 2867 wl_fixed_to_int(x), wl_fixed_to_int(y), serial); 2868 } 2869 } else { 2870 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2871 ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d for serial %d", 2872 WAYLAND_wl_proxy_get_id((struct wl_proxy *)id), 2873 wl_fixed_to_int(x), wl_fixed_to_int(y), serial); 2874 } 2875 } else { 2876 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2877 ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d for serial %d", 2878 -1, wl_fixed_to_int(x), wl_fixed_to_int(y), serial); 2879 } 2880} 2881 2882static void data_device_handle_leave(void *data, struct wl_data_device *wl_data_device) 2883{ 2884 SDL_WaylandDataDevice *data_device = data; 2885 2886 if (data_device->drag_offer) { 2887 if (data_device->dnd_window) { 2888 SDL_SendDropComplete(data_device->dnd_window); 2889 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2890 ". In wl_data_device_listener . data_device_handle_leave on data_offer 0x%08x from window %d for serial %d", 2891 WAYLAND_wl_proxy_get_id((struct wl_proxy *)data_device->drag_offer->offer), 2892 SDL_GetWindowID(data_device->dnd_window), data_device->drag_serial); 2893 } else { 2894 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2895 ". In wl_data_device_listener . data_device_handle_leave on data_offer 0x%08x for serial %d", 2896 WAYLAND_wl_proxy_get_id((struct wl_proxy *)data_device->drag_offer->offer), 2897 data_device->drag_serial); 2898 } 2899 Wayland_data_offer_destroy(data_device->drag_offer); 2900 data_device->drag_offer = NULL; 2901 } else { 2902 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2903 ". In wl_data_device_listener . data_device_handle_leave on data_offer 0x%08x for serial %d", 2904 -1, -1); 2905 } 2906 data_device->has_mime_file = false; 2907 data_device->has_mime_text = false; 2908} 2909 2910static void data_device_handle_motion(void *data, struct wl_data_device *wl_data_device, 2911 uint32_t time, wl_fixed_t x, wl_fixed_t y) 2912{ 2913 SDL_WaylandDataDevice *data_device = data; 2914 2915 if (data_device->drag_offer && data_device->dnd_window && (data_device->has_mime_file || data_device->has_mime_text)) { 2916 const float dx = (float)wl_fixed_to_double(x); 2917 const float dy = (float)wl_fixed_to_double(y); 2918 2919 /* XXX: Send the filename here if the event system ever starts passing it though. 2920 * Any future implementation should cache the filenames, as otherwise this could 2921 * hammer the DBus interface hundreds or even thousands of times per second. 2922 */ 2923 SDL_SendDropPosition(data_device->dnd_window, dx, dy); 2924 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2925 ". In wl_data_device_listener . data_device_handle_motion on data_offer 0x%08x at %d x %d in window %d serial %d", 2926 WAYLAND_wl_proxy_get_id((struct wl_proxy *)data_device->drag_offer->offer), 2927 wl_fixed_to_int(x), wl_fixed_to_int(y), 2928 SDL_GetWindowID(data_device->dnd_window), data_device->drag_serial); 2929 } else { 2930 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2931 ". In wl_data_device_listener . data_device_handle_motion on data_offer 0x%08x at %d x %d serial %d", 2932 -1, wl_fixed_to_int(x), wl_fixed_to_int(y), -1); 2933 } 2934} 2935 2936static void data_device_handle_drop(void *data, struct wl_data_device *wl_data_device) 2937{ 2938 SDL_WaylandDataDevice *data_device = data; 2939 2940 if (data_device->drag_offer && data_device->dnd_window && (data_device->has_mime_file || data_device->has_mime_text)) { 2941 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2942 ". In wl_data_device_listener . data_device_handle_drop on data_offer 0x%08x in window %d serial %d", 2943 WAYLAND_wl_proxy_get_id((struct wl_proxy *)data_device->drag_offer->offer), 2944 SDL_GetWindowID(data_device->dnd_window), data_device->drag_serial); 2945 // TODO: SDL Support more mime types 2946 size_t length; 2947 bool drop_handled = false; 2948#ifdef SDL_USE_LIBDBUS 2949 if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_PORTAL_MIME)) { 2950 void *buffer = Wayland_data_offer_receive(data_device->drag_offer, FILE_PORTAL_MIME, &length, false); 2951 if (buffer) { 2952 SDL_DBusContext *dbus = SDL_DBus_GetContext(); 2953 if (dbus) { 2954 int path_count = 0; 2955 char **paths = SDL_DBus_DocumentsPortalRetrieveFiles(buffer, &path_count); 2956 // If dropped files contain a directory the list is empty 2957 if (paths && path_count > 0) { 2958 int i; 2959 for (i = 0; i < path_count; i++) { 2960 SDL_SendDropFile(data_device->dnd_window, NULL, paths[i]); 2961 } 2962 dbus->free_string_array(paths); 2963 SDL_SendDropComplete(data_device->dnd_window); 2964 drop_handled = true; 2965 } 2966 } 2967 SDL_free(buffer); 2968 } 2969 } 2970#endif 2971 /* If XDG document portal fails fallback. 2972 * When running a flatpak sandbox this will most likely be a list of 2973 * non paths that are not visible to the application 2974 */ 2975 if (!drop_handled) { 2976 void *buffer = Wayland_data_offer_receive(data_device->drag_offer, data_device->mime_type, &length, false); 2977 if (data_device->has_mime_file) { 2978 if (buffer) { 2979 char *saveptr = NULL; 2980 char *token = SDL_strtok_r((char *)buffer, "\r\n", &saveptr); 2981 while (token) { 2982 if (SDL_URIToLocal(token, token) >= 0) { 2983 SDL_SendDropFile(data_device->dnd_window, NULL, token); 2984 } 2985 token = SDL_strtok_r(NULL, "\r\n", &saveptr); 2986 } 2987 SDL_free(buffer); 2988 SDL_SendDropComplete(data_device->dnd_window); 2989 } else { 2990 SDL_SendDropComplete(data_device->dnd_window); 2991 } 2992 drop_handled = true; 2993 } else if (data_device->has_mime_text) { 2994 if (buffer) { 2995 char *saveptr = NULL; 2996 char *token = SDL_strtok_r((char *)buffer, "\r\n", &saveptr); 2997 while (token) { 2998 SDL_SendDropText(data_device->dnd_window, token); 2999 token = SDL_strtok_r(NULL, "\r\n", &saveptr); 3000 } 3001 SDL_free(buffer); 3002 SDL_SendDropComplete(data_device->dnd_window); 3003 } else { 3004 /* Even though there has been a valid data offer, 3005 * and there have been valid Enter, Motion, and Drop callbacks, 3006 * Wayland_data_offer_receive may return an empty buffer, 3007 * because the data is actually in the primary selection device, 3008 * not in the data device. 3009 */ 3010 SDL_SendDropComplete(data_device->dnd_window); 3011 } 3012 drop_handled = true; 3013 } 3014 } 3015 3016 if (drop_handled && wl_data_offer_get_version(data_device->drag_offer->offer) >= WL_DATA_OFFER_FINISH_SINCE_VERSION) { 3017 wl_data_offer_finish(data_device->drag_offer->offer); 3018 } 3019 } else { 3020 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 3021 ". In wl_data_device_listener . data_device_handle_drop on data_offer 0x%08x serial %d", 3022 -1, -1); 3023 } 3024 3025 Wayland_data_offer_destroy(data_device->drag_offer); 3026 data_device->drag_offer = NULL; 3027} 3028 3029static void data_device_handle_selection(void *data, struct wl_data_device *wl_data_device, 3030 struct wl_data_offer *id) 3031{ 3032 SDL_WaylandDataDevice *data_device = data; 3033 SDL_WaylandDataOffer *offer = NULL; 3034 3035 if (id) { 3036 offer = wl_data_offer_get_user_data(id); 3037 } 3038 3039 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 3040 ". In data_device_listener . data_device_handle_selection on data_offer 0x%08x", 3041 (id ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)id) : -1)); 3042 if (data_device->selection_offer != offer) { 3043 Wayland_data_offer_destroy(data_device->selection_offer); 3044 data_device->selection_offer = offer; 3045 } 3046 3047 Wayland_data_offer_notify_from_mimes(offer, true); 3048} 3049 3050static const struct wl_data_device_listener data_device_listener = { 3051 data_device_handle_data_offer, 3052 data_device_handle_enter, 3053 data_device_handle_leave, 3054 data_device_handle_motion, 3055 data_device_handle_drop, 3056 data_device_handle_selection 3057}; 3058 3059static void primary_selection_device_handle_offer(void *data, struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1, 3060 struct zwp_primary_selection_offer_v1 *id) 3061{ 3062 SDL_WaylandPrimarySelectionOffer *primary_selection_offer = SDL_calloc(1, sizeof(*primary_selection_offer)); 3063 if (primary_selection_offer) { 3064 SDL_WaylandPrimarySelectionDevice *primary_selection_device = (SDL_WaylandPrimarySelectionDevice *)data; 3065 primary_selection_device->seat->display->last_incoming_primary_selection_seat = primary_selection_device->seat; 3066 primary_selection_offer->offer = id; 3067 primary_selection_offer->primary_selection_device = primary_selection_device; 3068 WAYLAND_wl_list_init(&(primary_selection_offer->mimes)); 3069 zwp_primary_selection_offer_v1_set_user_data(id, primary_selection_offer); 3070 zwp_primary_selection_offer_v1_add_listener(id, &primary_selection_offer_listener, primary_selection_offer); 3071 } 3072 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 3073 ". In zwp_primary_selection_device_v1_listener . primary_selection_device_handle_offer on primary_selection_offer 0x%08x", 3074 (id ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)id) : -1)); 3075} 3076 3077static void primary_selection_device_handle_selection(void *data, struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1, 3078 struct zwp_primary_selection_offer_v1 *id) 3079{ 3080 SDL_WaylandPrimarySelectionDevice *primary_selection_device = data; 3081 SDL_WaylandPrimarySelectionOffer *offer = NULL; 3082 3083 if (id) { 3084 offer = zwp_primary_selection_offer_v1_get_user_data(id); 3085 } 3086 3087 if (primary_selection_device->selection_offer != offer) { 3088 Wayland_primary_selection_offer_destroy(primary_selection_device->selection_offer); 3089 primary_selection_device->selection_offer = offer; 3090 } 3091 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 3092 ". In zwp_primary_selection_device_v1_listener . primary_selection_device_handle_selection on primary_selection_offer 0x%08x", 3093 (id ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)id) : -1)); 3094} 3095 3096static const struct zwp_primary_selection_device_v1_listener primary_selection_device_listener = { 3097 primary_selection_device_handle_offer, 3098 primary_selection_device_handle_selection 3099}; 3100 3101static void text_input_enter(void *data, 3102 struct zwp_text_input_v3 *zwp_text_input_v3, 3103 struct wl_surface *surface) 3104{ 3105 // No-op 3106} 3107 3108static void text_input_leave(void *data, 3109 struct zwp_text_input_v3 *zwp_text_input_v3, 3110 struct wl_surface *surface) 3111{ 3112 // No-op 3113} 3114 3115static void text_input_preedit_string(void *data, 3116 struct zwp_text_input_v3 *zwp_text_input_v3, 3117 const char *text, 3118 int32_t cursor_begin, 3119 int32_t cursor_end) 3120{ 3121 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 3122 seat->text_input.has_preedit = true; 3123 if (text) { 3124 int cursor_begin_utf8 = cursor_begin >= 0 ? (int)SDL_utf8strnlen(text, cursor_begin) : -1; 3125 int cursor_end_utf8 = cursor_end >= 0 ? (int)SDL_utf8strnlen(text, cursor_end) : -1; 3126 int cursor_size_utf8; 3127 if (cursor_end_utf8 >= 0) { 3128 if (cursor_begin_utf8 >= 0) { 3129 cursor_size_utf8 = cursor_end_utf8 - cursor_begin_utf8; 3130 } else { 3131 cursor_size_utf8 = cursor_end_utf8; 3132 } 3133 } else { 3134 cursor_size_utf8 = -1; 3135 } 3136 SDL_SendEditingText(text, cursor_begin_utf8, cursor_size_utf8); 3137 } else { 3138 SDL_SendEditingText("", 0, 0); 3139 } 3140} 3141 3142static void text_input_commit_string(void *data, 3143 struct zwp_text_input_v3 *zwp_text_input_v3, 3144 const char *text) 3145{ 3146 SDL_SendKeyboardText(text); 3147} 3148 3149static void text_input_delete_surrounding_text(void *data, 3150 struct zwp_text_input_v3 *zwp_text_input_v3, 3151 uint32_t before_length, 3152 uint32_t after_length) 3153{ 3154 // FIXME: Do we care about this event? 3155} 3156 3157static void text_input_done(void *data, 3158 struct zwp_text_input_v3 *zwp_text_input_v3, 3159 uint32_t serial) 3160{ 3161 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 3162 if (!seat->text_input.has_preedit) { 3163 SDL_SendEditingText("", 0, 0); 3164 } 3165 seat->text_input.has_preedit = false; 3166} 3167 3168static const struct zwp_text_input_v3_listener text_input_listener = { 3169 text_input_enter, 3170 text_input_leave, 3171 text_input_preedit_string, 3172 text_input_commit_string, 3173 text_input_delete_surrounding_text, 3174 text_input_done 3175}; 3176 3177static void Wayland_DataDeviceSetID(SDL_WaylandDataDevice *data_device) 3178{ 3179 if (!data_device->id_str) 3180#ifdef SDL_USE_LIBDBUS 3181 { 3182 SDL_DBusContext *dbus = SDL_DBus_GetContext(); 3183 if (dbus) { 3184 const char *id = dbus->bus_get_unique_name(dbus->session_conn); 3185 if (id) { 3186 data_device->id_str = SDL_strdup(id); 3187 } 3188 } 3189 } 3190 if (!data_device->id_str) 3191#endif 3192 { 3193 char id[24]; 3194 Uint64 pid = (Uint64)getpid(); 3195 SDL_snprintf(id, sizeof(id), "%" SDL_PRIu64, pid); 3196 data_device->id_str = SDL_strdup(id); 3197 } 3198} 3199 3200static void Wayland_SeatCreateDataDevice(SDL_WaylandSeat *seat) 3201{ 3202 if (!seat->display->data_device_manager) { 3203 return; 3204 } 3205 3206 SDL_WaylandDataDevice *data_device = SDL_calloc(1, sizeof(*data_device)); 3207 if (!data_device) { 3208 return; 3209 } 3210 3211 data_device->data_device = wl_data_device_manager_get_data_device( 3212 seat->display->data_device_manager, seat->wl_seat); 3213 data_device->seat = seat; 3214 3215 if (!data_device->data_device) { 3216 SDL_free(data_device); 3217 } else { 3218 Wayland_DataDeviceSetID(data_device); 3219 wl_data_device_set_user_data(data_device->data_device, data_device); 3220 wl_data_device_add_listener(data_device->data_device, 3221 &data_device_listener, data_device); 3222 seat->data_device = data_device; 3223 } 3224} 3225 3226void Wayland_DisplayInitDataDeviceManager(SDL_VideoData *display) 3227{ 3228 SDL_WaylandSeat *seat; 3229 wl_list_for_each (seat, &display->seat_list, link) { 3230 Wayland_SeatCreateDataDevice(seat); 3231 } 3232} 3233 3234static void Wayland_SeatCreatePrimarySelectionDevice(SDL_WaylandSeat *seat) 3235{ 3236 if (!seat->display->primary_selection_device_manager) { 3237 return; 3238 } 3239 3240 SDL_WaylandPrimarySelectionDevice *primary_selection_device = SDL_calloc(1, sizeof(*primary_selection_device)); 3241 if (!primary_selection_device) { 3242 return; 3243 } 3244 3245 primary_selection_device->primary_selection_device = zwp_primary_selection_device_manager_v1_get_device( 3246 seat->display->primary_selection_device_manager, seat->wl_seat); 3247 primary_selection_device->seat = seat; 3248 3249 if (!primary_selection_device->primary_selection_device) { 3250 SDL_free(primary_selection_device); 3251 } else { 3252 zwp_primary_selection_device_v1_set_user_data(primary_selection_device->primary_selection_device, 3253 primary_selection_device); 3254 zwp_primary_selection_device_v1_add_listener(primary_selection_device->primary_selection_device, 3255 &primary_selection_device_listener, primary_selection_device); 3256 seat->primary_selection_device = primary_selection_device; 3257 } 3258} 3259 3260void Wayland_DisplayInitPrimarySelectionDeviceManager(SDL_VideoData *display) 3261{ 3262 SDL_WaylandSeat *seat; 3263 wl_list_for_each (seat, &display->seat_list, link) { 3264 Wayland_SeatCreatePrimarySelectionDevice(seat); 3265 } 3266} 3267 3268static void Wayland_SeatCreateTextInput(SDL_WaylandSeat *seat) 3269{ 3270 if (seat->display->text_input_manager) { 3271 seat->text_input.zwp_text_input = zwp_text_input_manager_v3_get_text_input(seat->display->text_input_manager, seat->wl_seat); 3272 3273 if (seat->text_input.zwp_text_input) { 3274 zwp_text_input_v3_set_user_data(seat->text_input.zwp_text_input, seat); 3275 zwp_text_input_v3_add_listener(seat->text_input.zwp_text_input, 3276 &text_input_listener, seat); 3277 } 3278 } 3279} 3280 3281void Wayland_DisplayInitTextInputManager(SDL_VideoData *d, uint32_t id) 3282{ 3283 SDL_WaylandSeat *seat; 3284 wl_list_for_each(seat, &d->seat_list, link) { 3285 Wayland_SeatCreateTextInput(seat); 3286 } 3287} 3288 3289// Pen/Tablet support... 3290static void tablet_tool_handle_type(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t type) 3291{ 3292 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data; 3293 switch (type) { 3294 #define CASE(typ) case ZWP_TABLET_TOOL_V2_TYPE_##typ: sdltool->info.subtype = SDL_PEN_TYPE_##typ; return 3295 CASE(ERASER); 3296 CASE(PEN); 3297 CASE(PENCIL); 3298 CASE(AIRBRUSH); 3299 CASE(BRUSH); 3300 #undef CASE 3301 default: sdltool->info.subtype = SDL_PEN_TYPE_UNKNOWN; // we'll decline to add this when the `done` event comes through. 3302 } 3303} 3304 3305static void tablet_tool_handle_hardware_serial(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial_hi, uint32_t serial_lo) 3306{ 3307 // don't care about this atm. 3308} 3309 3310static void tablet_tool_handle_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t id_hi, uint32_t id_lo) 3311{ 3312 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data; 3313 sdltool->info.wacom_id = id_lo; 3314} 3315 3316static void tablet_tool_handle_capability(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t capability) 3317{ 3318 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data; 3319 switch (capability) { 3320 #define CASE(wltyp,sdltyp) case ZWP_TABLET_TOOL_V2_CAPABILITY_##wltyp: sdltool->info.capabilities |= sdltyp; return 3321 CASE(TILT, SDL_PEN_CAPABILITY_XTILT | SDL_PEN_CAPABILITY_YTILT); 3322 CASE(PRESSURE, SDL_PEN_CAPABILITY_PRESSURE); 3323 CASE(DISTANCE, SDL_PEN_CAPABILITY_DISTANCE); 3324 CASE(ROTATION, SDL_PEN_CAPABILITY_ROTATION); 3325 CASE(SLIDER, SDL_PEN_CAPABILITY_SLIDER); 3326 #undef CASE 3327 default: break; // unsupported here. 3328 } 3329} 3330 3331static void tablet_tool_handle_done(void *data, struct zwp_tablet_tool_v2 *tool) 3332{ 3333 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data; 3334 if (sdltool->info.subtype != SDL_PEN_TYPE_UNKNOWN) { // don't tell SDL about it if we don't know its role. 3335 SDL_Window *window = sdltool->focus ? sdltool->focus->sdlwindow : NULL; 3336 sdltool->instance_id = SDL_AddPenDevice(0, NULL, window, &sdltool->info, sdltool, false); 3337 } 3338} 3339 3340static void tablet_tool_handle_removed(void *data, struct zwp_tablet_tool_v2 *tool) 3341{ 3342 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data; 3343 if (sdltool->instance_id) { 3344 SDL_Window *window = sdltool->focus ? sdltool->focus->sdlwindow : NULL; 3345 SDL_RemovePenDevice(0, window, sdltool->instance_id); 3346 } 3347 3348 Wayland_CursorStateRelease(&sdltool->cursor_state); 3349 zwp_tablet_tool_v2_destroy(sdltool->wltool); 3350 WAYLAND_wl_list_remove(&sdltool->link); 3351 SDL_free(sdltool); 3352} 3353 3354static void tablet_tool_handle_proximity_in(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface) 3355{ 3356 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data; 3357 SDL_WindowData *windowdata = surface ? Wayland_GetWindowDataForOwnedSurface(surface) : NULL; 3358 sdltool->focus = windowdata && windowdata->surface == surface ? windowdata : NULL; 3359 sdltool->proximity_serial = serial; 3360 sdltool->frame.have_proximity = true; 3361 sdltool->frame.in_proximity = true; 3362 3363 // According to the docs, this should be followed by a frame event, where we'll send our SDL events. 3364} 3365 3366static void tablet_tool_handle_proximity_out(void *data, struct zwp_tablet_tool_v2 *tool) 3367{ 3368 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data; 3369 sdltool->frame.have_proximity = true; 3370 sdltool->frame.in_proximity = false; 3371} 3372 3373static void tablet_tool_handle_down(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial) 3374{ 3375 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data; 3376 sdltool->frame.tool_state = WAYLAND_TABLET_TOOL_STATE_DOWN; 3377} 3378 3379static void tablet_tool_handle_up(void *data, struct zwp_tablet_tool_v2 *tool) 3380{ 3381 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data; 3382 sdltool->frame.tool_state = WAYLAND_TABLET_TOOL_STATE_UP; 3383} 3384 3385static void tablet_tool_handle_motion(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t sx_w, wl_fixed_t sy_w) 3386{ 3387 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data; 3388 SDL_WindowData *windowdata = sdltool->focus; 3389 if (windowdata) { 3390 sdltool->frame.x = (float)(wl_fixed_to_double(sx_w) * windowdata->pointer_scale.x); 3391 sdltool->frame.y = (float)(wl_fixed_to_double(sy_w) * windowdata->pointer_scale.y); 3392 sdltool->frame.have_motion = true; 3393 } 3394} 3395 3396static void tablet_tool_handle_pressure(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t pressure) 3397{ 3398 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data; 3399 sdltool->frame.axes[SDL_PEN_AXIS_PRESSURE] = ((float) pressure) / 65535.0f; 3400 sdltool->frame.axes_set |= (1u << SDL_PEN_AXIS_PRESSURE); 3401 if (pressure) { 3402 sdltool->frame.axes[SDL_PEN_AXIS_DISTANCE] = 0.0f; 3403 sdltool->frame.axes_set |= (1u << SDL_PEN_AXIS_DISTANCE); 3404 } 3405} 3406 3407static void tablet_tool_handle_distance(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t distance) 3408{ 3409 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data; 3410 sdltool->frame.axes[SDL_PEN_AXIS_DISTANCE] = ((float) distance) / 65535.0f; 3411 sdltool->frame.axes_set |= (1u << SDL_PEN_AXIS_DISTANCE); 3412 if (distance) { 3413 sdltool->frame.axes[SDL_PEN_AXIS_PRESSURE] = 0.0f; 3414 sdltool->frame.axes_set |= (1u << SDL_PEN_AXIS_PRESSURE); 3415 } 3416} 3417 3418static void tablet_tool_handle_tilt(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t xtilt, wl_fixed_t ytilt) 3419{ 3420 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data; 3421 sdltool->frame.axes[SDL_PEN_AXIS_XTILT] = (float)(wl_fixed_to_double(xtilt)); 3422 sdltool->frame.axes[SDL_PEN_AXIS_YTILT] = (float)(wl_fixed_to_double(ytilt)); 3423 sdltool->frame.axes_set |= (1u << SDL_PEN_AXIS_XTILT) | (1u << SDL_PEN_AXIS_YTILT); 3424} 3425 3426static void tablet_tool_handle_button(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial, uint32_t button, uint32_t state) 3427{ 3428 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data; 3429 int sdlbutton; 3430 3431 switch (button) { 3432 case BTN_STYLUS: 3433 sdlbutton = 1; 3434 break; 3435 case BTN_STYLUS2: 3436 sdlbutton = 2; 3437 break; 3438 case BTN_STYLUS3: 3439 sdlbutton = 3; 3440 break; 3441 default: 3442 return; // don't care about this button, I guess. 3443 } 3444 3445 SDL_assert((sdlbutton >= 1) && (sdlbutton <= SDL_arraysize(sdltool->frame.buttons))); 3446 sdltool->frame.buttons[sdlbutton-1] = (state == ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED) ? WAYLAND_TABLET_TOOL_BUTTON_DOWN : WAYLAND_TABLET_TOOL_BUTTON_UP; 3447} 3448 3449static void tablet_tool_handle_rotation(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t degrees) 3450{ 3451 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data; 3452 const float rotation = (float)(wl_fixed_to_double(degrees)); 3453 sdltool->frame.axes[SDL_PEN_AXIS_ROTATION] = (rotation > 180.0f) ? (rotation - 360.0f) : rotation; // map to -180.0f ... 179.0f range 3454 sdltool->frame.axes_set |= (1u << SDL_PEN_AXIS_ROTATION); 3455} 3456 3457static void tablet_tool_handle_slider(void *data, struct zwp_tablet_tool_v2 *tool, int32_t position) 3458{ 3459 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data; 3460 sdltool->frame.axes[SDL_PEN_AXIS_SLIDER] = position / 65535.f; 3461 sdltool->frame.axes_set |= (1u << SDL_PEN_AXIS_SLIDER); 3462} 3463 3464static void tablet_tool_handle_wheel(void *data, struct zwp_tablet_tool_v2 *tool, int32_t degrees, int32_t clicks) 3465{ 3466 // not supported at the moment 3467} 3468 3469static void tablet_tool_handle_frame(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t time) 3470{ 3471 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data; 3472 const SDL_PenID instance_id = sdltool->instance_id; 3473 if (!instance_id) { 3474 return; // Not a pen we report on. 3475 } 3476 3477 const Uint64 timestamp = Wayland_AdjustEventTimestampBase(Wayland_EventTimestampMSToNS(time)); 3478 SDL_Window *window = sdltool->focus ? sdltool->focus->sdlwindow : NULL; 3479 3480 if (sdltool->frame.have_proximity && sdltool->frame.in_proximity) { 3481 SDL_SendPenProximity(timestamp, instance_id, window, true, true); 3482 Wayland_TabletToolUpdateCursor(sdltool); 3483 } 3484 3485 // !!! FIXME: Should hit testing be done if pens generate pointer motion? 3486 3487 // I don't know if this is necessary (or makes sense), but send motion before pen downs, but after pen ups, so you don't get unexpected lines drawn. 3488 if (sdltool->frame.have_motion && sdltool->frame.tool_state) { 3489 if (sdltool->frame.tool_state == WAYLAND_TABLET_TOOL_STATE_DOWN) { 3490 SDL_SendPenMotion(timestamp, instance_id, window, sdltool->frame.x, sdltool->frame.y); 3491 SDL_SendPenTouch(timestamp, instance_id, window, false, true); // !!! FIXME: how do we know what tip is in use? 3492 } else { 3493 SDL_SendPenTouch(timestamp, instance_id, window, false, false); // !!! FIXME: how do we know what tip is in use? 3494 SDL_SendPenMotion(timestamp, instance_id, window, sdltool->frame.x, sdltool->frame.y); 3495 } 3496 } else { 3497 if (sdltool->frame.tool_state) { 3498 SDL_SendPenTouch(timestamp, instance_id, window, false, sdltool->frame.tool_state == WAYLAND_TABLET_TOOL_STATE_DOWN); // !!! FIXME: how do we know what tip is in use? 3499 } 3500 3501 if (sdltool->frame.have_motion) { 3502 SDL_SendPenMotion(timestamp, instance_id, window, sdltool->frame.x, sdltool->frame.y); 3503 } 3504 } 3505 3506 for (SDL_PenAxis i = 0; i < SDL_PEN_AXIS_COUNT; i++) { 3507 if (sdltool->frame.axes_set & (1u << i)) { 3508 SDL_SendPenAxis(timestamp, instance_id, window, i, sdltool->frame.axes[i]); 3509 } 3510 } 3511 3512 for (int i = 0; i < SDL_arraysize(sdltool->frame.buttons); i++) { 3513 const int state = sdltool->frame.buttons[i]; 3514 if (state) { 3515 SDL_SendPenButton(timestamp, instance_id, window, (Uint8)(i + 1), state == WAYLAND_TABLET_TOOL_BUTTON_DOWN); 3516 } 3517 } 3518 3519 if (sdltool->frame.have_proximity && !sdltool->frame.in_proximity) { 3520 SDL_SendPenProximity(timestamp, instance_id, window, false, false); 3521 sdltool->focus = NULL; 3522 Wayland_TabletToolUpdateCursor(sdltool); 3523 } 3524 3525 // Reset for the next frame. 3526 SDL_zero(sdltool->frame); 3527} 3528 3529static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = { 3530 tablet_tool_handle_type, 3531 tablet_tool_handle_hardware_serial, 3532 tablet_tool_handle_hardware_id_wacom, 3533 tablet_tool_handle_capability, 3534 tablet_tool_handle_done, 3535 tablet_tool_handle_removed, 3536 tablet_tool_handle_proximity_in, 3537 tablet_tool_handle_proximity_out, 3538 tablet_tool_handle_down, 3539 tablet_tool_handle_up, 3540 tablet_tool_handle_motion, 3541 tablet_tool_handle_pressure, 3542 tablet_tool_handle_distance, 3543 tablet_tool_handle_tilt, 3544 tablet_tool_handle_rotation, 3545 tablet_tool_handle_slider, 3546 tablet_tool_handle_wheel, 3547 tablet_tool_handle_button, 3548 tablet_tool_handle_frame 3549}; 3550 3551 3552static void tablet_seat_handle_tablet_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_v2 *tablet) 3553{ 3554 // don't care atm. 3555} 3556 3557static void tablet_seat_handle_tool_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_tool_v2 *tool) 3558{ 3559 SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 3560 SDL_WaylandPenTool *sdltool = SDL_calloc(1, sizeof(*sdltool)); 3561 3562 if (sdltool) { // if allocation failed, oh well, we won't report this device. 3563 sdltool->wltool = tool; 3564 sdltool->info.max_tilt = -1.0f; 3565 sdltool->info.num_buttons = -1; 3566 3567 if (seat->display->cursor_shape_manager) { 3568 sdltool->cursor_state.cursor_shape = wp_cursor_shape_manager_v1_get_tablet_tool_v2(seat->display->cursor_shape_manager, tool); 3569 } 3570 3571 WAYLAND_wl_list_insert(&seat->tablet.tool_list, &sdltool->link); 3572 3573 // this will send a bunch of zwp_tablet_tool_v2 events right up front to tell 3574 // us device details, with a "done" event to let us know we have everything. 3575 zwp_tablet_tool_v2_add_listener(tool, &tablet_tool_listener, sdltool); 3576 } 3577} 3578 3579static void tablet_seat_handle_pad_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_pad_v2 *pad) 3580{ 3581 // we don't care atm. 3582} 3583 3584static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = { 3585 tablet_seat_handle_tablet_added, 3586 tablet_seat_handle_tool_added, 3587 tablet_seat_handle_pad_added 3588}; 3589 3590static void Wayland_SeatInitTabletSupport(SDL_WaylandSeat *seat) 3591{ 3592 seat->tablet.wl_tablet_seat = zwp_tablet_manager_v2_get_tablet_seat(seat->display->tablet_manager, seat->wl_seat); 3593 zwp_tablet_seat_v2_add_listener(seat->tablet.wl_tablet_seat, &tablet_seat_listener, seat); 3594} 3595 3596void Wayland_DisplayInitTabletManager(SDL_VideoData *display) 3597{ 3598 SDL_WaylandSeat *seat; 3599 wl_list_for_each (seat, &display->seat_list, link) { 3600 Wayland_SeatInitTabletSupport(seat); 3601 } 3602} 3603 3604static void Wayland_remove_all_pens_callback(SDL_PenID instance_id, void *handle, void *userdata) 3605{ 3606 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) handle; 3607 3608 Wayland_CursorStateRelease(&sdltool->cursor_state); 3609 zwp_tablet_tool_v2_destroy(sdltool->wltool); 3610 SDL_free(sdltool); 3611} 3612 3613static void Wayland_SeatDestroyTablet(SDL_WaylandSeat *seat, bool shutting_down) 3614{ 3615 if (!shutting_down) { 3616 SDL_WaylandPenTool *tool, *temp; 3617 wl_list_for_each_safe (tool, temp, &seat->tablet.tool_list, link) { 3618 // Remove all tools for this seat, sending PROXIMITY_OUT events. 3619 tablet_tool_handle_removed(tool, tool->wltool); 3620 } 3621 } else { 3622 // Shutting down, just delete everything. 3623 SDL_RemoveAllPenDevices(Wayland_remove_all_pens_callback, NULL); 3624 } 3625 3626 if (seat->tablet.wl_tablet_seat) { 3627 zwp_tablet_seat_v2_destroy(seat->tablet.wl_tablet_seat); 3628 seat->tablet.wl_tablet_seat = NULL; 3629 } 3630 3631 SDL_zero(seat->tablet); 3632 WAYLAND_wl_list_init(&seat->tablet.tool_list); 3633} 3634 3635void Wayland_DisplayCreateSeat(SDL_VideoData *display, struct wl_seat *wl_seat, Uint32 id) 3636{ 3637 SDL_WaylandSeat *seat = SDL_calloc(1, sizeof(SDL_WaylandSeat)); 3638 if (!seat) { 3639 return; 3640 } 3641 3642 // Keep the seats in the order in which they were added. 3643 WAYLAND_wl_list_insert(display->seat_list.prev, &seat->link); 3644 3645 WAYLAND_wl_list_init(&seat->touch.points); 3646 WAYLAND_wl_list_init(&seat->tablet.tool_list); 3647 seat->wl_seat = wl_seat; 3648 seat->display = display; 3649 seat->registry_id = id; 3650 3651 Wayland_SeatCreateDataDevice(seat); 3652 Wayland_SeatCreatePrimarySelectionDevice(seat); 3653 Wayland_SeatCreateTextInput(seat); 3654 3655 wl_seat_set_user_data(seat->wl_seat, seat); 3656 wl_seat_add_listener(seat->wl_seat, &seat_listener, seat); 3657 3658 if (display->tablet_manager) { 3659 Wayland_SeatInitTabletSupport(seat); 3660 } 3661} 3662 3663void Wayland_DisplayRemoveWindowReferencesFromSeats(SDL_VideoData *display, SDL_WindowData *window) 3664{ 3665 SDL_WaylandSeat *seat; 3666 wl_list_for_each (seat, &display->seat_list, link) 3667 { 3668 if (seat->keyboard.focus == window) { 3669 keyboard_handle_leave(seat, seat->keyboard.wl_keyboard, 0, window->surface); 3670 } 3671 3672 if (seat->pointer.focus == window) { 3673 seat->pointer.pending_frame.leave_surface = seat->pointer.focus->surface; 3674 pointer_dispatch_leave(seat, true); 3675 seat->pointer.pending_frame.leave_surface = NULL; 3676 } 3677 3678 // Need the safe loop variant here as cancelling a touch point removes it from the list. 3679 SDL_WaylandTouchPoint *tp, *temp; 3680 wl_list_for_each_safe (tp, temp, &seat->touch.points, link) { 3681 if (tp->surface == window->surface) { 3682 Wayland_SeatCancelTouch(seat, tp); 3683 } 3684 } 3685 3686 SDL_WaylandPenTool *tool; 3687 wl_list_for_each (tool, &seat->tablet.tool_list, link) { 3688 if (tool->focus == window) { 3689 tool->focus = NULL; 3690 Wayland_TabletToolUpdateCursor(tool); 3691 if (tool->instance_id) { 3692 SDL_RemovePenDevice(0, window->sdlwindow, tool->instance_id); 3693 tool->instance_id = 0; 3694 } 3695 } 3696 } 3697 } 3698} 3699 3700void Wayland_SeatDestroy(SDL_WaylandSeat *seat, bool shutting_down) 3701{ 3702 if (!seat) { 3703 return; 3704 } 3705 3706 SDL_free(seat->name); 3707 3708 if (seat->data_device) { 3709 Wayland_data_device_clear_selection(seat->data_device); 3710 if (seat->data_device->selection_offer) { 3711 Wayland_data_offer_destroy(seat->data_device->selection_offer); 3712 } 3713 if (seat->data_device->drag_offer) { 3714 Wayland_data_offer_destroy(seat->data_device->drag_offer); 3715 } 3716 if (seat->data_device->data_device) { 3717 if (wl_data_device_get_version(seat->data_device->data_device) >= WL_DATA_DEVICE_RELEASE_SINCE_VERSION) { 3718 wl_data_device_release(seat->data_device->data_device); 3719 } else { 3720 wl_data_device_destroy(seat->data_device->data_device); 3721 } 3722 } 3723 SDL_free(seat->data_device->id_str); 3724 SDL_free(seat->data_device); 3725 } 3726 3727 if (seat->primary_selection_device) { 3728 if (seat->primary_selection_device->selection_offer) { 3729 Wayland_primary_selection_offer_destroy(seat->primary_selection_device->selection_offer); 3730 } 3731 if (seat->primary_selection_device->selection_source) { 3732 Wayland_primary_selection_source_destroy(seat->primary_selection_device->selection_source); 3733 } 3734 if (seat->primary_selection_device->primary_selection_device) { 3735 zwp_primary_selection_device_v1_destroy(seat->primary_selection_device->primary_selection_device); 3736 } 3737 SDL_free(seat->primary_selection_device); 3738 } 3739 3740 if (seat->text_input.zwp_text_input) { 3741 zwp_text_input_v3_destroy(seat->text_input.zwp_text_input); 3742 } 3743 3744 Wayland_SeatDestroyKeyboard(seat); 3745 Wayland_SeatDestroyPointer(seat); 3746 Wayland_SeatDestroyTouch(seat); 3747 Wayland_SeatDestroyTablet(seat, shutting_down); 3748 3749 if (wl_seat_get_version(seat->wl_seat) >= WL_SEAT_RELEASE_SINCE_VERSION) { 3750 wl_seat_release(seat->wl_seat); 3751 } else { 3752 wl_seat_destroy(seat->wl_seat); 3753 } 3754 3755 WAYLAND_wl_list_remove(&seat->link); 3756 SDL_free(seat); 3757} 3758 3759static void Wayland_SeatUpdateRelativePointer(SDL_WaylandSeat *seat) 3760{ 3761 if (seat->display->relative_pointer_manager) { 3762 bool relative_focus = false; 3763 3764 if (seat->pointer.focus) { 3765 /* If a seat has both keyboard and pointer capabilities, relative focus will follow the keyboard 3766 * attached to that seat. Otherwise, relative focus will be gained if any other seat has keyboard 3767 * focus on the window with pointer focus. 3768 */ 3769 if (seat->pointer.focus->sdlwindow->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE) { 3770 if (seat->keyboard.wl_keyboard) { 3771 relative_focus = seat->keyboard.focus == seat->pointer.focus; 3772 } else { 3773 relative_focus = seat->pointer.focus->keyboard_focus_count != 0; 3774 } 3775 } else { 3776 relative_focus = SDL_GetMouse()->warp_emulation_active; 3777 } 3778 } 3779 3780 if (relative_focus) { 3781 if (!seat->pointer.relative_pointer) { 3782 seat->pointer.relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(seat->display->relative_pointer_manager, seat->pointer.wl_pointer); 3783 zwp_relative_pointer_v1_add_listener(seat->pointer.relative_pointer, &relative_pointer_listener, seat); 3784 } 3785 } else if (seat->pointer.relative_pointer) { 3786 zwp_relative_pointer_v1_destroy(seat->pointer.relative_pointer); 3787 seat->pointer.relative_pointer = NULL; 3788 } 3789 } 3790} 3791 3792static void Wayland_SeatUpdateKeyboardGrab(SDL_WaylandSeat *seat) 3793{ 3794 SDL_VideoData *display = seat->display; 3795 3796 if (display->key_inhibitor_manager) { 3797 // Destroy the existing key inhibitor. 3798 if (seat->keyboard.key_inhibitor) { 3799 zwp_keyboard_shortcuts_inhibitor_v1_destroy(seat->keyboard.key_inhibitor); 3800 seat->keyboard.key_inhibitor = NULL; 3801 } 3802 3803 if (seat->keyboard.wl_keyboard) { 3804 SDL_WindowData *w = seat->keyboard.focus; 3805 if (w) { 3806 SDL_Window *window = w->sdlwindow; 3807 3808 // Don't grab the keyboard if it shouldn't be grabbed. 3809 if (window->flags & SDL_WINDOW_KEYBOARD_GRABBED) { 3810 seat->keyboard.key_inhibitor = 3811 zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(display->key_inhibitor_manager, w->surface, seat->wl_seat); 3812 } 3813 } 3814 } 3815 } 3816} 3817 3818void Wayland_SeatUpdatePointerGrab(SDL_WaylandSeat *seat) 3819{ 3820 SDL_VideoData *display = seat->display; 3821 Wayland_SeatUpdateRelativePointer(seat); 3822 3823 if (display->pointer_constraints) { 3824 if (seat->pointer.locked_pointer && !seat->pointer.relative_pointer) { 3825 zwp_locked_pointer_v1_destroy(seat->pointer.locked_pointer); 3826 seat->pointer.locked_pointer = NULL; 3827 3828 // Update the cursor after destroying a relative move lock. 3829 Wayland_SeatUpdatePointerCursor(seat); 3830 } 3831 3832 if (seat->pointer.wl_pointer) { 3833 // If relative mode is active, and the pointer focus matches the keyboard focus, lock it. 3834 if (seat->pointer.relative_pointer) { 3835 if (!seat->pointer.locked_pointer) { 3836 // Creating a lock on a surface with an active confinement region on the same seat is a protocol error. 3837 if (seat->pointer.confined_pointer) { 3838 zwp_confined_pointer_v1_destroy(seat->pointer.confined_pointer); 3839 seat->pointer.confined_pointer = NULL; 3840 } 3841 3842 seat->pointer.locked_pointer = zwp_pointer_constraints_v1_lock_pointer(display->pointer_constraints, 3843 seat->pointer.focus->surface, 3844 seat->pointer.wl_pointer, NULL, 3845 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); 3846 zwp_locked_pointer_v1_add_listener(seat->pointer.locked_pointer, &locked_pointer_listener, seat); 3847 3848 // Ensure that the relative pointer is hidden, if required. 3849 Wayland_SeatUpdatePointerCursor(seat); 3850 } 3851 3852 // Locked the cursor for relative mode, nothing more to do. 3853 return; 3854 } 3855 3856 /* A confine may already be active, in which case we should destroy it and create a new one 3857 * in case it changed size. 3858 */ 3859 if (seat->pointer.confined_pointer) { 3860 zwp_confined_pointer_v1_destroy(seat->pointer.confined_pointer); 3861 seat->pointer.confined_pointer = NULL; 3862 } 3863 3864 SDL_WindowData *w = seat->pointer.focus; 3865 if (!w) { 3866 return; 3867 } 3868 3869 SDL_Window *window = w->sdlwindow; 3870 3871 // Don't confine the pointer if the window doesn't have input focus, or it shouldn't be confined. 3872 if (!(window->flags & SDL_WINDOW_INPUT_FOCUS) || 3873 (!(window->flags & SDL_WINDOW_MOUSE_GRABBED) && SDL_RectEmpty(&window->mouse_rect))) { 3874 return; 3875 } 3876 3877 struct wl_region *confine_rect = NULL; 3878 if (!SDL_RectEmpty(&window->mouse_rect)) { 3879 SDL_Rect scaled_mouse_rect; 3880 Wayland_GetScaledMouseRect(window, &scaled_mouse_rect); 3881 3882 confine_rect = wl_compositor_create_region(display->compositor); 3883 wl_region_add(confine_rect, 3884 scaled_mouse_rect.x, 3885 scaled_mouse_rect.y, 3886 scaled_mouse_rect.w, 3887 scaled_mouse_rect.h); 3888 3889 /* Some compositors will only confine the pointer to an arbitrary region if the pointer 3890 * is already within the confinement area when it is created. Warp the pointer to the 3891 * closest point within the confinement zone if outside. 3892 */ 3893 if (!SDL_PointInRect(&seat->pointer.last_motion, &scaled_mouse_rect)) { 3894 /* Warp the pointer to the closest point within the confinement zone if outside, 3895 * The confinement region will be created when a true position event is received. 3896 */ 3897 int closest_x = seat->pointer.last_motion.x; 3898 int closest_y = seat->pointer.last_motion.y; 3899 3900 if (closest_x < scaled_mouse_rect.x) { 3901 closest_x = scaled_mouse_rect.x; 3902 } else if (closest_x >= scaled_mouse_rect.x + scaled_mouse_rect.w) { 3903 closest_x = (scaled_mouse_rect.x + scaled_mouse_rect.w) - 1; 3904 } 3905 3906 if (closest_y < scaled_mouse_rect.y) { 3907 closest_y = scaled_mouse_rect.y; 3908 } else if (closest_y >= scaled_mouse_rect.y + scaled_mouse_rect.h) { 3909 closest_y = (scaled_mouse_rect.y + scaled_mouse_rect.h) - 1; 3910 } 3911 3912 Wayland_SeatWarpMouse(seat, w, closest_x, closest_y); 3913 } 3914 } 3915 3916 if (confine_rect || (window->flags & SDL_WINDOW_MOUSE_GRABBED)) { 3917 if (window->mouse_rect.w != 1 && window->mouse_rect.h != 1) { 3918 seat->pointer.confined_pointer = 3919 zwp_pointer_constraints_v1_confine_pointer(display->pointer_constraints, 3920 w->surface, 3921 seat->pointer.wl_pointer, 3922 confine_rect, 3923 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); 3924 zwp_confined_pointer_v1_add_listener(seat->pointer.confined_pointer, 3925 &confined_pointer_listener, 3926 seat); 3927 } else { 3928 /* Use a lock for 1x1 confinement regions, as the pointer can exhibit subpixel motion otherwise. 3929 * A null region is used since the warp *should* have placed the pointer where we want it, but 3930 * better to lock it slightly off than let the pointer escape, as confining to a specific region 3931 * seems to be a racy operation on some compositors. 3932 */ 3933 seat->pointer.locked_pointer = 3934 zwp_pointer_constraints_v1_lock_pointer(display->pointer_constraints, 3935 w->surface, 3936 seat->pointer.wl_pointer, 3937 NULL, 3938 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); 3939 zwp_locked_pointer_v1_add_listener(seat->pointer.locked_pointer, 3940 &locked_pointer_listener, 3941 seat); 3942 } 3943 if (confine_rect) { 3944 wl_region_destroy(confine_rect); 3945 } 3946 } 3947 } 3948 } 3949} 3950 3951void Wayland_DisplayUpdatePointerGrabs(SDL_VideoData *display, SDL_WindowData *window) 3952{ 3953 SDL_WaylandSeat *seat; 3954 wl_list_for_each (seat, &display->seat_list, link) { 3955 if (!window || seat->pointer.focus == window) { 3956 Wayland_SeatUpdatePointerGrab(seat); 3957 } 3958 } 3959} 3960 3961void Wayland_DisplayUpdateKeyboardGrabs(SDL_VideoData *display, SDL_WindowData *window) 3962{ 3963 SDL_WaylandSeat *seat; 3964 wl_list_for_each (seat, &display->seat_list, link) { 3965 if (!window || seat->keyboard.focus == window) { 3966 Wayland_SeatUpdateKeyboardGrab(seat); 3967 } 3968 } 3969} 3970 3971void Wayland_UpdateImplicitGrabSerial(SDL_WaylandSeat *seat, Uint32 serial) 3972{ 3973 if (serial > seat->last_implicit_grab_serial) { 3974 seat->last_implicit_grab_serial = serial; 3975 seat->display->last_implicit_grab_seat = seat; 3976 Wayland_data_device_set_serial(seat->data_device, serial); 3977 Wayland_primary_selection_device_set_serial(seat->primary_selection_device, serial); 3978 } 3979} 3980 3981#endif // SDL_VIDEO_DRIVER_WAYLAND 3982[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.