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