Skip to content

Commit 113475a

Browse files
committed
wayland: Add multi-seat support
Wayland environments can expose more than one seat for multiple collections of input devices, which can include multiple, simultaneously active, desktop pointers and keyboards with independent layouts. The Wayland input backend previously presumed that only one seat could exist, which caused broken behavior if the compositor exposed more than one, which is possible on wlroots based compositors such as Sway. This introduces support for handling multiple seats, including proper handling of dynamically added and removed seats and capabilities at run time. The SDL Wayland input system was accreted over time, and the assumption that only one seat will ever exist resulted in state and related objects not always being tied to their most appropriate owner in a multi-seat scenario, so refactoring was required to manage several bits of state per-seat, instead of per-window or globally. As Wayland keyboards can have per-seat layouts, fast keymap switching is required when multiplexing input from multiple seats to the global SDL keyboard device. A parameter was added to the keymap creation function to specify if the keymap lifetime should be externally managed to facilitate keymap reuse, and some layout info was moved from the global keyboard state to the keymap state to avoid unnecessarily redetermining it whenever a reused keymap is bound. This reduces the overhead of switching keymaps to setting a single pointer. Multiple seats also means that multiple windows can have keyboard and/or mouse focus at the same time on some compositors, but this is not currently a well-handled case in SDL, and will require more work to support, if necessary.
1 parent 4093e4a commit 113475a

25 files changed

+1805
-1403
lines changed

src/events/SDL_keyboard.c

+44-29
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,6 @@ typedef struct SDL_Keyboard
5858
Uint8 keysource[SDL_SCANCODE_COUNT];
5959
bool keystate[SDL_SCANCODE_COUNT];
6060
SDL_Keymap *keymap;
61-
bool french_numbers;
62-
bool latin_letters;
63-
bool thai_keyboard;
6461
Uint32 keycode_options;
6562
bool autorelease_pending;
6663
Uint64 hardware_timestamp;
@@ -175,6 +172,19 @@ void SDL_RemoveKeyboard(SDL_KeyboardID keyboardID, bool send_event)
175172
}
176173
}
177174

