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