diff --git a/src/events/SDL_keyboard.c b/src/events/SDL_keyboard.c index 7f1ac7aaa1..766a095257 100644 --- a/src/events/SDL_keyboard.c +++ b/src/events/SDL_keyboard.c @@ -58,9 +58,6 @@ typedef struct SDL_Keyboard Uint8 keysource[SDL_SCANCODE_COUNT]; bool keystate[SDL_SCANCODE_COUNT]; SDL_Keymap *keymap; - bool french_numbers; - bool latin_letters; - bool thai_keyboard; Uint32 keycode_options; bool autorelease_pending; Uint64 hardware_timestamp; @@ -175,6 +172,19 @@ void SDL_RemoveKeyboard(SDL_KeyboardID keyboardID, bool send_event) } } +void SDL_SetKeyboardName(SDL_KeyboardID keyboardID, const char *name) +{ + SDL_assert(keyboardID != 0); + + const int keyboard_index = SDL_GetKeyboardIndex(keyboardID); + + if (keyboard_index >= 0) { + SDL_KeyboardInstance *instance = &SDL_keyboards[keyboard_index]; + SDL_free(instance->name); + instance->name = SDL_strdup(name ? name : ""); + } +} + bool SDL_HasKeyboard(void) { return (SDL_keyboard_count > 0); @@ -232,14 +242,15 @@ void SDL_ResetKeyboard(void) SDL_Keymap *SDL_GetCurrentKeymap(void) { SDL_Keyboard *keyboard = &SDL_keyboard; + SDL_Keymap *keymap = SDL_keyboard.keymap; - if (keyboard->thai_keyboard) { + if (keymap && keymap->thai_keyboard) { // Thai keyboards are QWERTY plus Thai characters, use the default QWERTY keymap return NULL; } if ((keyboard->keycode_options & KEYCODE_OPTION_LATIN_LETTERS) && - !keyboard->latin_letters) { + keymap && !keymap->latin_letters) { // We'll use the default QWERTY keymap return NULL; } @@ -251,35 +262,39 @@ void SDL_SetKeymap(SDL_Keymap *keymap, bool send_event) { SDL_Keyboard *keyboard = &SDL_keyboard; - if (keyboard->keymap) { + if (keyboard->keymap && keyboard->keymap->auto_release) { SDL_DestroyKeymap(keyboard->keymap); } keyboard->keymap = keymap; - // Detect French number row (all symbols) - keyboard->french_numbers = true; - for (int i = SDL_SCANCODE_1; i <= SDL_SCANCODE_0; ++i) { - if (SDL_isdigit(SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_NONE)) || - !SDL_isdigit(SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_SHIFT))) { - keyboard->french_numbers = false; - break; - } - } + if (keymap && !keymap->layout_determined) { + keymap->layout_determined = true; - // Detect non-Latin keymap - keyboard->thai_keyboard = false; - keyboard->latin_letters = false; - for (int i = SDL_SCANCODE_A; i <= SDL_SCANCODE_D; ++i) { - SDL_Keycode key = SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_NONE); - if (key <= 0xFF) { - keyboard->latin_letters = true; - break; + // Detect French number row (all symbols) + keymap->french_numbers = true; + for (int i = SDL_SCANCODE_1; i <= SDL_SCANCODE_0; ++i) { + if (SDL_isdigit(SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_NONE)) || + !SDL_isdigit(SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_SHIFT))) { + keymap->french_numbers = false; + break; + } } - if (key >= 0x0E00 && key <= 0x0E7F) { - keyboard->thai_keyboard = true; - break; + // Detect non-Latin keymap + keymap->thai_keyboard = false; + keymap->latin_letters = false; + for (int i = SDL_SCANCODE_A; i <= SDL_SCANCODE_D; ++i) { + SDL_Keycode key = SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_NONE); + if (key <= 0xFF) { + keymap->latin_letters = true; + break; + } + + if (key >= 0x0E00 && key <= 0x0E7F) { + keymap->thai_keyboard = true; + break; + } } } @@ -308,7 +323,7 @@ static void SetKeymapEntry(SDL_Scancode scancode, SDL_Keymod modstate, SDL_Keyco SDL_Keyboard *keyboard = &SDL_keyboard; if (!keyboard->keymap) { - keyboard->keymap = SDL_CreateKeymap(); + keyboard->keymap = SDL_CreateKeymap(true); } SDL_SetKeymapEntry(keyboard->keymap, scancode, modstate, keycode); @@ -483,7 +498,7 @@ SDL_Keycode SDL_GetKeyFromScancode(SDL_Scancode scancode, SDL_Keymod modstate, b modstate = SDL_KMOD_NONE; if ((keyboard->keycode_options & KEYCODE_OPTION_FRENCH_NUMBERS) && - keyboard->french_numbers && + keymap && keymap->french_numbers && (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_0)) { // Add the shift state to generate a numeric keycode modstate |= SDL_KMOD_SHIFT; @@ -876,7 +891,7 @@ void SDL_QuitKeyboard(void) SDL_free(SDL_keyboards); SDL_keyboards = NULL; - if (SDL_keyboard.keymap) { + if (SDL_keyboard.keymap && SDL_keyboard.keymap->auto_release) { SDL_DestroyKeymap(SDL_keyboard.keymap); SDL_keyboard.keymap = NULL; } diff --git a/src/events/SDL_keyboard_c.h b/src/events/SDL_keyboard_c.h index ddfb5c5747..9a6589ddb2 100644 --- a/src/events/SDL_keyboard_c.h +++ b/src/events/SDL_keyboard_c.h @@ -43,6 +43,9 @@ extern void SDL_AddKeyboard(SDL_KeyboardID keyboardID, const char *name, bool se // A keyboard has been removed from the system extern void SDL_RemoveKeyboard(SDL_KeyboardID keyboardID, bool send_event); +// Set or update the name of a keyboard instance. +extern void SDL_SetKeyboardName(SDL_KeyboardID keyboardID, const char *name); + // Set the mapping of scancode to key codes extern void SDL_SetKeymap(SDL_Keymap *keymap, bool send_event); diff --git a/src/events/SDL_keymap.c b/src/events/SDL_keymap.c index bd08786073..f99a7975dc 100644 --- a/src/events/SDL_keymap.c +++ b/src/events/SDL_keymap.c @@ -23,22 +23,17 @@ #include "SDL_keymap_c.h" #include "SDL_keyboard_c.h" -struct SDL_Keymap -{ - SDL_HashTable *scancode_to_keycode; - SDL_HashTable *keycode_to_scancode; -}; - static SDL_Keycode SDL_GetDefaultKeyFromScancode(SDL_Scancode scancode, SDL_Keymod modstate); static SDL_Scancode SDL_GetDefaultScancodeFromKey(SDL_Keycode key, SDL_Keymod *modstate); -SDL_Keymap *SDL_CreateKeymap(void) +SDL_Keymap *SDL_CreateKeymap(bool auto_release) { - SDL_Keymap *keymap = (SDL_Keymap *)SDL_malloc(sizeof(*keymap)); + SDL_Keymap *keymap = (SDL_Keymap *)SDL_calloc(1, sizeof(*keymap)); if (!keymap) { return NULL; } + keymap->auto_release = auto_release; keymap->scancode_to_keycode = SDL_CreateHashTable(256, false, SDL_HashID, SDL_KeyMatchID, NULL, NULL); keymap->keycode_to_scancode = SDL_CreateHashTable(256, false, SDL_HashID, SDL_KeyMatchID, NULL, NULL); if (!keymap->scancode_to_keycode || !keymap->keycode_to_scancode) { diff --git a/src/events/SDL_keymap_c.h b/src/events/SDL_keymap_c.h index 9c8039781c..a9c2c171fb 100644 --- a/src/events/SDL_keymap_c.h +++ b/src/events/SDL_keymap_c.h @@ -23,10 +23,19 @@ #ifndef SDL_keymap_c_h_ #define SDL_keymap_c_h_ -typedef struct SDL_Keymap SDL_Keymap; +typedef struct SDL_Keymap +{ + SDL_HashTable *scancode_to_keycode; + SDL_HashTable *keycode_to_scancode; + bool auto_release; + bool layout_determined; + bool french_numbers; + bool latin_letters; + bool thai_keyboard; +} SDL_Keymap; SDL_Keymap *SDL_GetCurrentKeymap(void); -SDL_Keymap *SDL_CreateKeymap(void); +SDL_Keymap *SDL_CreateKeymap(bool auto_release); void SDL_SetKeymapEntry(SDL_Keymap *keymap, SDL_Scancode scancode, SDL_Keymod modstate, SDL_Keycode keycode); SDL_Keycode SDL_GetKeymapKeycode(SDL_Keymap *keymap, SDL_Scancode scancode, SDL_Keymod modstate); SDL_Scancode SDL_GetKeymapScancode(SDL_Keymap *keymap, SDL_Keycode keycode, SDL_Keymod *modstate); diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c index c663ca83ba..44f4f23992 100644 --- a/src/events/SDL_mouse.c +++ b/src/events/SDL_mouse.c @@ -409,6 +409,19 @@ void SDL_RemoveMouse(SDL_MouseID mouseID, bool send_event) } } +void SDL_SetMouseName(SDL_MouseID mouseID, const char *name) +{ + SDL_assert(mouseID != 0); + + const int mouse_index = SDL_GetMouseIndex(mouseID); + + if (mouse_index >= 0) { + SDL_MouseInstance *instance = &SDL_mice[mouse_index]; + SDL_free(instance->name); + instance->name = SDL_strdup(name ? name : ""); + } +} + bool SDL_HasMouse(void) { return (SDL_mouse_count > 0); diff --git a/src/events/SDL_mouse_c.h b/src/events/SDL_mouse_c.h index 6e235548dd..bfda17beaa 100644 --- a/src/events/SDL_mouse_c.h +++ b/src/events/SDL_mouse_c.h @@ -169,6 +169,9 @@ extern void SDL_AddMouse(SDL_MouseID mouseID, const char *name, bool send_event) // A mouse has been removed from the system extern void SDL_RemoveMouse(SDL_MouseID mouseID, bool send_event); +// Set or update the name of a mouse instance. +extern void SDL_SetMouseName(SDL_MouseID mouseID, const char *name); + // Get the mouse state structure extern SDL_Mouse *SDL_GetMouse(void); diff --git a/src/events/SDL_touch.c b/src/events/SDL_touch.c index e825117c82..bf3cc02354 100644 --- a/src/events/SDL_touch.c +++ b/src/events/SDL_touch.c @@ -205,6 +205,16 @@ int SDL_AddTouch(SDL_TouchID touchID, SDL_TouchDeviceType type, const char *name return index; } +// Set or update the name of a touch. +void SDL_SetTouchName(SDL_TouchID id, const char *name) +{ + SDL_Touch *touch = SDL_GetTouch(id); + if (touch) { + SDL_free(touch->name); + touch->name = SDL_strdup(name ? name : ""); + } +} + static bool SDL_AddFinger(SDL_Touch *touch, SDL_FingerID fingerid, float x, float y, float pressure) { SDL_Finger *finger; diff --git a/src/events/SDL_touch_c.h b/src/events/SDL_touch_c.h index db2d64b85f..e46ba68197 100644 --- a/src/events/SDL_touch_c.h +++ b/src/events/SDL_touch_c.h @@ -42,6 +42,9 @@ extern bool SDL_TouchDevicesAvailable(void); // Add a touch, returning the index of the touch, or -1 if there was an error. extern int SDL_AddTouch(SDL_TouchID id, SDL_TouchDeviceType type, const char *name); +// Set or update the name of a touch. +extern void SDL_SetTouchName(SDL_TouchID id, const char *name); + // Get the touch with a given id extern SDL_Touch *SDL_GetTouch(SDL_TouchID id); diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index 5f1ab6b920..2622b02af9 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -2511,7 +2511,13 @@ SDL_AppResult SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const /* Ctrl-G toggle mouse grab */ SDL_Window *window = SDL_GetWindowFromEvent(event); if (window) { - SDL_SetWindowMouseGrab(window, !SDL_GetWindowMouseGrab(window)); + if (SDL_RectEmpty(SDL_GetWindowMouseRect(window))) { + SDL_Rect r = { 10, 10, 200, 200}; + SDL_SetWindowMouseRect(window, &r); + } else { + SDL_SetWindowMouseRect(window, NULL); + } + //SDL_SetWindowMouseGrab(window, !SDL_GetWindowMouseGrab(window)); } } break; diff --git a/src/video/cocoa/SDL_cocoakeyboard.m b/src/video/cocoa/SDL_cocoakeyboard.m index e458be94f8..a0d0c233f5 100644 --- a/src/video/cocoa/SDL_cocoakeyboard.m +++ b/src/video/cocoa/SDL_cocoakeyboard.m @@ -327,7 +327,7 @@ static void UpdateKeymap(SDL_CocoaVideoData *data, bool send_event) UInt32 keyboard_type = LMGetKbdType(); - SDL_Keymap *keymap = SDL_CreateKeymap(); + SDL_Keymap *keymap = SDL_CreateKeymap(true); for (int m = 0; m < SDL_arraysize(mods); ++m) { for (int i = 0; i < SDL_arraysize(darwin_scancode_table); i++) { OSStatus err; diff --git a/src/video/wayland/SDL_waylandclipboard.c b/src/video/wayland/SDL_waylandclipboard.c index caab2e30b7..48598876e7 100644 --- a/src/video/wayland/SDL_waylandclipboard.c +++ b/src/video/wayland/SDL_waylandclipboard.c @@ -32,11 +32,16 @@ bool Wayland_SetClipboardData(SDL_VideoDevice *_this) { SDL_VideoData *video_data = _this->internal; - SDL_WaylandDataDevice *data_device = NULL; - bool result = true; + SDL_WaylandSeat *seat = video_data->last_implicit_grab_seat; + bool result = false; - if (video_data->input && video_data->input->data_device) { - data_device = video_data->input->data_device; + // If no implicit grab is available yet, just attach it to the first available seat. + if (!seat && !WAYLAND_wl_list_empty(&video_data->seat_list)) { + seat = wl_container_of(video_data->seat_list.next, seat, link); + } + + if (seat && seat->data_device) { + SDL_WaylandDataDevice *data_device = seat->data_device; if (_this->clipboard_callback && _this->clipboard_mime_types) { SDL_WaylandDataSource *source = Wayland_data_source_create(_this); @@ -57,11 +62,11 @@ bool Wayland_SetClipboardData(SDL_VideoDevice *_this) void *Wayland_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length) { SDL_VideoData *video_data = _this->internal; - SDL_WaylandDataDevice *data_device = NULL; + SDL_WaylandSeat *seat = video_data->last_incoming_data_offer_seat; void *buffer = NULL; - if (video_data->input && video_data->input->data_device) { - data_device = video_data->input->data_device; + if (seat && seat->data_device) { + SDL_WaylandDataDevice *data_device = seat->data_device; if (data_device->selection_source) { buffer = SDL_GetInternalClipboardData(_this, mime_type, length); } else if (Wayland_data_offer_has_mime(data_device->selection_offer, mime_type)) { @@ -75,11 +80,11 @@ void *Wayland_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, si bool Wayland_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type) { SDL_VideoData *video_data = _this->internal; - SDL_WaylandDataDevice *data_device = NULL; + SDL_WaylandSeat *seat = video_data->last_incoming_data_offer_seat; bool result = false; - if (video_data->input && video_data->input->data_device) { - data_device = video_data->input->data_device; + if (seat && seat->data_device) { + SDL_WaylandDataDevice *data_device = seat->data_device; if (data_device->selection_source) { result = SDL_HasInternalClipboardData(_this, mime_type); } else { @@ -106,11 +111,16 @@ const char **Wayland_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_t bool Wayland_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text) { SDL_VideoData *video_data = _this->internal; - SDL_WaylandPrimarySelectionDevice *primary_selection_device = NULL; + SDL_WaylandSeat *seat = video_data->last_implicit_grab_seat; bool result; - if (video_data->input && video_data->input->primary_selection_device) { - primary_selection_device = video_data->input->primary_selection_device; + // If no implicit grab is available yet, just attach it to the first available seat. + if (!seat && !WAYLAND_wl_list_empty(&video_data->seat_list)) { + seat = wl_container_of(video_data->seat_list.next, seat, link); + } + + if (seat && seat->primary_selection_device) { + SDL_WaylandPrimarySelectionDevice *primary_selection_device = seat->primary_selection_device; if (text[0] != '\0') { SDL_WaylandPrimarySelectionSource *source = Wayland_primary_selection_source_create(_this); Wayland_primary_selection_source_set_callback(source, SDL_ClipboardTextCallback, SDL_strdup(text)); @@ -134,12 +144,12 @@ bool Wayland_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text) char *Wayland_GetPrimarySelectionText(SDL_VideoDevice *_this) { SDL_VideoData *video_data = _this->internal; - SDL_WaylandPrimarySelectionDevice *primary_selection_device = NULL; + SDL_WaylandSeat *seat = video_data->last_incoming_primary_selection_seat; char *text = NULL; size_t length = 0; - if (video_data->input && video_data->input->primary_selection_device) { - primary_selection_device = video_data->input->primary_selection_device; + if (seat && seat->primary_selection_device) { + SDL_WaylandPrimarySelectionDevice *primary_selection_device = seat->primary_selection_device; if (primary_selection_device->selection_source) { text = Wayland_primary_selection_source_get_data(primary_selection_device->selection_source, TEXT_MIME, &length); } else { @@ -162,11 +172,11 @@ char *Wayland_GetPrimarySelectionText(SDL_VideoDevice *_this) bool Wayland_HasPrimarySelectionText(SDL_VideoDevice *_this) { SDL_VideoData *video_data = _this->internal; - SDL_WaylandPrimarySelectionDevice *primary_selection_device = NULL; + SDL_WaylandSeat *seat = video_data->last_incoming_primary_selection_seat; bool result = false; - if (video_data->input && video_data->input->primary_selection_device) { - primary_selection_device = video_data->input->primary_selection_device; + if (seat && seat->primary_selection_device) { + SDL_WaylandPrimarySelectionDevice *primary_selection_device = seat->primary_selection_device; if (primary_selection_device->selection_source) { result = true; } else { diff --git a/src/video/wayland/SDL_waylanddatamanager.c b/src/video/wayland/SDL_waylanddatamanager.c index fec2c3eb1d..d5e39a77e0 100644 --- a/src/video/wayland/SDL_waylanddatamanager.c +++ b/src/video/wayland/SDL_waylanddatamanager.c @@ -33,6 +33,7 @@ #include "../SDL_clipboard_c.h" #include "SDL_waylandvideo.h" +#include "SDL_waylandevents_c.h" #include "SDL_waylanddatamanager.h" #include "primary-selection-unstable-v1-client-protocol.h" @@ -373,7 +374,7 @@ void *Wayland_data_offer_receive(SDL_WaylandDataOffer *offer, wl_data_offer_receive(offer->offer, mime_type, pipefd[1]); close(pipefd[1]); - WAYLAND_wl_display_flush(data_device->video_data->display); + WAYLAND_wl_display_flush(data_device->seat->display->display); while (read_pipe(pipefd[0], &buffer, length) > 0) { } @@ -407,7 +408,7 @@ void *Wayland_primary_selection_offer_receive(SDL_WaylandPrimarySelectionOffer * zwp_primary_selection_offer_v1_receive(offer->offer, mime_type, pipefd[1]); close(pipefd[1]); - WAYLAND_wl_display_flush(primary_selection_device->video_data->display); + WAYLAND_wl_display_flush(primary_selection_device->seat->display->display); while (read_pipe(pipefd[0], &buffer, length) > 0) { } @@ -586,14 +587,14 @@ bool Wayland_primary_selection_device_set_selection(SDL_WaylandPrimarySelectionD void Wayland_data_device_set_serial(SDL_WaylandDataDevice *data_device, uint32_t serial) { if (data_device) { + data_device->selection_serial = serial; + // If there was no serial and there is a pending selection set it now. if (data_device->selection_serial == 0 && data_device->selection_source) { wl_data_device_set_selection(data_device->data_device, data_device->selection_source->source, data_device->selection_serial); } - - data_device->selection_serial = serial; } } @@ -601,14 +602,14 @@ void Wayland_primary_selection_device_set_serial(SDL_WaylandPrimarySelectionDevi uint32_t serial) { if (primary_selection_device) { + primary_selection_device->selection_serial = serial; + // If there was no serial and there is a pending selection set it now. if (primary_selection_device->selection_serial == 0 && primary_selection_device->selection_source) { zwp_primary_selection_device_v1_set_selection(primary_selection_device->primary_selection_device, primary_selection_device->selection_source->source, primary_selection_device->selection_serial); } - - primary_selection_device->selection_serial = serial; } } diff --git a/src/video/wayland/SDL_waylanddatamanager.h b/src/video/wayland/SDL_waylanddatamanager.h index fd55634c82..276620f906 100644 --- a/src/video/wayland/SDL_waylanddatamanager.h +++ b/src/video/wayland/SDL_waylanddatamanager.h @@ -20,6 +20,7 @@ */ #include "SDL_internal.h" +#include "SDL_waylandevents_c.h" #ifndef SDL_waylanddatamanager_h_ #define SDL_waylanddatamanager_h_ @@ -79,7 +80,7 @@ typedef struct typedef struct { struct wl_data_device *data_device; - SDL_VideoData *video_data; + struct SDL_WaylandSeat *seat; // Drag and Drop uint32_t drag_serial; @@ -97,7 +98,7 @@ typedef struct typedef struct { struct zwp_primary_selection_device_v1 *primary_selection_device; - SDL_VideoData *video_data; + struct SDL_WaylandSeat *seat; uint32_t selection_serial; SDL_WaylandPrimarySelectionSource *selection_source; diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 7d907a28a1..7802e8981f 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -66,6 +66,7 @@ #include #include #include "cursor-shape-v1-client-protocol.h" +#include "viewporter-client-protocol.h" // Weston uses a ratio of 10 units per scroll tick #define WAYLAND_WHEEL_AXIS_UNIT 10 @@ -81,10 +82,14 @@ // Keyboard and mouse names to match XWayland #define WAYLAND_DEFAULT_KEYBOARD_NAME "Virtual core keyboard" #define WAYLAND_DEFAULT_POINTER_NAME "Virtual core pointer" +#define WAYLAND_DEFAULT_TOUCH_NAME "Virtual core touch" // Focus clickthrough timeout #define WAYLAND_FOCUS_CLICK_TIMEOUT_NS SDL_MS_TO_NS(10) +// Scoped function declarations +static void Wayland_SeatUpdateKeyboardGrab(SDL_WaylandSeat *seat); + struct SDL_WaylandTouchPoint { SDL_TouchID id; @@ -95,9 +100,7 @@ struct SDL_WaylandTouchPoint struct wl_list link; }; -static struct wl_list touch_points; - -static void touch_add(SDL_TouchID id, wl_fixed_t fx, wl_fixed_t fy, struct wl_surface *surface) +static void Wayland_SeatAddTouch(SDL_WaylandSeat *seat, SDL_TouchID id, wl_fixed_t fx, wl_fixed_t fy, struct wl_surface *surface) { struct SDL_WaylandTouchPoint *tp = SDL_malloc(sizeof(struct SDL_WaylandTouchPoint)); @@ -107,14 +110,14 @@ static void touch_add(SDL_TouchID id, wl_fixed_t fx, wl_fixed_t fy, struct wl_su tp->fy = fy; tp->surface = surface; - WAYLAND_wl_list_insert(&touch_points, &tp->link); + WAYLAND_wl_list_insert(&seat->touch.points, &tp->link); } -static void touch_update(SDL_TouchID id, wl_fixed_t fx, wl_fixed_t fy, struct wl_surface **surface) +static void Wayland_SeatUpdateTouch(SDL_WaylandSeat *seat, SDL_TouchID id, wl_fixed_t fx, wl_fixed_t fy, struct wl_surface **surface) { struct SDL_WaylandTouchPoint *tp; - wl_list_for_each (tp, &touch_points, link) { + wl_list_for_each (tp, &seat->touch.points, link) { if (tp->id == id) { tp->fx = fx; tp->fy = fy; @@ -126,11 +129,11 @@ static void touch_update(SDL_TouchID id, wl_fixed_t fx, wl_fixed_t fy, struct wl } } -static void touch_del(SDL_TouchID id, wl_fixed_t *fx, wl_fixed_t *fy, struct wl_surface **surface) +static void Wayland_SeatRemoveTouch(SDL_WaylandSeat *seat, SDL_TouchID id, wl_fixed_t *fx, wl_fixed_t *fy, struct wl_surface **surface) { struct SDL_WaylandTouchPoint *tp; - wl_list_for_each (tp, &touch_points, link) { + wl_list_for_each (tp, &seat->touch.points, link) { if (tp->id == id) { if (fx) { *fx = tp->fx; @@ -149,19 +152,33 @@ static void touch_del(SDL_TouchID id, wl_fixed_t *fx, wl_fixed_t *fy, struct wl_ } } -static bool Wayland_SurfaceHasActiveTouches(struct wl_surface *surface) +static bool Wayland_SurfaceHasActiveTouches(SDL_VideoData *display, struct wl_surface *surface) { struct SDL_WaylandTouchPoint *tp; + SDL_WaylandSeat *seat; - wl_list_for_each (tp, &touch_points, link) { - if (tp->surface == surface) { - return true; + // Check all seats for active touches on the surface. + wl_list_for_each (seat, &display->seat_list, link) { + wl_list_for_each (tp, &seat->touch.points, link) { + if (tp->surface == surface) { + return true; + } } } return false; } +static void Wayland_GetScaledMouseRect(SDL_Window *window, SDL_Rect *scaled_rect) +{ + SDL_WindowData *window_data = window->internal; + + scaled_rect->x = (int)SDL_floor(window->mouse_rect.x / window_data->pointer_scale.x); + scaled_rect->y = (int)SDL_floor(window->mouse_rect.y / window_data->pointer_scale.y); + scaled_rect->w = (int)SDL_ceil(window->mouse_rect.w / window_data->pointer_scale.x); + scaled_rect->h = (int)SDL_ceil(window->mouse_rect.h / window_data->pointer_scale.y); +} + static Uint64 Wayland_GetEventTimestamp(Uint64 nsTimestamp) { static Uint64 last; @@ -187,85 +204,99 @@ static Uint64 Wayland_GetEventTimestamp(Uint64 nsTimestamp) return nsTimestamp; } -static void Wayland_input_timestamp_listener(void *data, struct zwp_input_timestamps_v1 *zwp_input_timestamps_v1, +static void input_timestamp_listener(void *data, struct zwp_input_timestamps_v1 *zwp_input_timestamps_v1, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { *((Uint64 *)data) = ((((Uint64)tv_sec_hi << 32) | (Uint64)tv_sec_lo) * SDL_NS_PER_SECOND) + tv_nsec; } static const struct zwp_input_timestamps_v1_listener timestamp_listener = { - Wayland_input_timestamp_listener + input_timestamp_listener }; -static Uint64 Wayland_GetKeyboardTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms) +static Uint64 Wayland_GetKeyboardTimestamp(SDL_WaylandSeat *seat, Uint32 wl_timestamp_ms) { if (wl_timestamp_ms) { - return Wayland_GetEventTimestamp(input->keyboard_timestamp_ns ? input->keyboard_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms)); + return Wayland_GetEventTimestamp(seat->keyboard.highres_timestamp_ns ? seat->keyboard.highres_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms)); } return 0; } -static Uint64 Wayland_GetKeyboardTimestampRaw(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms) +static Uint64 Wayland_GetKeyboardTimestampRaw(SDL_WaylandSeat *seat, Uint32 wl_timestamp_ms) { if (wl_timestamp_ms) { - return input->keyboard_timestamp_ns ? input->keyboard_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms); + return seat->keyboard.highres_timestamp_ns ? seat->keyboard.highres_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms); } return 0; } -static Uint64 Wayland_GetPointerTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms) +static Uint64 Wayland_GetPointerTimestamp(SDL_WaylandSeat *seat, Uint32 wl_timestamp_ms) { if (wl_timestamp_ms) { - return Wayland_GetEventTimestamp(input->pointer_timestamp_ns ? input->pointer_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms)); + return Wayland_GetEventTimestamp(seat->pointer.highres_timestamp_ns ? seat->pointer.highres_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms)); } return 0; } -Uint64 Wayland_GetTouchTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms) +Uint64 Wayland_GetTouchTimestamp(SDL_WaylandSeat *seat, Uint32 wl_timestamp_ms) { if (wl_timestamp_ms) { - return Wayland_GetEventTimestamp(input->touch_timestamp_ns ? input->touch_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms)); + return Wayland_GetEventTimestamp(seat->touch.highres_timestamp_ns ? seat->touch.highres_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms)); } return 0; } -void Wayland_RegisterTimestampListeners(struct SDL_WaylandInput *input) +static void Wayland_SeatRegisterInputTimestampListeners(SDL_WaylandSeat *seat) { - SDL_VideoData *viddata = input->display; - - if (viddata->input_timestamps_manager) { - if (input->keyboard && !input->keyboard_timestamps) { - input->keyboard_timestamps = zwp_input_timestamps_manager_v1_get_keyboard_timestamps(viddata->input_timestamps_manager, input->keyboard); - zwp_input_timestamps_v1_add_listener(input->keyboard_timestamps, ×tamp_listener, &input->keyboard_timestamp_ns); + if (seat->display->input_timestamps_manager) { + if (seat->keyboard.wl_keyboard && !seat->keyboard.timestamps) { + seat->keyboard.timestamps = zwp_input_timestamps_manager_v1_get_keyboard_timestamps(seat->display->input_timestamps_manager, seat->keyboard.wl_keyboard); + zwp_input_timestamps_v1_add_listener(seat->keyboard.timestamps, ×tamp_listener, &seat->keyboard.highres_timestamp_ns); } - if (input->pointer && !input->pointer_timestamps) { - input->pointer_timestamps = zwp_input_timestamps_manager_v1_get_pointer_timestamps(viddata->input_timestamps_manager, input->pointer); - zwp_input_timestamps_v1_add_listener(input->pointer_timestamps, ×tamp_listener, &input->pointer_timestamp_ns); + if (seat->pointer.wl_pointer && !seat->pointer.timestamps) { + seat->pointer.timestamps = zwp_input_timestamps_manager_v1_get_pointer_timestamps(seat->display->input_timestamps_manager, seat->pointer.wl_pointer); + zwp_input_timestamps_v1_add_listener(seat->pointer.timestamps, ×tamp_listener, &seat->pointer.highres_timestamp_ns); } - if (input->touch && !input->touch_timestamps) { - input->touch_timestamps = zwp_input_timestamps_manager_v1_get_touch_timestamps(viddata->input_timestamps_manager, input->touch); - zwp_input_timestamps_v1_add_listener(input->touch_timestamps, ×tamp_listener, &input->touch_timestamp_ns); + if (seat->touch.wl_touch && !seat->touch.timestamps) { + seat->touch.timestamps = zwp_input_timestamps_manager_v1_get_touch_timestamps(seat->display->input_timestamps_manager, seat->touch.wl_touch); + zwp_input_timestamps_v1_add_listener(seat->touch.timestamps, ×tamp_listener, &seat->touch.highres_timestamp_ns); } } } -void Wayland_CreateCursorShapeDevice(struct SDL_WaylandInput *input) +void Wayland_DisplayInitInputTimestampManager(SDL_VideoData *display) { - SDL_VideoData *viddata = input->display; - - if (viddata->cursor_shape_manager) { - if (input->pointer && !input->cursor_shape) { - input->cursor_shape = wp_cursor_shape_manager_v1_get_pointer(viddata->cursor_shape_manager, input->pointer); + if (display->input_timestamps_manager) { + SDL_WaylandSeat *seat; + wl_list_for_each (seat, &display->seat_list, link) { + Wayland_SeatRegisterInputTimestampListeners(seat); } } } +static void Wayland_SeatCreateCursorShape(SDL_WaylandSeat *seat) +{ + if (seat->display->cursor_shape_manager) { + if (seat->pointer.wl_pointer && !seat->pointer.cursor_shape) { + seat->pointer.cursor_shape = wp_cursor_shape_manager_v1_get_pointer(seat->display->cursor_shape_manager, seat->pointer.wl_pointer); + } + } +} + +void Wayland_DisplayInitCursorShapeManager(SDL_VideoData *display) +{ + SDL_WaylandSeat *seat; + wl_list_for_each (seat, &display->seat_list, link) { + Wayland_SeatCreateCursorShape(seat); + } +} + // Returns true if a key repeat event was due static bool keyboard_repeat_handle(SDL_WaylandKeyboardRepeat *repeat_info, Uint64 elapsed) { @@ -381,7 +412,7 @@ static int dispatch_queued_events(SDL_VideoData *viddata) int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS) { SDL_VideoData *d = _this->internal; - struct SDL_WaylandInput *input = d->input; + SDL_WaylandSeat *seat; bool key_repeat_active = false; WAYLAND_wl_display_flush(d->display); @@ -398,19 +429,26 @@ int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS) #endif // If key repeat is active, we'll need to cap our maximum wait time to handle repeats - if (input && keyboard_repeat_is_set(&input->keyboard_repeat)) { - const Uint64 elapsed = SDL_GetTicksNS() - input->keyboard_repeat.sdl_press_time_ns; - if (keyboard_repeat_handle(&input->keyboard_repeat, elapsed)) { - // A repeat key event was already due - return 1; - } else { - const Uint64 next_repeat_wait_time = (input->keyboard_repeat.next_repeat_ns - elapsed) + 1; - if (timeoutNS >= 0) { - timeoutNS = SDL_min(timeoutNS, next_repeat_wait_time); - } else { - timeoutNS = next_repeat_wait_time; + wl_list_for_each (seat, &d->seat_list, link) { + if (keyboard_repeat_is_set(&seat->keyboard.repeat)) { + if (seat->keyboard.sdl_keymap != SDL_GetCurrentKeymap()) { + SDL_SetKeymap(seat->keyboard.sdl_keymap, true); + SDL_SetModState(seat->keyboard.pressed_modifiers | seat->keyboard.locked_modifiers); + } + + const Uint64 elapsed = SDL_GetTicksNS() - seat->keyboard.repeat.sdl_press_time_ns; + if (keyboard_repeat_handle(&seat->keyboard.repeat, elapsed)) { + // A repeat key event was already due + return 1; + } else { + const Uint64 next_repeat_wait_time = (seat->keyboard.repeat.next_repeat_ns - elapsed) + 1; + if (timeoutNS >= 0) { + timeoutNS = SDL_min(timeoutNS, next_repeat_wait_time); + } else { + timeoutNS = next_repeat_wait_time; + } + key_repeat_active = true; } - key_repeat_active = true; } } @@ -424,18 +462,27 @@ int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS) WAYLAND_wl_display_read_events(d->display); return dispatch_queued_events(d); } else if (err == 0) { + int ret = 0; + // No events available within the timeout WAYLAND_wl_display_cancel_read(d->display); // If key repeat is active, we might have woken up to generate a key event if (key_repeat_active) { - const Uint64 elapsed = SDL_GetTicksNS() - input->keyboard_repeat.sdl_press_time_ns; - if (keyboard_repeat_handle(&input->keyboard_repeat, elapsed)) { - return 1; + wl_list_for_each (seat, &d->seat_list, link) { + if (seat->keyboard.sdl_keymap != SDL_GetCurrentKeymap()) { + SDL_SetKeymap(seat->keyboard.sdl_keymap, true); + SDL_SetModState(seat->keyboard.pressed_modifiers | seat->keyboard.locked_modifiers); + } + + const Uint64 elapsed = SDL_GetTicksNS() - seat->keyboard.repeat.sdl_press_time_ns; + if (keyboard_repeat_handle(&seat->keyboard.repeat, elapsed)) { + ++ret; + } } } - return 0; + return ret; } else { // Error returned from poll()/select() WAYLAND_wl_display_cancel_read(d->display); @@ -457,7 +504,7 @@ int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS) void Wayland_PumpEvents(SDL_VideoDevice *_this) { SDL_VideoData *d = _this->internal; - struct SDL_WaylandInput *input = d->input; + SDL_WaylandSeat *seat; int err; #ifdef SDL_USE_IME @@ -492,9 +539,16 @@ void Wayland_PumpEvents(SDL_VideoDevice *_this) // Dispatch any pre-existing pending events or new events we may have read err = WAYLAND_wl_display_dispatch_pending(d->display); - if (input && keyboard_repeat_is_set(&input->keyboard_repeat)) { - const Uint64 elapsed = SDL_GetTicksNS() - input->keyboard_repeat.sdl_press_time_ns; - keyboard_repeat_handle(&input->keyboard_repeat, elapsed); + wl_list_for_each (seat, &d->seat_list, link) { + if (keyboard_repeat_is_set(&seat->keyboard.repeat)) { + if (seat->keyboard.sdl_keymap != SDL_GetCurrentKeymap()) { + SDL_SetKeymap(seat->keyboard.sdl_keymap, true); + SDL_SetModState(seat->keyboard.pressed_modifiers | seat->keyboard.locked_modifiers); + } + + const Uint64 elapsed = SDL_GetTicksNS() - seat->keyboard.repeat.sdl_press_time_ns; + keyboard_repeat_handle(&seat->keyboard.repeat, elapsed); + } } if (err < 0 && !d->display_disconnected) { @@ -519,28 +573,37 @@ void Wayland_PumpEvents(SDL_VideoDevice *_this) static void pointer_handle_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w) { - struct SDL_WaylandInput *input = data; - SDL_WindowData *window_data = input->pointer_focus; + SDL_WaylandSeat *seat = data; + SDL_WindowData *window_data = seat->pointer.focus; SDL_Window *window = window_data ? window_data->sdlwindow : NULL; - input->sx_w = sx_w; - input->sy_w = sy_w; - if (input->pointer_focus) { + if (window_data) { const float sx = (float)(wl_fixed_to_double(sx_w) * window_data->pointer_scale.x); const float sy = (float)(wl_fixed_to_double(sy_w) * window_data->pointer_scale.y); - SDL_SendMouseMotion(Wayland_GetPointerTimestamp(input, time), window_data->sdlwindow, input->pointer_id, false, sx, sy); - } + SDL_SendMouseMotion(Wayland_GetPointerTimestamp(seat, time), window_data->sdlwindow, seat->pointer.sdl_id, false, sx, sy); - if (window && window->hit_test) { - const SDL_Point point = { (int)SDL_floor(wl_fixed_to_double(sx_w) * window_data->pointer_scale.x), - (int)SDL_floor(wl_fixed_to_double(sy_w) * window_data->pointer_scale.y) }; - SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data); - if (rc == window_data->hit_test_result) { - return; + seat->pointer.last_motion.x = (int)SDL_floorf(sx); + seat->pointer.last_motion.y = (int)SDL_floorf(sy); + + /* Pointer confinement regions are created only when the pointer actually enters the region via + * a motion event received from the compositor. + */ + if (!SDL_RectEmpty(&window->mouse_rect) && !seat->pointer.confined_pointer) { + SDL_Rect scaled_mouse_rect; + Wayland_GetScaledMouseRect(window, &scaled_mouse_rect); + + if (SDL_PointInRect(&seat->pointer.last_motion, &scaled_mouse_rect)) { + Wayland_SeatUpdatePointerGrab(seat); + } } - Wayland_SetHitTestCursor(rc); - window_data->hit_test_result = rc; + if (window->hit_test) { + SDL_HitTestResult rc = window->hit_test(window, &seat->pointer.last_motion, window->hit_test_data); + if (rc != window_data->hit_test_result) { + window_data->hit_test_result = rc; + Wayland_SeatUpdateCursor(seat); + } + } } } @@ -548,79 +611,93 @@ static void pointer_handle_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t sx_w, wl_fixed_t sy_w) { - struct SDL_WaylandInput *input = data; - SDL_WindowData *window; - if (!surface) { - // enter event for a window we've just destroyed + // Enter event for a destroyed surface. return; } - /* This handler will be called twice in Wayland 1.4 - * Once for the window surface which has valid user data - * and again for the mouse cursor surface which does not have valid user data - * We ignore the later - */ - window = Wayland_GetWindowDataForOwnedSurface(surface); - - if (window) { - input->pointer_focus = window; - input->pointer_enter_serial = serial; - SDL_SetMouseFocus(window->sdlwindow); - /* In the case of e.g. a pointer confine warp, we may receive an enter - * event with no following motion event, but with the new coordinates - * as part of the enter event. - * - * FIXME: This causes a movement event with an anomalous timestamp when - * the cursor enters the window. - */ - pointer_handle_motion(data, pointer, 0, sx_w, sy_w); - /* If the cursor was changed while our window didn't have pointer - * focus, we might need to trigger another call to - * wl_pointer_set_cursor() for the new cursor to be displayed. */ - Wayland_SetHitTestCursor(window->hit_test_result); + SDL_WindowData *window = Wayland_GetWindowDataForOwnedSurface(surface); + if (!window) { + // Not a surface owned by SDL. + return; } + + SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; + seat->pointer.focus = window; + seat->pointer.enter_serial = serial; + ++window->pointer_focus_count; + SDL_SetMouseFocus(window->sdlwindow); + + /* In the case of e.g. a pointer confine warp, we may receive an enter + * event with no following motion event, but with the new coordinates + * as part of the enter event. + * + * FIXME: This causes a movement event with an anomalous timestamp when + * the cursor enters the window. + */ + pointer_handle_motion(data, pointer, 0, sx_w, sy_w); + + // Update the pointer grab state. + Wayland_SeatUpdatePointerGrab(seat); + + /* If the cursor was changed while our window didn't have pointer + * focus, we might need to trigger another call to + * wl_pointer_set_cursor() for the new cursor to be displayed. + * + * This will also update the cursor if a second pointer entered a + * window that already has focus, as the focus change sequence + * won't be run. + */ + Wayland_SeatUpdateCursor(seat); } static void pointer_handle_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) { - struct SDL_WaylandInput *input = data; - if (!surface) { + // Leave event for a destroyed surface. return; } - if (input->pointer_focus) { - SDL_WindowData *wind = Wayland_GetWindowDataForOwnedSurface(surface); - - if (wind) { - // Clear the capture flag and raise all buttons - wind->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE; - - input->buttons_pressed = 0; - SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_LEFT, false); - SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_RIGHT, false); - SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_MIDDLE, false); - SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_X1, false); - SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_X2, false); - } - - /* A pointer leave event may be emitted if the compositor hides the pointer in response to receiving a touch event. - * Don't relinquish focus if the surface has active touches, as the compositor is just transitioning from mouse to touch mode. - */ - if (!Wayland_SurfaceHasActiveTouches(surface)) { - SDL_SetMouseFocus(NULL); - } - input->pointer_focus = NULL; + SDL_WindowData *window = Wayland_GetWindowDataForOwnedSurface(surface); + if (!window) { + // Not a surface owned by SDL. + return; } + + // Clear the capture flag and raise all buttons + window->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE; + + SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; + seat->pointer.focus = NULL; + seat->pointer.buttons_pressed = 0; + SDL_SendMouseButton(Wayland_GetPointerTimestamp(seat, 0), window->sdlwindow, seat->pointer.sdl_id, SDL_BUTTON_LEFT, false); + SDL_SendMouseButton(Wayland_GetPointerTimestamp(seat, 0), window->sdlwindow, seat->pointer.sdl_id, SDL_BUTTON_RIGHT, false); + SDL_SendMouseButton(Wayland_GetPointerTimestamp(seat, 0), window->sdlwindow, seat->pointer.sdl_id, SDL_BUTTON_MIDDLE, false); + SDL_SendMouseButton(Wayland_GetPointerTimestamp(seat, 0), window->sdlwindow, seat->pointer.sdl_id, SDL_BUTTON_X1, false); + SDL_SendMouseButton(Wayland_GetPointerTimestamp(seat, 0), window->sdlwindow, seat->pointer.sdl_id, SDL_BUTTON_X2, false); + + /* A pointer leave event may be emitted if the compositor hides the pointer in response to receiving a touch event. + * Don't relinquish focus if the surface has active touches, as the compositor is just transitioning from mouse to touch mode. + */ + SDL_Window *mouse_focus = SDL_GetMouseFocus(); + const bool had_focus = mouse_focus && window->sdlwindow == mouse_focus; + if (!--window->pointer_focus_count && had_focus && !Wayland_SurfaceHasActiveTouches(seat->display, surface)) { + SDL_SetMouseFocus(NULL); + } + + Wayland_SeatUpdatePointerGrab(seat); + Wayland_SeatUpdateCursor(seat); } -static bool ProcessHitTest(SDL_WindowData *window_data, - struct wl_seat *seat, - wl_fixed_t sx_w, wl_fixed_t sy_w, - uint32_t serial) +static bool Wayland_ProcessHitTest(SDL_WaylandSeat *seat, Uint32 serial) { + // Locked in relative mode, do nothing. + if (seat->pointer.locked_pointer) { + return false; + } + + SDL_WindowData *window_data = seat->pointer.focus; SDL_Window *window = window_data->sdlwindow; if (window->hit_test) { @@ -646,7 +723,7 @@ static bool ProcessHitTest(SDL_WindowData *window_data, if (window_data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) { if (window_data->shell_surface.libdecor.frame) { libdecor_frame_move(window_data->shell_surface.libdecor.frame, - seat, + seat->wl_seat, serial); } } else @@ -654,7 +731,7 @@ static bool ProcessHitTest(SDL_WindowData *window_data, if (window_data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) { if (window_data->shell_surface.xdg.toplevel.xdg_toplevel) { xdg_toplevel_move(window_data->shell_surface.xdg.toplevel.xdg_toplevel, - seat, + seat->wl_seat, serial); } } @@ -672,7 +749,7 @@ static bool ProcessHitTest(SDL_WindowData *window_data, if (window_data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) { if (window_data->shell_surface.libdecor.frame) { libdecor_frame_resize(window_data->shell_surface.libdecor.frame, - seat, + seat->wl_seat, serial, directions_libdecor[window_data->hit_test_result - SDL_HITTEST_RESIZE_TOPLEFT]); } @@ -681,7 +758,7 @@ static bool ProcessHitTest(SDL_WindowData *window_data, if (window_data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) { if (window_data->shell_surface.xdg.toplevel.xdg_toplevel) { xdg_toplevel_resize(window_data->shell_surface.xdg.toplevel.xdg_toplevel, - seat, + seat->wl_seat, serial, directions[window_data->hit_test_result - SDL_HITTEST_RESIZE_TOPLEFT]); } @@ -696,12 +773,12 @@ static bool ProcessHitTest(SDL_WindowData *window_data, return false; } -static void pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_t serial, +static void pointer_handle_button_common(SDL_WaylandSeat *seat, uint32_t serial, uint32_t time, uint32_t button, uint32_t state_w) { - SDL_WindowData *window = input->pointer_focus; + SDL_WindowData *window = seat->pointer.focus; enum wl_pointer_button_state state = state_w; - Uint64 timestamp = Wayland_GetPointerTimestamp(input, time); + Uint64 timestamp = Wayland_GetPointerTimestamp(seat, time); Uint8 sdl_button; const bool down = (state != 0); @@ -726,18 +803,16 @@ static void pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_ } if (window) { - SDL_VideoData *viddata = window->waylandData; bool ignore_click = false; if (state) { - Wayland_UpdateImplicitGrabSerial(input, serial); - input->buttons_pressed |= SDL_BUTTON_MASK(sdl_button); + Wayland_UpdateImplicitGrabSerial(seat, serial); + seat->pointer.buttons_pressed |= SDL_BUTTON_MASK(sdl_button); } else { - input->buttons_pressed &= ~(SDL_BUTTON_MASK(sdl_button)); + seat->pointer.buttons_pressed &= ~(SDL_BUTTON_MASK(sdl_button)); } - if (sdl_button == SDL_BUTTON_LEFT && - ProcessHitTest(input->pointer_focus, input->seat, input->sx_w, input->sy_w, serial)) { + if (sdl_button == SDL_BUTTON_LEFT && Wayland_ProcessHitTest(seat, serial)) { return; // don't pass this event on to app. } @@ -758,8 +833,8 @@ static void pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_ * * The mouse is not captured in relative mode. */ - if (!viddata->relative_mouse_mode) { - if (input->buttons_pressed != 0) { + if (!seat->display->relative_mode_enabled || !Wayland_SeatHasRelativePointerFocus(seat)) { + if (seat->pointer.buttons_pressed != 0) { window->sdlwindow->flags |= SDL_WINDOW_MOUSE_CAPTURE; } else { window->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE; @@ -767,7 +842,7 @@ static void pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_ } if (!ignore_click) { - SDL_SendMouseButton(timestamp, window->sdlwindow, input->pointer_id, sdl_button, down); + SDL_SendMouseButton(timestamp, window->sdlwindow, seat->pointer.sdl_id, sdl_button, down); } } } @@ -775,19 +850,19 @@ static void pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_ static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state_w) { - struct SDL_WaylandInput *input = data; + SDL_WaylandSeat *seat = data; - pointer_handle_button_common(input, serial, time, button, state_w); + pointer_handle_button_common(seat, serial, time, button, state_w); } -static void pointer_handle_axis_common_v1(struct SDL_WaylandInput *input, +static void pointer_handle_axis_common_v1(SDL_WaylandSeat *seat, uint32_t time, uint32_t axis, wl_fixed_t value) { - SDL_WindowData *window = input->pointer_focus; - const Uint64 timestamp = Wayland_GetPointerTimestamp(input, time); + SDL_WindowData *window = seat->pointer.focus; + const Uint64 timestamp = Wayland_GetPointerTimestamp(seat, time); const enum wl_pointer_axis a = axis; - if (input->pointer_focus) { + if (seat->pointer.focus) { float x, y; switch (a) { @@ -806,16 +881,16 @@ static void pointer_handle_axis_common_v1(struct SDL_WaylandInput *input, x /= WAYLAND_WHEEL_AXIS_UNIT; y /= WAYLAND_WHEEL_AXIS_UNIT; - SDL_SendMouseWheel(timestamp, window->sdlwindow, input->pointer_id, x, y, SDL_MOUSEWHEEL_NORMAL); + SDL_SendMouseWheel(timestamp, window->sdlwindow, seat->pointer.sdl_id, x, y, SDL_MOUSEWHEEL_NORMAL); } } -static void pointer_handle_axis_common(struct SDL_WaylandInput *input, enum SDL_WaylandAxisEvent type, +static void pointer_handle_axis_common(SDL_WaylandSeat *seat, enum SDL_WaylandAxisEvent type, uint32_t axis, wl_fixed_t value) { const enum wl_pointer_axis a = axis; - if (input->pointer_focus) { + if (seat->pointer.focus) { switch (a) { case WL_POINTER_AXIS_VERTICAL_SCROLL: switch (type) { @@ -824,26 +899,26 @@ static void pointer_handle_axis_common(struct SDL_WaylandInput *input, enum SDL_ * High resolution scroll event. The spec doesn't state that axis_value120 * events are limited to one per frame, so the values are accumulated. */ - if (input->pointer_curr_axis_info.y_axis_type != AXIS_EVENT_VALUE120) { - input->pointer_curr_axis_info.y_axis_type = AXIS_EVENT_VALUE120; - input->pointer_curr_axis_info.y = 0.0f; + if (seat->pointer.current_axis_info.y_axis_type != AXIS_EVENT_VALUE120) { + seat->pointer.current_axis_info.y_axis_type = AXIS_EVENT_VALUE120; + seat->pointer.current_axis_info.y = 0.0f; } - input->pointer_curr_axis_info.y += 0 - (float)wl_fixed_to_double(value); + seat->pointer.current_axis_info.y += 0 - (float)wl_fixed_to_double(value); break; case AXIS_EVENT_DISCRETE: /* * This is a discrete axis event, so we process it and set the * flag to ignore future continuous axis events in this frame. */ - if (input->pointer_curr_axis_info.y_axis_type != AXIS_EVENT_DISCRETE) { - input->pointer_curr_axis_info.y_axis_type = AXIS_EVENT_DISCRETE; - input->pointer_curr_axis_info.y = 0 - (float)wl_fixed_to_double(value); + if (seat->pointer.current_axis_info.y_axis_type != AXIS_EVENT_DISCRETE) { + seat->pointer.current_axis_info.y_axis_type = AXIS_EVENT_DISCRETE; + seat->pointer.current_axis_info.y = 0 - (float)wl_fixed_to_double(value); } break; case AXIS_EVENT_CONTINUOUS: // Only process continuous events if no discrete events have been received. - if (input->pointer_curr_axis_info.y_axis_type == AXIS_EVENT_CONTINUOUS) { - input->pointer_curr_axis_info.y = 0 - (float)wl_fixed_to_double(value); + if (seat->pointer.current_axis_info.y_axis_type == AXIS_EVENT_CONTINUOUS) { + seat->pointer.current_axis_info.y = 0 - (float)wl_fixed_to_double(value); } break; } @@ -855,26 +930,26 @@ static void pointer_handle_axis_common(struct SDL_WaylandInput *input, enum SDL_ * High resolution scroll event. The spec doesn't state that axis_value120 * events are limited to one per frame, so the values are accumulated. */ - if (input->pointer_curr_axis_info.x_axis_type != AXIS_EVENT_VALUE120) { - input->pointer_curr_axis_info.x_axis_type = AXIS_EVENT_VALUE120; - input->pointer_curr_axis_info.x = 0.0f; + if (seat->pointer.current_axis_info.x_axis_type != AXIS_EVENT_VALUE120) { + seat->pointer.current_axis_info.x_axis_type = AXIS_EVENT_VALUE120; + seat->pointer.current_axis_info.x = 0.0f; } - input->pointer_curr_axis_info.x += (float)wl_fixed_to_double(value); + seat->pointer.current_axis_info.x += (float)wl_fixed_to_double(value); break; case AXIS_EVENT_DISCRETE: /* * This is a discrete axis event, so we process it and set the * flag to ignore future continuous axis events in this frame. */ - if (input->pointer_curr_axis_info.x_axis_type != AXIS_EVENT_DISCRETE) { - input->pointer_curr_axis_info.x_axis_type = AXIS_EVENT_DISCRETE; - input->pointer_curr_axis_info.x = (float)wl_fixed_to_double(value); + if (seat->pointer.current_axis_info.x_axis_type != AXIS_EVENT_DISCRETE) { + seat->pointer.current_axis_info.x_axis_type = AXIS_EVENT_DISCRETE; + seat->pointer.current_axis_info.x = (float)wl_fixed_to_double(value); } break; case AXIS_EVENT_CONTINUOUS: // Only process continuous events if no discrete events have been received. - if (input->pointer_curr_axis_info.x_axis_type == AXIS_EVENT_CONTINUOUS) { - input->pointer_curr_axis_info.x = (float)wl_fixed_to_double(value); + if (seat->pointer.current_axis_info.x_axis_type == AXIS_EVENT_CONTINUOUS) { + seat->pointer.current_axis_info.x = (float)wl_fixed_to_double(value); } break; } @@ -886,76 +961,76 @@ static void pointer_handle_axis_common(struct SDL_WaylandInput *input, enum SDL_ static void pointer_handle_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { - struct SDL_WaylandInput *input = data; + SDL_WaylandSeat *seat = data; - if (wl_seat_get_version(input->seat) >= WL_POINTER_FRAME_SINCE_VERSION) { - input->pointer_curr_axis_info.timestamp_ns = Wayland_GetPointerTimestamp(input, time); - pointer_handle_axis_common(input, AXIS_EVENT_CONTINUOUS, axis, value); + if (wl_seat_get_version(seat->wl_seat) >= WL_POINTER_FRAME_SINCE_VERSION) { + seat->pointer.current_axis_info.timestamp_ns = Wayland_GetPointerTimestamp(seat, time); + pointer_handle_axis_common(seat, AXIS_EVENT_CONTINUOUS, axis, value); } else { - pointer_handle_axis_common_v1(input, time, axis, value); + pointer_handle_axis_common_v1(seat, time, axis, value); } } static void pointer_handle_axis_relative_direction(void *data, struct wl_pointer *pointer, uint32_t axis, uint32_t axis_relative_direction) { - struct SDL_WaylandInput *input = data; + SDL_WaylandSeat *seat = data; if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) { return; } switch (axis_relative_direction) { case WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL: - input->pointer_curr_axis_info.direction = SDL_MOUSEWHEEL_NORMAL; + seat->pointer.current_axis_info.direction = SDL_MOUSEWHEEL_NORMAL; break; case WL_POINTER_AXIS_RELATIVE_DIRECTION_INVERTED: - input->pointer_curr_axis_info.direction = SDL_MOUSEWHEEL_FLIPPED; + seat->pointer.current_axis_info.direction = SDL_MOUSEWHEEL_FLIPPED; break; } } static void pointer_handle_frame(void *data, struct wl_pointer *pointer) { - struct SDL_WaylandInput *input = data; - SDL_WindowData *window = input->pointer_focus; + SDL_WaylandSeat *seat = data; + SDL_WindowData *window = seat->pointer.focus; float x, y; - SDL_MouseWheelDirection direction = input->pointer_curr_axis_info.direction; + SDL_MouseWheelDirection direction = seat->pointer.current_axis_info.direction; - switch (input->pointer_curr_axis_info.x_axis_type) { + switch (seat->pointer.current_axis_info.x_axis_type) { case AXIS_EVENT_CONTINUOUS: - x = input->pointer_curr_axis_info.x / WAYLAND_WHEEL_AXIS_UNIT; + x = seat->pointer.current_axis_info.x / WAYLAND_WHEEL_AXIS_UNIT; break; case AXIS_EVENT_DISCRETE: - x = input->pointer_curr_axis_info.x; + x = seat->pointer.current_axis_info.x; break; case AXIS_EVENT_VALUE120: - x = input->pointer_curr_axis_info.x / 120.0f; + x = seat->pointer.current_axis_info.x / 120.0f; break; default: x = 0.0f; break; } - switch (input->pointer_curr_axis_info.y_axis_type) { + switch (seat->pointer.current_axis_info.y_axis_type) { case AXIS_EVENT_CONTINUOUS: - y = input->pointer_curr_axis_info.y / WAYLAND_WHEEL_AXIS_UNIT; + y = seat->pointer.current_axis_info.y / WAYLAND_WHEEL_AXIS_UNIT; break; case AXIS_EVENT_DISCRETE: - y = input->pointer_curr_axis_info.y; + y = seat->pointer.current_axis_info.y; break; case AXIS_EVENT_VALUE120: - y = input->pointer_curr_axis_info.y / 120.0f; + y = seat->pointer.current_axis_info.y / 120.0f; break; default: y = 0.0f; break; } - // clear pointer_curr_axis_info for next frame - SDL_memset(&input->pointer_curr_axis_info, 0, sizeof(input->pointer_curr_axis_info)); + // clear pointer.current_axis_info for next frame + SDL_memset(&seat->pointer.current_axis_info, 0, sizeof(seat->pointer.current_axis_info)); if (x != 0.0f || y != 0.0f) { - SDL_SendMouseWheel(input->pointer_curr_axis_info.timestamp_ns, - window->sdlwindow, input->pointer_id, x, y, direction); + SDL_SendMouseWheel(seat->pointer.current_axis_info.timestamp_ns, + window->sdlwindow, seat->pointer.sdl_id, x, y, direction); } } @@ -974,17 +1049,17 @@ static void pointer_handle_axis_stop(void *data, struct wl_pointer *pointer, static void pointer_handle_axis_discrete(void *data, struct wl_pointer *pointer, uint32_t axis, int32_t discrete) { - struct SDL_WaylandInput *input = data; + SDL_WaylandSeat *seat = data; - pointer_handle_axis_common(input, AXIS_EVENT_DISCRETE, axis, wl_fixed_from_int(discrete)); + pointer_handle_axis_common(seat, AXIS_EVENT_DISCRETE, axis, wl_fixed_from_int(discrete)); } static void pointer_handle_axis_value120(void *data, struct wl_pointer *pointer, uint32_t axis, int32_t value120) { - struct SDL_WaylandInput *input = data; + SDL_WaylandSeat *seat = data; - pointer_handle_axis_common(input, AXIS_EVENT_VALUE120, axis, wl_fixed_from_int(value120)); + pointer_handle_axis_common(seat, AXIS_EVENT_VALUE120, axis, wl_fixed_from_int(value120)); } static const struct wl_pointer_listener pointer_listener = { @@ -1010,26 +1085,30 @@ static void relative_pointer_handle_relative_motion(void *data, wl_fixed_t dx_unaccel_w, wl_fixed_t dy_unaccel_w) { - struct SDL_WaylandInput *input = data; - SDL_VideoData *d = input->display; - SDL_WindowData *window = input->pointer_focus; - SDL_Mouse *mouse = SDL_GetMouse(); + SDL_WaylandSeat *seat = data; - // Relative pointer event times are in microsecond granularity. - const Uint64 timestamp = Wayland_GetEventTimestamp(SDL_US_TO_NS(((Uint64)time_hi << 32) | (Uint64)time_lo)); + if (seat->display->relative_mode_enabled) { + SDL_WindowData *window = seat->pointer.focus; - if (input->pointer_focus && d->relative_mouse_mode) { - double dx; - double dy; - if (mouse->InputTransform || !mouse->enable_relative_system_scale) { - dx = wl_fixed_to_double(dx_unaccel_w); - dy = wl_fixed_to_double(dy_unaccel_w); - } else { - dx = wl_fixed_to_double(dx_w) * window->pointer_scale.x; - dy = wl_fixed_to_double(dy_w) * window->pointer_scale.y; + // Relative motion follows keyboard focus. + if (Wayland_SeatHasRelativePointerFocus(seat)) { + SDL_Mouse *mouse = SDL_GetMouse(); + + // Relative pointer event times are in microsecond granularity. + const Uint64 timestamp = Wayland_GetEventTimestamp(SDL_US_TO_NS(((Uint64)time_hi << 32) | (Uint64)time_lo)); + + double dx; + double dy; + if (mouse->InputTransform || !mouse->enable_relative_system_scale) { + dx = wl_fixed_to_double(dx_unaccel_w); + dy = wl_fixed_to_double(dy_unaccel_w); + } else { + dx = wl_fixed_to_double(dx_w) * window->pointer_scale.x; + dy = wl_fixed_to_double(dy_w) * window->pointer_scale.y; + } + + SDL_SendMouseMotion(timestamp, window->sdlwindow, seat->pointer.sdl_id, true, (float)dx, (float)dy); } - - SDL_SendMouseMotion(timestamp, window->sdlwindow, input->pointer_id, true, (float)dx, (float)dy); } } @@ -1052,58 +1131,6 @@ static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = { locked_pointer_unlocked, }; -bool Wayland_input_lock_pointer(struct SDL_WaylandInput *input, SDL_Window *window) -{ - SDL_WindowData *w = window->internal; - SDL_VideoData *d = input->display; - - if (!d->pointer_constraints || !input->pointer) { - return false; - } - - if (!w->locked_pointer) { - if (w->confined_pointer) { - // If the pointer is already confined to the surface, the lock will fail with a protocol error. - Wayland_input_unconfine_pointer(input, window); - } - - w->locked_pointer = zwp_pointer_constraints_v1_lock_pointer(d->pointer_constraints, - w->surface, - input->pointer, - NULL, - ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); - zwp_locked_pointer_v1_add_listener(w->locked_pointer, - &locked_pointer_listener, - window); - } - - return true; -} - -bool Wayland_input_unlock_pointer(struct SDL_WaylandInput *input, SDL_Window *window) -{ - SDL_WindowData *w = window->internal; - - if (w->locked_pointer) { - zwp_locked_pointer_v1_destroy(w->locked_pointer); - w->locked_pointer = NULL; - } - - // Restore existing pointer confinement. - Wayland_input_confine_pointer(input, window); - - return true; -} - -static void pointer_confine_destroy(SDL_Window *window) -{ - SDL_WindowData *w = window->internal; - if (w->confined_pointer) { - zwp_confined_pointer_v1_destroy(w->confined_pointer); - w->confined_pointer = NULL; - } -} - static void confined_pointer_confined(void *data, struct zwp_confined_pointer_v1 *confined_pointer) { @@ -1123,7 +1150,7 @@ static void touch_handler_down(void *data, struct wl_touch *touch, uint32_t seri uint32_t timestamp, struct wl_surface *surface, int id, wl_fixed_t fx, wl_fixed_t fy) { - struct SDL_WaylandInput *input = (struct SDL_WaylandInput *)data; + SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; SDL_WindowData *window_data; // Check that this surface is valid. @@ -1131,8 +1158,8 @@ static void touch_handler_down(void *data, struct wl_touch *touch, uint32_t seri return; } - touch_add(id, fx, fy, surface); - Wayland_UpdateImplicitGrabSerial(input, serial); + Wayland_SeatAddTouch(seat, id, fx, fy, surface); + Wayland_UpdateImplicitGrabSerial(seat, serial); window_data = Wayland_GetWindowDataForOwnedSurface(surface); if (window_data) { @@ -1151,7 +1178,7 @@ static void touch_handler_down(void *data, struct wl_touch *touch, uint32_t seri SDL_SetMouseFocus(window_data->sdlwindow); - SDL_SendTouch(Wayland_GetTouchTimestamp(input, timestamp), (SDL_TouchID)(uintptr_t)touch, + SDL_SendTouch(Wayland_GetTouchTimestamp(seat, timestamp), (SDL_TouchID)(uintptr_t)touch, (SDL_FingerID)(id + 1), window_data->sdlwindow, SDL_EVENT_FINGER_DOWN, x, y, 1.0f); } } @@ -1159,11 +1186,11 @@ static void touch_handler_down(void *data, struct wl_touch *touch, uint32_t seri static void touch_handler_up(void *data, struct wl_touch *touch, uint32_t serial, uint32_t timestamp, int id) { - struct SDL_WaylandInput *input = (struct SDL_WaylandInput *)data; + SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; wl_fixed_t fx = 0, fy = 0; struct wl_surface *surface = NULL; - touch_del(id, &fx, &fy, &surface); + Wayland_SeatRemoveTouch(seat, id, &fx, &fy, &surface); if (surface) { SDL_WindowData *window_data = (SDL_WindowData *)wl_surface_get_user_data(surface); @@ -1172,14 +1199,14 @@ static void touch_handler_up(void *data, struct wl_touch *touch, uint32_t serial const float x = (float)wl_fixed_to_double(fx) / window_data->current.logical_width; const float y = (float)wl_fixed_to_double(fy) / window_data->current.logical_height; - SDL_SendTouch(Wayland_GetTouchTimestamp(input, timestamp), (SDL_TouchID)(uintptr_t)touch, + SDL_SendTouch(Wayland_GetTouchTimestamp(seat, timestamp), (SDL_TouchID)(uintptr_t)touch, (SDL_FingerID)(id + 1), window_data->sdlwindow, SDL_EVENT_FINGER_UP, x, y, 0.0f); - /* If the seat lacks pointer focus, the seat's keyboard focus is another window or NULL, this window currently - * has mouse focus, and the surface has no active touch events, consider mouse focus to be lost. + /* If the window currently has mouse focus, the keyboard focus is another window or NULL, the window has no + * pointers active on it, and the surface has no active touch events, then consider mouse focus to be lost. */ - if (!input->pointer_focus && input->keyboard_focus != window_data && - SDL_GetMouseFocus() == window_data->sdlwindow && !Wayland_SurfaceHasActiveTouches(surface)) { + if (SDL_GetMouseFocus() == window_data->sdlwindow && seat->keyboard.focus != window_data && + !window_data->pointer_focus_count && !Wayland_SurfaceHasActiveTouches(seat->display, surface)) { SDL_SetMouseFocus(NULL); } } @@ -1189,10 +1216,10 @@ static void touch_handler_up(void *data, struct wl_touch *touch, uint32_t serial static void touch_handler_motion(void *data, struct wl_touch *touch, uint32_t timestamp, int id, wl_fixed_t fx, wl_fixed_t fy) { - struct SDL_WaylandInput *input = (struct SDL_WaylandInput *)data; + SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; struct wl_surface *surface = NULL; - touch_update(id, fx, fy, &surface); + Wayland_SeatUpdateTouch(seat, id, fx, fy, &surface); if (surface) { SDL_WindowData *window_data = (SDL_WindowData *)wl_surface_get_user_data(surface); @@ -1201,7 +1228,7 @@ static void touch_handler_motion(void *data, struct wl_touch *touch, uint32_t ti const float x = (float)wl_fixed_to_double(fx) / window_data->current.logical_width; const float y = (float)wl_fixed_to_double(fy) / window_data->current.logical_height; - SDL_SendTouchMotion(Wayland_GetPointerTimestamp(input, timestamp), (SDL_TouchID)(uintptr_t)touch, + SDL_SendTouchMotion(Wayland_GetPointerTimestamp(seat, timestamp), (SDL_TouchID)(uintptr_t)touch, (SDL_FingerID)(id + 1), window_data->sdlwindow, x, y, 1.0f); } } @@ -1213,10 +1240,10 @@ static void touch_handler_frame(void *data, struct wl_touch *touch) static void touch_handler_cancel(void *data, struct wl_touch *touch) { - struct SDL_WaylandInput *input = (struct SDL_WaylandInput *)data; + SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; struct SDL_WaylandTouchPoint *tp, *temp; - wl_list_for_each_safe (tp, temp, &touch_points, link) { + wl_list_for_each_safe (tp, temp, &seat->touch.points, link) { bool removed = false; if (tp->surface) { @@ -1233,11 +1260,11 @@ static void touch_handler_cancel(void *data, struct wl_touch *touch) WAYLAND_wl_list_remove(&tp->link); removed = true; - /* If the seat lacks pointer focus, the seat's keyboard focus is another window or NULL, this window currently - * has mouse focus, and the surface has no active touch events, consider mouse focus to be lost. + /* If the window currently has mouse focus, the keyboard focus is another window or NULL, the window has no + * pointers active on it, and the surface has no active touch events, then consider mouse focus to be lost. */ - if (!input->pointer_focus && input->keyboard_focus != window_data && - SDL_GetMouseFocus() == window_data->sdlwindow && !Wayland_SurfaceHasActiveTouches(tp->surface)) { + if (SDL_GetMouseFocus() == window_data->sdlwindow && seat->keyboard.focus != window_data && + !window_data->pointer_focus_count && !Wayland_SurfaceHasActiveTouches(seat->display, tp->surface)) { SDL_SetMouseFocus(NULL); } } @@ -1250,26 +1277,34 @@ static void touch_handler_cancel(void *data, struct wl_touch *touch) } } +static void touch_handler_shape(void *data, struct wl_touch *wl_touch, int32_t id, wl_fixed_t major, wl_fixed_t minor) +{ +} + +static void touch_handler_orientation(void *data, struct wl_touch *wl_touch, int32_t id, wl_fixed_t orientation) +{ +} + static const struct wl_touch_listener touch_listener = { touch_handler_down, touch_handler_up, touch_handler_motion, touch_handler_frame, touch_handler_cancel, - NULL, // shape - NULL, // orientation + touch_handler_shape, // Version 6 + touch_handler_orientation // Version 6 }; -typedef struct Wayland_Keymap +typedef struct Wayland_KeymapBuilderState { SDL_Keymap *keymap; struct xkb_state *state; SDL_Keymod modstate; -} Wayland_Keymap; +} Wayland_KeymapBuilderState; static void Wayland_keymap_iter(struct xkb_keymap *keymap, xkb_keycode_t key, void *data) { - Wayland_Keymap *sdlKeymap = (Wayland_Keymap *)data; + Wayland_KeymapBuilderState *sdlKeymap = (Wayland_KeymapBuilderState *)data; const xkb_keysym_t *syms; const SDL_Scancode scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, (key - 8)); if (scancode == SDL_SCANCODE_UNKNOWN) { @@ -1303,7 +1338,7 @@ static void Wayland_keymap_iter(struct xkb_keymap *keymap, xkb_keycode_t key, vo } } -static void Wayland_UpdateKeymap(struct SDL_WaylandInput *input) +static void Wayland_UpdateKeymap(SDL_WaylandSeat *seat) { struct Keymod_masks { @@ -1311,32 +1346,32 @@ static void Wayland_UpdateKeymap(struct SDL_WaylandInput *input) xkb_mod_mask_t xkb_mask; } const keymod_masks[] = { { SDL_KMOD_NONE, 0 }, - { SDL_KMOD_SHIFT, input->xkb.idx_shift }, - { SDL_KMOD_CAPS, input->xkb.idx_caps }, - { SDL_KMOD_SHIFT | SDL_KMOD_CAPS, input->xkb.idx_shift | input->xkb.idx_caps }, - { SDL_KMOD_MODE, input->xkb.idx_mod5 }, - { SDL_KMOD_MODE | SDL_KMOD_SHIFT, input->xkb.idx_mod5 | input->xkb.idx_shift }, - { SDL_KMOD_MODE | SDL_KMOD_CAPS, input->xkb.idx_mod5 | input->xkb.idx_caps }, - { SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, input->xkb.idx_mod5 | input->xkb.idx_shift | input->xkb.idx_caps }, - { SDL_KMOD_LEVEL5, input->xkb.idx_mod3 }, - { SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT, input->xkb.idx_mod3 | input->xkb.idx_shift }, - { SDL_KMOD_LEVEL5 | SDL_KMOD_CAPS, input->xkb.idx_mod3 | input->xkb.idx_caps }, - { SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, input->xkb.idx_mod3 | input->xkb.idx_shift | input->xkb.idx_caps }, - { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE, input->xkb.idx_mod3 | input->xkb.idx_mod5 }, - { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT, input->xkb.idx_mod3 | input->xkb.idx_mod5 | input->xkb.idx_shift }, - { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_CAPS, input->xkb.idx_mod3 | input->xkb.idx_mod5 | input->xkb.idx_caps }, - { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, input->xkb.idx_mod3 | input->xkb.idx_mod5 | input->xkb.idx_shift | input->xkb.idx_caps }, + { SDL_KMOD_SHIFT, seat->keyboard.xkb.idx_shift }, + { SDL_KMOD_CAPS, seat->keyboard.xkb.idx_caps }, + { SDL_KMOD_SHIFT | SDL_KMOD_CAPS, seat->keyboard.xkb.idx_shift | seat->keyboard.xkb.idx_caps }, + { SDL_KMOD_MODE, seat->keyboard.xkb.idx_mod5 }, + { SDL_KMOD_MODE | SDL_KMOD_SHIFT, seat->keyboard.xkb.idx_mod5 | seat->keyboard.xkb.idx_shift }, + { SDL_KMOD_MODE | SDL_KMOD_CAPS, seat->keyboard.xkb.idx_mod5 | seat->keyboard.xkb.idx_caps }, + { SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, seat->keyboard.xkb.idx_mod5 | seat->keyboard.xkb.idx_shift | seat->keyboard.xkb.idx_caps }, + { SDL_KMOD_LEVEL5, seat->keyboard.xkb.idx_mod3 }, + { SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT, seat->keyboard.xkb.idx_mod3 | seat->keyboard.xkb.idx_shift }, + { SDL_KMOD_LEVEL5 | SDL_KMOD_CAPS, seat->keyboard.xkb.idx_mod3 | seat->keyboard.xkb.idx_caps }, + { SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, seat->keyboard.xkb.idx_mod3 | seat->keyboard.xkb.idx_shift | seat->keyboard.xkb.idx_caps }, + { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE, seat->keyboard.xkb.idx_mod3 | seat->keyboard.xkb.idx_mod5 }, + { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT, seat->keyboard.xkb.idx_mod3 | seat->keyboard.xkb.idx_mod5 | seat->keyboard.xkb.idx_shift }, + { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_CAPS, seat->keyboard.xkb.idx_mod3 | seat->keyboard.xkb.idx_mod5 | seat->keyboard.xkb.idx_caps }, + { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, seat->keyboard.xkb.idx_mod3 | seat->keyboard.xkb.idx_mod5 | seat->keyboard.xkb.idx_shift | seat->keyboard.xkb.idx_caps }, }; - if (!input->keyboard_is_virtual) { - Wayland_Keymap keymap; + if (!seat->keyboard.is_virtual) { + Wayland_KeymapBuilderState keymap; - keymap.keymap = SDL_CreateKeymap(); + keymap.keymap = SDL_CreateKeymap(false); if (!keymap.keymap) { return; } - keymap.state = WAYLAND_xkb_state_new(input->xkb.keymap); + keymap.state = WAYLAND_xkb_state_new(seat->keyboard.xkb.keymap); if (!keymap.state) { SDL_SetError("failed to create XKB state"); SDL_DestroyKeymap(keymap.keymap); @@ -1346,25 +1381,29 @@ static void Wayland_UpdateKeymap(struct SDL_WaylandInput *input) for (int i = 0; i < SDL_arraysize(keymod_masks); ++i) { keymap.modstate = keymod_masks[i].sdl_mask; WAYLAND_xkb_state_update_mask(keymap.state, - keymod_masks[i].xkb_mask & (input->xkb.idx_shift | input->xkb.idx_mod5 | input->xkb.idx_mod3), 0, keymod_masks[i].xkb_mask & input->xkb.idx_caps, - 0, 0, input->xkb.current_group); - WAYLAND_xkb_keymap_key_for_each(input->xkb.keymap, + keymod_masks[i].xkb_mask & (seat->keyboard.xkb.idx_shift | seat->keyboard.xkb.idx_mod5 | seat->keyboard.xkb.idx_mod3), 0, keymod_masks[i].xkb_mask & seat->keyboard.xkb.idx_caps, + 0, 0, seat->keyboard.xkb.current_group); + WAYLAND_xkb_keymap_key_for_each(seat->keyboard.xkb.keymap, Wayland_keymap_iter, &keymap); } WAYLAND_xkb_state_unref(keymap.state); SDL_SetKeymap(keymap.keymap, true); + SDL_DestroyKeymap(seat->keyboard.sdl_keymap); + seat->keyboard.sdl_keymap = keymap.keymap; } else { // Virtual keyboards use the default keymap. SDL_SetKeymap(NULL, true); + SDL_DestroyKeymap(seat->keyboard.sdl_keymap); + seat->keyboard.sdl_keymap = NULL; } } static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) { - struct SDL_WaylandInput *input = data; + SDL_WaylandSeat *seat = data; char *map_str; const char *locale; @@ -1384,49 +1423,49 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, return; } - if (input->xkb.keymap != NULL) { + if (seat->keyboard.xkb.keymap != NULL) { /* if there's already a keymap loaded, throw it away rather than leaking it before * parsing the new one */ - WAYLAND_xkb_keymap_unref(input->xkb.keymap); - input->xkb.keymap = NULL; + WAYLAND_xkb_keymap_unref(seat->keyboard.xkb.keymap); + seat->keyboard.xkb.keymap = NULL; } - input->xkb.keymap = WAYLAND_xkb_keymap_new_from_string(input->display->xkb_context, + seat->keyboard.xkb.keymap = WAYLAND_xkb_keymap_new_from_string(seat->display->xkb_context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0); munmap(map_str, size); close(fd); - if (!input->xkb.keymap) { + if (!seat->keyboard.xkb.keymap) { SDL_SetError("failed to compile keymap"); return; } #define GET_MOD_INDEX(mod) \ - WAYLAND_xkb_keymap_mod_get_index(input->xkb.keymap, XKB_MOD_NAME_##mod) - input->xkb.idx_shift = 1 << GET_MOD_INDEX(SHIFT); - input->xkb.idx_ctrl = 1 << GET_MOD_INDEX(CTRL); - input->xkb.idx_alt = 1 << GET_MOD_INDEX(ALT); - input->xkb.idx_gui = 1 << GET_MOD_INDEX(LOGO); - input->xkb.idx_mod3 = 1 << GET_MOD_INDEX(MOD3); - input->xkb.idx_mod5 = 1 << GET_MOD_INDEX(MOD5); - input->xkb.idx_num = 1 << GET_MOD_INDEX(NUM); - input->xkb.idx_caps = 1 << GET_MOD_INDEX(CAPS); + WAYLAND_xkb_keymap_mod_get_index(seat->keyboard.xkb.keymap, XKB_MOD_NAME_##mod) + seat->keyboard.xkb.idx_shift = 1 << GET_MOD_INDEX(SHIFT); + seat->keyboard.xkb.idx_ctrl = 1 << GET_MOD_INDEX(CTRL); + seat->keyboard.xkb.idx_alt = 1 << GET_MOD_INDEX(ALT); + seat->keyboard.xkb.idx_gui = 1 << GET_MOD_INDEX(LOGO); + seat->keyboard.xkb.idx_mod3 = 1 << GET_MOD_INDEX(MOD3); + seat->keyboard.xkb.idx_mod5 = 1 << GET_MOD_INDEX(MOD5); + seat->keyboard.xkb.idx_num = 1 << GET_MOD_INDEX(NUM); + seat->keyboard.xkb.idx_caps = 1 << GET_MOD_INDEX(CAPS); #undef GET_MOD_INDEX - if (input->xkb.state != NULL) { + if (seat->keyboard.xkb.state != NULL) { /* if there's already a state, throw it away rather than leaking it before * trying to create a new one with the new keymap. */ - WAYLAND_xkb_state_unref(input->xkb.state); - input->xkb.state = NULL; + WAYLAND_xkb_state_unref(seat->keyboard.xkb.state); + seat->keyboard.xkb.state = NULL; } - input->xkb.state = WAYLAND_xkb_state_new(input->xkb.keymap); - if (!input->xkb.state) { + seat->keyboard.xkb.state = WAYLAND_xkb_state_new(seat->keyboard.xkb.keymap); + if (!seat->keyboard.xkb.state) { SDL_SetError("failed to create XKB state"); - WAYLAND_xkb_keymap_unref(input->xkb.keymap); - input->xkb.keymap = NULL; + WAYLAND_xkb_keymap_unref(seat->keyboard.xkb.keymap); + seat->keyboard.xkb.keymap = NULL; return; } @@ -1434,11 +1473,11 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, * Assume that a nameless layout implies a virtual keyboard with an arbitrary layout. * TODO: Use a better method of detection? */ - input->keyboard_is_virtual = WAYLAND_xkb_keymap_layout_get_name(input->xkb.keymap, 0) == NULL; + seat->keyboard.is_virtual = WAYLAND_xkb_keymap_layout_get_name(seat->keyboard.xkb.keymap, 0) == NULL; // Update the keymap if changed. - if (input->xkb.current_group != XKB_GROUP_INVALID) { - Wayland_UpdateKeymap(input); + if (seat->keyboard.xkb.current_group != XKB_GROUP_INVALID) { + Wayland_UpdateKeymap(seat); } /* @@ -1459,24 +1498,24 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, } // Set up XKB compose table - if (input->xkb.compose_table != NULL) { - WAYLAND_xkb_compose_table_unref(input->xkb.compose_table); - input->xkb.compose_table = NULL; + if (seat->keyboard.xkb.compose_table != NULL) { + WAYLAND_xkb_compose_table_unref(seat->keyboard.xkb.compose_table); + seat->keyboard.xkb.compose_table = NULL; } - input->xkb.compose_table = WAYLAND_xkb_compose_table_new_from_locale(input->display->xkb_context, + seat->keyboard.xkb.compose_table = WAYLAND_xkb_compose_table_new_from_locale(seat->display->xkb_context, locale, XKB_COMPOSE_COMPILE_NO_FLAGS); - if (input->xkb.compose_table) { + if (seat->keyboard.xkb.compose_table) { // Set up XKB compose state - if (input->xkb.compose_state != NULL) { - WAYLAND_xkb_compose_state_unref(input->xkb.compose_state); - input->xkb.compose_state = NULL; + if (seat->keyboard.xkb.compose_state != NULL) { + WAYLAND_xkb_compose_state_unref(seat->keyboard.xkb.compose_state); + seat->keyboard.xkb.compose_state = NULL; } - input->xkb.compose_state = WAYLAND_xkb_compose_state_new(input->xkb.compose_table, + seat->keyboard.xkb.compose_state = WAYLAND_xkb_compose_state_new(seat->keyboard.xkb.compose_table, XKB_COMPOSE_STATE_NO_FLAGS); - if (!input->xkb.compose_state) { + if (!seat->keyboard.xkb.compose_state) { SDL_SetError("could not create XKB compose state"); - WAYLAND_xkb_compose_table_unref(input->xkb.compose_table); - input->xkb.compose_table = NULL; + WAYLAND_xkb_compose_table_unref(seat->keyboard.xkb.compose_table); + seat->keyboard.xkb.compose_table = NULL; } } } @@ -1485,15 +1524,15 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, * Virtual keyboards can have arbitrary layouts, arbitrary scancodes/keycodes, etc... * Key presses from these devices must be looked up by their keysym value. */ -static SDL_Scancode Wayland_GetScancodeForKey(struct SDL_WaylandInput *input, uint32_t key) +static SDL_Scancode Wayland_GetScancodeForKey(SDL_WaylandSeat *seat, uint32_t key) { SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN; - if (!input->keyboard_is_virtual) { + if (!seat->keyboard.is_virtual) { scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, key); } else { const xkb_keysym_t *syms; - if (WAYLAND_xkb_keymap_key_get_syms_by_level(input->xkb.keymap, key + 8, input->xkb.current_group, 0, &syms) > 0) { + if (WAYLAND_xkb_keymap_key_get_syms_by_level(seat->keyboard.xkb.keymap, key + 8, seat->keyboard.xkb.current_group, 0, &syms) > 0) { scancode = SDL_GetScancodeFromKeySym(syms[0], key); } } @@ -1501,7 +1540,7 @@ static SDL_Scancode Wayland_GetScancodeForKey(struct SDL_WaylandInput *input, ui return scancode; } -static void Wayland_ReconcileModifiers(struct SDL_WaylandInput *input, bool key_pressed) +static void Wayland_ReconcileModifiers(SDL_WaylandSeat *seat, bool key_pressed) { /* Handle explicit pressed modifier state. This will correct the modifier state * if common modifier keys were remapped and the modifiers presumed to be set @@ -1509,55 +1548,55 @@ static void Wayland_ReconcileModifiers(struct SDL_WaylandInput *input, bool key_ * pressed state via means other than pressing the physical key. */ if (!key_pressed) { - if (input->xkb.wl_pressed_modifiers & input->xkb.idx_shift) { - if (!(input->pressed_modifiers & SDL_KMOD_SHIFT)) { - input->pressed_modifiers |= SDL_KMOD_SHIFT; + if (seat->keyboard.xkb.wl_pressed_modifiers & seat->keyboard.xkb.idx_shift) { + if (!(seat->keyboard.pressed_modifiers & SDL_KMOD_SHIFT)) { + seat->keyboard.pressed_modifiers |= SDL_KMOD_SHIFT; } } else { - input->pressed_modifiers &= ~SDL_KMOD_SHIFT; + seat->keyboard.pressed_modifiers &= ~SDL_KMOD_SHIFT; } - if (input->xkb.wl_pressed_modifiers & input->xkb.idx_ctrl) { - if (!(input->pressed_modifiers & SDL_KMOD_CTRL)) { - input->pressed_modifiers |= SDL_KMOD_CTRL; + if (seat->keyboard.xkb.wl_pressed_modifiers & seat->keyboard.xkb.idx_ctrl) { + if (!(seat->keyboard.pressed_modifiers & SDL_KMOD_CTRL)) { + seat->keyboard.pressed_modifiers |= SDL_KMOD_CTRL; } } else { - input->pressed_modifiers &= ~SDL_KMOD_CTRL; + seat->keyboard.pressed_modifiers &= ~SDL_KMOD_CTRL; } - if (input->xkb.wl_pressed_modifiers & input->xkb.idx_alt) { - if (!(input->pressed_modifiers & SDL_KMOD_ALT)) { - input->pressed_modifiers |= SDL_KMOD_ALT; + if (seat->keyboard.xkb.wl_pressed_modifiers & seat->keyboard.xkb.idx_alt) { + if (!(seat->keyboard.pressed_modifiers & SDL_KMOD_ALT)) { + seat->keyboard.pressed_modifiers |= SDL_KMOD_ALT; } } else { - input->pressed_modifiers &= ~SDL_KMOD_ALT; + seat->keyboard.pressed_modifiers &= ~SDL_KMOD_ALT; } - if (input->xkb.wl_pressed_modifiers & input->xkb.idx_gui) { - if (!(input->pressed_modifiers & SDL_KMOD_GUI)) { - input->pressed_modifiers |= SDL_KMOD_GUI; + if (seat->keyboard.xkb.wl_pressed_modifiers & seat->keyboard.xkb.idx_gui) { + if (!(seat->keyboard.pressed_modifiers & SDL_KMOD_GUI)) { + seat->keyboard.pressed_modifiers |= SDL_KMOD_GUI; } } else { - input->pressed_modifiers &= ~SDL_KMOD_GUI; + seat->keyboard.pressed_modifiers &= ~SDL_KMOD_GUI; } /* Note: This is not backwards: in the default keymap, Mod5 is typically * level 3 shift, and Mod3 is typically level 5 shift. */ - if (input->xkb.wl_pressed_modifiers & input->xkb.idx_mod3) { - if (!(input->pressed_modifiers & SDL_KMOD_LEVEL5)) { - input->pressed_modifiers |= SDL_KMOD_LEVEL5; + if (seat->keyboard.xkb.wl_pressed_modifiers & seat->keyboard.xkb.idx_mod3) { + if (!(seat->keyboard.pressed_modifiers & SDL_KMOD_LEVEL5)) { + seat->keyboard.pressed_modifiers |= SDL_KMOD_LEVEL5; } } else { - input->pressed_modifiers &= ~SDL_KMOD_LEVEL5; + seat->keyboard.pressed_modifiers &= ~SDL_KMOD_LEVEL5; } - if (input->xkb.wl_pressed_modifiers & input->xkb.idx_mod5) { - if (!(input->pressed_modifiers & SDL_KMOD_MODE)) { - input->pressed_modifiers |= SDL_KMOD_MODE; + if (seat->keyboard.xkb.wl_pressed_modifiers & seat->keyboard.xkb.idx_mod5) { + if (!(seat->keyboard.pressed_modifiers & SDL_KMOD_MODE)) { + seat->keyboard.pressed_modifiers |= SDL_KMOD_MODE; } } else { - input->pressed_modifiers &= ~SDL_KMOD_MODE; + seat->keyboard.pressed_modifiers &= ~SDL_KMOD_MODE; } } @@ -1568,80 +1607,80 @@ static void Wayland_ReconcileModifiers(struct SDL_WaylandInput *input, bool key_ * The modifier will remain active until the latch/lock is released by * the system. */ - if (input->xkb.wl_locked_modifiers & input->xkb.idx_shift) { - if (input->pressed_modifiers & SDL_KMOD_SHIFT) { - input->locked_modifiers &= ~SDL_KMOD_SHIFT; - input->locked_modifiers |= (input->pressed_modifiers & SDL_KMOD_SHIFT); - } else if (!(input->locked_modifiers & SDL_KMOD_SHIFT)) { - input->locked_modifiers |= SDL_KMOD_SHIFT; + if (seat->keyboard.xkb.wl_locked_modifiers & seat->keyboard.xkb.idx_shift) { + if (seat->keyboard.pressed_modifiers & SDL_KMOD_SHIFT) { + seat->keyboard.locked_modifiers &= ~SDL_KMOD_SHIFT; + seat->keyboard.locked_modifiers |= (seat->keyboard.pressed_modifiers & SDL_KMOD_SHIFT); + } else if (!(seat->keyboard.locked_modifiers & SDL_KMOD_SHIFT)) { + seat->keyboard.locked_modifiers |= SDL_KMOD_SHIFT; } } else { - input->locked_modifiers &= ~SDL_KMOD_SHIFT; + seat->keyboard.locked_modifiers &= ~SDL_KMOD_SHIFT; } - if (input->xkb.wl_locked_modifiers & input->xkb.idx_ctrl) { - if (input->pressed_modifiers & SDL_KMOD_CTRL) { - input->locked_modifiers &= ~SDL_KMOD_CTRL; - input->locked_modifiers |= (input->pressed_modifiers & SDL_KMOD_CTRL); - } else if (!(input->locked_modifiers & SDL_KMOD_CTRL)) { - input->locked_modifiers |= SDL_KMOD_CTRL; + if (seat->keyboard.xkb.wl_locked_modifiers & seat->keyboard.xkb.idx_ctrl) { + if (seat->keyboard.pressed_modifiers & SDL_KMOD_CTRL) { + seat->keyboard.locked_modifiers &= ~SDL_KMOD_CTRL; + seat->keyboard.locked_modifiers |= (seat->keyboard.pressed_modifiers & SDL_KMOD_CTRL); + } else if (!(seat->keyboard.locked_modifiers & SDL_KMOD_CTRL)) { + seat->keyboard.locked_modifiers |= SDL_KMOD_CTRL; } } else { - input->locked_modifiers &= ~SDL_KMOD_CTRL; + seat->keyboard.locked_modifiers &= ~SDL_KMOD_CTRL; } - if (input->xkb.wl_locked_modifiers & input->xkb.idx_alt) { - if (input->pressed_modifiers & SDL_KMOD_ALT) { - input->locked_modifiers &= ~SDL_KMOD_ALT; - input->locked_modifiers |= (input->pressed_modifiers & SDL_KMOD_ALT); - } else if (!(input->locked_modifiers & SDL_KMOD_ALT)) { - input->locked_modifiers |= SDL_KMOD_ALT; + if (seat->keyboard.xkb.wl_locked_modifiers & seat->keyboard.xkb.idx_alt) { + if (seat->keyboard.pressed_modifiers & SDL_KMOD_ALT) { + seat->keyboard.locked_modifiers &= ~SDL_KMOD_ALT; + seat->keyboard.locked_modifiers |= (seat->keyboard.pressed_modifiers & SDL_KMOD_ALT); + } else if (!(seat->keyboard.locked_modifiers & SDL_KMOD_ALT)) { + seat->keyboard.locked_modifiers |= SDL_KMOD_ALT; } } else { - input->locked_modifiers &= ~SDL_KMOD_ALT; + seat->keyboard.locked_modifiers &= ~SDL_KMOD_ALT; } - if (input->xkb.wl_locked_modifiers & input->xkb.idx_gui) { - if (input->pressed_modifiers & SDL_KMOD_GUI) { - input->locked_modifiers &= ~SDL_KMOD_GUI; - input->locked_modifiers |= (input->pressed_modifiers & SDL_KMOD_GUI); - } else if (!(input->locked_modifiers & SDL_KMOD_GUI)) { - input->locked_modifiers |= SDL_KMOD_GUI; + if (seat->keyboard.xkb.wl_locked_modifiers & seat->keyboard.xkb.idx_gui) { + if (seat->keyboard.pressed_modifiers & SDL_KMOD_GUI) { + seat->keyboard.locked_modifiers &= ~SDL_KMOD_GUI; + seat->keyboard.locked_modifiers |= (seat->keyboard.pressed_modifiers & SDL_KMOD_GUI); + } else if (!(seat->keyboard.locked_modifiers & SDL_KMOD_GUI)) { + seat->keyboard.locked_modifiers |= SDL_KMOD_GUI; } } else { - input->locked_modifiers &= ~SDL_KMOD_GUI; + seat->keyboard.locked_modifiers &= ~SDL_KMOD_GUI; } // As above, this is correct: Mod3 is typically level 5 shift, and Mod5 is typically level 3 shift. - if (input->xkb.wl_locked_modifiers & input->xkb.idx_mod3) { - input->locked_modifiers |= SDL_KMOD_LEVEL5; + if (seat->keyboard.xkb.wl_locked_modifiers & seat->keyboard.xkb.idx_mod3) { + seat->keyboard.locked_modifiers |= SDL_KMOD_LEVEL5; } else { - input->locked_modifiers &= ~SDL_KMOD_LEVEL5; + seat->keyboard.locked_modifiers &= ~SDL_KMOD_LEVEL5; } - if (input->xkb.wl_locked_modifiers & input->xkb.idx_mod5) { - input->locked_modifiers |= SDL_KMOD_MODE; + if (seat->keyboard.xkb.wl_locked_modifiers & seat->keyboard.xkb.idx_mod5) { + seat->keyboard.locked_modifiers |= SDL_KMOD_MODE; } else { - input->locked_modifiers &= ~SDL_KMOD_MODE; + seat->keyboard.locked_modifiers &= ~SDL_KMOD_MODE; } // Capslock and Numlock can only be locked, not pressed. - if (input->xkb.wl_locked_modifiers & input->xkb.idx_caps) { - input->locked_modifiers |= SDL_KMOD_CAPS; + if (seat->keyboard.xkb.wl_locked_modifiers & seat->keyboard.xkb.idx_caps) { + seat->keyboard.locked_modifiers |= SDL_KMOD_CAPS; } else { - input->locked_modifiers &= ~SDL_KMOD_CAPS; + seat->keyboard.locked_modifiers &= ~SDL_KMOD_CAPS; } - if (input->xkb.wl_locked_modifiers & input->xkb.idx_num) { - input->locked_modifiers |= SDL_KMOD_NUM; + if (seat->keyboard.xkb.wl_locked_modifiers & seat->keyboard.xkb.idx_num) { + seat->keyboard.locked_modifiers |= SDL_KMOD_NUM; } else { - input->locked_modifiers &= ~SDL_KMOD_NUM; + seat->keyboard.locked_modifiers &= ~SDL_KMOD_NUM; } - SDL_SetModState(input->pressed_modifiers | input->locked_modifiers); + SDL_SetModState(seat->keyboard.pressed_modifiers | seat->keyboard.locked_modifiers); } -static void Wayland_HandleModifierKeys(struct SDL_WaylandInput *input, SDL_Scancode scancode, bool pressed) +static void Wayland_HandleModifierKeys(SDL_WaylandSeat *seat, SDL_Scancode scancode, bool pressed) { const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false); SDL_Keymod mod; @@ -1688,41 +1727,47 @@ static void Wayland_HandleModifierKeys(struct SDL_WaylandInput *input, SDL_Scanc } if (pressed) { - input->pressed_modifiers |= mod; + seat->keyboard.pressed_modifiers |= mod; } else { - input->pressed_modifiers &= ~mod; + seat->keyboard.pressed_modifiers &= ~mod; } - Wayland_ReconcileModifiers(input, true); + Wayland_ReconcileModifiers(seat, true); } static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { - struct SDL_WaylandInput *input = data; - SDL_WindowData *window; + SDL_WaylandSeat *seat = data; uint32_t *key; if (!surface) { - // enter event for a window we've just destroyed + // Enter event for a destroyed surface. return; } - window = Wayland_GetWindowDataForOwnedSurface(surface); - + SDL_WindowData *window = Wayland_GetWindowDataForOwnedSurface(surface); if (!window) { + // Not a surface owned by SDL. return; } - input->keyboard_focus = window; - window->keyboard_device = input; + ++window->keyboard_focus_count; + seat->keyboard.focus = window; // Restore the keyboard focus to the child popup that was holding it SDL_SetKeyboardFocus(window->keyboard_focus ? window->keyboard_focus : window->sdlwindow); + // Update the keyboard grab and any relative pointer grabs related to this keyboard focus. + Wayland_SeatUpdateKeyboardGrab(seat); + Wayland_DisplayUpdatePointerGrabs(seat->display, window); + + // Update text input and IME focus. + Wayland_UpdateTextInput(seat->display); + #ifdef SDL_USE_IME - if (!input->text_input) { + if (!seat->text_input.zwp_text_input) { SDL_IME_SetFocus(true); } #endif @@ -1730,8 +1775,12 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, Uint64 timestamp = SDL_GetTicksNS(); window->last_focus_event_time_ns = timestamp; + if (SDL_GetCurrentKeymap() != seat->keyboard.sdl_keymap) { + SDL_SetKeymap(seat->keyboard.sdl_keymap, true); + } + wl_array_for_each (key, keys) { - const SDL_Scancode scancode = Wayland_GetScancodeForKey(input, *key); + const SDL_Scancode scancode = Wayland_GetScancodeForKey(seat, *key); const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false); switch (keycode) { @@ -1745,8 +1794,8 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, case SDLK_RGUI: case SDLK_MODE: case SDLK_LEVEL5_SHIFT: - Wayland_HandleModifierKeys(input, scancode, true); - SDL_SendKeyboardKeyIgnoreModifiers(timestamp, input->keyboard_id, *key, scancode, true); + Wayland_HandleModifierKeys(seat, scancode, true); + SDL_SendKeyboardKeyIgnoreModifiers(timestamp, seat->keyboard.sdl_id, *key, scancode, true); break; default: break; @@ -1757,58 +1806,74 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) { - struct SDL_WaylandInput *input = data; - SDL_WindowData *wind; - SDL_Window *window = NULL; + SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; if (!surface) { + // Leave event for a destroyed surface. return; } - wind = Wayland_GetWindowDataForOwnedSurface(surface); - if (!wind) { + SDL_WindowData *window = Wayland_GetWindowDataForOwnedSurface(surface); + if (!window) { + // Not a surface owned by SDL. return; } - wind->keyboard_device = NULL; - window = wind->sdlwindow; - // Stop key repeat before clearing keyboard focus - keyboard_repeat_clear(&input->keyboard_repeat); + keyboard_repeat_clear(&seat->keyboard.repeat); - // This will release any keys still pressed - SDL_SetKeyboardFocus(NULL); - input->keyboard_focus = NULL; + SDL_Window *keyboard_focus = SDL_GetKeyboardFocus(); + + // The keyboard focus may be a child popup + while (keyboard_focus && SDL_WINDOW_IS_POPUP(keyboard_focus)) { + keyboard_focus = keyboard_focus->parent; + } + + const bool had_focus = keyboard_focus && window->sdlwindow == keyboard_focus; + seat->keyboard.focus = NULL; + --window->keyboard_focus_count; + + // Only relinquish focus if this window has the active focus, and no other keyboards have focus on the window. + if (!window->keyboard_focus_count && had_focus) { + SDL_SetKeyboardFocus(NULL); + } + + // Release the keyboard grab and any relative pointer grabs related to this keyboard focus. + Wayland_SeatUpdateKeyboardGrab(seat); + Wayland_DisplayUpdatePointerGrabs(seat->display, window); // Clear the pressed modifiers. - input->pressed_modifiers = SDL_KMOD_NONE; + seat->keyboard.pressed_modifiers = SDL_KMOD_NONE; + + // Update text input and IME focus. + Wayland_UpdateTextInput(seat->display); #ifdef SDL_USE_IME - if (!input->text_input) { + if (!seat->text_input.zwp_text_input && !window->keyboard_focus_count) { SDL_IME_SetFocus(false); } #endif - /* If the surface had a pointer leave event while still having active touch events, it retained mouse focus. - * Clear it now if all touch events are raised. + /* If the window has mouse focus, has no pointers within it, and no active touches, consider + * mouse focus to be lost. */ - if (!input->pointer_focus && SDL_GetMouseFocus() == window && !Wayland_SurfaceHasActiveTouches(surface)) { + if (SDL_GetMouseFocus() == window->sdlwindow && !window->pointer_focus_count && + !Wayland_SurfaceHasActiveTouches(seat->display, surface)) { SDL_SetMouseFocus(NULL); } } -static bool keyboard_input_get_text(char text[8], const struct SDL_WaylandInput *input, uint32_t key, bool down, bool *handled_by_ime) +static bool keyboard_input_get_text(char text[8], const SDL_WaylandSeat *seat, uint32_t key, bool down, bool *handled_by_ime) { - SDL_WindowData *window = input->keyboard_focus; const xkb_keysym_t *syms; xkb_keysym_t sym; - if (!window || window->keyboard_device != input || !input->xkb.state) { + if (!seat->keyboard.focus || !seat->keyboard.xkb.state) { return false; } // TODO: Can this happen? - if (WAYLAND_xkb_state_key_get_syms(input->xkb.state, key + 8, &syms) != 1) { + if (WAYLAND_xkb_state_key_get_syms(seat->keyboard.xkb.state, key + 8, &syms) != 1) { return false; } sym = syms[0]; @@ -1826,8 +1891,8 @@ static bool keyboard_input_get_text(char text[8], const struct SDL_WaylandInput return false; } - if (input->xkb.compose_state && WAYLAND_xkb_compose_state_feed(input->xkb.compose_state, sym) == XKB_COMPOSE_FEED_ACCEPTED) { - switch (WAYLAND_xkb_compose_state_get_status(input->xkb.compose_state)) { + if (seat->keyboard.xkb.compose_state && WAYLAND_xkb_compose_state_feed(seat->keyboard.xkb.compose_state, sym) == XKB_COMPOSE_FEED_ACCEPTED) { + switch (WAYLAND_xkb_compose_state_get_status(seat->keyboard.xkb.compose_state)) { case XKB_COMPOSE_COMPOSING: if (handled_by_ime) { *handled_by_ime = true; @@ -1840,7 +1905,7 @@ static bool keyboard_input_get_text(char text[8], const struct SDL_WaylandInput case XKB_COMPOSE_NOTHING: break; case XKB_COMPOSE_COMPOSED: - sym = WAYLAND_xkb_compose_state_get_one_sym(input->xkb.compose_state); + sym = WAYLAND_xkb_compose_state_get_one_sym(seat->keyboard.xkb.compose_state); break; } } @@ -1852,38 +1917,43 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state_w) { - struct SDL_WaylandInput *input = data; + SDL_WaylandSeat *seat = data; enum wl_keyboard_key_state state = state_w; char text[8]; bool has_text = false; bool handled_by_ime = false; - const Uint64 timestamp_raw_ns = Wayland_GetKeyboardTimestampRaw(input, time); + const Uint64 timestamp_raw_ns = Wayland_GetKeyboardTimestampRaw(seat, time); - Wayland_UpdateImplicitGrabSerial(input, serial); + Wayland_UpdateImplicitGrabSerial(seat, serial); + + if (seat->keyboard.sdl_keymap != SDL_GetCurrentKeymap()) { + SDL_SetKeymap(seat->keyboard.sdl_keymap, true); + SDL_SetModState(seat->keyboard.pressed_modifiers | seat->keyboard.locked_modifiers); + } if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { SDL_Window *keyboard_focus = SDL_GetKeyboardFocus(); if (keyboard_focus && SDL_TextInputActive(keyboard_focus)) { - has_text = keyboard_input_get_text(text, input, key, true, &handled_by_ime); + has_text = keyboard_input_get_text(text, seat, key, true, &handled_by_ime); } } else { - if (keyboard_repeat_key_is_set(&input->keyboard_repeat, key)) { + if (keyboard_repeat_key_is_set(&seat->keyboard.repeat, key)) { /* Send any due key repeat events before stopping the repeat and generating the key up event. * Compute time based on the Wayland time, as it reports when the release event happened. * Using SDL_GetTicks would be wrong, as it would report when the release event is processed, * which may be off if the application hasn't pumped events for a while. */ - keyboard_repeat_handle(&input->keyboard_repeat, timestamp_raw_ns - input->keyboard_repeat.wl_press_time_ns); - keyboard_repeat_clear(&input->keyboard_repeat); + keyboard_repeat_handle(&seat->keyboard.repeat, timestamp_raw_ns - seat->keyboard.repeat.wl_press_time_ns); + keyboard_repeat_clear(&seat->keyboard.repeat); } - keyboard_input_get_text(text, input, key, false, &handled_by_ime); + keyboard_input_get_text(text, seat, key, false, &handled_by_ime); } - const SDL_Scancode scancode = Wayland_GetScancodeForKey(input, key); - Wayland_HandleModifierKeys(input, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED); - Uint64 timestamp = Wayland_GetKeyboardTimestamp(input, time); + const SDL_Scancode scancode = Wayland_GetScancodeForKey(seat, key); + Wayland_HandleModifierKeys(seat, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED); + Uint64 timestamp = Wayland_GetKeyboardTimestamp(seat, time); - SDL_SendKeyboardKeyIgnoreModifiers(timestamp, input->keyboard_id, key, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED); + SDL_SendKeyboardKeyIgnoreModifiers(timestamp, seat->keyboard.sdl_id, key, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED); if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { if (has_text && !(SDL_GetModState() & (SDL_KMOD_CTRL | SDL_KMOD_ALT))) { @@ -1891,8 +1961,8 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard, SDL_SendKeyboardText(text); } } - if (input->xkb.keymap && WAYLAND_xkb_keymap_key_repeats(input->xkb.keymap, key + 8)) { - keyboard_repeat_set(&input->keyboard_repeat, input->keyboard_id, key, timestamp_raw_ns, scancode, has_text, text); + if (seat->keyboard.xkb.keymap && WAYLAND_xkb_keymap_key_repeats(seat->keyboard.xkb.keymap, key + 8)) { + keyboard_repeat_set(&seat->keyboard.repeat, seat->keyboard.sdl_id, key, timestamp_raw_ns, scancode, has_text, text); } } } @@ -1902,48 +1972,48 @@ static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { - struct SDL_WaylandInput *input = data; + SDL_WaylandSeat *seat = data; - if (input->xkb.state == NULL) { + if (seat->keyboard.xkb.state == NULL) { /* if we get a modifier notification before the keymap, there's nothing we can do with the information */ return; } - WAYLAND_xkb_state_update_mask(input->xkb.state, mods_depressed, mods_latched, + WAYLAND_xkb_state_update_mask(seat->keyboard.xkb.state, mods_depressed, mods_latched, mods_locked, 0, 0, group); - input->xkb.wl_pressed_modifiers = mods_depressed; - input->xkb.wl_locked_modifiers = mods_latched | mods_locked; + seat->keyboard.xkb.wl_pressed_modifiers = mods_depressed; + seat->keyboard.xkb.wl_locked_modifiers = mods_latched | mods_locked; - Wayland_ReconcileModifiers(input, false); + Wayland_ReconcileModifiers(seat, false); // If a key is repeating, update the text to apply the modifier. - if (keyboard_repeat_is_set(&input->keyboard_repeat)) { + if (keyboard_repeat_is_set(&seat->keyboard.repeat)) { char text[8]; - const uint32_t key = keyboard_repeat_get_key(&input->keyboard_repeat); + const uint32_t key = keyboard_repeat_get_key(&seat->keyboard.repeat); - if (keyboard_input_get_text(text, input, key, true, NULL)) { - keyboard_repeat_set_text(&input->keyboard_repeat, text); + if (keyboard_input_get_text(text, seat, key, true, NULL)) { + keyboard_repeat_set_text(&seat->keyboard.repeat, text); } } - if (group == input->xkb.current_group) { + if (group == seat->keyboard.xkb.current_group) { return; } // The layout changed, remap and fire an event. Virtual keyboards use the default keymap. - input->xkb.current_group = group; - Wayland_UpdateKeymap(input); + seat->keyboard.xkb.current_group = group; + Wayland_UpdateKeymap(seat); } static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) { - struct SDL_WaylandInput *input = data; - input->keyboard_repeat.repeat_rate = SDL_clamp(rate, 0, 1000); - input->keyboard_repeat.repeat_delay_ms = delay; - input->keyboard_repeat.is_initialized = true; + SDL_WaylandSeat *seat = data; + seat->keyboard.repeat.repeat_rate = SDL_clamp(rate, 0, 1000); + seat->keyboard.repeat.repeat_delay_ms = delay; + seat->keyboard.repeat.is_initialized = true; } static const struct wl_keyboard_listener keyboard_listener = { @@ -1955,96 +2025,243 @@ static const struct wl_keyboard_listener keyboard_listener = { keyboard_handle_repeat_info, // Version 4 }; -void Wayland_input_init_relative_pointer(SDL_VideoData *d) +static void Wayland_SeatCreateRelativePointer(SDL_WaylandSeat *seat) { - struct SDL_WaylandInput *input = d->input; - - if (!d->relative_pointer_manager) { - return; - } - - if (input->pointer && !input->relative_pointer) { - input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(input->display->relative_pointer_manager, input->pointer); - zwp_relative_pointer_v1_add_listener(input->relative_pointer, - &relative_pointer_listener, - input); + if (seat->display->relative_pointer_manager) { + if (seat->pointer.wl_pointer && !seat->pointer.relative_pointer) { + seat->pointer.relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(seat->display->relative_pointer_manager, seat->pointer.wl_pointer); + zwp_relative_pointer_v1_add_listener(seat->pointer.relative_pointer, + &relative_pointer_listener, + seat); + } } } -static void seat_handle_capabilities(void *data, struct wl_seat *seat, - enum wl_seat_capability caps) +void Wayland_DisplayInitRelativePointerManager(SDL_VideoData *display) { - struct SDL_WaylandInput *input = data; + SDL_WaylandSeat *seat; + wl_list_for_each(seat, &display->seat_list, link) { + Wayland_SeatCreateRelativePointer(seat); + } +} - if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) { - input->pointer = wl_seat_get_pointer(seat); - SDL_memset(&input->pointer_curr_axis_info, 0, sizeof(input->pointer_curr_axis_info)); - input->display->pointer = input->pointer; +static void Wayland_SeatDestroyPointer(SDL_WaylandSeat *seat, bool send_event) +{ + // Make sure focus is removed from a surface before the pointer is destroyed. + if (seat->pointer.focus) { + pointer_handle_leave(seat, seat->pointer.wl_pointer, 0, seat->pointer.focus->surface); + } - Wayland_CreateCursorShapeDevice(input); + SDL_RemoveMouse(seat->pointer.sdl_id, send_event); - wl_pointer_set_user_data(input->pointer, input); - wl_pointer_add_listener(input->pointer, &pointer_listener, input); + if (seat->pointer.confined_pointer) { + zwp_confined_pointer_v1_destroy(seat->pointer.confined_pointer); + } - Wayland_input_init_relative_pointer(input->display); + if (seat->pointer.locked_pointer) { + zwp_locked_pointer_v1_destroy(seat->pointer.locked_pointer); + } - input->pointer_id = SDL_GetNextObjectID(); - SDL_AddMouse(input->pointer_id, WAYLAND_DEFAULT_POINTER_NAME, !input->display->initializing); - } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) { - if (input->relative_pointer) { - zwp_relative_pointer_v1_destroy(input->relative_pointer); - input->relative_pointer = NULL; - } - if (input->cursor_shape) { - wp_cursor_shape_device_v1_destroy(input->cursor_shape); - input->cursor_shape = NULL; - } - if (wl_pointer_get_version(input->pointer) >= WL_POINTER_RELEASE_SINCE_VERSION) { - wl_pointer_release(input->pointer); + if (seat->pointer.relative_pointer) { + zwp_relative_pointer_v1_destroy(seat->pointer.relative_pointer); + } + + if (seat->pointer.timestamps) { + zwp_input_timestamps_v1_destroy(seat->pointer.timestamps); + } + + if (seat->pointer.cursor_state.frame_callback) { + wl_callback_destroy(seat->pointer.cursor_state.frame_callback); + } + + if (seat->pointer.cursor_state.surface) { + wl_surface_destroy(seat->pointer.cursor_state.surface); + } + + if (seat->pointer.cursor_state.viewport) { + wp_viewport_destroy(seat->pointer.cursor_state.viewport); + } + + if (seat->pointer.cursor_shape) { + wp_cursor_shape_device_v1_destroy(seat->pointer.cursor_shape); + } + + if (seat->pointer.wl_pointer) { + if (wl_pointer_get_version(seat->pointer.wl_pointer) >= WL_POINTER_RELEASE_SINCE_VERSION) { + wl_pointer_release(seat->pointer.wl_pointer); } else { - wl_pointer_destroy(input->pointer); + wl_pointer_destroy(seat->pointer.wl_pointer); } - input->pointer = NULL; - input->display->pointer = NULL; - - SDL_RemoveMouse(input->pointer_id, true); - input->pointer_id = 0; } - if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !input->touch) { - input->touch = wl_seat_get_touch(seat); - SDL_AddTouch((SDL_TouchID)(uintptr_t)input->touch, SDL_TOUCH_DEVICE_DIRECT, "wayland_touch"); - wl_touch_set_user_data(input->touch, input); - wl_touch_add_listener(input->touch, &touch_listener, - input); - } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && input->touch) { - SDL_DelTouch((SDL_TouchID)(intptr_t)input->touch); - wl_touch_destroy(input->touch); - input->touch = NULL; + SDL_zero(seat->pointer); +} + +static void Wayland_SeatDestroyKeyboard(SDL_WaylandSeat *seat, bool send_event) +{ + // Make sure focus is removed from a surface before the keyboard is destroyed. + if (seat->keyboard.focus) { + keyboard_handle_leave(seat, seat->keyboard.wl_keyboard, 0, seat->keyboard.focus->surface); } - if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) { - input->keyboard = wl_seat_get_keyboard(seat); - wl_keyboard_set_user_data(input->keyboard, input); - wl_keyboard_add_listener(input->keyboard, &keyboard_listener, - input); + SDL_RemoveKeyboard(seat->keyboard.sdl_id, send_event); - input->keyboard_id = SDL_GetNextObjectID(); - SDL_AddKeyboard(input->keyboard_id, WAYLAND_DEFAULT_KEYBOARD_NAME, !input->display->initializing); - } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) { - wl_keyboard_destroy(input->keyboard); - input->keyboard = NULL; - - SDL_RemoveKeyboard(input->keyboard_id, true); - input->keyboard_id = 0; + if (seat->keyboard.sdl_keymap) { + if (seat->keyboard.sdl_keymap == SDL_GetCurrentKeymap()) { + SDL_SetKeymap(NULL, false); + } + SDL_DestroyKeymap(seat->keyboard.sdl_keymap); } - Wayland_RegisterTimestampListeners(input); + if (seat->keyboard.key_inhibitor) { + zwp_keyboard_shortcuts_inhibitor_v1_destroy(seat->keyboard.key_inhibitor); + } + + if (seat->keyboard.timestamps) { + zwp_input_timestamps_v1_destroy(seat->keyboard.timestamps); + } + + if (seat->keyboard.wl_keyboard) { + if (wl_keyboard_get_version(seat->keyboard.wl_keyboard) >= WL_KEYBOARD_RELEASE_SINCE_VERSION) { + wl_keyboard_release(seat->keyboard.wl_keyboard); + } else { + wl_keyboard_destroy(seat->keyboard.wl_keyboard); + } + } + + if (seat->keyboard.xkb.compose_state) { + WAYLAND_xkb_compose_state_unref(seat->keyboard.xkb.compose_state); + } + + if (seat->keyboard.xkb.compose_table) { + WAYLAND_xkb_compose_table_unref(seat->keyboard.xkb.compose_table); + } + + if (seat->keyboard.xkb.state) { + WAYLAND_xkb_state_unref(seat->keyboard.xkb.state); + } + + if (seat->keyboard.xkb.keymap) { + WAYLAND_xkb_keymap_unref(seat->keyboard.xkb.keymap); + } + + SDL_zero(seat->keyboard); +} + +static void Wayland_SeatDestroyTouch(SDL_WaylandSeat *seat) +{ + // Cancel any active touches before the touch object is destroyed. + if (seat->touch.wl_touch) { + touch_handler_cancel(seat, seat->touch.wl_touch); + } + + SDL_DelTouch((SDL_TouchID)(uintptr_t)seat->touch.wl_touch); + + if (seat->touch.timestamps) { + zwp_input_timestamps_v1_destroy(seat->touch.timestamps); + } + + if (seat->touch.wl_touch) { + if (wl_touch_get_version(seat->touch.wl_touch) >= WL_TOUCH_RELEASE_SINCE_VERSION) { + wl_touch_release(seat->touch.wl_touch); + } else { + wl_touch_destroy(seat->touch.wl_touch); + } + } + + SDL_zero(seat->touch); + WAYLAND_wl_list_init(&seat->touch.points); +} + +static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability capabilities) +{ + SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; + char name_fmt[256]; + + if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !seat->pointer.wl_pointer) { + seat->pointer.wl_pointer = wl_seat_get_pointer(wl_seat); + SDL_memset(&seat->pointer.current_axis_info, 0, sizeof(seat->pointer.current_axis_info)); + + Wayland_SeatCreateCursorShape(seat); + + wl_pointer_set_user_data(seat->pointer.wl_pointer, seat); + wl_pointer_add_listener(seat->pointer.wl_pointer, &pointer_listener, seat); + + Wayland_SeatCreateRelativePointer(seat); + + seat->pointer.sdl_id = SDL_GetNextObjectID(); + + if (seat->name) { + SDL_snprintf(name_fmt, sizeof(name_fmt), "%s (%s)", WAYLAND_DEFAULT_POINTER_NAME, seat->name); + } else { + SDL_snprintf(name_fmt, sizeof(name_fmt), "%s %" SDL_PRIu32, WAYLAND_DEFAULT_POINTER_NAME, seat->pointer.sdl_id); + } + + SDL_AddMouse(seat->pointer.sdl_id, name_fmt, !seat->display->initializing); + } else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && seat->pointer.wl_pointer) { + Wayland_SeatDestroyPointer(seat, true); + } + + if ((capabilities & WL_SEAT_CAPABILITY_TOUCH) && !seat->touch.wl_touch) { + seat->touch.wl_touch = wl_seat_get_touch(wl_seat); + wl_touch_set_user_data(seat->touch.wl_touch, seat); + wl_touch_add_listener(seat->touch.wl_touch, &touch_listener, seat); + + if (seat->name) { + SDL_snprintf(name_fmt, sizeof(name_fmt), "%s (%s)", WAYLAND_DEFAULT_TOUCH_NAME, seat->name); + } else { + SDL_snprintf(name_fmt, sizeof(name_fmt), "%s %" SDL_PRIu64, WAYLAND_DEFAULT_TOUCH_NAME, (SDL_TouchID)(uintptr_t)seat->touch.wl_touch); + } + + SDL_AddTouch((SDL_TouchID)(uintptr_t)seat->touch.wl_touch, SDL_TOUCH_DEVICE_DIRECT, name_fmt); + } else if (!(capabilities & WL_SEAT_CAPABILITY_TOUCH) && seat->touch.wl_touch) { + Wayland_SeatDestroyTouch(seat); + } + + if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && !seat->keyboard.wl_keyboard) { + seat->keyboard.wl_keyboard = wl_seat_get_keyboard(wl_seat); + wl_keyboard_set_user_data(seat->keyboard.wl_keyboard, seat); + wl_keyboard_add_listener(seat->keyboard.wl_keyboard, &keyboard_listener, seat); + + seat->keyboard.sdl_id = SDL_GetNextObjectID(); + + if (seat->name) { + SDL_snprintf(name_fmt, sizeof(name_fmt), "%s (%s)", WAYLAND_DEFAULT_KEYBOARD_NAME, seat->name); + } else { + SDL_snprintf(name_fmt, sizeof(name_fmt), "%s %" SDL_PRIu32, WAYLAND_DEFAULT_KEYBOARD_NAME, seat->keyboard.sdl_id); + } + + SDL_AddKeyboard(seat->keyboard.sdl_id, name_fmt, !seat->display->initializing); + } else if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && seat->keyboard.wl_keyboard) { + Wayland_SeatDestroyKeyboard(seat, true); + } + + Wayland_SeatRegisterInputTimestampListeners(seat); } static void seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) { - // unimplemented + SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; + char name_fmt[256]; + + if (name && *name != '\0') { + seat->name = SDL_strdup(name); + + if (seat->keyboard.wl_keyboard) { + SDL_snprintf(name_fmt, sizeof(name_fmt), "%s (%s)", WAYLAND_DEFAULT_KEYBOARD_NAME, seat->name); + SDL_SetKeyboardName(seat->keyboard.sdl_id, name_fmt); + } + + if (seat->pointer.wl_pointer) { + SDL_snprintf(name_fmt, sizeof(name_fmt), "%s (%s)", WAYLAND_DEFAULT_POINTER_NAME, seat->name); + SDL_SetMouseName(seat->pointer.sdl_id, name_fmt); + } + + if (seat->touch.wl_touch) { + SDL_snprintf(name_fmt, sizeof(name_fmt), "%s (%s)", WAYLAND_DEFAULT_TOUCH_NAME, seat->name); + SDL_SetTouchName((SDL_TouchID)(uintptr_t)seat->touch.wl_touch, name_fmt); + } + } } static const struct wl_seat_listener seat_listener = { @@ -2230,8 +2447,10 @@ static void data_device_handle_data_offer(void *data, struct wl_data_device *wl_ { SDL_WaylandDataOffer *data_offer = SDL_calloc(1, sizeof(*data_offer)); if (data_offer) { + SDL_WaylandDataDevice *data_device = (SDL_WaylandDataDevice *)data; + data_device->seat->display->last_incoming_data_offer_seat = data_device->seat; data_offer->offer = id; - data_offer->data_device = data; + data_offer->data_device = data_device; WAYLAND_wl_list_init(&(data_offer->mimes)); wl_data_offer_set_user_data(id, data_offer); wl_data_offer_add_listener(id, &data_offer_listener, data_offer); @@ -2543,8 +2762,10 @@ static void primary_selection_device_handle_offer(void *data, struct zwp_primary { SDL_WaylandPrimarySelectionOffer *primary_selection_offer = SDL_calloc(1, sizeof(*primary_selection_offer)); if (primary_selection_offer) { + SDL_WaylandPrimarySelectionDevice *primary_selection_device = (SDL_WaylandPrimarySelectionDevice *)data; + primary_selection_device->seat->display->last_incoming_primary_selection_seat = primary_selection_device->seat; primary_selection_offer->offer = id; - primary_selection_offer->primary_selection_device = data; + primary_selection_offer->primary_selection_device = primary_selection_device; WAYLAND_wl_list_init(&(primary_selection_offer->mimes)); zwp_primary_selection_offer_v1_set_user_data(id, primary_selection_offer); zwp_primary_selection_offer_v1_add_listener(id, &primary_selection_offer_listener, primary_selection_offer); @@ -2598,8 +2819,8 @@ static void text_input_preedit_string(void *data, int32_t cursor_begin, int32_t cursor_end) { - SDL_WaylandTextInput *text_input = data; - text_input->has_preedit = true; + SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; + seat->text_input.has_preedit = true; if (text) { int cursor_begin_utf8 = cursor_begin >= 0 ? (int)SDL_utf8strnlen(text, cursor_begin) : -1; int cursor_end_utf8 = cursor_end >= 0 ? (int)SDL_utf8strnlen(text, cursor_end) : -1; @@ -2638,11 +2859,11 @@ static void text_input_done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, uint32_t serial) { - SDL_WaylandTextInput *text_input = data; - if (!text_input->has_preedit) { + SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; + if (!seat->text_input.has_preedit) { SDL_SendEditingText("", 0, 0); } - text_input->has_preedit = false; + seat->text_input.has_preedit = false; } static const struct zwp_text_input_v3_listener text_input_listener = { @@ -2654,23 +2875,20 @@ static const struct zwp_text_input_v3_listener text_input_listener = { text_input_done }; -void Wayland_create_data_device(SDL_VideoData *d) +static void Wayland_SeatCreateDataDevice(SDL_WaylandSeat *seat) { - SDL_WaylandDataDevice *data_device = NULL; - - if (!d->input->seat) { - // No seat yet, will be initialized later. + if (!seat->display->data_device_manager) { return; } - data_device = SDL_calloc(1, sizeof(*data_device)); + SDL_WaylandDataDevice *data_device = SDL_calloc(1, sizeof(*data_device)); if (!data_device) { return; } data_device->data_device = wl_data_device_manager_get_data_device( - d->data_device_manager, d->input->seat); - data_device->video_data = d; + seat->display->data_device_manager, seat->wl_seat); + data_device->seat = seat; if (!data_device->data_device) { SDL_free(data_device); @@ -2678,27 +2896,32 @@ void Wayland_create_data_device(SDL_VideoData *d) wl_data_device_set_user_data(data_device->data_device, data_device); wl_data_device_add_listener(data_device->data_device, &data_device_listener, data_device); - d->input->data_device = data_device; + seat->data_device = data_device; } } -void Wayland_create_primary_selection_device(SDL_VideoData *d) +void Wayland_DisplayInitDataDeviceManager(SDL_VideoData *display) { - SDL_WaylandPrimarySelectionDevice *primary_selection_device = NULL; + SDL_WaylandSeat *seat; + wl_list_for_each (seat, &display->seat_list, link) { + Wayland_SeatCreateDataDevice(seat); + } +} - if (!d->input->seat) { - // No seat yet, will be initialized later. +static void Wayland_SeatCreatePrimarySelectionDevice(SDL_WaylandSeat *seat) +{ + if (!seat->display->primary_selection_device_manager) { return; } - primary_selection_device = SDL_calloc(1, sizeof(*primary_selection_device)); + SDL_WaylandPrimarySelectionDevice *primary_selection_device = SDL_calloc(1, sizeof(*primary_selection_device)); if (!primary_selection_device) { return; } primary_selection_device->primary_selection_device = zwp_primary_selection_device_manager_v1_get_device( - d->primary_selection_device_manager, d->input->seat); - primary_selection_device->video_data = d; + seat->display->primary_selection_device_manager, seat->wl_seat); + primary_selection_device->seat = seat; if (!primary_selection_device->primary_selection_device) { SDL_free(primary_selection_device); @@ -2707,38 +2930,32 @@ void Wayland_create_primary_selection_device(SDL_VideoData *d) primary_selection_device); zwp_primary_selection_device_v1_add_listener(primary_selection_device->primary_selection_device, &primary_selection_device_listener, primary_selection_device); - d->input->primary_selection_device = primary_selection_device; + seat->primary_selection_device = primary_selection_device; } } -static void Wayland_create_text_input(SDL_VideoData *d) +void Wayland_DisplayInitPrimarySelectionDeviceManager(SDL_VideoData *display) { - SDL_WaylandTextInput *text_input = NULL; - - if (!d->input->seat) { - // No seat yet, will be initialized later. - return; - } - - text_input = SDL_calloc(1, sizeof(*text_input)); - if (!text_input) { - return; - } - - text_input->text_input = zwp_text_input_manager_v3_get_text_input( - d->text_input_manager, d->input->seat); - - if (!text_input->text_input) { - SDL_free(text_input); - } else { - zwp_text_input_v3_set_user_data(text_input->text_input, text_input); - zwp_text_input_v3_add_listener(text_input->text_input, - &text_input_listener, text_input); - d->input->text_input = text_input; + SDL_WaylandSeat *seat; + wl_list_for_each (seat, &display->seat_list, link) { + Wayland_SeatCreatePrimarySelectionDevice(seat); } } -void Wayland_create_text_input_manager(SDL_VideoData *d, uint32_t id) +static void Wayland_SeatCreateTextInput(SDL_WaylandSeat *seat) +{ + if (seat->display->text_input_manager) { + seat->text_input.zwp_text_input = zwp_text_input_manager_v3_get_text_input(seat->display->text_input_manager, seat->wl_seat); + + if (seat->text_input.zwp_text_input) { + zwp_text_input_v3_set_user_data(seat->text_input.zwp_text_input, seat); + zwp_text_input_v3_add_listener(seat->text_input.zwp_text_input, + &text_input_listener, seat); + } + } +} + +void Wayland_DisplayCreateTextInputManager(SDL_VideoData *d, uint32_t id) { #ifdef HAVE_FCITX const char *im_module = SDL_getenv("SDL_IM_MODULE"); @@ -2753,7 +2970,11 @@ void Wayland_create_text_input_manager(SDL_VideoData *d, uint32_t id) #endif d->text_input_manager = wl_registry_bind(d->registry, id, &zwp_text_input_manager_v3_interface, 1); - Wayland_create_text_input(d); + + SDL_WaylandSeat *seat; + wl_list_for_each(seat, &d->seat_list, link) { + Wayland_SeatCreateTextInput(seat); + } } // Pen/Tablet support... @@ -2771,6 +2992,7 @@ typedef struct SDL_WaylandPenTool // a stylus, etc, on a tablet. Uint32 frame_axes_set; int frame_pen_down; int frame_buttons[3]; + struct wl_list link; } SDL_WaylandPenTool; static void tablet_tool_handle_type(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t type) @@ -2825,6 +3047,7 @@ static void tablet_tool_handle_removed(void *data, struct zwp_tablet_tool_v2 *to SDL_RemovePenDevice(0, sdltool->instance_id); } zwp_tablet_tool_v2_destroy(tool); + WAYLAND_wl_list_remove(&sdltool->link); SDL_free(sdltool); } @@ -3025,13 +3248,14 @@ static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = { }; -static void tablet_seat_handle_tablet_added(void *data, struct zwp_tablet_seat_v2 *seat, struct zwp_tablet_v2 *tablet) +static void tablet_seat_handle_tablet_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_v2 *tablet) { // don't care atm. } -static void tablet_seat_handle_tool_added(void *data, struct zwp_tablet_seat_v2 *seat, struct zwp_tablet_tool_v2 *tool) +static void tablet_seat_handle_tool_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_tool_v2 *tool) { + SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; SDL_WaylandPenTool *sdltool = SDL_calloc(1, sizeof(*sdltool)); if (sdltool) { // if allocation failed, oh well, we won't report this device. @@ -3042,6 +3266,7 @@ static void tablet_seat_handle_tool_added(void *data, struct zwp_tablet_seat_v2 for (int i = 0; i < SDL_arraysize(sdltool->frame_buttons); i++) { sdltool->frame_buttons[i] = -1; } + WAYLAND_wl_list_insert(&seat->tablet.tool_list, &sdltool->link); // this will send a bunch of zwp_tablet_tool_v2 events right up front to tell // us device details, with a "done" event to let us know we have everything. @@ -3049,7 +3274,7 @@ static void tablet_seat_handle_tool_added(void *data, struct zwp_tablet_seat_v2 } } -static void tablet_seat_handle_pad_added(void *data, struct zwp_tablet_seat_v2 *seat, struct zwp_tablet_pad_v2 *pad) +static void tablet_seat_handle_pad_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_pad_v2 *pad) { // we don't care atm. } @@ -3060,23 +3285,19 @@ static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = { tablet_seat_handle_pad_added }; -void Wayland_input_init_tablet_support(struct SDL_WaylandInput *input, struct zwp_tablet_manager_v2 *tablet_manager) +static void Wayland_SeatInitTabletSupport(SDL_WaylandSeat *seat) { - if (!tablet_manager || !input->seat) { - return; + WAYLAND_wl_list_init(&seat->tablet.tool_list); + seat->tablet.wl_tablet_seat = zwp_tablet_manager_v2_get_tablet_seat(seat->display->tablet_manager, seat->wl_seat); + zwp_tablet_seat_v2_add_listener(seat->tablet.wl_tablet_seat, &tablet_seat_listener, seat); +} + +void Wayland_DisplayInitTabletManager(SDL_VideoData *display) +{ + SDL_WaylandSeat *seat; + wl_list_for_each (seat, &display->seat_list, link) { + Wayland_SeatInitTabletSupport(seat); } - - SDL_WaylandTabletInput *tablet_input = SDL_calloc(1, sizeof(*tablet_input)); - if (!tablet_input) { - return; - } - - tablet_input->input = input; - tablet_input->seat = zwp_tablet_manager_v2_get_tablet_seat(tablet_manager, input->seat); - - zwp_tablet_seat_v2_add_listener(tablet_input->seat, &tablet_seat_listener, tablet_input); - - input->tablet_input = tablet_input; } static void Wayland_remove_all_pens_callback(SDL_PenID instance_id, void *handle, void *userdata) @@ -3086,338 +3307,302 @@ static void Wayland_remove_all_pens_callback(SDL_PenID instance_id, void *handle SDL_free(sdltool); } -void Wayland_input_quit_tablet_support(struct SDL_WaylandInput *input) +static void Wayland_SeatDestroyTablet(SDL_WaylandSeat *seat, bool send_events) { - SDL_RemoveAllPenDevices(Wayland_remove_all_pens_callback, NULL); - - if (input && input->tablet_input) { - zwp_tablet_seat_v2_destroy(input->tablet_input->seat); - SDL_free(input->tablet_input); - input->tablet_input = NULL; + if (send_events) { + SDL_WaylandPenTool *pen, *temp; + wl_list_for_each_safe (pen, temp, &seat->tablet.tool_list, link) { + // Remove all tools for this seat, sending PROXIMITY_OUT events. + tablet_tool_handle_removed(pen, pen->wltool); + } + } else { + // Shutting down, just delete everything. + SDL_RemoveAllPenDevices(Wayland_remove_all_pens_callback, NULL); } + + if (seat && seat->tablet.wl_tablet_seat) { + zwp_tablet_seat_v2_destroy(seat->tablet.wl_tablet_seat); + seat->tablet.wl_tablet_seat = NULL; + } + + SDL_zero(seat->tablet); + WAYLAND_wl_list_init(&seat->tablet.tool_list); } -void Wayland_input_initialize_seat(SDL_VideoData *d) +void Wayland_DisplayCreateSeat(SDL_VideoData *display, struct wl_seat *wl_seat, Uint32 id) { - struct SDL_WaylandInput *input = d->input; - - WAYLAND_wl_list_init(&touch_points); - - if (d->data_device_manager) { - Wayland_create_data_device(d); - } - if (d->primary_selection_device_manager) { - Wayland_create_primary_selection_device(d); - } - if (d->text_input_manager) { - Wayland_create_text_input(d); + SDL_WaylandSeat *seat = SDL_calloc(1, sizeof(SDL_WaylandSeat)); + if (!seat) { + return; } - wl_seat_add_listener(input->seat, &seat_listener, input); - wl_seat_set_user_data(input->seat, input); + // Keep the seats in the order in which they were added. + WAYLAND_wl_list_insert(display->seat_list.prev, &seat->link); - if (d->tablet_manager) { - Wayland_input_init_tablet_support(d->input, d->tablet_manager); + WAYLAND_wl_list_init(&seat->touch.points); + seat->wl_seat = wl_seat; + seat->display = display; + seat->registry_id = id; + seat->keyboard.xkb.current_group = XKB_GROUP_INVALID; + + Wayland_SeatCreateDataDevice(seat); + Wayland_SeatCreatePrimarySelectionDevice(seat); + Wayland_SeatCreateTextInput(seat); + + wl_seat_set_user_data(seat->wl_seat, seat); + wl_seat_add_listener(seat->wl_seat, &seat_listener, seat); + + if (display->tablet_manager) { + Wayland_SeatInitTabletSupport(seat); } - WAYLAND_wl_display_flush(d->display); + WAYLAND_wl_display_flush(display->display); } -void Wayland_display_destroy_input(SDL_VideoData *d) +void Wayland_SeatDestroy(SDL_WaylandSeat *seat, bool send_events) { - struct SDL_WaylandInput *input = d->input; - - if (input->keyboard_timestamps) { - zwp_input_timestamps_v1_destroy(input->keyboard_timestamps); - } - if (input->pointer_timestamps) { - zwp_input_timestamps_v1_destroy(input->pointer_timestamps); - } - if (input->touch_timestamps) { - zwp_input_timestamps_v1_destroy(input->touch_timestamps); + if (!seat) { + return; } - if (input->data_device) { - Wayland_data_device_clear_selection(input->data_device); - if (input->data_device->selection_offer) { - Wayland_data_offer_destroy(input->data_device->selection_offer); + SDL_free(seat->name); + + if (seat->data_device) { + Wayland_data_device_clear_selection(seat->data_device); + if (seat->data_device->selection_offer) { + Wayland_data_offer_destroy(seat->data_device->selection_offer); } - if (input->data_device->drag_offer) { - Wayland_data_offer_destroy(input->data_device->drag_offer); + if (seat->data_device->drag_offer) { + Wayland_data_offer_destroy(seat->data_device->drag_offer); } - if (input->data_device->data_device) { - if (wl_data_device_get_version(input->data_device->data_device) >= WL_DATA_DEVICE_RELEASE_SINCE_VERSION) { - wl_data_device_release(input->data_device->data_device); + if (seat->data_device->data_device) { + if (wl_data_device_get_version(seat->data_device->data_device) >= WL_DATA_DEVICE_RELEASE_SINCE_VERSION) { + wl_data_device_release(seat->data_device->data_device); } else { - wl_data_device_destroy(input->data_device->data_device); + wl_data_device_destroy(seat->data_device->data_device); } } - SDL_free(input->data_device); + SDL_free(seat->data_device); } - if (input->primary_selection_device) { - if (input->primary_selection_device->selection_offer) { - Wayland_primary_selection_offer_destroy(input->primary_selection_device->selection_offer); + if (seat->primary_selection_device) { + if (seat->primary_selection_device->selection_offer) { + Wayland_primary_selection_offer_destroy(seat->primary_selection_device->selection_offer); } - if (input->primary_selection_device->selection_source) { - Wayland_primary_selection_source_destroy(input->primary_selection_device->selection_source); + if (seat->primary_selection_device->selection_source) { + Wayland_primary_selection_source_destroy(seat->primary_selection_device->selection_source); } - if (input->primary_selection_device->primary_selection_device) { - zwp_primary_selection_device_v1_destroy(input->primary_selection_device->primary_selection_device); + if (seat->primary_selection_device->primary_selection_device) { + zwp_primary_selection_device_v1_destroy(seat->primary_selection_device->primary_selection_device); } - SDL_free(input->primary_selection_device); + SDL_free(seat->primary_selection_device); } - if (input->text_input) { - zwp_text_input_v3_destroy(input->text_input->text_input); - SDL_free(input->text_input); + if (seat->text_input.zwp_text_input) { + zwp_text_input_v3_destroy(seat->text_input.zwp_text_input); } - if (input->keyboard) { - if (wl_keyboard_get_version(input->keyboard) >= WL_KEYBOARD_RELEASE_SINCE_VERSION) { - wl_keyboard_release(input->keyboard); - } else { - wl_keyboard_destroy(input->keyboard); - } - } + Wayland_SeatDestroyKeyboard(seat, send_events); + Wayland_SeatDestroyPointer(seat, send_events); + Wayland_SeatDestroyTouch(seat); + Wayland_SeatDestroyTablet(seat, send_events); - if (input->relative_pointer) { - zwp_relative_pointer_v1_destroy(input->relative_pointer); - } - - if (input->cursor_shape) { - wp_cursor_shape_device_v1_destroy(input->cursor_shape); - } - - if (input->pointer) { - if (wl_pointer_get_version(input->pointer) >= WL_POINTER_RELEASE_SINCE_VERSION) { - wl_pointer_release(input->pointer); - } else { - wl_pointer_destroy(input->pointer); - } - } - - if (input->touch) { - struct SDL_WaylandTouchPoint *tp, *tmp; - - SDL_DelTouch(1); - if (wl_touch_get_version(input->touch) >= WL_TOUCH_RELEASE_SINCE_VERSION) { - wl_touch_release(input->touch); - } else { - wl_touch_destroy(input->touch); - } - - wl_list_for_each_safe (tp, tmp, &touch_points, link) { - WAYLAND_wl_list_remove(&tp->link); - SDL_free(tp); - } - } - - if (input->tablet_input) { - Wayland_input_quit_tablet_support(input); - } - - if (input->seat) { - if (wl_seat_get_version(input->seat) >= WL_SEAT_RELEASE_SINCE_VERSION) { - wl_seat_release(input->seat); - } else { - wl_seat_destroy(input->seat); - } - } - - if (input->xkb.compose_state) { - WAYLAND_xkb_compose_state_unref(input->xkb.compose_state); - } - - if (input->xkb.compose_table) { - WAYLAND_xkb_compose_table_unref(input->xkb.compose_table); - } - - if (input->xkb.state) { - WAYLAND_xkb_state_unref(input->xkb.state); - } - - if (input->xkb.keymap) { - WAYLAND_xkb_keymap_unref(input->xkb.keymap); - } - - SDL_free(input); - d->input = NULL; -} - -bool Wayland_input_enable_relative_pointer(struct SDL_WaylandInput *input) -{ - SDL_VideoDevice *vd = SDL_GetVideoDevice(); - SDL_VideoData *d = input->display; - SDL_Window *window; - - if (!d->relative_pointer_manager) { - return false; - } - - if (!d->pointer_constraints) { - return false; - } - - if (!input->pointer) { - return false; - } - - /* If we have a pointer confine active, we must destroy it here because - * creating a locked pointer otherwise would be a protocol error. - */ - for (window = vd->windows; window; window = window->next) { - pointer_confine_destroy(window); - } - - for (window = vd->windows; window; window = window->next) { - Wayland_input_lock_pointer(input, window); - } - - d->relative_mouse_mode = 1; - - return true; -} - -bool Wayland_input_disable_relative_pointer(struct SDL_WaylandInput *input) -{ - SDL_VideoDevice *vd = SDL_GetVideoDevice(); - SDL_VideoData *d = input->display; - SDL_Window *window; - - for (window = vd->windows; window; window = window->next) { - Wayland_input_unlock_pointer(input, window); - } - - d->relative_mouse_mode = 0; - - for (window = vd->windows; window; window = window->next) { - Wayland_input_confine_pointer(input, window); - } - - return true; -} - -bool Wayland_input_confine_pointer(struct SDL_WaylandInput *input, SDL_Window *window) -{ - SDL_WindowData *w = window->internal; - SDL_VideoData *d = input->display; - struct wl_region *confine_rect; - - if (!d->pointer_constraints) { - return SDL_SetError("Failed to confine pointer: compositor lacks support for the required zwp_pointer_constraints_v1 protocol"); - } - - if (!input->pointer) { - return SDL_SetError("No pointer to confine"); - } - - // The confinement region will be created when the window is mapped. - if (w->shell_surface_status != WAYLAND_SHELL_SURFACE_STATUS_SHOWN) { - return true; - } - - /* A confine may already be active, in which case we should destroy it and - * create a new one. - */ - pointer_confine_destroy(window); - - /* We cannot create a confine if the pointer is already locked. Defer until - * the pointer is unlocked. - */ - if (d->relative_mouse_mode) { - return true; - } - - // Don't confine the pointer if it shouldn't be confined. - if (SDL_RectEmpty(&window->mouse_rect) && !(window->flags & SDL_WINDOW_MOUSE_GRABBED)) { - return true; - } - - if (SDL_RectEmpty(&window->mouse_rect)) { - confine_rect = NULL; + if (wl_seat_get_version(seat->wl_seat) >= WL_SEAT_RELEASE_SINCE_VERSION) { + wl_seat_release(seat->wl_seat); } else { - SDL_Rect scaled_mouse_rect; - - scaled_mouse_rect.x = (int)SDL_floor(window->mouse_rect.x / w->pointer_scale.x); - scaled_mouse_rect.y = (int)SDL_floor(window->mouse_rect.y / w->pointer_scale.y); - scaled_mouse_rect.w = (int)SDL_ceil(window->mouse_rect.w / w->pointer_scale.x); - scaled_mouse_rect.h = (int)SDL_ceil(window->mouse_rect.h / w->pointer_scale.y); - - confine_rect = wl_compositor_create_region(d->compositor); - wl_region_add(confine_rect, - scaled_mouse_rect.x, - scaled_mouse_rect.y, - scaled_mouse_rect.w, - scaled_mouse_rect.h); + wl_seat_destroy(seat->wl_seat); } - struct zwp_confined_pointer_v1 *confined_pointer = - zwp_pointer_constraints_v1_confine_pointer(d->pointer_constraints, - w->surface, - input->pointer, - confine_rect, - ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); - zwp_confined_pointer_v1_add_listener(confined_pointer, - &confined_pointer_listener, - window); - - if (confine_rect) { - wl_region_destroy(confine_rect); - } - - // Commit the double-buffered confinement region. - wl_surface_commit(w->surface); - - w->confined_pointer = confined_pointer; - return true; + WAYLAND_wl_list_remove(&seat->link); + SDL_free(seat); } -bool Wayland_input_unconfine_pointer(struct SDL_WaylandInput *input, SDL_Window *window) +bool Wayland_SeatHasRelativePointerFocus(SDL_WaylandSeat *seat) { - pointer_confine_destroy(window); - return true; + /* If a seat has both keyboard and pointer capabilities, relative focus will follow the keyboard + * attached to that seat. Otherwise, relative focus will be gained if any other seat has keyboard + * focus on the window with pointer focus. + */ + if (seat->keyboard.wl_keyboard) { + return seat->keyboard.focus && seat->keyboard.focus == seat->pointer.focus; + } else { + return seat->pointer.focus && seat->pointer.focus->keyboard_focus_count != 0; + } } -bool Wayland_input_grab_keyboard(SDL_Window *window, struct SDL_WaylandInput *input) +static void Wayland_SeatUpdateKeyboardGrab(SDL_WaylandSeat *seat) { - SDL_WindowData *w = window->internal; - SDL_VideoData *d = input->display; + SDL_VideoData *display = seat->display; - if (!d->key_inhibitor_manager) { - return SDL_SetError("Failed to grab keyboard: compositor lacks support for the required zwp_keyboard_shortcuts_inhibit_manager_v1 protocol"); + if (display->key_inhibitor_manager) { + // Destroy the existing key inhibitor. + if (seat->keyboard.key_inhibitor) { + zwp_keyboard_shortcuts_inhibitor_v1_destroy(seat->keyboard.key_inhibitor); + seat->keyboard.key_inhibitor = NULL; + } + + if (seat->keyboard.wl_keyboard) { + SDL_WindowData *w = seat->keyboard.focus; + if (w) { + SDL_Window *window = w->sdlwindow; + + // Don't grab the keyboard if it shouldn't be grabbed. + if (window->flags & SDL_WINDOW_KEYBOARD_GRABBED) { + seat->keyboard.key_inhibitor = + zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(display->key_inhibitor_manager, w->surface, seat->wl_seat); + } + } + } } - - if (w->key_inhibitor) { - return true; - } - - w->key_inhibitor = - zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(d->key_inhibitor_manager, - w->surface, - input->seat); - - return true; } -bool Wayland_input_ungrab_keyboard(SDL_Window *window) +void Wayland_SeatUpdatePointerGrab(SDL_WaylandSeat *seat) { - SDL_WindowData *w = window->internal; + SDL_VideoData *display = seat->display; - if (w->key_inhibitor) { - zwp_keyboard_shortcuts_inhibitor_v1_destroy(w->key_inhibitor); - w->key_inhibitor = NULL; + if (display->pointer_constraints) { + const bool has_relative_focus = Wayland_SeatHasRelativePointerFocus(seat); + + if (seat->pointer.locked_pointer && (!display->relative_mode_enabled || !has_relative_focus)) { + zwp_locked_pointer_v1_destroy(seat->pointer.locked_pointer); + seat->pointer.locked_pointer = NULL; + + // Update the cursor after destroying a relative move lock. + Wayland_SeatUpdateCursor(seat); + } + + if (seat->pointer.wl_pointer) { + // If relative mode is active, and the pointer focus matches the keyboard focus, lock it. + if (seat->display->relative_mode_enabled && has_relative_focus) { + if (!seat->pointer.locked_pointer) { + // Creating a lock on a surface with an active confinement region on the same seat is a protocol error. + if (seat->pointer.confined_pointer) { + zwp_confined_pointer_v1_destroy(seat->pointer.confined_pointer); + seat->pointer.confined_pointer = NULL; + } + + seat->pointer.locked_pointer = zwp_pointer_constraints_v1_lock_pointer(display->pointer_constraints, + seat->pointer.focus->surface, + seat->pointer.wl_pointer, NULL, + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); + zwp_locked_pointer_v1_add_listener(seat->pointer.locked_pointer, &locked_pointer_listener, seat); + + // Ensure that the relative pointer is hidden, if required. + Wayland_SeatUpdateCursor(seat); + } + + // Locked the cursor for relative mode, nothing more to do. + return; + } + + /* A confine may already be active, in which case we should destroy it and create a new one + * in case it changed size. + */ + if (seat->pointer.confined_pointer) { + zwp_confined_pointer_v1_destroy(seat->pointer.confined_pointer); + seat->pointer.confined_pointer = NULL; + } + + SDL_WindowData *w = seat->pointer.focus; + if (!w) { + return; + } + + SDL_Window *window = w->sdlwindow; + + // Don't confine the pointer if the window doesn't have input focus, or it shouldn't be confined. + if (!(window->flags & SDL_WINDOW_INPUT_FOCUS) || + (!(window->flags & SDL_WINDOW_MOUSE_GRABBED) && SDL_RectEmpty(&window->mouse_rect))) { + return; + } + + struct wl_region *confine_rect = NULL; + if (!SDL_RectEmpty(&window->mouse_rect)) { + SDL_Rect scaled_mouse_rect; + Wayland_GetScaledMouseRect(window, &scaled_mouse_rect); + + /* Some compositors will only confine the pointer to an arbitrary region if the pointer + * is already within the confinement area when it is created. + */ + if (SDL_PointInRect(&seat->pointer.last_motion, &scaled_mouse_rect)) { + confine_rect = wl_compositor_create_region(display->compositor); + wl_region_add(confine_rect, + scaled_mouse_rect.x, + scaled_mouse_rect.y, + scaled_mouse_rect.w, + scaled_mouse_rect.h); + } else { + /* Warp the pointer to the closest point within the confinement zone if outside, + * The confinement region will be created when a true position event is received. + */ + int closest_x = seat->pointer.last_motion.x; + int closest_y = seat->pointer.last_motion.y; + + if (closest_x < scaled_mouse_rect.x) { + closest_x = scaled_mouse_rect.x; + } else if (closest_x >= scaled_mouse_rect.x + scaled_mouse_rect.w) { + closest_x = (scaled_mouse_rect.x + scaled_mouse_rect.w) - 1; + } + + if (closest_y < scaled_mouse_rect.y) { + closest_y = scaled_mouse_rect.y; + } else if (closest_y >= scaled_mouse_rect.y + scaled_mouse_rect.h) { + closest_y = (scaled_mouse_rect.y + scaled_mouse_rect.h) - 1; + } + + Wayland_SeatWarpMouse(seat, w, closest_x, closest_y); + } + } + + if (confine_rect || (window->flags & SDL_WINDOW_MOUSE_GRABBED)) { + seat->pointer.confined_pointer = + zwp_pointer_constraints_v1_confine_pointer(display->pointer_constraints, + w->surface, + seat->pointer.wl_pointer, + confine_rect, + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); + zwp_confined_pointer_v1_add_listener(seat->pointer.confined_pointer, + &confined_pointer_listener, + window); + + if (confine_rect) { + wl_region_destroy(confine_rect); + } + + // Commit the new confinement region immediately. + wl_surface_commit(w->surface); + } + } } - - return true; } -void Wayland_UpdateImplicitGrabSerial(struct SDL_WaylandInput *input, Uint32 serial) +void Wayland_DisplayUpdatePointerGrabs(SDL_VideoData *display, SDL_WindowData *window) { - if (serial > input->last_implicit_grab_serial) { - input->last_implicit_grab_serial = serial; - Wayland_data_device_set_serial(input->data_device, serial); - Wayland_primary_selection_device_set_serial(input->primary_selection_device, serial); + SDL_WaylandSeat *seat; + wl_list_for_each (seat, &display->seat_list, link) { + if (!window || seat->pointer.focus == window) { + Wayland_SeatUpdatePointerGrab(seat); + } + } +} + +void Wayland_DisplayUpdateKeyboardGrabs(SDL_VideoData *display, SDL_WindowData *window) +{ + SDL_WaylandSeat *seat; + wl_list_for_each (seat, &display->seat_list, link) { + if (!window || seat->keyboard.focus == window) { + Wayland_SeatUpdateKeyboardGrab(seat); + } + } +} + +void Wayland_UpdateImplicitGrabSerial(SDL_WaylandSeat *seat, Uint32 serial) +{ + if (serial > seat->last_implicit_grab_serial) { + seat->last_implicit_grab_serial = serial; + seat->display->last_implicit_grab_seat = seat; + Wayland_data_device_set_serial(seat->data_device, serial); + Wayland_primary_selection_device_set_serial(seat->primary_selection_device, serial); } } diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h index 6158882ac5..3c3aed69d5 100644 --- a/src/video/wayland/SDL_waylandevents_c.h +++ b/src/video/wayland/SDL_waylandevents_c.h @@ -24,6 +24,7 @@ #ifndef SDL_waylandevents_h_ #define SDL_waylandevents_h_ +#include "../../events/SDL_keymap_c.h" #include "../../events/SDL_mouse_c.h" #include "../../events/SDL_pen_c.h" @@ -39,14 +40,6 @@ enum SDL_WaylandAxisEvent AXIS_EVENT_VALUE120 }; -struct SDL_WaylandTabletSeat; - -typedef struct SDL_WaylandTabletInput -{ - struct SDL_WaylandInput *input; - struct zwp_tablet_seat_v2 *seat; -} SDL_WaylandTabletInput; - typedef struct { int32_t repeat_rate; // Repeat rate in range of [1, 1000] character(s) per second @@ -63,125 +56,155 @@ typedef struct char text[8]; } SDL_WaylandKeyboardRepeat; -struct SDL_WaylandInput +typedef struct SDL_WaylandSeat { SDL_VideoData *display; - struct wl_seat *seat; - struct wl_pointer *pointer; - struct wl_touch *touch; - struct wl_keyboard *keyboard; + struct wl_seat *wl_seat; SDL_WaylandDataDevice *data_device; SDL_WaylandPrimarySelectionDevice *primary_selection_device; - SDL_WaylandTextInput *text_input; - struct wp_cursor_shape_device_v1 *cursor_shape; - struct zwp_relative_pointer_v1 *relative_pointer; - struct zwp_input_timestamps_v1 *keyboard_timestamps; - struct zwp_input_timestamps_v1 *pointer_timestamps; - struct zwp_input_timestamps_v1 *touch_timestamps; - SDL_WindowData *pointer_focus; - SDL_WindowData *keyboard_focus; - SDL_CursorData *current_cursor; - SDL_KeyboardID keyboard_id; - SDL_MouseID pointer_id; - uint32_t pointer_enter_serial; + char *name; + struct wl_list link; - // High-resolution event timestamps - Uint64 keyboard_timestamp_ns; - Uint64 pointer_timestamp_ns; - Uint64 touch_timestamp_ns; - - // Last motion location - wl_fixed_t sx_w; - wl_fixed_t sy_w; - - SDL_MouseButtonFlags buttons_pressed; - - // The serial of the last implicit grab event for window activation and selection data. - Uint32 last_implicit_grab_serial; + Uint32 last_implicit_grab_serial; // The serial of the last implicit grab event for window activation and selection data. + Uint32 registry_id; // The ID of the Wayland seat object, struct { - struct xkb_keymap *keymap; - struct xkb_state *state; - struct xkb_compose_table *compose_table; - struct xkb_compose_state *compose_state; + struct wl_keyboard *wl_keyboard; + struct zwp_input_timestamps_v1 *timestamps; + struct zwp_keyboard_shortcuts_inhibitor_v1 *key_inhibitor; + SDL_WindowData *focus; + SDL_Keymap *sdl_keymap; - // Keyboard layout "group" - uint32_t current_group; + SDL_WaylandKeyboardRepeat repeat; + Uint64 highres_timestamp_ns; - // Modifier bitshift values - uint32_t idx_shift; - uint32_t idx_ctrl; - uint32_t idx_alt; - uint32_t idx_gui; - uint32_t idx_mod3; - uint32_t idx_mod5; - uint32_t idx_num; - uint32_t idx_caps; + // Current SDL modifier flags + SDL_Keymod pressed_modifiers; + SDL_Keymod locked_modifiers; - // Current system modifier flags - uint32_t wl_pressed_modifiers; - uint32_t wl_locked_modifiers; - } xkb; + SDL_KeyboardID sdl_id; + bool is_virtual; + + struct + { + struct xkb_keymap *keymap; + struct xkb_state *state; + struct xkb_compose_table *compose_table; + struct xkb_compose_state *compose_state; + + // Keyboard layout "group" + Uint32 current_group; + + // Modifier bitshift values + Uint32 idx_shift; + Uint32 idx_ctrl; + Uint32 idx_alt; + Uint32 idx_gui; + Uint32 idx_mod3; + Uint32 idx_mod5; + Uint32 idx_num; + Uint32 idx_caps; + + // Current system modifier flags + Uint32 wl_pressed_modifiers; + Uint32 wl_locked_modifiers; + } xkb; + } keyboard; - // information about axis events on current frame struct { - enum SDL_WaylandAxisEvent x_axis_type; - float x; + struct wl_pointer *wl_pointer; + struct zwp_relative_pointer_v1 *relative_pointer; + struct zwp_input_timestamps_v1 *timestamps; + struct wp_cursor_shape_device_v1 *cursor_shape; + struct zwp_locked_pointer_v1 *locked_pointer; + struct zwp_confined_pointer_v1 *confined_pointer; - enum SDL_WaylandAxisEvent y_axis_type; - float y; + SDL_WindowData *focus; + SDL_CursorData *current_cursor; - // Event timestamp in nanoseconds - Uint64 timestamp_ns; - SDL_MouseWheelDirection direction; - } pointer_curr_axis_info; + Uint64 highres_timestamp_ns; + Uint32 enter_serial; + SDL_MouseButtonFlags buttons_pressed; + SDL_Point last_motion; - SDL_WaylandKeyboardRepeat keyboard_repeat; + SDL_MouseID sdl_id; - SDL_WaylandTabletInput *tablet_input; + // Information about axis events on the current frame + struct + { + enum SDL_WaylandAxisEvent x_axis_type; + float x; - bool keyboard_is_virtual; + enum SDL_WaylandAxisEvent y_axis_type; + float y; - // Current SDL modifier flags - SDL_Keymod pressed_modifiers; - SDL_Keymod locked_modifiers; -}; + // Event timestamp in nanoseconds + Uint64 timestamp_ns; + SDL_MouseWheelDirection direction; + } current_axis_info; + + // Cursor state + struct + { + struct wl_surface *surface; + struct wp_viewport *viewport; + + // Animation state for legacy animated cursors + struct wl_callback *frame_callback; + Uint64 last_frame_callback_time_ns; + Uint64 current_frame_time_ns; + int current_frame; + } cursor_state; + } pointer; + + struct + { + struct wl_touch *wl_touch; + struct zwp_input_timestamps_v1 *timestamps; + Uint64 highres_timestamp_ns; + struct wl_list points; + } touch; + + struct + { + struct zwp_text_input_v3 *zwp_text_input; + SDL_Rect cursor_rect; + bool enabled; + bool has_preedit; + } text_input; + + struct + { + struct zwp_tablet_seat_v2 *wl_tablet_seat; + struct wl_list tool_list; + } tablet; +} SDL_WaylandSeat; -extern Uint64 Wayland_GetTouchTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms); +extern Uint64 Wayland_GetTouchTimestamp(struct SDL_WaylandSeat *seat, Uint32 wl_timestamp_ms); extern void Wayland_PumpEvents(SDL_VideoDevice *_this); extern void Wayland_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window); extern int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS); -extern void Wayland_create_data_device(SDL_VideoData *d); -extern void Wayland_create_primary_selection_device(SDL_VideoData *d); +extern void Wayland_DisplayInitInputTimestampManager(SDL_VideoData *display); +extern void Wayland_DisplayInitCursorShapeManager(SDL_VideoData *display); +extern void Wayland_DisplayInitRelativePointerManager(SDL_VideoData *display); +extern void Wayland_DisplayInitTabletManager(SDL_VideoData *display); +extern void Wayland_DisplayInitDataDeviceManager(SDL_VideoData *display); +extern void Wayland_DisplayInitPrimarySelectionDeviceManager(SDL_VideoData *display); -extern void Wayland_create_text_input_manager(SDL_VideoData *d, uint32_t id); +extern void Wayland_DisplayCreateTextInputManager(SDL_VideoData *d, uint32_t id); -extern void Wayland_input_initialize_seat(SDL_VideoData *d); -extern void Wayland_display_destroy_input(SDL_VideoData *d); +extern void Wayland_DisplayCreateSeat(SDL_VideoData *display, struct wl_seat *wl_seat, Uint32 id); +extern void Wayland_SeatDestroy(SDL_WaylandSeat *seat, bool send_events); -extern void Wayland_input_init_relative_pointer(SDL_VideoData *d); -extern bool Wayland_input_enable_relative_pointer(struct SDL_WaylandInput *input); -extern bool Wayland_input_disable_relative_pointer(struct SDL_WaylandInput *input); - -extern bool Wayland_input_lock_pointer(struct SDL_WaylandInput *input, SDL_Window *window); -extern bool Wayland_input_unlock_pointer(struct SDL_WaylandInput *input, SDL_Window *window); - -extern bool Wayland_input_confine_pointer(struct SDL_WaylandInput *input, SDL_Window *window); -extern bool Wayland_input_unconfine_pointer(struct SDL_WaylandInput *input, SDL_Window *window); - -extern bool Wayland_input_grab_keyboard(SDL_Window *window, struct SDL_WaylandInput *input); -extern bool Wayland_input_ungrab_keyboard(SDL_Window *window); - -extern void Wayland_input_init_tablet_support(struct SDL_WaylandInput *input, struct zwp_tablet_manager_v2 *tablet_manager); -extern void Wayland_input_quit_tablet_support(struct SDL_WaylandInput *input); - -extern void Wayland_RegisterTimestampListeners(struct SDL_WaylandInput *input); -extern void Wayland_CreateCursorShapeDevice(struct SDL_WaylandInput *input); +extern bool Wayland_SeatHasRelativePointerFocus(SDL_WaylandSeat *seat); +extern void Wayland_SeatUpdatePointerGrab(SDL_WaylandSeat *seat); +extern void Wayland_DisplayUpdatePointerGrabs(SDL_VideoData *display, SDL_WindowData *window); +extern void Wayland_DisplayUpdateKeyboardGrabs(SDL_VideoData *display, SDL_WindowData *window); /* The implicit grab serial needs to be updated on: * - Keyboard key down/up @@ -190,6 +213,6 @@ extern void Wayland_CreateCursorShapeDevice(struct SDL_WaylandInput *input); * - Tablet tool down * - Tablet tool button down/up */ -extern void Wayland_UpdateImplicitGrabSerial(struct SDL_WaylandInput *input, Uint32 serial); +extern void Wayland_UpdateImplicitGrabSerial(struct SDL_WaylandSeat *seat, Uint32 serial); #endif // SDL_waylandevents_h_ diff --git a/src/video/wayland/SDL_waylandkeyboard.c b/src/video/wayland/SDL_waylandkeyboard.c index df1628cd5b..967d76cef1 100644 --- a/src/video/wayland/SDL_waylandkeyboard.c +++ b/src/video/wayland/SDL_waylandkeyboard.c @@ -51,109 +51,140 @@ void Wayland_QuitKeyboard(SDL_VideoDevice *_this) #endif } -bool Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) +void Wayland_UpdateTextInput(SDL_VideoData *display) { - SDL_VideoData *internal = _this->internal; - struct SDL_WaylandInput *input = internal->input; + SDL_WaylandSeat *seat = NULL; - if (internal->text_input_manager) { - if (input && input->text_input) { - const SDL_Rect *rect = &input->text_input->cursor_rect; - enum zwp_text_input_v3_content_hint hint = ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE; - enum zwp_text_input_v3_content_purpose purpose; + if (display->text_input_manager) { + wl_list_for_each(seat, &display->seat_list, link) { + SDL_WindowData *focus = seat->keyboard.focus; - switch (SDL_GetTextInputType(props)) { - default: - case SDL_TEXTINPUT_TYPE_TEXT: - purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL; - break; - case SDL_TEXTINPUT_TYPE_TEXT_NAME: - purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NAME; - break; - case SDL_TEXTINPUT_TYPE_TEXT_EMAIL: - purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_EMAIL; - break; - case SDL_TEXTINPUT_TYPE_TEXT_USERNAME: - purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL; - hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA; - break; - case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN: - purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PASSWORD; - hint |= (ZWP_TEXT_INPUT_V3_CONTENT_HINT_HIDDEN_TEXT | ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA); - break; - case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE: - purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PASSWORD; - hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA; - break; - case SDL_TEXTINPUT_TYPE_NUMBER: - purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER; - break; - case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN: - purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PIN; - hint |= (ZWP_TEXT_INPUT_V3_CONTENT_HINT_HIDDEN_TEXT | ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA); - break; - case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE: - purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PIN; - hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA; - break; + if (seat->text_input.zwp_text_input) { + if (focus && focus->text_input_props.active) { + // Enabling will reset all state, so don't do it redundantly. + if (!seat->text_input.enabled) { + seat->text_input.enabled = true; + zwp_text_input_v3_enable(seat->text_input.zwp_text_input); + + // Now that it's enabled, set the input properties + zwp_text_input_v3_set_content_type(seat->text_input.zwp_text_input, focus->text_input_props.hint, focus->text_input_props.purpose); + if (!SDL_RectEmpty(&focus->sdlwindow->text_input_rect)) { + SDL_copyp(&seat->text_input.cursor_rect, &focus->sdlwindow->text_input_rect); + + // This gets reset on enable so we have to cache it + zwp_text_input_v3_set_cursor_rectangle(seat->text_input.zwp_text_input, + focus->sdlwindow->text_input_rect.x, + focus->sdlwindow->text_input_rect.y, + focus->sdlwindow->text_input_rect.w, + focus->sdlwindow->text_input_rect.h); + } + zwp_text_input_v3_commit(seat->text_input.zwp_text_input); + + if (seat->keyboard.xkb.compose_state) { + // Reset compose state so composite and dead keys don't carry over + WAYLAND_xkb_compose_state_reset(seat->keyboard.xkb.compose_state); + } + } + } else { + if (seat->text_input.enabled) { + seat->text_input.enabled = false; + SDL_zero(seat->text_input.cursor_rect); + zwp_text_input_v3_disable(seat->text_input.zwp_text_input); + zwp_text_input_v3_commit(seat->text_input.zwp_text_input); + } + + if (seat->keyboard.xkb.compose_state) { + // Reset compose state so composite and dead keys don't carry over + WAYLAND_xkb_compose_state_reset(seat->keyboard.xkb.compose_state); + } + } } - - switch (SDL_GetTextInputCapitalization(props)) { - default: - case SDL_CAPITALIZE_NONE: - break; - case SDL_CAPITALIZE_LETTERS: - hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_UPPERCASE; - break; - case SDL_CAPITALIZE_WORDS: - hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_TITLECASE; - break; - case SDL_CAPITALIZE_SENTENCES: - hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_AUTO_CAPITALIZATION; - break; - } - - if (SDL_GetTextInputAutocorrect(props)) { - hint |= (ZWP_TEXT_INPUT_V3_CONTENT_HINT_COMPLETION | ZWP_TEXT_INPUT_V3_CONTENT_HINT_SPELLCHECK); - } - if (SDL_GetTextInputMultiline(props)) { - hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_MULTILINE; - } - - zwp_text_input_v3_enable(input->text_input->text_input); - - // Now that it's enabled, set the input properties - zwp_text_input_v3_set_content_type(input->text_input->text_input, hint, purpose); - if (!SDL_RectEmpty(rect)) { - // This gets reset on enable so we have to cache it - zwp_text_input_v3_set_cursor_rectangle(input->text_input->text_input, - rect->x, - rect->y, - rect->w, - rect->h); - } - zwp_text_input_v3_commit(input->text_input->text_input); } } +} - if (input && input->xkb.compose_state) { - // Reset compose state so composite and dead keys don't carry over - WAYLAND_xkb_compose_state_reset(input->xkb.compose_state); +bool Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) +{ + SDL_VideoData *display = _this->internal; + + if (display->text_input_manager) { + SDL_WindowData *wind = window->internal; + wind->text_input_props.hint = ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE; + + switch (SDL_GetTextInputType(props)) { + default: + case SDL_TEXTINPUT_TYPE_TEXT: + wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL; + break; + case SDL_TEXTINPUT_TYPE_TEXT_NAME: + wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NAME; + break; + case SDL_TEXTINPUT_TYPE_TEXT_EMAIL: + wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_EMAIL; + break; + case SDL_TEXTINPUT_TYPE_TEXT_USERNAME: + wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL; + wind->text_input_props.hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA; + break; + case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN: + wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PASSWORD; + wind->text_input_props.hint |= (ZWP_TEXT_INPUT_V3_CONTENT_HINT_HIDDEN_TEXT | ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA); + break; + case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE: + wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PASSWORD; + wind->text_input_props.hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA; + break; + case SDL_TEXTINPUT_TYPE_NUMBER: + wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER; + break; + case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN: + wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PIN; + wind->text_input_props.hint |= (ZWP_TEXT_INPUT_V3_CONTENT_HINT_HIDDEN_TEXT | ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA); + break; + case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE: + wind->text_input_props.purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PIN; + wind->text_input_props.hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA; + break; + } + + switch (SDL_GetTextInputCapitalization(props)) { + default: + case SDL_CAPITALIZE_NONE: + break; + case SDL_CAPITALIZE_LETTERS: + wind->text_input_props.hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_UPPERCASE; + break; + case SDL_CAPITALIZE_WORDS: + wind->text_input_props.hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_TITLECASE; + break; + case SDL_CAPITALIZE_SENTENCES: + wind->text_input_props.hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_AUTO_CAPITALIZATION; + break; + } + + if (SDL_GetTextInputAutocorrect(props)) { + wind->text_input_props.hint |= (ZWP_TEXT_INPUT_V3_CONTENT_HINT_COMPLETION | ZWP_TEXT_INPUT_V3_CONTENT_HINT_SPELLCHECK); + } + if (SDL_GetTextInputMultiline(props)) { + wind->text_input_props.hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_MULTILINE; + } + + wind->text_input_props.active = true; + Wayland_UpdateTextInput(display); + + return true; } - return Wayland_UpdateTextInputArea(_this, window); + return false; } bool Wayland_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window) { - SDL_VideoData *internal = _this->internal; - struct SDL_WaylandInput *input = internal->input; + SDL_VideoData *display = _this->internal; - if (internal->text_input_manager) { - if (input && input->text_input) { - zwp_text_input_v3_disable(input->text_input->text_input); - zwp_text_input_v3_commit(input->text_input->text_input); - } + if (display->text_input_manager) { + window->internal->text_input_props.active = false; + Wayland_UpdateTextInput(display); } #ifdef SDL_USE_IME else { @@ -161,10 +192,6 @@ bool Wayland_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window) } #endif - if (input && input->xkb.compose_state) { - // Reset compose state so composite and dead keys don't carry over - WAYLAND_xkb_compose_state_reset(input->xkb.compose_state); - } return true; } @@ -172,20 +199,22 @@ bool Wayland_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window) { SDL_VideoData *internal = _this->internal; if (internal->text_input_manager) { - struct SDL_WaylandInput *input = internal->input; - if (input && input->text_input) { - if (!SDL_RectsEqual(&window->text_input_rect, &input->text_input->cursor_rect)) { - SDL_copyp(&input->text_input->cursor_rect, &window->text_input_rect); - zwp_text_input_v3_set_cursor_rectangle(input->text_input->text_input, - window->text_input_rect.x, - window->text_input_rect.y, - window->text_input_rect.w, - window->text_input_rect.h); - zwp_text_input_v3_commit(input->text_input->text_input); + SDL_WaylandSeat *seat; + + wl_list_for_each (seat, &internal->seat_list, link) { + if (seat->text_input.zwp_text_input && seat->keyboard.focus == window->internal) { + if (!SDL_RectsEqual(&window->text_input_rect, &seat->text_input.cursor_rect)) { + SDL_copyp(&seat->text_input.cursor_rect, &window->text_input_rect); + zwp_text_input_v3_set_cursor_rectangle(seat->text_input.zwp_text_input, + window->text_input_rect.x, + window->text_input_rect.y, + window->text_input_rect.w, + window->text_input_rect.h); + zwp_text_input_v3_commit(seat->text_input.zwp_text_input); + } } } } - #ifdef SDL_USE_IME else { SDL_IME_UpdateTextInputArea(window); @@ -196,13 +225,23 @@ bool Wayland_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window) bool Wayland_HasScreenKeyboardSupport(SDL_VideoDevice *_this) { - /* In reality we just want to return true when the screen keyboard is the + /* In reality, we just want to return true when the screen keyboard is the * _only_ way to get text input. So, in addition to checking for the text * input protocol, make sure we don't have any physical keyboards either. */ SDL_VideoData *internal = _this->internal; - bool haskeyboard = (internal->input != NULL) && (internal->input->keyboard != NULL); + SDL_WaylandSeat *seat; bool hastextmanager = (internal->text_input_manager != NULL); + bool haskeyboard = false; + + // Check for at least one keyboard object on one seat. + wl_list_for_each (seat, &internal->seat_list, link) { + if (seat->keyboard.wl_keyboard) { + haskeyboard = true; + break; + } + } + return !haskeyboard && hastextmanager; } diff --git a/src/video/wayland/SDL_waylandkeyboard.h b/src/video/wayland/SDL_waylandkeyboard.h index f570edb32f..b1897b8ba6 100644 --- a/src/video/wayland/SDL_waylandkeyboard.h +++ b/src/video/wayland/SDL_waylandkeyboard.h @@ -23,18 +23,12 @@ #ifndef SDL_waylandkeyboard_h_ #define SDL_waylandkeyboard_h_ -typedef struct SDL_WaylandTextInput -{ - struct zwp_text_input_v3 *text_input; - SDL_Rect cursor_rect; - bool has_preedit; -} SDL_WaylandTextInput; - extern bool Wayland_InitKeyboard(SDL_VideoDevice *_this); extern void Wayland_QuitKeyboard(SDL_VideoDevice *_this); extern bool Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); extern bool Wayland_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window); extern bool Wayland_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window); +extern void Wayland_UpdateTextInput(SDL_VideoData *display); extern bool Wayland_HasScreenKeyboardSupport(SDL_VideoDevice *_this); #endif // SDL_waylandkeyboard_h_ diff --git a/src/video/wayland/SDL_waylandmouse.c b/src/video/wayland/SDL_waylandmouse.c index d464cf3ff9..956087fdd8 100644 --- a/src/video/wayland/SDL_waylandmouse.c +++ b/src/video/wayland/SDL_waylandmouse.c @@ -63,18 +63,14 @@ typedef struct typedef struct { struct wl_buffer *wl_buffer; - Uint32 duration; + Uint64 duration_ns; } Wayland_SystemCursorFrame; typedef struct { Wayland_SystemCursorFrame *frames; - struct wl_callback *frame_callback; - Uint64 last_frame_callback_time_ms; - Uint64 current_frame_time_ms; - Uint32 total_duration; + Uint64 total_duration_ns; int num_frames; - int current_frame; SDL_SystemCursor id; } Wayland_SystemCursor; @@ -86,9 +82,6 @@ struct SDL_CursorData Wayland_SystemCursor system; } cursor_data; - struct wl_surface *surface; - struct wp_viewport *viewport; - bool is_system_cursor; }; @@ -298,40 +291,41 @@ struct wl_callback_listener cursor_frame_listener = { static void cursor_frame_done(void *data, struct wl_callback *cb, uint32_t time) { - SDL_CursorData *c = (SDL_CursorData *)data; + SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; + SDL_CursorData *c = (struct SDL_CursorData *)seat->pointer.current_cursor; - const Uint64 now = SDL_GetTicks(); - const Uint64 elapsed = (now - c->cursor_data.system.last_frame_callback_time_ms) % c->cursor_data.system.total_duration; + const Uint64 now = SDL_GetTicksNS(); + const Uint64 elapsed = (now - seat->pointer.cursor_state.last_frame_callback_time_ns) % c->cursor_data.system.total_duration_ns; Uint64 advance = 0; - int next = c->cursor_data.system.current_frame; + int next = seat->pointer.cursor_state.current_frame; wl_callback_destroy(cb); - c->cursor_data.system.frame_callback = wl_surface_frame(c->surface); - wl_callback_add_listener(c->cursor_data.system.frame_callback, &cursor_frame_listener, data); + seat->pointer.cursor_state.frame_callback = wl_surface_frame(seat->pointer.cursor_state.surface); + wl_callback_add_listener(seat->pointer.cursor_state.frame_callback, &cursor_frame_listener, data); - c->cursor_data.system.current_frame_time_ms += elapsed; + seat->pointer.cursor_state.current_frame_time_ns += elapsed; // Calculate the next frame based on the elapsed duration. - for (Uint64 t = c->cursor_data.system.frames[next].duration; t <= c->cursor_data.system.current_frame_time_ms; t += c->cursor_data.system.frames[next].duration) { + for (Uint64 t = c->cursor_data.system.frames[next].duration_ns; t <= seat->pointer.cursor_state.current_frame_time_ns; t += c->cursor_data.system.frames[next].duration_ns) { next = (next + 1) % c->cursor_data.system.num_frames; advance = t; // Make sure we don't end up in an infinite loop if a cursor has frame durations of 0. - if (!c->cursor_data.system.frames[next].duration) { + if (!c->cursor_data.system.frames[next].duration_ns) { break; } } - c->cursor_data.system.current_frame_time_ms -= advance; - c->cursor_data.system.last_frame_callback_time_ms = now; - c->cursor_data.system.current_frame = next; - wl_surface_attach(c->surface, c->cursor_data.system.frames[next].wl_buffer, 0, 0); - if (wl_surface_get_version(c->surface) >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) { - wl_surface_damage_buffer(c->surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32); + seat->pointer.cursor_state.current_frame_time_ns -= advance; + seat->pointer.cursor_state.last_frame_callback_time_ns = now; + seat->pointer.cursor_state.current_frame = next; + wl_surface_attach(seat->pointer.cursor_state.surface, c->cursor_data.system.frames[next].wl_buffer, 0, 0); + if (wl_surface_get_version(seat->pointer.cursor_state.surface) >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) { + wl_surface_damage_buffer(seat->pointer.cursor_state.surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32); } else { - wl_surface_damage(c->surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32); + wl_surface_damage(seat->pointer.cursor_state.surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32); } - wl_surface_commit(c->surface); + wl_surface_commit(seat->pointer.cursor_state.surface); } static bool Wayland_GetSystemCursor(SDL_VideoData *vdata, SDL_CursorData *cdata, int *scale, int *dst_size, int *hot_x, int *hot_y) @@ -415,11 +409,11 @@ static bool Wayland_GetSystemCursor(SDL_VideoData *vdata, SDL_CursorData *cdata, // ... Set the cursor data, finally. cdata->cursor_data.system.num_frames = cursor->image_count; - cdata->cursor_data.system.total_duration = 0; + cdata->cursor_data.system.total_duration_ns = 0; for (int i = 0; i < cursor->image_count; ++i) { cdata->cursor_data.system.frames[i].wl_buffer = WAYLAND_wl_cursor_image_get_buffer(cursor->images[i]); - cdata->cursor_data.system.frames[i].duration = cursor->images[i]->delay; - cdata->cursor_data.system.total_duration += cursor->images[i]->delay; + cdata->cursor_data.system.frames[i].duration_ns = SDL_MS_TO_NS((Uint64)cursor->images[i]->delay); + cdata->cursor_data.system.total_duration_ns += cdata->cursor_data.system.frames[i].duration_ns; } *scale = SDL_ceil(scale_factor) == scale_factor ? (int)scale_factor : 0; @@ -533,10 +527,8 @@ static bool Wayland_GetCustomCursor(SDL_Cursor *cursor, struct wl_buffer **buffe static SDL_Cursor *Wayland_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y) { - SDL_VideoDevice *vd = SDL_GetVideoDevice(); - SDL_VideoData *wd = vd->internal; - SDL_Cursor *cursor = SDL_calloc(1, sizeof(*cursor)); + if (cursor) { SDL_CursorData *data = SDL_calloc(1, sizeof(*data)); if (!data) { @@ -547,7 +539,6 @@ static SDL_Cursor *Wayland_CreateCursor(SDL_Surface *surface, int hot_x, int hot WAYLAND_wl_list_init(&data->cursor_data.custom.scaled_cursor_cache); data->cursor_data.custom.hot_x = hot_x; data->cursor_data.custom.hot_y = hot_y; - data->surface = wl_compositor_create_surface(wd->compositor); data->cursor_data.custom.sdl_cursor_surface = surface; ++surface->refcount; @@ -563,8 +554,8 @@ static SDL_Cursor *Wayland_CreateCursor(SDL_Surface *surface, int hot_x, int hot static SDL_Cursor *Wayland_CreateSystemCursor(SDL_SystemCursor id) { - SDL_VideoData *data = SDL_GetVideoDevice()->internal; SDL_Cursor *cursor = SDL_calloc(1, sizeof(*cursor)); + if (cursor) { SDL_CursorData *cdata = SDL_calloc(1, sizeof(*cdata)); if (!cdata) { @@ -573,16 +564,6 @@ static SDL_Cursor *Wayland_CreateSystemCursor(SDL_SystemCursor id) } cursor->internal = cdata; - /* The surface is only necessary if the cursor shape manager is not present. - * - * Note that we can't actually set any other cursor properties, as this - * is window-specific. See Wayland_GetSystemCursor for the rest! - */ - if (!data->cursor_shape_manager) { - cdata->surface = wl_compositor_create_surface(data->compositor); - wl_surface_set_user_data(cdata->surface, NULL); - } - cdata->cursor_data.system.id = id; cdata->is_system_cursor = true; } @@ -598,18 +579,28 @@ static SDL_Cursor *Wayland_CreateDefaultCursor(void) static void Wayland_FreeCursorData(SDL_CursorData *d) { - SDL_VideoDevice *vd = SDL_GetVideoDevice(); - struct SDL_WaylandInput *input = vd->internal->input; + SDL_VideoDevice *video_device = SDL_GetVideoDevice(); + SDL_VideoData *video_data = video_device->internal; + SDL_WaylandSeat *seat; - if (input->current_cursor == d) { - input->current_cursor = NULL; + // Stop any frame callbacks and detach buffers associated with the cursor being destroyed. + wl_list_for_each (seat, &video_data->seat_list, link) + { + if (seat->pointer.current_cursor == d) { + if (seat->pointer.cursor_state.frame_callback) { + wl_callback_destroy(seat->pointer.cursor_state.frame_callback); + seat->pointer.cursor_state.frame_callback = NULL; + } + if (seat->pointer.cursor_state.surface) { + wl_surface_attach(seat->pointer.cursor_state.surface, NULL, 0, 0); + } + + seat->pointer.current_cursor = NULL; + } } // Buffers for system cursors must not be destroyed. if (d->is_system_cursor) { - if (d->cursor_data.system.frame_callback) { - wl_callback_destroy(d->cursor_data.system.frame_callback); - } SDL_free(d->cursor_data.system.frames); } else { Wayland_ScaledCustomCursor *c, *temp; @@ -620,16 +611,6 @@ static void Wayland_FreeCursorData(SDL_CursorData *d) SDL_DestroySurface(d->cursor_data.custom.sdl_cursor_surface); } - - if (d->viewport) { - wp_viewport_destroy(d->viewport); - d->viewport = NULL; - } - - if (d->surface) { - wl_surface_destroy(d->surface); - d->surface = NULL; - } } static void Wayland_FreeCursor(SDL_Cursor *cursor) @@ -649,7 +630,7 @@ static void Wayland_FreeCursor(SDL_Cursor *cursor) SDL_free(cursor); } -static void Wayland_SetSystemCursorShape(struct SDL_WaylandInput *input, SDL_SystemCursor id) +static void Wayland_SetSystemCursorShape(SDL_WaylandSeat *seat, SDL_SystemCursor id) { Uint32 shape; @@ -719,110 +700,140 @@ static void Wayland_SetSystemCursorShape(struct SDL_WaylandInput *input, SDL_Sys shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT; } - wp_cursor_shape_device_v1_set_shape(input->cursor_shape, input->pointer_enter_serial, shape); + wp_cursor_shape_device_v1_set_shape(seat->pointer.cursor_shape, seat->pointer.enter_serial, shape); +} + +static void Wayland_SeatSetCursor(SDL_WaylandSeat *seat, SDL_Cursor *cursor) +{ + if (seat->pointer.wl_pointer) { + struct wl_buffer *buffer = NULL; + int scale = 1; + int dst_width = 0; + int dst_height = 0; + int hot_x; + int hot_y; + SDL_CursorData *cursor_data = cursor ? cursor->internal : NULL; + + // Stop the frame callback for old animated cursors. + if (seat->pointer.cursor_state.frame_callback && cursor_data != seat->pointer.current_cursor) { + wl_callback_destroy(seat->pointer.cursor_state.frame_callback); + seat->pointer.cursor_state.frame_callback = NULL; + } + + if (cursor) { + if (cursor_data == seat->pointer.current_cursor) { + return; + } + + if (cursor_data->is_system_cursor) { + // If the cursor shape protocol is supported, the compositor will draw nicely scaled cursors for us, so nothing more to do. + if (seat->pointer.cursor_shape) { + // Don't need the surface or viewport if using the cursor shape protocol. + if (seat->pointer.cursor_state.surface) { + wl_pointer_set_cursor(seat->pointer.wl_pointer, seat->pointer.enter_serial, NULL, 0, 0); + wl_surface_destroy(seat->pointer.cursor_state.surface); + seat->pointer.cursor_state.surface = NULL; + } + if (seat->pointer.cursor_state.viewport) { + wp_viewport_destroy(seat->pointer.cursor_state.viewport); + seat->pointer.cursor_state.viewport = NULL; + } + + Wayland_SetSystemCursorShape(seat, cursor_data->cursor_data.system.id); + seat->pointer.current_cursor = cursor_data; + + return; + } + + if (!Wayland_GetSystemCursor(seat->display, cursor_data, &scale, &dst_width, &hot_x, &hot_y)) { + return; + } + + dst_height = dst_width; + + if (!seat->pointer.cursor_state.surface) { + seat->pointer.cursor_state.surface = wl_compositor_create_surface(seat->display->compositor); + } + wl_surface_attach(seat->pointer.cursor_state.surface, cursor_data->cursor_data.system.frames[0].wl_buffer, 0, 0); + + // If more than one frame is available, create a frame callback to run the animation. + if (cursor_data->cursor_data.system.num_frames > 1) { + seat->pointer.cursor_state.last_frame_callback_time_ns = SDL_GetTicks(); + seat->pointer.cursor_state.current_frame_time_ns = 0; + seat->pointer.cursor_state.current_frame = 0; + seat->pointer.cursor_state.frame_callback = wl_surface_frame(seat->pointer.cursor_state.surface); + wl_callback_add_listener(seat->pointer.cursor_state.frame_callback, &cursor_frame_listener, seat); + } + } else { + if (!Wayland_GetCustomCursor(cursor, &buffer, &scale, &dst_width, &dst_height, &hot_x, &hot_y)) { + return; + } + + if (!seat->pointer.cursor_state.surface) { + seat->pointer.cursor_state.surface = wl_compositor_create_surface(seat->display->compositor); + } + wl_surface_attach(seat->pointer.cursor_state.surface, buffer, 0, 0); + } + + // A scale value of 0 indicates that a viewport with the returned destination size should be used. + if (!scale) { + if (!seat->pointer.cursor_state.viewport) { + seat->pointer.cursor_state.viewport = wp_viewporter_get_viewport(seat->display->viewporter, seat->pointer.cursor_state.surface); + } + wl_surface_set_buffer_scale(seat->pointer.cursor_state.surface, 1); + wp_viewport_set_source(seat->pointer.cursor_state.viewport, wl_fixed_from_int(-1), wl_fixed_from_int(-1), wl_fixed_from_int(-1), wl_fixed_from_int(-1)); + wp_viewport_set_destination(seat->pointer.cursor_state.viewport, dst_width, dst_height); + } else { + if (seat->pointer.cursor_state.viewport) { + wp_viewport_destroy(seat->pointer.cursor_state.viewport); + seat->pointer.cursor_state.viewport = NULL; + } + wl_surface_set_buffer_scale(seat->pointer.cursor_state.surface, scale); + } + + wl_pointer_set_cursor(seat->pointer.wl_pointer, seat->pointer.enter_serial, seat->pointer.cursor_state.surface, hot_x, hot_y); + + if (wl_surface_get_version(seat->pointer.cursor_state.surface) >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) { + wl_surface_damage_buffer(seat->pointer.cursor_state.surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32); + } else { + wl_surface_damage(seat->pointer.cursor_state.surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32); + } + + seat->pointer.current_cursor = cursor_data; + wl_surface_commit(seat->pointer.cursor_state.surface); + } else { + seat->pointer.current_cursor = NULL; + wl_pointer_set_cursor(seat->pointer.wl_pointer, seat->pointer.enter_serial, NULL, 0, 0); + } + } } static bool Wayland_ShowCursor(SDL_Cursor *cursor) { SDL_VideoDevice *vd = SDL_GetVideoDevice(); SDL_VideoData *d = vd->internal; - struct SDL_WaylandInput *input = d->input; - struct wl_pointer *pointer = d->pointer; - struct wl_buffer *buffer = NULL; - int scale = 1; - int dst_width = 0; - int dst_height = 0; - int hot_x; - int hot_y; + SDL_Mouse *mouse = SDL_GetMouse(); + SDL_WaylandSeat *seat; - if (!pointer) { - return false; - } - - // Stop the frame callback for old animated cursors. - if (input->current_cursor && input->current_cursor->is_system_cursor && - input->current_cursor->cursor_data.system.frame_callback) { - wl_callback_destroy(input->current_cursor->cursor_data.system.frame_callback); - input->current_cursor->cursor_data.system.frame_callback = NULL; - } - - if (cursor) { - SDL_CursorData *data = cursor->internal; - - if (data->is_system_cursor) { - // If the cursor shape protocol is supported, the compositor will draw nicely scaled cursors for us, so nothing more to do. - if (input->cursor_shape) { - Wayland_SetSystemCursorShape(input, data->cursor_data.system.id); - input->current_cursor = data; - return true; - } - - if (!Wayland_GetSystemCursor(d, data, &scale, &dst_width, &hot_x, &hot_y)) { - return false; - } - - dst_height = dst_width; - wl_surface_attach(data->surface, data->cursor_data.system.frames[0].wl_buffer, 0, 0); - - // If more than one frame is available, create a frame callback to run the animation. - if (data->cursor_data.system.num_frames > 1) { - data->cursor_data.system.last_frame_callback_time_ms = SDL_GetTicks(); - data->cursor_data.system.current_frame_time_ms = 0; - data->cursor_data.system.current_frame = 0; - data->cursor_data.system.frame_callback = wl_surface_frame(data->surface); - wl_callback_add_listener(data->cursor_data.system.frame_callback, &cursor_frame_listener, data); - } - } else { - if (!Wayland_GetCustomCursor(cursor, &buffer, &scale, &dst_width, &dst_height, &hot_x, &hot_y)) { - return false; - } - - wl_surface_attach(data->surface, buffer, 0, 0); + wl_list_for_each (seat, &d->seat_list, link) { + if (mouse->focus && mouse->focus->internal == seat->pointer.focus) { + Wayland_SeatSetCursor(seat, cursor); + } else if (!seat->pointer.focus) { + Wayland_SeatSetCursor(seat, NULL); } - - // A scale value of 0 indicates that a viewport with the returned destination size should be used. - if (!scale) { - if (!data->viewport) { - data->viewport = wp_viewporter_get_viewport(d->viewporter, data->surface); - } - wl_surface_set_buffer_scale(data->surface, 1); - wp_viewport_set_source(data->viewport, wl_fixed_from_int(-1), wl_fixed_from_int(-1), wl_fixed_from_int(-1), wl_fixed_from_int(-1)); - wp_viewport_set_destination(data->viewport, dst_width, dst_height); - } else { - if (data->viewport) { - wp_viewport_destroy(data->viewport); - data->viewport = NULL; - } - wl_surface_set_buffer_scale(data->surface, scale); - } - - wl_pointer_set_cursor(pointer, input->pointer_enter_serial, data->surface, hot_x, hot_y); - - if (wl_surface_get_version(data->surface) >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) { - wl_surface_damage_buffer(data->surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32); - } else { - wl_surface_damage(data->surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32); - } - - wl_surface_commit(data->surface); - input->current_cursor = data; - } else { - input->current_cursor = NULL; - wl_pointer_set_cursor(pointer, input->pointer_enter_serial, NULL, 0, 0); } return true; } -static bool Wayland_WarpMouse(SDL_Window *window, float x, float y) +void Wayland_SeatWarpMouse(SDL_WaylandSeat *seat, SDL_WindowData *window, float x, float y) { SDL_VideoDevice *vd = SDL_GetVideoDevice(); SDL_VideoData *d = vd->internal; - SDL_WindowData *wind = window->internal; - struct SDL_WaylandInput *input = d->input; - if (d->pointer_constraints) { - const bool toggle_lock = !wind->locked_pointer; + if (seat->pointer.wl_pointer) { + bool toggle_lock = !seat->pointer.locked_pointer; + bool update_grabs = false; /* The pointer confinement protocol allows setting a hint to warp the pointer, * but only when the pointer is locked. @@ -830,22 +841,51 @@ static bool Wayland_WarpMouse(SDL_Window *window, float x, float y) * Lock the pointer, set the position hint, unlock, and hope for the best. */ if (toggle_lock) { - Wayland_input_lock_pointer(input, window); - } - if (wind->locked_pointer) { - const wl_fixed_t f_x = wl_fixed_from_double(x / wind->pointer_scale.x); - const wl_fixed_t f_y = wl_fixed_from_double(y / wind->pointer_scale.y); - zwp_locked_pointer_v1_set_cursor_position_hint(wind->locked_pointer, f_x, f_y); - wl_surface_commit(wind->surface); + if (seat->pointer.confined_pointer) { + zwp_confined_pointer_v1_destroy(seat->pointer.confined_pointer); + seat->pointer.confined_pointer = NULL; + update_grabs = true; + } + seat->pointer.locked_pointer = zwp_pointer_constraints_v1_lock_pointer(d->pointer_constraints, window->surface, + seat->pointer.wl_pointer, NULL, + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT); } + + const wl_fixed_t f_x = wl_fixed_from_double(x / window->pointer_scale.x); + const wl_fixed_t f_y = wl_fixed_from_double(y / window->pointer_scale.y); + zwp_locked_pointer_v1_set_cursor_position_hint(seat->pointer.locked_pointer, f_x, f_y); + wl_surface_commit(window->surface); + if (toggle_lock) { - Wayland_input_unlock_pointer(input, window); + zwp_locked_pointer_v1_destroy(seat->pointer.locked_pointer); + seat->pointer.locked_pointer = NULL; + + if (update_grabs) { + Wayland_SeatUpdatePointerGrab(seat); + } } /* NOTE: There is a pending warp event under discussion that should replace this when available. * https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/340 */ - SDL_SendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, x, y); + SDL_SendMouseMotion(0, window->sdlwindow, seat->pointer.sdl_id, false, x, y); + } +} + +static bool Wayland_WarpMouseRelative(SDL_Window *window, float x, float y) +{ + SDL_VideoDevice *vd = SDL_GetVideoDevice(); + SDL_VideoData *d = vd->internal; + SDL_WindowData *wind = window->internal; + SDL_WaylandSeat *seat; + + if (d->pointer_constraints) { + wl_list_for_each (seat, &d->seat_list, link) { + if (wind == seat->pointer.focus || + (!seat->pointer.focus && wind == seat->keyboard.focus)) { + Wayland_SeatWarpMouse(seat, wind, x, y); + } + } } else { return SDL_SetError("wayland: mouse warp failed; compositor lacks support for the required zwp_pointer_confinement_v1 protocol"); } @@ -857,16 +897,33 @@ static bool Wayland_WarpMouseGlobal(float x, float y) { SDL_VideoDevice *vd = SDL_GetVideoDevice(); SDL_VideoData *d = vd->internal; - struct SDL_WaylandInput *input = d->input; - SDL_WindowData *wind = input->pointer_focus; + SDL_WaylandSeat *seat; - // If the client wants the coordinates warped to within the focused window, just convert the coordinates to relative. - if (wind) { - SDL_Window *window = wind->sdlwindow; - return Wayland_WarpMouse(window, x - (float)window->x, y - (float)window->y); + if (d->pointer_constraints) { + wl_list_for_each (seat, &d->seat_list, link) { + SDL_WindowData *wind = seat->pointer.focus ? seat->pointer.focus : seat->keyboard.focus; + + // If the client wants the coordinates warped to within a focused window, just convert the coordinates to relative. + if (wind) { + SDL_Window *window = wind->sdlwindow; + + int abs_x, abs_y; + SDL_RelativeToGlobalForWindow(window, window->x, window->y, &abs_x, &abs_y); + + const SDL_FPoint p = { x, y }; + const SDL_FRect r = { abs_x, abs_y, window->w, window->h }; + + // Try to warp the cursor if the point is within the seat's focused window. + if (SDL_PointInRectFloat(&p, &r)) { + Wayland_SeatWarpMouse(seat, wind, p.x - abs_x, p.y - abs_y); + } + } + } + } else { + return SDL_SetError("wayland: mouse warp failed; compositor lacks support for the required zwp_pointer_confinement_v1 protocol"); } - return SDL_SetError("wayland: can't warp the mouse when a window does not have focus"); + return true; } static bool Wayland_SetRelativeMouseMode(bool enabled) @@ -874,11 +931,17 @@ static bool Wayland_SetRelativeMouseMode(bool enabled) SDL_VideoDevice *vd = SDL_GetVideoDevice(); SDL_VideoData *data = vd->internal; - if (enabled) { - return Wayland_input_enable_relative_pointer(data->input); - } else { - return Wayland_input_disable_relative_pointer(data->input); + // Relative mode requires both the relative motion and pointer confinement protocols. + if (!data->relative_pointer_manager) { + return SDL_SetError("Failed to enable relative mode: compositor lacks support for the required zwp_relative_pointer_manager_v1 protocol"); } + if (!data->pointer_constraints) { + return SDL_SetError("Failed to enable relative mode: compositor lacks support for the required zwp_pointer_constraints_v1 protocol"); + } + + data->relative_mode_enabled = enabled; + Wayland_DisplayUpdatePointerGrabs(data, NULL); + return true; } /* Wayland doesn't support getting the true global cursor position, but it can @@ -895,18 +958,19 @@ static bool Wayland_SetRelativeMouseMode(bool enabled) */ static SDL_MouseButtonFlags SDLCALL Wayland_GetGlobalMouseState(float *x, float *y) { - SDL_Window *focus = SDL_GetMouseFocus(); + SDL_Mouse *mouse = SDL_GetMouse(); SDL_MouseButtonFlags result = 0; - if (focus) { - SDL_VideoData *viddata = SDL_GetVideoDevice()->internal; + // If there is no window with mouse focus, we have no idea what the actual position or button state is. + if (mouse->focus) { int off_x, off_y; - - result = viddata->input->buttons_pressed; - SDL_GetMouseState(x, y); - SDL_RelativeToGlobalForWindow(focus, focus->x, focus->y, &off_x, &off_y); - *x += off_x; - *y += off_y; + SDL_RelativeToGlobalForWindow(mouse->focus, mouse->focus->x, mouse->focus->y, &off_x, &off_y); + result = SDL_GetMouseState(x, y); + *x = mouse->x + off_x; + *y = mouse->y + off_y; + } else { + *x = 0.f; + *y = 0.f; } return result; @@ -978,7 +1042,7 @@ void Wayland_InitMouse(void) mouse->CreateSystemCursor = Wayland_CreateSystemCursor; mouse->ShowCursor = Wayland_ShowCursor; mouse->FreeCursor = Wayland_FreeCursor; - mouse->WarpMouse = Wayland_WarpMouse; + mouse->WarpMouse = Wayland_WarpMouseRelative; mouse->WarpMouseGlobal = Wayland_WarpMouseGlobal; mouse->SetRelativeMouseMode = Wayland_SetRelativeMouseMode; mouse->GetGlobalMouseState = Wayland_GetGlobalMouseState; @@ -1046,12 +1110,32 @@ void Wayland_FiniMouse(SDL_VideoData *data) } } -void Wayland_SetHitTestCursor(SDL_HitTestResult rc) +void Wayland_SeatUpdateCursor(SDL_WaylandSeat *seat) { - if (rc == SDL_HITTEST_NORMAL || rc == SDL_HITTEST_DRAGGABLE) { - SDL_SetCursor(NULL); + SDL_Mouse *mouse = SDL_GetMouse(); + SDL_WindowData *pointer_focus = seat->pointer.focus; + + if (pointer_focus) { + const bool has_relative_focus = Wayland_SeatHasRelativePointerFocus(seat); + + if (!seat->display->relative_mode_enabled || !has_relative_focus || mouse->relative_mode_cursor_visible) { + const SDL_HitTestResult rc = pointer_focus->hit_test_result; + + if ((seat->display->relative_mode_enabled && has_relative_focus) || + rc == SDL_HITTEST_NORMAL || rc == SDL_HITTEST_DRAGGABLE) { + Wayland_SeatSetCursor(seat, mouse->cur_cursor); + } else { + Wayland_SeatSetCursor(seat, sys_cursors[rc]); + } + } else { + // Hide the cursor in relative mode, unless requested otherwise by the hint. + Wayland_SeatSetCursor(seat, NULL); + } } else { - Wayland_ShowCursor(sys_cursors[rc]); + /* The spec states "The cursor actually changes only if the input device focus is one of the + * requesting client's surfaces", so just clear the cursor if the seat has no pointer focus. + */ + Wayland_SeatSetCursor(seat, NULL); } } diff --git a/src/video/wayland/SDL_waylandmouse.h b/src/video/wayland/SDL_waylandmouse.h index 58188c0e4f..82f877c1e5 100644 --- a/src/video/wayland/SDL_waylandmouse.h +++ b/src/video/wayland/SDL_waylandmouse.h @@ -26,7 +26,8 @@ extern void Wayland_InitMouse(void); extern void Wayland_FiniMouse(SDL_VideoData *data); -extern void Wayland_SetHitTestCursor(SDL_HitTestResult rc); +extern void Wayland_SeatUpdateCursor(SDL_WaylandSeat *seat); +extern void Wayland_SeatWarpMouse(SDL_WaylandSeat *seat, SDL_WindowData *window, float x, float y); #if 0 // TODO RECONNECT: See waylandvideo.c for more information! extern void Wayland_RecreateCursors(void); #endif // 0 diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index 96b65d65cb..d0b339c575 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -502,7 +502,6 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols) { SDL_VideoDevice *device; SDL_VideoData *data; - struct SDL_WaylandInput *input; struct wl_display *display = SDL_GetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, NULL); bool display_is_external = !!display; @@ -549,32 +548,16 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols) return NULL; } - input = SDL_calloc(1, sizeof(*input)); - if (!input) { - SDL_free(data); - if (!display_is_external) { - WAYLAND_wl_display_disconnect(display); - } - SDL_WAYLAND_UnloadSymbols(); - return NULL; - } - - input->display = data; - input->sx_w = wl_fixed_from_int(0); - input->sy_w = wl_fixed_from_int(0); - input->xkb.current_group = XKB_GROUP_INVALID; - data->initializing = true; data->display = display; - data->input = input; data->display_externally_owned = display_is_external; data->scale_to_display_enabled = SDL_GetHintBoolean(SDL_HINT_VIDEO_WAYLAND_SCALE_TO_DISPLAY, false); + WAYLAND_wl_list_init(&data->seat_list); WAYLAND_wl_list_init(&external_window_list); // Initialize all variables that we clean on shutdown device = SDL_calloc(1, sizeof(SDL_VideoDevice)); if (!device) { - SDL_free(input); SDL_free(data); if (!display_is_external) { WAYLAND_wl_display_disconnect(display); @@ -1267,8 +1250,8 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint } else if (SDL_strcmp(interface, "wl_output") == 0) { Wayland_add_display(d, id, SDL_min(version, SDL_WL_OUTPUT_VERSION)); } else if (SDL_strcmp(interface, "wl_seat") == 0) { - d->input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface, SDL_min(SDL_WL_SEAT_VERSION, version)); - Wayland_input_initialize_seat(d); + struct wl_seat *seat = wl_registry_bind(d->registry, id, &wl_seat_interface, SDL_min(SDL_WL_SEAT_VERSION, version)); + Wayland_DisplayCreateSeat(d, seat, id); } else if (SDL_strcmp(interface, "xdg_wm_base") == 0) { d->shell.xdg = wl_registry_bind(d->registry, id, &xdg_wm_base_interface, SDL_min(version, 6)); xdg_wm_base_add_listener(d->shell.xdg, &shell_listener_xdg, NULL); @@ -1276,7 +1259,7 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); } else if (SDL_strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) { d->relative_pointer_manager = wl_registry_bind(d->registry, id, &zwp_relative_pointer_manager_v1_interface, 1); - Wayland_input_init_relative_pointer(d); + Wayland_DisplayInitRelativePointerManager(d); } else if (SDL_strcmp(interface, "zwp_pointer_constraints_v1") == 0) { d->pointer_constraints = wl_registry_bind(d->registry, id, &zwp_pointer_constraints_v1_interface, 1); } else if (SDL_strcmp(interface, "zwp_keyboard_shortcuts_inhibit_manager_v1") == 0) { @@ -1286,18 +1269,18 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint } else if (SDL_strcmp(interface, "xdg_activation_v1") == 0) { d->activation_manager = wl_registry_bind(d->registry, id, &xdg_activation_v1_interface, 1); } else if (SDL_strcmp(interface, "zwp_text_input_manager_v3") == 0) { - Wayland_create_text_input_manager(d, id); + Wayland_DisplayCreateTextInputManager(d, id); } else if (SDL_strcmp(interface, "wl_data_device_manager") == 0) { d->data_device_manager = wl_registry_bind(d->registry, id, &wl_data_device_manager_interface, SDL_min(3, version)); - Wayland_create_data_device(d); + Wayland_DisplayInitDataDeviceManager(d); } else if (SDL_strcmp(interface, "zwp_primary_selection_device_manager_v1") == 0) { d->primary_selection_device_manager = wl_registry_bind(d->registry, id, &zwp_primary_selection_device_manager_v1_interface, 1); - Wayland_create_primary_selection_device(d); + Wayland_DisplayInitPrimarySelectionDeviceManager(d); } else if (SDL_strcmp(interface, "zxdg_decoration_manager_v1") == 0) { d->decoration_manager = wl_registry_bind(d->registry, id, &zxdg_decoration_manager_v1_interface, 1); } else if (SDL_strcmp(interface, "zwp_tablet_manager_v2") == 0) { d->tablet_manager = wl_registry_bind(d->registry, id, &zwp_tablet_manager_v2_interface, 1); - Wayland_input_init_tablet_support(d->input, d->tablet_manager); + Wayland_DisplayInitTabletManager(d); } else if (SDL_strcmp(interface, "zxdg_output_manager_v1") == 0) { version = SDL_min(version, 3); // Versions 1 through 3 are supported. d->xdg_output_manager = wl_registry_bind(d->registry, id, &zxdg_output_manager_v1_interface, version); @@ -1308,14 +1291,10 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint d->fractional_scale_manager = wl_registry_bind(d->registry, id, &wp_fractional_scale_manager_v1_interface, 1); } else if (SDL_strcmp(interface, "zwp_input_timestamps_manager_v1") == 0) { d->input_timestamps_manager = wl_registry_bind(d->registry, id, &zwp_input_timestamps_manager_v1_interface, 1); - if (d->input) { - Wayland_RegisterTimestampListeners(d->input); - } + Wayland_DisplayInitInputTimestampManager(d); } else if (SDL_strcmp(interface, "wp_cursor_shape_manager_v1") == 0) { d->cursor_shape_manager = wl_registry_bind(d->registry, id, &wp_cursor_shape_manager_v1_interface, 1); - if (d->input) { - Wayland_CreateCursorShapeDevice(d->input); - } + Wayland_DisplayInitCursorShapeManager(d); } else if (SDL_strcmp(interface, "zxdg_exporter_v2") == 0) { d->zxdg_exporter_v2 = wl_registry_bind(d->registry, id, &zxdg_exporter_v2_interface, 1); } else if (SDL_strcmp(interface, "xdg_wm_dialog_v1") == 0) { @@ -1336,7 +1315,7 @@ static void display_remove_global(void *data, struct wl_registry *registry, uint { SDL_VideoData *d = data; - // We don't get an interface, just an ID, so assume it's a wl_output :shrug: + // We don't get an interface, just an ID, so check outputs and seats. for (int i = 0; i < d->output_count; ++i) { SDL_DisplayData *disp = d->output_list[i]; if (disp->registry_id == id) { @@ -1347,7 +1326,21 @@ static void display_remove_global(void *data, struct wl_registry *registry, uint } d->output_count--; - break; + return; + } + } + + struct SDL_WaylandSeat *seat, *temp; + wl_list_for_each_safe (seat, temp, &d->seat_list, link) + { + if (seat->registry_id == id) { + if (seat->keyboard.wl_keyboard) { + SDL_RemoveKeyboard(seat->keyboard.sdl_id, true); + } + if (seat->keyboard.wl_keyboard) { + SDL_RemoveMouse(seat->pointer.sdl_id, true); + } + Wayland_SeatDestroy(seat, true); } } } @@ -1487,6 +1480,7 @@ static bool Wayland_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *d static void Wayland_VideoCleanup(SDL_VideoDevice *_this) { SDL_VideoData *data = _this->internal; + SDL_WaylandSeat *seat, *tmp; int i; Wayland_FiniMouse(data); @@ -1497,7 +1491,9 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this) } SDL_free(data->output_list); - Wayland_display_destroy_input(data); + wl_list_for_each_safe (seat, tmp, &data->seat_list, link) { + Wayland_SeatDestroy(seat, false); + } if (data->pointer_constraints) { zwp_pointer_constraints_v1_destroy(data->pointer_constraints); diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h index 8cde64c9ac..33f2091966 100644 --- a/src/video/wayland/SDL_waylandvideo.h +++ b/src/video/wayland/SDL_waylandvideo.h @@ -32,7 +32,7 @@ #include "../../core/linux/SDL_ime.h" struct xkb_context; -struct SDL_WaylandInput; +struct SDL_WaylandSeat; typedef struct { @@ -56,7 +56,6 @@ struct SDL_VideoData struct wl_shm *shm; SDL_WaylandCursorTheme *cursor_themes; int num_cursor_themes; - struct wl_pointer *pointer; struct { struct xdg_wm_base *xdg; @@ -87,12 +86,17 @@ struct SDL_VideoData struct zwp_tablet_manager_v2 *tablet_manager; struct xkb_context *xkb_context; - struct SDL_WaylandInput *input; + + struct wl_list seat_list; + struct SDL_WaylandSeat *last_implicit_grab_seat; + struct SDL_WaylandSeat *last_incoming_data_offer_seat; + struct SDL_WaylandSeat *last_incoming_primary_selection_seat; + SDL_DisplayData **output_list; int output_count; int output_max; - int relative_mouse_mode; + bool relative_mode_enabled; bool display_externally_owned; bool scale_to_display_enabled; diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index 1fea144e17..43a6b328e7 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -707,9 +707,6 @@ static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time } } - // Create the pointer confinement region, if necessary. - Wayland_input_confine_pointer(wind->waylandData->input, wind->sdlwindow); - /* If the window was initially set to the suspended state, send the occluded event now, * as we don't want to mark the window as occluded until at least one frame has been submitted. */ @@ -2223,9 +2220,17 @@ static const struct xdg_activation_token_v1_listener activation_listener_xdg = { */ static void Wayland_activate_window(SDL_VideoData *data, SDL_WindowData *target_wind, bool set_serial) { - struct SDL_WaylandInput * input = data->input; - SDL_Window *focus = SDL_GetKeyboardFocus(); - struct wl_surface *requesting_surface = focus ? focus->internal->surface : NULL; + SDL_WaylandSeat *seat = data->last_implicit_grab_seat; + SDL_WindowData *focus = NULL; + + if (seat) { + focus = seat->keyboard.focus; + if (!focus) { + focus = seat->pointer.focus; + } + } + + struct wl_surface *requesting_surface = focus ? focus->surface : NULL; if (data->activation_manager) { if (target_wind->activation_token) { @@ -2249,8 +2254,8 @@ static void Wayland_activate_window(SDL_VideoData *data, SDL_WindowData *target_ // This specifies the surface from which the activation request is originating, not the activation target surface. xdg_activation_token_v1_set_surface(target_wind->activation_token, requesting_surface); } - if (set_serial && input && input->seat) { - xdg_activation_token_v1_set_serial(target_wind->activation_token, input->last_implicit_grab_serial, input->seat); + if (set_serial && seat && seat->wl_seat) { + xdg_activation_token_v1_set_serial(target_wind->activation_token, seat->last_implicit_grab_serial, seat->wl_seat); } xdg_activation_token_v1_commit(target_wind->activation_token); } @@ -2505,35 +2510,31 @@ bool Wayland_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window) * Just know that this call lets you confine with a rect, SetWindowGrab * lets you confine without a rect. */ - if (SDL_RectEmpty(&window->mouse_rect) && !(window->flags & SDL_WINDOW_MOUSE_GRABBED)) { - return Wayland_input_unconfine_pointer(data->input, window); - } else { - return Wayland_input_confine_pointer(data->input, window); + if (!data->pointer_constraints) { + return SDL_SetError("Failed to grab mouse: compositor lacks support for the required zwp_pointer_constraints_v1 protocol"); } + Wayland_DisplayUpdatePointerGrabs(data, window->internal); + return true; } bool Wayland_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed) { SDL_VideoData *data = _this->internal; - - if (grabbed) { - return Wayland_input_confine_pointer(data->input, window); - } else if (SDL_RectEmpty(&window->mouse_rect)) { - return Wayland_input_unconfine_pointer(data->input, window); + if (!data->pointer_constraints) { + return SDL_SetError("Failed to grab mouse: compositor lacks support for the required zwp_pointer_constraints_v1 protocol"); } - + Wayland_DisplayUpdatePointerGrabs(data, window->internal); return true; } bool Wayland_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed) { SDL_VideoData *data = _this->internal; - - if (grabbed) { - return Wayland_input_grab_keyboard(window, data->input); - } else { - return Wayland_input_ungrab_keyboard(window); + if (!data->key_inhibitor_manager) { + return SDL_SetError("Failed to grab keyboard: compositor lacks support for the required zwp_keyboard_shortcuts_inhibit_manager_v1 protocol"); } + Wayland_DisplayUpdateKeyboardGrabs(data, window->internal); + return true; } bool Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props) @@ -2689,10 +2690,6 @@ bool Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Proper } #endif - if (c->relative_mouse_mode) { - Wayland_input_enable_relative_pointer(c->input); - } - // We may need to create an idle inhibitor for this new window Wayland_SuspendScreenSaver(_this); @@ -2994,6 +2991,11 @@ bool Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window) void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y) { SDL_WindowData *wind = window->internal; + SDL_WaylandSeat *seat = wind->waylandData->last_implicit_grab_seat; + + if (!seat) { + return; + } if (wind->scale_to_display) { x = PixelToPoint(window, x); @@ -3003,13 +3005,13 @@ void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y) #ifdef HAVE_LIBDECOR_H if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) { if (wind->shell_surface.libdecor.frame) { - libdecor_frame_show_window_menu(wind->shell_surface.libdecor.frame, wind->waylandData->input->seat, wind->waylandData->input->last_implicit_grab_serial, x, y); + libdecor_frame_show_window_menu(wind->shell_surface.libdecor.frame, seat->wl_seat, seat->last_implicit_grab_serial, x, y); } } else #endif if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) { if (wind->shell_surface.xdg.toplevel.xdg_toplevel) { - xdg_toplevel_show_window_menu(wind->shell_surface.xdg.toplevel.xdg_toplevel, wind->waylandData->input->seat, wind->waylandData->input->last_implicit_grab_serial, x, y); + xdg_toplevel_show_window_menu(wind->shell_surface.xdg.toplevel.xdg_toplevel, seat->wl_seat, seat->last_implicit_grab_serial, x, y); } } } diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index ee27f337b8..36458d434e 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -30,8 +30,6 @@ #include "SDL_waylandvideo.h" #include "SDL_waylandshmbuffer.h" -struct SDL_WaylandInput; - struct SDL_WindowData { SDL_Window *sdlwindow; @@ -99,14 +97,10 @@ struct SDL_WindowData } wm_caps; struct wl_egl_window *egl_window; - struct SDL_WaylandInput *keyboard_device; #ifdef SDL_VIDEO_OPENGL_EGL EGLSurface egl_surface; #endif - struct zwp_locked_pointer_v1 *locked_pointer; - struct zwp_confined_pointer_v1 *confined_pointer; struct zxdg_toplevel_decoration_v1 *server_decoration; - struct zwp_keyboard_shortcuts_inhibitor_v1 *key_inhibitor; struct zwp_idle_inhibitor_v1 *idle_inhibitor; struct xdg_activation_token_v1 *activation_token; struct wp_viewport *viewport; @@ -133,6 +127,10 @@ struct SDL_WindowData struct Wayland_SHMBuffer *icon_buffers; int icon_buffer_count; + // Keyboard and pointer focus refcount. + int keyboard_focus_count; + int pointer_focus_count; + struct { double x; @@ -184,6 +182,13 @@ struct SDL_WindowData int height; } toplevel_bounds; + struct + { + int hint; + int purpose; + bool active; + } text_input_props; + SDL_DisplayID last_displayID; int fullscreen_deadline_count; int maximized_restored_deadline_count; diff --git a/src/video/windows/SDL_windowskeyboard.c b/src/video/windows/SDL_windowskeyboard.c index 75e8ad4a28..560437598f 100644 --- a/src/video/windows/SDL_windowskeyboard.c +++ b/src/video/windows/SDL_windowskeyboard.c @@ -96,7 +96,7 @@ void WIN_UpdateKeymap(bool send_event) WIN_ResetDeadKeys(); - keymap = SDL_CreateKeymap(); + keymap = SDL_CreateKeymap(true); for (int m = 0; m < SDL_arraysize(mods); ++m) { for (int i = 0; i < SDL_arraysize(windows_scancode_table); i++) { diff --git a/src/video/x11/SDL_x11keyboard.c b/src/video/x11/SDL_x11keyboard.c index c48e82917f..cff0788964 100644 --- a/src/video/x11/SDL_x11keyboard.c +++ b/src/video/x11/SDL_x11keyboard.c @@ -383,7 +383,7 @@ void X11_UpdateKeymap(SDL_VideoDevice *_this, bool send_event) SDL_VideoData *data = _this->internal; SDL_Scancode scancode; - SDL_Keymap *keymap = SDL_CreateKeymap(); + SDL_Keymap *keymap = SDL_CreateKeymap(true); #ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM if (data->xkb.desc_ptr) {