175+
void SDL_SetKeyboardName(SDL_KeyboardID keyboardID, const char *name)
176+
{
177+
SDL_assert(keyboardID != 0);
178+
179+
const int keyboard_index = SDL_GetKeyboardIndex(keyboardID);
180+
181+
if (keyboard_index >= 0) {
182+
SDL_KeyboardInstance *instance = &SDL_keyboards[keyboard_index];
183+
SDL_free(instance->name);
184+
instance->name = SDL_strdup(name ? name : "");
185+
}
186+
}
187+
178188
bool SDL_HasKeyboard(void)
179189
{
180190
return (SDL_keyboard_count > 0);
@@ -232,14 +242,15 @@ void SDL_ResetKeyboard(void)
232242
SDL_Keymap *SDL_GetCurrentKeymap(void)
233243
{
234244
SDL_Keyboard *keyboard = &SDL_keyboard;
245+
SDL_Keymap *keymap = SDL_keyboard.keymap;
235246

236-
if (keyboard->thai_keyboard) {
247+
if (keymap && keymap->thai_keyboard) {
237248
// Thai keyboards are QWERTY plus Thai characters, use the default QWERTY keymap
238249
return NULL;
239250
}
240251

241252
if ((keyboard->keycode_options & KEYCODE_OPTION_LATIN_LETTERS) &&
242-
!keyboard->latin_letters) {
253+
keymap && !keymap->latin_letters) {
243254
// We'll use the default QWERTY keymap
244255
return NULL;
245256
}
@@ -251,35 +262,39 @@ void SDL_SetKeymap(SDL_Keymap *keymap, bool send_event)
251262
{
252263
SDL_Keyboard *keyboard = &SDL_keyboard;
253264

254-
if (keyboard->keymap) {
265+
if (keyboard->keymap && keyboard->keymap->auto_release) {
255266
SDL_DestroyKeymap(keyboard->keymap);
256267
}
257268

258269
keyboard->keymap = keymap;
259270

260-
// Detect French number row (all symbols)
261-
keyboard->french_numbers = true;
262-
for (int i = SDL_SCANCODE_1; i <= SDL_SCANCODE_0; ++i) {
263-
if (SDL_isdigit(SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_NONE)) ||
264-
!SDL_isdigit(SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_SHIFT))) {
265-
keyboard->french_numbers = false;
266-
break;
267-
}
268-
}
271+
if (keymap && !keymap->layout_determined) {
272+
keymap->layout_determined = true;
269273

270-
// Detect non-Latin keymap
271-
keyboard->thai_keyboard = false;
272-
keyboard->latin_letters = false;
273-
for (int i = SDL_SCANCODE_A; i <= SDL_SCANCODE_D; ++i) {
274-
SDL_Keycode key = SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_NONE);
275-
if (key <= 0xFF) {
276-
keyboard->latin_letters = true;
277-
break;
274+
// Detect French number row (all symbols)
275+
keymap->french_numbers = true;
276+
for (int i = SDL_SCANCODE_1; i <= SDL_SCANCODE_0; ++i) {
277+
if (SDL_isdigit(SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_NONE)) ||
278+
!SDL_isdigit(SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_SHIFT))) {
279+
keymap->french_numbers = false;
280+
break;
281+
}
278282
}
279283

280-
if (key >= 0x0E00 && key <= 0x0E7F) {
281-
keyboard->thai_keyboard = true;
282-
break;
284+
// Detect non-Latin keymap
285+
keymap->thai_keyboard = false;
286+
keymap->latin_letters = false;
287+
for (int i = SDL_SCANCODE_A; i <= SDL_SCANCODE_D; ++i) {
288+
SDL_Keycode key = SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_NONE);
289+
if (key <= 0xFF) {
290+
keymap->latin_letters = true;
291+
break;
292+
}
293+
294+
if (key >= 0x0E00 && key <= 0x0E7F) {
295+
keymap->thai_keyboard = true;
296+
break;
297+
}
283298
}
284299
}
285300

@@ -308,7 +323,7 @@ static void SetKeymapEntry(SDL_Scancode scancode, SDL_Keymod modstate, SDL_Keyco
308323
SDL_Keyboard *keyboard = &SDL_keyboard;
309324

310325
if (!keyboard->keymap) {
311-
keyboard->keymap = SDL_CreateKeymap();
326+
keyboard->keymap = SDL_CreateKeymap(true);
312327
}
313328

314329
SDL_SetKeymapEntry(keyboard->keymap, scancode, modstate, keycode);
@@ -483,7 +498,7 @@ SDL_Keycode SDL_GetKeyFromScancode(SDL_Scancode scancode, SDL_Keymod modstate, b
483498
modstate = SDL_KMOD_NONE;
484499

485500
if ((keyboard->keycode_options & KEYCODE_OPTION_FRENCH_NUMBERS) &&
486-
keyboard->french_numbers &&
501+
keymap && keymap->french_numbers &&
487502
(scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_0)) {
488503
// Add the shift state to generate a numeric keycode
489504
modstate |= SDL_KMOD_SHIFT;
@@ -876,7 +891,7 @@ void SDL_QuitKeyboard(void)
876891
SDL_free(SDL_keyboards);
877892
SDL_keyboards = NULL;
878893

879-
if (SDL_keyboard.keymap) {
894+
if (SDL_keyboard.keymap && SDL_keyboard.keymap->auto_release) {
880895
SDL_DestroyKeymap(SDL_keyboard.keymap);
881896
SDL_keyboard.keymap = NULL;
882897
}

src/events/SDL_keyboard_c.h

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ extern void SDL_AddKeyboard(SDL_KeyboardID keyboardID, const char *name, bool se
4343
// A keyboard has been removed from the system
4444
extern void SDL_RemoveKeyboard(SDL_KeyboardID keyboardID, bool send_event);
4545

46+
// Set or update the name of a keyboard instance.
47+
extern void SDL_SetKeyboardName(SDL_KeyboardID keyboardID, const char *name);
48+
4649
// Set the mapping of scancode to key codes
4750
extern void SDL_SetKeymap(SDL_Keymap *keymap, bool send_event);
4851

src/events/SDL_keymap.c

+3-8
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,17 @@
2323
#include "SDL_keymap_c.h"
2424
#include "SDL_keyboard_c.h"
2525

26-
struct SDL_Keymap
27-
{
28-
SDL_HashTable *scancode_to_keycode;
29-
SDL_HashTable *keycode_to_scancode;
30-
};
31-
3226
static SDL_Keycode SDL_GetDefaultKeyFromScancode(SDL_Scancode scancode, SDL_Keymod modstate);
3327
static SDL_Scancode SDL_GetDefaultScancodeFromKey(SDL_Keycode key, SDL_Keymod *modstate);
3428

35-
SDL_Keymap *SDL_CreateKeymap(void)
29+
SDL_Keymap *SDL_CreateKeymap(bool auto_release)
3630
{
37-
SDL_Keymap *keymap = (SDL_Keymap *)SDL_malloc(sizeof(*keymap));
31+
SDL_Keymap *keymap = (SDL_Keymap *)SDL_calloc(1, sizeof(*keymap));
3832
if (!keymap) {
3933
return NULL;
4034
}
4135

36+
keymap->auto_release = auto_release;
4237
keymap->scancode_to_keycode = SDL_CreateHashTable(256, false, SDL_HashID, SDL_KeyMatchID, NULL, NULL);
4338
keymap->keycode_to_scancode = SDL_CreateHashTable(256, false, SDL_HashID, SDL_KeyMatchID, NULL, NULL);
4439
if (!keymap->scancode_to_keycode || !keymap->keycode_to_scancode) {

src/events/SDL_keymap_c.h

+11-2
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,19 @@
2323
#ifndef SDL_keymap_c_h_
2424
#define SDL_keymap_c_h_
2525

26-
typedef struct SDL_Keymap SDL_Keymap;
26+
typedef struct SDL_Keymap
27+
{
28+
SDL_HashTable *scancode_to_keycode;
29+
SDL_HashTable *keycode_to_scancode;
30+
bool auto_release;
31+
bool layout_determined;
32+
bool french_numbers;
33+
bool latin_letters;
34+
bool thai_keyboard;
35+
} SDL_Keymap;
2736

2837
SDL_Keymap *SDL_GetCurrentKeymap(void);
29-
SDL_Keymap *SDL_CreateKeymap(void);
38+
SDL_Keymap *SDL_CreateKeymap(bool auto_release);
3039
void SDL_SetKeymapEntry(SDL_Keymap *keymap, SDL_Scancode scancode, SDL_Keymod modstate, SDL_Keycode keycode);
3140
SDL_Keycode SDL_GetKeymapKeycode(SDL_Keymap *keymap, SDL_Scancode scancode, SDL_Keymod modstate);
3241
SDL_Scancode SDL_GetKeymapScancode(SDL_Keymap *keymap, SDL_Keycode keycode, SDL_Keymod *modstate);

src/events/SDL_mouse.c

+13
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,19 @@ void SDL_RemoveMouse(SDL_MouseID mouseID, bool send_event)
409409
}
410410
}
411411

412+
void SDL_SetMouseName(SDL_MouseID mouseID, const char *name)
413+
{
414+
SDL_assert(mouseID != 0);
415+
416+
const int mouse_index = SDL_GetMouseIndex(mouseID);
417+
418+
if (mouse_index >= 0) {
419+
SDL_MouseInstance *instance = &SDL_mice[mouse_index];
420+
SDL_free(instance->name);
421+
instance->name = SDL_strdup(name ? name : "");
422+
}
423+
}
424+
412425
bool SDL_HasMouse(void)
413426
{
414427
return (SDL_mouse_count > 0);

src/events/SDL_mouse_c.h

+3
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@ extern void SDL_AddMouse(SDL_MouseID mouseID, const char *name, bool send_event)
169169
// A mouse has been removed from the system
170170
extern void SDL_RemoveMouse(SDL_MouseID mouseID, bool send_event);
171171

172+
// Set or update the name of a mouse instance.
173+
extern void SDL_SetMouseName(SDL_MouseID mouseID, const char *name);
174+
172175
// Get the mouse state structure
173176
extern SDL_Mouse *SDL_GetMouse(void);
174177

src/events/SDL_touch.c

+10
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,16 @@ int SDL_AddTouch(SDL_TouchID touchID, SDL_TouchDeviceType type, const char *name
205205
return index;
206206
}
207207

208+
// Set or update the name of a touch.
209+
void SDL_SetTouchName(SDL_TouchID id, const char *name)
210+
{
211+
SDL_Touch *touch = SDL_GetTouch(id);
212+
if (touch) {
213+
SDL_free(touch->name);
214+
touch->name = SDL_strdup(name ? name : "");
215+
}
216+
}
217+
208218
static bool SDL_AddFinger(SDL_Touch *touch, SDL_FingerID fingerid, float x, float y, float pressure)
209219
{
210220
SDL_Finger *finger;

src/events/SDL_touch_c.h

+3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ extern bool SDL_TouchDevicesAvailable(void);
4242
// Add a touch, returning the index of the touch, or -1 if there was an error.
4343
extern int SDL_AddTouch(SDL_TouchID id, SDL_TouchDeviceType type, const char *name);
4444

45+
// Set or update the name of a touch.
46+
extern void SDL_SetTouchName(SDL_TouchID id, const char *name);
47+
4548
// Get the touch with a given id
4649
extern SDL_Touch *SDL_GetTouch(SDL_TouchID id);
4750

src/test/SDL_test_common.c

+7-1
Original file line numberDiff line numberDiff line change
@@ -2511,7 +2511,13 @@ SDL_AppResult SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const
25112511
/* Ctrl-G toggle mouse grab */
25122512
SDL_Window *window = SDL_GetWindowFromEvent(event);
25132513
if (window) {
2514-
SDL_SetWindowMouseGrab(window, !SDL_GetWindowMouseGrab(window));
2514+
if (SDL_RectEmpty(SDL_GetWindowMouseRect(window))) {
2515+
SDL_Rect r = { 10, 10, 200, 200};
2516+
SDL_SetWindowMouseRect(window, &r);
2517+
} else {
2518+
SDL_SetWindowMouseRect(window, NULL);
2519+
}
2520+
//SDL_SetWindowMouseGrab(window, !SDL_GetWindowMouseGrab(window));
25152521
}
25162522
}
25172523
break;

src/video/cocoa/SDL_cocoakeyboard.m

+1-1
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ static void UpdateKeymap(SDL_CocoaVideoData *data, bool send_event)
327327

328328
UInt32 keyboard_type = LMGetKbdType();
329329

330-
SDL_Keymap *keymap = SDL_CreateKeymap();
330+
SDL_Keymap *keymap = SDL_CreateKeymap(true);
331331
for (int m = 0; m < SDL_arraysize(mods); ++m) {
332332
for (int i = 0; i < SDL_arraysize(darwin_scancode_table); i++) {
333333
OSStatus err;

src/video/wayland/SDL_waylandclipboard.c

+29-19
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,16 @@
3232
bool Wayland_SetClipboardData(SDL_VideoDevice *_this)
3333
{
3434
SDL_VideoData *video_data = _this->internal;
35-
SDL_WaylandDataDevice *data_device = NULL;
36-
bool result = true;
35+
SDL_WaylandSeat *seat = video_data->last_implicit_grab_seat;
36+
bool result = false;
37+
38+
// If no implicit grab is available yet, just attach it to the first available seat.
39+
if (!seat && !WAYLAND_wl_list_empty(&video_data->seat_list)) {
40+
seat = wl_container_of(video_data->seat_list.next, seat, link);
41+
}
3742

38-
if (video_data->input && video_data->input->data_device) {
39-
data_device = video_data->input->data_device;
43+
if (seat && seat->data_device) {
44+
SDL_WaylandDataDevice *data_device = seat->data_device;
4045

4146
if (_this->clipboard_callback && _this->clipboard_mime_types) {
4247
SDL_WaylandDataSource *source = Wayland_data_source_create(_this);
@@ -57,11 +62,11 @@ bool Wayland_SetClipboardData(SDL_VideoDevice *_this)
5762
void *Wayland_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length)
5863
{
5964
SDL_VideoData *video_data = _this->internal;
60-
SDL_WaylandDataDevice *data_device = NULL;
65+
SDL_WaylandSeat *seat = video_data->last_incoming_data_offer_seat;
6166
void *buffer = NULL;
6267

63-
if (video_data->input && video_data->input->data_device) {
64-
data_device = video_data->input->data_device;
68+
if (seat && seat->data_device) {
69+
SDL_WaylandDataDevice *data_device = seat->data_device;
6570
if (data_device->selection_source) {
6671
buffer = SDL_GetInternalClipboardData(_this, mime_type, length);
6772
} 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
7580
bool Wayland_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
7681
{
7782
SDL_VideoData *video_data = _this->internal;
78-
SDL_WaylandDataDevice *data_device = NULL;
83+
SDL_WaylandSeat *seat = video_data->last_incoming_data_offer_seat;
7984
bool result = false;
8085

81-
if (video_data->input && video_data->input->data_device) {
82-
data_device = video_data->input->data_device;
86+
if (seat && seat->data_device) {
87+
SDL_WaylandDataDevice *data_device = seat->data_device;
8388
if (data_device->selection_source) {
8489
result = SDL_HasInternalClipboardData(_this, mime_type);
8590
} else {
@@ -106,11 +111,16 @@ const char **Wayland_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_t
106111
bool Wayland_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text)
107112
{
108113
SDL_VideoData *video_data = _this->internal;
109-
SDL_WaylandPrimarySelectionDevice *primary_selection_device = NULL;
114+
SDL_WaylandSeat *seat = video_data->last_implicit_grab_seat;
110115
bool result;
111116

112-
if (video_data->input && video_data->input->primary_selection_device) {
113-
primary_selection_device = video_data->input->primary_selection_device;
117+
// If no implicit grab is available yet, just attach it to the first available seat.
118+
if (!seat && !WAYLAND_wl_list_empty(&video_data->seat_list)) {
119+
seat = wl_container_of(video_data->seat_list.next, seat, link);
120+
}
121+
122+
if (seat && seat->primary_selection_device) {
123+
SDL_WaylandPrimarySelectionDevice *primary_selection_device = seat->primary_selection_device;
114124
if (text[0] != '\0') {
115125
SDL_WaylandPrimarySelectionSource *source = Wayland_primary_selection_source_create(_this);
116126
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)
134144
char *Wayland_GetPrimarySelectionText(SDL_VideoDevice *_this)
135145
{
136146
SDL_VideoData *video_data = _this->internal;
137-
SDL_WaylandPrimarySelectionDevice *primary_selection_device = NULL;
147+
SDL_WaylandSeat *seat = video_data->last_incoming_primary_selection_seat;
138148
char *text = NULL;
139149
size_t length = 0;
140150

141-
if (video_data->input && video_data->input->primary_selection_device) {
142-
primary_selection_device = video_data->input->primary_selection_device;
151+
if (seat && seat->primary_selection_device) {
152+
SDL_WaylandPrimarySelectionDevice *primary_selection_device = seat->primary_selection_device;
143153
if (primary_selection_device->selection_source) {
144154
text = Wayland_primary_selection_source_get_data(primary_selection_device->selection_source, TEXT_MIME, &length);
145155
} else {
@@ -162,11 +172,11 @@ char *Wayland_GetPrimarySelectionText(SDL_VideoDevice *_this)
162172
bool Wayland_HasPrimarySelectionText(SDL_VideoDevice *_this)
163173
{
164174
SDL_VideoData *video_data = _this->internal;
165-
SDL_WaylandPrimarySelectionDevice *primary_selection_device = NULL;
175+
SDL_WaylandSeat *seat = video_data->last_incoming_primary_selection_seat;
166176
bool result = false;
167177

168-
if (video_data->input && video_data->input->primary_selection_device) {
169-
primary_selection_device = video_data->input->primary_selection_device;
178+
if (seat && seat->primary_selection_device) {
179+
SDL_WaylandPrimarySelectionDevice *primary_selection_device = seat->primary_selection_device;
170180
if (primary_selection_device->selection_source) {
171181
result = true;
172182
} else {

0 commit comments

Comments
 (0)