summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/video/x11
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/video/x11
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/x11')
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11clipboard.c329
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11clipboard.h46
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11dyn.c211
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11dyn.h113
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11events.c2205
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11events.h40
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11framebuffer.c261
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11framebuffer.h34
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11keyboard.c789
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11keyboard.h40
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11messagebox.c887
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11messagebox.h31
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11modes.c1051
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11modes.h69
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11mouse.c552
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11mouse.h40
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11opengl.c1116
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11opengl.h96
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11opengles.c152
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11opengles.h55
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11pen.c437
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11pen.h72
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11settings.c129
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11settings.h39
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11shape.c111
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11shape.h28
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11sym.h354
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11touch.c46
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11touch.h30
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11video.c505
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11video.h177
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11vulkan.c290
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11vulkan.h52
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11window.c2243
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11window.h169
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11xfixes.c214
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11xfixes.h39
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11xinput2.c829
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11xinput2.h44
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11xsync.c148
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11xsync.h39
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/edid-parse.c753
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/edid.h191
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/xsettings-client.c859
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/xsettings-client.h153
45 files changed, 16068 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11clipboard.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11clipboard.c
new file mode 100644
index 0000000..5e33555
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11clipboard.c
@@ -0,0 +1,329 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_DRIVER_X11
24
25#include <limits.h> // For INT_MAX
26
27#include "SDL_x11video.h"
28#include "SDL_x11clipboard.h"
29#include "../SDL_clipboard_c.h"
30#include "../../events/SDL_events_c.h"
31
32static const char *text_mime_types[] = {
33 "UTF8_STRING",
34 "text/plain;charset=utf-8",
35 "text/plain",
36 "TEXT",
37 "STRING"
38};
39
40// Get any application owned window handle for clipboard association
41Window GetWindow(SDL_VideoDevice *_this)
42{
43 SDL_VideoData *data = _this->internal;
44
45 /* We create an unmapped window that exists just to manage the clipboard,
46 since X11 selection data is tied to a specific window and dies with it.
47 We create the window on demand, so apps that don't use the clipboard
48 don't have to keep an unnecessary resource around. */
49 if (data->clipboard_window == None) {
50 Display *dpy = data->display;
51 Window parent = RootWindow(dpy, DefaultScreen(dpy));
52 XSetWindowAttributes xattr;
53 data->clipboard_window = X11_XCreateWindow(dpy, parent, -10, -10, 1, 1, 0,
54 CopyFromParent, InputOnly,
55 CopyFromParent, 0, &xattr);
56
57 X11_XSelectInput(dpy, data->clipboard_window, PropertyChangeMask);
58 X11_XFlush(data->display);
59 }
60
61 return data->clipboard_window;
62}
63
64static bool SetSelectionData(SDL_VideoDevice *_this, Atom selection, SDL_ClipboardDataCallback callback,
65 void *userdata, const char **mime_types, size_t mime_count, Uint32 sequence)
66{
67 SDL_VideoData *videodata = _this->internal;
68 Display *display = videodata->display;
69 Window window;
70 SDLX11_ClipboardData *clipboard;
71 bool clipboard_owner = false;
72
73 window = GetWindow(_this);
74 if (window == None) {
75 return SDL_SetError("Couldn't find a window to own the selection");
76 }
77
78 if (selection == XA_PRIMARY) {
79 clipboard = &videodata->primary_selection;
80 } else {
81 clipboard = &videodata->clipboard;
82 }
83
84 clipboard_owner = X11_XGetSelectionOwner(display, selection) == window;
85
86 // If we are canceling our own data we need to clean it up
87 if (clipboard_owner && clipboard->sequence == 0) {
88 SDL_free(clipboard->userdata);
89 }
90
91 clipboard->callback = callback;
92 clipboard->userdata = userdata;
93 clipboard->mime_types = mime_types;
94 clipboard->mime_count = mime_count;
95 clipboard->sequence = sequence;
96
97 X11_XSetSelectionOwner(display, selection, window, CurrentTime);
98 return true;
99}
100
101static void *CloneDataBuffer(const void *buffer, const size_t len)
102{
103 void *clone = NULL;
104 if (len > 0 && buffer) {
105 clone = SDL_malloc(len + sizeof(Uint32));
106 if (clone) {
107 SDL_memcpy(clone, buffer, len);
108 SDL_memset((Uint8 *)clone + len, 0, sizeof(Uint32));
109 }
110 }
111 return clone;
112}
113
114/*
115 * original_buffer is considered unusable after the function is called.
116 */
117static void *AppendDataBuffer(void *original_buffer, const size_t old_len, const void *buffer, const size_t buffer_len)
118{
119 void *resized_buffer;
120
121 if (buffer_len > 0 && buffer) {
122 resized_buffer = SDL_realloc(original_buffer, old_len + buffer_len + sizeof(Uint32));
123 if (resized_buffer) {
124 SDL_memcpy((Uint8 *)resized_buffer + old_len, buffer, buffer_len);
125 SDL_memset((Uint8 *)resized_buffer + old_len + buffer_len, 0, sizeof(Uint32));
126 }
127
128 return resized_buffer;
129 } else {
130 return original_buffer;
131 }
132}
133
134static bool WaitForSelection(SDL_VideoDevice *_this, Atom selection_type, bool *flag)
135{
136 Uint64 waitStart;
137 Uint64 waitElapsed;
138
139 waitStart = SDL_GetTicks();
140 *flag = true;
141 while (*flag) {
142 SDL_PumpEvents();
143 waitElapsed = SDL_GetTicks() - waitStart;
144 // Wait one second for a selection response.
145 if (waitElapsed > 1000) {
146 *flag = false;
147 SDL_SetError("Selection timeout");
148 /* We need to set the selection text so that next time we won't
149 timeout, otherwise we will hang on every call to this function. */
150 SetSelectionData(_this, selection_type, SDL_ClipboardTextCallback, NULL,
151 text_mime_types, SDL_arraysize(text_mime_types), 0);
152 return false;
153 }
154 }
155
156 return true;
157}
158
159static void *GetSelectionData(SDL_VideoDevice *_this, Atom selection_type,
160 const char *mime_type, size_t *length)
161{
162 SDL_VideoData *videodata = _this->internal;
163 Display *display = videodata->display;
164 Window window;
165 Window owner;
166 Atom selection;
167 Atom seln_type;
168 int seln_format;
169 unsigned long count;
170 unsigned long overflow;
171
172 SDLX11_ClipboardData *clipboard;
173 void *data = NULL;
174 unsigned char *src = NULL;
175 bool incr_success = false;
176 Atom XA_MIME = X11_XInternAtom(display, mime_type, False);
177
178 *length = 0;
179
180 // Get the window that holds the selection
181 window = GetWindow(_this);
182 owner = X11_XGetSelectionOwner(display, selection_type);
183 if (owner == None) {
184 // This requires a fallback to ancient X10 cut-buffers. We will just skip those for now
185 data = NULL;
186 } else if (owner == window) {
187 owner = DefaultRootWindow(display);
188 if (selection_type == XA_PRIMARY) {
189 clipboard = &videodata->primary_selection;
190 } else {
191 clipboard = &videodata->clipboard;
192 }
193
194 if (clipboard->callback) {
195 const void *clipboard_data = clipboard->callback(clipboard->userdata, mime_type, length);
196 data = CloneDataBuffer(clipboard_data, *length);
197 }
198 } else {
199 // Request that the selection owner copy the data to our window
200 owner = window;
201 selection = videodata->atoms.SDL_SELECTION;
202 X11_XConvertSelection(display, selection_type, XA_MIME, selection, owner,
203 CurrentTime);
204
205 if (WaitForSelection(_this, selection_type, &videodata->selection_waiting) == false) {
206 data = NULL;
207 *length = 0;
208 }
209
210 if (X11_XGetWindowProperty(display, owner, selection, 0, INT_MAX / 4, False,
211 XA_MIME, &seln_type, &seln_format, &count, &overflow, &src) == Success) {
212 if (seln_type == XA_MIME) {
213 *length = (size_t)count;
214 data = CloneDataBuffer(src, count);
215 } else if (seln_type == videodata->atoms.INCR) {
216 while (1) {
217 // Only delete the property after being done with the previous "chunk".
218 X11_XDeleteProperty(display, owner, selection);
219 X11_XFlush(display);
220
221 if (WaitForSelection(_this, selection_type, &videodata->selection_incr_waiting) == false) {
222 break;
223 }
224
225 X11_XFree(src);
226 if (X11_XGetWindowProperty(display, owner, selection, 0, INT_MAX / 4, False,
227 XA_MIME, &seln_type, &seln_format, &count, &overflow, &src) != Success) {
228 break;
229 }
230
231 if (count == 0) {
232 incr_success = true;
233 break;
234 }
235
236 if (*length == 0) {
237 *length = (size_t)count;
238 data = CloneDataBuffer(src, count);
239 } else {
240 data = AppendDataBuffer(data, *length, src, count);
241 *length += (size_t)count;
242 }
243
244 if (data == NULL) {
245 break;
246 }
247 }
248
249 if (incr_success == false) {
250 SDL_free(data);
251 data = 0;
252 *length = 0;
253 }
254 }
255 X11_XFree(src);
256 }
257 }
258 return data;
259}
260
261const char **X11_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types)
262{
263 *num_mime_types = SDL_arraysize(text_mime_types);
264 return text_mime_types;
265}
266
267bool X11_SetClipboardData(SDL_VideoDevice *_this)
268{
269 SDL_VideoData *videodata = _this->internal;
270 return SetSelectionData(_this, videodata->atoms.CLIPBOARD, _this->clipboard_callback, _this->clipboard_userdata, (const char **)_this->clipboard_mime_types, _this->num_clipboard_mime_types, _this->clipboard_sequence);
271}
272
273void *X11_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length)
274{
275 SDL_VideoData *videodata = _this->internal;
276 return GetSelectionData(_this, videodata->atoms.CLIPBOARD, mime_type, length);
277}
278
279bool X11_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
280{
281 size_t length;
282 void *data;
283 data = X11_GetClipboardData(_this, mime_type, &length);
284 if (data) {
285 SDL_free(data);
286 }
287 return length > 0;
288}
289
290bool X11_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text)
291{
292 return SetSelectionData(_this, XA_PRIMARY, SDL_ClipboardTextCallback, SDL_strdup(text), text_mime_types, SDL_arraysize(text_mime_types), 0);
293}
294
295char *X11_GetPrimarySelectionText(SDL_VideoDevice *_this)
296{
297 size_t length;
298 char *text = GetSelectionData(_this, XA_PRIMARY, text_mime_types[0], &length);
299 if (!text) {
300 text = SDL_strdup("");
301 }
302 return text;
303}
304
305bool X11_HasPrimarySelectionText(SDL_VideoDevice *_this)
306{
307 bool result = false;
308 char *text = X11_GetPrimarySelectionText(_this);
309 if (text) {
310 if (text[0] != '\0') {
311 result = true;
312 }
313 SDL_free(text);
314 }
315 return result;
316}
317
318void X11_QuitClipboard(SDL_VideoDevice *_this)
319{
320 SDL_VideoData *data = _this->internal;
321 if (data->primary_selection.sequence == 0) {
322 SDL_free(data->primary_selection.userdata);
323 }
324 if (data->clipboard.sequence == 0) {
325 SDL_free(data->clipboard.userdata);
326 }
327}
328
329#endif // SDL_VIDEO_DRIVER_X11
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11clipboard.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11clipboard.h
new file mode 100644
index 0000000..da5990a
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11clipboard.h
@@ -0,0 +1,46 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifndef SDL_x11clipboard_h_
24#define SDL_x11clipboard_h_
25
26#include <X11/Xlib.h>
27
28typedef struct X11_ClipboardData {
29 SDL_ClipboardDataCallback callback;
30 void *userdata;
31 const char **mime_types;
32 size_t mime_count;
33 Uint32 sequence;
34} SDLX11_ClipboardData;
35
36extern const char **X11_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types);
37extern bool X11_SetClipboardData(SDL_VideoDevice *_this);
38extern void *X11_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length);
39extern bool X11_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type);
40extern bool X11_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text);
41extern char *X11_GetPrimarySelectionText(SDL_VideoDevice *_this);
42extern bool X11_HasPrimarySelectionText(SDL_VideoDevice *_this);
43extern void X11_QuitClipboard(SDL_VideoDevice *_this);
44Window GetWindow(SDL_VideoDevice *_this);
45
46#endif // SDL_x11clipboard_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11dyn.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11dyn.c
new file mode 100644
index 0000000..7c48ed5
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11dyn.c
@@ -0,0 +1,211 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_DRIVER_X11
24
25#define DEBUG_DYNAMIC_X11 0
26
27#include "SDL_x11dyn.h"
28
29#if DEBUG_DYNAMIC_X11
30#include <stdio.h>
31#endif
32
33#ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC
34
35typedef struct
36{
37 SDL_SharedObject *lib;
38 const char *libname;
39} x11dynlib;
40
41#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT
42#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT NULL
43#endif
44#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XCURSOR
45#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XCURSOR NULL
46#endif
47#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2
48#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2 NULL
49#endif
50#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XFIXES
51#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XFIXES NULL
52#endif
53#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR
54#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR NULL
55#endif
56#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS
57#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS NULL
58#endif
59
60static x11dynlib x11libs[] = {
61 { NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC },
62 { NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT },
63 { NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XCURSOR },
64 { NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2 },
65 { NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XFIXES },
66 { NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR },
67 { NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS }
68};
69
70static void *X11_GetSym(const char *fnname, int *pHasModule)
71{
72 int i;
73 void *fn = NULL;
74 for (i = 0; i < SDL_arraysize(x11libs); i++) {
75 if (x11libs[i].lib) {
76 fn = SDL_LoadFunction(x11libs[i].lib, fnname);
77 if (fn) {
78 break;
79 }
80 }
81 }
82
83#if DEBUG_DYNAMIC_X11
84 if (fn)
85 printf("X11: Found '%s' in %s (%p)\n", fnname, x11libs[i].libname, fn);
86 else
87 printf("X11: Symbol '%s' NOT FOUND!\n", fnname);
88#endif
89
90 if (!fn) {
91 *pHasModule = 0; // kill this module.
92 }
93
94 return fn;
95}
96
97#endif // SDL_VIDEO_DRIVER_X11_DYNAMIC
98
99// Define all the function pointers and wrappers...
100#define SDL_X11_SYM(rc, fn, params, args, ret) SDL_DYNX11FN_##fn X11_##fn = NULL;
101#include "SDL_x11sym.h"
102
103// Annoying varargs entry point...
104#ifdef X_HAVE_UTF8_STRING
105SDL_DYNX11FN_XCreateIC X11_XCreateIC = NULL;
106SDL_DYNX11FN_XGetICValues X11_XGetICValues = NULL;
107SDL_DYNX11FN_XSetICValues X11_XSetICValues = NULL;
108SDL_DYNX11FN_XVaCreateNestedList X11_XVaCreateNestedList = NULL;
109#endif
110
111/* These SDL_X11_HAVE_* flags are here whether you have dynamic X11 or not. */
112#define SDL_X11_MODULE(modname) int SDL_X11_HAVE_##modname = 0;
113#include "SDL_x11sym.h"
114
115static int x11_load_refcount = 0;
116
117void SDL_X11_UnloadSymbols(void)
118{
119 // Don't actually unload if more than one module is using the libs...
120 if (x11_load_refcount > 0) {
121 if (--x11_load_refcount == 0) {
122#ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC
123 int i;
124#endif
125
126 // set all the function pointers to NULL.
127#define SDL_X11_MODULE(modname) SDL_X11_HAVE_##modname = 0;
128#define SDL_X11_SYM(rc, fn, params, args, ret) X11_##fn = NULL;
129#include "SDL_x11sym.h"
130
131#ifdef X_HAVE_UTF8_STRING
132 X11_XCreateIC = NULL;
133 X11_XGetICValues = NULL;
134 X11_XSetICValues = NULL;
135 X11_XVaCreateNestedList = NULL;
136#endif
137
138#ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC
139 for (i = 0; i < SDL_arraysize(x11libs); i++) {
140 if (x11libs[i].lib) {
141 SDL_UnloadObject(x11libs[i].lib);
142 x11libs[i].lib = NULL;
143 }
144 }
145#endif
146 }
147 }
148}
149
150// returns non-zero if all needed symbols were loaded.
151bool SDL_X11_LoadSymbols(void)
152{
153 bool result = true; // always succeed if not using Dynamic X11 stuff.
154
155 // deal with multiple modules (dga, x11, etc) needing these symbols...
156 if (x11_load_refcount++ == 0) {
157#ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC
158 int i;
159 int *thismod = NULL;
160 for (i = 0; i < SDL_arraysize(x11libs); i++) {
161 if (x11libs[i].libname) {
162 x11libs[i].lib = SDL_LoadObject(x11libs[i].libname);
163 }
164 }
165
166#define SDL_X11_MODULE(modname) SDL_X11_HAVE_##modname = 1; // default yes
167#include "SDL_x11sym.h"
168
169#define SDL_X11_MODULE(modname) thismod = &SDL_X11_HAVE_##modname;
170#define SDL_X11_SYM(a, fn, x, y, z) X11_##fn = (SDL_DYNX11FN_##fn)X11_GetSym(#fn, thismod);
171#include "SDL_x11sym.h"
172
173#ifdef X_HAVE_UTF8_STRING
174 X11_XCreateIC = (SDL_DYNX11FN_XCreateIC)
175 X11_GetSym("XCreateIC", &SDL_X11_HAVE_UTF8);
176 X11_XGetICValues = (SDL_DYNX11FN_XGetICValues)
177 X11_GetSym("XGetICValues", &SDL_X11_HAVE_UTF8);
178 X11_XSetICValues = (SDL_DYNX11FN_XSetICValues)
179 X11_GetSym("XSetICValues", &SDL_X11_HAVE_UTF8);
180 X11_XVaCreateNestedList = (SDL_DYNX11FN_XVaCreateNestedList)
181 X11_GetSym("XVaCreateNestedList", &SDL_X11_HAVE_UTF8);
182#endif
183
184 if (SDL_X11_HAVE_BASEXLIB) {
185 // all required symbols loaded.
186 SDL_ClearError();
187 } else {
188 // in case something got loaded...
189 SDL_X11_UnloadSymbols();
190 result = false;
191 }
192
193#else // no dynamic X11
194
195#define SDL_X11_MODULE(modname) SDL_X11_HAVE_##modname = 1; // default yes
196#define SDL_X11_SYM(a, fn, x, y, z) X11_##fn = (SDL_DYNX11FN_##fn)fn;
197#include "SDL_x11sym.h"
198
199#ifdef X_HAVE_UTF8_STRING
200 X11_XCreateIC = XCreateIC;
201 X11_XGetICValues = XGetICValues;
202 X11_XSetICValues = XSetICValues;
203 X11_XVaCreateNestedList = XVaCreateNestedList;
204#endif
205#endif
206 }
207
208 return result;
209}
210
211#endif // SDL_VIDEO_DRIVER_X11
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11dyn.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11dyn.h
new file mode 100644
index 0000000..e9831fc
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11dyn.h
@@ -0,0 +1,113 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifndef SDL_x11dyn_h_
24#define SDL_x11dyn_h_
25
26#include <X11/Xlib.h>
27#include <X11/Xutil.h>
28#include <X11/Xatom.h>
29#include <X11/Xresource.h>
30
31#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
32#include <X11/XKBlib.h>
33#endif
34
35// Apparently some X11 systems can't include this multiple times...
36#ifndef SDL_INCLUDED_XLIBINT_H
37#define SDL_INCLUDED_XLIBINT_H 1
38#include <X11/Xlibint.h>
39#endif
40
41#include <X11/Xproto.h>
42#include <X11/extensions/Xext.h>
43
44#ifndef NO_SHARED_MEMORY
45#include <sys/ipc.h>
46#include <sys/shm.h>
47#include <X11/extensions/XShm.h>
48#endif
49
50#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR
51#include <X11/Xcursor/Xcursor.h>
52#endif
53#ifdef SDL_VIDEO_DRIVER_X11_XDBE
54#include <X11/extensions/Xdbe.h>
55#endif
56#if defined(SDL_VIDEO_DRIVER_X11_XINPUT2) || defined(SDL_VIDEO_DRIVER_X11_XFIXES)
57#include <X11/extensions/XInput2.h>
58#endif
59#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
60#include <X11/extensions/Xfixes.h>
61#endif
62#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
63#include <X11/extensions/sync.h>
64#endif
65#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
66#include <X11/extensions/Xrandr.h>
67#endif
68#ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER
69#include <X11/extensions/scrnsaver.h>
70#endif
71#ifdef SDL_VIDEO_DRIVER_X11_XSHAPE
72#include <X11/extensions/shape.h>
73#endif
74
75#ifdef __cplusplus
76extern "C" {
77#endif
78
79// evil function signatures...
80typedef Bool (*SDL_X11_XESetWireToEventRetType)(Display *, XEvent *, xEvent *);
81typedef int (*SDL_X11_XSynchronizeRetType)(Display *);
82typedef Status (*SDL_X11_XESetEventToWireRetType)(Display *, XEvent *, xEvent *);
83
84extern bool SDL_X11_LoadSymbols(void);
85extern void SDL_X11_UnloadSymbols(void);
86
87// Declare all the function pointers and wrappers...
88#define SDL_X11_SYM(rc, fn, params, args, ret) \
89 typedef rc(*SDL_DYNX11FN_##fn) params; \
90 extern SDL_DYNX11FN_##fn X11_##fn;
91#include "SDL_x11sym.h"
92
93// Annoying varargs entry point...
94#ifdef X_HAVE_UTF8_STRING
95typedef XIC (*SDL_DYNX11FN_XCreateIC)(XIM, ...);
96typedef char *(*SDL_DYNX11FN_XGetICValues)(XIC, ...);
97typedef char *(*SDL_DYNX11FN_XSetICValues)(XIC, ...);
98typedef XVaNestedList (*SDL_DYNX11FN_XVaCreateNestedList)(int, ...);
99extern SDL_DYNX11FN_XCreateIC X11_XCreateIC;
100extern SDL_DYNX11FN_XGetICValues X11_XGetICValues;
101extern SDL_DYNX11FN_XSetICValues X11_XSetICValues;
102extern SDL_DYNX11FN_XVaCreateNestedList X11_XVaCreateNestedList;
103#endif
104
105/* These SDL_X11_HAVE_* flags are here whether you have dynamic X11 or not. */
106#define SDL_X11_MODULE(modname) extern int SDL_X11_HAVE_##modname;
107#include "SDL_x11sym.h"
108
109#ifdef __cplusplus
110}
111#endif
112
113#endif // !defined SDL_x11dyn_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11events.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11events.c
new file mode 100644
index 0000000..02c2d90
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11events.c
@@ -0,0 +1,2205 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_DRIVER_X11
24
25#include <sys/types.h>
26#include <sys/time.h>
27#include <signal.h>
28#include <unistd.h>
29#include <limits.h> // For INT_MAX
30
31#include "SDL_x11video.h"
32#include "SDL_x11pen.h"
33#include "SDL_x11touch.h"
34#include "SDL_x11xinput2.h"
35#include "SDL_x11xfixes.h"
36#include "SDL_x11settings.h"
37#include "../SDL_clipboard_c.h"
38#include "SDL_x11xsync.h"
39#include "../../core/unix/SDL_poll.h"
40#include "../../events/SDL_events_c.h"
41#include "../../events/SDL_mouse_c.h"
42#include "../../events/SDL_touch_c.h"
43#include "../../core/linux/SDL_system_theme.h"
44#include "../SDL_sysvideo.h"
45
46#include <stdio.h>
47
48#if 0
49#define DEBUG_XEVENTS
50#endif
51
52#ifndef _NET_WM_MOVERESIZE_SIZE_TOPLEFT
53#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
54#endif
55
56#ifndef _NET_WM_MOVERESIZE_SIZE_TOP
57#define _NET_WM_MOVERESIZE_SIZE_TOP 1
58#endif
59
60#ifndef _NET_WM_MOVERESIZE_SIZE_TOPRIGHT
61#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
62#endif
63
64#ifndef _NET_WM_MOVERESIZE_SIZE_RIGHT
65#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
66#endif
67
68#ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT
69#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
70#endif
71
72#ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOM
73#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
74#endif
75
76#ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT
77#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
78#endif
79
80#ifndef _NET_WM_MOVERESIZE_SIZE_LEFT
81#define _NET_WM_MOVERESIZE_SIZE_LEFT 7
82#endif
83
84#ifndef _NET_WM_MOVERESIZE_MOVE
85#define _NET_WM_MOVERESIZE_MOVE 8
86#endif
87
88typedef struct
89{
90 unsigned char *data;
91 int format, count;
92 Atom type;
93} SDL_x11Prop;
94
95/* Reads property
96 Must call X11_XFree on results
97 */
98static void X11_ReadProperty(SDL_x11Prop *p, Display *disp, Window w, Atom prop)
99{
100 unsigned char *ret = NULL;
101 Atom type;
102 int fmt;
103 unsigned long count;
104 unsigned long bytes_left;
105 int bytes_fetch = 0;
106
107 do {
108 if (ret) {
109 X11_XFree(ret);
110 }
111 X11_XGetWindowProperty(disp, w, prop, 0, bytes_fetch, False, AnyPropertyType, &type, &fmt, &count, &bytes_left, &ret);
112 bytes_fetch += bytes_left;
113 } while (bytes_left != 0);
114
115 p->data = ret;
116 p->format = fmt;
117 p->count = count;
118 p->type = type;
119}
120
121/* Find text-uri-list in a list of targets and return it's atom
122 if available, else return None */
123static Atom X11_PickTarget(Display *disp, Atom list[], int list_count)
124{
125 Atom request = None;
126 char *name;
127 int i;
128 for (i = 0; i < list_count && request == None; i++) {
129 name = X11_XGetAtomName(disp, list[i]);
130 // Preferred MIME targets
131 if ((SDL_strcmp("text/uri-list", name) == 0) ||
132 (SDL_strcmp("text/plain;charset=utf-8", name) == 0) ||
133 (SDL_strcmp("UTF8_STRING", name) == 0)) {
134 request = list[i];
135 }
136 // Fallback MIME targets
137 if ((SDL_strcmp("text/plain", name) == 0) ||
138 (SDL_strcmp("TEXT", name) == 0)) {
139 if (request == None) {
140 request = list[i];
141 }
142 }
143 X11_XFree(name);
144 }
145 return request;
146}
147
148/* Wrapper for X11_PickTarget for a maximum of three targets, a special
149 case in the Xdnd protocol */
150static Atom X11_PickTargetFromAtoms(Display *disp, Atom a0, Atom a1, Atom a2)
151{
152 int count = 0;
153 Atom atom[3];
154 if (a0 != None) {
155 atom[count++] = a0;
156 }
157 if (a1 != None) {
158 atom[count++] = a1;
159 }
160 if (a2 != None) {
161 atom[count++] = a2;
162 }
163 return X11_PickTarget(disp, atom, count);
164}
165
166struct KeyRepeatCheckData
167{
168 XEvent *event;
169 bool found;
170};
171
172static Bool X11_KeyRepeatCheckIfEvent(Display *display, XEvent *chkev,
173 XPointer arg)
174{
175 struct KeyRepeatCheckData *d = (struct KeyRepeatCheckData *)arg;
176 if (chkev->type == KeyPress && chkev->xkey.keycode == d->event->xkey.keycode && chkev->xkey.time - d->event->xkey.time < 2) {
177 d->found = true;
178 }
179 return False;
180}
181
182/* Check to see if this is a repeated key.
183 (idea shamelessly lifted from GII -- thanks guys! :)
184 */
185static bool X11_KeyRepeat(Display *display, XEvent *event)
186{
187 XEvent dummyev;
188 struct KeyRepeatCheckData d;
189 d.event = event;
190 d.found = false;
191 if (X11_XPending(display)) {
192 X11_XCheckIfEvent(display, &dummyev, X11_KeyRepeatCheckIfEvent, (XPointer)&d);
193 }
194 return d.found;
195}
196
197static bool X11_IsWheelEvent(Display *display, int button, int *xticks, int *yticks)
198{
199 /* according to the xlib docs, no specific mouse wheel events exist.
200 However, the defacto standard is that the vertical wheel is X buttons
201 4 (up) and 5 (down) and a horizontal wheel is 6 (left) and 7 (right). */
202
203 // Xlib defines "Button1" through 5, so we just use literals here.
204 switch (button) {
205 case 4:
206 *yticks = 1;
207 return true;
208 case 5:
209 *yticks = -1;
210 return true;
211 case 6:
212 *xticks = 1;
213 return true;
214 case 7:
215 *xticks = -1;
216 return true;
217 default:
218 break;
219 }
220 return false;
221}
222
223// An X11 event hook
224static SDL_X11EventHook g_X11EventHook = NULL;
225static void *g_X11EventHookData = NULL;
226
227void SDL_SetX11EventHook(SDL_X11EventHook callback, void *userdata)
228{
229 g_X11EventHook = callback;
230 g_X11EventHookData = userdata;
231}
232
233#ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
234static void X11_HandleGenericEvent(SDL_VideoDevice *_this, XEvent *xev)
235{
236 SDL_VideoData *videodata = _this->internal;
237
238 // event is a union, so cookie == &event, but this is type safe.
239 XGenericEventCookie *cookie = &xev->xcookie;
240 if (X11_XGetEventData(videodata->display, cookie)) {
241 if (!g_X11EventHook || g_X11EventHook(g_X11EventHookData, xev)) {
242 X11_HandleXinput2Event(_this, cookie);
243 }
244 X11_XFreeEventData(videodata->display, cookie);
245 }
246}
247#endif // SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
248
249static void X11_UpdateSystemKeyModifiers(SDL_VideoData *viddata)
250{
251 Window junk_window;
252 int x, y;
253
254 X11_XQueryPointer(viddata->display, DefaultRootWindow(viddata->display), &junk_window, &junk_window, &x, &y, &x, &y, &viddata->xkb.xkb_modifiers);
255}
256
257static void X11_ReconcileModifiers(SDL_VideoData *viddata)
258{
259 const Uint32 xk_modifiers = viddata->xkb.xkb_modifiers;
260
261 /* If a modifier was activated by a keypress, it will be tied to the
262 * specific left/right key that initiated it. Otherwise, the ambiguous
263 * left/right combo is used.
264 */
265 if (xk_modifiers & ShiftMask) {
266 if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_SHIFT)) {
267 viddata->xkb.sdl_modifiers |= SDL_KMOD_SHIFT;
268 }
269 } else {
270 viddata->xkb.sdl_modifiers &= ~SDL_KMOD_SHIFT;
271 }
272
273 if (xk_modifiers & ControlMask) {
274 if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_CTRL)) {
275 viddata->xkb.sdl_modifiers |= SDL_KMOD_CTRL;
276 }
277 } else {
278 viddata->xkb.sdl_modifiers &= ~SDL_KMOD_CTRL;
279 }
280
281 // Mod1 is used for the Alt keys
282 if (xk_modifiers & Mod1Mask) {
283 if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_ALT)) {
284 viddata->xkb.sdl_modifiers |= SDL_KMOD_ALT;
285 }
286 } else {
287 viddata->xkb.sdl_modifiers &= ~SDL_KMOD_ALT;
288 }
289
290 // Mod4 is used for the Super (aka GUI/Logo) keys.
291 if (xk_modifiers & Mod4Mask) {
292 if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_GUI)) {
293 viddata->xkb.sdl_modifiers |= SDL_KMOD_GUI;
294 }
295 } else {
296 viddata->xkb.sdl_modifiers &= ~SDL_KMOD_GUI;
297 }
298
299 // Mod3 is typically Level 5 shift.
300 if (xk_modifiers & Mod3Mask) {
301 viddata->xkb.sdl_modifiers |= SDL_KMOD_LEVEL5;
302 } else {
303 viddata->xkb.sdl_modifiers &= ~SDL_KMOD_LEVEL5;
304 }
305
306 // Mod5 is typically Level 3 shift (aka AltGr).
307 if (xk_modifiers & Mod5Mask) {
308 viddata->xkb.sdl_modifiers |= SDL_KMOD_MODE;
309 } else {
310 viddata->xkb.sdl_modifiers &= ~SDL_KMOD_MODE;
311 }
312
313 if (xk_modifiers & LockMask) {
314 viddata->xkb.sdl_modifiers |= SDL_KMOD_CAPS;
315 } else {
316 viddata->xkb.sdl_modifiers &= ~SDL_KMOD_CAPS;
317 }
318
319 if (xk_modifiers & viddata->xkb.numlock_mask) {
320 viddata->xkb.sdl_modifiers |= SDL_KMOD_NUM;
321 } else {
322 viddata->xkb.sdl_modifiers &= ~SDL_KMOD_NUM;
323 }
324
325 if (xk_modifiers & viddata->xkb.scrolllock_mask) {
326 viddata->xkb.sdl_modifiers |= SDL_KMOD_SCROLL;
327 } else {
328 viddata->xkb.sdl_modifiers &= ~SDL_KMOD_SCROLL;
329 }
330
331 SDL_SetModState(viddata->xkb.sdl_modifiers);
332}
333
334static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode, bool pressed, bool allow_reconciliation)
335{
336 const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false);
337 SDL_Keymod mod = SDL_KMOD_NONE;
338 bool reconcile = false;
339
340 /* SDL clients expect modifier state to be activated at the same time as the
341 * source keypress, so we set pressed modifier state with the usual modifier
342 * keys here, as the explicit modifier event won't arrive until after the
343 * keypress event. If this is wrong, it will be corrected when the explicit
344 * modifier state is checked.
345 */
346 switch (keycode) {
347 case SDLK_LSHIFT:
348 mod = SDL_KMOD_LSHIFT;
349 break;
350 case SDLK_RSHIFT:
351 mod = SDL_KMOD_RSHIFT;
352 break;
353 case SDLK_LCTRL:
354 mod = SDL_KMOD_LCTRL;
355 break;
356 case SDLK_RCTRL:
357 mod = SDL_KMOD_RCTRL;
358 break;
359 case SDLK_LALT:
360 mod = SDL_KMOD_LALT;
361 break;
362 case SDLK_RALT:
363 mod = SDL_KMOD_RALT;
364 break;
365 case SDLK_LGUI:
366 mod = SDL_KMOD_LGUI;
367 break;
368 case SDLK_RGUI:
369 mod = SDL_KMOD_RGUI;
370 break;
371 case SDLK_MODE:
372 mod = SDL_KMOD_MODE;
373 break;
374 case SDLK_LEVEL5_SHIFT:
375 mod = SDL_KMOD_LEVEL5;
376 break;
377 case SDLK_CAPSLOCK:
378 case SDLK_NUMLOCKCLEAR:
379 case SDLK_SCROLLLOCK:
380 {
381 /* For locking modifier keys, query the lock state directly, or we may have to wait until the next
382 * key press event to know if a lock was actually activated from the key event.
383 */
384 unsigned int cur_mask = viddata->xkb.xkb_modifiers;
385 X11_UpdateSystemKeyModifiers(viddata);
386
387 if (viddata->xkb.xkb_modifiers & LockMask) {
388 cur_mask |= LockMask;
389 } else {
390 cur_mask &= ~LockMask;
391 }
392 if (viddata->xkb.xkb_modifiers & viddata->xkb.numlock_mask) {
393 cur_mask |= viddata->xkb.numlock_mask;
394 } else {
395 cur_mask &= ~viddata->xkb.numlock_mask;
396 }
397 if (viddata->xkb.xkb_modifiers & viddata->xkb.scrolllock_mask) {
398 cur_mask |= viddata->xkb.scrolllock_mask;
399 } else {
400 cur_mask &= ~viddata->xkb.scrolllock_mask;
401 }
402
403 viddata->xkb.xkb_modifiers = cur_mask;
404 } SDL_FALLTHROUGH;
405 default:
406 reconcile = true;
407 break;
408 }
409
410 if (pressed) {
411 viddata->xkb.sdl_modifiers |= mod;
412 } else {
413 viddata->xkb.sdl_modifiers &= ~mod;
414 }
415
416 if (allow_reconciliation) {
417 if (reconcile) {
418 X11_ReconcileModifiers(viddata);
419 } else {
420 SDL_SetModState(viddata->xkb.sdl_modifiers);
421 }
422 }
423}
424
425void X11_ReconcileKeyboardState(SDL_VideoDevice *_this)
426{
427 SDL_VideoData *videodata = _this->internal;
428 Display *display = videodata->display;
429 char keys[32];
430 int keycode;
431 const bool *keyboardState;
432
433 X11_XQueryKeymap(display, keys);
434
435 keyboardState = SDL_GetKeyboardState(0);
436 for (keycode = 0; keycode < SDL_arraysize(videodata->key_layout); ++keycode) {
437 SDL_Scancode scancode = videodata->key_layout[keycode];
438 bool x11KeyPressed = (keys[keycode / 8] & (1 << (keycode % 8))) != 0;
439 bool sdlKeyPressed = keyboardState[scancode];
440
441 if (x11KeyPressed && !sdlKeyPressed) {
442 // Only update modifier state for keys that are pressed in another application
443 switch (SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false)) {
444 case SDLK_LCTRL:
445 case SDLK_RCTRL:
446 case SDLK_LSHIFT:
447 case SDLK_RSHIFT:
448 case SDLK_LALT:
449 case SDLK_RALT:
450 case SDLK_LGUI:
451 case SDLK_RGUI:
452 case SDLK_MODE:
453 case SDLK_LEVEL5_SHIFT:
454 X11_HandleModifierKeys(videodata, scancode, true, false);
455 SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, true);
456 break;
457 default:
458 break;
459 }
460 } else if (!x11KeyPressed && sdlKeyPressed) {
461 X11_HandleModifierKeys(videodata, scancode, false, false);
462 SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, false);
463 }
464 }
465
466 X11_UpdateSystemKeyModifiers(videodata);
467 X11_ReconcileModifiers(videodata);
468}
469
470static void X11_DispatchFocusIn(SDL_VideoDevice *_this, SDL_WindowData *data)
471{
472#ifdef DEBUG_XEVENTS
473 SDL_Log("window 0x%lx: Dispatching FocusIn", data->xwindow);
474#endif
475 SDL_SetKeyboardFocus(data->window);
476 X11_ReconcileKeyboardState(_this);
477#ifdef X_HAVE_UTF8_STRING
478 if (data->ic) {
479 X11_XSetICFocus(data->ic);
480 }
481#endif
482 if (data->flashing_window) {
483 X11_FlashWindow(_this, data->window, SDL_FLASH_CANCEL);
484 }
485}
486
487static void X11_DispatchFocusOut(SDL_VideoDevice *_this, SDL_WindowData *data)
488{
489#ifdef DEBUG_XEVENTS
490 SDL_Log("window 0x%lx: Dispatching FocusOut", data->xwindow);
491#endif
492 /* If another window has already processed a focus in, then don't try to
493 * remove focus here. Doing so will incorrectly remove focus from that
494 * window, and the focus lost event for this window will have already
495 * been dispatched anyway. */
496 if (data->window == SDL_GetKeyboardFocus()) {
497 SDL_SetKeyboardFocus(NULL);
498 }
499#ifdef X_HAVE_UTF8_STRING
500 if (data->ic) {
501 X11_XUnsetICFocus(data->ic);
502 }
503#endif
504}
505
506static void X11_DispatchMapNotify(SDL_WindowData *data)
507{
508 SDL_Window *window = data->window;
509 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
510 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0);
511 if (!(window->flags & SDL_WINDOW_HIDDEN) && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
512 SDL_UpdateWindowGrab(window);
513 }
514}
515
516static void X11_DispatchUnmapNotify(SDL_WindowData *data)
517{
518 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_HIDDEN, 0, 0);
519 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
520}
521
522static void DispatchWindowMove(SDL_VideoDevice *_this, const SDL_WindowData *data, const SDL_Point *point)
523{
524 SDL_VideoData *videodata = _this->internal;
525 SDL_Window *window = data->window;
526 Display *display = videodata->display;
527 XEvent evt;
528
529 // !!! FIXME: we need to regrab this if necessary when the drag is done.
530 X11_XUngrabPointer(display, 0L);
531 X11_XFlush(display);
532
533 evt.xclient.type = ClientMessage;
534 evt.xclient.window = data->xwindow;
535 evt.xclient.message_type = videodata->atoms._NET_WM_MOVERESIZE;
536 evt.xclient.format = 32;
537 evt.xclient.data.l[0] = (size_t)window->x + point->x;
538 evt.xclient.data.l[1] = (size_t)window->y + point->y;
539 evt.xclient.data.l[2] = _NET_WM_MOVERESIZE_MOVE;
540 evt.xclient.data.l[3] = Button1;
541 evt.xclient.data.l[4] = 0;
542 X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt);
543
544 X11_XSync(display, 0);
545}
546
547static void ScheduleWindowMove(SDL_VideoDevice *_this, SDL_WindowData *data, const SDL_Point *point)
548{
549 data->pending_move = true;
550 data->pending_move_point = *point;
551}
552
553static void InitiateWindowResize(SDL_VideoDevice *_this, const SDL_WindowData *data, const SDL_Point *point, int direction)
554{
555 SDL_VideoData *videodata = _this->internal;
556 SDL_Window *window = data->window;
557 Display *display = videodata->display;
558 XEvent evt;
559
560 if (direction < _NET_WM_MOVERESIZE_SIZE_TOPLEFT || direction > _NET_WM_MOVERESIZE_SIZE_LEFT) {
561 return;
562 }
563
564 // !!! FIXME: we need to regrab this if necessary when the drag is done.
565 X11_XUngrabPointer(display, 0L);
566 X11_XFlush(display);
567
568 evt.xclient.type = ClientMessage;
569 evt.xclient.window = data->xwindow;
570 evt.xclient.message_type = videodata->atoms._NET_WM_MOVERESIZE;
571 evt.xclient.format = 32;
572 evt.xclient.data.l[0] = (size_t)window->x + point->x;
573 evt.xclient.data.l[1] = (size_t)window->y + point->y;
574 evt.xclient.data.l[2] = direction;
575 evt.xclient.data.l[3] = Button1;
576 evt.xclient.data.l[4] = 0;
577 X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt);
578
579 X11_XSync(display, 0);
580}
581
582bool X11_ProcessHitTest(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y, bool force_new_result)
583{
584 SDL_Window *window = data->window;
585 if (!window->hit_test) return false;
586 const SDL_Point point = { (int)x, (int)y };
587 SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
588 if (!force_new_result && rc == data->hit_test_result) {
589 return true;
590 }
591 X11_SetHitTestCursor(rc);
592 data->hit_test_result = rc;
593 return true;
594}
595
596bool X11_TriggerHitTestAction(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y)
597{
598 SDL_Window *window = data->window;
599
600 if (window->hit_test) {
601 const SDL_Point point = { (int)x, (int)y };
602 static const int directions[] = {
603 _NET_WM_MOVERESIZE_SIZE_TOPLEFT, _NET_WM_MOVERESIZE_SIZE_TOP,
604 _NET_WM_MOVERESIZE_SIZE_TOPRIGHT, _NET_WM_MOVERESIZE_SIZE_RIGHT,
605 _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT, _NET_WM_MOVERESIZE_SIZE_BOTTOM,
606 _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT, _NET_WM_MOVERESIZE_SIZE_LEFT
607 };
608
609 switch (data->hit_test_result) {
610 case SDL_HITTEST_DRAGGABLE:
611 /* Some window managers get in a bad state when a move event starts while input is transitioning
612 to the SDL window. This can happen when clicking on a drag region of an unfocused window
613 where the same mouse down event will trigger a drag event and a window activate. */
614 if (data->window->flags & SDL_WINDOW_INPUT_FOCUS) {
615 DispatchWindowMove(_this, data, &point);
616 } else {
617 ScheduleWindowMove(_this, data, &point);
618 }
619 return true;
620
621 case SDL_HITTEST_RESIZE_TOPLEFT:
622 case SDL_HITTEST_RESIZE_TOP:
623 case SDL_HITTEST_RESIZE_TOPRIGHT:
624 case SDL_HITTEST_RESIZE_RIGHT:
625 case SDL_HITTEST_RESIZE_BOTTOMRIGHT:
626 case SDL_HITTEST_RESIZE_BOTTOM:
627 case SDL_HITTEST_RESIZE_BOTTOMLEFT:
628 case SDL_HITTEST_RESIZE_LEFT:
629 InitiateWindowResize(_this, data, &point, directions[data->hit_test_result - SDL_HITTEST_RESIZE_TOPLEFT]);
630 return true;
631
632 default:
633 return false;
634 }
635 }
636
637 return false;
638}
639
640static void X11_UpdateUserTime(SDL_WindowData *data, const unsigned long latest)
641{
642 if (latest && (latest != data->user_time)) {
643 SDL_VideoData *videodata = data->videodata;
644 Display *display = videodata->display;
645 X11_XChangeProperty(display, data->xwindow, videodata->atoms._NET_WM_USER_TIME,
646 XA_CARDINAL, 32, PropModeReplace,
647 (const unsigned char *)&latest, 1);
648#ifdef DEBUG_XEVENTS
649 SDL_Log("window 0x%lx: updating _NET_WM_USER_TIME to %lu", data->xwindow, latest);
650#endif
651 data->user_time = latest;
652 }
653}
654
655static void X11_HandleClipboardEvent(SDL_VideoDevice *_this, const XEvent *xevent)
656{
657 int i;
658 SDL_VideoData *videodata = _this->internal;
659 Display *display = videodata->display;
660
661 SDL_assert(videodata->clipboard_window != None);
662 SDL_assert(xevent->xany.window == videodata->clipboard_window);
663
664 switch (xevent->type) {
665 // Copy the selection from our own CUTBUFFER to the requested property
666 case SelectionRequest:
667 {
668 const XSelectionRequestEvent *req = &xevent->xselectionrequest;
669 XEvent sevent;
670 int mime_formats;
671 unsigned char *seln_data;
672 size_t seln_length = 0;
673 Atom XA_TARGETS = videodata->atoms.TARGETS;
674 SDLX11_ClipboardData *clipboard;
675
676#ifdef DEBUG_XEVENTS
677 char *atom_name;
678 atom_name = X11_XGetAtomName(display, req->target);
679 SDL_Log("window CLIPBOARD: SelectionRequest (requestor = 0x%lx, target = 0x%lx, mime_type = %s)",
680 req->requestor, req->target, atom_name);
681 if (atom_name) {
682 X11_XFree(atom_name);
683 }
684#endif
685
686 if (req->selection == XA_PRIMARY) {
687 clipboard = &videodata->primary_selection;
688 } else {
689 clipboard = &videodata->clipboard;
690 }
691
692 SDL_zero(sevent);
693 sevent.xany.type = SelectionNotify;
694 sevent.xselection.selection = req->selection;
695 sevent.xselection.target = None;
696 sevent.xselection.property = None; // tell them no by default
697 sevent.xselection.requestor = req->requestor;
698 sevent.xselection.time = req->time;
699
700 /* !!! FIXME: We were probably storing this on the root window
701 because an SDL window might go away...? but we don't have to do
702 this now (or ever, really). */
703
704 if (req->target == XA_TARGETS) {
705 Atom *supportedFormats;
706 supportedFormats = SDL_malloc((clipboard->mime_count + 1) * sizeof(Atom));
707 supportedFormats[0] = XA_TARGETS;
708 mime_formats = 1;
709 for (i = 0; i < clipboard->mime_count; ++i) {
710 supportedFormats[mime_formats++] = X11_XInternAtom(display, clipboard->mime_types[i], False);
711 }
712 X11_XChangeProperty(display, req->requestor, req->property,
713 XA_ATOM, 32, PropModeReplace,
714 (unsigned char *)supportedFormats,
715 mime_formats);
716 sevent.xselection.property = req->property;
717 sevent.xselection.target = XA_TARGETS;
718 SDL_free(supportedFormats);
719 } else {
720 if (clipboard->callback) {
721 for (i = 0; i < clipboard->mime_count; ++i) {
722 const char *mime_type = clipboard->mime_types[i];
723 if (X11_XInternAtom(display, mime_type, False) != req->target) {
724 continue;
725 }
726
727 // FIXME: We don't support the X11 INCR protocol for large clipboards. Do we want that? - Yes, yes we do.
728 // This is a safe cast, XChangeProperty() doesn't take a const value, but it doesn't modify the data
729 seln_data = (unsigned char *)clipboard->callback(clipboard->userdata, mime_type, &seln_length);
730 if (seln_data) {
731 X11_XChangeProperty(display, req->requestor, req->property,
732 req->target, 8, PropModeReplace,
733 seln_data, seln_length);
734 sevent.xselection.property = req->property;
735 sevent.xselection.target = req->target;
736 }
737 break;
738 }
739 }
740 }
741 X11_XSendEvent(display, req->requestor, False, 0, &sevent);
742 X11_XSync(display, False);
743 } break;
744
745 case SelectionNotify:
746 {
747 const XSelectionEvent *xsel = &xevent->xselection;
748#ifdef DEBUG_XEVENTS
749 const char *propName = xsel->property ? X11_XGetAtomName(display, xsel->property) : "None";
750 const char *targetName = xsel->target ? X11_XGetAtomName(display, xsel->target) : "None";
751
752 SDL_Log("window CLIPBOARD: SelectionNotify (requestor = 0x%lx, target = %s, property = %s)",
753 xsel->requestor, targetName, propName);
754#endif
755 if (xsel->target == videodata->atoms.TARGETS && xsel->property == videodata->atoms.SDL_FORMATS) {
756 /* the new mime formats are the SDL_FORMATS property as an array of Atoms */
757 Atom atom = None;
758 Atom *patom;
759 unsigned char* data = NULL;
760 int format_property = 0;
761 unsigned long length = 0;
762 unsigned long bytes_left = 0;
763 int j;
764
765 X11_XGetWindowProperty(display, GetWindow(_this), videodata->atoms.SDL_FORMATS, 0, 200,
766 0, XA_ATOM, &atom, &format_property, &length, &bytes_left, &data);
767
768 int allocationsize = (length + 1) * sizeof(char*);
769 for (j = 0, patom = (Atom*)data; j < length; j++, patom++) {
770 char *atomStr = X11_XGetAtomName(display, *patom);
771 allocationsize += SDL_strlen(atomStr) + 1;
772 X11_XFree(atomStr);
773 }
774
775 char **new_mime_types = SDL_AllocateTemporaryMemory(allocationsize);
776 if (new_mime_types) {
777 char *strPtr = (char *)(new_mime_types + length + 1);
778
779 for (j = 0, patom = (Atom*)data; j < length; j++, patom++) {
780 char *atomStr = X11_XGetAtomName(display, *patom);
781 new_mime_types[j] = strPtr;
782 strPtr = stpcpy(strPtr, atomStr) + 1;
783 X11_XFree(atomStr);
784 }
785 new_mime_types[length] = NULL;
786
787 SDL_SendClipboardUpdate(false, new_mime_types, length);
788 }
789
790 if (data) {
791 X11_XFree(data);
792 }
793 }
794
795 videodata->selection_waiting = false;
796 } break;
797
798 case SelectionClear:
799 {
800 Atom XA_CLIPBOARD = videodata->atoms.CLIPBOARD;
801 SDLX11_ClipboardData *clipboard = NULL;
802
803#ifdef DEBUG_XEVENTS
804 SDL_Log("window CLIPBOARD: SelectionClear (requestor = 0x%lx, target = 0x%lx)",
805 xevent->xselection.requestor, xevent->xselection.target);
806#endif
807
808 if (xevent->xselectionclear.selection == XA_PRIMARY) {
809 clipboard = &videodata->primary_selection;
810 } else if (XA_CLIPBOARD != None && xevent->xselectionclear.selection == XA_CLIPBOARD) {
811 clipboard = &videodata->clipboard;
812 }
813 if (clipboard && clipboard->callback) {
814 if (clipboard->sequence) {
815 SDL_CancelClipboardData(clipboard->sequence);
816 } else {
817 SDL_free(clipboard->userdata);
818 }
819 SDL_zerop(clipboard);
820 }
821 } break;
822
823 case PropertyNotify:
824 {
825 char *name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom);
826
827 if (SDL_strncmp(name_of_atom, "SDL_SELECTION", sizeof("SDL_SELECTION") - 1) == 0 && xevent->xproperty.state == PropertyNewValue) {
828 videodata->selection_incr_waiting = false;
829 }
830
831 if (name_of_atom) {
832 X11_XFree(name_of_atom);
833 }
834 } break;
835 }
836}
837
838static void X11_HandleSettingsEvent(SDL_VideoDevice *_this, const XEvent *xevent)
839{
840 SDL_VideoData *videodata = _this->internal;
841
842 SDL_assert(videodata->xsettings_window != None);
843 SDL_assert(xevent->xany.window == videodata->xsettings_window);
844
845 X11_HandleXsettings(_this, xevent);
846}
847
848static Bool isMapNotify(Display *display, XEvent *ev, XPointer arg)
849{
850 XUnmapEvent *unmap;
851
852 unmap = (XUnmapEvent *)arg;
853
854 return ev->type == MapNotify &&
855 ev->xmap.window == unmap->window &&
856 ev->xmap.serial == unmap->serial;
857}
858
859static Bool isReparentNotify(Display *display, XEvent *ev, XPointer arg)
860{
861 XUnmapEvent *unmap;
862
863 unmap = (XUnmapEvent *)arg;
864
865 return ev->type == ReparentNotify &&
866 ev->xreparent.window == unmap->window &&
867 ev->xreparent.serial == unmap->serial;
868}
869
870static bool IsHighLatin1(const char *string, int length)
871{
872 while (length-- > 0) {
873 Uint8 ch = (Uint8)*string;
874 if (ch >= 0x80) {
875 return true;
876 }
877 ++string;
878 }
879 return false;
880}
881
882static int XLookupStringAsUTF8(XKeyEvent *event_struct, char *buffer_return, int bytes_buffer, KeySym *keysym_return, XComposeStatus *status_in_out)
883{
884 int result = X11_XLookupString(event_struct, buffer_return, bytes_buffer, keysym_return, status_in_out);
885 if (IsHighLatin1(buffer_return, result)) {
886 char *utf8_text = SDL_iconv_string("UTF-8", "ISO-8859-1", buffer_return, result + 1);
887 if (utf8_text) {
888 SDL_strlcpy(buffer_return, utf8_text, bytes_buffer);
889 SDL_free(utf8_text);
890 return SDL_strlen(buffer_return);
891 } else {
892 return 0;
893 }
894 }
895 return result;
896}
897
898SDL_WindowData *X11_FindWindow(SDL_VideoDevice *_this, Window window)
899{
900 const SDL_VideoData *videodata = _this->internal;
901 int i;
902
903 if (videodata && videodata->windowlist) {
904 for (i = 0; i < videodata->numwindows; ++i) {
905 if ((videodata->windowlist[i] != NULL) &&
906 (videodata->windowlist[i]->xwindow == window)) {
907 return videodata->windowlist[i];
908 }
909 }
910 }
911 return NULL;
912}
913
914Uint64 X11_GetEventTimestamp(unsigned long time)
915{
916 // FIXME: Get the event time in the SDL tick time base
917 return SDL_GetTicksNS();
918}
919
920void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_KeyboardID keyboardID, XEvent *xevent)
921{
922 SDL_VideoData *videodata = _this->internal;
923 Display *display = videodata->display;
924 KeyCode keycode = xevent->xkey.keycode;
925 KeySym keysym = NoSymbol;
926 int text_length = 0;
927 char text[64];
928 Status status = 0;
929 bool handled_by_ime = false;
930 bool pressed = (xevent->type == KeyPress);
931 SDL_Scancode scancode = videodata->key_layout[keycode];
932 Uint64 timestamp = X11_GetEventTimestamp(xevent->xkey.time);
933
934#ifdef DEBUG_XEVENTS
935 SDL_Log("window 0x%lx %s (X11 keycode = 0x%X)", xevent->xany.window, (xevent->type == KeyPress ? "KeyPress" : "KeyRelease"), xevent->xkey.keycode);
936#endif
937#ifdef DEBUG_SCANCODES
938 if (scancode == SDL_SCANCODE_UNKNOWN && keycode) {
939 int min_keycode, max_keycode;
940 X11_XDisplayKeycodes(display, &min_keycode, &max_keycode);
941 keysym = X11_KeyCodeToSym(_this, keycode, xevent->xkey.state >> 13);
942 SDL_Log("The key you just pressed is not recognized by SDL. To help get this fixed, please report this to the SDL forums/mailing list <https://discourse.libsdl.org/> X11 KeyCode %d (%d), X11 KeySym 0x%lX (%s).",
943 keycode, keycode - min_keycode, keysym,
944 X11_XKeysymToString(keysym));
945 }
946#endif // DEBUG SCANCODES
947
948 text[0] = '\0';
949 videodata->xkb.xkb_modifiers = xevent->xkey.state;
950
951 if (SDL_TextInputActive(windowdata->window)) {
952 // filter events catches XIM events and sends them to the correct handler
953 if (X11_XFilterEvent(xevent, None)) {
954#ifdef DEBUG_XEVENTS
955 SDL_Log("Filtered event type = %d display = %p window = 0x%lx",
956 xevent->type, xevent->xany.display, xevent->xany.window);
957#endif
958 handled_by_ime = true;
959 }
960
961 if (!handled_by_ime) {
962#ifdef X_HAVE_UTF8_STRING
963 if (windowdata->ic && xevent->type == KeyPress) {
964 text_length = X11_Xutf8LookupString(windowdata->ic, &xevent->xkey, text, sizeof(text) - 1,
965 &keysym, &status);
966 } else {
967 text_length = XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text) - 1, &keysym, NULL);
968 }
969#else
970 text_length = XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text) - 1, &keysym, NULL);
971#endif
972 }
973 }
974
975 if (!handled_by_ime) {
976 if (pressed) {
977 X11_HandleModifierKeys(videodata, scancode, true, true);
978 SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, true);
979
980 if (*text) {
981 text[text_length] = '\0';
982 X11_ClearComposition(windowdata);
983 SDL_SendKeyboardText(text);
984 }
985 } else {
986 if (X11_KeyRepeat(display, xevent)) {
987 // We're about to get a repeated key down, ignore the key up
988 return;
989 }
990
991 X11_HandleModifierKeys(videodata, scancode, false, true);
992 SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, false);
993 }
994 }
995
996 if (pressed) {
997 X11_UpdateUserTime(windowdata, xevent->xkey.time);
998 }
999}
1000
1001void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time)
1002{
1003 SDL_Window *window = windowdata->window;
1004 const SDL_VideoData *videodata = _this->internal;
1005 Display *display = videodata->display;
1006 int xticks = 0, yticks = 0;
1007 Uint64 timestamp = X11_GetEventTimestamp(time);
1008
1009#ifdef DEBUG_XEVENTS
1010 SDL_Log("window 0x%lx: ButtonPress (X11 button = %d)", windowdata->xwindow, button);
1011#endif
1012
1013 SDL_Mouse *mouse = SDL_GetMouse();
1014 if (!mouse->relative_mode && (x != mouse->x || y != mouse->y)) {
1015 X11_ProcessHitTest(_this, windowdata, x, y, false);
1016 SDL_SendMouseMotion(timestamp, window, mouseID, false, x, y);
1017 }
1018
1019 if (X11_IsWheelEvent(display, button, &xticks, &yticks)) {
1020 SDL_SendMouseWheel(timestamp, window, mouseID, (float)-xticks, (float)yticks, SDL_MOUSEWHEEL_NORMAL);
1021 } else {
1022 bool ignore_click = false;
1023 if (button > 7) {
1024 /* X button values 4-7 are used for scrolling, so X1 is 8, X2 is 9, ...
1025 => subtract (8-SDL_BUTTON_X1) to get value SDL expects */
1026 button -= (8 - SDL_BUTTON_X1);
1027 }
1028 if (button == Button1) {
1029 if (X11_TriggerHitTestAction(_this, windowdata, x, y)) {
1030 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_HIT_TEST, 0, 0);
1031 return; // don't pass this event on to app.
1032 }
1033 }
1034 if (windowdata->last_focus_event_time) {
1035 const int X11_FOCUS_CLICK_TIMEOUT = 10;
1036 if (SDL_GetTicks() < (windowdata->last_focus_event_time + X11_FOCUS_CLICK_TIMEOUT)) {
1037 ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, false);
1038 }
1039 windowdata->last_focus_event_time = 0;
1040 }
1041 if (!ignore_click) {
1042 SDL_SendMouseButton(timestamp, window, mouseID, button, true);
1043 }
1044 }
1045 X11_UpdateUserTime(windowdata, time);
1046}
1047
1048void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, unsigned long time)
1049{
1050 SDL_Window *window = windowdata->window;
1051 const SDL_VideoData *videodata = _this->internal;
1052 Display *display = videodata->display;
1053 // The X server sends a Release event for each Press for wheels. Ignore them.
1054 int xticks = 0, yticks = 0;
1055 Uint64 timestamp = X11_GetEventTimestamp(time);
1056
1057#ifdef DEBUG_XEVENTS
1058 SDL_Log("window 0x%lx: ButtonRelease (X11 button = %d)", windowdata->xwindow, button);
1059#endif
1060 if (!X11_IsWheelEvent(display, button, &xticks, &yticks)) {
1061 if (button > 7) {
1062 // see explanation at case ButtonPress
1063 button -= (8 - SDL_BUTTON_X1);
1064 }
1065 SDL_SendMouseButton(timestamp, window, mouseID, button, false);
1066 }
1067}
1068
1069void X11_GetBorderValues(SDL_WindowData *data)
1070{
1071 SDL_VideoData *videodata = data->videodata;
1072 Display *display = videodata->display;
1073
1074 Atom type;
1075 int format;
1076 unsigned long nitems, bytes_after;
1077 unsigned char *property;
1078
1079 // Some compositors will send extents even when the border hint is turned off. Ignore them in this case.
1080 if (!(data->window->flags & SDL_WINDOW_BORDERLESS)) {
1081 if (X11_XGetWindowProperty(display, data->xwindow, videodata->atoms._NET_FRAME_EXTENTS, 0, 16, 0, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &property) == Success) {
1082 if (type != None && nitems == 4) {
1083 data->border_left = (int)((long *)property)[0];
1084 data->border_right = (int)((long *)property)[1];
1085 data->border_top = (int)((long *)property)[2];
1086 data->border_bottom = (int)((long *)property)[3];
1087 }
1088 X11_XFree(property);
1089
1090#ifdef DEBUG_XEVENTS
1091 SDL_Log("New _NET_FRAME_EXTENTS: left=%d right=%d, top=%d, bottom=%d", data->border_left, data->border_right, data->border_top, data->border_bottom);
1092#endif
1093 }
1094 } else {
1095 data->border_left = data->border_top = data->border_right = data->border_bottom = 0;
1096 }
1097}
1098
1099static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
1100{
1101 SDL_VideoData *videodata = _this->internal;
1102 Display *display;
1103 SDL_WindowData *data;
1104 XClientMessageEvent m;
1105 int i;
1106
1107 SDL_assert(videodata != NULL);
1108 display = videodata->display;
1109
1110 // filter events catches XIM events and sends them to the correct handler
1111 // Key press/release events are filtered in X11_HandleKeyEvent()
1112 if (xevent->type != KeyPress && xevent->type != KeyRelease) {
1113 if (X11_XFilterEvent(xevent, None)) {
1114#ifdef DEBUG_XEVENTS
1115 SDL_Log("Filtered event type = %d display = %p window = 0x%lx",
1116 xevent->type, xevent->xany.display, xevent->xany.window);
1117#endif
1118 return;
1119 }
1120 }
1121
1122#ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
1123 if (xevent->type == GenericEvent) {
1124 X11_HandleGenericEvent(_this, xevent);
1125 return;
1126 }
1127#endif
1128
1129 // Calling the event hook for generic events happens in X11_HandleGenericEvent(), where the event data is available
1130 if (g_X11EventHook) {
1131 if (!g_X11EventHook(g_X11EventHookData, xevent)) {
1132 return;
1133 }
1134 }
1135
1136#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
1137 if (videodata->xrandr_event_base && (xevent->type == (videodata->xrandr_event_base + RRNotify))) {
1138 X11_HandleXRandREvent(_this, xevent);
1139 }
1140#endif
1141
1142#ifdef DEBUG_XEVENTS
1143 SDL_Log("X11 event type = %d display = %p window = 0x%lx",
1144 xevent->type, xevent->xany.display, xevent->xany.window);
1145#endif
1146
1147#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
1148 if (SDL_X11_HAVE_XFIXES &&
1149 xevent->type == X11_GetXFixesSelectionNotifyEvent()) {
1150 XFixesSelectionNotifyEvent *ev = (XFixesSelectionNotifyEvent *)xevent;
1151
1152#ifdef DEBUG_XEVENTS
1153 SDL_Log("window CLIPBOARD: XFixesSelectionNotify (selection = %s)",
1154 X11_XGetAtomName(display, ev->selection));
1155#endif
1156
1157 if (ev->subtype == XFixesSetSelectionOwnerNotify)
1158 {
1159 if (ev->selection != videodata->atoms.CLIPBOARD)
1160 return;
1161
1162 if (X11_XGetSelectionOwner(display, ev->selection) == videodata->clipboard_window)
1163 return;
1164
1165 /* when here we're notified that the clipboard had an external change, we request the
1166 * available mime types by asking for a conversion to the TARGETS format. We should get a
1167 * SelectionNotify event later, and when treating these results, we will push a ClipboardUpdated
1168 * event
1169 */
1170
1171 X11_XConvertSelection(display, videodata->atoms.CLIPBOARD, videodata->atoms.TARGETS,
1172 videodata->atoms.SDL_FORMATS, GetWindow(_this), CurrentTime);
1173 }
1174
1175 return;
1176 }
1177#endif // SDL_VIDEO_DRIVER_X11_XFIXES
1178
1179 if ((videodata->clipboard_window != None) &&
1180 (videodata->clipboard_window == xevent->xany.window)) {
1181 X11_HandleClipboardEvent(_this, xevent);
1182 return;
1183 }
1184
1185 if ((videodata->xsettings_window != None) &&
1186 (videodata->xsettings_window == xevent->xany.window)) {
1187 X11_HandleSettingsEvent(_this, xevent);
1188 return;
1189 }
1190
1191 data = X11_FindWindow(_this, xevent->xany.window);
1192
1193 if (!data) {
1194 // The window for KeymapNotify, etc events is 0
1195 if (xevent->type == KeymapNotify) {
1196#ifdef DEBUG_XEVENTS
1197 SDL_Log("window 0x%lx: KeymapNotify!", xevent->xany.window);
1198#endif
1199 if (SDL_GetKeyboardFocus() != NULL) {
1200#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
1201 if (videodata->xkb.desc_ptr) {
1202 XkbStateRec state;
1203 if (X11_XkbGetState(videodata->display, XkbUseCoreKbd, &state) == Success) {
1204 if (state.group != videodata->xkb.current_group) {
1205 // Only rebuild the keymap if the layout has changed.
1206 videodata->xkb.current_group = state.group;
1207 X11_UpdateKeymap(_this, true);
1208 }
1209 }
1210 }
1211#endif
1212 X11_ReconcileKeyboardState(_this);
1213 }
1214 } else if (xevent->type == MappingNotify) {
1215 // Has the keyboard layout changed?
1216 const int request = xevent->xmapping.request;
1217
1218#ifdef DEBUG_XEVENTS
1219 SDL_Log("window 0x%lx: MappingNotify!", xevent->xany.window);
1220#endif
1221 if ((request == MappingKeyboard) || (request == MappingModifier)) {
1222 X11_XRefreshKeyboardMapping(&xevent->xmapping);
1223 }
1224
1225 X11_UpdateKeymap(_this, true);
1226 } else if (xevent->type == PropertyNotify && videodata && videodata->windowlist) {
1227 char *name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom);
1228
1229 if (SDL_strncmp(name_of_atom, "_ICC_PROFILE", sizeof("_ICC_PROFILE") - 1) == 0) {
1230 XWindowAttributes attrib;
1231 int screennum;
1232 for (i = 0; i < videodata->numwindows; ++i) {
1233 if (videodata->windowlist[i] != NULL) {
1234 data = videodata->windowlist[i];
1235 X11_XGetWindowAttributes(display, data->xwindow, &attrib);
1236 screennum = X11_XScreenNumberOfScreen(attrib.screen);
1237 if (screennum == 0 && SDL_strcmp(name_of_atom, "_ICC_PROFILE") == 0) {
1238 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0);
1239 } else if (SDL_strncmp(name_of_atom, "_ICC_PROFILE_", sizeof("_ICC_PROFILE_") - 1) == 0 && SDL_strlen(name_of_atom) > sizeof("_ICC_PROFILE_") - 1) {
1240 int iccscreennum = SDL_atoi(&name_of_atom[sizeof("_ICC_PROFILE_") - 1]);
1241
1242 if (screennum == iccscreennum) {
1243 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0);
1244 }
1245 }
1246 }
1247 }
1248 }
1249
1250 if (name_of_atom) {
1251 X11_XFree(name_of_atom);
1252 }
1253 }
1254 return;
1255 }
1256
1257 switch (xevent->type) {
1258
1259 // Gaining mouse coverage?
1260 case EnterNotify:
1261 {
1262 SDL_Mouse *mouse = SDL_GetMouse();
1263#ifdef DEBUG_XEVENTS
1264 SDL_Log("window 0x%lx: EnterNotify! (%d,%d,%d)", xevent->xany.window,
1265 xevent->xcrossing.x,
1266 xevent->xcrossing.y,
1267 xevent->xcrossing.mode);
1268 if (xevent->xcrossing.mode == NotifyGrab) {
1269 SDL_Log("Mode: NotifyGrab");
1270 }
1271 if (xevent->xcrossing.mode == NotifyUngrab) {
1272 SDL_Log("Mode: NotifyUngrab");
1273 }
1274#endif
1275 SDL_SetMouseFocus(data->window);
1276
1277 mouse->last_x = xevent->xcrossing.x;
1278 mouse->last_y = xevent->xcrossing.y;
1279
1280#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
1281 {
1282 // Only create the barriers if we have input focus
1283 SDL_WindowData *windowdata = data->window->internal;
1284 if ((data->pointer_barrier_active == true) && windowdata->window->flags & SDL_WINDOW_INPUT_FOCUS) {
1285 X11_ConfineCursorWithFlags(_this, windowdata->window, &windowdata->barrier_rect, X11_BARRIER_HANDLED_BY_EVENT);
1286 }
1287 }
1288#endif
1289
1290 if (!mouse->relative_mode) {
1291 SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xcrossing.x, (float)xevent->xcrossing.y);
1292 }
1293
1294 // We ungrab in LeaveNotify, so we may need to grab again here
1295 SDL_UpdateWindowGrab(data->window);
1296
1297 X11_ProcessHitTest(_this, data, mouse->last_x, mouse->last_y, true);
1298 } break;
1299 // Losing mouse coverage?
1300 case LeaveNotify:
1301 {
1302#ifdef DEBUG_XEVENTS
1303 SDL_Log("window 0x%lx: LeaveNotify! (%d,%d,%d)", xevent->xany.window,
1304 xevent->xcrossing.x,
1305 xevent->xcrossing.y,
1306 xevent->xcrossing.mode);
1307 if (xevent->xcrossing.mode == NotifyGrab) {
1308 SDL_Log("Mode: NotifyGrab");
1309 }
1310 if (xevent->xcrossing.mode == NotifyUngrab) {
1311 SDL_Log("Mode: NotifyUngrab");
1312 }
1313#endif
1314 if (!SDL_GetMouse()->relative_mode) {
1315 SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xcrossing.x, (float)xevent->xcrossing.y);
1316 }
1317
1318 if (xevent->xcrossing.mode != NotifyGrab &&
1319 xevent->xcrossing.mode != NotifyUngrab &&
1320 xevent->xcrossing.detail != NotifyInferior) {
1321
1322 /* In order for interaction with the window decorations and menu to work properly
1323 on Mutter, we need to ungrab the keyboard when the the mouse leaves. */
1324 if (!(data->window->flags & SDL_WINDOW_FULLSCREEN)) {
1325 X11_SetWindowKeyboardGrab(_this, data->window, false);
1326 }
1327
1328 SDL_SetMouseFocus(NULL);
1329 }
1330 } break;
1331
1332 // Gaining input focus?
1333 case FocusIn:
1334 {
1335 if (xevent->xfocus.mode == NotifyGrab || xevent->xfocus.mode == NotifyUngrab) {
1336 // Someone is handling a global hotkey, ignore it
1337#ifdef DEBUG_XEVENTS
1338 SDL_Log("window 0x%lx: FocusIn (NotifyGrab/NotifyUngrab, ignoring)", xevent->xany.window);
1339#endif
1340 break;
1341 }
1342
1343 if (xevent->xfocus.detail == NotifyInferior || xevent->xfocus.detail == NotifyPointer) {
1344#ifdef DEBUG_XEVENTS
1345 SDL_Log("window 0x%lx: FocusIn (NotifyInferior/NotifyPointer, ignoring)", xevent->xany.window);
1346#endif
1347 break;
1348 }
1349#ifdef DEBUG_XEVENTS
1350 SDL_Log("window 0x%lx: FocusIn!", xevent->xany.window);
1351#endif
1352 if (!videodata->last_mode_change_deadline) /* no recent mode changes */ {
1353 data->pending_focus = PENDING_FOCUS_NONE;
1354 data->pending_focus_time = 0;
1355 X11_DispatchFocusIn(_this, data);
1356 } else {
1357 data->pending_focus = PENDING_FOCUS_IN;
1358 data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME;
1359 }
1360 data->last_focus_event_time = SDL_GetTicks();
1361 } break;
1362
1363 // Losing input focus?
1364 case FocusOut:
1365 {
1366 if (xevent->xfocus.mode == NotifyGrab || xevent->xfocus.mode == NotifyUngrab) {
1367 // Someone is handling a global hotkey, ignore it
1368#ifdef DEBUG_XEVENTS
1369 SDL_Log("window 0x%lx: FocusOut (NotifyGrab/NotifyUngrab, ignoring)", xevent->xany.window);
1370#endif
1371 break;
1372 }
1373 if (xevent->xfocus.detail == NotifyInferior || xevent->xfocus.detail == NotifyPointer) {
1374 /* We still have focus if a child gets focus. We also don't
1375 care about the position of the pointer when the keyboard
1376 focus changed. */
1377#ifdef DEBUG_XEVENTS
1378 SDL_Log("window 0x%lx: FocusOut (NotifyInferior/NotifyPointer, ignoring)", xevent->xany.window);
1379#endif
1380 break;
1381 }
1382#ifdef DEBUG_XEVENTS
1383 SDL_Log("window 0x%lx: FocusOut!", xevent->xany.window);
1384#endif
1385 if (!videodata->last_mode_change_deadline) /* no recent mode changes */ {
1386 data->pending_focus = PENDING_FOCUS_NONE;
1387 data->pending_focus_time = 0;
1388 X11_DispatchFocusOut(_this, data);
1389 } else {
1390 data->pending_focus = PENDING_FOCUS_OUT;
1391 data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME;
1392 }
1393
1394#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
1395 // Disable confinement if it is activated.
1396 if (data->pointer_barrier_active == true) {
1397 X11_ConfineCursorWithFlags(_this, data->window, NULL, X11_BARRIER_HANDLED_BY_EVENT);
1398 }
1399#endif // SDL_VIDEO_DRIVER_X11_XFIXES
1400 } break;
1401
1402
1403 // Have we been iconified?
1404 case UnmapNotify:
1405 {
1406 XEvent ev;
1407
1408#ifdef DEBUG_XEVENTS
1409 SDL_Log("window 0x%lx: UnmapNotify!", xevent->xany.window);
1410#endif
1411
1412 if (X11_XCheckIfEvent(display, &ev, &isReparentNotify, (XPointer)&xevent->xunmap)) {
1413 X11_XCheckIfEvent(display, &ev, &isMapNotify, (XPointer)&xevent->xunmap);
1414 } else {
1415 X11_DispatchUnmapNotify(data);
1416 }
1417
1418#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
1419 // Disable confinement if the window gets hidden.
1420 if (data->pointer_barrier_active == true) {
1421 X11_ConfineCursorWithFlags(_this, data->window, NULL, X11_BARRIER_HANDLED_BY_EVENT);
1422 }
1423#endif // SDL_VIDEO_DRIVER_X11_XFIXES
1424 } break;
1425
1426 // Have we been restored?
1427 case MapNotify:
1428 {
1429#ifdef DEBUG_XEVENTS
1430 SDL_Log("window 0x%lx: MapNotify!", xevent->xany.window);
1431#endif
1432 X11_DispatchMapNotify(data);
1433
1434#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
1435 // Enable confinement if it was activated.
1436 if (data->pointer_barrier_active == true) {
1437 X11_ConfineCursorWithFlags(_this, data->window, &data->barrier_rect, X11_BARRIER_HANDLED_BY_EVENT);
1438 }
1439#endif // SDL_VIDEO_DRIVER_X11_XFIXES
1440 } break;
1441
1442 // Have we been resized or moved?
1443 case ConfigureNotify:
1444 {
1445#ifdef DEBUG_XEVENTS
1446 SDL_Log("window 0x%lx: ConfigureNotify! (position: %d,%d, size: %dx%d)", xevent->xany.window,
1447 xevent->xconfigure.x, xevent->xconfigure.y,
1448 xevent->xconfigure.width, xevent->xconfigure.height);
1449#endif
1450 // Real configure notify events are relative to the parent, synthetic events are absolute.
1451 if (!xevent->xconfigure.send_event)
1452 {
1453 unsigned int NumChildren;
1454 Window ChildReturn, Root, Parent;
1455 Window *Children;
1456 // Translate these coordinates back to relative to root
1457 X11_XQueryTree(data->videodata->display, xevent->xconfigure.window, &Root, &Parent, &Children, &NumChildren);
1458 X11_XTranslateCoordinates(xevent->xconfigure.display,
1459 Parent, DefaultRootWindow(xevent->xconfigure.display),
1460 xevent->xconfigure.x, xevent->xconfigure.y,
1461 &xevent->xconfigure.x, &xevent->xconfigure.y,
1462 &ChildReturn);
1463 }
1464
1465 if (xevent->xconfigure.x != data->last_xconfigure.x ||
1466 xevent->xconfigure.y != data->last_xconfigure.y) {
1467 if (!data->size_move_event_flags) {
1468 SDL_Window *w;
1469 int x = xevent->xconfigure.x;
1470 int y = xevent->xconfigure.y;
1471
1472 data->pending_operation &= ~X11_PENDING_OP_MOVE;
1473 SDL_GlobalToRelativeForWindow(data->window, x, y, &x, &y);
1474 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED, x, y);
1475
1476 for (w = data->window->first_child; w; w = w->next_sibling) {
1477 // Don't update hidden child popup windows, their relative position doesn't change
1478 if (SDL_WINDOW_IS_POPUP(w) && !(w->flags & SDL_WINDOW_HIDDEN)) {
1479 X11_UpdateWindowPosition(w, true);
1480 }
1481 }
1482 }
1483 }
1484
1485#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
1486 X11_HandleConfigure(data->window, &xevent->xconfigure);
1487#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
1488
1489 if (xevent->xconfigure.width != data->last_xconfigure.width ||
1490 xevent->xconfigure.height != data->last_xconfigure.height) {
1491 if (!data->size_move_event_flags) {
1492 data->pending_operation &= ~X11_PENDING_OP_RESIZE;
1493 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESIZED,
1494 xevent->xconfigure.width,
1495 xevent->xconfigure.height);
1496 }
1497 }
1498
1499 data->last_xconfigure = xevent->xconfigure;
1500 } break;
1501
1502 // Have we been requested to quit (or another client message?)
1503 case ClientMessage:
1504 {
1505 static int xdnd_version = 0;
1506
1507 if (xevent->xclient.message_type == videodata->atoms.XdndEnter) {
1508
1509 bool use_list = xevent->xclient.data.l[1] & 1;
1510 data->xdnd_source = xevent->xclient.data.l[0];
1511 xdnd_version = (xevent->xclient.data.l[1] >> 24);
1512#ifdef DEBUG_XEVENTS
1513 SDL_Log("XID of source window : 0x%lx", data->xdnd_source);
1514 SDL_Log("Protocol version to use : %d", xdnd_version);
1515 SDL_Log("More then 3 data types : %d", (int)use_list);
1516#endif
1517
1518 if (use_list) {
1519 // fetch conversion targets
1520 SDL_x11Prop p;
1521 X11_ReadProperty(&p, display, data->xdnd_source, videodata->atoms.XdndTypeList);
1522 // pick one
1523 data->xdnd_req = X11_PickTarget(display, (Atom *)p.data, p.count);
1524 X11_XFree(p.data);
1525 } else {
1526 // pick from list of three
1527 data->xdnd_req = X11_PickTargetFromAtoms(display, xevent->xclient.data.l[2], xevent->xclient.data.l[3], xevent->xclient.data.l[4]);
1528 }
1529 } else if (xevent->xclient.message_type == videodata->atoms.XdndLeave) {
1530#ifdef DEBUG_XEVENTS
1531 SDL_Log("XID of source window : 0x%lx", xevent->xclient.data.l[0]);
1532#endif
1533 SDL_SendDropComplete(data->window);
1534 } else if (xevent->xclient.message_type == videodata->atoms.XdndPosition) {
1535
1536#ifdef DEBUG_XEVENTS
1537 Atom act = videodata->atoms.XdndActionCopy;
1538 if (xdnd_version >= 2) {
1539 act = xevent->xclient.data.l[4];
1540 }
1541 SDL_Log("Action requested by user is : %s", X11_XGetAtomName(display, act));
1542#endif
1543 {
1544 // Drag and Drop position
1545 int root_x, root_y, window_x, window_y;
1546 Window ChildReturn;
1547 root_x = xevent->xclient.data.l[2] >> 16;
1548 root_y = xevent->xclient.data.l[2] & 0xffff;
1549 // Translate from root to current window position
1550 X11_XTranslateCoordinates(display, DefaultRootWindow(display), data->xwindow,
1551 root_x, root_y, &window_x, &window_y, &ChildReturn);
1552
1553 SDL_SendDropPosition(data->window, (float)window_x, (float)window_y);
1554 }
1555
1556 // reply with status
1557 SDL_memset(&m, 0, sizeof(XClientMessageEvent));
1558 m.type = ClientMessage;
1559 m.display = xevent->xclient.display;
1560 m.window = xevent->xclient.data.l[0];
1561 m.message_type = videodata->atoms.XdndStatus;
1562 m.format = 32;
1563 m.data.l[0] = data->xwindow;
1564 m.data.l[1] = (data->xdnd_req != None);
1565 m.data.l[2] = 0; // specify an empty rectangle
1566 m.data.l[3] = 0;
1567 m.data.l[4] = videodata->atoms.XdndActionCopy; // we only accept copying anyway
1568
1569 X11_XSendEvent(display, xevent->xclient.data.l[0], False, NoEventMask, (XEvent *)&m);
1570 X11_XFlush(display);
1571 } else if (xevent->xclient.message_type == videodata->atoms.XdndDrop) {
1572 if (data->xdnd_req == None) {
1573 // say again - not interested!
1574 SDL_memset(&m, 0, sizeof(XClientMessageEvent));
1575 m.type = ClientMessage;
1576 m.display = xevent->xclient.display;
1577 m.window = xevent->xclient.data.l[0];
1578 m.message_type = videodata->atoms.XdndFinished;
1579 m.format = 32;
1580 m.data.l[0] = data->xwindow;
1581 m.data.l[1] = 0;
1582 m.data.l[2] = None; // fail!
1583 X11_XSendEvent(display, xevent->xclient.data.l[0], False, NoEventMask, (XEvent *)&m);
1584 } else {
1585 // convert
1586 if (xdnd_version >= 1) {
1587 X11_XConvertSelection(display, videodata->atoms.XdndSelection, data->xdnd_req, videodata->atoms.PRIMARY, data->xwindow, xevent->xclient.data.l[2]);
1588 } else {
1589 X11_XConvertSelection(display, videodata->atoms.XdndSelection, data->xdnd_req, videodata->atoms.PRIMARY, data->xwindow, CurrentTime);
1590 }
1591 }
1592 } else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) &&
1593 (xevent->xclient.format == 32) &&
1594 (xevent->xclient.data.l[0] == videodata->atoms._NET_WM_PING)) {
1595 Window root = DefaultRootWindow(display);
1596
1597#ifdef DEBUG_XEVENTS
1598 SDL_Log("window 0x%lx: _NET_WM_PING", xevent->xany.window);
1599#endif
1600 xevent->xclient.window = root;
1601 X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, xevent);
1602 break;
1603 }
1604
1605 else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) &&
1606 (xevent->xclient.format == 32) &&
1607 (xevent->xclient.data.l[0] == videodata->atoms.WM_DELETE_WINDOW)) {
1608
1609#ifdef DEBUG_XEVENTS
1610 SDL_Log("window 0x%lx: WM_DELETE_WINDOW", xevent->xany.window);
1611#endif
1612 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_CLOSE_REQUESTED, 0, 0);
1613 break;
1614 } else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) &&
1615 (xevent->xclient.format == 32) &&
1616 (xevent->xclient.data.l[0] == videodata->atoms._NET_WM_SYNC_REQUEST)) {
1617
1618#ifdef DEBUG_XEVENTS
1619 printf("window %p: _NET_WM_SYNC_REQUEST\n", data);
1620#endif
1621#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
1622 X11_HandleSyncRequest(data->window, &xevent->xclient);
1623#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
1624 break;
1625 }
1626 } break;
1627
1628 // Do we need to refresh ourselves?
1629 case Expose:
1630 {
1631#ifdef DEBUG_XEVENTS
1632 SDL_Log("window 0x%lx: Expose (count = %d)", xevent->xany.window, xevent->xexpose.count);
1633#endif
1634 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_EXPOSED, 0, 0);
1635 } break;
1636
1637 /* Use XInput2 instead of the xevents API if possible, for:
1638 - KeyPress
1639 - KeyRelease
1640 - MotionNotify
1641 - ButtonPress
1642 - ButtonRelease
1643 XInput2 has more precise information, e.g., to distinguish different input devices. */
1644 case KeyPress:
1645 case KeyRelease:
1646 {
1647 if (data->xinput2_keyboard_enabled) {
1648 // This input is being handled by XInput2
1649 break;
1650 }
1651
1652 X11_HandleKeyEvent(_this, data, SDL_GLOBAL_KEYBOARD_ID, xevent);
1653 } break;
1654
1655 case MotionNotify:
1656 {
1657 if (data->xinput2_mouse_enabled && !data->mouse_grabbed) {
1658 // This input is being handled by XInput2
1659 break;
1660 }
1661
1662 SDL_Mouse *mouse = SDL_GetMouse();
1663 if (!mouse->relative_mode) {
1664#ifdef DEBUG_MOTION
1665 SDL_Log("window 0x%lx: X11 motion: %d,%d", xevent->xany.window, xevent->xmotion.x, xevent->xmotion.y);
1666#endif
1667
1668 X11_ProcessHitTest(_this, data, (float)xevent->xmotion.x, (float)xevent->xmotion.y, false);
1669 SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xmotion.x, (float)xevent->xmotion.y);
1670 }
1671 } break;
1672
1673 case ButtonPress:
1674 {
1675 if (data->xinput2_mouse_enabled) {
1676 // This input is being handled by XInput2
1677 break;
1678 }
1679
1680 X11_HandleButtonPress(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button,
1681 xevent->xbutton.x, xevent->xbutton.y, xevent->xbutton.time);
1682 } break;
1683
1684 case ButtonRelease:
1685 {
1686 if (data->xinput2_mouse_enabled) {
1687 // This input is being handled by XInput2
1688 break;
1689 }
1690
1691 X11_HandleButtonRelease(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button, xevent->xbutton.time);
1692 } break;
1693
1694 case PropertyNotify:
1695 {
1696#ifdef DEBUG_XEVENTS
1697 unsigned char *propdata;
1698 int status, real_format;
1699 Atom real_type;
1700 unsigned long items_read, items_left;
1701
1702 char *name = X11_XGetAtomName(display, xevent->xproperty.atom);
1703 if (name) {
1704 SDL_Log("window 0x%lx: PropertyNotify: %s %s time=%lu", xevent->xany.window, name, (xevent->xproperty.state == PropertyDelete) ? "deleted" : "changed", xevent->xproperty.time);
1705 X11_XFree(name);
1706 }
1707
1708 status = X11_XGetWindowProperty(display, data->xwindow, xevent->xproperty.atom, 0L, 8192L, False, AnyPropertyType, &real_type, &real_format, &items_read, &items_left, &propdata);
1709 if (status == Success && items_read > 0) {
1710 if (real_type == XA_INTEGER) {
1711 int *values = (int *)propdata;
1712
1713 SDL_Log("{");
1714 for (i = 0; i < items_read; i++) {
1715 SDL_Log(" %d", values[i]);
1716 }
1717 SDL_Log(" }");
1718 } else if (real_type == XA_CARDINAL) {
1719 if (real_format == 32) {
1720 Uint32 *values = (Uint32 *)propdata;
1721
1722 SDL_Log("{");
1723 for (i = 0; i < items_read; i++) {
1724 SDL_Log(" %d", values[i]);
1725 }
1726 SDL_Log(" }");
1727 } else if (real_format == 16) {
1728 Uint16 *values = (Uint16 *)propdata;
1729
1730 SDL_Log("{");
1731 for (i = 0; i < items_read; i++) {
1732 SDL_Log(" %d", values[i]);
1733 }
1734 SDL_Log(" }");
1735 } else if (real_format == 8) {
1736 Uint8 *values = (Uint8 *)propdata;
1737
1738 SDL_Log("{");
1739 for (i = 0; i < items_read; i++) {
1740 SDL_Log(" %d", values[i]);
1741 }
1742 SDL_Log(" }");
1743 }
1744 } else if (real_type == XA_STRING ||
1745 real_type == videodata->atoms.UTF8_STRING) {
1746 SDL_Log("{ \"%s\" }", propdata);
1747 } else if (real_type == XA_ATOM) {
1748 Atom *atoms = (Atom *)propdata;
1749
1750 SDL_Log("{");
1751 for (i = 0; i < items_read; i++) {
1752 char *atomname = X11_XGetAtomName(display, atoms[i]);
1753 if (atomname) {
1754 SDL_Log(" %s", atomname);
1755 X11_XFree(atomname);
1756 }
1757 }
1758 SDL_Log(" }");
1759 } else {
1760 char *atomname = X11_XGetAtomName(display, real_type);
1761 SDL_Log("Unknown type: 0x%lx (%s)", real_type, atomname ? atomname : "UNKNOWN");
1762 if (atomname) {
1763 X11_XFree(atomname);
1764 }
1765 }
1766 }
1767 if (status == Success) {
1768 X11_XFree(propdata);
1769 }
1770#endif // DEBUG_XEVENTS
1771
1772 /* Take advantage of this moment to make sure user_time has a
1773 valid timestamp from the X server, so if we later try to
1774 raise/restore this window, _NET_ACTIVE_WINDOW can have a
1775 non-zero timestamp, even if there's never been a mouse or
1776 key press to this window so far. Note that we don't try to
1777 set _NET_WM_USER_TIME here, though. That's only for legit
1778 user interaction with the window. */
1779 if (!data->user_time) {
1780 data->user_time = xevent->xproperty.time;
1781 }
1782
1783 if (xevent->xproperty.atom == data->videodata->atoms._NET_WM_STATE) {
1784 /* Get the new state from the window manager.
1785 Compositing window managers can alter visibility of windows
1786 without ever mapping / unmapping them, so we handle that here,
1787 because they use the NETWM protocol to notify us of changes.
1788 */
1789 const SDL_WindowFlags flags = X11_GetNetWMState(_this, data->window, xevent->xproperty.window);
1790 const SDL_WindowFlags changed = flags ^ data->window->flags;
1791
1792 if ((changed & (SDL_WINDOW_HIDDEN | SDL_WINDOW_FULLSCREEN)) != 0) {
1793 if (flags & SDL_WINDOW_HIDDEN) {
1794 X11_DispatchUnmapNotify(data);
1795 } else {
1796 X11_DispatchMapNotify(data);
1797 }
1798 }
1799
1800 if (!SDL_WINDOW_IS_POPUP(data->window)) {
1801 if (changed & SDL_WINDOW_FULLSCREEN) {
1802 data->pending_operation &= ~X11_PENDING_OP_FULLSCREEN;
1803
1804 if (flags & SDL_WINDOW_FULLSCREEN) {
1805 if (!(flags & SDL_WINDOW_MINIMIZED)) {
1806 const bool commit = SDL_memcmp(&data->window->current_fullscreen_mode, &data->requested_fullscreen_mode, sizeof(SDL_DisplayMode)) != 0;
1807
1808 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0);
1809 if (commit) {
1810 /* This was initiated by the compositor, or the mode was changed between the request and the window
1811 * becoming fullscreen. Switch to the application requested mode if necessary.
1812 */
1813 SDL_copyp(&data->window->current_fullscreen_mode, &data->window->requested_fullscreen_mode);
1814 SDL_UpdateFullscreenMode(data->window, SDL_FULLSCREEN_OP_UPDATE, true);
1815 } else {
1816 SDL_UpdateFullscreenMode(data->window, SDL_FULLSCREEN_OP_ENTER, false);
1817 }
1818 }
1819 } else {
1820 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0);
1821 SDL_UpdateFullscreenMode(data->window, false, false);
1822
1823 SDL_zero(data->requested_fullscreen_mode);
1824
1825 // Need to restore or update any limits changed while the window was fullscreen.
1826 X11_SetWindowMinMax(data->window, !!(flags & SDL_WINDOW_MAXIMIZED));
1827
1828 // Toggle the borders if they were forced on while creating a borderless fullscreen window.
1829 if (data->fullscreen_borders_forced_on) {
1830 data->toggle_borders = true;
1831 data->fullscreen_borders_forced_on = false;
1832 }
1833 }
1834
1835 if ((flags & SDL_WINDOW_FULLSCREEN) &&
1836 (data->border_top || data->border_left || data->border_bottom || data->border_right)) {
1837 /* If the window is entering fullscreen and the borders are
1838 * non-zero sized, turn off size events until the borders are
1839 * shut off to avoid bogus window sizes and positions, and
1840 * note that the old borders were non-zero for restoration.
1841 */
1842 data->size_move_event_flags |= X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS;
1843 data->previous_borders_nonzero = true;
1844 } else if (!(flags & SDL_WINDOW_FULLSCREEN) &&
1845 data->previous_borders_nonzero &&
1846 (!data->border_top && !data->border_left && !data->border_bottom && !data->border_right)) {
1847 /* If the window is leaving fullscreen and the current borders
1848 * are zero sized, but weren't when entering fullscreen, turn
1849 * off size events until the borders come back to avoid bogus
1850 * window sizes and positions.
1851 */
1852 data->size_move_event_flags |= X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS;
1853 data->previous_borders_nonzero = false;
1854 } else {
1855 data->size_move_event_flags = 0;
1856 data->previous_borders_nonzero = false;
1857
1858 if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) {
1859 data->toggle_borders = false;
1860 X11_SetWindowBordered(_this, data->window, !(data->window->flags & SDL_WINDOW_BORDERLESS));
1861 }
1862 }
1863 }
1864 if ((changed & SDL_WINDOW_MAXIMIZED) && ((flags & SDL_WINDOW_MAXIMIZED) && !(flags & SDL_WINDOW_MINIMIZED))) {
1865 data->pending_operation &= ~X11_PENDING_OP_MAXIMIZE;
1866 if ((changed & SDL_WINDOW_MINIMIZED)) {
1867 data->pending_operation &= ~X11_PENDING_OP_RESTORE;
1868 // If coming out of minimized, send a restore event before sending maximized.
1869 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
1870 }
1871 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
1872 }
1873 if ((changed & SDL_WINDOW_MINIMIZED) && (flags & SDL_WINDOW_MINIMIZED)) {
1874 data->pending_operation &= ~X11_PENDING_OP_MINIMIZE;
1875 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
1876 }
1877 if (!(flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) {
1878 data->pending_operation &= ~X11_PENDING_OP_RESTORE;
1879 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
1880
1881 // Apply any pending state if restored.
1882 if (!(flags & SDL_WINDOW_FULLSCREEN)) {
1883 if (data->pending_position) {
1884 data->pending_position = false;
1885 data->pending_operation |= X11_PENDING_OP_MOVE;
1886 data->expected.x = data->window->pending.x - data->border_left;
1887 data->expected.y = data->window->pending.y - data->border_top;
1888 X11_XMoveWindow(display, data->xwindow, data->expected.x, data->expected.y);
1889 }
1890 if (data->pending_size) {
1891 data->pending_size = false;
1892 data->pending_operation |= X11_PENDING_OP_RESIZE;
1893 data->expected.w = data->window->pending.w;
1894 data->expected.h = data->window->pending.h;
1895 X11_XResizeWindow(display, data->xwindow, data->window->pending.w, data->window->pending.h);
1896 }
1897 }
1898 }
1899 if ((flags & SDL_WINDOW_INPUT_FOCUS)) {
1900 if (data->pending_move) {
1901 DispatchWindowMove(_this, data, &data->pending_move_point);
1902 data->pending_move = false;
1903 }
1904 }
1905 }
1906 if (changed & SDL_WINDOW_OCCLUDED) {
1907 SDL_SendWindowEvent(data->window, (flags & SDL_WINDOW_OCCLUDED) ? SDL_EVENT_WINDOW_OCCLUDED : SDL_EVENT_WINDOW_EXPOSED, 0, 0);
1908 }
1909 } else if (xevent->xproperty.atom == videodata->atoms.XKLAVIER_STATE) {
1910 /* Hack for Ubuntu 12.04 (etc) that doesn't send MappingNotify
1911 events when the keyboard layout changes (for example,
1912 changing from English to French on the menubar's keyboard
1913 icon). Since it changes the XKLAVIER_STATE property, we
1914 notice and reinit our keymap here. This might not be the
1915 right approach, but it seems to work. */
1916 X11_UpdateKeymap(_this, true);
1917 } else if (xevent->xproperty.atom == videodata->atoms._NET_FRAME_EXTENTS) {
1918 /* Events are disabled when leaving fullscreen until the borders appear to avoid
1919 * incorrect size/position events.
1920 */
1921 if (data->size_move_event_flags) {
1922 data->size_move_event_flags &= ~X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS;
1923 X11_GetBorderValues(data);
1924
1925 }
1926 if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) {
1927 data->toggle_borders = false;
1928 X11_SetWindowBordered(_this, data->window, !(data->window->flags & SDL_WINDOW_BORDERLESS));
1929 }
1930 }
1931 } break;
1932
1933 case SelectionNotify:
1934 {
1935 Atom target = xevent->xselection.target;
1936#ifdef DEBUG_XEVENTS
1937 SDL_Log("window 0x%lx: SelectionNotify (requestor = 0x%lx, target = 0x%lx)", xevent->xany.window,
1938 xevent->xselection.requestor, xevent->xselection.target);
1939#endif
1940 if (target == data->xdnd_req) {
1941 // read data
1942 SDL_x11Prop p;
1943 X11_ReadProperty(&p, display, data->xwindow, videodata->atoms.PRIMARY);
1944
1945 if (p.format == 8) {
1946 char *saveptr = NULL;
1947 char *name = X11_XGetAtomName(display, target);
1948 if (name) {
1949 char *token = SDL_strtok_r((char *)p.data, "\r\n", &saveptr);
1950 while (token) {
1951 if ((SDL_strcmp("text/plain;charset=utf-8", name) == 0) ||
1952 (SDL_strcmp("UTF8_STRING", name) == 0) ||
1953 (SDL_strcmp("text/plain", name) == 0) ||
1954 (SDL_strcmp("TEXT", name) == 0)) {
1955 SDL_SendDropText(data->window, token);
1956 } else if (SDL_strcmp("text/uri-list", name) == 0) {
1957 if (SDL_URIToLocal(token, token) >= 0) {
1958 SDL_SendDropFile(data->window, NULL, token);
1959 }
1960 }
1961 token = SDL_strtok_r(NULL, "\r\n", &saveptr);
1962 }
1963 X11_XFree(name);
1964 }
1965 SDL_SendDropComplete(data->window);
1966 }
1967 X11_XFree(p.data);
1968
1969 // send reply
1970 SDL_memset(&m, 0, sizeof(XClientMessageEvent));
1971 m.type = ClientMessage;
1972 m.display = display;
1973 m.window = data->xdnd_source;
1974 m.message_type = videodata->atoms.XdndFinished;
1975 m.format = 32;
1976 m.data.l[0] = data->xwindow;
1977 m.data.l[1] = 1;
1978 m.data.l[2] = videodata->atoms.XdndActionCopy;
1979 X11_XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent *)&m);
1980
1981 X11_XSync(display, False);
1982 }
1983 } break;
1984
1985 default:
1986 {
1987#ifdef DEBUG_XEVENTS
1988 SDL_Log("window 0x%lx: Unhandled event %d", xevent->xany.window, xevent->type);
1989#endif
1990 } break;
1991 }
1992}
1993
1994static void X11_HandleFocusChanges(SDL_VideoDevice *_this)
1995{
1996 SDL_VideoData *videodata = _this->internal;
1997 int i;
1998
1999 if (videodata && videodata->windowlist) {
2000 for (i = 0; i < videodata->numwindows; ++i) {
2001 SDL_WindowData *data = videodata->windowlist[i];
2002 if (data && data->pending_focus != PENDING_FOCUS_NONE) {
2003 Uint64 now = SDL_GetTicks();
2004 if (now >= data->pending_focus_time) {
2005 if (data->pending_focus == PENDING_FOCUS_IN) {
2006 X11_DispatchFocusIn(_this, data);
2007 } else {
2008 X11_DispatchFocusOut(_this, data);
2009 }
2010 data->pending_focus = PENDING_FOCUS_NONE;
2011 }
2012 }
2013 }
2014 }
2015}
2016
2017static Bool isAnyEvent(Display *display, XEvent *ev, XPointer arg)
2018{
2019 return True;
2020}
2021
2022static bool X11_PollEvent(Display *display, XEvent *event)
2023{
2024 if (!X11_XCheckIfEvent(display, event, isAnyEvent, NULL)) {
2025 return false;
2026 }
2027
2028 return true;
2029}
2030
2031void X11_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window)
2032{
2033 SDL_VideoData *data = _this->internal;
2034 Display *req_display = data->request_display;
2035 Window xwindow = window->internal->xwindow;
2036 XClientMessageEvent event;
2037
2038 SDL_memset(&event, 0, sizeof(XClientMessageEvent));
2039 event.type = ClientMessage;
2040 event.display = req_display;
2041 event.send_event = True;
2042 event.message_type = data->atoms._SDL_WAKEUP;
2043 event.format = 8;
2044
2045 X11_XSendEvent(req_display, xwindow, False, NoEventMask, (XEvent *)&event);
2046 /* XSendEvent returns a status and it could be BadValue or BadWindow. If an
2047 error happens it is an SDL's internal error and there is nothing we can do here. */
2048 X11_XFlush(req_display);
2049}
2050
2051int X11_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS)
2052{
2053 SDL_VideoData *videodata = _this->internal;
2054 Display *display;
2055 XEvent xevent;
2056 display = videodata->display;
2057
2058 SDL_zero(xevent);
2059
2060 // Flush and poll to grab any events already read and queued
2061 X11_XFlush(display);
2062 if (X11_PollEvent(display, &xevent)) {
2063 // Fall through
2064 } else if (timeoutNS == 0) {
2065 return 0;
2066 } else {
2067 // Use SDL_IOR_NO_RETRY to ensure SIGINT will break us out of our wait
2068 int err = SDL_IOReady(ConnectionNumber(display), SDL_IOR_READ | SDL_IOR_NO_RETRY, timeoutNS);
2069 if (err > 0) {
2070 if (!X11_PollEvent(display, &xevent)) {
2071 /* Someone may have beat us to reading the fd. Return 1 here to
2072 * trigger the normal spurious wakeup logic in the event core. */
2073 return 1;
2074 }
2075 } else if (err == 0) {
2076 // Timeout
2077 return 0;
2078 } else {
2079 // Error returned from poll()/select()
2080
2081 if (errno == EINTR) {
2082 /* If the wait was interrupted by a signal, we may have generated a
2083 * SDL_EVENT_QUIT event. Let the caller know to call SDL_PumpEvents(). */
2084 return 1;
2085 } else {
2086 return err;
2087 }
2088 }
2089 }
2090
2091 X11_DispatchEvent(_this, &xevent);
2092
2093#ifdef SDL_USE_LIBDBUS
2094 SDL_DBus_PumpEvents();
2095#endif
2096 return 1;
2097}
2098
2099void X11_PumpEvents(SDL_VideoDevice *_this)
2100{
2101 SDL_VideoData *data = _this->internal;
2102 XEvent xevent;
2103 int i;
2104
2105 /* Check if a display had the mode changed and is waiting for a window to asynchronously become
2106 * fullscreen. If there is no fullscreen window past the elapsed timeout, revert the mode switch.
2107 */
2108 for (i = 0; i < _this->num_displays; ++i) {
2109 if (_this->displays[i]->internal->mode_switch_deadline_ns) {
2110 if (_this->displays[i]->fullscreen_window) {
2111 _this->displays[i]->internal->mode_switch_deadline_ns = 0;
2112 } else if (SDL_GetTicksNS() >= _this->displays[i]->internal->mode_switch_deadline_ns) {
2113 SDL_LogError(SDL_LOG_CATEGORY_VIDEO,
2114 "Time out elapsed after mode switch on display %" SDL_PRIu32 " with no window becoming fullscreen; reverting", _this->displays[i]->id);
2115 SDL_SetDisplayModeForDisplay(_this->displays[i], NULL);
2116 }
2117 }
2118 }
2119
2120 if (data->last_mode_change_deadline) {
2121 if (SDL_GetTicks() >= data->last_mode_change_deadline) {
2122 data->last_mode_change_deadline = 0; // assume we're done.
2123 }
2124 }
2125
2126 // Update activity every 30 seconds to prevent screensaver
2127 if (_this->suspend_screensaver) {
2128 Uint64 now = SDL_GetTicks();
2129 if (!data->screensaver_activity || now >= (data->screensaver_activity + 30000)) {
2130 X11_XResetScreenSaver(data->display);
2131
2132#ifdef SDL_USE_LIBDBUS
2133 SDL_DBus_ScreensaverTickle();
2134#endif
2135
2136 data->screensaver_activity = now;
2137 }
2138 }
2139
2140 SDL_zero(xevent);
2141
2142 // Keep processing pending events
2143 while (X11_PollEvent(data->display, &xevent)) {
2144 X11_DispatchEvent(_this, &xevent);
2145 }
2146
2147#ifdef SDL_USE_LIBDBUS
2148 SDL_DBus_PumpEvents();
2149#endif
2150
2151 // FIXME: Only need to do this when there are pending focus changes
2152 X11_HandleFocusChanges(_this);
2153
2154 // FIXME: Only need to do this when there are flashing windows
2155 for (i = 0; i < data->numwindows; ++i) {
2156 if (data->windowlist[i] != NULL &&
2157 data->windowlist[i]->flash_cancel_time &&
2158 SDL_GetTicks() >= data->windowlist[i]->flash_cancel_time) {
2159 X11_FlashWindow(_this, data->windowlist[i]->window, SDL_FLASH_CANCEL);
2160 }
2161 }
2162
2163 if (data->xinput_hierarchy_changed) {
2164 X11_Xinput2UpdateDevices(_this, false);
2165 data->xinput_hierarchy_changed = false;
2166 }
2167}
2168
2169bool X11_SuspendScreenSaver(SDL_VideoDevice *_this)
2170{
2171#ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER
2172 SDL_VideoData *data = _this->internal;
2173 int dummy;
2174 int major_version, minor_version;
2175#endif // SDL_VIDEO_DRIVER_X11_XSCRNSAVER
2176
2177#ifdef SDL_USE_LIBDBUS
2178 if (SDL_DBus_ScreensaverInhibit(_this->suspend_screensaver)) {
2179 return true;
2180 }
2181
2182 if (_this->suspend_screensaver) {
2183 SDL_DBus_ScreensaverTickle();
2184 }
2185#endif
2186
2187#ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER
2188 if (SDL_X11_HAVE_XSS) {
2189 // X11_XScreenSaverSuspend was introduced in MIT-SCREEN-SAVER 1.1
2190 if (!X11_XScreenSaverQueryExtension(data->display, &dummy, &dummy) ||
2191 !X11_XScreenSaverQueryVersion(data->display,
2192 &major_version, &minor_version) ||
2193 major_version < 1 || (major_version == 1 && minor_version < 1)) {
2194 return SDL_Unsupported();
2195 }
2196
2197 X11_XScreenSaverSuspend(data->display, _this->suspend_screensaver);
2198 X11_XResetScreenSaver(data->display);
2199 return true;
2200 }
2201#endif
2202 return SDL_Unsupported();
2203}
2204
2205#endif // SDL_VIDEO_DRIVER_X11
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11events.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11events.h
new file mode 100644
index 0000000..bb76f83
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11events.h
@@ -0,0 +1,40 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifndef SDL_x11events_h_
24#define SDL_x11events_h_
25
26extern void X11_PumpEvents(SDL_VideoDevice *_this);
27extern int X11_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS);
28extern void X11_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window);
29extern bool X11_SuspendScreenSaver(SDL_VideoDevice *_this);
30extern void X11_ReconcileKeyboardState(SDL_VideoDevice *_this);
31extern void X11_GetBorderValues(SDL_WindowData *data);
32extern Uint64 X11_GetEventTimestamp(unsigned long time);
33extern void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_KeyboardID keyboardID, XEvent *xevent);
34extern void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time);
35extern void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, unsigned long time);
36extern SDL_WindowData *X11_FindWindow(SDL_VideoDevice *_this, Window window);
37extern bool X11_ProcessHitTest(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y, bool force_new_result);
38extern bool X11_TriggerHitTestAction(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y);
39
40#endif // SDL_x11events_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11framebuffer.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11framebuffer.c
new file mode 100644
index 0000000..12642cc
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11framebuffer.c
@@ -0,0 +1,261 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_DRIVER_X11
24
25#include "SDL_x11video.h"
26#include "SDL_x11framebuffer.h"
27#include "SDL_x11xsync.h"
28
29#ifndef NO_SHARED_MEMORY
30
31// Shared memory error handler routine
32static int shm_error;
33static int (*X_handler)(Display *, XErrorEvent *) = NULL;
34static int shm_errhandler(Display *d, XErrorEvent *e)
35{
36 if (e->error_code == BadAccess) {
37 shm_error = True;
38 return 0;
39 }
40 return X_handler(d, e);
41}
42
43static bool have_mitshm(Display *dpy)
44{
45 // Only use shared memory on local X servers
46 return X11_XShmQueryExtension(dpy) ? SDL_X11_HAVE_SHM : false;
47}
48
49#endif // !NO_SHARED_MEMORY
50
51bool X11_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, SDL_PixelFormat *format,
52 void **pixels, int *pitch)
53{
54 SDL_WindowData *data = window->internal;
55 Display *display = data->videodata->display;
56 XGCValues gcv;
57 XVisualInfo vinfo;
58 int w, h;
59
60 SDL_GetWindowSizeInPixels(window, &w, &h);
61
62 // Free the old framebuffer surface
63 X11_DestroyWindowFramebuffer(_this, window);
64
65 // Create the graphics context for drawing
66 gcv.graphics_exposures = False;
67 data->gc = X11_XCreateGC(display, data->xwindow, GCGraphicsExposures, &gcv);
68 if (!data->gc) {
69 return SDL_SetError("Couldn't create graphics context");
70 }
71
72 // Find out the pixel format and depth
73 if (!X11_GetVisualInfoFromVisual(display, data->visual, &vinfo)) {
74 return SDL_SetError("Couldn't get window visual information");
75 }
76
77 *format = X11_GetPixelFormatFromVisualInfo(display, &vinfo);
78 if (*format == SDL_PIXELFORMAT_UNKNOWN) {
79 return SDL_SetError("Unknown window pixel format");
80 }
81
82 // Calculate pitch
83 *pitch = (((w * SDL_BYTESPERPIXEL(*format)) + 3) & ~3);
84
85 // Create the actual image
86#ifndef NO_SHARED_MEMORY
87 if (have_mitshm(display)) {
88 XShmSegmentInfo *shminfo = &data->shminfo;
89
90 shminfo->shmid = shmget(IPC_PRIVATE, (size_t)h * (*pitch), IPC_CREAT | 0777);
91 if (shminfo->shmid >= 0) {
92 shminfo->shmaddr = (char *)shmat(shminfo->shmid, 0, 0);
93 shminfo->readOnly = False;
94 if (shminfo->shmaddr != (char *)-1) {
95 shm_error = False;
96 X_handler = X11_XSetErrorHandler(shm_errhandler);
97 X11_XShmAttach(display, shminfo);
98 X11_XSync(display, False);
99 X11_XSetErrorHandler(X_handler);
100 if (shm_error) {
101 shmdt(shminfo->shmaddr);
102 }
103 } else {
104 shm_error = True;
105 }
106 shmctl(shminfo->shmid, IPC_RMID, NULL);
107 } else {
108 shm_error = True;
109 }
110 if (!shm_error) {
111 data->ximage = X11_XShmCreateImage(display, data->visual,
112 vinfo.depth, ZPixmap,
113 shminfo->shmaddr, shminfo,
114 w, h);
115 if (!data->ximage) {
116 X11_XShmDetach(display, shminfo);
117 X11_XSync(display, False);
118 shmdt(shminfo->shmaddr);
119 } else {
120 // Done!
121 data->ximage->byte_order = (SDL_BYTEORDER == SDL_BIG_ENDIAN) ? MSBFirst : LSBFirst;
122 data->use_mitshm = true;
123 *pixels = shminfo->shmaddr;
124 return true;
125 }
126 }
127 }
128#endif // not NO_SHARED_MEMORY
129
130 *pixels = SDL_malloc((size_t)h * (*pitch));
131 if (!*pixels) {
132 return false;
133 }
134
135 data->ximage = X11_XCreateImage(display, data->visual,
136 vinfo.depth, ZPixmap, 0, (char *)(*pixels),
137 w, h, 32, 0);
138 if (!data->ximage) {
139 SDL_free(*pixels);
140 return SDL_SetError("Couldn't create XImage");
141 }
142 data->ximage->byte_order = (SDL_BYTEORDER == SDL_BIG_ENDIAN) ? MSBFirst : LSBFirst;
143 return true;
144}
145
146bool X11_UpdateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rects,
147 int numrects)
148{
149 SDL_WindowData *data = window->internal;
150 Display *display = data->videodata->display;
151 int i;
152 int x, y, w, h;
153 int window_w, window_h;
154
155 SDL_GetWindowSizeInPixels(window, &window_w, &window_h);
156
157#ifndef NO_SHARED_MEMORY
158 if (data->use_mitshm) {
159 for (i = 0; i < numrects; ++i) {
160 x = rects[i].x;
161 y = rects[i].y;
162 w = rects[i].w;
163 h = rects[i].h;
164
165 if (w <= 0 || h <= 0 || (x + w) <= 0 || (y + h) <= 0) {
166 // Clipped?
167 continue;
168 }
169 if (x < 0) {
170 x += w;
171 w += rects[i].x;
172 }
173 if (y < 0) {
174 y += h;
175 h += rects[i].y;
176 }
177 if (x + w > window_w) {
178 w = window_w - x;
179 }
180 if (y + h > window_h) {
181 h = window_h - y;
182 }
183
184 X11_XShmPutImage(display, data->xwindow, data->gc, data->ximage,
185 x, y, x, y, w, h, False);
186 }
187 } else
188#endif // !NO_SHARED_MEMORY
189 {
190 for (i = 0; i < numrects; ++i) {
191 x = rects[i].x;
192 y = rects[i].y;
193 w = rects[i].w;
194 h = rects[i].h;
195
196 if (w <= 0 || h <= 0 || (x + w) <= 0 || (y + h) <= 0) {
197 // Clipped?
198 continue;
199 }
200 if (x < 0) {
201 x += w;
202 w += rects[i].x;
203 }
204 if (y < 0) {
205 y += h;
206 h += rects[i].y;
207 }
208 if (x + w > window_w) {
209 w = window_w - x;
210 }
211 if (y + h > window_h) {
212 h = window_h - y;
213 }
214
215 X11_XPutImage(display, data->xwindow, data->gc, data->ximage,
216 x, y, x, y, w, h);
217 }
218 }
219
220#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
221 X11_HandlePresent(data->window);
222#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
223
224 X11_XSync(display, False);
225
226 return true;
227}
228
229void X11_DestroyWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window)
230{
231 SDL_WindowData *data = window->internal;
232 Display *display;
233
234 if (!data) {
235 // The window wasn't fully initialized
236 return;
237 }
238
239 display = data->videodata->display;
240
241 if (data->ximage) {
242 XDestroyImage(data->ximage);
243
244#ifndef NO_SHARED_MEMORY
245 if (data->use_mitshm) {
246 X11_XShmDetach(display, &data->shminfo);
247 X11_XSync(display, False);
248 shmdt(data->shminfo.shmaddr);
249 data->use_mitshm = false;
250 }
251#endif // !NO_SHARED_MEMORY
252
253 data->ximage = NULL;
254 }
255 if (data->gc) {
256 X11_XFreeGC(display, data->gc);
257 data->gc = NULL;
258 }
259}
260
261#endif // SDL_VIDEO_DRIVER_X11
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11framebuffer.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11framebuffer.h
new file mode 100644
index 0000000..08feda4
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11framebuffer.h
@@ -0,0 +1,34 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22#ifndef SDL_x11framebuffer_h_
23#define SDL_x11framebuffer_h_
24
25#include "SDL_internal.h"
26
27extern bool X11_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window,
28 SDL_PixelFormat *format,
29 void **pixels, int *pitch);
30extern bool X11_UpdateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window,
31 const SDL_Rect *rects, int numrects);
32extern void X11_DestroyWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window);
33
34#endif // SDL_x11framebuffer_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11keyboard.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11keyboard.c
new file mode 100644
index 0000000..c48e829
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11keyboard.c
@@ -0,0 +1,789 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_DRIVER_X11
24
25#include "SDL_x11video.h"
26
27#include "../../events/SDL_keyboard_c.h"
28#include "../../events/SDL_scancode_tables_c.h"
29
30#include <X11/keysym.h>
31#include <X11/XKBlib.h>
32
33#include "../../events/imKStoUCS.h"
34#include "../../events/SDL_keysym_to_scancode_c.h"
35#include "../../events/SDL_keysym_to_keycode_c.h"
36
37#ifdef X_HAVE_UTF8_STRING
38#include <locale.h>
39#endif
40
41static SDL_ScancodeTable scancode_set[] = {
42 SDL_SCANCODE_TABLE_DARWIN,
43 SDL_SCANCODE_TABLE_XFREE86_1,
44 SDL_SCANCODE_TABLE_XFREE86_2,
45 SDL_SCANCODE_TABLE_XVNC,
46};
47
48static bool X11_ScancodeIsRemappable(SDL_Scancode scancode)
49{
50 /*
51 * XKB remappings can assign different keysyms for these scancodes, but
52 * as these keys are in fixed positions, the scancodes themselves shouldn't
53 * be switched. Mark them as not being remappable.
54 */
55 switch (scancode) {
56 case SDL_SCANCODE_ESCAPE:
57 case SDL_SCANCODE_CAPSLOCK:
58 case SDL_SCANCODE_NUMLOCKCLEAR:
59 case SDL_SCANCODE_LSHIFT:
60 case SDL_SCANCODE_RSHIFT:
61 case SDL_SCANCODE_LCTRL:
62 case SDL_SCANCODE_RCTRL:
63 case SDL_SCANCODE_LALT:
64 case SDL_SCANCODE_RALT:
65 case SDL_SCANCODE_LGUI:
66 case SDL_SCANCODE_RGUI:
67 return false;
68 default:
69 return true;
70 }
71}
72
73// This function only correctly maps letters and numbers for keyboards in US QWERTY layout
74static SDL_Scancode X11_KeyCodeToSDLScancode(SDL_VideoDevice *_this, KeyCode keycode)
75{
76 const KeySym keysym = X11_KeyCodeToSym(_this, keycode, 0, 0);
77
78 if (keysym == NoSymbol) {
79 return SDL_SCANCODE_UNKNOWN;
80 }
81
82 return SDL_GetScancodeFromKeySym(keysym, keycode);
83}
84
85KeySym X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode keycode, unsigned char group, unsigned int mod_mask)
86{
87 SDL_VideoData *data = _this->internal;
88 KeySym keysym;
89 unsigned int mods_ret[16];
90
91 SDL_zero(mods_ret);
92
93#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
94 if (data->xkb.desc_ptr) {
95 int num_groups = XkbKeyNumGroups(data->xkb.desc_ptr, keycode);
96 unsigned char info = XkbKeyGroupInfo(data->xkb.desc_ptr, keycode);
97
98 if (num_groups && group >= num_groups) {
99
100 int action = XkbOutOfRangeGroupAction(info);
101
102 if (action == XkbRedirectIntoRange) {
103 group = XkbOutOfRangeGroupNumber(info);
104 if (group >= num_groups) {
105 group = 0;
106 }
107 } else if (action == XkbClampIntoRange) {
108 group = num_groups - 1;
109 } else {
110 group %= num_groups;
111 }
112 }
113
114 if (X11_XkbLookupKeySym(data->display, keycode, XkbBuildCoreState(mod_mask, group), mods_ret, &keysym) == NoSymbol) {
115 keysym = NoSymbol;
116 }
117 } else
118#endif
119 {
120 // TODO: Handle groups and modifiers on the legacy path.
121 keysym = X11_XKeycodeToKeysym(data->display, keycode, 0);
122 }
123
124 return keysym;
125}
126
127bool X11_InitKeyboard(SDL_VideoDevice *_this)
128{
129 SDL_VideoData *data = _this->internal;
130 int i = 0;
131 int j = 0;
132 int min_keycode, max_keycode;
133 struct
134 {
135 SDL_Scancode scancode;
136 KeySym keysym;
137 int value;
138 } fingerprint[] = {
139 { SDL_SCANCODE_HOME, XK_Home, 0 },
140 { SDL_SCANCODE_PAGEUP, XK_Prior, 0 },
141 { SDL_SCANCODE_UP, XK_Up, 0 },
142 { SDL_SCANCODE_LEFT, XK_Left, 0 },
143 { SDL_SCANCODE_DELETE, XK_Delete, 0 },
144 { SDL_SCANCODE_KP_ENTER, XK_KP_Enter, 0 },
145 };
146 int best_distance;
147 int best_index;
148 int distance;
149 Bool xkb_repeat = 0;
150
151#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
152 {
153 int xkb_major = XkbMajorVersion;
154 int xkb_minor = XkbMinorVersion;
155
156 if (X11_XkbQueryExtension(data->display, NULL, &data->xkb.event, NULL, &xkb_major, &xkb_minor)) {
157 data->xkb.desc_ptr = X11_XkbGetMap(data->display, XkbAllClientInfoMask, XkbUseCoreKbd);
158 }
159
160 // This will remove KeyRelease events for held keys
161 X11_XkbSetDetectableAutoRepeat(data->display, True, &xkb_repeat);
162 }
163#endif
164
165 // Open a connection to the X input manager
166#ifdef X_HAVE_UTF8_STRING
167 if (SDL_X11_HAVE_UTF8) {
168 /* Set the locale, and call XSetLocaleModifiers before XOpenIM so that
169 Compose keys will work correctly. */
170 char *prev_locale = setlocale(LC_ALL, NULL);
171 char *prev_xmods = X11_XSetLocaleModifiers(NULL);
172
173 if (prev_locale) {
174 prev_locale = SDL_strdup(prev_locale);
175 }
176
177 if (prev_xmods) {
178 prev_xmods = SDL_strdup(prev_xmods);
179 }
180
181 (void)setlocale(LC_ALL, "");
182 X11_XSetLocaleModifiers("");
183
184 data->im = X11_XOpenIM(data->display, NULL, NULL, NULL);
185
186 /* Reset the locale + X locale modifiers back to how they were,
187 locale first because the X locale modifiers depend on it. */
188 (void)setlocale(LC_ALL, prev_locale);
189 X11_XSetLocaleModifiers(prev_xmods);
190
191 if (prev_locale) {
192 SDL_free(prev_locale);
193 }
194
195 if (prev_xmods) {
196 SDL_free(prev_xmods);
197 }
198 }
199#endif
200 // Try to determine which scancodes are being used based on fingerprint
201 best_distance = SDL_arraysize(fingerprint) + 1;
202 best_index = -1;
203 X11_XDisplayKeycodes(data->display, &min_keycode, &max_keycode);
204 for (i = 0; i < SDL_arraysize(fingerprint); ++i) {
205 fingerprint[i].value = X11_XKeysymToKeycode(data->display, fingerprint[i].keysym) - min_keycode;
206 }
207 for (i = 0; i < SDL_arraysize(scancode_set); ++i) {
208 int table_size;
209 const SDL_Scancode *table = SDL_GetScancodeTable(scancode_set[i], &table_size);
210
211 distance = 0;
212 for (j = 0; j < SDL_arraysize(fingerprint); ++j) {
213 if (fingerprint[j].value < 0 || fingerprint[j].value >= table_size) {
214 distance += 1;
215 } else if (table[fingerprint[j].value] != fingerprint[j].scancode) {
216 distance += 1;
217 }
218 }
219 if (distance < best_distance) {
220 best_distance = distance;
221 best_index = i;
222 }
223 }
224 if (best_index < 0 || best_distance > 2) {
225 // This is likely to be SDL_SCANCODE_TABLE_XFREE86_2 with remapped keys, double check a rarely remapped value
226 int fingerprint_value = X11_XKeysymToKeycode(data->display, 0x1008FF5B /* XF86Documents */) - min_keycode;
227 if (fingerprint_value == 235) {
228 for (i = 0; i < SDL_arraysize(scancode_set); ++i) {
229 if (scancode_set[i] == SDL_SCANCODE_TABLE_XFREE86_2) {
230 best_index = i;
231 best_distance = 0;
232 break;
233 }
234 }
235 }
236 }
237 if (best_index >= 0 && best_distance <= 2) {
238 int table_size;
239 const SDL_Scancode *table = SDL_GetScancodeTable(scancode_set[best_index], &table_size);
240
241#ifdef DEBUG_KEYBOARD
242 SDL_Log("Using scancode set %d, min_keycode = %d, max_keycode = %d, table_size = %d", best_index, min_keycode, max_keycode, table_size);
243#endif
244 // This should never happen, but just in case...
245 if (table_size > (SDL_arraysize(data->key_layout) - min_keycode)) {
246 table_size = (SDL_arraysize(data->key_layout) - min_keycode);
247 }
248 SDL_memcpy(&data->key_layout[min_keycode], table, sizeof(SDL_Scancode) * table_size);
249
250 /* Scancodes represent physical locations on the keyboard, unaffected by keyboard mapping.
251 However, there are a number of extended scancodes that have no standard location, so use
252 the X11 mapping for all non-character keys.
253 */
254 for (i = min_keycode; i <= max_keycode; ++i) {
255 SDL_Scancode scancode = X11_KeyCodeToSDLScancode(_this, i);
256#ifdef DEBUG_KEYBOARD
257 {
258 KeySym sym;
259 sym = X11_KeyCodeToSym(_this, (KeyCode)i, 0);
260 SDL_Log("code = %d, sym = 0x%X (%s) ", i - min_keycode,
261 (unsigned int)sym, sym == NoSymbol ? "NoSymbol" : X11_XKeysymToString(sym));
262 }
263#endif
264 if (scancode == data->key_layout[i]) {
265 continue;
266 }
267 if ((SDL_GetKeymapKeycode(NULL, scancode, SDL_KMOD_NONE) & (SDLK_SCANCODE_MASK | SDLK_EXTENDED_MASK)) && X11_ScancodeIsRemappable(scancode)) {
268 // Not a character key and the scancode is safe to remap
269#ifdef DEBUG_KEYBOARD
270 SDL_Log("Changing scancode, was %d (%s), now %d (%s)", data->key_layout[i], SDL_GetScancodeName(data->key_layout[i]), scancode, SDL_GetScancodeName(scancode));
271#endif
272 data->key_layout[i] = scancode;
273 }
274 }
275 } else {
276#ifdef DEBUG_SCANCODES
277 SDL_Log("Keyboard layout unknown, please report the following to the SDL forums/mailing list (https://discourse.libsdl.org/):");
278#endif
279
280 // Determine key_layout - only works on US QWERTY layout
281 for (i = min_keycode; i <= max_keycode; ++i) {
282 SDL_Scancode scancode = X11_KeyCodeToSDLScancode(_this, i);
283#ifdef DEBUG_SCANCODES
284 {
285 KeySym sym;
286 sym = X11_KeyCodeToSym(_this, (KeyCode)i, 0);
287 SDL_Log("code = %d, sym = 0x%X (%s) ", i - min_keycode,
288 (unsigned int)sym, sym == NoSymbol ? "NoSymbol" : X11_XKeysymToString(sym));
289 }
290 if (scancode == SDL_SCANCODE_UNKNOWN) {
291 SDL_Log("scancode not found");
292 } else {
293 SDL_Log("scancode = %d (%s)", scancode, SDL_GetScancodeName(scancode));
294 }
295#endif
296 data->key_layout[i] = scancode;
297 }
298 }
299
300 X11_UpdateKeymap(_this, false);
301
302 SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu");
303
304 X11_ReconcileKeyboardState(_this);
305
306 return true;
307}
308
309static unsigned X11_GetNumLockModifierMask(SDL_VideoDevice *_this)
310{
311 SDL_VideoData *videodata = _this->internal;
312 Display *display = videodata->display;
313 unsigned num_mask = 0;
314 int i, j;
315 XModifierKeymap *xmods;
316 unsigned n;
317
318 xmods = X11_XGetModifierMapping(display);
319 n = xmods->max_keypermod;
320 for (i = 3; i < 8; i++) {
321 for (j = 0; j < n; j++) {
322 KeyCode kc = xmods->modifiermap[i * n + j];
323 if (videodata->key_layout[kc] == SDL_SCANCODE_NUMLOCKCLEAR) {
324 num_mask = 1 << i;
325 break;
326 }
327 }
328 }
329 X11_XFreeModifiermap(xmods);
330
331 return num_mask;
332}
333
334static unsigned X11_GetScrollLockModifierMask(SDL_VideoDevice *_this)
335{
336 SDL_VideoData *videodata = _this->internal;
337 Display *display = videodata->display;
338 unsigned num_mask = 0;
339 int i, j;
340 XModifierKeymap *xmods;
341 unsigned n;
342
343 xmods = X11_XGetModifierMapping(display);
344 n = xmods->max_keypermod;
345 for (i = 3; i < 8; i++) {
346 for (j = 0; j < n; j++) {
347 KeyCode kc = xmods->modifiermap[i * n + j];
348 if (videodata->key_layout[kc] == SDL_SCANCODE_SCROLLLOCK) {
349 num_mask = 1 << i;
350 break;
351 }
352 }
353 }
354 X11_XFreeModifiermap(xmods);
355
356 return num_mask;
357}
358
359void X11_UpdateKeymap(SDL_VideoDevice *_this, bool send_event)
360{
361 struct Keymod_masks
362 {
363 SDL_Keymod sdl_mask;
364 unsigned int xkb_mask;
365 } const keymod_masks[] = {
366 { SDL_KMOD_NONE, 0 },
367 { SDL_KMOD_SHIFT, ShiftMask },
368 { SDL_KMOD_CAPS, LockMask },
369 { SDL_KMOD_SHIFT | SDL_KMOD_CAPS, ShiftMask | LockMask },
370 { SDL_KMOD_MODE, Mod5Mask },
371 { SDL_KMOD_MODE | SDL_KMOD_SHIFT, Mod5Mask | ShiftMask },
372 { SDL_KMOD_MODE | SDL_KMOD_CAPS, Mod5Mask | LockMask },
373 { SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, Mod5Mask | ShiftMask | LockMask },
374 { SDL_KMOD_LEVEL5, Mod3Mask },
375 { SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT, Mod3Mask | ShiftMask },
376 { SDL_KMOD_LEVEL5 | SDL_KMOD_CAPS, Mod3Mask | LockMask },
377 { SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, Mod3Mask | ShiftMask | LockMask },
378 { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE, Mod5Mask | Mod3Mask },
379 { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT, Mod3Mask | Mod5Mask | ShiftMask },
380 { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_CAPS, Mod3Mask | Mod5Mask | LockMask },
381 { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, Mod3Mask | Mod5Mask | ShiftMask | LockMask }
382 };
383
384 SDL_VideoData *data = _this->internal;
385 SDL_Scancode scancode;
386 SDL_Keymap *keymap = SDL_CreateKeymap();
387
388#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
389 if (data->xkb.desc_ptr) {
390 XkbStateRec state;
391 X11_XkbGetUpdatedMap(data->display, XkbAllClientInfoMask, data->xkb.desc_ptr);
392
393 if (X11_XkbGetState(data->display, XkbUseCoreKbd, &state) == Success) {
394 data->xkb.current_group = state.group;
395 }
396 }
397#endif
398
399 for (int m = 0; m < SDL_arraysize(keymod_masks); ++m) {
400 for (int i = 0; i < SDL_arraysize(data->key_layout); ++i) {
401 // Make sure this is a valid scancode
402 scancode = data->key_layout[i];
403 if (scancode == SDL_SCANCODE_UNKNOWN) {
404 continue;
405 }
406
407 const KeySym keysym = X11_KeyCodeToSym(_this, i, data->xkb.current_group, keymod_masks[m].xkb_mask);
408
409 if (keysym != NoSymbol) {
410 SDL_Keycode keycode = SDL_GetKeyCodeFromKeySym(keysym, i, keymod_masks[m].sdl_mask);
411
412 if (!keycode) {
413 switch (scancode) {
414 case SDL_SCANCODE_RETURN:
415 keycode = SDLK_RETURN;
416 break;
417 case SDL_SCANCODE_ESCAPE:
418 keycode = SDLK_ESCAPE;
419 break;
420 case SDL_SCANCODE_BACKSPACE:
421 keycode = SDLK_BACKSPACE;
422 break;
423 case SDL_SCANCODE_DELETE:
424 keycode = SDLK_DELETE;
425 break;
426 default:
427 keycode = SDL_SCANCODE_TO_KEYCODE(scancode);
428 break;
429 }
430 }
431
432 SDL_SetKeymapEntry(keymap, scancode, keymod_masks[m].sdl_mask, keycode);
433 }
434 }
435 }
436
437 data->xkb.numlock_mask = X11_GetNumLockModifierMask(_this);
438 data->xkb.scrolllock_mask = X11_GetScrollLockModifierMask(_this);
439 SDL_SetKeymap(keymap, send_event);
440}
441
442void X11_QuitKeyboard(SDL_VideoDevice *_this)
443{
444 SDL_VideoData *data = _this->internal;
445
446#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
447 if (data->xkb.desc_ptr) {
448 X11_XkbFreeKeyboard(data->xkb.desc_ptr, 0, True);
449 data->xkb.desc_ptr = NULL;
450 }
451#endif
452}
453
454void X11_ClearComposition(SDL_WindowData *data)
455{
456 if (data->preedit_length > 0) {
457 data->preedit_text[0] = '\0';
458 data->preedit_length = 0;
459 }
460
461 if (data->ime_needs_clear_composition) {
462 SDL_SendEditingText("", 0, 0);
463 data->ime_needs_clear_composition = false;
464 }
465}
466
467static void X11_SendEditingEvent(SDL_WindowData *data)
468{
469 if (data->preedit_length == 0) {
470 X11_ClearComposition(data);
471 return;
472 }
473
474 bool in_highlight = false;
475 int start = -1, length = 0, i;
476 for (i = 0; i < data->preedit_length; ++i) {
477 if (data->preedit_feedback[i] & (XIMReverse | XIMHighlight)) {
478 if (start < 0) {
479 start = i;
480 in_highlight = true;
481 }
482 } else if (in_highlight) {
483 // Found the end of the highlight
484 break;
485 }
486 }
487 if (in_highlight) {
488 length = (i - start);
489 } else {
490 start = SDL_clamp(data->preedit_cursor, 0, data->preedit_length);
491 }
492 SDL_SendEditingText(data->preedit_text, start, length);
493
494 data->ime_needs_clear_composition = true;
495}
496
497static int preedit_start_callback(XIC xic, XPointer client_data, XPointer call_data)
498{
499 // No limit on preedit text length
500 return -1;
501}
502
503static void preedit_done_callback(XIC xic, XPointer client_data, XPointer call_data)
504{
505}
506
507static void preedit_draw_callback(XIC xic, XPointer client_data, XIMPreeditDrawCallbackStruct *call_data)
508{
509 SDL_WindowData *data = (SDL_WindowData *)client_data;
510 int chg_first = SDL_clamp(call_data->chg_first, 0, data->preedit_length);
511 int chg_length = SDL_clamp(call_data->chg_length, 0, data->preedit_length - chg_first);
512
513 const char *start = data->preedit_text;
514 if (chg_length > 0) {
515 // Delete text in range
516 for (int i = 0; start && *start && i < chg_first; ++i) {
517 SDL_StepUTF8(&start, NULL);
518 }
519
520 const char *end = start;
521 for (int i = 0; end && *end && i < chg_length; ++i) {
522 SDL_StepUTF8(&end, NULL);
523 }
524
525 if (end > start) {
526 SDL_memmove((char *)start, end, SDL_strlen(end) + 1);
527 if ((chg_first + chg_length) > data->preedit_length) {
528 SDL_memmove(&data->preedit_feedback[chg_first], &data->preedit_feedback[chg_first + chg_length], (data->preedit_length - chg_first - chg_length) * sizeof(*data->preedit_feedback));
529 }
530 }
531 data->preedit_length -= chg_length;
532 }
533
534 XIMText *text = call_data->text;
535 if (text) {
536 // Insert text in range
537 SDL_assert(!text->encoding_is_wchar);
538
539 // The text length isn't calculated as directed by the spec, recalculate it now
540 if (text->string.multi_byte) {
541 text->length = SDL_utf8strlen(text->string.multi_byte);
542 }
543
544 size_t string_size = SDL_strlen(text->string.multi_byte);
545 size_t size = string_size + 1;
546 if (data->preedit_text) {
547 size += SDL_strlen(data->preedit_text);
548 }
549 char *preedit_text = (char *)SDL_malloc(size * sizeof(*preedit_text));
550 if (preedit_text) {
551 size_t pre_size = (start - data->preedit_text);
552 size_t post_size = start ? SDL_strlen(start) : 0;
553 if (pre_size > 0) {
554 SDL_memcpy(&preedit_text[0], data->preedit_text, pre_size);
555 }
556 SDL_memcpy(&preedit_text[pre_size], text->string.multi_byte, string_size);
557 if (post_size > 0) {
558 SDL_memcpy(&preedit_text[pre_size + string_size], start, post_size);
559 }
560 preedit_text[size - 1] = '\0';
561 }
562
563 size_t feedback_size = data->preedit_length + text->length;
564 XIMFeedback *feedback = (XIMFeedback *)SDL_malloc(feedback_size * sizeof(*feedback));
565 if (feedback) {
566 size_t pre_size = (size_t)chg_first;
567 size_t post_size = (size_t)data->preedit_length - pre_size;
568 if (pre_size > 0) {
569 SDL_memcpy(&feedback[0], data->preedit_feedback, pre_size * sizeof(*feedback));
570 }
571 SDL_memcpy(&feedback[pre_size], text->feedback, text->length * sizeof(*feedback));
572 if (post_size > 0) {
573 SDL_memcpy(&feedback[pre_size + text->length], &data->preedit_feedback[pre_size], post_size * sizeof(*feedback));
574 }
575 }
576
577 if (preedit_text && feedback) {
578 SDL_free(data->preedit_text);
579 data->preedit_text = preedit_text;
580
581 SDL_free(data->preedit_feedback);
582 data->preedit_feedback = feedback;
583
584 data->preedit_length += text->length;
585 } else {
586 SDL_free(preedit_text);
587 SDL_free(feedback);
588 }
589 }
590
591 data->preedit_cursor = call_data->caret;
592
593#ifdef DEBUG_XIM
594 if (call_data->chg_length > 0) {
595 SDL_Log("Draw callback deleted %d characters at %d", call_data->chg_length, call_data->chg_first);
596 }
597 if (text) {
598 SDL_Log("Draw callback inserted %s at %d, caret: %d", text->string.multi_byte, call_data->chg_first, call_data->caret);
599 }
600 SDL_Log("Pre-edit text: %s", data->preedit_text);
601#endif
602
603 X11_SendEditingEvent(data);
604}
605
606static void preedit_caret_callback(XIC xic, XPointer client_data, XIMPreeditCaretCallbackStruct *call_data)
607{
608 SDL_WindowData *data = (SDL_WindowData *)client_data;
609
610 switch (call_data->direction) {
611 case XIMAbsolutePosition:
612 if (call_data->position != data->preedit_cursor) {
613 data->preedit_cursor = call_data->position;
614 X11_SendEditingEvent(data);
615 }
616 break;
617 case XIMDontChange:
618 break;
619 default:
620 // Not currently supported
621 break;
622 }
623}
624
625void X11_CreateInputContext(SDL_WindowData *data)
626{
627#ifdef X_HAVE_UTF8_STRING
628 SDL_VideoData *videodata = data->videodata;
629
630 if (SDL_X11_HAVE_UTF8 && videodata->im) {
631 const char *hint = SDL_GetHint(SDL_HINT_IME_IMPLEMENTED_UI);
632 if (hint && SDL_strstr(hint, "composition")) {
633 XIMCallback draw_callback;
634 draw_callback.client_data = (XPointer)data;
635 draw_callback.callback = (XIMProc)preedit_draw_callback;
636
637 XIMCallback start_callback;
638 start_callback.client_data = (XPointer)data;
639 start_callback.callback = (XIMProc)preedit_start_callback;
640
641 XIMCallback done_callback;
642 done_callback.client_data = (XPointer)data;
643 done_callback.callback = (XIMProc)preedit_done_callback;
644
645 XIMCallback caret_callback;
646 caret_callback.client_data = (XPointer)data;
647 caret_callback.callback = (XIMProc)preedit_caret_callback;
648
649 XVaNestedList attr = X11_XVaCreateNestedList(0,
650 XNPreeditStartCallback, &start_callback,
651 XNPreeditDoneCallback, &done_callback,
652 XNPreeditDrawCallback, &draw_callback,
653 XNPreeditCaretCallback, &caret_callback,
654 NULL);
655 if (attr) {
656 data->ic = X11_XCreateIC(videodata->im,
657 XNInputStyle, XIMPreeditCallbacks | XIMStatusCallbacks,
658 XNPreeditAttributes, attr,
659 XNClientWindow, data->xwindow,
660 NULL);
661 X11_XFree(attr);
662 }
663 }
664 if (!data->ic) {
665 data->ic = X11_XCreateIC(videodata->im,
666 XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
667 XNClientWindow, data->xwindow,
668 NULL);
669 }
670 data->xim_spot.x = -1;
671 data->xim_spot.y = -1;
672 }
673#endif // X_HAVE_UTF8_STRING
674}
675
676static void X11_ResetXIM(SDL_VideoDevice *_this, SDL_Window *window)
677{
678#ifdef X_HAVE_UTF8_STRING
679 SDL_WindowData *data = window->internal;
680
681 if (data && data->ic) {
682 // Clear any partially entered dead keys
683 char *contents = X11_Xutf8ResetIC(data->ic);
684 if (contents) {
685 X11_XFree(contents);
686 }
687 }
688#endif // X_HAVE_UTF8_STRING
689}
690
691bool X11_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props)
692{
693 X11_ResetXIM(_this, window);
694
695 return X11_UpdateTextInputArea(_this, window);
696}
697
698bool X11_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window)
699{
700 X11_ResetXIM(_this, window);
701 return true;
702}
703
704bool X11_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window)
705{
706#ifdef X_HAVE_UTF8_STRING
707 SDL_WindowData *data = window->internal;
708
709 if (data && data->ic) {
710 XPoint spot;
711 spot.x = window->text_input_rect.x + window->text_input_cursor;
712 spot.y = window->text_input_rect.y + window->text_input_rect.h;
713 if (spot.x != data->xim_spot.x || spot.y != data->xim_spot.y) {
714 XVaNestedList attr = X11_XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
715 if (attr) {
716 X11_XSetICValues(data->ic, XNPreeditAttributes, attr, NULL);
717 X11_XFree(attr);
718 }
719 SDL_copyp(&data->xim_spot, &spot);
720 }
721 }
722#endif
723 return true;
724}
725
726bool X11_HasScreenKeyboardSupport(SDL_VideoDevice *_this)
727{
728 SDL_VideoData *videodata = _this->internal;
729 return videodata->is_steam_deck;
730}
731
732void X11_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props)
733{
734 SDL_VideoData *videodata = _this->internal;
735
736 if (videodata->is_steam_deck) {
737 /* For more documentation of the URL parameters, see:
738 * https://partner.steamgames.com/doc/api/ISteamUtils#ShowFloatingGamepadTextInput
739 */
740 const int k_EFloatingGamepadTextInputModeModeSingleLine = 0; // Enter dismisses the keyboard
741 const int k_EFloatingGamepadTextInputModeModeMultipleLines = 1; // User needs to explicitly dismiss the keyboard
742 const int k_EFloatingGamepadTextInputModeModeEmail = 2; // Keyboard is displayed in a special mode that makes it easier to enter emails
743 const int k_EFloatingGamepadTextInputModeModeNumeric = 3; // Numeric keypad is shown
744 char deeplink[128];
745 int mode;
746
747 switch (SDL_GetTextInputType(props)) {
748 case SDL_TEXTINPUT_TYPE_TEXT_EMAIL:
749 mode = k_EFloatingGamepadTextInputModeModeEmail;
750 break;
751 case SDL_TEXTINPUT_TYPE_NUMBER:
752 case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN:
753 case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE:
754 mode = k_EFloatingGamepadTextInputModeModeNumeric;
755 break;
756 default:
757 if (SDL_GetTextInputMultiline(props)) {
758 mode = k_EFloatingGamepadTextInputModeModeMultipleLines;
759 } else {
760 mode = k_EFloatingGamepadTextInputModeModeSingleLine;
761 }
762 break;
763 }
764 (void)SDL_snprintf(deeplink, sizeof(deeplink),
765 "steam://open/keyboard?XPosition=0&YPosition=0&Width=0&Height=0&Mode=%d",
766 mode);
767 SDL_OpenURL(deeplink);
768 videodata->steam_keyboard_open = true;
769 }
770}
771
772void X11_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
773{
774 SDL_VideoData *videodata = _this->internal;
775
776 if (videodata->is_steam_deck) {
777 SDL_OpenURL("steam://close/keyboard");
778 videodata->steam_keyboard_open = false;
779 }
780}
781
782bool X11_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window)
783{
784 SDL_VideoData *videodata = _this->internal;
785
786 return videodata->steam_keyboard_open;
787}
788
789#endif // SDL_VIDEO_DRIVER_X11
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11keyboard.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11keyboard.h
new file mode 100644
index 0000000..a6cd2f7
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11keyboard.h
@@ -0,0 +1,40 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifndef SDL_x11keyboard_h_
24#define SDL_x11keyboard_h_
25
26extern bool X11_InitKeyboard(SDL_VideoDevice *_this);
27extern void X11_UpdateKeymap(SDL_VideoDevice *_this, bool send_event);
28extern void X11_QuitKeyboard(SDL_VideoDevice *_this);
29extern void X11_CreateInputContext(SDL_WindowData *data);
30extern void X11_ClearComposition(SDL_WindowData *data);
31extern bool X11_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
32extern bool X11_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window);
33extern bool X11_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window);
34extern bool X11_HasScreenKeyboardSupport(SDL_VideoDevice *_this);
35extern void X11_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
36extern void X11_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window);
37extern bool X11_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window);
38extern KeySym X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode, unsigned char group, unsigned int mod_mask);
39
40#endif // SDL_x11keyboard_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11messagebox.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11messagebox.c
new file mode 100644
index 0000000..8aa1c6a
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11messagebox.c
@@ -0,0 +1,887 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22#include "SDL_internal.h"
23
24#ifdef SDL_VIDEO_DRIVER_X11
25
26#include "SDL_x11video.h"
27#include "SDL_x11dyn.h"
28#include "SDL_x11messagebox.h"
29
30#include <X11/keysym.h>
31#include <locale.h>
32
33#define SDL_FORK_MESSAGEBOX 1
34#define SDL_SET_LOCALE 1
35
36#if SDL_FORK_MESSAGEBOX
37#include <sys/types.h>
38#include <sys/wait.h>
39#include <unistd.h>
40#include <errno.h>
41#endif
42
43#define MAX_BUTTONS 8 // Maximum number of buttons supported
44#define MIN_BUTTON_WIDTH 64 // Minimum button width
45#define MIN_DIALOG_WIDTH 200 // Minimum dialog width
46#define MIN_DIALOG_HEIGHT 100 // Minimum dialog height
47
48static const char g_MessageBoxFontLatin1[] =
49 "-*-*-medium-r-normal--0-120-*-*-p-0-iso8859-1";
50
51static const char* g_MessageBoxFont[] = {
52 "-*-*-medium-r-normal--*-120-*-*-*-*-iso10646-1", // explicitly unicode (iso10646-1)
53 "-*-*-medium-r-*--*-120-*-*-*-*-iso10646-1", // explicitly unicode (iso10646-1)
54 "-misc-*-*-*-*--*-*-*-*-*-*-iso10646-1", // misc unicode (fix for some systems)
55 "-*-*-*-*-*--*-*-*-*-*-*-iso10646-1", // just give me anything Unicode.
56 "-*-*-medium-r-normal--*-120-*-*-*-*-iso8859-1", // explicitly latin1, in case low-ASCII works out.
57 "-*-*-medium-r-*--*-120-*-*-*-*-iso8859-1", // explicitly latin1, in case low-ASCII works out.
58 "-misc-*-*-*-*--*-*-*-*-*-*-iso8859-1", // misc latin1 (fix for some systems)
59 "-*-*-*-*-*--*-*-*-*-*-*-iso8859-1", // just give me anything latin1.
60 NULL
61};
62
63static const SDL_MessageBoxColor g_default_colors[SDL_MESSAGEBOX_COLOR_COUNT] = {
64 { 56, 54, 53 }, // SDL_MESSAGEBOX_COLOR_BACKGROUND,
65 { 209, 207, 205 }, // SDL_MESSAGEBOX_COLOR_TEXT,
66 { 140, 135, 129 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BORDER,
67 { 105, 102, 99 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND,
68 { 205, 202, 53 }, // SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED,
69};
70
71#define SDL_MAKE_RGB(_r, _g, _b) (((Uint32)(_r) << 16) | \
72 ((Uint32)(_g) << 8) | \
73 ((Uint32)(_b)))
74
75typedef struct SDL_MessageBoxButtonDataX11
76{
77 int x, y; // Text position
78 int length; // Text length
79 int text_width; // Text width
80
81 SDL_Rect rect; // Rectangle for entire button
82
83 const SDL_MessageBoxButtonData *buttondata; // Button data from caller
84} SDL_MessageBoxButtonDataX11;
85
86typedef struct TextLineData
87{
88 int width; // Width of this text line
89 int length; // String length of this text line
90 const char *text; // Text for this line
91} TextLineData;
92
93typedef struct SDL_MessageBoxDataX11
94{
95 Display *display;
96 int screen;
97 Window window;
98#ifdef SDL_VIDEO_DRIVER_X11_XDBE
99 XdbeBackBuffer buf;
100 bool xdbe; // Whether Xdbe is present or not
101#endif
102 long event_mask;
103 Atom wm_protocols;
104 Atom wm_delete_message;
105
106 int dialog_width; // Dialog box width.
107 int dialog_height; // Dialog box height.
108
109 XFontSet font_set; // for UTF-8 systems
110 XFontStruct *font_struct; // Latin1 (ASCII) fallback.
111 int xtext, ytext; // Text position to start drawing at.
112 int numlines; // Count of Text lines.
113 int text_height; // Height for text lines.
114 TextLineData *linedata;
115
116 int *pbuttonid; // Pointer to user return buttonID value.
117
118 int button_press_index; // Index into buttondata/buttonpos for button which is pressed (or -1).
119 int mouse_over_index; // Index into buttondata/buttonpos for button mouse is over (or -1).
120
121 int numbuttons; // Count of buttons.
122 const SDL_MessageBoxButtonData *buttondata;
123 SDL_MessageBoxButtonDataX11 buttonpos[MAX_BUTTONS];
124
125 Uint32 color[SDL_MESSAGEBOX_COLOR_COUNT];
126
127 const SDL_MessageBoxData *messageboxdata;
128} SDL_MessageBoxDataX11;
129
130// Maximum helper for ints.
131static SDL_INLINE int IntMax(int a, int b)
132{
133 return (a > b) ? a : b;
134}
135
136// Return width and height for a string.
137static void GetTextWidthHeight(SDL_MessageBoxDataX11 *data, const char *str, int nbytes, int *pwidth, int *pheight)
138{
139#ifdef X_HAVE_UTF8_STRING
140 if (SDL_X11_HAVE_UTF8) {
141 XRectangle overall_ink, overall_logical;
142 X11_Xutf8TextExtents(data->font_set, str, nbytes, &overall_ink, &overall_logical);
143 *pwidth = overall_logical.width;
144 *pheight = overall_logical.height;
145 } else
146#endif
147 {
148 XCharStruct text_structure;
149 int font_direction, font_ascent, font_descent;
150 X11_XTextExtents(data->font_struct, str, nbytes,
151 &font_direction, &font_ascent, &font_descent,
152 &text_structure);
153 *pwidth = text_structure.width;
154 *pheight = text_structure.ascent + text_structure.descent;
155 }
156}
157
158// Return index of button if position x,y is contained therein.
159static int GetHitButtonIndex(SDL_MessageBoxDataX11 *data, int x, int y)
160{
161 int i;
162 int numbuttons = data->numbuttons;
163 SDL_MessageBoxButtonDataX11 *buttonpos = data->buttonpos;
164
165 for (i = 0; i < numbuttons; i++) {
166 SDL_Rect *rect = &buttonpos[i].rect;
167
168 if ((x >= rect->x) &&
169 (x <= (rect->x + rect->w)) &&
170 (y >= rect->y) &&
171 (y <= (rect->y + rect->h))) {
172 return i;
173 }
174 }
175
176 return -1;
177}
178
179// Initialize SDL_MessageBoxData structure and Display, etc.
180static bool X11_MessageBoxInit(SDL_MessageBoxDataX11 *data, const SDL_MessageBoxData *messageboxdata, int *pbuttonid)
181{
182 int i;
183 int numbuttons = messageboxdata->numbuttons;
184 const SDL_MessageBoxButtonData *buttondata = messageboxdata->buttons;
185 const SDL_MessageBoxColor *colorhints;
186
187 if (numbuttons > MAX_BUTTONS) {
188 return SDL_SetError("Too many buttons (%d max allowed)", MAX_BUTTONS);
189 }
190
191 data->dialog_width = MIN_DIALOG_WIDTH;
192 data->dialog_height = MIN_DIALOG_HEIGHT;
193 data->messageboxdata = messageboxdata;
194 data->buttondata = buttondata;
195 data->numbuttons = numbuttons;
196 data->pbuttonid = pbuttonid;
197
198 data->display = X11_XOpenDisplay(NULL);
199 if (!data->display) {
200 return SDL_SetError("Couldn't open X11 display");
201 }
202
203#ifdef X_HAVE_UTF8_STRING
204 if (SDL_X11_HAVE_UTF8) {
205 char **missing = NULL;
206 int num_missing = 0;
207 int i_font;
208 for (i_font = 0; g_MessageBoxFont[i_font]; ++i_font) {
209 data->font_set = X11_XCreateFontSet(data->display, g_MessageBoxFont[i_font],
210 &missing, &num_missing, NULL);
211 if (missing) {
212 X11_XFreeStringList(missing);
213 }
214 if (data->font_set) {
215 break;
216 }
217 }
218 if (!data->font_set) {
219 return SDL_SetError("Couldn't load x11 message box font");
220 }
221 } else
222#endif
223 {
224 data->font_struct = X11_XLoadQueryFont(data->display, g_MessageBoxFontLatin1);
225 if (!data->font_struct) {
226 return SDL_SetError("Couldn't load font %s", g_MessageBoxFontLatin1);
227 }
228 }
229
230 if (messageboxdata->colorScheme) {
231 colorhints = messageboxdata->colorScheme->colors;
232 } else {
233 colorhints = g_default_colors;
234 }
235
236 // Convert our SDL_MessageBoxColor r,g,b values to packed RGB format.
237 for (i = 0; i < SDL_MESSAGEBOX_COLOR_COUNT; i++) {
238 data->color[i] = SDL_MAKE_RGB(colorhints[i].r, colorhints[i].g, colorhints[i].b);
239 }
240
241 return true;
242}
243
244static int CountLinesOfText(const char *text)
245{
246 int result = 0;
247 while (text && *text) {
248 const char *lf = SDL_strchr(text, '\n');
249 result++; // even without an endline, this counts as a line.
250 text = lf ? lf + 1 : NULL;
251 }
252 return result;
253}
254
255// Calculate and initialize text and button locations.
256static bool X11_MessageBoxInitPositions(SDL_MessageBoxDataX11 *data)
257{
258 int i;
259 int ybuttons;
260 int text_width_max = 0;
261 int button_text_height = 0;
262 int button_width = MIN_BUTTON_WIDTH;
263 const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
264
265 // Go over text and break linefeeds into separate lines.
266 if (messageboxdata && messageboxdata->message[0]) {
267 const char *text = messageboxdata->message;
268 const int linecount = CountLinesOfText(text);
269 TextLineData *plinedata = (TextLineData *)SDL_malloc(sizeof(TextLineData) * linecount);
270
271 if (!plinedata) {
272 return false;
273 }
274
275 data->linedata = plinedata;
276 data->numlines = linecount;
277
278 for (i = 0; i < linecount; i++, plinedata++) {
279 const char *lf = SDL_strchr(text, '\n');
280 const int length = lf ? (lf - text) : SDL_strlen(text);
281 int height;
282
283 plinedata->text = text;
284
285 GetTextWidthHeight(data, text, length, &plinedata->width, &height);
286
287 // Text and widths are the largest we've ever seen.
288 data->text_height = IntMax(data->text_height, height);
289 text_width_max = IntMax(text_width_max, plinedata->width);
290
291 plinedata->length = length;
292 if (lf && (lf > text) && (lf[-1] == '\r')) {
293 plinedata->length--;
294 }
295
296 text += length + 1;
297
298 // Break if there are no more linefeeds.
299 if (!lf) {
300 break;
301 }
302 }
303
304 // Bump up the text height slightly.
305 data->text_height += 2;
306 }
307
308 // Loop through all buttons and calculate the button widths and height.
309 for (i = 0; i < data->numbuttons; i++) {
310 int height;
311
312 data->buttonpos[i].buttondata = &data->buttondata[i];
313 data->buttonpos[i].length = SDL_strlen(data->buttondata[i].text);
314
315 GetTextWidthHeight(data, data->buttondata[i].text, SDL_strlen(data->buttondata[i].text),
316 &data->buttonpos[i].text_width, &height);
317
318 button_width = IntMax(button_width, data->buttonpos[i].text_width);
319 button_text_height = IntMax(button_text_height, height);
320 }
321
322 if (data->numlines) {
323 // x,y for this line of text.
324 data->xtext = data->text_height;
325 data->ytext = data->text_height + data->text_height;
326
327 // Bump button y down to bottom of text.
328 ybuttons = 3 * data->ytext / 2 + (data->numlines - 1) * data->text_height;
329
330 // Bump the dialog box width and height up if needed.
331 data->dialog_width = IntMax(data->dialog_width, 2 * data->xtext + text_width_max);
332 data->dialog_height = IntMax(data->dialog_height, ybuttons);
333 } else {
334 // Button y starts at height of button text.
335 ybuttons = button_text_height;
336 }
337
338 if (data->numbuttons) {
339 int x, y;
340 int width_of_buttons;
341 int button_spacing = button_text_height;
342 int button_height = 2 * button_text_height;
343
344 // Bump button width up a bit.
345 button_width += button_text_height;
346
347 // Get width of all buttons lined up.
348 width_of_buttons = data->numbuttons * button_width + (data->numbuttons - 1) * button_spacing;
349
350 // Bump up dialog width and height if buttons are wider than text.
351 data->dialog_width = IntMax(data->dialog_width, width_of_buttons + 2 * button_spacing);
352 data->dialog_height = IntMax(data->dialog_height, ybuttons + 2 * button_height);
353
354 // Location for first button.
355 if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) {
356 x = data->dialog_width - (data->dialog_width - width_of_buttons) / 2 - (button_width + button_spacing);
357 } else {
358 x = (data->dialog_width - width_of_buttons) / 2;
359 }
360 y = ybuttons + (data->dialog_height - ybuttons - button_height) / 2;
361
362 for (i = 0; i < data->numbuttons; i++) {
363 // Button coordinates.
364 data->buttonpos[i].rect.x = x;
365 data->buttonpos[i].rect.y = y;
366 data->buttonpos[i].rect.w = button_width;
367 data->buttonpos[i].rect.h = button_height;
368
369 // Button text coordinates.
370 data->buttonpos[i].x = x + (button_width - data->buttonpos[i].text_width) / 2;
371 data->buttonpos[i].y = y + (button_height - button_text_height - 1) / 2 + button_text_height;
372
373 // Scoot over for next button.
374 if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) {
375 x -= button_width + button_spacing;
376 } else {
377 x += button_width + button_spacing;
378 }
379 }
380 }
381
382 return true;
383}
384
385// Free SDL_MessageBoxData data.
386static void X11_MessageBoxShutdown(SDL_MessageBoxDataX11 *data)
387{
388 if (data->font_set) {
389 X11_XFreeFontSet(data->display, data->font_set);
390 data->font_set = NULL;
391 }
392
393 if (data->font_struct) {
394 X11_XFreeFont(data->display, data->font_struct);
395 data->font_struct = NULL;
396 }
397
398#ifdef SDL_VIDEO_DRIVER_X11_XDBE
399 if (SDL_X11_HAVE_XDBE && data->xdbe) {
400 X11_XdbeDeallocateBackBufferName(data->display, data->buf);
401 }
402#endif
403
404 if (data->display) {
405 if (data->window != None) {
406 X11_XWithdrawWindow(data->display, data->window, data->screen);
407 X11_XDestroyWindow(data->display, data->window);
408 data->window = None;
409 }
410
411 X11_XCloseDisplay(data->display);
412 data->display = NULL;
413 }
414
415 SDL_free(data->linedata);
416}
417
418// Create and set up our X11 dialog box indow.
419static bool X11_MessageBoxCreateWindow(SDL_MessageBoxDataX11 *data)
420{
421 int x, y;
422 XSizeHints *sizehints;
423 XSetWindowAttributes wnd_attr;
424 Atom _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG;
425 Display *display = data->display;
426 SDL_WindowData *windowdata = NULL;
427 const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
428
429 if (messageboxdata->window) {
430 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(messageboxdata->window);
431 windowdata = messageboxdata->window->internal;
432 data->screen = displaydata->screen;
433 } else {
434 data->screen = DefaultScreen(display);
435 }
436
437 data->event_mask = ExposureMask |
438 ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
439 StructureNotifyMask | FocusChangeMask | PointerMotionMask;
440 wnd_attr.event_mask = data->event_mask;
441
442 data->window = X11_XCreateWindow(
443 display, RootWindow(display, data->screen),
444 0, 0,
445 data->dialog_width, data->dialog_height,
446 0, CopyFromParent, InputOutput, CopyFromParent,
447 CWEventMask, &wnd_attr);
448 if (data->window == None) {
449 return SDL_SetError("Couldn't create X window");
450 }
451
452 if (windowdata) {
453 Atom _NET_WM_STATE = X11_XInternAtom(display, "_NET_WM_STATE", False);
454 Atom stateatoms[16];
455 size_t statecount = 0;
456 // Set some message-boxy window states when attached to a parent window...
457 // we skip the taskbar since this will pop to the front when the parent window is clicked in the taskbar, etc
458 stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", False);
459 stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_PAGER", False);
460 stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_FOCUSED", False);
461 stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_MODAL", False);
462 SDL_assert(statecount <= SDL_arraysize(stateatoms));
463 X11_XChangeProperty(display, data->window, _NET_WM_STATE, XA_ATOM, 32,
464 PropModeReplace, (unsigned char *)stateatoms, statecount);
465
466 // http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR
467 X11_XSetTransientForHint(display, data->window, windowdata->xwindow);
468 }
469
470 SDL_X11_SetWindowTitle(display, data->window, (char *)messageboxdata->title);
471
472 // Let the window manager know this is a dialog box
473 _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
474 _NET_WM_WINDOW_TYPE_DIALOG = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
475 X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
476 PropModeReplace,
477 (unsigned char *)&_NET_WM_WINDOW_TYPE_DIALOG, 1);
478
479 // Allow the window to be deleted by the window manager
480 data->wm_delete_message = X11_XInternAtom(display, "WM_DELETE_WINDOW", False);
481 X11_XSetWMProtocols(display, data->window, &data->wm_delete_message, 1);
482
483 data->wm_protocols = X11_XInternAtom(display, "WM_PROTOCOLS", False);
484
485 if (windowdata) {
486 XWindowAttributes attrib;
487 Window dummy;
488
489 X11_XGetWindowAttributes(display, windowdata->xwindow, &attrib);
490 x = attrib.x + (attrib.width - data->dialog_width) / 2;
491 y = attrib.y + (attrib.height - data->dialog_height) / 3;
492 X11_XTranslateCoordinates(display, windowdata->xwindow, RootWindow(display, data->screen), x, y, &x, &y, &dummy);
493 } else {
494 const SDL_VideoDevice *dev = SDL_GetVideoDevice();
495 if (dev && dev->displays && dev->num_displays > 0) {
496 const SDL_VideoDisplay *dpy = dev->displays[0];
497 const SDL_DisplayData *dpydata = dpy->internal;
498 x = dpydata->x + ((dpy->current_mode->w - data->dialog_width) / 2);
499 y = dpydata->y + ((dpy->current_mode->h - data->dialog_height) / 3);
500 } else { // oh well. This will misposition on a multi-head setup. Init first next time.
501 x = (DisplayWidth(display, data->screen) - data->dialog_width) / 2;
502 y = (DisplayHeight(display, data->screen) - data->dialog_height) / 3;
503 }
504 }
505 X11_XMoveWindow(display, data->window, x, y);
506
507 sizehints = X11_XAllocSizeHints();
508 if (sizehints) {
509 sizehints->flags = USPosition | USSize | PMaxSize | PMinSize;
510 sizehints->x = x;
511 sizehints->y = y;
512 sizehints->width = data->dialog_width;
513 sizehints->height = data->dialog_height;
514
515 sizehints->min_width = sizehints->max_width = data->dialog_width;
516 sizehints->min_height = sizehints->max_height = data->dialog_height;
517
518 X11_XSetWMNormalHints(display, data->window, sizehints);
519
520 X11_XFree(sizehints);
521 }
522
523 X11_XMapRaised(display, data->window);
524
525#ifdef SDL_VIDEO_DRIVER_X11_XDBE
526 // Initialise a back buffer for double buffering
527 if (SDL_X11_HAVE_XDBE) {
528 int xdbe_major, xdbe_minor;
529 if (X11_XdbeQueryExtension(display, &xdbe_major, &xdbe_minor) != 0) {
530 data->xdbe = true;
531 data->buf = X11_XdbeAllocateBackBufferName(display, data->window, XdbeUndefined);
532 } else {
533 data->xdbe = false;
534 }
535 }
536#endif
537
538 return true;
539}
540
541// Draw our message box.
542static void X11_MessageBoxDraw(SDL_MessageBoxDataX11 *data, GC ctx)
543{
544 int i;
545 Drawable window = data->window;
546 Display *display = data->display;
547
548#ifdef SDL_VIDEO_DRIVER_X11_XDBE
549 if (SDL_X11_HAVE_XDBE && data->xdbe) {
550 window = data->buf;
551 X11_XdbeBeginIdiom(data->display);
552 }
553#endif
554
555 X11_XSetForeground(display, ctx, data->color[SDL_MESSAGEBOX_COLOR_BACKGROUND]);
556 X11_XFillRectangle(display, window, ctx, 0, 0, data->dialog_width, data->dialog_height);
557
558 X11_XSetForeground(display, ctx, data->color[SDL_MESSAGEBOX_COLOR_TEXT]);
559 for (i = 0; i < data->numlines; i++) {
560 TextLineData *plinedata = &data->linedata[i];
561
562#ifdef X_HAVE_UTF8_STRING
563 if (SDL_X11_HAVE_UTF8) {
564 X11_Xutf8DrawString(display, window, data->font_set, ctx,
565 data->xtext, data->ytext + i * data->text_height,
566 plinedata->text, plinedata->length);
567 } else
568#endif
569 {
570 X11_XDrawString(display, window, ctx,
571 data->xtext, data->ytext + i * data->text_height,
572 plinedata->text, plinedata->length);
573 }
574 }
575
576 for (i = 0; i < data->numbuttons; i++) {
577 SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[i];
578 const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata;
579 int border = (buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) ? 2 : 0;
580 int offset = ((data->mouse_over_index == i) && (data->button_press_index == data->mouse_over_index)) ? 1 : 0;
581
582 X11_XSetForeground(display, ctx, data->color[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND]);
583 X11_XFillRectangle(display, window, ctx,
584 buttondatax11->rect.x - border, buttondatax11->rect.y - border,
585 buttondatax11->rect.w + 2 * border, buttondatax11->rect.h + 2 * border);
586
587 X11_XSetForeground(display, ctx, data->color[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER]);
588 X11_XDrawRectangle(display, window, ctx,
589 buttondatax11->rect.x, buttondatax11->rect.y,
590 buttondatax11->rect.w, buttondatax11->rect.h);
591
592 X11_XSetForeground(display, ctx, (data->mouse_over_index == i) ? data->color[SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED] : data->color[SDL_MESSAGEBOX_COLOR_TEXT]);
593
594#ifdef X_HAVE_UTF8_STRING
595 if (SDL_X11_HAVE_UTF8) {
596 X11_Xutf8DrawString(display, window, data->font_set, ctx,
597 buttondatax11->x + offset,
598 buttondatax11->y + offset,
599 buttondata->text, buttondatax11->length);
600 } else
601#endif
602 {
603 X11_XDrawString(display, window, ctx,
604 buttondatax11->x + offset, buttondatax11->y + offset,
605 buttondata->text, buttondatax11->length);
606 }
607 }
608
609#ifdef SDL_VIDEO_DRIVER_X11_XDBE
610 if (SDL_X11_HAVE_XDBE && data->xdbe) {
611 XdbeSwapInfo swap_info;
612 swap_info.swap_window = data->window;
613 swap_info.swap_action = XdbeUndefined;
614 X11_XdbeSwapBuffers(data->display, &swap_info, 1);
615 X11_XdbeEndIdiom(data->display);
616 }
617#endif
618}
619
620// NOLINTNEXTLINE(readability-non-const-parameter): cannot make XPointer a const pointer due to typedef
621static Bool X11_MessageBoxEventTest(Display *display, XEvent *event, XPointer arg)
622{
623 const SDL_MessageBoxDataX11 *data = (const SDL_MessageBoxDataX11 *)arg;
624 return ((event->xany.display == data->display) && (event->xany.window == data->window)) ? True : False;
625}
626
627// Loop and handle message box event messages until something kills it.
628static bool X11_MessageBoxLoop(SDL_MessageBoxDataX11 *data)
629{
630 GC ctx;
631 XGCValues ctx_vals;
632 bool close_dialog = false;
633 bool has_focus = true;
634 KeySym last_key_pressed = XK_VoidSymbol;
635 unsigned long gcflags = GCForeground | GCBackground;
636#ifdef X_HAVE_UTF8_STRING
637 const int have_utf8 = SDL_X11_HAVE_UTF8;
638#else
639 const int have_utf8 = 0;
640#endif
641
642 SDL_zero(ctx_vals);
643 ctx_vals.foreground = data->color[SDL_MESSAGEBOX_COLOR_BACKGROUND];
644 ctx_vals.background = data->color[SDL_MESSAGEBOX_COLOR_BACKGROUND];
645
646 if (!have_utf8) {
647 gcflags |= GCFont;
648 ctx_vals.font = data->font_struct->fid;
649 }
650
651 ctx = X11_XCreateGC(data->display, data->window, gcflags, &ctx_vals);
652 if (ctx == None) {
653 return SDL_SetError("Couldn't create graphics context");
654 }
655
656 data->button_press_index = -1; // Reset what button is currently depressed.
657 data->mouse_over_index = -1; // Reset what button the mouse is over.
658
659 while (!close_dialog) {
660 XEvent e;
661 bool draw = true;
662
663 // can't use XWindowEvent() because it can't handle ClientMessage events.
664 // can't use XNextEvent() because we only want events for this window.
665 X11_XIfEvent(data->display, &e, X11_MessageBoxEventTest, (XPointer)data);
666
667 /* If X11_XFilterEvent returns True, then some input method has filtered the
668 event, and the client should discard the event. */
669 if ((e.type != Expose) && X11_XFilterEvent(&e, None)) {
670 continue;
671 }
672
673 switch (e.type) {
674 case Expose:
675 if (e.xexpose.count > 0) {
676 draw = false;
677 }
678 break;
679
680 case FocusIn:
681 // Got focus.
682 has_focus = true;
683 break;
684
685 case FocusOut:
686 // lost focus. Reset button and mouse info.
687 has_focus = false;
688 data->button_press_index = -1;
689 data->mouse_over_index = -1;
690 break;
691
692 case MotionNotify:
693 if (has_focus) {
694 // Mouse moved...
695 const int previndex = data->mouse_over_index;
696 data->mouse_over_index = GetHitButtonIndex(data, e.xbutton.x, e.xbutton.y);
697 if (data->mouse_over_index == previndex) {
698 draw = false;
699 }
700 }
701 break;
702
703 case ClientMessage:
704 if (e.xclient.message_type == data->wm_protocols &&
705 e.xclient.format == 32 &&
706 e.xclient.data.l[0] == data->wm_delete_message) {
707 close_dialog = true;
708 }
709 break;
710
711 case KeyPress:
712 // Store key press - we make sure in key release that we got both.
713 last_key_pressed = X11_XLookupKeysym(&e.xkey, 0);
714 break;
715
716 case KeyRelease:
717 {
718 Uint32 mask = 0;
719 KeySym key = X11_XLookupKeysym(&e.xkey, 0);
720
721 // If this is a key release for something we didn't get the key down for, then bail.
722 if (key != last_key_pressed) {
723 break;
724 }
725
726 if (key == XK_Escape) {
727 mask = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
728 } else if ((key == XK_Return) || (key == XK_KP_Enter)) {
729 mask = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
730 }
731
732 if (mask) {
733 int i;
734
735 // Look for first button with this mask set, and return it if found.
736 for (i = 0; i < data->numbuttons; i++) {
737 SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[i];
738
739 if (buttondatax11->buttondata->flags & mask) {
740 *data->pbuttonid = buttondatax11->buttondata->buttonID;
741 close_dialog = true;
742 break;
743 }
744 }
745 }
746 break;
747 }
748
749 case ButtonPress:
750 data->button_press_index = -1;
751 if (e.xbutton.button == Button1) {
752 // Find index of button they clicked on.
753 data->button_press_index = GetHitButtonIndex(data, e.xbutton.x, e.xbutton.y);
754 }
755 break;
756
757 case ButtonRelease:
758 // If button is released over the same button that was clicked down on, then return it.
759 if ((e.xbutton.button == Button1) && (data->button_press_index >= 0)) {
760 int button = GetHitButtonIndex(data, e.xbutton.x, e.xbutton.y);
761
762 if (data->button_press_index == button) {
763 SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[button];
764
765 *data->pbuttonid = buttondatax11->buttondata->buttonID;
766 close_dialog = true;
767 }
768 }
769 data->button_press_index = -1;
770 break;
771 }
772
773 if (draw) {
774 // Draw our dialog box.
775 X11_MessageBoxDraw(data, ctx);
776 }
777 }
778
779 X11_XFreeGC(data->display, ctx);
780 return true;
781}
782
783static bool X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonID)
784{
785 bool result = false;
786 SDL_MessageBoxDataX11 data;
787#if SDL_SET_LOCALE
788 char *origlocale;
789#endif
790
791 SDL_zero(data);
792
793 if (!SDL_X11_LoadSymbols()) {
794 return false;
795 }
796
797#if SDL_SET_LOCALE
798 origlocale = setlocale(LC_ALL, NULL);
799 if (origlocale) {
800 origlocale = SDL_strdup(origlocale);
801 if (!origlocale) {
802 return false;
803 }
804 (void)setlocale(LC_ALL, "");
805 }
806#endif
807
808 // This code could get called from multiple threads maybe?
809 X11_XInitThreads();
810
811 // Initialize the return buttonID value to -1 (for error or dialogbox closed).
812 *buttonID = -1;
813
814 // Init and display the message box.
815 if (X11_MessageBoxInit(&data, messageboxdata, buttonID) &&
816 X11_MessageBoxInitPositions(&data) &&
817 X11_MessageBoxCreateWindow(&data)) {
818 result = X11_MessageBoxLoop(&data);
819 }
820
821 X11_MessageBoxShutdown(&data);
822
823#if SDL_SET_LOCALE
824 if (origlocale) {
825 (void)setlocale(LC_ALL, origlocale);
826 SDL_free(origlocale);
827 }
828#endif
829
830 return result;
831}
832
833// Display an x11 message box.
834bool X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
835{
836#if SDL_FORK_MESSAGEBOX
837 // Use a child process to protect against setlocale(). Annoying.
838 pid_t pid;
839 int fds[2];
840 int status = 0;
841 bool result = true;
842
843 if (pipe(fds) == -1) {
844 return X11_ShowMessageBoxImpl(messageboxdata, buttonID); // oh well.
845 }
846
847 pid = fork();
848 if (pid == -1) { // failed
849 close(fds[0]);
850 close(fds[1]);
851 return X11_ShowMessageBoxImpl(messageboxdata, buttonID); // oh well.
852 } else if (pid == 0) { // we're the child
853 int exitcode = 0;
854 close(fds[0]);
855 result = X11_ShowMessageBoxImpl(messageboxdata, buttonID);
856 if (write(fds[1], &result, sizeof(result)) != sizeof(result)) {
857 exitcode = 1;
858 } else if (write(fds[1], buttonID, sizeof(*buttonID)) != sizeof(*buttonID)) {
859 exitcode = 1;
860 }
861 close(fds[1]);
862 _exit(exitcode); // don't run atexit() stuff, static destructors, etc.
863 } else { // we're the parent
864 pid_t rc;
865 close(fds[1]);
866 do {
867 rc = waitpid(pid, &status, 0);
868 } while ((rc == -1) && (errno == EINTR));
869
870 SDL_assert(rc == pid); // not sure what to do if this fails.
871
872 if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) {
873 result = SDL_SetError("msgbox child process failed");
874 } else if ((read(fds[0], &result, sizeof(result)) != sizeof(result)) ||
875 (read(fds[0], buttonID, sizeof(*buttonID)) != sizeof(*buttonID))) {
876 result = SDL_SetError("read from msgbox child process failed");
877 *buttonID = 0;
878 }
879 close(fds[0]);
880
881 return result;
882 }
883#else
884 return X11_ShowMessageBoxImpl(messageboxdata, buttonID);
885#endif
886}
887#endif // SDL_VIDEO_DRIVER_X11
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11messagebox.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11messagebox.h
new file mode 100644
index 0000000..9baa875
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11messagebox.h
@@ -0,0 +1,31 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22#ifndef SDL_x11messagebox_h_
23#define SDL_x11messagebox_h_
24
25#ifdef SDL_VIDEO_DRIVER_X11
26
27extern bool X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID);
28
29#endif // SDL_VIDEO_DRIVER_X11
30
31#endif // SDL_x11messagebox_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11modes.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11modes.c
new file mode 100644
index 0000000..e17a2d1
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11modes.c
@@ -0,0 +1,1051 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_DRIVER_X11
24
25#include "SDL_x11video.h"
26#include "SDL_x11settings.h"
27#include "edid.h"
28#include "../../events/SDL_displayevents_c.h"
29
30// #define X11MODES_DEBUG
31
32/* Timeout and revert mode switches if the timespan has elapsed without the window becoming fullscreen.
33 * 5 seconds seems good from testing.
34 */
35#define MODE_SWITCH_TIMEOUT_NS SDL_NS_PER_SECOND * 5
36
37/* I'm becoming more and more convinced that the application should never
38 * use XRandR, and it's the window manager's responsibility to track and
39 * manage display modes for fullscreen windows. Right now XRandR is completely
40 * broken with respect to window manager behavior on every window manager that
41 * I can find. For example, on Unity 3D if you show a fullscreen window while
42 * the resolution is changing (within ~250 ms) your window will retain the
43 * fullscreen state hint but be decorated and windowed.
44 *
45 * However, many people swear by it, so let them swear at it. :)
46 */
47// #define XRANDR_DISABLED_BY_DEFAULT
48
49static float GetGlobalContentScale(SDL_VideoDevice *_this)
50{
51 static double scale_factor = 0.0;
52
53 if (scale_factor <= 0.0) {
54
55 // First use the forced scaling factor specified by the app/user
56 const char *hint = SDL_GetHint(SDL_HINT_VIDEO_X11_SCALING_FACTOR);
57 if (hint && *hint) {
58 double value = SDL_atof(hint);
59 if (value >= 1.0f && value <= 10.0f) {
60 scale_factor = value;
61 }
62 }
63
64 // If that failed, try "Xft.dpi" from the XResourcesDatabase...
65 if (scale_factor <= 0.0)
66 {
67 SDL_VideoData *data = _this->internal;
68 Display *display = data->display;
69 char *resource_manager;
70 XrmDatabase db;
71 XrmValue value;
72 char *type;
73
74 X11_XrmInitialize();
75
76 resource_manager = X11_XResourceManagerString(display);
77 if (resource_manager) {
78 db = X11_XrmGetStringDatabase(resource_manager);
79
80 // Get the value of Xft.dpi from the Database
81 if (X11_XrmGetResource(db, "Xft.dpi", "String", &type, &value)) {
82 if (value.addr && type && SDL_strcmp(type, "String") == 0) {
83 int dpi = SDL_atoi(value.addr);
84 scale_factor = dpi / 96.0;
85 }
86 }
87 X11_XrmDestroyDatabase(db);
88 }
89 }
90
91 // If that failed, try the XSETTINGS keys...
92 if (scale_factor <= 0.0) {
93 scale_factor = X11_GetXsettingsIntKey(_this, "Gdk/WindowScalingFactor", -1);
94
95 // The Xft/DPI key is stored in increments of 1024th
96 if (scale_factor <= 0.0) {
97 int dpi = X11_GetXsettingsIntKey(_this, "Xft/DPI", -1);
98 if (dpi > 0) {
99 scale_factor = (double) dpi / 1024.0;
100 scale_factor /= 96.0;
101 }
102 }
103 }
104
105 // If that failed, try the GDK_SCALE envvar...
106 if (scale_factor <= 0.0) {
107 const char *scale_str = SDL_getenv("GDK_SCALE");
108 if (scale_str) {
109 scale_factor = SDL_atoi(scale_str);
110 }
111 }
112
113 // Nothing or a bad value, just fall back to 1.0
114 if (scale_factor <= 0.0) {
115 scale_factor = 1.0;
116 }
117 }
118
119 return (float)scale_factor;
120}
121
122static bool get_visualinfo(Display *display, int screen, XVisualInfo *vinfo)
123{
124 const char *visual_id = SDL_GetHint(SDL_HINT_VIDEO_X11_VISUALID);
125 int depth;
126
127 // Look for an exact visual, if requested
128 if (visual_id && *visual_id) {
129 XVisualInfo *vi, template;
130 int nvis;
131
132 SDL_zero(template);
133 template.visualid = SDL_strtol(visual_id, NULL, 0);
134 vi = X11_XGetVisualInfo(display, VisualIDMask, &template, &nvis);
135 if (vi) {
136 *vinfo = *vi;
137 X11_XFree(vi);
138 return true;
139 }
140 }
141
142 depth = DefaultDepth(display, screen);
143 if ((X11_UseDirectColorVisuals() &&
144 X11_XMatchVisualInfo(display, screen, depth, DirectColor, vinfo)) ||
145 X11_XMatchVisualInfo(display, screen, depth, TrueColor, vinfo) ||
146 X11_XMatchVisualInfo(display, screen, depth, PseudoColor, vinfo) ||
147 X11_XMatchVisualInfo(display, screen, depth, StaticColor, vinfo)) {
148 return true;
149 }
150 return false;
151}
152
153bool X11_GetVisualInfoFromVisual(Display *display, Visual *visual, XVisualInfo *vinfo)
154{
155 XVisualInfo *vi;
156 int nvis;
157
158 vinfo->visualid = X11_XVisualIDFromVisual(visual);
159 vi = X11_XGetVisualInfo(display, VisualIDMask, vinfo, &nvis);
160 if (vi) {
161 *vinfo = *vi;
162 X11_XFree(vi);
163 return true;
164 }
165 return false;
166}
167
168SDL_PixelFormat X11_GetPixelFormatFromVisualInfo(Display *display, XVisualInfo *vinfo)
169{
170 if (vinfo->class == DirectColor || vinfo->class == TrueColor) {
171 int bpp;
172 Uint32 Rmask, Gmask, Bmask, Amask;
173
174 Rmask = vinfo->visual->red_mask;
175 Gmask = vinfo->visual->green_mask;
176 Bmask = vinfo->visual->blue_mask;
177 if (vinfo->depth == 32) {
178 Amask = (0xFFFFFFFF & ~(Rmask | Gmask | Bmask));
179 } else {
180 Amask = 0;
181 }
182
183 bpp = vinfo->depth;
184 if (bpp == 24) {
185 int i, n;
186 XPixmapFormatValues *p = X11_XListPixmapFormats(display, &n);
187 if (p) {
188 for (i = 0; i < n; ++i) {
189 if (p[i].depth == 24) {
190 bpp = p[i].bits_per_pixel;
191 break;
192 }
193 }
194 X11_XFree(p);
195 }
196 }
197
198 return SDL_GetPixelFormatForMasks(bpp, Rmask, Gmask, Bmask, Amask);
199 }
200
201 if (vinfo->class == PseudoColor || vinfo->class == StaticColor) {
202 switch (vinfo->depth) {
203 case 8:
204 return SDL_PIXELFORMAT_INDEX8;
205 case 4:
206 if (BitmapBitOrder(display) == LSBFirst) {
207 return SDL_PIXELFORMAT_INDEX4LSB;
208 } else {
209 return SDL_PIXELFORMAT_INDEX4MSB;
210 }
211 // break; -Wunreachable-code-break
212 case 1:
213 if (BitmapBitOrder(display) == LSBFirst) {
214 return SDL_PIXELFORMAT_INDEX1LSB;
215 } else {
216 return SDL_PIXELFORMAT_INDEX1MSB;
217 }
218 // break; -Wunreachable-code-break
219 }
220 }
221
222 return SDL_PIXELFORMAT_UNKNOWN;
223}
224
225#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
226static bool CheckXRandR(Display *display, int *major, int *minor)
227{
228 // Default the extension not available
229 *major = *minor = 0;
230
231 // Allow environment override
232#ifdef XRANDR_DISABLED_BY_DEFAULT
233 if (!SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XRANDR, false)) {
234#ifdef X11MODES_DEBUG
235 printf("XRandR disabled by default due to window manager issues\n");
236#endif
237 return false;
238 }
239#else
240 if (!SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XRANDR, true)) {
241#ifdef X11MODES_DEBUG
242 printf("XRandR disabled due to hint\n");
243#endif
244 return false;
245 }
246#endif // XRANDR_DISABLED_BY_DEFAULT
247
248 if (!SDL_X11_HAVE_XRANDR) {
249#ifdef X11MODES_DEBUG
250 printf("XRandR support not available\n");
251#endif
252 return false;
253 }
254
255 // Query the extension version
256 *major = 1;
257 *minor = 3; // we want 1.3
258 if (!X11_XRRQueryVersion(display, major, minor)) {
259#ifdef X11MODES_DEBUG
260 printf("XRandR not active on the display\n");
261#endif
262 *major = *minor = 0;
263 return false;
264 }
265#ifdef X11MODES_DEBUG
266 printf("XRandR available at version %d.%d!\n", *major, *minor);
267#endif
268 return true;
269}
270
271#define XRANDR_ROTATION_LEFT (1 << 1)
272#define XRANDR_ROTATION_RIGHT (1 << 3)
273
274static void CalculateXRandRRefreshRate(const XRRModeInfo *info, int *numerator, int *denominator)
275{
276 unsigned int vTotal = info->vTotal;
277
278 if (info->modeFlags & RR_DoubleScan) {
279 // doublescan doubles the number of lines
280 vTotal *= 2;
281 }
282
283 if (info->modeFlags & RR_Interlace) {
284 // interlace splits the frame into two fields
285 // the field rate is what is typically reported by monitors
286 vTotal /= 2;
287 }
288
289 if (info->hTotal && vTotal) {
290 *numerator = info->dotClock;
291 *denominator = (info->hTotal * vTotal);
292 } else {
293 *numerator = 0;
294 *denominator = 0;
295 }
296}
297
298static bool SetXRandRModeInfo(Display *display, XRRScreenResources *res, RRCrtc crtc,
299 RRMode modeID, SDL_DisplayMode *mode)
300{
301 int i;
302 for (i = 0; i < res->nmode; ++i) {
303 const XRRModeInfo *info = &res->modes[i];
304 if (info->id == modeID) {
305 XRRCrtcInfo *crtcinfo;
306 Rotation rotation = 0;
307 XFixed scale_w = 0x10000, scale_h = 0x10000;
308 XRRCrtcTransformAttributes *attr;
309
310 crtcinfo = X11_XRRGetCrtcInfo(display, res, crtc);
311 if (crtcinfo) {
312 rotation = crtcinfo->rotation;
313 X11_XRRFreeCrtcInfo(crtcinfo);
314 }
315 if (X11_XRRGetCrtcTransform(display, crtc, &attr) && attr) {
316 scale_w = attr->currentTransform.matrix[0][0];
317 scale_h = attr->currentTransform.matrix[1][1];
318 X11_XFree(attr);
319 }
320
321 if (rotation & (XRANDR_ROTATION_LEFT | XRANDR_ROTATION_RIGHT)) {
322 mode->w = (info->height * scale_w + 0xffff) >> 16;
323 mode->h = (info->width * scale_h + 0xffff) >> 16;
324 } else {
325 mode->w = (info->width * scale_w + 0xffff) >> 16;
326 mode->h = (info->height * scale_h + 0xffff) >> 16;
327 }
328 CalculateXRandRRefreshRate(info, &mode->refresh_rate_numerator, &mode->refresh_rate_denominator);
329 mode->internal->xrandr_mode = modeID;
330#ifdef X11MODES_DEBUG
331 printf("XRandR mode %d: %dx%d@%d/%dHz\n", (int)modeID,
332 mode->screen_w, mode->screen_h, mode->refresh_rate_numerator, mode->refresh_rate_denominator);
333#endif
334 return true;
335 }
336 }
337 return false;
338}
339
340static void SetXRandRDisplayName(Display *dpy, Atom EDID, char *name, const size_t namelen, RROutput output, const unsigned long widthmm, const unsigned long heightmm)
341{
342 // See if we can get the EDID data for the real monitor name
343 int inches;
344 int nprop;
345 Atom *props = X11_XRRListOutputProperties(dpy, output, &nprop);
346 int i;
347
348 for (i = 0; i < nprop; ++i) {
349 unsigned char *prop;
350 int actual_format;
351 unsigned long nitems, bytes_after;
352 Atom actual_type;
353
354 if (props[i] == EDID) {
355 if (X11_XRRGetOutputProperty(dpy, output, props[i], 0, 100, False,
356 False, AnyPropertyType, &actual_type,
357 &actual_format, &nitems, &bytes_after,
358 &prop) == Success) {
359 MonitorInfo *info = decode_edid(prop);
360 if (info) {
361#ifdef X11MODES_DEBUG
362 printf("Found EDID data for %s\n", name);
363 dump_monitor_info(info);
364#endif
365 SDL_strlcpy(name, info->dsc_product_name, namelen);
366 SDL_free(info);
367 }
368 X11_XFree(prop);
369 }
370 break;
371 }
372 }
373
374 if (props) {
375 X11_XFree(props);
376 }
377
378 inches = (int)((SDL_sqrtf(widthmm * widthmm + heightmm * heightmm) / 25.4f) + 0.5f);
379 if (*name && inches) {
380 const size_t len = SDL_strlen(name);
381 (void)SDL_snprintf(&name[len], namelen - len, " %d\"", inches);
382 }
383
384#ifdef X11MODES_DEBUG
385 printf("Display name: %s\n", name);
386#endif
387}
388
389static bool X11_FillXRandRDisplayInfo(SDL_VideoDevice *_this, Display *dpy, int screen, RROutput outputid, XRRScreenResources *res, SDL_VideoDisplay *display, char *display_name, size_t display_name_size)
390{
391 Atom EDID = X11_XInternAtom(dpy, "EDID", False);
392 XRROutputInfo *output_info;
393 int display_x, display_y;
394 unsigned long display_mm_width, display_mm_height;
395 SDL_DisplayData *displaydata;
396 SDL_DisplayMode mode;
397 SDL_DisplayModeData *modedata;
398 RRMode modeID;
399 RRCrtc output_crtc;
400 XRRCrtcInfo *crtc;
401 XVisualInfo vinfo;
402 Uint32 pixelformat;
403 XPixmapFormatValues *pixmapformats;
404 int scanline_pad;
405 int i, n;
406
407 if (!display || !display_name) {
408 return false; // invalid parameters
409 }
410
411 if (!get_visualinfo(dpy, screen, &vinfo)) {
412 return false; // uh, skip this screen?
413 }
414
415 pixelformat = X11_GetPixelFormatFromVisualInfo(dpy, &vinfo);
416 if (SDL_ISPIXELFORMAT_INDEXED(pixelformat)) {
417 return false; // Palettized video modes are no longer supported, ignore this one.
418 }
419
420 scanline_pad = SDL_BYTESPERPIXEL(pixelformat) * 8;
421 pixmapformats = X11_XListPixmapFormats(dpy, &n);
422 if (pixmapformats) {
423 for (i = 0; i < n; i++) {
424 if (pixmapformats[i].depth == vinfo.depth) {
425 scanline_pad = pixmapformats[i].scanline_pad;
426 break;
427 }
428 }
429 X11_XFree(pixmapformats);
430 }
431
432 output_info = X11_XRRGetOutputInfo(dpy, res, outputid);
433 if (!output_info || !output_info->crtc || output_info->connection == RR_Disconnected) {
434 X11_XRRFreeOutputInfo(output_info);
435 return false; // ignore this one.
436 }
437
438 SDL_strlcpy(display_name, output_info->name, display_name_size);
439 display_mm_width = output_info->mm_width;
440 display_mm_height = output_info->mm_height;
441 output_crtc = output_info->crtc;
442 X11_XRRFreeOutputInfo(output_info);
443
444 crtc = X11_XRRGetCrtcInfo(dpy, res, output_crtc);
445 if (!crtc) {
446 return false; // oh well, ignore it.
447 }
448
449 SDL_zero(mode);
450 modeID = crtc->mode;
451 mode.w = crtc->width;
452 mode.h = crtc->height;
453 mode.format = pixelformat;
454
455 display_x = crtc->x;
456 display_y = crtc->y;
457
458 X11_XRRFreeCrtcInfo(crtc);
459
460 displaydata = (SDL_DisplayData *)SDL_calloc(1, sizeof(*displaydata));
461 if (!displaydata) {
462 return false;
463 }
464
465 modedata = (SDL_DisplayModeData *)SDL_calloc(1, sizeof(SDL_DisplayModeData));
466 if (!modedata) {
467 SDL_free(displaydata);
468 return false;
469 }
470
471 modedata->xrandr_mode = modeID;
472 mode.internal = modedata;
473
474 displaydata->screen = screen;
475 displaydata->visual = vinfo.visual;
476 displaydata->depth = vinfo.depth;
477 displaydata->scanline_pad = scanline_pad;
478 displaydata->x = display_x;
479 displaydata->y = display_y;
480 displaydata->use_xrandr = true;
481 displaydata->xrandr_output = outputid;
482 SDL_strlcpy(displaydata->connector_name, display_name, sizeof(displaydata->connector_name));
483
484 SetXRandRModeInfo(dpy, res, output_crtc, modeID, &mode);
485 SetXRandRDisplayName(dpy, EDID, display_name, display_name_size, outputid, display_mm_width, display_mm_height);
486
487 SDL_zero(*display);
488 if (*display_name) {
489 display->name = display_name;
490 }
491 display->desktop_mode = mode;
492 display->content_scale = GetGlobalContentScale(_this);
493 display->internal = displaydata;
494
495 return true;
496}
497
498static bool X11_AddXRandRDisplay(SDL_VideoDevice *_this, Display *dpy, int screen, RROutput outputid, XRRScreenResources *res, bool send_event)
499{
500 SDL_VideoDisplay display;
501 char display_name[128];
502
503 if (!X11_FillXRandRDisplayInfo(_this, dpy, screen, outputid, res, &display, display_name, sizeof(display_name))) {
504 return true; // failed to query data, skip this display
505 }
506
507 if (SDL_AddVideoDisplay(&display, send_event) == 0) {
508 return false;
509 }
510
511 return true;
512}
513
514
515static bool X11_UpdateXRandRDisplay(SDL_VideoDevice *_this, Display *dpy, int screen, RROutput outputid, XRRScreenResources *res, SDL_VideoDisplay *existing_display)
516{
517 SDL_VideoDisplay display;
518 char display_name[128];
519
520 if (!X11_FillXRandRDisplayInfo(_this, dpy, screen, outputid, res, &display, display_name, sizeof(display_name))) {
521 return false; // failed to query current display state
522 }
523
524 // update mode - this call takes ownership of display.desktop_mode.internal
525 SDL_SetDesktopDisplayMode(existing_display, &display.desktop_mode);
526
527 // update bounds
528 if (existing_display->internal->x != display.internal->x ||
529 existing_display->internal->y != display.internal->y) {
530 existing_display->internal->x = display.internal->x;
531 existing_display->internal->y = display.internal->y;
532 SDL_SendDisplayEvent(existing_display, SDL_EVENT_DISPLAY_MOVED, 0, 0);
533 }
534
535 // update scale
536 SDL_SetDisplayContentScale(existing_display, display.content_scale);
537
538 // SDL_DisplayData is updated piece-meal above, free our local copy of this data
539 SDL_free( display.internal );
540
541 return true;
542}
543
544static XRRScreenResources *X11_GetScreenResources(Display *dpy, int screen)
545{
546 XRRScreenResources *res = X11_XRRGetScreenResourcesCurrent(dpy, RootWindow(dpy, screen));
547 if (!res || res->noutput == 0) {
548 if (res) {
549 X11_XRRFreeScreenResources(res);
550 }
551 res = X11_XRRGetScreenResources(dpy, RootWindow(dpy, screen));
552 }
553 return res;
554}
555
556static void X11_CheckDisplaysMoved(SDL_VideoDevice *_this, Display *dpy)
557{
558 const int screen = DefaultScreen(dpy);
559 XRRScreenResources *res = X11_GetScreenResources(dpy, screen);
560 if (!res) {
561 return;
562 }
563
564 SDL_DisplayID *displays = SDL_GetDisplays(NULL);
565 if (displays) {
566 for (int i = 0; displays[i]; ++i) {
567 SDL_VideoDisplay *display = SDL_GetVideoDisplay(displays[i]);
568 const SDL_DisplayData *displaydata = display->internal;
569 X11_UpdateXRandRDisplay(_this, dpy, screen, displaydata->xrandr_output, res, display);
570 }
571 SDL_free(displays);
572 }
573 X11_XRRFreeScreenResources(res);
574}
575
576static void X11_HandleXRandROutputChange(SDL_VideoDevice *_this, const XRROutputChangeNotifyEvent *ev)
577{
578 SDL_DisplayID *displays;
579 SDL_VideoDisplay *display = NULL;
580 int i;
581
582#if 0
583 printf("XRROutputChangeNotifyEvent! [output=%u, crtc=%u, mode=%u, rotation=%u, connection=%u]", (unsigned int) ev->output, (unsigned int) ev->crtc, (unsigned int) ev->mode, (unsigned int) ev->rotation, (unsigned int) ev->connection);
584#endif
585
586 displays = SDL_GetDisplays(NULL);
587 if (displays) {
588 for (i = 0; displays[i]; ++i) {
589 SDL_VideoDisplay *thisdisplay = SDL_GetVideoDisplay(displays[i]);
590 const SDL_DisplayData *displaydata = thisdisplay->internal;
591 if (displaydata->xrandr_output == ev->output) {
592 display = thisdisplay;
593 break;
594 }
595 }
596 SDL_free(displays);
597 }
598
599 if (ev->connection == RR_Disconnected) { // output is going away
600 if (display) {
601 SDL_DelVideoDisplay(display->id, true);
602 }
603 X11_CheckDisplaysMoved(_this, ev->display);
604
605 } else if (ev->connection == RR_Connected) { // output is coming online
606 if (!display) {
607 Display *dpy = ev->display;
608 const int screen = DefaultScreen(dpy);
609 XRRScreenResources *res = X11_GetScreenResources(dpy, screen);
610 if (res) {
611 X11_AddXRandRDisplay(_this, dpy, screen, ev->output, res, true);
612 X11_XRRFreeScreenResources(res);
613 }
614 }
615 X11_CheckDisplaysMoved(_this, ev->display);
616 }
617}
618
619void X11_HandleXRandREvent(SDL_VideoDevice *_this, const XEvent *xevent)
620{
621 SDL_VideoData *videodata = _this->internal;
622 SDL_assert(xevent->type == (videodata->xrandr_event_base + RRNotify));
623
624 switch (((const XRRNotifyEvent *)xevent)->subtype) {
625 case RRNotify_OutputChange:
626 X11_HandleXRandROutputChange(_this, (const XRROutputChangeNotifyEvent *)xevent);
627 break;
628 default:
629 break;
630 }
631}
632
633static void X11_SortOutputsByPriorityHint(SDL_VideoDevice *_this)
634{
635 const char *name_hint = SDL_GetHint(SDL_HINT_VIDEO_DISPLAY_PRIORITY);
636
637 if (name_hint) {
638 char *saveptr;
639 char *str = SDL_strdup(name_hint);
640 SDL_VideoDisplay **sorted_list = SDL_malloc(sizeof(SDL_VideoDisplay *) * _this->num_displays);
641
642 if (str && sorted_list) {
643 int sorted_index = 0;
644
645 // Sort the requested displays to the front of the list.
646 const char *token = SDL_strtok_r(str, ",", &saveptr);
647 while (token) {
648 for (int i = 0; i < _this->num_displays; ++i) {
649 SDL_VideoDisplay *d = _this->displays[i];
650 if (d) {
651 SDL_DisplayData *data = d->internal;
652 if (SDL_strcmp(token, data->connector_name) == 0) {
653 sorted_list[sorted_index++] = d;
654 _this->displays[i] = NULL;
655 break;
656 }
657 }
658 }
659
660 token = SDL_strtok_r(NULL, ",", &saveptr);
661 }
662
663 // Append the remaining displays to the end of the list.
664 for (int i = 0; i < _this->num_displays; ++i) {
665 if (_this->displays[i]) {
666 sorted_list[sorted_index++] = _this->displays[i];
667 }
668 }
669
670 // Copy the sorted list back to the display list.
671 SDL_memcpy(_this->displays, sorted_list, sizeof(SDL_VideoDisplay *) * _this->num_displays);
672 }
673
674 SDL_free(str);
675 SDL_free(sorted_list);
676 }
677}
678
679static bool X11_InitModes_XRandR(SDL_VideoDevice *_this)
680{
681 SDL_VideoData *data = _this->internal;
682 Display *dpy = data->display;
683 const int screencount = ScreenCount(dpy);
684 const int default_screen = DefaultScreen(dpy);
685 RROutput primary = X11_XRRGetOutputPrimary(dpy, RootWindow(dpy, default_screen));
686 int xrandr_error_base = 0;
687 int looking_for_primary;
688 int output;
689 int screen;
690
691 if (!X11_XRRQueryExtension(dpy, &data->xrandr_event_base, &xrandr_error_base)) {
692 return SDL_SetError("XRRQueryExtension failed");
693 }
694
695 for (looking_for_primary = 1; looking_for_primary >= 0; looking_for_primary--) {
696 for (screen = 0; screen < screencount; screen++) {
697
698 // we want the primary output first, and then skipped later.
699 if (looking_for_primary && (screen != default_screen)) {
700 continue;
701 }
702
703 XRRScreenResources *res = X11_GetScreenResources(dpy, screen);
704 if (!res) {
705 continue;
706 }
707
708 for (output = 0; output < res->noutput; output++) {
709 // The primary output _should_ always be sorted first, but just in case...
710 if ((looking_for_primary && (res->outputs[output] != primary)) ||
711 (!looking_for_primary && (screen == default_screen) && (res->outputs[output] == primary))) {
712 continue;
713 }
714 if (!X11_AddXRandRDisplay(_this, dpy, screen, res->outputs[output], res, false)) {
715 break;
716 }
717 }
718
719 X11_XRRFreeScreenResources(res);
720
721 // This will generate events for displays that come and go at runtime.
722 X11_XRRSelectInput(dpy, RootWindow(dpy, screen), RROutputChangeNotifyMask);
723 }
724 }
725
726 if (_this->num_displays == 0) {
727 return SDL_SetError("No available displays");
728 }
729
730 X11_SortOutputsByPriorityHint(_this);
731
732 return true;
733}
734#endif // SDL_VIDEO_DRIVER_X11_XRANDR
735
736/* This is used if there's no better functionality--like XRandR--to use.
737 It won't attempt to supply different display modes at all, but it can
738 enumerate the current displays and their current sizes. */
739static bool X11_InitModes_StdXlib(SDL_VideoDevice *_this)
740{
741 // !!! FIXME: a lot of copy/paste from X11_InitModes_XRandR in this function.
742 SDL_VideoData *data = _this->internal;
743 Display *dpy = data->display;
744 const int default_screen = DefaultScreen(dpy);
745 Screen *screen = ScreenOfDisplay(dpy, default_screen);
746 int scanline_pad, n, i;
747 SDL_DisplayModeData *modedata;
748 SDL_DisplayData *displaydata;
749 SDL_DisplayMode mode;
750 XPixmapFormatValues *pixmapformats;
751 Uint32 pixelformat;
752 XVisualInfo vinfo;
753 SDL_VideoDisplay display;
754
755 // note that generally even if you have a multiple physical monitors, ScreenCount(dpy) still only reports ONE screen.
756
757 if (!get_visualinfo(dpy, default_screen, &vinfo)) {
758 return SDL_SetError("Failed to find an X11 visual for the primary display");
759 }
760
761 pixelformat = X11_GetPixelFormatFromVisualInfo(dpy, &vinfo);
762 if (SDL_ISPIXELFORMAT_INDEXED(pixelformat)) {
763 return SDL_SetError("Palettized video modes are no longer supported");
764 }
765
766 SDL_zero(mode);
767 mode.w = WidthOfScreen(screen);
768 mode.h = HeightOfScreen(screen);
769 mode.format = pixelformat;
770
771 displaydata = (SDL_DisplayData *)SDL_calloc(1, sizeof(*displaydata));
772 if (!displaydata) {
773 return false;
774 }
775
776 modedata = (SDL_DisplayModeData *)SDL_calloc(1, sizeof(SDL_DisplayModeData));
777 if (!modedata) {
778 SDL_free(displaydata);
779 return false;
780 }
781 mode.internal = modedata;
782
783 displaydata->screen = default_screen;
784 displaydata->visual = vinfo.visual;
785 displaydata->depth = vinfo.depth;
786
787 scanline_pad = SDL_BYTESPERPIXEL(pixelformat) * 8;
788 pixmapformats = X11_XListPixmapFormats(dpy, &n);
789 if (pixmapformats) {
790 for (i = 0; i < n; ++i) {
791 if (pixmapformats[i].depth == vinfo.depth) {
792 scanline_pad = pixmapformats[i].scanline_pad;
793 break;
794 }
795 }
796 X11_XFree(pixmapformats);
797 }
798
799 displaydata->scanline_pad = scanline_pad;
800 displaydata->x = 0;
801 displaydata->y = 0;
802 displaydata->use_xrandr = false;
803
804 SDL_zero(display);
805 display.name = (char *)"Generic X11 Display"; /* this is just copied and thrown away, it's safe to cast to char* here. */
806 display.desktop_mode = mode;
807 display.internal = displaydata;
808 display.content_scale = GetGlobalContentScale(_this);
809 if (SDL_AddVideoDisplay(&display, true) == 0) {
810 return false;
811 }
812 return true;
813}
814
815bool X11_InitModes(SDL_VideoDevice *_this)
816{
817 /* XRandR is the One True Modern Way to do this on X11. If this
818 fails, we just won't report any display modes except the current
819 desktop size. */
820#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
821 {
822 SDL_VideoData *data = _this->internal;
823 int xrandr_major, xrandr_minor;
824 // require at least XRandR v1.3
825 if (CheckXRandR(data->display, &xrandr_major, &xrandr_minor) &&
826 (xrandr_major >= 2 || (xrandr_major == 1 && xrandr_minor >= 3)) &&
827 X11_InitModes_XRandR(_this)) {
828 return true;
829 }
830 }
831#endif // SDL_VIDEO_DRIVER_X11_XRANDR
832
833 // still here? Just set up an extremely basic display.
834 return X11_InitModes_StdXlib(_this);
835}
836
837bool X11_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display)
838{
839#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
840 SDL_DisplayData *data = sdl_display->internal;
841 SDL_DisplayMode mode;
842
843 /* Unfortunately X11 requires the window to be created with the correct
844 * visual and depth ahead of time, but the SDL API allows you to create
845 * a window before setting the fullscreen display mode. This means that
846 * we have to use the same format for all windows and all display modes.
847 * (or support recreating the window with a new visual behind the scenes)
848 */
849 SDL_zero(mode);
850 mode.format = sdl_display->desktop_mode.format;
851
852 if (data->use_xrandr) {
853 Display *display = _this->internal->display;
854 XRRScreenResources *res;
855
856 res = X11_XRRGetScreenResources(display, RootWindow(display, data->screen));
857 if (res) {
858 SDL_DisplayModeData *modedata;
859 XRROutputInfo *output_info;
860 int i;
861
862 output_info = X11_XRRGetOutputInfo(display, res, data->xrandr_output);
863 if (output_info && output_info->connection != RR_Disconnected) {
864 for (i = 0; i < output_info->nmode; ++i) {
865 modedata = (SDL_DisplayModeData *)SDL_calloc(1, sizeof(SDL_DisplayModeData));
866 if (!modedata) {
867 continue;
868 }
869 mode.internal = modedata;
870
871 if (!SetXRandRModeInfo(display, res, output_info->crtc, output_info->modes[i], &mode) ||
872 !SDL_AddFullscreenDisplayMode(sdl_display, &mode)) {
873 SDL_free(modedata);
874 }
875 }
876 }
877 X11_XRRFreeOutputInfo(output_info);
878 X11_XRRFreeScreenResources(res);
879 }
880 }
881#endif // SDL_VIDEO_DRIVER_X11_XRANDR
882 return true;
883}
884
885#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
886// This catches an error from XRRSetScreenSize, as a workaround for now.
887// !!! FIXME: remove this later when we have a better solution.
888static int (*PreXRRSetScreenSizeErrorHandler)(Display *, XErrorEvent *) = NULL;
889static int SDL_XRRSetScreenSizeErrHandler(Display *d, XErrorEvent *e)
890{
891 // BadMatch: https://github.com/libsdl-org/SDL/issues/4561
892 // BadValue: https://github.com/libsdl-org/SDL/issues/4840
893 if ((e->error_code == BadMatch) || (e->error_code == BadValue)) {
894 return 0;
895 }
896
897 return PreXRRSetScreenSizeErrorHandler(d, e);
898}
899#endif
900
901bool X11_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, SDL_DisplayMode *mode)
902{
903 SDL_VideoData *viddata = _this->internal;
904 SDL_DisplayData *data = sdl_display->internal;
905
906 viddata->last_mode_change_deadline = SDL_GetTicks() + (PENDING_FOCUS_TIME * 2);
907
908 // XWayland mode switches are emulated with viewports and thus instantaneous.
909 if (!viddata->is_xwayland) {
910 if (sdl_display->current_mode != mode) {
911 data->mode_switch_deadline_ns = SDL_GetTicksNS() + MODE_SWITCH_TIMEOUT_NS;
912 } else {
913 data->mode_switch_deadline_ns = 0;
914 }
915 }
916
917#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
918 if (data->use_xrandr) {
919 Display *display = viddata->display;
920 SDL_DisplayModeData *modedata = mode->internal;
921 int mm_width, mm_height;
922 XRRScreenResources *res;
923 XRROutputInfo *output_info;
924 XRRCrtcInfo *crtc;
925 Status status;
926
927 res = X11_XRRGetScreenResources(display, RootWindow(display, data->screen));
928 if (!res) {
929 return SDL_SetError("Couldn't get XRandR screen resources");
930 }
931
932 output_info = X11_XRRGetOutputInfo(display, res, data->xrandr_output);
933 if (!output_info || output_info->connection == RR_Disconnected) {
934 X11_XRRFreeScreenResources(res);
935 return SDL_SetError("Couldn't get XRandR output info");
936 }
937
938 crtc = X11_XRRGetCrtcInfo(display, res, output_info->crtc);
939 if (!crtc) {
940 X11_XRRFreeOutputInfo(output_info);
941 X11_XRRFreeScreenResources(res);
942 return SDL_SetError("Couldn't get XRandR crtc info");
943 }
944
945 if (crtc->mode == modedata->xrandr_mode) {
946#ifdef X11MODES_DEBUG
947 printf("already in desired mode 0x%lx (%ux%u), nothing to do\n",
948 crtc->mode, crtc->width, crtc->height);
949#endif
950 status = Success;
951 goto freeInfo;
952 }
953
954 X11_XGrabServer(display);
955 status = X11_XRRSetCrtcConfig(display, res, output_info->crtc, CurrentTime,
956 0, 0, None, crtc->rotation, NULL, 0);
957 if (status != Success) {
958 goto ungrabServer;
959 }
960
961 mm_width = mode->w * DisplayWidthMM(display, data->screen) / DisplayWidth(display, data->screen);
962 mm_height = mode->h * DisplayHeightMM(display, data->screen) / DisplayHeight(display, data->screen);
963
964 /* !!! FIXME: this can get into a problem scenario when a window is
965 bigger than a physical monitor in a configuration where one screen
966 spans multiple physical monitors. A detailed reproduction case is
967 discussed at https://github.com/libsdl-org/SDL/issues/4561 ...
968 for now we cheat and just catch the X11 error and carry on, which
969 is likely to cause subtle issues but is better than outright
970 crashing */
971 X11_XSync(display, False);
972 PreXRRSetScreenSizeErrorHandler = X11_XSetErrorHandler(SDL_XRRSetScreenSizeErrHandler);
973 X11_XRRSetScreenSize(display, RootWindow(display, data->screen),
974 mode->w, mode->h, mm_width, mm_height);
975 X11_XSync(display, False);
976 X11_XSetErrorHandler(PreXRRSetScreenSizeErrorHandler);
977
978 status = X11_XRRSetCrtcConfig(display, res, output_info->crtc, CurrentTime,
979 crtc->x, crtc->y, modedata->xrandr_mode, crtc->rotation,
980 &data->xrandr_output, 1);
981
982 ungrabServer:
983 X11_XUngrabServer(display);
984 freeInfo:
985 X11_XRRFreeCrtcInfo(crtc);
986 X11_XRRFreeOutputInfo(output_info);
987 X11_XRRFreeScreenResources(res);
988
989 if (status != Success) {
990 return SDL_SetError("X11_XRRSetCrtcConfig failed");
991 }
992 }
993#else
994 (void)data;
995#endif // SDL_VIDEO_DRIVER_X11_XRANDR
996
997 return true;
998}
999
1000void X11_QuitModes(SDL_VideoDevice *_this)
1001{
1002}
1003
1004bool X11_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, SDL_Rect *rect)
1005{
1006 SDL_DisplayData *data = sdl_display->internal;
1007
1008 rect->x = data->x;
1009 rect->y = data->y;
1010 rect->w = sdl_display->current_mode->w;
1011 rect->h = sdl_display->current_mode->h;
1012 return true;
1013}
1014
1015bool X11_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, SDL_Rect *rect)
1016{
1017 SDL_VideoData *data = _this->internal;
1018 Display *display = data->display;
1019 Atom _NET_WORKAREA;
1020 int real_format;
1021 Atom real_type;
1022 unsigned long items_read = 0, items_left = 0;
1023 unsigned char *propdata = NULL;
1024 bool result = false;
1025
1026 if (!X11_GetDisplayBounds(_this, sdl_display, rect)) {
1027 return false;
1028 }
1029
1030 _NET_WORKAREA = X11_XInternAtom(display, "_NET_WORKAREA", False);
1031 int status = X11_XGetWindowProperty(display, DefaultRootWindow(display),
1032 _NET_WORKAREA, 0L, 4L, False, XA_CARDINAL,
1033 &real_type, &real_format, &items_read,
1034 &items_left, &propdata);
1035 if ((status == Success) && (items_read >= 4)) {
1036 const long *p = (long *)propdata;
1037 const SDL_Rect usable = { (int)p[0], (int)p[1], (int)p[2], (int)p[3] };
1038 result = true;
1039 if (!SDL_GetRectIntersection(rect, &usable, rect)) {
1040 SDL_zerop(rect);
1041 }
1042 }
1043
1044 if (propdata) {
1045 X11_XFree(propdata);
1046 }
1047
1048 return result;
1049}
1050
1051#endif // SDL_VIDEO_DRIVER_X11
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11modes.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11modes.h
new file mode 100644
index 0000000..35fd866
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11modes.h
@@ -0,0 +1,69 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifndef SDL_x11modes_h_
24#define SDL_x11modes_h_
25
26struct SDL_DisplayData
27{
28 int screen;
29 Visual *visual;
30 int depth;
31 int scanline_pad;
32 int x;
33 int y;
34
35 Uint64 mode_switch_deadline_ns;
36
37 bool use_xrandr;
38
39#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
40 RROutput xrandr_output;
41 char connector_name[16];
42#endif
43};
44
45struct SDL_DisplayModeData
46{
47#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
48 RRMode xrandr_mode;
49#else
50 int unused; // just so struct isn't empty.
51#endif
52};
53
54extern bool X11_InitModes(SDL_VideoDevice *_this);
55extern bool X11_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display);
56extern bool X11_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
57extern void X11_QuitModes(SDL_VideoDevice *_this);
58
59// Some utility functions for working with visuals
60extern bool X11_GetVisualInfoFromVisual(Display *display, Visual *visual, XVisualInfo *vinfo);
61extern SDL_PixelFormat X11_GetPixelFormatFromVisualInfo(Display *display, XVisualInfo *vinfo);
62extern bool X11_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, SDL_Rect *rect);
63extern bool X11_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, SDL_Rect *rect);
64
65#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
66extern void X11_HandleXRandREvent(SDL_VideoDevice *_this, const XEvent *xevent);
67#endif
68
69#endif // SDL_x11modes_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11mouse.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11mouse.c
new file mode 100644
index 0000000..5c72dbf
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11mouse.c
@@ -0,0 +1,552 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_DRIVER_X11
24
25#include <X11/cursorfont.h>
26#include "SDL_x11video.h"
27#include "SDL_x11mouse.h"
28#include "SDL_x11xinput2.h"
29#include "../SDL_video_c.h"
30#include "../../events/SDL_mouse_c.h"
31
32struct SDL_CursorData
33{
34 Cursor cursor;
35};
36
37// FIXME: Find a better place to put this...
38static Cursor x11_empty_cursor = None;
39static bool x11_cursor_visible = true;
40
41static SDL_Cursor *sys_cursors[SDL_HITTEST_RESIZE_LEFT + 1];
42
43static Display *GetDisplay(void)
44{
45 return SDL_GetVideoDevice()->internal->display;
46}
47
48static Cursor X11_CreateEmptyCursor(void)
49{
50 if (x11_empty_cursor == None) {
51 Display *display = GetDisplay();
52 char data[1];
53 XColor color;
54 Pixmap pixmap;
55
56 SDL_zeroa(data);
57 color.red = color.green = color.blue = 0;
58 pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
59 data, 1, 1);
60 if (pixmap) {
61 x11_empty_cursor = X11_XCreatePixmapCursor(display, pixmap, pixmap,
62 &color, &color, 0, 0);
63 X11_XFreePixmap(display, pixmap);
64 }
65 }
66 return x11_empty_cursor;
67}
68
69static void X11_DestroyEmptyCursor(void)
70{
71 if (x11_empty_cursor != None) {
72 X11_XFreeCursor(GetDisplay(), x11_empty_cursor);
73 x11_empty_cursor = None;
74 }
75}
76
77static SDL_Cursor *X11_CreateCursorAndData(Cursor x11_cursor)
78{
79 SDL_Cursor *cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor));
80 if (cursor) {
81 SDL_CursorData *data = (SDL_CursorData *)SDL_calloc(1, sizeof(*data));
82 if (!data) {
83 SDL_free(cursor);
84 return NULL;
85 }
86 data->cursor = x11_cursor;
87 cursor->internal = data;
88 }
89 return cursor;
90}
91
92#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR
93static Cursor X11_CreateXCursorCursor(SDL_Surface *surface, int hot_x, int hot_y)
94{
95 Display *display = GetDisplay();
96 Cursor cursor = None;
97 XcursorImage *image;
98
99 image = X11_XcursorImageCreate(surface->w, surface->h);
100 if (!image) {
101 SDL_OutOfMemory();
102 return None;
103 }
104 image->xhot = hot_x;
105 image->yhot = hot_y;
106 image->delay = 0;
107
108 SDL_assert(surface->format == SDL_PIXELFORMAT_ARGB8888);
109 SDL_assert(surface->pitch == surface->w * 4);
110 SDL_memcpy(image->pixels, surface->pixels, (size_t)surface->h * surface->pitch);
111
112 cursor = X11_XcursorImageLoadCursor(display, image);
113
114 X11_XcursorImageDestroy(image);
115
116 return cursor;
117}
118#endif // SDL_VIDEO_DRIVER_X11_XCURSOR
119
120static Cursor X11_CreatePixmapCursor(SDL_Surface *surface, int hot_x, int hot_y)
121{
122 Display *display = GetDisplay();
123 XColor fg, bg;
124 Cursor cursor = None;
125 Uint32 *ptr;
126 Uint8 *data_bits, *mask_bits;
127 Pixmap data_pixmap, mask_pixmap;
128 int x, y;
129 unsigned int rfg, gfg, bfg, rbg, gbg, bbg, fgBits, bgBits;
130 size_t width_bytes = ((surface->w + 7) & ~((size_t)7)) / 8;
131
132 data_bits = SDL_calloc(1, surface->h * width_bytes);
133 if (!data_bits) {
134 return None;
135 }
136
137 mask_bits = SDL_calloc(1, surface->h * width_bytes);
138 if (!mask_bits) {
139 SDL_free(data_bits);
140 return None;
141 }
142
143 // Code below assumes ARGB pixel format
144 SDL_assert(surface->format == SDL_PIXELFORMAT_ARGB8888);
145
146 rfg = gfg = bfg = rbg = gbg = bbg = fgBits = bgBits = 0;
147 for (y = 0; y < surface->h; ++y) {
148 ptr = (Uint32 *)((Uint8 *)surface->pixels + y * surface->pitch);
149 for (x = 0; x < surface->w; ++x) {
150 int alpha = (*ptr >> 24) & 0xff;
151 int red = (*ptr >> 16) & 0xff;
152 int green = (*ptr >> 8) & 0xff;
153 int blue = (*ptr >> 0) & 0xff;
154 if (alpha > 25) {
155 mask_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8));
156
157 if ((red + green + blue) > 0x40) {
158 fgBits++;
159 rfg += red;
160 gfg += green;
161 bfg += blue;
162 data_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8));
163 } else {
164 bgBits++;
165 rbg += red;
166 gbg += green;
167 bbg += blue;
168 }
169 }
170 ++ptr;
171 }
172 }
173
174 if (fgBits) {
175 fg.red = rfg * 257 / fgBits;
176 fg.green = gfg * 257 / fgBits;
177 fg.blue = bfg * 257 / fgBits;
178 } else {
179 fg.red = fg.green = fg.blue = 0;
180 }
181
182 if (bgBits) {
183 bg.red = rbg * 257 / bgBits;
184 bg.green = gbg * 257 / bgBits;
185 bg.blue = bbg * 257 / bgBits;
186 } else {
187 bg.red = bg.green = bg.blue = 0;
188 }
189
190 data_pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
191 (char *)data_bits,
192 surface->w, surface->h);
193 mask_pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
194 (char *)mask_bits,
195 surface->w, surface->h);
196 cursor = X11_XCreatePixmapCursor(display, data_pixmap, mask_pixmap,
197 &fg, &bg, hot_x, hot_y);
198 X11_XFreePixmap(display, data_pixmap);
199 X11_XFreePixmap(display, mask_pixmap);
200 SDL_free(data_bits);
201 SDL_free(mask_bits);
202
203 return cursor;
204}
205
206static SDL_Cursor *X11_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
207{
208 Cursor x11_cursor = None;
209
210#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR
211 if (SDL_X11_HAVE_XCURSOR) {
212 x11_cursor = X11_CreateXCursorCursor(surface, hot_x, hot_y);
213 }
214#endif
215 if (x11_cursor == None) {
216 x11_cursor = X11_CreatePixmapCursor(surface, hot_x, hot_y);
217 }
218 return X11_CreateCursorAndData(x11_cursor);
219}
220
221static unsigned int GetLegacySystemCursorShape(SDL_SystemCursor id)
222{
223 switch (id) {
224 // X Font Cursors reference:
225 // http://tronche.com/gui/x/xlib/appendix/b/
226 case SDL_SYSTEM_CURSOR_DEFAULT: return XC_left_ptr;
227 case SDL_SYSTEM_CURSOR_TEXT: return XC_xterm;
228 case SDL_SYSTEM_CURSOR_WAIT: return XC_watch;
229 case SDL_SYSTEM_CURSOR_CROSSHAIR: return XC_tcross;
230 case SDL_SYSTEM_CURSOR_PROGRESS: return XC_watch;
231 case SDL_SYSTEM_CURSOR_NWSE_RESIZE: return XC_top_left_corner;
232 case SDL_SYSTEM_CURSOR_NESW_RESIZE: return XC_top_right_corner;
233 case SDL_SYSTEM_CURSOR_EW_RESIZE: return XC_sb_h_double_arrow;
234 case SDL_SYSTEM_CURSOR_NS_RESIZE: return XC_sb_v_double_arrow;
235 case SDL_SYSTEM_CURSOR_MOVE: return XC_fleur;
236 case SDL_SYSTEM_CURSOR_NOT_ALLOWED: return XC_pirate;
237 case SDL_SYSTEM_CURSOR_POINTER: return XC_hand2;
238 case SDL_SYSTEM_CURSOR_NW_RESIZE: return XC_top_left_corner;
239 case SDL_SYSTEM_CURSOR_N_RESIZE: return XC_top_side;
240 case SDL_SYSTEM_CURSOR_NE_RESIZE: return XC_top_right_corner;
241 case SDL_SYSTEM_CURSOR_E_RESIZE: return XC_right_side;
242 case SDL_SYSTEM_CURSOR_SE_RESIZE: return XC_bottom_right_corner;
243 case SDL_SYSTEM_CURSOR_S_RESIZE: return XC_bottom_side;
244 case SDL_SYSTEM_CURSOR_SW_RESIZE: return XC_bottom_left_corner;
245 case SDL_SYSTEM_CURSOR_W_RESIZE: return XC_left_side;
246 case SDL_SYSTEM_CURSOR_COUNT: break; // so the compiler might notice if an enum value is missing here.
247 }
248
249 SDL_assert(0);
250 return 0;
251}
252
253static SDL_Cursor *X11_CreateSystemCursor(SDL_SystemCursor id)
254{
255 SDL_Cursor *cursor = NULL;
256 Display *dpy = GetDisplay();
257 Cursor x11_cursor = None;
258
259#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR
260 if (SDL_X11_HAVE_XCURSOR) {
261 x11_cursor = X11_XcursorLibraryLoadCursor(dpy, SDL_GetCSSCursorName(id, NULL));
262 }
263#endif
264
265 if (x11_cursor == None) {
266 x11_cursor = X11_XCreateFontCursor(dpy, GetLegacySystemCursorShape(id));
267 }
268
269 if (x11_cursor != None) {
270 cursor = X11_CreateCursorAndData(x11_cursor);
271 }
272
273 return cursor;
274}
275
276static SDL_Cursor *X11_CreateDefaultCursor(void)
277{
278 SDL_SystemCursor id = SDL_GetDefaultSystemCursor();
279 return X11_CreateSystemCursor(id);
280}
281
282static void X11_FreeCursor(SDL_Cursor *cursor)
283{
284 Cursor x11_cursor = cursor->internal->cursor;
285
286 if (x11_cursor != None) {
287 X11_XFreeCursor(GetDisplay(), x11_cursor);
288 }
289 SDL_free(cursor->internal);
290 SDL_free(cursor);
291}
292
293static bool X11_ShowCursor(SDL_Cursor *cursor)
294{
295 Cursor x11_cursor = 0;
296
297 if (cursor) {
298 x11_cursor = cursor->internal->cursor;
299 } else {
300 x11_cursor = X11_CreateEmptyCursor();
301 }
302
303 // FIXME: Is there a better way than this?
304 {
305 SDL_VideoDevice *video = SDL_GetVideoDevice();
306 Display *display = GetDisplay();
307 SDL_Window *window;
308
309 x11_cursor_visible = !!cursor;
310
311 for (window = video->windows; window; window = window->next) {
312 SDL_WindowData *data = window->internal;
313 if (data) {
314 if (x11_cursor != None) {
315 X11_XDefineCursor(display, data->xwindow, x11_cursor);
316 } else {
317 X11_XUndefineCursor(display, data->xwindow);
318 }
319 }
320 }
321 X11_XFlush(display);
322 }
323 return true;
324}
325
326static void X11_WarpMouseInternal(Window xwindow, float x, float y)
327{
328 SDL_VideoData *videodata = SDL_GetVideoDevice()->internal;
329 Display *display = videodata->display;
330 bool warp_hack = false;
331
332 // XWayland will only warp the cursor if it is hidden, so this workaround is required.
333 if (videodata->is_xwayland && x11_cursor_visible) {
334 warp_hack = true;
335 }
336
337 if (warp_hack) {
338 X11_ShowCursor(NULL);
339 }
340#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
341 int deviceid = 0;
342 if (X11_Xinput2IsInitialized()) {
343 /* It seems XIWarpPointer() doesn't work correctly on multi-head setups:
344 * https://developer.blender.org/rB165caafb99c6846e53d11c4e966990aaffc06cea
345 */
346 if (ScreenCount(display) == 1) {
347 X11_XIGetClientPointer(display, None, &deviceid);
348 }
349 }
350 if (deviceid != 0) {
351 SDL_assert(SDL_X11_HAVE_XINPUT2);
352 X11_XIWarpPointer(display, deviceid, None, xwindow, 0.0, 0.0, 0, 0, x, y);
353 } else
354#endif
355 {
356 X11_XWarpPointer(display, None, xwindow, 0, 0, 0, 0, (int)x, (int)y);
357 }
358
359 if (warp_hack) {
360 X11_ShowCursor(SDL_GetCursor());
361 }
362 X11_XSync(display, False);
363 videodata->global_mouse_changed = true;
364}
365
366static bool X11_WarpMouse(SDL_Window *window, float x, float y)
367{
368 SDL_WindowData *data = window->internal;
369
370#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
371 // If we have no barrier, we need to warp
372 if (data->pointer_barrier_active == false) {
373 X11_WarpMouseInternal(data->xwindow, x, y);
374 }
375#else
376 X11_WarpMouseInternal(data->xwindow, x, y);
377#endif
378 return true;
379}
380
381static bool X11_WarpMouseGlobal(float x, float y)
382{
383 X11_WarpMouseInternal(DefaultRootWindow(GetDisplay()), x, y);
384 return true;
385}
386
387static bool X11_SetRelativeMouseMode(bool enabled)
388{
389 if (!X11_Xinput2IsInitialized()) {
390 return SDL_Unsupported();
391 }
392 return true;
393}
394
395static bool X11_CaptureMouse(SDL_Window *window)
396{
397 Display *display = GetDisplay();
398 SDL_Window *mouse_focus = SDL_GetMouseFocus();
399
400 if (window) {
401 SDL_WindowData *data = window->internal;
402
403 /* If XInput2 is handling the pointer input, non-confinement grabs will always fail with 'AlreadyGrabbed',
404 * since the pointer is being grabbed by XInput2.
405 */
406 if (!data->xinput2_mouse_enabled || data->mouse_grabbed) {
407 const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
408 Window confined = (data->mouse_grabbed ? data->xwindow : None);
409 const int rc = X11_XGrabPointer(display, data->xwindow, False,
410 mask, GrabModeAsync, GrabModeAsync,
411 confined, None, CurrentTime);
412 if (rc != GrabSuccess) {
413 return SDL_SetError("X server refused mouse capture");
414 }
415
416 if (data->mouse_grabbed) {
417 // XGrabPointer can warp the cursor when confining, so update the coordinates.
418 data->videodata->global_mouse_changed = true;
419 }
420 }
421 } else if (mouse_focus) {
422 SDL_UpdateWindowGrab(mouse_focus);
423 } else {
424 X11_XUngrabPointer(display, CurrentTime);
425 }
426
427 X11_XSync(display, False);
428
429 return true;
430}
431
432static SDL_MouseButtonFlags X11_GetGlobalMouseState(float *x, float *y)
433{
434 SDL_VideoData *videodata = SDL_GetVideoDevice()->internal;
435 SDL_DisplayID *displays;
436 Display *display = GetDisplay();
437 int i;
438
439 // !!! FIXME: should we XSync() here first?
440
441 if (!X11_Xinput2IsInitialized()) {
442 videodata->global_mouse_changed = true;
443 }
444
445 // check if we have this cached since XInput last saw the mouse move.
446 // !!! FIXME: can we just calculate this from XInput's events?
447 if (videodata->global_mouse_changed) {
448 displays = SDL_GetDisplays(NULL);
449 if (displays) {
450 for (i = 0; displays[i]; ++i) {
451 SDL_DisplayData *data = SDL_GetDisplayDriverData(displays[i]);
452 if (data) {
453 Window root, child;
454 int rootx, rooty, winx, winy;
455 unsigned int mask;
456 if (X11_XQueryPointer(display, RootWindow(display, data->screen), &root, &child, &rootx, &rooty, &winx, &winy, &mask)) {
457 XWindowAttributes root_attrs;
458 SDL_MouseButtonFlags buttons = 0;
459 buttons |= (mask & Button1Mask) ? SDL_BUTTON_LMASK : 0;
460 buttons |= (mask & Button2Mask) ? SDL_BUTTON_MMASK : 0;
461 buttons |= (mask & Button3Mask) ? SDL_BUTTON_RMASK : 0;
462 // Use the SDL state for the extended buttons - it's better than nothing
463 buttons |= (SDL_GetMouseState(NULL, NULL) & (SDL_BUTTON_X1MASK | SDL_BUTTON_X2MASK));
464 /* SDL_DisplayData->x,y point to screen origin, and adding them to mouse coordinates relative to root window doesn't do the right thing
465 * (observed on dual monitor setup with primary display being the rightmost one - mouse was offset to the right).
466 *
467 * Adding root position to root-relative coordinates seems to be a better way to get absolute position. */
468 X11_XGetWindowAttributes(display, root, &root_attrs);
469 videodata->global_mouse_position.x = root_attrs.x + rootx;
470 videodata->global_mouse_position.y = root_attrs.y + rooty;
471 videodata->global_mouse_buttons = buttons;
472 videodata->global_mouse_changed = false;
473 break;
474 }
475 }
476 }
477 SDL_free(displays);
478 }
479 }
480
481 SDL_assert(!videodata->global_mouse_changed); // The pointer wasn't on any X11 screen?!
482
483 *x = (float)videodata->global_mouse_position.x;
484 *y = (float)videodata->global_mouse_position.y;
485 return videodata->global_mouse_buttons;
486}
487
488void X11_InitMouse(SDL_VideoDevice *_this)
489{
490 SDL_Mouse *mouse = SDL_GetMouse();
491
492 mouse->CreateCursor = X11_CreateCursor;
493 mouse->CreateSystemCursor = X11_CreateSystemCursor;
494 mouse->ShowCursor = X11_ShowCursor;
495 mouse->FreeCursor = X11_FreeCursor;
496 mouse->WarpMouse = X11_WarpMouse;
497 mouse->WarpMouseGlobal = X11_WarpMouseGlobal;
498 mouse->SetRelativeMouseMode = X11_SetRelativeMouseMode;
499 mouse->CaptureMouse = X11_CaptureMouse;
500 mouse->GetGlobalMouseState = X11_GetGlobalMouseState;
501
502 SDL_HitTestResult r = SDL_HITTEST_NORMAL;
503 while (r <= SDL_HITTEST_RESIZE_LEFT) {
504 switch (r) {
505 case SDL_HITTEST_NORMAL: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT); break;
506 case SDL_HITTEST_DRAGGABLE: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT); break;
507 case SDL_HITTEST_RESIZE_TOPLEFT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_NW_RESIZE); break;
508 case SDL_HITTEST_RESIZE_TOP: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_N_RESIZE); break;
509 case SDL_HITTEST_RESIZE_TOPRIGHT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_NE_RESIZE); break;
510 case SDL_HITTEST_RESIZE_RIGHT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_E_RESIZE); break;
511 case SDL_HITTEST_RESIZE_BOTTOMRIGHT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_SE_RESIZE); break;
512 case SDL_HITTEST_RESIZE_BOTTOM: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_S_RESIZE); break;
513 case SDL_HITTEST_RESIZE_BOTTOMLEFT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_SW_RESIZE); break;
514 case SDL_HITTEST_RESIZE_LEFT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_W_RESIZE); break;
515 }
516 r++;
517 }
518
519 SDL_SetDefaultCursor(X11_CreateDefaultCursor());
520}
521
522void X11_QuitMouse(SDL_VideoDevice *_this)
523{
524 SDL_VideoData *data = _this->internal;
525 SDL_XInput2DeviceInfo *i;
526 SDL_XInput2DeviceInfo *next;
527 int j;
528
529 for (j = 0; j < SDL_arraysize(sys_cursors); j++) {
530 X11_FreeCursor(sys_cursors[j]);
531 sys_cursors[j] = NULL;
532 }
533
534 for (i = data->mouse_device_info; i; i = next) {
535 next = i->next;
536 SDL_free(i);
537 }
538 data->mouse_device_info = NULL;
539
540 X11_DestroyEmptyCursor();
541}
542
543void X11_SetHitTestCursor(SDL_HitTestResult rc)
544{
545 if (rc == SDL_HITTEST_NORMAL || rc == SDL_HITTEST_DRAGGABLE) {
546 SDL_SetCursor(NULL);
547 } else {
548 X11_ShowCursor(sys_cursors[rc]);
549 }
550}
551
552#endif // SDL_VIDEO_DRIVER_X11
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11mouse.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11mouse.h
new file mode 100644
index 0000000..2a2973c
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11mouse.h
@@ -0,0 +1,40 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifndef SDL_x11mouse_h_
24#define SDL_x11mouse_h_
25
26typedef struct SDL_XInput2DeviceInfo
27{
28 int device_id;
29 bool relative[2];
30 double minval[2];
31 double maxval[2];
32 double prev_coords[2];
33 struct SDL_XInput2DeviceInfo *next;
34} SDL_XInput2DeviceInfo;
35
36extern void X11_InitMouse(SDL_VideoDevice *_this);
37extern void X11_QuitMouse(SDL_VideoDevice *_this);
38extern void X11_SetHitTestCursor(SDL_HitTestResult rc);
39
40#endif // SDL_x11mouse_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengl.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengl.c
new file mode 100644
index 0000000..d46409f
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengl.c
@@ -0,0 +1,1116 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4 Copyright (C) 2021 NVIDIA Corporation
5
6 This software is provided 'as-is', without any express or implied
7 warranty. In no event will the authors be held liable for any damages
8 arising from the use of this software.
9
10 Permission is granted to anyone to use this software for any purpose,
11 including commercial applications, and to alter it and redistribute it
12 freely, subject to the following restrictions:
13
14 1. The origin of this software must not be misrepresented; you must not
15 claim that you wrote the original software. If you use this software
16 in a product, an acknowledgment in the product documentation would be
17 appreciated but is not required.
18 2. Altered source versions must be plainly marked as such, and must not be
19 misrepresented as being the original software.
20 3. This notice may not be removed or altered from any source distribution.
21*/
22#include "SDL_internal.h"
23
24#ifdef SDL_VIDEO_DRIVER_X11
25
26#include "SDL_x11video.h"
27#include "SDL_x11xsync.h"
28
29// GLX implementation of SDL OpenGL support
30
31#ifdef SDL_VIDEO_OPENGL_GLX
32#include "SDL_x11opengles.h"
33
34#if defined(SDL_PLATFORM_IRIX) || defined(SDL_PLATFORM_NETBSD) || defined(SDL_PLATFORM_OPENBSD)
35/*
36 * IRIX doesn't have a GL library versioning system.
37 * NetBSD and OpenBSD have different GL library versions depending on how
38 * the library was installed.
39 */
40#define DEFAULT_OPENGL "libGL.so"
41#elif defined(SDL_PLATFORM_MACOS)
42#define DEFAULT_OPENGL "/opt/X11/lib/libGL.1.dylib"
43#else
44#define DEFAULT_OPENGL "libGL.so.1"
45#endif
46
47#ifndef GLX_NONE_EXT
48#define GLX_NONE_EXT 0x8000
49#endif
50
51#ifndef GLX_ARB_multisample
52#define GLX_ARB_multisample
53#define GLX_SAMPLE_BUFFERS_ARB 100000
54#define GLX_SAMPLES_ARB 100001
55#endif
56
57#ifndef GLX_EXT_visual_rating
58#define GLX_EXT_visual_rating
59#define GLX_VISUAL_CAVEAT_EXT 0x20
60#define GLX_NONE_EXT 0x8000
61#define GLX_SLOW_VISUAL_EXT 0x8001
62#define GLX_NON_CONFORMANT_VISUAL_EXT 0x800D
63#endif
64
65#ifndef GLX_EXT_visual_info
66#define GLX_EXT_visual_info
67#define GLX_X_VISUAL_TYPE_EXT 0x22
68#define GLX_DIRECT_COLOR_EXT 0x8003
69#endif
70
71#ifndef GLX_ARB_create_context
72#define GLX_ARB_create_context
73#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
74#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
75#define GLX_CONTEXT_FLAGS_ARB 0x2094
76#define GLX_CONTEXT_DEBUG_BIT_ARB 0x0001
77#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
78
79// Typedef for the GL 3.0 context creation function
80typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display *dpy,
81 GLXFBConfig config,
82 GLXContext
83 share_context,
84 Bool direct,
85 const int
86 *attrib_list);
87#endif
88
89#ifndef GLX_ARB_create_context_profile
90#define GLX_ARB_create_context_profile
91#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
92#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
93#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
94#endif
95
96#ifndef GLX_ARB_create_context_robustness
97#define GLX_ARB_create_context_robustness
98#define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004
99#define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
100#define GLX_NO_RESET_NOTIFICATION_ARB 0x8261
101#define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252
102#endif
103
104#ifndef GLX_EXT_create_context_es2_profile
105#define GLX_EXT_create_context_es2_profile
106#ifndef GLX_CONTEXT_ES2_PROFILE_BIT_EXT
107#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000002
108#endif
109#endif
110
111#ifndef GLX_ARB_framebuffer_sRGB
112#define GLX_ARB_framebuffer_sRGB
113#ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
114#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20B2
115#endif
116#endif
117
118#ifndef GLX_ARB_fbconfig_float
119#define GLX_ARB_fbconfig_float
120#ifndef GLX_RGBA_FLOAT_TYPE_ARB
121#define GLX_RGBA_FLOAT_TYPE_ARB 0x20B9
122#endif
123#ifndef GLX_RGBA_FLOAT_BIT_ARB
124#define GLX_RGBA_FLOAT_BIT_ARB 0x00000004
125#endif
126#endif
127
128#ifndef GLX_ARB_create_context_no_error
129#define GLX_ARB_create_context_no_error
130#ifndef GLX_CONTEXT_OPENGL_NO_ERROR_ARB
131#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31B3
132#endif
133#endif
134
135#ifndef GLX_EXT_swap_control
136#define GLX_SWAP_INTERVAL_EXT 0x20F1
137#define GLX_MAX_SWAP_INTERVAL_EXT 0x20F2
138#endif
139
140#ifndef GLX_EXT_swap_control_tear
141#define GLX_LATE_SWAPS_TEAR_EXT 0x20F3
142#endif
143
144#ifndef GLX_ARB_context_flush_control
145#define GLX_ARB_context_flush_control
146#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097
147#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0x0000
148#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098
149#endif
150
151#define OPENGL_REQUIRES_DLOPEN
152#if defined(OPENGL_REQUIRES_DLOPEN) && defined(HAVE_DLOPEN)
153#include <dlfcn.h>
154#define GL_LoadObject(X) dlopen(X, (RTLD_NOW | RTLD_GLOBAL))
155#define GL_LoadFunction dlsym
156#define GL_UnloadObject dlclose
157#else
158#define GL_LoadObject SDL_LoadObject
159#define GL_LoadFunction SDL_LoadFunction
160#define GL_UnloadObject SDL_UnloadObject
161#endif
162
163static void X11_GL_InitExtensions(SDL_VideoDevice *_this);
164
165bool X11_GL_LoadLibrary(SDL_VideoDevice *_this, const char *path)
166{
167 Display *display;
168 SDL_SharedObject *handle;
169
170 if (_this->gl_data) {
171 return SDL_SetError("OpenGL context already created");
172 }
173
174 // Load the OpenGL library
175 if (path == NULL) {
176 path = SDL_GetHint(SDL_HINT_OPENGL_LIBRARY);
177 }
178 if (path == NULL) {
179 path = DEFAULT_OPENGL;
180 }
181 _this->gl_config.dll_handle = GL_LoadObject(path);
182 if (!_this->gl_config.dll_handle) {
183#if defined(OPENGL_REQUIRES_DLOPEN) && defined(HAVE_DLOPEN)
184 SDL_SetError("Failed loading %s: %s", path, dlerror());
185#endif
186 return false;
187 }
188 SDL_strlcpy(_this->gl_config.driver_path, path,
189 SDL_arraysize(_this->gl_config.driver_path));
190
191 // Allocate OpenGL memory
192 _this->gl_data =
193 (struct SDL_GLDriverData *)SDL_calloc(1,
194 sizeof(struct
195 SDL_GLDriverData));
196 if (!_this->gl_data) {
197 return false;
198 }
199
200 // Load function pointers
201 handle = _this->gl_config.dll_handle;
202 _this->gl_data->glXQueryExtension =
203 (Bool(*)(Display *, int *, int *))
204 GL_LoadFunction(handle, "glXQueryExtension");
205 _this->gl_data->glXGetProcAddress =
206 (__GLXextFuncPtr (*)(const GLubyte *))
207 GL_LoadFunction(handle, "glXGetProcAddressARB");
208 _this->gl_data->glXChooseVisual =
209 (XVisualInfo * (*)(Display *, int, int *))
210 X11_GL_GetProcAddress(_this, "glXChooseVisual");
211 _this->gl_data->glXCreateContext =
212 (GLXContext(*)(Display *, XVisualInfo *, GLXContext, int))
213 X11_GL_GetProcAddress(_this, "glXCreateContext");
214 _this->gl_data->glXDestroyContext =
215 (void (*)(Display *, GLXContext))
216 X11_GL_GetProcAddress(_this, "glXDestroyContext");
217 _this->gl_data->glXMakeCurrent =
218 (int (*)(Display *, GLXDrawable, GLXContext))
219 X11_GL_GetProcAddress(_this, "glXMakeCurrent");
220 _this->gl_data->glXSwapBuffers =
221 (void (*)(Display *, GLXDrawable))
222 X11_GL_GetProcAddress(_this, "glXSwapBuffers");
223 _this->gl_data->glXQueryDrawable =
224 (void (*)(Display *, GLXDrawable, int, unsigned int *))
225 X11_GL_GetProcAddress(_this, "glXQueryDrawable");
226
227 if (!_this->gl_data->glXQueryExtension ||
228 !_this->gl_data->glXChooseVisual ||
229 !_this->gl_data->glXCreateContext ||
230 !_this->gl_data->glXDestroyContext ||
231 !_this->gl_data->glXMakeCurrent ||
232 !_this->gl_data->glXSwapBuffers) {
233 return SDL_SetError("Could not retrieve OpenGL functions");
234 }
235
236 display = _this->internal->display;
237 if (!_this->gl_data->glXQueryExtension(display, &_this->gl_data->errorBase, &_this->gl_data->eventBase)) {
238 return SDL_SetError("GLX is not supported");
239 }
240
241 _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_UNTESTED;
242
243 // Initialize extensions
244 /* See lengthy comment about the inc/dec in
245 ../windows/SDL_windowsopengl.c. */
246 ++_this->gl_config.driver_loaded;
247 X11_GL_InitExtensions(_this);
248 --_this->gl_config.driver_loaded;
249
250 /* If we need a GL ES context and there's no
251 * GLX_EXT_create_context_es2_profile extension, switch over to X11_GLES functions
252 */
253 if (((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) ||
254 SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) &&
255 X11_GL_UseEGL(_this)) {
256#ifdef SDL_VIDEO_OPENGL_EGL
257 X11_GL_UnloadLibrary(_this);
258 _this->GL_LoadLibrary = X11_GLES_LoadLibrary;
259 _this->GL_GetProcAddress = X11_GLES_GetProcAddress;
260 _this->GL_UnloadLibrary = X11_GLES_UnloadLibrary;
261 _this->GL_CreateContext = X11_GLES_CreateContext;
262 _this->GL_MakeCurrent = X11_GLES_MakeCurrent;
263 _this->GL_SetSwapInterval = X11_GLES_SetSwapInterval;
264 _this->GL_GetSwapInterval = X11_GLES_GetSwapInterval;
265 _this->GL_SwapWindow = X11_GLES_SwapWindow;
266 _this->GL_DestroyContext = X11_GLES_DestroyContext;
267 return X11_GLES_LoadLibrary(_this, NULL);
268#else
269 return SDL_SetError("SDL not configured with EGL support");
270#endif
271 }
272
273 return true;
274}
275
276SDL_FunctionPointer X11_GL_GetProcAddress(SDL_VideoDevice *_this, const char *proc)
277{
278 if (_this->gl_data->glXGetProcAddress) {
279 return _this->gl_data->glXGetProcAddress((const GLubyte *)proc);
280 }
281 return GL_LoadFunction(_this->gl_config.dll_handle, proc);
282}
283
284void X11_GL_UnloadLibrary(SDL_VideoDevice *_this)
285{
286 /* Don't actually unload the library, since it may have registered
287 * X11 shutdown hooks, per the notes at:
288 * http://dri.sourceforge.net/doc/DRIuserguide.html
289 */
290#if 0
291 GL_UnloadObject(_this->gl_config.dll_handle);
292 _this->gl_config.dll_handle = NULL;
293#endif
294
295 // Free OpenGL memory
296 SDL_free(_this->gl_data);
297 _this->gl_data = NULL;
298}
299
300static bool HasExtension(const char *extension, const char *extensions)
301{
302 const char *start;
303 const char *where, *terminator;
304
305 if (!extensions) {
306 return false;
307 }
308
309 // Extension names should not have spaces.
310 where = SDL_strchr(extension, ' ');
311 if (where || *extension == '\0') {
312 return false;
313 }
314
315 /* It takes a bit of care to be fool-proof about parsing the
316 * OpenGL extensions string. Don't be fooled by sub-strings,
317 * etc. */
318
319 start = extensions;
320
321 for (;;) {
322 where = SDL_strstr(start, extension);
323 if (!where) {
324 break;
325 }
326
327 terminator = where + SDL_strlen(extension);
328 if (where == start || *(where - 1) == ' ') {
329 if (*terminator == ' ' || *terminator == '\0') {
330 return true;
331 }
332 }
333
334 start = terminator;
335 }
336 return false;
337}
338
339static void X11_GL_InitExtensions(SDL_VideoDevice *_this)
340{
341 Display *display = _this->internal->display;
342 const int screen = DefaultScreen(display);
343 XVisualInfo *vinfo = NULL;
344 Window w = 0;
345 GLXContext prev_ctx = 0;
346 GLXDrawable prev_drawable = 0;
347 GLXContext context = 0;
348 const char *(*glXQueryExtensionsStringFunc)(Display *, int);
349 const char *extensions;
350
351 vinfo = X11_GL_GetVisual(_this, display, screen, false);
352 if (vinfo) {
353 GLXContext (*glXGetCurrentContextFunc)(void) =
354 (GLXContext(*)(void))
355 X11_GL_GetProcAddress(_this, "glXGetCurrentContext");
356
357 GLXDrawable (*glXGetCurrentDrawableFunc)(void) =
358 (GLXDrawable(*)(void))
359 X11_GL_GetProcAddress(_this, "glXGetCurrentDrawable");
360
361 if (glXGetCurrentContextFunc && glXGetCurrentDrawableFunc) {
362 XSetWindowAttributes xattr;
363 prev_ctx = glXGetCurrentContextFunc();
364 prev_drawable = glXGetCurrentDrawableFunc();
365
366 xattr.background_pixel = 0;
367 xattr.border_pixel = 0;
368 xattr.colormap =
369 X11_XCreateColormap(display, RootWindow(display, screen),
370 vinfo->visual, AllocNone);
371 w = X11_XCreateWindow(display, RootWindow(display, screen), 0, 0,
372 32, 32, 0, vinfo->depth, InputOutput, vinfo->visual,
373 (CWBackPixel | CWBorderPixel | CWColormap), &xattr);
374
375 context = _this->gl_data->glXCreateContext(display, vinfo,
376 NULL, True);
377 if (context) {
378 _this->gl_data->glXMakeCurrent(display, w, context);
379 }
380 }
381
382 X11_XFree(vinfo);
383 }
384
385 glXQueryExtensionsStringFunc =
386 (const char *(*)(Display *, int))X11_GL_GetProcAddress(_this,
387 "glXQueryExtensionsString");
388 if (glXQueryExtensionsStringFunc) {
389 extensions = glXQueryExtensionsStringFunc(display, screen);
390 } else {
391 extensions = NULL;
392 }
393
394 // Check for GLX_EXT_swap_control(_tear)
395 _this->gl_data->HAS_GLX_EXT_swap_control_tear = false;
396 if (HasExtension("GLX_EXT_swap_control", extensions)) {
397 _this->gl_data->glXSwapIntervalEXT =
398 (void (*)(Display *, GLXDrawable, int))
399 X11_GL_GetProcAddress(_this, "glXSwapIntervalEXT");
400 if (HasExtension("GLX_EXT_swap_control_tear", extensions)) {
401 _this->gl_data->HAS_GLX_EXT_swap_control_tear = true;
402 }
403 }
404
405 // Check for GLX_MESA_swap_control
406 if (HasExtension("GLX_MESA_swap_control", extensions)) {
407 _this->gl_data->glXSwapIntervalMESA =
408 (int (*)(int))X11_GL_GetProcAddress(_this, "glXSwapIntervalMESA");
409 _this->gl_data->glXGetSwapIntervalMESA =
410 (int (*)(void))X11_GL_GetProcAddress(_this,
411 "glXGetSwapIntervalMESA");
412 }
413
414 // Check for GLX_SGI_swap_control
415 if (HasExtension("GLX_SGI_swap_control", extensions)) {
416 _this->gl_data->glXSwapIntervalSGI =
417 (int (*)(int))X11_GL_GetProcAddress(_this, "glXSwapIntervalSGI");
418 }
419
420 // Check for GLX_ARB_create_context
421 if (HasExtension("GLX_ARB_create_context", extensions)) {
422 _this->gl_data->glXCreateContextAttribsARB =
423 (GLXContext(*)(Display *, GLXFBConfig, GLXContext, Bool, const int *))
424 X11_GL_GetProcAddress(_this, "glXCreateContextAttribsARB");
425 _this->gl_data->glXChooseFBConfig =
426 (GLXFBConfig * (*)(Display *, int, const int *, int *))
427 X11_GL_GetProcAddress(_this, "glXChooseFBConfig");
428 _this->gl_data->glXGetVisualFromFBConfig =
429 (XVisualInfo * (*)(Display *, GLXFBConfig))
430 X11_GL_GetProcAddress(_this, "glXGetVisualFromFBConfig");
431 }
432
433 // Check for GLX_EXT_visual_rating
434 if (HasExtension("GLX_EXT_visual_rating", extensions)) {
435 _this->gl_data->HAS_GLX_EXT_visual_rating = true;
436 }
437
438 // Check for GLX_EXT_visual_info
439 if (HasExtension("GLX_EXT_visual_info", extensions)) {
440 _this->gl_data->HAS_GLX_EXT_visual_info = true;
441 }
442
443 // Check for GLX_EXT_create_context_es2_profile
444 if (HasExtension("GLX_EXT_create_context_es2_profile", extensions)) {
445 // this wants to call glGetString(), so it needs a context.
446 // !!! FIXME: it would be nice not to make a context here though!
447 if (context) {
448 SDL_GL_DeduceMaxSupportedESProfile(
449 &_this->gl_data->es_profile_max_supported_version.major,
450 &_this->gl_data->es_profile_max_supported_version.minor);
451 }
452 }
453
454 // Check for GLX_ARB_context_flush_control
455 if (HasExtension("GLX_ARB_context_flush_control", extensions)) {
456 _this->gl_data->HAS_GLX_ARB_context_flush_control = true;
457 }
458
459 // Check for GLX_ARB_create_context_robustness
460 if (HasExtension("GLX_ARB_create_context_robustness", extensions)) {
461 _this->gl_data->HAS_GLX_ARB_create_context_robustness = true;
462 }
463
464 // Check for GLX_ARB_create_context_no_error
465 if (HasExtension("GLX_ARB_create_context_no_error", extensions)) {
466 _this->gl_data->HAS_GLX_ARB_create_context_no_error = true;
467 }
468
469 if (context) {
470 _this->gl_data->glXMakeCurrent(display, None, NULL);
471 _this->gl_data->glXDestroyContext(display, context);
472 if (prev_ctx && prev_drawable) {
473 _this->gl_data->glXMakeCurrent(display, prev_drawable, prev_ctx);
474 }
475 }
476
477 if (w) {
478 X11_XDestroyWindow(display, w);
479 }
480 X11_PumpEvents(_this);
481}
482
483/* glXChooseVisual and glXChooseFBConfig have some small differences in
484 * the attribute encoding, it can be chosen with the for_FBConfig parameter.
485 * Some targets fail if you use GLX_X_VISUAL_TYPE_EXT/GLX_DIRECT_COLOR_EXT,
486 * so it gets specified last if used and is pointed to by *_pvistypeattr.
487 * In case of failure, if that pointer is not NULL, set that pointer to None
488 * and try again.
489 */
490static int X11_GL_GetAttributes(SDL_VideoDevice *_this, Display *display, int screen, int *attribs, int size, Bool for_FBConfig, int **_pvistypeattr, bool transparent)
491{
492 int i = 0;
493 const int MAX_ATTRIBUTES = 64;
494 int *pvistypeattr = NULL;
495
496 // assert buffer is large enough to hold all SDL attributes.
497 SDL_assert(size >= MAX_ATTRIBUTES);
498
499 // Setup our GLX attributes according to the gl_config.
500 if (for_FBConfig) {
501 attribs[i++] = GLX_RENDER_TYPE;
502 if (_this->gl_config.floatbuffers) {
503 attribs[i++] = GLX_RGBA_FLOAT_BIT_ARB;
504 } else {
505 attribs[i++] = GLX_RGBA_BIT;
506 }
507 } else {
508 attribs[i++] = GLX_RGBA;
509 }
510 attribs[i++] = GLX_RED_SIZE;
511 attribs[i++] = _this->gl_config.red_size;
512 attribs[i++] = GLX_GREEN_SIZE;
513 attribs[i++] = _this->gl_config.green_size;
514 attribs[i++] = GLX_BLUE_SIZE;
515 attribs[i++] = _this->gl_config.blue_size;
516
517 if (_this->gl_config.alpha_size) {
518 attribs[i++] = GLX_ALPHA_SIZE;
519 attribs[i++] = _this->gl_config.alpha_size;
520 }
521
522 if (_this->gl_config.double_buffer) {
523 attribs[i++] = GLX_DOUBLEBUFFER;
524 if (for_FBConfig) {
525 attribs[i++] = True;
526 }
527 }
528
529 attribs[i++] = GLX_DEPTH_SIZE;
530 attribs[i++] = _this->gl_config.depth_size;
531
532 if (_this->gl_config.stencil_size) {
533 attribs[i++] = GLX_STENCIL_SIZE;
534 attribs[i++] = _this->gl_config.stencil_size;
535 }
536
537 if (_this->gl_config.accum_red_size) {
538 attribs[i++] = GLX_ACCUM_RED_SIZE;
539 attribs[i++] = _this->gl_config.accum_red_size;
540 }
541
542 if (_this->gl_config.accum_green_size) {
543 attribs[i++] = GLX_ACCUM_GREEN_SIZE;
544 attribs[i++] = _this->gl_config.accum_green_size;
545 }
546
547 if (_this->gl_config.accum_blue_size) {
548 attribs[i++] = GLX_ACCUM_BLUE_SIZE;
549 attribs[i++] = _this->gl_config.accum_blue_size;
550 }
551
552 if (_this->gl_config.accum_alpha_size) {
553 attribs[i++] = GLX_ACCUM_ALPHA_SIZE;
554 attribs[i++] = _this->gl_config.accum_alpha_size;
555 }
556
557 if (_this->gl_config.stereo) {
558 attribs[i++] = GLX_STEREO;
559 if (for_FBConfig) {
560 attribs[i++] = True;
561 }
562 }
563
564 if (_this->gl_config.multisamplebuffers) {
565 attribs[i++] = GLX_SAMPLE_BUFFERS_ARB;
566 attribs[i++] = _this->gl_config.multisamplebuffers;
567 }
568
569 if (_this->gl_config.multisamplesamples) {
570 attribs[i++] = GLX_SAMPLES_ARB;
571 attribs[i++] = _this->gl_config.multisamplesamples;
572 }
573
574 if (_this->gl_config.floatbuffers) {
575 attribs[i++] = GLX_RENDER_TYPE;
576 attribs[i++] = GLX_RGBA_FLOAT_TYPE_ARB;
577 }
578
579 if (_this->gl_config.framebuffer_srgb_capable) {
580 attribs[i++] = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
581 attribs[i++] = True; // always needed, for_FBConfig or not!
582 }
583
584 if (_this->gl_config.accelerated >= 0 &&
585 _this->gl_data->HAS_GLX_EXT_visual_rating) {
586 attribs[i++] = GLX_VISUAL_CAVEAT_EXT;
587 attribs[i++] = _this->gl_config.accelerated ? GLX_NONE_EXT : GLX_SLOW_VISUAL_EXT;
588 }
589
590 // Un-wanted when we request a transparent buffer
591 if (!transparent) {
592 /* If we're supposed to use DirectColor visuals, and we've got the
593 EXT_visual_info extension, then add GLX_X_VISUAL_TYPE_EXT. */
594 if (X11_UseDirectColorVisuals() && _this->gl_data->HAS_GLX_EXT_visual_info) {
595 pvistypeattr = &attribs[i];
596 attribs[i++] = GLX_X_VISUAL_TYPE_EXT;
597 attribs[i++] = GLX_DIRECT_COLOR_EXT;
598 }
599 }
600
601 attribs[i++] = None;
602
603 SDL_assert(i <= MAX_ATTRIBUTES);
604
605 if (_pvistypeattr) {
606 *_pvistypeattr = pvistypeattr;
607 }
608
609 return i;
610}
611
612//get the first transparent Visual
613static XVisualInfo* X11_GL_GetTransparentVisualInfo(Display *display, int screen)
614{
615 XVisualInfo* visualinfo = NULL;
616 XVisualInfo vi_in;
617 int out_count = 0;
618
619 vi_in.screen = screen;
620 visualinfo = X11_XGetVisualInfo(display, VisualScreenMask, &vi_in, &out_count);
621 if (visualinfo != NULL) {
622 int i = 0;
623 for (i = 0; i < out_count; i++) {
624 XVisualInfo* v = &visualinfo[i];
625 Uint32 format = X11_GetPixelFormatFromVisualInfo(display, v);
626 if (SDL_ISPIXELFORMAT_ALPHA(format)) {
627 vi_in.screen = screen;
628 vi_in.visualid = v->visualid;
629 X11_XFree(visualinfo);
630 visualinfo = X11_XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &vi_in, &out_count);
631 break;
632 }
633 }
634 }
635 return visualinfo;
636}
637
638XVisualInfo *X11_GL_GetVisual(SDL_VideoDevice *_this, Display *display, int screen, bool transparent)
639{
640 // 64 seems nice.
641 int attribs[64];
642 XVisualInfo *vinfo = NULL;
643 int *pvistypeattr = NULL;
644
645 if (!_this->gl_data) {
646 // The OpenGL library wasn't loaded, SDL_GetError() should have info
647 return NULL;
648 }
649
650 if (_this->gl_data->glXChooseFBConfig &&
651 _this->gl_data->glXGetVisualFromFBConfig) {
652 GLXFBConfig *framebuffer_config = NULL;
653 int fbcount = 0;
654
655 X11_GL_GetAttributes(_this, display, screen, attribs, 64, true, &pvistypeattr, transparent);
656 framebuffer_config = _this->gl_data->glXChooseFBConfig(display, screen, attribs, &fbcount);
657 if (!framebuffer_config && (pvistypeattr != NULL)) {
658 *pvistypeattr = None;
659 framebuffer_config = _this->gl_data->glXChooseFBConfig(display, screen, attribs, &fbcount);
660 }
661
662 if (transparent) {
663 // Return the first transparent Visual
664 int i;
665 for (i = 0; i < fbcount; i++) {
666 Uint32 format;
667 vinfo = _this->gl_data->glXGetVisualFromFBConfig(display, framebuffer_config[i]);
668 format = X11_GetPixelFormatFromVisualInfo(display, vinfo);
669 if (SDL_ISPIXELFORMAT_ALPHA(format)) { // found!
670 X11_XFree(framebuffer_config);
671 framebuffer_config = NULL;
672 break;
673 }
674 X11_XFree(vinfo);
675 vinfo = NULL;
676 }
677 }
678
679 if (framebuffer_config) {
680 vinfo = _this->gl_data->glXGetVisualFromFBConfig(display, framebuffer_config[0]);
681 }
682
683 X11_XFree(framebuffer_config);
684 }
685
686 if (!vinfo) {
687 X11_GL_GetAttributes(_this, display, screen, attribs, 64, false, &pvistypeattr, transparent);
688 vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs);
689
690 if (!vinfo && (pvistypeattr != NULL)) {
691 *pvistypeattr = None;
692 vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs);
693 }
694 }
695
696 if (transparent && vinfo) {
697 Uint32 format = X11_GetPixelFormatFromVisualInfo(display, vinfo);
698 if (!SDL_ISPIXELFORMAT_ALPHA(format)) {
699 // not transparent!
700 XVisualInfo* visualinfo = X11_GL_GetTransparentVisualInfo(display, screen);
701 if (visualinfo != NULL) {
702 X11_XFree(vinfo);
703 vinfo = visualinfo;
704 }
705 }
706 }
707
708 if (!vinfo) {
709 SDL_SetError("Couldn't find matching GLX visual");
710 }
711 return vinfo;
712}
713
714static int (*handler)(Display *, XErrorEvent *) = NULL;
715static const char *errorHandlerOperation = NULL;
716static int errorBase = 0;
717static int errorCode = 0;
718static int X11_GL_ErrorHandler(Display *d, XErrorEvent *e)
719{
720 char *x11_error = NULL;
721 char x11_error_locale[256];
722
723 errorCode = e->error_code;
724 if (X11_XGetErrorText(d, errorCode, x11_error_locale, sizeof(x11_error_locale)) == Success) {
725 x11_error = SDL_iconv_string("UTF-8", "", x11_error_locale, SDL_strlen(x11_error_locale) + 1);
726 }
727
728 if (x11_error) {
729 SDL_SetError("Could not %s: %s", errorHandlerOperation, x11_error);
730 SDL_free(x11_error);
731 } else {
732 SDL_SetError("Could not %s: %i (Base %i)", errorHandlerOperation, errorCode, errorBase);
733 }
734
735 return 0;
736}
737
738bool X11_GL_UseEGL(SDL_VideoDevice *_this)
739{
740 SDL_assert(_this->gl_data != NULL);
741 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) {
742 // use of EGL has been requested, even for desktop GL
743 return true;
744 }
745
746 SDL_assert(_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES);
747 return (SDL_GetHintBoolean(SDL_HINT_OPENGL_ES_DRIVER, false) || _this->gl_config.major_version == 1 // No GLX extension for OpenGL ES 1.x profiles.
748 || _this->gl_config.major_version > _this->gl_data->es_profile_max_supported_version.major || (_this->gl_config.major_version == _this->gl_data->es_profile_max_supported_version.major && _this->gl_config.minor_version > _this->gl_data->es_profile_max_supported_version.minor));
749}
750
751SDL_GLContext X11_GL_CreateContext(SDL_VideoDevice *_this, SDL_Window *window)
752{
753 SDL_WindowData *data = window->internal;
754 Display *display = data->videodata->display;
755 int screen = SDL_GetDisplayDriverDataForWindow(window)->screen;
756 XWindowAttributes xattr;
757 XVisualInfo v, *vinfo;
758 int n;
759 SDL_GLContext context = NULL;
760 GLXContext share_context;
761 const int transparent = (window->flags & SDL_WINDOW_TRANSPARENT) ? true : false;
762
763 if (_this->gl_config.share_with_current_context) {
764 share_context = (GLXContext)SDL_GL_GetCurrentContext();
765 } else {
766 share_context = NULL;
767 }
768
769 // We do this to create a clean separation between X and GLX errors.
770 X11_XSync(display, False);
771 errorHandlerOperation = "create GL context";
772 errorBase = _this->gl_data->errorBase;
773 errorCode = Success;
774 handler = X11_XSetErrorHandler(X11_GL_ErrorHandler);
775 X11_XGetWindowAttributes(display, data->xwindow, &xattr);
776 v.screen = screen;
777 v.visualid = X11_XVisualIDFromVisual(xattr.visual);
778 vinfo = X11_XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &v, &n);
779 if (vinfo) {
780 if (_this->gl_config.major_version < 3 &&
781 _this->gl_config.profile_mask == 0 &&
782 _this->gl_config.flags == 0 && !transparent) {
783 // Create legacy context
784 context =
785 (SDL_GLContext)_this->gl_data->glXCreateContext(display, vinfo, share_context, True);
786 } else {
787 // max 14 attributes plus terminator
788 int attribs[15] = {
789 GLX_CONTEXT_MAJOR_VERSION_ARB,
790 _this->gl_config.major_version,
791 GLX_CONTEXT_MINOR_VERSION_ARB,
792 _this->gl_config.minor_version,
793 0
794 };
795 int iattr = 4;
796
797 // SDL profile bits match GLX profile bits
798 if (_this->gl_config.profile_mask != 0) {
799 attribs[iattr++] = GLX_CONTEXT_PROFILE_MASK_ARB;
800 attribs[iattr++] = _this->gl_config.profile_mask;
801 }
802
803 // SDL flags match GLX flags
804 if (_this->gl_config.flags != 0) {
805 attribs[iattr++] = GLX_CONTEXT_FLAGS_ARB;
806 attribs[iattr++] = _this->gl_config.flags;
807 }
808
809 // only set if glx extension is available and not the default setting
810 if ((_this->gl_data->HAS_GLX_ARB_context_flush_control) && (_this->gl_config.release_behavior == 0)) {
811 attribs[iattr++] = GLX_CONTEXT_RELEASE_BEHAVIOR_ARB;
812 attribs[iattr++] =
813 _this->gl_config.release_behavior ? GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB : GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB;
814 }
815
816 // only set if glx extension is available and not the default setting
817 if ((_this->gl_data->HAS_GLX_ARB_create_context_robustness) && (_this->gl_config.reset_notification != 0)) {
818 attribs[iattr++] = GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB;
819 attribs[iattr++] =
820 _this->gl_config.reset_notification ? GLX_LOSE_CONTEXT_ON_RESET_ARB : GLX_NO_RESET_NOTIFICATION_ARB;
821 }
822
823 // only set if glx extension is available and not the default setting
824 if ((_this->gl_data->HAS_GLX_ARB_create_context_no_error) && (_this->gl_config.no_error != 0)) {
825 attribs[iattr++] = GLX_CONTEXT_OPENGL_NO_ERROR_ARB;
826 attribs[iattr++] = _this->gl_config.no_error;
827 }
828
829 attribs[iattr++] = 0;
830
831 // Get a pointer to the context creation function for GL 3.0
832 if (!_this->gl_data->glXCreateContextAttribsARB) {
833 SDL_SetError("OpenGL 3.0 and later are not supported by this system");
834 } else {
835 int glxAttribs[64];
836
837 // Create a GL 3.x context
838 GLXFBConfig *framebuffer_config = NULL;
839 int fbcount = 0;
840 int *pvistypeattr = NULL;
841
842 X11_GL_GetAttributes(_this, display, screen, glxAttribs, 64, true, &pvistypeattr, transparent);
843
844 if (_this->gl_data->glXChooseFBConfig) {
845 framebuffer_config = _this->gl_data->glXChooseFBConfig(display,
846 DefaultScreen(display), glxAttribs,
847 &fbcount);
848
849 if (!framebuffer_config && (pvistypeattr != NULL)) {
850 *pvistypeattr = None;
851 framebuffer_config = _this->gl_data->glXChooseFBConfig(display,
852 DefaultScreen(display), glxAttribs,
853 &fbcount);
854 }
855
856 if (transparent && (framebuffer_config != NULL)) {
857 int i;
858 for (i = 0; i < fbcount; i++) {
859 XVisualInfo* vinfo_temp = _this->gl_data->glXGetVisualFromFBConfig(display, framebuffer_config[i]);
860 if ( vinfo_temp != NULL) {
861 Uint32 format = X11_GetPixelFormatFromVisualInfo(display, vinfo_temp);
862 if (SDL_ISPIXELFORMAT_ALPHA(format)) {
863 // found!
864 context = (SDL_GLContext)_this->gl_data->glXCreateContextAttribsARB(display,
865 framebuffer_config[i],
866 share_context, True, attribs);
867 X11_XFree(framebuffer_config);
868 framebuffer_config = NULL;
869 X11_XFree(vinfo_temp);
870 break;
871 }
872 X11_XFree(vinfo_temp);
873 }
874 }
875 }
876 if (framebuffer_config) {
877 context = (SDL_GLContext)_this->gl_data->glXCreateContextAttribsARB(display,
878 framebuffer_config[0],
879 share_context, True, attribs);
880 X11_XFree(framebuffer_config);
881 }
882 }
883 }
884 }
885 X11_XFree(vinfo);
886 }
887 X11_XSync(display, False);
888 X11_XSetErrorHandler(handler);
889
890 if (!context) {
891 if (errorCode == Success) {
892 SDL_SetError("Could not create GL context");
893 }
894 return NULL;
895 }
896
897 if (!X11_GL_MakeCurrent(_this, window, context)) {
898 X11_GL_DestroyContext(_this, context);
899 return NULL;
900 }
901
902 return context;
903}
904
905bool X11_GL_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context)
906{
907 Display *display = _this->internal->display;
908 Window drawable =
909 (context ? window->internal->xwindow : None);
910 GLXContext glx_context = (GLXContext)context;
911 int rc;
912
913 if (!_this->gl_data) {
914 return SDL_SetError("OpenGL not initialized");
915 }
916
917 // We do this to create a clean separation between X and GLX errors.
918 X11_XSync(display, False);
919 errorHandlerOperation = "make GL context current";
920 errorBase = _this->gl_data->errorBase;
921 errorCode = Success;
922 handler = X11_XSetErrorHandler(X11_GL_ErrorHandler);
923 rc = _this->gl_data->glXMakeCurrent(display, drawable, glx_context);
924 X11_XSetErrorHandler(handler);
925
926 if (errorCode != Success) { // uhoh, an X error was thrown!
927 return false; // the error handler called SDL_SetError() already.
928 } else if (!rc) { // glXMakeCurrent() failed without throwing an X error
929 return SDL_SetError("Unable to make GL context current");
930 }
931
932 return true;
933}
934
935/*
936 0 is a valid argument to glXSwapInterval(MESA|EXT) and setting it to 0
937 will undo the effect of a previous call with a value that is greater
938 than zero (or at least that is what the docs say). OTOH, 0 is an invalid
939 argument to glXSwapIntervalSGI and it returns an error if you call it
940 with 0 as an argument.
941*/
942
943static int swapinterval = 0;
944bool X11_GL_SetSwapInterval(SDL_VideoDevice *_this, int interval)
945{
946 bool result = false;
947
948 if ((interval < 0) && (!_this->gl_data->HAS_GLX_EXT_swap_control_tear)) {
949 return SDL_SetError("Negative swap interval unsupported in this GL");
950 } else if (_this->gl_data->glXSwapIntervalEXT) {
951 Display *display = _this->internal->display;
952 const SDL_WindowData *windowdata = SDL_GL_GetCurrentWindow()->internal;
953
954 Window drawable = windowdata->xwindow;
955
956 /*
957 * This is a workaround for a bug in NVIDIA drivers. Bug has been reported
958 * and will be fixed in a future release (probably 319.xx).
959 *
960 * There's a bug where glXSetSwapIntervalEXT ignores updates because
961 * it has the wrong value cached. To work around it, we just run a no-op
962 * update to the current value.
963 */
964 int currentInterval = 0;
965 X11_GL_GetSwapInterval(_this, &currentInterval);
966 _this->gl_data->glXSwapIntervalEXT(display, drawable, currentInterval);
967 _this->gl_data->glXSwapIntervalEXT(display, drawable, interval);
968 result = true;
969 swapinterval = interval;
970 } else if (_this->gl_data->glXSwapIntervalMESA) {
971 const int rc = _this->gl_data->glXSwapIntervalMESA(interval);
972 if (rc == 0) {
973 swapinterval = interval;
974 result = true;
975 } else {
976 result = SDL_SetError("glXSwapIntervalMESA failed");
977 }
978 } else if (_this->gl_data->glXSwapIntervalSGI) {
979 const int rc = _this->gl_data->glXSwapIntervalSGI(interval);
980 if (rc == 0) {
981 swapinterval = interval;
982 result = true;
983 } else {
984 result = SDL_SetError("glXSwapIntervalSGI failed");
985 }
986 } else {
987 return SDL_Unsupported();
988 }
989 return result;
990}
991
992static SDL_GLSwapIntervalTearBehavior CheckSwapIntervalTearBehavior(SDL_VideoDevice *_this, Window drawable, unsigned int current_val, unsigned int current_allow_late)
993{
994 /* Mesa and Nvidia interpret GLX_EXT_swap_control_tear differently, as of this writing, so
995 figure out which behavior we have.
996 Technical details: https://github.com/libsdl-org/SDL/issues/8004#issuecomment-1819603282 */
997 if (_this->gl_data->swap_interval_tear_behavior == SDL_SWAPINTERVALTEAR_UNTESTED) {
998 if (!_this->gl_data->HAS_GLX_EXT_swap_control_tear) {
999 _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_UNKNOWN;
1000 } else {
1001 Display *display = _this->internal->display;
1002 unsigned int allow_late_swap_tearing = 22;
1003 int original_val = (int) current_val;
1004
1005 /*
1006 * This is a workaround for a bug in NVIDIA drivers. Bug has been reported
1007 * and will be fixed in a future release (probably 319.xx).
1008 *
1009 * There's a bug where glXSetSwapIntervalEXT ignores updates because
1010 * it has the wrong value cached. To work around it, we just run a no-op
1011 * update to the current value.
1012 */
1013 _this->gl_data->glXSwapIntervalEXT(display, drawable, current_val);
1014
1015 // set it to no swap interval and see how it affects GLX_LATE_SWAPS_TEAR_EXT...
1016 _this->gl_data->glXSwapIntervalEXT(display, drawable, 0);
1017 _this->gl_data->glXQueryDrawable(display, drawable, GLX_LATE_SWAPS_TEAR_EXT, &allow_late_swap_tearing);
1018
1019 if (allow_late_swap_tearing == 0) { // GLX_LATE_SWAPS_TEAR_EXT says whether late swapping is currently in use
1020 _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_NVIDIA;
1021 if (current_allow_late) {
1022 original_val = -original_val;
1023 }
1024 } else if (allow_late_swap_tearing == 1) { // GLX_LATE_SWAPS_TEAR_EXT says whether the Drawable can use late swapping at all
1025 _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_MESA;
1026 } else { // unexpected outcome!
1027 _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_UNKNOWN;
1028 }
1029
1030 // set us back to what it was originally...
1031 _this->gl_data->glXSwapIntervalEXT(display, drawable, original_val);
1032 }
1033 }
1034
1035 return _this->gl_data->swap_interval_tear_behavior;
1036}
1037
1038
1039bool X11_GL_GetSwapInterval(SDL_VideoDevice *_this, int *interval)
1040{
1041 if (_this->gl_data->glXSwapIntervalEXT) {
1042 Display *display = _this->internal->display;
1043 const SDL_WindowData *windowdata = SDL_GL_GetCurrentWindow()->internal;
1044 Window drawable = windowdata->xwindow;
1045 unsigned int allow_late_swap_tearing = 0;
1046 unsigned int val = 0;
1047
1048 if (_this->gl_data->HAS_GLX_EXT_swap_control_tear) {
1049 allow_late_swap_tearing = 22; // set this to nonsense.
1050 _this->gl_data->glXQueryDrawable(display, drawable,
1051 GLX_LATE_SWAPS_TEAR_EXT,
1052 &allow_late_swap_tearing);
1053 }
1054
1055 _this->gl_data->glXQueryDrawable(display, drawable,
1056 GLX_SWAP_INTERVAL_EXT, &val);
1057
1058 *interval = (int)val;
1059
1060 switch (CheckSwapIntervalTearBehavior(_this, drawable, val, allow_late_swap_tearing)) {
1061 case SDL_SWAPINTERVALTEAR_MESA:
1062 *interval = (int)val; // unsigned int cast to signed that generates negative value if necessary.
1063 break;
1064
1065 case SDL_SWAPINTERVALTEAR_NVIDIA:
1066 default:
1067 if ((allow_late_swap_tearing) && (val > 0)) {
1068 *interval = -((int)val);
1069 }
1070 break;
1071 }
1072
1073 return true;
1074 } else if (_this->gl_data->glXGetSwapIntervalMESA) {
1075 int val = _this->gl_data->glXGetSwapIntervalMESA();
1076 if (val == GLX_BAD_CONTEXT) {
1077 return SDL_SetError("GLX_BAD_CONTEXT");
1078 }
1079 *interval = val;
1080 return true;
1081 } else {
1082 *interval = swapinterval;
1083 return true;
1084 }
1085}
1086
1087bool X11_GL_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
1088{
1089 SDL_WindowData *data = window->internal;
1090 Display *display = data->videodata->display;
1091
1092 _this->gl_data->glXSwapBuffers(display, data->xwindow);
1093
1094#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
1095 X11_HandlePresent(data->window);
1096#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
1097
1098 return true;
1099}
1100
1101bool X11_GL_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context)
1102{
1103 Display *display = _this->internal->display;
1104 GLXContext glx_context = (GLXContext)context;
1105
1106 if (!_this->gl_data) {
1107 return true;
1108 }
1109 _this->gl_data->glXDestroyContext(display, glx_context);
1110 X11_XSync(display, False);
1111 return true;
1112}
1113
1114#endif // SDL_VIDEO_OPENGL_GLX
1115
1116#endif // SDL_VIDEO_DRIVER_X11
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengl.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengl.h
new file mode 100644
index 0000000..24db485
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengl.h
@@ -0,0 +1,96 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifndef SDL_x11opengl_h_
24#define SDL_x11opengl_h_
25
26#ifdef SDL_VIDEO_OPENGL_GLX
27#include <SDL3/SDL_opengl.h>
28#include <GL/glx.h>
29
30typedef void (*__GLXextFuncPtr)(void);
31
32typedef enum SDL_GLSwapIntervalTearBehavior
33{
34 SDL_SWAPINTERVALTEAR_UNTESTED,
35 SDL_SWAPINTERVALTEAR_UNKNOWN,
36 SDL_SWAPINTERVALTEAR_MESA,
37 SDL_SWAPINTERVALTEAR_NVIDIA
38} SDL_GLSwapIntervalTearBehavior;
39
40struct SDL_GLDriverData
41{
42 int errorBase, eventBase;
43
44 bool HAS_GLX_EXT_visual_rating;
45 bool HAS_GLX_EXT_visual_info;
46 bool HAS_GLX_EXT_swap_control_tear;
47 bool HAS_GLX_ARB_context_flush_control;
48 bool HAS_GLX_ARB_create_context_robustness;
49 bool HAS_GLX_ARB_create_context_no_error;
50
51 /* Max version of OpenGL ES context that can be created if the
52 implementation supports GLX_EXT_create_context_es2_profile.
53 major = minor = 0 when unsupported.
54 */
55 struct
56 {
57 int major;
58 int minor;
59 } es_profile_max_supported_version;
60
61 SDL_GLSwapIntervalTearBehavior swap_interval_tear_behavior;
62
63 Bool (*glXQueryExtension)(Display *, int *, int *);
64 __GLXextFuncPtr (*glXGetProcAddress)(const GLubyte *);
65 XVisualInfo *(*glXChooseVisual)(Display *, int, int *);
66 GLXContext (*glXCreateContext)(Display *, XVisualInfo *, GLXContext, Bool);
67 GLXContext (*glXCreateContextAttribsARB)(Display *, GLXFBConfig, GLXContext, Bool, const int *);
68 GLXFBConfig *(*glXChooseFBConfig)(Display *, int, const int *, int *);
69 XVisualInfo *(*glXGetVisualFromFBConfig)(Display *, GLXFBConfig);
70 void (*glXDestroyContext)(Display *, GLXContext);
71 Bool (*glXMakeCurrent)(Display *, GLXDrawable, GLXContext);
72 void (*glXSwapBuffers)(Display *, GLXDrawable);
73 void (*glXQueryDrawable)(Display *, GLXDrawable, int, unsigned int *);
74 void (*glXSwapIntervalEXT)(Display *, GLXDrawable, int);
75 int (*glXSwapIntervalSGI)(int);
76 int (*glXSwapIntervalMESA)(int);
77 int (*glXGetSwapIntervalMESA)(void);
78};
79
80// OpenGL functions
81extern bool X11_GL_LoadLibrary(SDL_VideoDevice *_this, const char *path);
82extern SDL_FunctionPointer X11_GL_GetProcAddress(SDL_VideoDevice *_this, const char *proc);
83extern void X11_GL_UnloadLibrary(SDL_VideoDevice *_this);
84extern bool X11_GL_UseEGL(SDL_VideoDevice *_this);
85extern XVisualInfo *X11_GL_GetVisual(SDL_VideoDevice *_this, Display *display, int screen, bool transparent);
86extern SDL_GLContext X11_GL_CreateContext(SDL_VideoDevice *_this, SDL_Window *window);
87extern bool X11_GL_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window,
88 SDL_GLContext context);
89extern bool X11_GL_SetSwapInterval(SDL_VideoDevice *_this, int interval);
90extern bool X11_GL_GetSwapInterval(SDL_VideoDevice *_this, int *interval);
91extern bool X11_GL_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window);
92extern bool X11_GL_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context);
93
94#endif // SDL_VIDEO_OPENGL_GLX
95
96#endif // SDL_x11opengl_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengles.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengles.c
new file mode 100644
index 0000000..9c1910f
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengles.c
@@ -0,0 +1,152 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#if defined(SDL_VIDEO_DRIVER_X11) && defined(SDL_VIDEO_OPENGL_EGL)
24
25#include "SDL_x11video.h"
26#include "SDL_x11opengles.h"
27#include "SDL_x11opengl.h"
28#include "SDL_x11xsync.h"
29
30// EGL implementation of SDL OpenGL support
31
32bool X11_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path)
33{
34 SDL_VideoData *data = _this->internal;
35
36 // If the profile requested is not GL ES, switch over to X11_GL functions
37 if ((_this->gl_config.profile_mask != SDL_GL_CONTEXT_PROFILE_ES) &&
38 !SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) {
39#ifdef SDL_VIDEO_OPENGL_GLX
40 X11_GLES_UnloadLibrary(_this);
41 _this->GL_LoadLibrary = X11_GL_LoadLibrary;
42 _this->GL_GetProcAddress = X11_GL_GetProcAddress;
43 _this->GL_UnloadLibrary = X11_GL_UnloadLibrary;
44 _this->GL_CreateContext = X11_GL_CreateContext;
45 _this->GL_MakeCurrent = X11_GL_MakeCurrent;
46 _this->GL_SetSwapInterval = X11_GL_SetSwapInterval;
47 _this->GL_GetSwapInterval = X11_GL_GetSwapInterval;
48 _this->GL_SwapWindow = X11_GL_SwapWindow;
49 _this->GL_DestroyContext = X11_GL_DestroyContext;
50 return X11_GL_LoadLibrary(_this, path);
51#else
52 return SDL_SetError("SDL not configured with OpenGL/GLX support");
53#endif
54 }
55
56 return SDL_EGL_LoadLibrary(_this, path, (NativeDisplayType)data->display, _this->gl_config.egl_platform);
57}
58
59XVisualInfo *X11_GLES_GetVisual(SDL_VideoDevice *_this, Display *display, int screen, bool transparent)
60{
61
62 XVisualInfo *egl_visualinfo = NULL;
63 EGLint visual_id = 0;
64 XVisualInfo vi_in;
65 int out_count = 0;
66
67 if (!_this->egl_data) {
68 // The EGL library wasn't loaded, SDL_GetError() should have info
69 return NULL;
70 }
71
72 if (_this->egl_data->eglGetConfigAttrib(_this->egl_data->egl_display,
73 _this->egl_data->egl_config,
74 EGL_NATIVE_VISUAL_ID,
75 &visual_id) == EGL_FALSE) {
76 visual_id = 0;
77 }
78 if (visual_id != 0) {
79 vi_in.screen = screen;
80 vi_in.visualid = visual_id;
81 egl_visualinfo = X11_XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &vi_in, &out_count);
82 if (transparent && egl_visualinfo) {
83 Uint32 format = X11_GetPixelFormatFromVisualInfo(display, egl_visualinfo);
84 if (!SDL_ISPIXELFORMAT_ALPHA(format)) {
85 // not transparent!
86 X11_XFree(egl_visualinfo);
87 egl_visualinfo = NULL;
88 }
89 }
90 }
91
92 if(!egl_visualinfo) {
93 // Use the default visual when all else fails
94 vi_in.screen = screen;
95 egl_visualinfo = X11_XGetVisualInfo(display,
96 VisualScreenMask,
97 &vi_in, &out_count);
98
99 // Return the first transparent Visual
100 if (transparent) {
101 int i;
102 for (i = 0; i < out_count; i++) {
103 XVisualInfo *v = &egl_visualinfo[i];
104 Uint32 format = X11_GetPixelFormatFromVisualInfo(display, v);
105 if (SDL_ISPIXELFORMAT_ALPHA(format)) { // found!
106 // re-request it to have a copy that can be X11_XFree'ed later
107 vi_in.screen = screen;
108 vi_in.visualid = v->visualid;
109 X11_XFree(egl_visualinfo);
110 egl_visualinfo = X11_XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &vi_in, &out_count);
111 return egl_visualinfo;
112 }
113 }
114 }
115 }
116 return egl_visualinfo;
117}
118
119SDL_GLContext X11_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window)
120{
121 SDL_GLContext context;
122 SDL_WindowData *data = window->internal;
123 Display *display = data->videodata->display;
124
125 X11_XSync(display, False);
126 context = SDL_EGL_CreateContext(_this, data->egl_surface);
127 X11_XSync(display, False);
128
129 return context;
130}
131
132SDL_EGLSurface X11_GLES_GetEGLSurface(SDL_VideoDevice *_this, SDL_Window *window)
133{
134 SDL_WindowData *data = window->internal;
135 return data->egl_surface;
136}
137
138bool X11_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
139{
140 const bool ret = SDL_EGL_SwapBuffers(_this, window->internal->egl_surface); \
141
142#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
143 X11_HandlePresent(window);
144#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
145
146 return ret;
147}
148
149SDL_EGL_MakeCurrent_impl(X11)
150
151#endif // SDL_VIDEO_DRIVER_X11 && SDL_VIDEO_OPENGL_EGL
152
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengles.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengles.h
new file mode 100644
index 0000000..f8e8d3b
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengles.h
@@ -0,0 +1,55 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifndef SDL_x11opengles_h_
24#define SDL_x11opengles_h_
25
26#ifdef SDL_VIDEO_OPENGL_EGL
27
28#include "../SDL_sysvideo.h"
29#include "../SDL_egl_c.h"
30
31typedef struct SDL_PrivateGLESData
32{
33 // 1401 If the struct-declaration-list contains no named members, the behavior is undefined.
34 // warning: empty struct has size 0 in C, size 1 in C++ [-Wc++-compat]
35 int dummy;
36} SDL_PrivateGLESData;
37
38// OpenGLES functions
39#define X11_GLES_GetAttribute SDL_EGL_GetAttribute
40#define X11_GLES_GetProcAddress SDL_EGL_GetProcAddressInternal
41#define X11_GLES_UnloadLibrary SDL_EGL_UnloadLibrary
42#define X11_GLES_SetSwapInterval SDL_EGL_SetSwapInterval
43#define X11_GLES_GetSwapInterval SDL_EGL_GetSwapInterval
44#define X11_GLES_DestroyContext SDL_EGL_DestroyContext
45
46extern bool X11_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path);
47extern XVisualInfo *X11_GLES_GetVisual(SDL_VideoDevice *_this, Display *display, int screen, bool transparent);
48extern SDL_GLContext X11_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window);
49extern bool X11_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window);
50extern bool X11_GLES_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context);
51extern SDL_EGLSurface X11_GLES_GetEGLSurface(SDL_VideoDevice *_this, SDL_Window *window);
52
53#endif // SDL_VIDEO_OPENGL_EGL
54
55#endif // SDL_x11opengles_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11pen.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11pen.c
new file mode 100644
index 0000000..f16da51
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11pen.c
@@ -0,0 +1,437 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "../../SDL_internal.h"
22
23#include "../../events/SDL_pen_c.h"
24#include "../SDL_sysvideo.h"
25#include "SDL_x11pen.h"
26#include "SDL_x11video.h"
27#include "SDL_x11xinput2.h"
28
29#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
30
31// Does this device have a valuator for pressure sensitivity?
32static bool X11_XInput2DeviceIsPen(SDL_VideoDevice *_this, const XIDeviceInfo *dev)
33{
34 const SDL_VideoData *data = _this->internal;
35 for (int i = 0; i < dev->num_classes; i++) {
36 const XIAnyClassInfo *classinfo = dev->classes[i];
37 if (classinfo->type == XIValuatorClass) {
38 const XIValuatorClassInfo *val_classinfo = (const XIValuatorClassInfo *)classinfo;
39 if (val_classinfo->label == data->atoms.pen_atom_abs_pressure) {
40 return true;
41 }
42 }
43 }
44
45 return false;
46}
47
48// Heuristically determines if device is an eraser
49static bool X11_XInput2PenIsEraser(SDL_VideoDevice *_this, int deviceid, char *devicename)
50{
51 #define PEN_ERASER_NAME_TAG "eraser" // String constant to identify erasers
52 SDL_VideoData *data = _this->internal;
53
54 if (data->atoms.pen_atom_wacom_tool_type != None) {
55 Atom type_return;
56 int format_return;
57 unsigned long num_items_return;
58 unsigned long bytes_after_return;
59 unsigned char *tooltype_name_info = NULL;
60
61 // Try Wacom-specific method
62 if (Success == X11_XIGetProperty(data->display, deviceid,
63 data->atoms.pen_atom_wacom_tool_type,
64 0, 32, False,
65 AnyPropertyType, &type_return, &format_return,
66 &num_items_return, &bytes_after_return,
67 &tooltype_name_info) &&
68 tooltype_name_info != NULL && num_items_return > 0) {
69
70 bool result = false;
71 char *tooltype_name = NULL;
72
73 if (type_return == XA_ATOM) {
74 // Atom instead of string? Un-intern
75 Atom atom = *((Atom *)tooltype_name_info);
76 if (atom != None) {
77 tooltype_name = X11_XGetAtomName(data->display, atom);
78 }
79 } else if (type_return == XA_STRING && format_return == 8) {
80 tooltype_name = (char *)tooltype_name_info;
81 }
82
83 if (tooltype_name) {
84 if (SDL_strcasecmp(tooltype_name, PEN_ERASER_NAME_TAG) == 0) {
85 result = true;
86 }
87 if (tooltype_name != (char *)tooltype_name_info) {
88 X11_XFree(tooltype_name_info);
89 }
90 X11_XFree(tooltype_name);
91
92 return result;
93 }
94 }
95 }
96
97 // Non-Wacom device?
98
99 /* We assume that a device is an eraser if its name contains the string "eraser".
100 * Unfortunately there doesn't seem to be a clean way to distinguish these cases (as of 2022-03). */
101 return (SDL_strcasestr(devicename, PEN_ERASER_NAME_TAG)) ? true : false;
102}
103
104// Read out an integer property and store into a preallocated Sint32 array, extending 8 and 16 bit values suitably.
105// Returns number of Sint32s written (<= max_words), or 0 on error.
106static size_t X11_XInput2PenGetIntProperty(SDL_VideoDevice *_this, int deviceid, Atom property, Sint32 *dest, size_t max_words)
107{
108 const SDL_VideoData *data = _this->internal;
109 Atom type_return;
110 int format_return;
111 unsigned long num_items_return;
112 unsigned long bytes_after_return;
113 unsigned char *output;
114
115 if (property == None) {
116 return 0;
117 }
118
119 if (Success != X11_XIGetProperty(data->display, deviceid,
120 property,
121 0, max_words, False,
122 XA_INTEGER, &type_return, &format_return,
123 &num_items_return, &bytes_after_return,
124 &output) ||
125 num_items_return == 0 || output == NULL) {
126 return 0;
127 }
128
129 if (type_return == XA_INTEGER) {
130 int k;
131 const int to_copy = SDL_min(max_words, num_items_return);
132
133 if (format_return == 8) {
134 Sint8 *numdata = (Sint8 *)output;
135 for (k = 0; k < to_copy; ++k) {
136 dest[k] = numdata[k];
137 }
138 } else if (format_return == 16) {
139 Sint16 *numdata = (Sint16 *)output;
140 for (k = 0; k < to_copy; ++k) {
141 dest[k] = numdata[k];
142 }
143 } else {
144 SDL_memcpy(dest, output, sizeof(Sint32) * to_copy);
145 }
146 X11_XFree(output);
147 return to_copy;
148 }
149
150 return 0; // type mismatch
151}
152
153// Identify Wacom devices (if true is returned) and extract their device type and serial IDs
154static bool X11_XInput2PenWacomDeviceID(SDL_VideoDevice *_this, int deviceid, Uint32 *wacom_devicetype_id, Uint32 *wacom_serial)
155{
156 SDL_VideoData *data = _this->internal;
157 Sint32 serial_id_buf[3];
158 int result;
159
160 if ((result = X11_XInput2PenGetIntProperty(_this, deviceid, data->atoms.pen_atom_wacom_serial_ids, serial_id_buf, 3)) == 3) {
161 *wacom_devicetype_id = serial_id_buf[2];
162 *wacom_serial = serial_id_buf[1];
163 return true;
164 }
165
166 *wacom_devicetype_id = *wacom_serial = 0;
167 return false;
168}
169
170
171typedef struct FindPenByDeviceIDData
172{
173 int x11_deviceid;
174 void *handle;
175} FindPenByDeviceIDData;
176
177static bool FindPenByDeviceID(void *handle, void *userdata)
178{
179 const X11_PenHandle *x11_handle = (const X11_PenHandle *) handle;
180 FindPenByDeviceIDData *data = (FindPenByDeviceIDData *) userdata;
181 if (x11_handle->x11_deviceid != data->x11_deviceid) {
182 return false;
183 }
184 data->handle = handle;
185 return true;
186}
187
188X11_PenHandle *X11_FindPenByDeviceID(int deviceid)
189{
190 FindPenByDeviceIDData data;
191 data.x11_deviceid = deviceid;
192 data.handle = NULL;
193 SDL_FindPenByCallback(FindPenByDeviceID, &data);
194 return (X11_PenHandle *) data.handle;
195}
196
197static X11_PenHandle *X11_MaybeAddPen(SDL_VideoDevice *_this, const XIDeviceInfo *dev)
198{
199 SDL_VideoData *data = _this->internal;
200 SDL_PenCapabilityFlags capabilities = 0;
201 X11_PenHandle *handle = NULL;
202
203 if ((dev->use != XISlavePointer && (dev->use != XIFloatingSlave)) || dev->enabled == 0 || !X11_XInput2DeviceIsPen(_this, dev)) {
204 return NULL; // Only track physical devices that are enabled and look like pens
205 } else if ((handle = X11_FindPenByDeviceID(dev->deviceid)) != 0) {
206 return handle; // already have this pen, skip it.
207 } else if ((handle = SDL_calloc(1, sizeof (*handle))) == NULL) {
208 return NULL; // oh well.
209 }
210
211 for (int i = 0; i < SDL_arraysize(handle->valuator_for_axis); i++) {
212 handle->valuator_for_axis[i] = SDL_X11_PEN_AXIS_VALUATOR_MISSING; // until proven otherwise
213 }
214
215 int total_buttons = 0;
216 for (int i = 0; i < dev->num_classes; i++) {
217 const XIAnyClassInfo *classinfo = dev->classes[i];
218 if (classinfo->type == XIButtonClass) {
219 const XIButtonClassInfo *button_classinfo = (const XIButtonClassInfo *)classinfo;
220 total_buttons += button_classinfo->num_buttons;
221 } else if (classinfo->type == XIValuatorClass) {
222 const XIValuatorClassInfo *val_classinfo = (const XIValuatorClassInfo *)classinfo;
223 const Sint8 valuator_nr = val_classinfo->number;
224 const Atom vname = val_classinfo->label;
225 const float min = (float)val_classinfo->min;
226 const float max = (float)val_classinfo->max;
227 bool use_this_axis = true;
228 SDL_PenAxis axis = SDL_PEN_AXIS_COUNT;
229
230 // afaict, SDL_PEN_AXIS_DISTANCE is never reported by XInput2 (Wayland can offer it, though)
231 if (vname == data->atoms.pen_atom_abs_pressure) {
232 axis = SDL_PEN_AXIS_PRESSURE;
233 } else if (vname == data->atoms.pen_atom_abs_tilt_x) {
234 axis = SDL_PEN_AXIS_XTILT;
235 } else if (vname == data->atoms.pen_atom_abs_tilt_y) {
236 axis = SDL_PEN_AXIS_YTILT;
237 } else {
238 use_this_axis = false;
239 }
240
241 // !!! FIXME: there are wacom-specific hacks for getting SDL_PEN_AXIS_(ROTATION|SLIDER) on some devices, but for simplicity, we're skipping all that for now.
242
243 if (use_this_axis) {
244 capabilities |= SDL_GetPenCapabilityFromAxis(axis);
245 handle->valuator_for_axis[axis] = valuator_nr;
246 handle->axis_min[axis] = min;
247 handle->axis_max[axis] = max;
248 }
249 }
250 }
251
252 // We have a pen if and only if the device measures pressure.
253 // We checked this in X11_XInput2DeviceIsPen, so just assert it here.
254 SDL_assert(capabilities & SDL_PEN_CAPABILITY_PRESSURE);
255
256 const bool is_eraser = X11_XInput2PenIsEraser(_this, dev->deviceid, dev->name);
257 Uint32 wacom_devicetype_id = 0;
258 Uint32 wacom_serial = 0;
259 X11_XInput2PenWacomDeviceID(_this, dev->deviceid, &wacom_devicetype_id, &wacom_serial);
260
261 SDL_PenInfo peninfo;
262 SDL_zero(peninfo);
263 peninfo.capabilities = capabilities;
264 peninfo.max_tilt = -1;
265 peninfo.wacom_id = wacom_devicetype_id;
266 peninfo.num_buttons = total_buttons;
267 peninfo.subtype = is_eraser ? SDL_PEN_TYPE_ERASER : SDL_PEN_TYPE_PEN;
268 if (is_eraser) {
269 peninfo.capabilities |= SDL_PEN_CAPABILITY_ERASER;
270 }
271
272 handle->is_eraser = is_eraser;
273 handle->x11_deviceid = dev->deviceid;
274
275 handle->pen = SDL_AddPenDevice(0, dev->name, &peninfo, handle);
276 if (!handle->pen) {
277 SDL_free(handle);
278 return NULL;
279 }
280
281 return handle;
282}
283
284X11_PenHandle *X11_MaybeAddPenByDeviceID(SDL_VideoDevice *_this, int deviceid)
285{
286 SDL_VideoData *data = _this->internal;
287 int num_device_info = 0;
288 XIDeviceInfo *device_info = X11_XIQueryDevice(data->display, deviceid, &num_device_info);
289 if (device_info) {
290 SDL_assert(num_device_info == 1);
291 X11_PenHandle *handle = X11_MaybeAddPen(_this, device_info);
292 X11_XIFreeDeviceInfo(device_info);
293 return handle;
294 }
295 return NULL;
296}
297
298void X11_RemovePenByDeviceID(int deviceid)
299{
300 X11_PenHandle *handle = X11_FindPenByDeviceID(deviceid);
301 if (handle) {
302 SDL_RemovePenDevice(0, handle->pen);
303 SDL_free(handle);
304 }
305}
306
307void X11_InitPen(SDL_VideoDevice *_this)
308{
309 SDL_VideoData *data = _this->internal;
310
311 #define LOOKUP_PEN_ATOM(X) X11_XInternAtom(data->display, X, False)
312 data->atoms.pen_atom_device_product_id = LOOKUP_PEN_ATOM("Device Product ID");
313 data->atoms.pen_atom_wacom_serial_ids = LOOKUP_PEN_ATOM("Wacom Serial IDs");
314 data->atoms.pen_atom_wacom_tool_type = LOOKUP_PEN_ATOM("Wacom Tool Type");
315 data->atoms.pen_atom_abs_pressure = LOOKUP_PEN_ATOM("Abs Pressure");
316 data->atoms.pen_atom_abs_tilt_x = LOOKUP_PEN_ATOM("Abs Tilt X");
317 data->atoms.pen_atom_abs_tilt_y = LOOKUP_PEN_ATOM("Abs Tilt Y");
318 #undef LOOKUP_PEN_ATOM
319
320 // Do an initial check on devices. After this, we'll add/remove individual pens when XI_HierarchyChanged events alert us.
321 int num_device_info = 0;
322 XIDeviceInfo *device_info = X11_XIQueryDevice(data->display, XIAllDevices, &num_device_info);
323 if (device_info) {
324 for (int i = 0; i < num_device_info; i++) {
325 X11_MaybeAddPen(_this, &device_info[i]);
326 }
327 X11_XIFreeDeviceInfo(device_info);
328 }
329}
330
331static void X11_FreePenHandle(SDL_PenID instance_id, void *handle, void *userdata)
332{
333 SDL_free(handle);
334}
335
336void X11_QuitPen(SDL_VideoDevice *_this)
337{
338 SDL_RemoveAllPenDevices(X11_FreePenHandle, NULL);
339}
340
341static void X11_XInput2NormalizePenAxes(const X11_PenHandle *pen, float *coords)
342{
343 // Normalise axes
344 for (int axis = 0; axis < SDL_PEN_AXIS_COUNT; ++axis) {
345 const int valuator = pen->valuator_for_axis[axis];
346 if (valuator == SDL_X11_PEN_AXIS_VALUATOR_MISSING) {
347 continue;
348 }
349
350 float value = coords[axis];
351 const float min = pen->axis_min[axis];
352 const float max = pen->axis_max[axis];
353
354 if (axis == SDL_PEN_AXIS_SLIDER) {
355 value += pen->slider_bias;
356 }
357
358 // min ... 0 ... max
359 if (min < 0.0) {
360 // Normalise so that 0 remains 0.0
361 if (value < 0) {
362 value = value / (-min);
363 } else {
364 if (max == 0.0f) {
365 value = 0.0f;
366 } else {
367 value = value / max;
368 }
369 }
370 } else {
371 // 0 ... min ... max
372 // including 0.0 = min
373 if (max == 0.0f) {
374 value = 0.0f;
375 } else {
376 value = (value - min) / max;
377 }
378 }
379
380 switch (axis) {
381 case SDL_PEN_AXIS_XTILT:
382 case SDL_PEN_AXIS_YTILT:
383 //if (peninfo->info.max_tilt > 0.0f) {
384 // value *= peninfo->info.max_tilt; // normalize to physical max
385 //}
386 break;
387
388 case SDL_PEN_AXIS_ROTATION:
389 // normalised to -1..1, so let's convert to degrees
390 value *= 180.0f;
391 value += pen->rotation_bias;
392
393 // handle simple over/underflow
394 if (value >= 180.0f) {
395 value -= 360.0f;
396 } else if (value < -180.0f) {
397 value += 360.0f;
398 }
399 break;
400
401 default:
402 break;
403 }
404
405 coords[axis] = value;
406 }
407}
408
409void X11_PenAxesFromValuators(const X11_PenHandle *pen,
410 const double *input_values, const unsigned char *mask, int mask_len,
411 float axis_values[SDL_PEN_AXIS_COUNT])
412{
413 for (int i = 0; i < SDL_PEN_AXIS_COUNT; i++) {
414 const int valuator = pen->valuator_for_axis[i];
415 if ((valuator == SDL_X11_PEN_AXIS_VALUATOR_MISSING) || (valuator >= mask_len * 8) || !(XIMaskIsSet(mask, valuator))) {
416 axis_values[i] = 0.0f;
417 } else {
418 axis_values[i] = (float)input_values[valuator];
419 }
420 }
421 X11_XInput2NormalizePenAxes(pen, axis_values);
422}
423
424#else
425
426void X11_InitPen(SDL_VideoDevice *_this)
427{
428 (void) _this;
429}
430
431void X11_QuitPen(SDL_VideoDevice *_this)
432{
433 (void) _this;
434}
435
436#endif // SDL_VIDEO_DRIVER_X11_XINPUT2
437
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11pen.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11pen.h
new file mode 100644
index 0000000..de75181
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11pen.h
@@ -0,0 +1,72 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "../../SDL_internal.h"
22
23#ifndef SDL_x11pen_h_
24#define SDL_x11pen_h_
25
26// Pressure-sensitive pen support for X11.
27
28#include "SDL_x11video.h"
29#include "../../events/SDL_pen_c.h"
30
31// Prep pen support (never fails; pens simply won't be added if there's a problem).
32extern void X11_InitPen(SDL_VideoDevice *_this);
33
34// Clean up pen support.
35extern void X11_QuitPen(SDL_VideoDevice *_this);
36
37#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
38
39// Forward definition for SDL_x11video.h
40struct SDL_VideoData;
41
42#define SDL_X11_PEN_AXIS_VALUATOR_MISSING -1
43
44typedef struct X11_PenHandle
45{
46 SDL_PenID pen;
47 bool is_eraser;
48 int x11_deviceid;
49 int valuator_for_axis[SDL_PEN_AXIS_COUNT];
50 float slider_bias; // shift value to add to PEN_AXIS_SLIDER (before normalisation)
51 float rotation_bias; // rotation to add to PEN_AXIS_ROTATION (after normalisation)
52 float axis_min[SDL_PEN_AXIS_COUNT];
53 float axis_max[SDL_PEN_AXIS_COUNT];
54} X11_PenHandle;
55
56// Converts XINPUT2 valuators into pen axis information, including normalisation.
57extern void X11_PenAxesFromValuators(const X11_PenHandle *pen,
58 const double *input_values, const unsigned char *mask, int mask_len,
59 float axis_values[SDL_PEN_AXIS_COUNT]);
60
61// Add a pen (if this function's further checks validate it).
62extern X11_PenHandle *X11_MaybeAddPenByDeviceID(SDL_VideoDevice *_this, int deviceid);
63
64// Remove a pen. It's okay if deviceid is bogus or not a pen, we'll check it.
65extern void X11_RemovePenByDeviceID(int deviceid);
66
67// Map X11 device ID to pen ID.
68extern X11_PenHandle *X11_FindPenByDeviceID(int deviceid);
69
70#endif // SDL_VIDEO_DRIVER_X11_XINPUT2
71
72#endif // SDL_x11pen_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11settings.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11settings.c
new file mode 100644
index 0000000..7a7ae01
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11settings.c
@@ -0,0 +1,129 @@
1/*
2 Simple DirectMedia Layer
3 Copyright 2024 Igalia S.L.
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22#include "SDL_internal.h"
23
24#if defined(SDL_VIDEO_DRIVER_X11)
25
26#include "SDL_x11video.h"
27#include "SDL_x11settings.h"
28
29#define SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR "Gdk/WindowScalingFactor"
30#define SDL_XSETTINGS_XFT_DPI "Xft/DPI"
31
32static void X11_XsettingsNotify(const char *name, XSettingsAction action, XSettingsSetting *setting, void *data)
33{
34 SDL_VideoDevice *_this = data;
35 float scale_factor = 1.0;
36 int i;
37
38 if (SDL_strcmp(name, SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR) != 0 ||
39 SDL_strcmp(name, SDL_XSETTINGS_XFT_DPI) != 0) {
40 return;
41 }
42
43 if (setting->type != XSETTINGS_TYPE_INT) {
44 return;
45 }
46
47 switch (action) {
48 case XSETTINGS_ACTION_NEW:
49 SDL_FALLTHROUGH;
50 case XSETTINGS_ACTION_CHANGED:
51 scale_factor = setting->data.v_int;
52 if (SDL_strcmp(name, SDL_XSETTINGS_XFT_DPI) == 0) {
53 scale_factor = scale_factor / 1024.0f / 96.0f;
54 }
55 break;
56 case XSETTINGS_ACTION_DELETED:
57 scale_factor = 1.0;
58 break;
59 }
60
61 if (_this) {
62 for (i = 0; i < _this->num_displays; ++i) {
63 SDL_SetDisplayContentScale(_this->displays[i], scale_factor);
64 }
65 }
66}
67
68void X11_InitXsettings(SDL_VideoDevice *_this)
69{
70 SDL_VideoData *data = _this->internal;
71 SDLX11_SettingsData *xsettings_data = &data->xsettings_data;
72
73 xsettings_data->xsettings = xsettings_client_new(data->display,
74 DefaultScreen(data->display), X11_XsettingsNotify, NULL, _this);
75
76}
77
78void X11_QuitXsettings(SDL_VideoDevice *_this)
79{
80 SDL_VideoData *data = _this->internal;
81 SDLX11_SettingsData *xsettings_data = &data->xsettings_data;
82
83 if (xsettings_data->xsettings) {
84 xsettings_client_destroy(xsettings_data->xsettings);
85 xsettings_data->xsettings = NULL;
86 }
87}
88
89void X11_HandleXsettings(SDL_VideoDevice *_this, const XEvent *xevent)
90{
91 SDL_VideoData *data = _this->internal;
92 SDLX11_SettingsData *xsettings_data = &data->xsettings_data;
93
94 if (xsettings_data->xsettings) {
95 if (!xsettings_client_process_event(xsettings_data->xsettings, xevent)) {
96 xsettings_client_destroy(xsettings_data->xsettings);
97 xsettings_data->xsettings = NULL;
98 }
99 }
100}
101
102int X11_GetXsettingsIntKey(SDL_VideoDevice *_this, const char *key, int fallback_value) {
103 SDL_VideoData *data = _this->internal;
104 SDLX11_SettingsData *xsettings_data = &data->xsettings_data;
105 XSettingsSetting *setting = NULL;
106 int res = fallback_value;
107
108
109 if (xsettings_data->xsettings) {
110 if (xsettings_client_get_setting(xsettings_data->xsettings, key, &setting) != XSETTINGS_SUCCESS) {
111 goto no_key;
112 }
113
114 if (setting->type != XSETTINGS_TYPE_INT) {
115 goto no_key;
116 }
117
118 res = setting->data.v_int;
119 }
120
121no_key:
122 if (setting) {
123 xsettings_setting_free(setting);
124 }
125
126 return res;
127}
128
129#endif // SDL_VIDEO_DRIVER_X11
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11settings.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11settings.h
new file mode 100644
index 0000000..5b36884
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11settings.h
@@ -0,0 +1,39 @@
1/*
2 Simple DirectMedia Layer
3 Copyright 2024 Igalia S.L.
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22#include "SDL_internal.h"
23
24#ifndef SDL_x11settings_h_
25#define SDL_x11settings_h_
26
27#include <X11/Xlib.h>
28#include "xsettings-client.h"
29
30typedef struct X11_SettingsData {
31 XSettingsClient *xsettings;
32} SDLX11_SettingsData;
33
34extern void X11_InitXsettings(SDL_VideoDevice *_this);
35extern void X11_QuitXsettings(SDL_VideoDevice *_this);
36extern void X11_HandleXsettings(SDL_VideoDevice *_this, const XEvent *xevent);
37extern int X11_GetXsettingsIntKey(SDL_VideoDevice *_this, const char *key, int fallback_value);
38
39#endif // SDL_x11settings_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11shape.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11shape.c
new file mode 100644
index 0000000..e433598
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11shape.c
@@ -0,0 +1,111 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_DRIVER_X11
24
25#include "SDL_x11video.h"
26#include "SDL_x11shape.h"
27
28
29#ifdef SDL_VIDEO_DRIVER_X11_XSHAPE
30static Uint8 *GenerateShapeMask(SDL_Surface *shape)
31{
32 int x, y;
33 const size_t ppb = 8;
34 const size_t bytes_per_scanline = (shape->w + (ppb - 1)) / ppb;
35 const Uint8 *a;
36 Uint8 *mask;
37 Uint8 *mask_scanline;
38 Uint8 mask_value;
39
40 mask = (Uint8 *)SDL_calloc(1, shape->h * bytes_per_scanline);
41 if (mask) {
42 for (y = 0; y < shape->h; y++) {
43 a = (const Uint8 *)shape->pixels + y * shape->pitch;
44 mask_scanline = mask + y * bytes_per_scanline;
45 for (x = 0; x < shape->w; x++) {
46 mask_value = (*a == SDL_ALPHA_TRANSPARENT) ? 0 : 1;
47 mask_scanline[x / ppb] |= mask_value << (x % ppb);
48 a += 4;
49 }
50 }
51 }
52 return mask;
53}
54#endif // SDL_VIDEO_DRIVER_X11_XSHAPE
55
56bool X11_UpdateWindowShape(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *shape)
57{
58 bool result = false;
59
60#ifdef SDL_VIDEO_DRIVER_X11_XSHAPE
61 SDL_WindowData *windowdata = window->internal;
62
63 // Generate a set of spans for the region
64 if (shape) {
65 SDL_Surface *stretched = NULL;
66 Uint8 *mask;
67 Pixmap pixmap;
68
69 if (shape->w != window->w || shape->h != window->h) {
70 stretched = SDL_CreateSurface(window->w, window->h, SDL_PIXELFORMAT_ARGB32);
71 if (!stretched) {
72 return false;
73 }
74 if (!SDL_StretchSurface(shape, NULL, stretched, NULL, SDL_SCALEMODE_LINEAR)) {
75 SDL_DestroySurface(stretched);
76 return false;
77 }
78 shape = stretched;
79 }
80
81 mask = GenerateShapeMask(shape);
82 if (mask) {
83 pixmap = X11_XCreateBitmapFromData(windowdata->videodata->display, windowdata->xwindow, (const char *)mask, shape->w, shape->h);
84 X11_XShapeCombineMask(windowdata->videodata->display, windowdata->xwindow, ShapeInput, 0, 0, pixmap, ShapeSet);
85 SDL_free(mask);
86
87 result = true;
88 }
89
90 if (stretched) {
91 SDL_DestroySurface(stretched);
92 }
93 } else {
94 Region region = X11_XCreateRegion();
95 XRectangle rect;
96
97 rect.x = 0;
98 rect.y = 0;
99 rect.width = window->w;
100 rect.height = window->h;
101 X11_XUnionRectWithRegion(&rect, region, region);
102 X11_XShapeCombineRegion(windowdata->videodata->display, windowdata->xwindow, ShapeInput, 0, 0, region, ShapeSet);
103 X11_XDestroyRegion(region);
104 result = true;
105 }
106#endif // SDL_VIDEO_DRIVER_X11_XSHAPE
107
108 return result;
109}
110
111#endif // SDL_VIDEO_DRIVER_X11
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11shape.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11shape.h
new file mode 100644
index 0000000..d47d103
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11shape.h
@@ -0,0 +1,28 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifndef SDL_x11shape_h_
24#define SDL_x11shape_h_
25
26extern bool X11_UpdateWindowShape(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *shape);
27
28#endif // SDL_x11shape_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11sym.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11sym.h
new file mode 100644
index 0000000..68d70cd
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11sym.h
@@ -0,0 +1,354 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22/* *INDENT-OFF* */ // clang-format off
23
24#ifndef SDL_X11_MODULE
25#define SDL_X11_MODULE(modname)
26#endif
27
28#ifndef SDL_X11_SYM
29#define SDL_X11_SYM(rc,fn,params,args,ret)
30#endif
31
32SDL_X11_MODULE(BASEXLIB)
33SDL_X11_SYM(XSizeHints*,XAllocSizeHints,(void),(),return)
34SDL_X11_SYM(XWMHints*,XAllocWMHints,(void),(),return)
35SDL_X11_SYM(XClassHint*,XAllocClassHint,(void),(),return)
36SDL_X11_SYM(int,XChangePointerControl,(Display* a,Bool b,Bool c,int d,int e,int f),(a,b,c,d,e,f),return)
37SDL_X11_SYM(int,XChangeProperty,(Display* a,Window b,Atom c,Atom d,int e,int f,_Xconst unsigned char* g,int h),(a,b,c,d,e,f,g,h),return)
38SDL_X11_SYM(Bool,XCheckIfEvent,(Display* a,XEvent *b,Bool (*c)(Display*,XEvent*,XPointer),XPointer d),(a,b,c,d),return)
39SDL_X11_SYM(int,XClearWindow,(Display* a,Window b),(a,b),return)
40SDL_X11_SYM(int,XCloseDisplay,(Display* a),(a),return)
41SDL_X11_SYM(int,XConvertSelection,(Display* a,Atom b,Atom c,Atom d,Window e,Time f),(a,b,c,d,e,f),return)
42SDL_X11_SYM(Pixmap,XCreateBitmapFromData,(Display *dpy,Drawable d,_Xconst char *data,unsigned int width,unsigned int height),(dpy,d,data,width,height),return)
43SDL_X11_SYM(Colormap,XCreateColormap,(Display* a,Window b,Visual* c,int d),(a,b,c,d),return)
44SDL_X11_SYM(Cursor,XCreatePixmapCursor,(Display* a,Pixmap b,Pixmap c,XColor* d,XColor* e,unsigned int f,unsigned int g),(a,b,c,d,e,f,g),return)
45SDL_X11_SYM(Cursor,XCreateFontCursor,(Display* a,unsigned int b),(a,b),return)
46SDL_X11_SYM(XFontSet,XCreateFontSet,(Display* a, _Xconst char* b, char*** c, int* d, char** e),(a,b,c,d,e),return)
47SDL_X11_SYM(GC,XCreateGC,(Display* a,Drawable b,unsigned long c,XGCValues* d),(a,b,c,d),return)
48SDL_X11_SYM(XImage*,XCreateImage,(Display* a,Visual* b,unsigned int c,int d,int e,char* f,unsigned int g,unsigned int h,int i,int j),(a,b,c,d,e,f,g,h,i,j),return)
49SDL_X11_SYM(Window,XCreateWindow,(Display* a,Window b,int c,int d,unsigned int e,unsigned int f,unsigned int g,int h,unsigned int i,Visual* j,unsigned long k,XSetWindowAttributes* l),(a,b,c,d,e,f,g,h,i,j,k,l),return)
50SDL_X11_SYM(int,XDefineCursor,(Display* a,Window b,Cursor c),(a,b,c),return)
51SDL_X11_SYM(int,XDeleteProperty,(Display* a,Window b,Atom c),(a,b,c),return)
52SDL_X11_SYM(int,XDestroyWindow,(Display* a,Window b),(a,b),return)
53SDL_X11_SYM(int,XDisplayKeycodes,(Display* a,int* b,int* c),(a,b,c),return)
54SDL_X11_SYM(int,XDrawRectangle,(Display* a,Drawable b,GC c,int d,int e,unsigned int f,unsigned int g),(a,b,c,d,e,f,g),return)
55SDL_X11_SYM(char*,XDisplayName,(_Xconst char* a),(a),return)
56SDL_X11_SYM(int,XDrawString,(Display* a,Drawable b,GC c,int d,int e,_Xconst char* f,int g),(a,b,c,d,e,f,g),return)
57SDL_X11_SYM(int,XEventsQueued,(Display* a,int b),(a,b),return)
58SDL_X11_SYM(int,XFillRectangle,(Display* a,Drawable b,GC c,int d,int e,unsigned int f,unsigned int g),(a,b,c,d,e,f,g),return)
59SDL_X11_SYM(Bool,XFilterEvent,(XEvent *event,Window w),(event,w),return)
60SDL_X11_SYM(int,XFlush,(Display* a),(a),return)
61SDL_X11_SYM(int,XFree,(void*a),(a),return)
62SDL_X11_SYM(int,XFreeCursor,(Display* a,Cursor b),(a,b),return)
63SDL_X11_SYM(void,XFreeFontSet,(Display* a, XFontSet b),(a,b),)
64SDL_X11_SYM(int,XFreeGC,(Display* a,GC b),(a,b),return)
65SDL_X11_SYM(int,XFreeFont,(Display* a, XFontStruct* b),(a,b),return)
66SDL_X11_SYM(int,XFreeModifiermap,(XModifierKeymap* a),(a),return)
67SDL_X11_SYM(int,XFreePixmap,(Display* a,Pixmap b),(a,b),return)
68SDL_X11_SYM(void,XFreeStringList,(char** a),(a),)
69SDL_X11_SYM(char*,XGetAtomName,(Display *a,Atom b),(a,b),return)
70SDL_X11_SYM(int,XGetInputFocus,(Display *a,Window *b,int *c),(a,b,c),return)
71SDL_X11_SYM(int,XGetErrorDatabaseText,(Display* a,_Xconst char* b,_Xconst char* c,_Xconst char* d,char* e,int f),(a,b,c,d,e,f),return)
72SDL_X11_SYM(XModifierKeymap*,XGetModifierMapping,(Display* a),(a),return)
73SDL_X11_SYM(int,XGetPointerControl,(Display* a,int* b,int* c,int* d),(a,b,c,d),return)
74SDL_X11_SYM(Window,XGetSelectionOwner,(Display* a,Atom b),(a,b),return)
75SDL_X11_SYM(XVisualInfo*,XGetVisualInfo,(Display* a,long b,XVisualInfo* c,int* d),(a,b,c,d),return)
76SDL_X11_SYM(Status,XGetWindowAttributes,(Display* a,Window b,XWindowAttributes* c),(a,b,c),return)
77SDL_X11_SYM(int,XGetWindowProperty,(Display* a,Window b,Atom c,long d,long e,Bool f,Atom g,Atom* h,int* i,unsigned long* j,unsigned long *k,unsigned char **l),(a,b,c,d,e,f,g,h,i,j,k,l),return)
78SDL_X11_SYM(XWMHints*,XGetWMHints,(Display* a,Window b),(a,b),return)
79SDL_X11_SYM(Status,XGetWMNormalHints,(Display *a,Window b, XSizeHints *c, long *d),(a,b,c,d),return)
80SDL_X11_SYM(int,XIfEvent,(Display* a,XEvent *b,Bool (*c)(Display*,XEvent*,XPointer),XPointer d),(a,b,c,d),return)
81SDL_X11_SYM(int,XGrabKeyboard,(Display* a,Window b,Bool c,int d,int e,Time f),(a,b,c,d,e,f),return)
82SDL_X11_SYM(int,XGrabPointer,(Display* a,Window b,Bool c,unsigned int d,int e,int f,Window g,Cursor h,Time i),(a,b,c,d,e,f,g,h,i),return)
83SDL_X11_SYM(int,XGrabServer,(Display* a),(a),return)
84SDL_X11_SYM(Status,XIconifyWindow,(Display* a,Window b,int c),(a,b,c),return)
85SDL_X11_SYM(KeyCode,XKeysymToKeycode,(Display* a,KeySym b),(a,b),return)
86SDL_X11_SYM(char*,XKeysymToString,(KeySym a),(a),return)
87SDL_X11_SYM(int,XInstallColormap,(Display* a,Colormap b),(a,b),return)
88SDL_X11_SYM(Atom,XInternAtom,(Display* a,_Xconst char* b,Bool c),(a,b,c),return)
89SDL_X11_SYM(XPixmapFormatValues*,XListPixmapFormats,(Display* a,int* b),(a,b),return)
90SDL_X11_SYM(XFontStruct*,XLoadQueryFont,(Display* a,_Xconst char* b),(a,b),return)
91SDL_X11_SYM(KeySym,XLookupKeysym,(XKeyEvent* a,int b),(a,b),return)
92SDL_X11_SYM(int,XLookupString,(XKeyEvent* a,char* b,int c,KeySym* d,XComposeStatus* e),(a,b,c,d,e),return)
93SDL_X11_SYM(int,XMapRaised,(Display* a,Window b),(a,b),return)
94SDL_X11_SYM(Status,XMatchVisualInfo,(Display* a,int b,int c,int d,XVisualInfo* e),(a,b,c,d,e),return)
95SDL_X11_SYM(int,XMissingExtension,(Display* a,_Xconst char* b),(a,b),return)
96SDL_X11_SYM(int,XMoveWindow,(Display* a,Window b,int c,int d),(a,b,c,d),return)
97SDL_X11_SYM(Display*,XOpenDisplay,(_Xconst char* a),(a),return)
98SDL_X11_SYM(Status,XInitThreads,(void),(),return)
99SDL_X11_SYM(int,XPeekEvent,(Display* a,XEvent* b),(a,b),return)
100SDL_X11_SYM(int,XPending,(Display* a),(a),return)
101SDL_X11_SYM(int,XPutImage,(Display* a,Drawable b,GC c,XImage* d,int e,int f,int g,int h,unsigned int i,unsigned int j),(a,b,c,d,e,f,g,h,i,j),return)
102SDL_X11_SYM(int,XQueryKeymap,(Display* a,char b[32]),(a,b),return)
103SDL_X11_SYM(Bool,XQueryPointer,(Display* a,Window b,Window* c,Window* d,int* e,int* f,int* g,int* h,unsigned int* i),(a,b,c,d,e,f,g,h,i),return)
104SDL_X11_SYM(int,XRaiseWindow,(Display* a,Window b),(a,b),return)
105SDL_X11_SYM(int,XReparentWindow,(Display* a,Window b,Window c,int d,int e),(a,b,c,d,e),return)
106SDL_X11_SYM(int,XResetScreenSaver,(Display* a),(a),return)
107SDL_X11_SYM(int,XResizeWindow,(Display* a,Window b,unsigned int c,unsigned int d),(a,b,c,d),return)
108SDL_X11_SYM(int,XScreenNumberOfScreen,(Screen* a),(a),return)
109SDL_X11_SYM(int,XSelectInput,(Display* a,Window b,long c),(a,b,c),return)
110SDL_X11_SYM(Status,XSendEvent,(Display* a,Window b,Bool c,long d,XEvent* e),(a,b,c,d,e),return)
111SDL_X11_SYM(XErrorHandler,XSetErrorHandler,(XErrorHandler a),(a),return)
112SDL_X11_SYM(int,XSetForeground,(Display* a,GC b,unsigned long c),(a,b,c),return)
113SDL_X11_SYM(XIOErrorHandler,XSetIOErrorHandler,(XIOErrorHandler a),(a),return)
114SDL_X11_SYM(int,XSetInputFocus,(Display *a,Window b,int c,Time d),(a,b,c,d),return)
115SDL_X11_SYM(int,XSetSelectionOwner,(Display* a,Atom b,Window c,Time d),(a,b,c,d),return)
116SDL_X11_SYM(int,XSetTransientForHint,(Display* a,Window b,Window c),(a,b,c),return)
117SDL_X11_SYM(void,XSetTextProperty,(Display* a,Window b,XTextProperty* c,Atom d),(a,b,c,d),)
118SDL_X11_SYM(int,XSetWindowBackground,(Display* a,Window b,unsigned long c),(a,b,c),return)
119SDL_X11_SYM(void,XSetWMHints,(Display* a,Window b,XWMHints* c),(a,b,c),)
120SDL_X11_SYM(void,XSetWMNormalHints,(Display* a,Window b,XSizeHints* c),(a,b,c),)
121SDL_X11_SYM(void,XSetWMProperties,(Display* a,Window b,XTextProperty* c,XTextProperty* d,char** e,int f,XSizeHints* g,XWMHints* h,XClassHint* i),(a,b,c,d,e,f,g,h,i),)
122SDL_X11_SYM(Status,XSetWMProtocols,(Display* a,Window b,Atom* c,int d),(a,b,c,d),return)
123SDL_X11_SYM(int,XStoreColors,(Display* a,Colormap b,XColor* c,int d),(a,b,c,d),return)
124SDL_X11_SYM(int,XStoreName,(Display* a,Window b,_Xconst char* c),(a,b,c),return)
125SDL_X11_SYM(Status,XStringListToTextProperty,(char** a,int b,XTextProperty* c),(a,b,c),return)
126SDL_X11_SYM(int,XSync,(Display* a,Bool b),(a,b),return)
127SDL_X11_SYM(int,XTextExtents,(XFontStruct* a,_Xconst char* b,int c,int* d,int* e,int* f,XCharStruct* g),(a,b,c,d,e,f,g),return)
128SDL_X11_SYM(Bool,XTranslateCoordinates,(Display *a,Window b,Window c,int d,int e,int* f,int* g,Window* h),(a,b,c,d,e,f,g,h),return)
129SDL_X11_SYM(int,XUndefineCursor,(Display* a,Window b),(a,b),return)
130SDL_X11_SYM(int,XUngrabKeyboard,(Display* a,Time b),(a,b),return)
131SDL_X11_SYM(int,XUngrabPointer,(Display* a,Time b),(a,b),return)
132SDL_X11_SYM(int,XUngrabServer,(Display* a),(a),return)
133SDL_X11_SYM(int,XUninstallColormap,(Display* a,Colormap b),(a,b),return)
134SDL_X11_SYM(int,XUnloadFont,(Display* a,Font b),(a,b),return)
135SDL_X11_SYM(int,XWarpPointer,(Display* a,Window b,Window c,int d,int e,unsigned int f,unsigned int g,int h,int i),(a,b,c,d,e,f,g,h,i),return)
136SDL_X11_SYM(int,XWindowEvent,(Display* a,Window b,long c,XEvent* d),(a,b,c,d),return)
137SDL_X11_SYM(Status,XWithdrawWindow,(Display* a,Window b,int c),(a,b,c),return)
138SDL_X11_SYM(VisualID,XVisualIDFromVisual,(Visual* a),(a),return)
139SDL_X11_SYM(char*,XGetDefault,(Display* a,_Xconst char* b, _Xconst char* c),(a,b,c),return)
140SDL_X11_SYM(Bool,XQueryExtension,(Display* a,_Xconst char* b,int* c,int* d,int* e),(a,b,c,d,e),return)
141SDL_X11_SYM(char *,XDisplayString,(Display* a),(a),return)
142SDL_X11_SYM(int,XGetErrorText,(Display* a,int b,char* c,int d),(a,b,c,d),return)
143SDL_X11_SYM(void,_XEatData,(Display* a,unsigned long b),(a,b),)
144SDL_X11_SYM(void,_XFlush,(Display* a),(a),)
145SDL_X11_SYM(void,_XFlushGCCache,(Display* a,GC b),(a,b),)
146SDL_X11_SYM(int,_XRead,(Display* a,char* b,long c),(a,b,c),return)
147SDL_X11_SYM(void,_XReadPad,(Display* a,char* b,long c),(a,b,c),)
148SDL_X11_SYM(void,_XSend,(Display* a,_Xconst char* b,long c),(a,b,c),)
149SDL_X11_SYM(Status,_XReply,(Display* a,xReply* b,int c,Bool d),(a,b,c,d),return)
150SDL_X11_SYM(unsigned long,_XSetLastRequestRead,(Display* a,xGenericReply* b),(a,b),return)
151SDL_X11_SYM(SDL_X11_XSynchronizeRetType,XSynchronize,(Display* a,Bool b),(a,b),return)
152SDL_X11_SYM(SDL_X11_XESetWireToEventRetType,XESetWireToEvent,(Display* a,int b,SDL_X11_XESetWireToEventRetType c),(a,b,c),return)
153SDL_X11_SYM(SDL_X11_XESetEventToWireRetType,XESetEventToWire,(Display* a,int b,SDL_X11_XESetEventToWireRetType c),(a,b,c),return)
154SDL_X11_SYM(void,XRefreshKeyboardMapping,(XMappingEvent *a),(a),)
155SDL_X11_SYM(int,XQueryTree,(Display* a,Window b,Window* c,Window* d,Window** e,unsigned int* f),(a,b,c,d,e,f),return)
156SDL_X11_SYM(Bool,XSupportsLocale,(void),(),return)
157SDL_X11_SYM(Status,XmbTextListToTextProperty,(Display* a,char** b,int c,XICCEncodingStyle d,XTextProperty* e),(a,b,c,d,e),return)
158SDL_X11_SYM(Region,XCreateRegion,(void),(),return)
159SDL_X11_SYM(int,XUnionRectWithRegion,(XRectangle *a, Region b, Region c),(a,b,c), return)
160SDL_X11_SYM(void,XDestroyRegion,(Region),(a),)
161SDL_X11_SYM(void,XrmInitialize,(void),(),)
162SDL_X11_SYM(char*,XResourceManagerString,(Display *display),(display),)
163SDL_X11_SYM(XrmDatabase,XrmGetStringDatabase,(char *data),(data),)
164SDL_X11_SYM(void,XrmDestroyDatabase,(XrmDatabase db),(db),)
165SDL_X11_SYM(Bool,XrmGetResource,(XrmDatabase db, char* str_name, char* str_class, char **str_type_return, XrmValue *),(db, str_name, str_class,str_type_return,value_return),)
166
167#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
168SDL_X11_MODULE(XFIXES)
169SDL_X11_SYM(PointerBarrier, XFixesCreatePointerBarrier, (Display* a, Window b, int c, int d, int e, int f, int g, int h, int *i),(a,b,c,d,e,f,g,h,i),return)
170SDL_X11_SYM(void, XFixesDestroyPointerBarrier, (Display* a, PointerBarrier b), (a,b),)
171SDL_X11_SYM(int, XIBarrierReleasePointer,(Display* a, int b, PointerBarrier c, BarrierEventID d), (a,b,c,d), return) // this is actually Xinput2
172SDL_X11_SYM(Status, XFixesQueryVersion,(Display* a, int* b, int* c), (a,b,c), return)
173SDL_X11_SYM(Status, XFixesSelectSelectionInput, (Display* a, Window b, Atom c, unsigned long d), (a,b,c,d), return)
174#endif
175
176#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
177SDL_X11_MODULE(XSYNC)
178SDL_X11_SYM(Status, XSyncQueryExtension, (Display* a, int* b, int* c), (a, b, c), return)
179SDL_X11_SYM(Status, XSyncInitialize, (Display* a, int* b, int* c), (a, b, c), return)
180SDL_X11_SYM(XSyncCounter, XSyncCreateCounter, (Display* a, XSyncValue b), (a, b), return)
181SDL_X11_SYM(Status, XSyncDestroyCounter, (Display* a, XSyncCounter b), (a, b), return)
182SDL_X11_SYM(Status, XSyncSetCounter, (Display* a, XSyncCounter b, XSyncValue c), (a, b, c), return)
183#endif
184
185#ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
186SDL_X11_SYM(Bool,XGetEventData,(Display* a,XGenericEventCookie* b),(a,b),return)
187SDL_X11_SYM(void,XFreeEventData,(Display* a,XGenericEventCookie* b),(a,b),)
188#endif
189
190#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
191SDL_X11_SYM(Bool,XkbQueryExtension,(Display* a,int * b,int * c,int * d,int * e, int *f),(a,b,c,d,e,f),return)
192#if NeedWidePrototypes
193SDL_X11_SYM(Bool,XkbLookupKeySym,(Display* a, unsigned int b, unsigned int c, unsigned int* d, KeySym* e),(a,b,c,d,e),return)
194#else
195SDL_X11_SYM(Bool,XkbLookupKeySym,(Display* a, KeyCode b, unsigned int c, unsigned int* d, KeySym* e),(a,b,c,d,e),return)
196#endif
197SDL_X11_SYM(Status,XkbGetState,(Display* a,unsigned int b,XkbStatePtr c),(a,b,c),return)
198SDL_X11_SYM(Status,XkbGetUpdatedMap,(Display* a,unsigned int b,XkbDescPtr c),(a,b,c),return)
199SDL_X11_SYM(XkbDescPtr,XkbGetMap,(Display* a,unsigned int b,unsigned int c),(a,b,c),return)
200SDL_X11_SYM(void,XkbFreeClientMap,(XkbDescPtr a,unsigned int b, Bool c),(a,b,c),)
201SDL_X11_SYM(void,XkbFreeKeyboard,(XkbDescPtr a,unsigned int b, Bool c),(a,b,c),)
202SDL_X11_SYM(Bool,XkbSetDetectableAutoRepeat,(Display* a, Bool b, Bool* c),(a,b,c),return)
203#endif
204
205// XKeycodeToKeysym is a deprecated function
206#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
207#pragma GCC diagnostic push
208#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
209#endif
210#if NeedWidePrototypes
211SDL_X11_SYM(KeySym,XKeycodeToKeysym,(Display* a,unsigned int b,int c),(a,b,c),return)
212#else
213SDL_X11_SYM(KeySym,XKeycodeToKeysym,(Display* a,KeyCode b,int c),(a,b,c),return)
214#endif
215#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
216#pragma GCC diagnostic pop
217#endif
218
219#ifdef X_HAVE_UTF8_STRING
220SDL_X11_MODULE(UTF8)
221SDL_X11_SYM(int,Xutf8TextListToTextProperty,(Display* a,char** b,int c,XICCEncodingStyle d,XTextProperty* e),(a,b,c,d,e),return)
222SDL_X11_SYM(int,Xutf8LookupString,(XIC a,XKeyPressedEvent* b,char* c,int d,KeySym* e,Status* f),(a,b,c,d,e,f),return)
223// SDL_X11_SYM(XIC,XCreateIC,(XIM, ...),return) !!! ARGH!
224SDL_X11_SYM(void,XDestroyIC,(XIC a),(a),)
225/* SDL_X11_SYM(char*,XGetICValues,(XIC, ...),return) !!! ARGH! */
226/* SDL_X11_SYM(char*,XSetICValues,(XIC, ...),return) !!! ARGH! */
227/* SDL_X11_SYM(XVaNestedList,XVaCreateNestedList,(int, ...),return) !!! ARGH! */
228SDL_X11_SYM(void,XSetICFocus,(XIC a),(a),)
229SDL_X11_SYM(void,XUnsetICFocus,(XIC a),(a),)
230SDL_X11_SYM(XIM,XOpenIM,(Display* a,struct _XrmHashBucketRec* b,char* c,char* d),(a,b,c,d),return)
231SDL_X11_SYM(Status,XCloseIM,(XIM a),(a),return)
232SDL_X11_SYM(void,Xutf8DrawString,(Display *a, Drawable b, XFontSet c, GC d, int e, int f, _Xconst char *g, int h),(a,b,c,d,e,f,g,h),)
233SDL_X11_SYM(int,Xutf8TextExtents,(XFontSet a, _Xconst char* b, int c, XRectangle* d, XRectangle* e),(a,b,c,d,e),return)
234SDL_X11_SYM(char*,XSetLocaleModifiers,(const char *a),(a),return)
235SDL_X11_SYM(char*,Xutf8ResetIC,(XIC a),(a),return)
236#endif
237
238#ifndef NO_SHARED_MEMORY
239SDL_X11_MODULE(SHM)
240SDL_X11_SYM(Status,XShmAttach,(Display* a,XShmSegmentInfo* b),(a,b),return)
241SDL_X11_SYM(Status,XShmDetach,(Display* a,XShmSegmentInfo* b),(a,b),return)
242SDL_X11_SYM(Status,XShmPutImage,(Display* a,Drawable b,GC c,XImage* d,int e,int f,int g,int h,unsigned int i,unsigned int j,Bool k),(a,b,c,d,e,f,g,h,i,j,k),return)
243SDL_X11_SYM(XImage*,XShmCreateImage,(Display* a,Visual* b,unsigned int c,int d,char* e,XShmSegmentInfo* f,unsigned int g,unsigned int h),(a,b,c,d,e,f,g,h),return)
244SDL_X11_SYM(Pixmap,XShmCreatePixmap,(Display *a,Drawable b,char* c,XShmSegmentInfo* d, unsigned int e, unsigned int f, unsigned int g),(a,b,c,d,e,f,g),return)
245SDL_X11_SYM(Bool,XShmQueryExtension,(Display* a),(a),return)
246#endif
247
248/*
249 * Not required...these only exist in code in headers on some 64-bit platforms,
250 * and are removed via macros elsewhere, so it's safe for them to be missing.
251 */
252#ifdef LONG64
253SDL_X11_MODULE(IO_32BIT)
254SDL_X11_SYM(int,_XData32,(Display *dpy,register _Xconst long *data,unsigned len),(dpy,data,len),return)
255SDL_X11_SYM(void,_XRead32,(Display *dpy,register long *data,long len),(dpy,data,len),)
256#endif
257
258/*
259 * These only show up on some variants of Unix.
260 */
261#ifdef SDL_PLATFORM_OSF
262SDL_X11_MODULE(OSF_ENTRY_POINTS)
263SDL_X11_SYM(void,_SmtBufferOverflow,(Display *dpy,register smtDisplayPtr p),(dpy,p),)
264SDL_X11_SYM(void,_SmtIpError,(Display *dpy,register smtDisplayPtr p,int i),(dpy,p,i),)
265SDL_X11_SYM(int,ipAllocateData,(ChannelPtr a,IPCard b,IPDataPtr * c),(a,b,c),return)
266SDL_X11_SYM(int,ipUnallocateAndSendData,(ChannelPtr a,IPCard b),(a,b),return)
267#endif
268
269// XCursor support
270#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR
271SDL_X11_MODULE(XCURSOR)
272SDL_X11_SYM(XcursorImage*,XcursorImageCreate,(int a,int b),(a,b),return)
273SDL_X11_SYM(void,XcursorImageDestroy,(XcursorImage *a),(a),)
274SDL_X11_SYM(Cursor,XcursorImageLoadCursor,(Display *a,const XcursorImage *b),(a,b),return)
275SDL_X11_SYM(Cursor,XcursorLibraryLoadCursor,(Display *a, const char *b),(a,b),return)
276#endif
277
278// Xdbe support
279#ifdef SDL_VIDEO_DRIVER_X11_XDBE
280SDL_X11_MODULE(XDBE)
281SDL_X11_SYM(Status,XdbeQueryExtension,(Display *dpy,int *major_version_return,int *minor_version_return),(dpy,major_version_return,minor_version_return),return)
282SDL_X11_SYM(XdbeBackBuffer,XdbeAllocateBackBufferName,(Display *dpy,Window window,XdbeSwapAction swap_action),(dpy,window,swap_action),return)
283SDL_X11_SYM(Status,XdbeDeallocateBackBufferName,(Display *dpy,XdbeBackBuffer buffer),(dpy,buffer),return)
284SDL_X11_SYM(Status,XdbeSwapBuffers,(Display *dpy,XdbeSwapInfo *swap_info,int num_windows),(dpy,swap_info,num_windows),return)
285SDL_X11_SYM(Status,XdbeBeginIdiom,(Display *dpy),(dpy),return)
286SDL_X11_SYM(Status,XdbeEndIdiom,(Display *dpy),(dpy),return)
287SDL_X11_SYM(XdbeScreenVisualInfo*,XdbeGetVisualInfo,(Display *dpy,Drawable *screen_specifiers,int *num_screens),(dpy,screen_specifiers,num_screens),return)
288SDL_X11_SYM(void,XdbeFreeVisualInfo,(XdbeScreenVisualInfo *visual_info),(visual_info),)
289SDL_X11_SYM(XdbeBackBufferAttributes*,XdbeGetBackBufferAttributes,(Display *dpy,XdbeBackBuffer buffer),(dpy,buffer),return)
290#endif
291
292// XInput2 support for multiple mice, tablets, etc.
293#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
294SDL_X11_MODULE(XINPUT2)
295SDL_X11_SYM(XIDeviceInfo*,XIQueryDevice,(Display *a,int b,int *c),(a,b,c),return)
296SDL_X11_SYM(void,XIFreeDeviceInfo,(XIDeviceInfo *a),(a),)
297SDL_X11_SYM(int,XISelectEvents,(Display *a,Window b,XIEventMask *c,int d),(a,b,c,d),return)
298SDL_X11_SYM(int,XIGrabTouchBegin,(Display *a,int b,Window c,int d,XIEventMask *e,int f,XIGrabModifiers *g),(a,b,c,d,e,f,g),return)
299SDL_X11_SYM(int,XIUngrabTouchBegin, (Display *a,int b,Window c, int d,XIGrabModifiers *e),(a, b, c, d, e),return)
300SDL_X11_SYM(Status,XIQueryVersion,(Display *a,int *b,int *c),(a,b,c),return)
301SDL_X11_SYM(XIEventMask*,XIGetSelectedEvents,(Display *a,Window b,int *c),(a,b,c),return)
302SDL_X11_SYM(Bool,XIGetClientPointer,(Display *a,Window b,int *c),(a,b,c),return)
303SDL_X11_SYM(Bool,XIWarpPointer,(Display *a,int b,Window c,Window d,double e,double f,int g,int h,double i,double j),(a,b,c,d,e,f,g,h,i,j),return)
304SDL_X11_SYM(Status,XIGetProperty,(Display *a,int b,Atom c,long d,long e,Bool f, Atom g, Atom *h, int *i, unsigned long *j, unsigned long *k, unsigned char **l),(a,b,c,d,e,f,g,h,i,j,k,l),return)
305#endif
306
307// XRandR support
308#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
309SDL_X11_MODULE(XRANDR)
310SDL_X11_SYM(Status,XRRQueryVersion,(Display *dpy,int *major_versionp,int *minor_versionp),(dpy,major_versionp,minor_versionp),return)
311SDL_X11_SYM(Bool,XRRQueryExtension,(Display *dpy,int *event_base_return,int *error_base_return),(dpy,event_base_return,error_base_return),return)
312SDL_X11_SYM(XRRScreenConfiguration *,XRRGetScreenInfo,(Display *dpy,Drawable draw),(dpy,draw),return)
313SDL_X11_SYM(SizeID,XRRConfigCurrentConfiguration,(XRRScreenConfiguration *config,Rotation *rotation),(config,rotation),return)
314SDL_X11_SYM(short,XRRConfigCurrentRate,(XRRScreenConfiguration *config),(config),return)
315SDL_X11_SYM(short *,XRRConfigRates,(XRRScreenConfiguration *config,int sizeID,int *nrates),(config,sizeID,nrates),return)
316SDL_X11_SYM(XRRScreenSize *,XRRConfigSizes,(XRRScreenConfiguration *config,int *nsizes),(config,nsizes),return)
317SDL_X11_SYM(Status,XRRSetScreenConfigAndRate,(Display *dpy,XRRScreenConfiguration *config,Drawable draw,int size_index,Rotation rotation,short rate,Time timestamp),(dpy,config,draw,size_index,rotation,rate,timestamp),return)
318SDL_X11_SYM(void,XRRFreeScreenConfigInfo,(XRRScreenConfiguration *config),(config),)
319SDL_X11_SYM(void,XRRSetScreenSize,(Display *dpy, Window window,int width, int height,int mmWidth, int mmHeight),(dpy,window,width,height,mmWidth,mmHeight),)
320SDL_X11_SYM(Status,XRRGetScreenSizeRange,(Display *dpy, Window window,int *minWidth, int *minHeight, int *maxWidth, int *maxHeight),(dpy,window,minWidth,minHeight,maxWidth,maxHeight),return)
321SDL_X11_SYM(XRRScreenResources *,XRRGetScreenResources,(Display *dpy, Window window),(dpy, window),return)
322SDL_X11_SYM(XRRScreenResources *,XRRGetScreenResourcesCurrent,(Display *dpy, Window window),(dpy, window),return)
323SDL_X11_SYM(void,XRRFreeScreenResources,(XRRScreenResources *resources),(resources),)
324SDL_X11_SYM(XRROutputInfo *,XRRGetOutputInfo,(Display *dpy, XRRScreenResources *resources, RROutput output),(dpy,resources,output),return)
325SDL_X11_SYM(void,XRRFreeOutputInfo,(XRROutputInfo *outputInfo),(outputInfo),)
326SDL_X11_SYM(XRRCrtcInfo *,XRRGetCrtcInfo,(Display *dpy, XRRScreenResources *resources, RRCrtc crtc),(dpy,resources,crtc),return)
327SDL_X11_SYM(void,XRRFreeCrtcInfo,(XRRCrtcInfo *crtcInfo),(crtcInfo),)
328SDL_X11_SYM(Status,XRRSetCrtcConfig,(Display *dpy, XRRScreenResources *resources, RRCrtc crtc, Time timestamp, int x, int y, RRMode mode, Rotation rotation, RROutput *outputs, int noutputs),(dpy,resources,crtc,timestamp,x,y,mode,rotation,outputs,noutputs),return)
329SDL_X11_SYM(Atom*,XRRListOutputProperties,(Display *dpy, RROutput output, int *nprop),(dpy,output,nprop),return)
330SDL_X11_SYM(XRRPropertyInfo*,XRRQueryOutputProperty,(Display *dpy,RROutput output, Atom property),(dpy,output,property),return)
331SDL_X11_SYM(int,XRRGetOutputProperty,(Display *dpy,RROutput output, Atom property, long offset, long length, Bool _delete, Bool pending, Atom req_type, Atom *actual_type, int *actual_format, unsigned long *nitems, unsigned long *bytes_after, unsigned char **prop),(dpy,output,property,offset,length, _delete, pending, req_type, actual_type, actual_format, nitems, bytes_after, prop),return)
332SDL_X11_SYM(RROutput,XRRGetOutputPrimary,(Display *dpy,Window window),(dpy,window),return)
333SDL_X11_SYM(void,XRRSelectInput,(Display *dpy, Window window, int mask),(dpy,window,mask),)
334SDL_X11_SYM(Status,XRRGetCrtcTransform,(Display *dpy,RRCrtc crtc,XRRCrtcTransformAttributes **attributes),(dpy,crtc,attributes),return)
335#endif
336
337// MIT-SCREEN-SAVER support
338#ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER
339SDL_X11_MODULE(XSS)
340SDL_X11_SYM(Bool,XScreenSaverQueryExtension,(Display *dpy,int *event_base,int *error_base),(dpy,event_base,error_base),return)
341SDL_X11_SYM(Status,XScreenSaverQueryVersion,(Display *dpy,int *major_versionp,int *minor_versionp),(dpy,major_versionp,minor_versionp),return)
342SDL_X11_SYM(void,XScreenSaverSuspend,(Display *dpy,Bool suspend),(dpy,suspend),return)
343#endif
344
345#ifdef SDL_VIDEO_DRIVER_X11_XSHAPE
346SDL_X11_MODULE(XSHAPE)
347SDL_X11_SYM(void,XShapeCombineMask,(Display *dpy,Window dest,int dest_kind,int x_off,int y_off,Pixmap src,int op),(dpy,dest,dest_kind,x_off,y_off,src,op),)
348SDL_X11_SYM(void,XShapeCombineRegion,(Display *a,Window b,int c,int d,int e,Region f,int g),(a,b,c,d,e,f,g),)
349#endif
350
351#undef SDL_X11_MODULE
352#undef SDL_X11_SYM
353
354/* *INDENT-ON* */ // clang-format on
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11touch.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11touch.c
new file mode 100644
index 0000000..5b42c97
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11touch.c
@@ -0,0 +1,46 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_DRIVER_X11
24
25#include "SDL_x11video.h"
26#include "SDL_x11touch.h"
27#include "SDL_x11xinput2.h"
28#include "../../events/SDL_touch_c.h"
29
30void X11_InitTouch(SDL_VideoDevice *_this)
31{
32 X11_InitXinput2Multitouch(_this);
33}
34
35void X11_QuitTouch(SDL_VideoDevice *_this)
36{
37 SDL_QuitTouch();
38}
39
40void X11_ResetTouch(SDL_VideoDevice *_this)
41{
42 X11_QuitTouch(_this);
43 X11_InitTouch(_this);
44}
45
46#endif // SDL_VIDEO_DRIVER_X11
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11touch.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11touch.h
new file mode 100644
index 0000000..548bf07
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11touch.h
@@ -0,0 +1,30 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifndef SDL_x11touch_h_
24#define SDL_x11touch_h_
25
26extern void X11_InitTouch(SDL_VideoDevice *_this);
27extern void X11_QuitTouch(SDL_VideoDevice *_this);
28extern void X11_ResetTouch(SDL_VideoDevice *_this);
29
30#endif // SDL_x11touch_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11video.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11video.c
new file mode 100644
index 0000000..75862db
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11video.c
@@ -0,0 +1,505 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_DRIVER_X11
24
25#include <unistd.h> // For getpid() and readlink()
26
27#include "../../core/linux/SDL_system_theme.h"
28#include "../../events/SDL_keyboard_c.h"
29#include "../../events/SDL_mouse_c.h"
30#include "../SDL_pixels_c.h"
31#include "../SDL_sysvideo.h"
32
33#include "SDL_x11framebuffer.h"
34#include "SDL_x11pen.h"
35#include "SDL_x11touch.h"
36#include "SDL_x11video.h"
37#include "SDL_x11xfixes.h"
38#include "SDL_x11xinput2.h"
39#include "SDL_x11messagebox.h"
40#include "SDL_x11shape.h"
41#include "SDL_x11xsync.h"
42
43#ifdef SDL_VIDEO_OPENGL_EGL
44#include "SDL_x11opengles.h"
45#endif
46
47// Initialization/Query functions
48static bool X11_VideoInit(SDL_VideoDevice *_this);
49static void X11_VideoQuit(SDL_VideoDevice *_this);
50
51// X11 driver bootstrap functions
52
53static void X11_DeleteDevice(SDL_VideoDevice *device)
54{
55 SDL_VideoData *data = device->internal;
56 if (device->vulkan_config.loader_handle) {
57 device->Vulkan_UnloadLibrary(device);
58 }
59 if (data->display) {
60 X11_XCloseDisplay(data->display);
61 }
62 if (data->request_display) {
63 X11_XCloseDisplay(data->request_display);
64 }
65 SDL_free(data->windowlist);
66 if (device->wakeup_lock) {
67 SDL_DestroyMutex(device->wakeup_lock);
68 }
69 SDL_free(device->internal);
70 SDL_free(device);
71
72 SDL_X11_UnloadSymbols();
73}
74
75static bool X11_IsXWayland(Display *d)
76{
77 int opcode, event, error;
78 return X11_XQueryExtension(d, "XWAYLAND", &opcode, &event, &error) == True;
79}
80
81static bool X11_CheckCurrentDesktop(const char *name)
82{
83 SDL_Environment *env = SDL_GetEnvironment();
84
85 const char *desktopVar = SDL_GetEnvironmentVariable(env, "DESKTOP_SESSION");
86 if (desktopVar && SDL_strcasecmp(desktopVar, name) == 0) {
87 return true;
88 }
89
90 desktopVar = SDL_GetEnvironmentVariable(env, "XDG_CURRENT_DESKTOP");
91 if (desktopVar && SDL_strcasestr(desktopVar, name)) {
92 return true;
93 }
94
95 return false;
96}
97
98static SDL_VideoDevice *X11_CreateDevice(void)
99{
100 SDL_VideoDevice *device;
101 SDL_VideoData *data;
102 const char *display = NULL; // Use the DISPLAY environment variable
103 Display *x11_display = NULL;
104
105 if (!SDL_X11_LoadSymbols()) {
106 return NULL;
107 }
108
109 /* Need for threading gl calls. This is also required for the proprietary
110 nVidia driver to be threaded. */
111 X11_XInitThreads();
112
113 // Open the display first to be sure that X11 is available
114 x11_display = X11_XOpenDisplay(display);
115
116 if (!x11_display) {
117 SDL_X11_UnloadSymbols();
118 return NULL;
119 }
120
121 // Initialize all variables that we clean on shutdown
122 device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice));
123 if (!device) {
124 return NULL;
125 }
126 data = (struct SDL_VideoData *)SDL_calloc(1, sizeof(SDL_VideoData));
127 if (!data) {
128 SDL_free(device);
129 return NULL;
130 }
131 device->internal = data;
132
133 data->global_mouse_changed = true;
134
135#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
136 data->active_cursor_confined_window = NULL;
137#endif // SDL_VIDEO_DRIVER_X11_XFIXES
138
139 data->display = x11_display;
140 data->request_display = X11_XOpenDisplay(display);
141 if (!data->request_display) {
142 X11_XCloseDisplay(data->display);
143 SDL_free(device->internal);
144 SDL_free(device);
145 SDL_X11_UnloadSymbols();
146 return NULL;
147 }
148
149 device->wakeup_lock = SDL_CreateMutex();
150
151#ifdef X11_DEBUG
152 X11_XSynchronize(data->display, True);
153#endif
154
155 /* Steam Deck will have an on-screen keyboard, so check their environment
156 * variable so we can make use of SDL_StartTextInput.
157 */
158 data->is_steam_deck = SDL_GetHintBoolean("SteamDeck", false);
159
160 // Set the function pointers
161 device->VideoInit = X11_VideoInit;
162 device->VideoQuit = X11_VideoQuit;
163 device->ResetTouch = X11_ResetTouch;
164 device->GetDisplayModes = X11_GetDisplayModes;
165 device->GetDisplayBounds = X11_GetDisplayBounds;
166 device->GetDisplayUsableBounds = X11_GetDisplayUsableBounds;
167 device->GetWindowICCProfile = X11_GetWindowICCProfile;
168 device->SetDisplayMode = X11_SetDisplayMode;
169 device->SuspendScreenSaver = X11_SuspendScreenSaver;
170 device->PumpEvents = X11_PumpEvents;
171 device->WaitEventTimeout = X11_WaitEventTimeout;
172 device->SendWakeupEvent = X11_SendWakeupEvent;
173
174 device->CreateSDLWindow = X11_CreateWindow;
175 device->SetWindowTitle = X11_SetWindowTitle;
176 device->SetWindowIcon = X11_SetWindowIcon;
177 device->SetWindowPosition = X11_SetWindowPosition;
178 device->SetWindowSize = X11_SetWindowSize;
179 device->SetWindowMinimumSize = X11_SetWindowMinimumSize;
180 device->SetWindowMaximumSize = X11_SetWindowMaximumSize;
181 device->SetWindowAspectRatio = X11_SetWindowAspectRatio;
182 device->GetWindowBordersSize = X11_GetWindowBordersSize;
183 device->SetWindowOpacity = X11_SetWindowOpacity;
184 device->SetWindowParent = X11_SetWindowParent;
185 device->SetWindowModal = X11_SetWindowModal;
186 device->ShowWindow = X11_ShowWindow;
187 device->HideWindow = X11_HideWindow;
188 device->RaiseWindow = X11_RaiseWindow;
189 device->MaximizeWindow = X11_MaximizeWindow;
190 device->MinimizeWindow = X11_MinimizeWindow;
191 device->RestoreWindow = X11_RestoreWindow;
192 device->SetWindowBordered = X11_SetWindowBordered;
193 device->SetWindowResizable = X11_SetWindowResizable;
194 device->SetWindowAlwaysOnTop = X11_SetWindowAlwaysOnTop;
195 device->SetWindowFullscreen = X11_SetWindowFullscreen;
196 device->SetWindowMouseGrab = X11_SetWindowMouseGrab;
197 device->SetWindowKeyboardGrab = X11_SetWindowKeyboardGrab;
198 device->DestroyWindow = X11_DestroyWindow;
199 device->CreateWindowFramebuffer = X11_CreateWindowFramebuffer;
200 device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer;
201 device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer;
202 device->SetWindowHitTest = X11_SetWindowHitTest;
203 device->AcceptDragAndDrop = X11_AcceptDragAndDrop;
204 device->UpdateWindowShape = X11_UpdateWindowShape;
205 device->FlashWindow = X11_FlashWindow;
206 device->ShowWindowSystemMenu = X11_ShowWindowSystemMenu;
207 device->SetWindowFocusable = X11_SetWindowFocusable;
208 device->SyncWindow = X11_SyncWindow;
209
210#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
211 device->SetWindowMouseRect = X11_SetWindowMouseRect;
212#endif // SDL_VIDEO_DRIVER_X11_XFIXES
213
214#ifdef SDL_VIDEO_OPENGL_GLX
215 device->GL_LoadLibrary = X11_GL_LoadLibrary;
216 device->GL_GetProcAddress = X11_GL_GetProcAddress;
217 device->GL_UnloadLibrary = X11_GL_UnloadLibrary;
218 device->GL_CreateContext = X11_GL_CreateContext;
219 device->GL_MakeCurrent = X11_GL_MakeCurrent;
220 device->GL_SetSwapInterval = X11_GL_SetSwapInterval;
221 device->GL_GetSwapInterval = X11_GL_GetSwapInterval;
222 device->GL_SwapWindow = X11_GL_SwapWindow;
223 device->GL_DestroyContext = X11_GL_DestroyContext;
224 device->GL_GetEGLSurface = NULL;
225#endif
226#ifdef SDL_VIDEO_OPENGL_EGL
227#ifdef SDL_VIDEO_OPENGL_GLX
228 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) {
229#endif
230 device->GL_LoadLibrary = X11_GLES_LoadLibrary;
231 device->GL_GetProcAddress = X11_GLES_GetProcAddress;
232 device->GL_UnloadLibrary = X11_GLES_UnloadLibrary;
233 device->GL_CreateContext = X11_GLES_CreateContext;
234 device->GL_MakeCurrent = X11_GLES_MakeCurrent;
235 device->GL_SetSwapInterval = X11_GLES_SetSwapInterval;
236 device->GL_GetSwapInterval = X11_GLES_GetSwapInterval;
237 device->GL_SwapWindow = X11_GLES_SwapWindow;
238 device->GL_DestroyContext = X11_GLES_DestroyContext;
239 device->GL_GetEGLSurface = X11_GLES_GetEGLSurface;
240#ifdef SDL_VIDEO_OPENGL_GLX
241 }
242#endif
243#endif
244
245 device->GetTextMimeTypes = X11_GetTextMimeTypes;
246 device->SetClipboardData = X11_SetClipboardData;
247 device->GetClipboardData = X11_GetClipboardData;
248 device->HasClipboardData = X11_HasClipboardData;
249 device->SetPrimarySelectionText = X11_SetPrimarySelectionText;
250 device->GetPrimarySelectionText = X11_GetPrimarySelectionText;
251 device->HasPrimarySelectionText = X11_HasPrimarySelectionText;
252 device->StartTextInput = X11_StartTextInput;
253 device->StopTextInput = X11_StopTextInput;
254 device->UpdateTextInputArea = X11_UpdateTextInputArea;
255 device->HasScreenKeyboardSupport = X11_HasScreenKeyboardSupport;
256 device->ShowScreenKeyboard = X11_ShowScreenKeyboard;
257 device->HideScreenKeyboard = X11_HideScreenKeyboard;
258 device->IsScreenKeyboardShown = X11_IsScreenKeyboardShown;
259
260 device->free = X11_DeleteDevice;
261
262#ifdef SDL_VIDEO_VULKAN
263 device->Vulkan_LoadLibrary = X11_Vulkan_LoadLibrary;
264 device->Vulkan_UnloadLibrary = X11_Vulkan_UnloadLibrary;
265 device->Vulkan_GetInstanceExtensions = X11_Vulkan_GetInstanceExtensions;
266 device->Vulkan_CreateSurface = X11_Vulkan_CreateSurface;
267 device->Vulkan_DestroySurface = X11_Vulkan_DestroySurface;
268 device->Vulkan_GetPresentationSupport = X11_Vulkan_GetPresentationSupport;
269#endif
270
271#ifdef SDL_USE_LIBDBUS
272 if (SDL_SystemTheme_Init())
273 device->system_theme = SDL_SystemTheme_Get();
274#endif
275
276 device->device_caps = VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT;
277
278 /* Openbox doesn't send the new window dimensions when entering fullscreen, so the events must be synthesized.
279 * This is otherwise not wanted, as it can break fullscreen window positioning on multi-monitor configurations.
280 */
281 if (!X11_CheckCurrentDesktop("openbox")) {
282 device->device_caps |= VIDEO_DEVICE_CAPS_SENDS_DISPLAY_CHANGES;
283 }
284
285 data->is_xwayland = X11_IsXWayland(x11_display);
286 if (data->is_xwayland) {
287 device->device_caps |= VIDEO_DEVICE_CAPS_MODE_SWITCHING_EMULATED |
288 VIDEO_DEVICE_CAPS_DISABLE_MOUSE_WARP_ON_FULLSCREEN_TRANSITIONS;
289 }
290
291 return device;
292}
293
294VideoBootStrap X11_bootstrap = {
295 "x11", "SDL X11 video driver",
296 X11_CreateDevice,
297 X11_ShowMessageBox,
298 false
299};
300
301static int (*handler)(Display *, XErrorEvent *) = NULL;
302static int X11_CheckWindowManagerErrorHandler(Display *d, XErrorEvent *e)
303{
304 if (e->error_code == BadWindow) {
305 return 0;
306 } else {
307 return handler(d, e);
308 }
309}
310
311static void X11_CheckWindowManager(SDL_VideoDevice *_this)
312{
313 SDL_VideoData *data = _this->internal;
314 Display *display = data->display;
315 Atom _NET_SUPPORTING_WM_CHECK;
316 int status, real_format;
317 Atom real_type;
318 unsigned long items_read = 0, items_left = 0;
319 unsigned char *propdata = NULL;
320 Window wm_window = 0;
321#ifdef DEBUG_WINDOW_MANAGER
322 char *wm_name;
323#endif
324
325 // Set up a handler to gracefully catch errors
326 X11_XSync(display, False);
327 handler = X11_XSetErrorHandler(X11_CheckWindowManagerErrorHandler);
328
329 _NET_SUPPORTING_WM_CHECK = X11_XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
330 status = X11_XGetWindowProperty(display, DefaultRootWindow(display), _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
331 if (status == Success) {
332 if (items_read) {
333 wm_window = ((Window *)propdata)[0];
334 }
335 if (propdata) {
336 X11_XFree(propdata);
337 propdata = NULL;
338 }
339 }
340
341 if (wm_window) {
342 status = X11_XGetWindowProperty(display, wm_window, _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
343 if (status != Success || !items_read || wm_window != ((Window *)propdata)[0]) {
344 wm_window = None;
345 }
346 if (status == Success && propdata) {
347 X11_XFree(propdata);
348 propdata = NULL;
349 }
350 }
351
352 // Reset the error handler, we're done checking
353 X11_XSync(display, False);
354 X11_XSetErrorHandler(handler);
355
356 if (!wm_window) {
357#ifdef DEBUG_WINDOW_MANAGER
358 printf("Couldn't get _NET_SUPPORTING_WM_CHECK property\n");
359#endif
360 return;
361 }
362 data->net_wm = true;
363
364#ifdef DEBUG_WINDOW_MANAGER
365 wm_name = X11_GetWindowTitle(_this, wm_window);
366 printf("Window manager: %s\n", wm_name);
367 SDL_free(wm_name);
368#endif
369}
370
371static bool X11_VideoInit(SDL_VideoDevice *_this)
372{
373 SDL_VideoData *data = _this->internal;
374
375 // Get the process PID to be associated to the window
376 data->pid = getpid();
377
378 // I have no idea how random this actually is, or has to be.
379 data->window_group = (XID)(((size_t)data->pid) ^ ((size_t)_this));
380
381 // Look up some useful Atoms
382#define GET_ATOM(X) data->atoms.X = X11_XInternAtom(data->display, #X, False)
383 GET_ATOM(WM_PROTOCOLS);
384 GET_ATOM(WM_DELETE_WINDOW);
385 GET_ATOM(WM_TAKE_FOCUS);
386 GET_ATOM(WM_NAME);
387 GET_ATOM(WM_TRANSIENT_FOR);
388 GET_ATOM(_NET_WM_STATE);
389 GET_ATOM(_NET_WM_STATE_HIDDEN);
390 GET_ATOM(_NET_WM_STATE_FOCUSED);
391 GET_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
392 GET_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
393 GET_ATOM(_NET_WM_STATE_FULLSCREEN);
394 GET_ATOM(_NET_WM_STATE_ABOVE);
395 GET_ATOM(_NET_WM_STATE_SKIP_TASKBAR);
396 GET_ATOM(_NET_WM_STATE_SKIP_PAGER);
397 GET_ATOM(_NET_WM_MOVERESIZE);
398 GET_ATOM(_NET_WM_STATE_MODAL);
399 GET_ATOM(_NET_WM_ALLOWED_ACTIONS);
400 GET_ATOM(_NET_WM_ACTION_FULLSCREEN);
401 GET_ATOM(_NET_WM_NAME);
402 GET_ATOM(_NET_WM_ICON_NAME);
403 GET_ATOM(_NET_WM_ICON);
404 GET_ATOM(_NET_WM_PING);
405 GET_ATOM(_NET_WM_SYNC_REQUEST);
406 GET_ATOM(_NET_WM_SYNC_REQUEST_COUNTER);
407 GET_ATOM(_NET_WM_WINDOW_OPACITY);
408 GET_ATOM(_NET_WM_USER_TIME);
409 GET_ATOM(_NET_ACTIVE_WINDOW);
410 GET_ATOM(_NET_FRAME_EXTENTS);
411 GET_ATOM(_SDL_WAKEUP);
412 GET_ATOM(UTF8_STRING);
413 GET_ATOM(PRIMARY);
414 GET_ATOM(CLIPBOARD);
415 GET_ATOM(INCR);
416 GET_ATOM(SDL_SELECTION);
417 GET_ATOM(TARGETS);
418 GET_ATOM(SDL_FORMATS);
419 GET_ATOM(XdndAware);
420 GET_ATOM(XdndEnter);
421 GET_ATOM(XdndLeave);
422 GET_ATOM(XdndPosition);
423 GET_ATOM(XdndStatus);
424 GET_ATOM(XdndTypeList);
425 GET_ATOM(XdndActionCopy);
426 GET_ATOM(XdndDrop);
427 GET_ATOM(XdndFinished);
428 GET_ATOM(XdndSelection);
429 GET_ATOM(XKLAVIER_STATE);
430
431 // Detect the window manager
432 X11_CheckWindowManager(_this);
433
434 if (!X11_InitModes(_this)) {
435 return false;
436 }
437
438 if (!X11_InitXinput2(_this)) {
439 // Assume a mouse and keyboard are attached
440 SDL_AddKeyboard(SDL_DEFAULT_KEYBOARD_ID, NULL, false);
441 SDL_AddMouse(SDL_DEFAULT_MOUSE_ID, NULL, false);
442 }
443
444#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
445 X11_InitXfixes(_this);
446#endif // SDL_VIDEO_DRIVER_X11_XFIXES
447
448 X11_InitXsettings(_this);
449
450#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
451 X11_InitXsync(_this);
452#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
453
454#ifndef X_HAVE_UTF8_STRING
455#warning X server does not support UTF8_STRING, a feature introduced in 2000! This is likely to become a hard error in a future libSDL3.
456#endif
457
458 if (!X11_InitKeyboard(_this)) {
459 return false;
460 }
461 X11_InitMouse(_this);
462
463 X11_InitTouch(_this);
464
465 X11_InitPen(_this);
466
467 return true;
468}
469
470void X11_VideoQuit(SDL_VideoDevice *_this)
471{
472 SDL_VideoData *data = _this->internal;
473
474 if (data->clipboard_window) {
475 X11_XDestroyWindow(data->display, data->clipboard_window);
476 }
477
478 if (data->xsettings_window) {
479 X11_XDestroyWindow(data->display, data->xsettings_window);
480 }
481
482#ifdef X_HAVE_UTF8_STRING
483 if (data->im) {
484 X11_XCloseIM(data->im);
485 }
486#endif
487
488 X11_QuitModes(_this);
489 X11_QuitKeyboard(_this);
490 X11_QuitMouse(_this);
491 X11_QuitTouch(_this);
492 X11_QuitPen(_this);
493 X11_QuitClipboard(_this);
494 X11_QuitXsettings(_this);
495}
496
497bool X11_UseDirectColorVisuals(void)
498{
499 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NODIRECTCOLOR, false)) {
500 return false;
501 }
502 return true;
503}
504
505#endif // SDL_VIDEO_DRIVER_X11
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11video.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11video.h
new file mode 100644
index 0000000..a336a80
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11video.h
@@ -0,0 +1,177 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifndef SDL_x11video_h_
24#define SDL_x11video_h_
25
26#include "../SDL_sysvideo.h"
27
28#include "../../core/linux/SDL_dbus.h"
29#include "../../core/linux/SDL_ime.h"
30
31#include "SDL_x11dyn.h"
32
33#include "SDL_x11clipboard.h"
34#include "SDL_x11events.h"
35#include "SDL_x11keyboard.h"
36#include "SDL_x11modes.h"
37#include "SDL_x11mouse.h"
38#include "SDL_x11opengl.h"
39#include "SDL_x11settings.h"
40#include "SDL_x11window.h"
41#include "SDL_x11vulkan.h"
42
43// Private display data
44
45struct SDL_VideoData
46{
47 Display *display;
48 Display *request_display;
49 pid_t pid;
50 XIM im;
51 Uint64 screensaver_activity;
52 int numwindows;
53 SDL_WindowData **windowlist;
54 int windowlistlength;
55 XID window_group;
56 Window clipboard_window;
57 SDLX11_ClipboardData clipboard;
58 SDLX11_ClipboardData primary_selection;
59#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
60 SDL_Window *active_cursor_confined_window;
61#endif // SDL_VIDEO_DRIVER_X11_XFIXES
62 Window xsettings_window;
63 SDLX11_SettingsData xsettings_data;
64
65 // This is true for ICCCM2.0-compliant window managers
66 bool net_wm;
67
68 // Useful atoms
69 struct {
70 Atom WM_PROTOCOLS;
71 Atom WM_DELETE_WINDOW;
72 Atom WM_TAKE_FOCUS;
73 Atom WM_NAME;
74 Atom WM_TRANSIENT_FOR;
75 Atom _NET_WM_STATE;
76 Atom _NET_WM_STATE_HIDDEN;
77 Atom _NET_WM_STATE_FOCUSED;
78 Atom _NET_WM_STATE_MAXIMIZED_VERT;
79 Atom _NET_WM_STATE_MAXIMIZED_HORZ;
80 Atom _NET_WM_STATE_FULLSCREEN;
81 Atom _NET_WM_STATE_ABOVE;
82 Atom _NET_WM_STATE_SKIP_TASKBAR;
83 Atom _NET_WM_STATE_SKIP_PAGER;
84 Atom _NET_WM_STATE_MODAL;
85 Atom _NET_WM_MOVERESIZE;
86 Atom _NET_WM_ALLOWED_ACTIONS;
87 Atom _NET_WM_ACTION_FULLSCREEN;
88 Atom _NET_WM_NAME;
89 Atom _NET_WM_ICON_NAME;
90 Atom _NET_WM_ICON;
91 Atom _NET_WM_PING;
92 Atom _NET_WM_SYNC_REQUEST;
93 Atom _NET_WM_SYNC_REQUEST_COUNTER;
94 Atom _NET_WM_WINDOW_OPACITY;
95 Atom _NET_WM_USER_TIME;
96 Atom _NET_ACTIVE_WINDOW;
97 Atom _NET_FRAME_EXTENTS;
98 Atom _SDL_WAKEUP;
99 Atom UTF8_STRING;
100 Atom PRIMARY;
101 Atom CLIPBOARD;
102 Atom INCR;
103 Atom SDL_SELECTION;
104 Atom TARGETS;
105 Atom SDL_FORMATS;
106 Atom XdndAware;
107 Atom XdndEnter;
108 Atom XdndLeave;
109 Atom XdndPosition;
110 Atom XdndStatus;
111 Atom XdndTypeList;
112 Atom XdndActionCopy;
113 Atom XdndDrop;
114 Atom XdndFinished;
115 Atom XdndSelection;
116 Atom XKLAVIER_STATE;
117
118 // Pen atoms (these have names that don't map well to C symbols)
119 Atom pen_atom_device_product_id;
120 Atom pen_atom_abs_pressure;
121 Atom pen_atom_abs_tilt_x;
122 Atom pen_atom_abs_tilt_y;
123 Atom pen_atom_wacom_serial_ids;
124 Atom pen_atom_wacom_tool_type;
125 } atoms;
126
127 SDL_Scancode key_layout[256];
128 bool selection_waiting;
129 bool selection_incr_waiting;
130
131 bool broken_pointer_grab; // true if XGrabPointer seems unreliable.
132
133 Uint64 last_mode_change_deadline;
134
135 bool global_mouse_changed;
136 SDL_Point global_mouse_position;
137 Uint32 global_mouse_buttons;
138
139 SDL_XInput2DeviceInfo *mouse_device_info;
140 int xinput_master_pointer_device;
141 bool xinput_hierarchy_changed;
142
143 int xrandr_event_base;
144 struct
145 {
146#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
147 XkbDescPtr desc_ptr;
148#endif
149 int event;
150 unsigned int current_group;
151 unsigned int xkb_modifiers;
152
153 SDL_Keymod sdl_modifiers;
154
155 Uint32 numlock_mask;
156 Uint32 scrolllock_mask;
157 } xkb;
158
159 KeyCode filter_code;
160 Time filter_time;
161
162#ifdef SDL_VIDEO_VULKAN
163 // Vulkan variables only valid if _this->vulkan_config.loader_handle is not NULL
164 SDL_SharedObject *vulkan_xlib_xcb_library;
165 PFN_XGetXCBConnection vulkan_XGetXCBConnection;
166#endif
167
168 // Used to interact with the on-screen keyboard
169 bool is_steam_deck;
170 bool steam_keyboard_open;
171
172 bool is_xwayland;
173};
174
175extern bool X11_UseDirectColorVisuals(void);
176
177#endif // SDL_x11video_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11vulkan.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11vulkan.c
new file mode 100644
index 0000000..065549b
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11vulkan.c
@@ -0,0 +1,290 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_X11)
24
25#include "../SDL_vulkan_internal.h"
26
27#include "SDL_x11video.h"
28
29#include "SDL_x11vulkan.h"
30
31#include <X11/Xlib.h>
32// #include <xcb/xcb.h>
33
34#ifdef SDL_PLATFORM_OPENBSD
35#define DEFAULT_VULKAN "libvulkan.so"
36#define DEFAULT_X11_XCB "libX11-xcb.so"
37#else
38#define DEFAULT_VULKAN "libvulkan.so.1"
39#define DEFAULT_X11_XCB "libX11-xcb.so.1"
40#endif
41
42/*
43typedef uint32_t xcb_window_t;
44typedef uint32_t xcb_visualid_t;
45*/
46
47bool X11_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path)
48{
49 SDL_VideoData *videoData = _this->internal;
50 VkExtensionProperties *extensions = NULL;
51 Uint32 extensionCount = 0;
52 bool hasSurfaceExtension = false;
53 bool hasXlibSurfaceExtension = false;
54 bool hasXCBSurfaceExtension = false;
55 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
56 Uint32 i;
57 if (_this->vulkan_config.loader_handle) {
58 return SDL_SetError("Vulkan already loaded");
59 }
60
61 // Load the Vulkan loader library
62 if (!path) {
63 path = SDL_GetHint(SDL_HINT_VULKAN_LIBRARY);
64 }
65 if (!path) {
66 path = DEFAULT_VULKAN;
67 }
68 _this->vulkan_config.loader_handle = SDL_LoadObject(path);
69 if (!_this->vulkan_config.loader_handle) {
70 return false;
71 }
72 SDL_strlcpy(_this->vulkan_config.loader_path, path, SDL_arraysize(_this->vulkan_config.loader_path));
73 vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction(
74 _this->vulkan_config.loader_handle, "vkGetInstanceProcAddr");
75 if (!vkGetInstanceProcAddr) {
76 goto fail;
77 }
78 _this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr;
79 _this->vulkan_config.vkEnumerateInstanceExtensionProperties =
80 (void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)(
81 VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties");
82 if (!_this->vulkan_config.vkEnumerateInstanceExtensionProperties) {
83 goto fail;
84 }
85 extensions = SDL_Vulkan_CreateInstanceExtensionsList(
86 (PFN_vkEnumerateInstanceExtensionProperties)
87 _this->vulkan_config.vkEnumerateInstanceExtensionProperties,
88 &extensionCount);
89 if (!extensions) {
90 goto fail;
91 }
92 for (i = 0; i < extensionCount; i++) {
93 if (SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
94 hasSurfaceExtension = true;
95 } else if (SDL_strcmp(VK_KHR_XCB_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
96 hasXCBSurfaceExtension = true;
97 } else if (SDL_strcmp(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
98 hasXlibSurfaceExtension = true;
99 }
100 }
101 SDL_free(extensions);
102 if (!hasSurfaceExtension) {
103 SDL_SetError("Installed Vulkan doesn't implement the " VK_KHR_SURFACE_EXTENSION_NAME " extension");
104 goto fail;
105 }
106 if (hasXlibSurfaceExtension) {
107 videoData->vulkan_xlib_xcb_library = NULL;
108 } else if (!hasXCBSurfaceExtension) {
109 SDL_SetError("Installed Vulkan doesn't implement either the " VK_KHR_XCB_SURFACE_EXTENSION_NAME "extension or the " VK_KHR_XLIB_SURFACE_EXTENSION_NAME " extension");
110 goto fail;
111 } else {
112 const char *libX11XCBLibraryName = SDL_GetHint(SDL_HINT_X11_XCB_LIBRARY);
113 if (!libX11XCBLibraryName || !*libX11XCBLibraryName) {
114 libX11XCBLibraryName = DEFAULT_X11_XCB;
115 }
116 videoData->vulkan_xlib_xcb_library = SDL_LoadObject(libX11XCBLibraryName);
117 if (!videoData->vulkan_xlib_xcb_library) {
118 goto fail;
119 }
120 videoData->vulkan_XGetXCBConnection =
121 (PFN_XGetXCBConnection)SDL_LoadFunction(videoData->vulkan_xlib_xcb_library, "XGetXCBConnection");
122 if (!videoData->vulkan_XGetXCBConnection) {
123 SDL_UnloadObject(videoData->vulkan_xlib_xcb_library);
124 goto fail;
125 }
126 }
127 return true;
128
129fail:
130 SDL_UnloadObject(_this->vulkan_config.loader_handle);
131 _this->vulkan_config.loader_handle = NULL;
132 return false;
133}
134
135void X11_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
136{
137 SDL_VideoData *videoData = _this->internal;
138 if (_this->vulkan_config.loader_handle) {
139 if (videoData->vulkan_xlib_xcb_library) {
140 SDL_UnloadObject(videoData->vulkan_xlib_xcb_library);
141 }
142 SDL_UnloadObject(_this->vulkan_config.loader_handle);
143 _this->vulkan_config.loader_handle = NULL;
144 }
145}
146
147char const* const* X11_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
148 Uint32 *count)
149{
150 SDL_VideoData *videoData = _this->internal;
151 if (videoData->vulkan_xlib_xcb_library) {
152 static const char *const extensionsForXCB[] = {
153 VK_KHR_SURFACE_EXTENSION_NAME,
154 VK_KHR_XCB_SURFACE_EXTENSION_NAME,
155 };
156 if(count) {
157 *count = SDL_arraysize(extensionsForXCB);
158 }
159 return extensionsForXCB;
160 } else {
161 static const char *const extensionsForXlib[] = {
162 VK_KHR_SURFACE_EXTENSION_NAME,
163 VK_KHR_XLIB_SURFACE_EXTENSION_NAME,
164 };
165 if(count) {
166 *count = SDL_arraysize(extensionsForXlib);
167 }
168 return extensionsForXlib;
169 }
170}
171
172bool X11_Vulkan_CreateSurface(SDL_VideoDevice *_this,
173 SDL_Window *window,
174 VkInstance instance,
175 const struct VkAllocationCallbacks *allocator,
176 VkSurfaceKHR *surface)
177{
178 SDL_VideoData *videoData = _this->internal;
179 SDL_WindowData *windowData = window->internal;
180 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
181 if (!_this->vulkan_config.loader_handle) {
182 return SDL_SetError("Vulkan is not loaded");
183 }
184 vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
185 if (videoData->vulkan_xlib_xcb_library) {
186 PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR =
187 (PFN_vkCreateXcbSurfaceKHR)vkGetInstanceProcAddr(instance,
188 "vkCreateXcbSurfaceKHR");
189 VkXcbSurfaceCreateInfoKHR createInfo;
190 VkResult result;
191 if (!vkCreateXcbSurfaceKHR) {
192 return SDL_SetError(VK_KHR_XCB_SURFACE_EXTENSION_NAME " extension is not enabled in the Vulkan instance.");
193 }
194 SDL_zero(createInfo);
195 createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
196 createInfo.connection = videoData->vulkan_XGetXCBConnection(videoData->display);
197 if (!createInfo.connection) {
198 return SDL_SetError("XGetXCBConnection failed");
199 }
200 createInfo.window = (xcb_window_t)windowData->xwindow;
201 result = vkCreateXcbSurfaceKHR(instance, &createInfo, allocator, surface);
202 if (result != VK_SUCCESS) {
203 return SDL_SetError("vkCreateXcbSurfaceKHR failed: %s", SDL_Vulkan_GetResultString(result));
204 }
205 } else {
206 PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR =
207 (PFN_vkCreateXlibSurfaceKHR)vkGetInstanceProcAddr(instance,
208 "vkCreateXlibSurfaceKHR");
209 VkXlibSurfaceCreateInfoKHR createInfo;
210 VkResult result;
211 if (!vkCreateXlibSurfaceKHR) {
212 return SDL_SetError(VK_KHR_XLIB_SURFACE_EXTENSION_NAME " extension is not enabled in the Vulkan instance.");
213 }
214 SDL_zero(createInfo);
215 createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
216 createInfo.dpy = videoData->display;
217 createInfo.window = (xcb_window_t)windowData->xwindow;
218 result = vkCreateXlibSurfaceKHR(instance, &createInfo, allocator, surface);
219 if (result != VK_SUCCESS) {
220 return SDL_SetError("vkCreateXlibSurfaceKHR failed: %s", SDL_Vulkan_GetResultString(result));
221 }
222 }
223
224 return true; // success!
225}
226
227void X11_Vulkan_DestroySurface(SDL_VideoDevice *_this,
228 VkInstance instance,
229 VkSurfaceKHR surface,
230 const struct VkAllocationCallbacks *allocator)
231{
232 if (_this->vulkan_config.loader_handle) {
233 SDL_Vulkan_DestroySurface_Internal(_this->vulkan_config.vkGetInstanceProcAddr, instance, surface, allocator);
234 }
235}
236
237bool X11_Vulkan_GetPresentationSupport(SDL_VideoDevice *_this,
238 VkInstance instance,
239 VkPhysicalDevice physicalDevice,
240 Uint32 queueFamilyIndex)
241{
242 SDL_VideoData *videoData = _this->internal;
243 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
244 const char *forced_visual_id;
245 VisualID visualid;
246
247 if (!_this->vulkan_config.loader_handle) {
248 return SDL_SetError("Vulkan is not loaded");
249 }
250 vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
251
252 forced_visual_id = SDL_GetHint(SDL_HINT_VIDEO_X11_WINDOW_VISUALID);
253 if (forced_visual_id) {
254 visualid = SDL_strtol(forced_visual_id, NULL, 0);
255 } else {
256 visualid = X11_XVisualIDFromVisual(DefaultVisual(videoData->display, DefaultScreen(videoData->display)));
257 }
258
259 if (videoData->vulkan_xlib_xcb_library) {
260 PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR =
261 (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)vkGetInstanceProcAddr(
262 instance,
263 "vkGetPhysicalDeviceXcbPresentationSupportKHR");
264
265 if (!vkGetPhysicalDeviceXcbPresentationSupportKHR) {
266 return SDL_SetError(VK_KHR_XCB_SURFACE_EXTENSION_NAME " extension is not enabled in the Vulkan instance.");
267 }
268
269 return vkGetPhysicalDeviceXcbPresentationSupportKHR(physicalDevice,
270 queueFamilyIndex,
271 videoData->vulkan_XGetXCBConnection(videoData->display),
272 visualid);
273 } else {
274 PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR =
275 (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)vkGetInstanceProcAddr(
276 instance,
277 "vkGetPhysicalDeviceXlibPresentationSupportKHR");
278
279 if (!vkGetPhysicalDeviceXlibPresentationSupportKHR) {
280 return SDL_SetError(VK_KHR_XLIB_SURFACE_EXTENSION_NAME " extension is not enabled in the Vulkan instance.");
281 }
282
283 return vkGetPhysicalDeviceXlibPresentationSupportKHR(physicalDevice,
284 queueFamilyIndex,
285 videoData->display,
286 visualid);
287 }
288}
289
290#endif
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11vulkan.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11vulkan.h
new file mode 100644
index 0000000..2a62596
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11vulkan.h
@@ -0,0 +1,52 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifndef SDL_x11vulkan_h_
24#define SDL_x11vulkan_h_
25
26#include <SDL3/SDL_vulkan.h>
27
28#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_X11)
29
30typedef struct xcb_connection_t xcb_connection_t;
31typedef xcb_connection_t *(*PFN_XGetXCBConnection)(Display *dpy);
32
33extern bool X11_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path);
34extern void X11_Vulkan_UnloadLibrary(SDL_VideoDevice *_this);
35extern char const* const* X11_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count);
36extern bool X11_Vulkan_CreateSurface(SDL_VideoDevice *_this,
37 SDL_Window *window,
38 VkInstance instance,
39 const struct VkAllocationCallbacks *allocator,
40 VkSurfaceKHR *surface);
41extern void X11_Vulkan_DestroySurface(SDL_VideoDevice *_this,
42 VkInstance instance,
43 VkSurfaceKHR surface,
44 const struct VkAllocationCallbacks *allocator);
45extern bool X11_Vulkan_GetPresentationSupport(SDL_VideoDevice *_this,
46 VkInstance instance,
47 VkPhysicalDevice physicalDevice,
48 Uint32 queueFamilyIndex);
49
50#endif
51
52#endif // SDL_x11vulkan_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11window.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11window.c
new file mode 100644
index 0000000..5562053
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11window.c
@@ -0,0 +1,2243 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_DRIVER_X11
24
25#include "../SDL_sysvideo.h"
26#include "../SDL_pixels_c.h"
27#include "../../events/SDL_keyboard_c.h"
28#include "../../events/SDL_mouse_c.h"
29#include "../../events/SDL_events_c.h"
30#include "../../core/unix/SDL_appid.h"
31
32#include "SDL_x11video.h"
33#include "SDL_x11mouse.h"
34#include "SDL_x11xinput2.h"
35#include "SDL_x11xfixes.h"
36
37#ifdef SDL_VIDEO_OPENGL_EGL
38#include "SDL_x11opengles.h"
39#endif
40
41#include "SDL_x11xsync.h"
42
43#define _NET_WM_STATE_REMOVE 0l
44#define _NET_WM_STATE_ADD 1l
45
46#define CHECK_WINDOW_DATA(window) \
47 if (!window) { \
48 return SDL_SetError("Invalid window"); \
49 } \
50 if (!window->internal) { \
51 return SDL_SetError("Invalid window driver data"); \
52 }
53
54#define CHECK_DISPLAY_DATA(display) \
55 if (!_display) { \
56 return SDL_SetError("Invalid display"); \
57 } \
58 if (!_display->internal) { \
59 return SDL_SetError("Invalid display driver data"); \
60 }
61
62static Bool isMapNotify(Display *dpy, XEvent *ev, XPointer win) // NOLINT(readability-non-const-parameter): cannot make XPointer a const pointer due to typedef
63{
64 return ev->type == MapNotify && ev->xmap.window == *((Window *)win);
65}
66static Bool isUnmapNotify(Display *dpy, XEvent *ev, XPointer win) // NOLINT(readability-non-const-parameter): cannot make XPointer a const pointer due to typedef
67{
68 return ev->type == UnmapNotify && ev->xunmap.window == *((Window *)win);
69}
70
71/*
72static Bool isConfigureNotify(Display *dpy, XEvent *ev, XPointer win)
73{
74 return ev->type == ConfigureNotify && ev->xconfigure.window == *((Window*)win);
75}
76static Bool X11_XIfEventTimeout(Display *display, XEvent *event_return, Bool (*predicate)(), XPointer arg, int timeoutMS)
77{
78 Uint64 start = SDL_GetTicks();
79
80 while (!X11_XCheckIfEvent(display, event_return, predicate, arg)) {
81 if (SDL_GetTicks() >= (start + timeoutMS)) {
82 return False;
83 }
84 }
85 return True;
86}
87*/
88
89static bool X11_IsWindowMapped(SDL_VideoDevice *_this, SDL_Window *window)
90{
91 SDL_WindowData *data = window->internal;
92 SDL_VideoData *videodata = _this->internal;
93 XWindowAttributes attr;
94
95 X11_XGetWindowAttributes(videodata->display, data->xwindow, &attr);
96 if (attr.map_state != IsUnmapped) {
97 return true;
98 } else {
99 return false;
100 }
101}
102
103static bool X11_IsDisplayOk(Display *display)
104{
105 if (display->flags & XlibDisplayIOError) {
106 return false;
107 }
108 return true;
109}
110
111#if 0
112static bool X11_IsActionAllowed(SDL_Window *window, Atom action)
113{
114 SDL_WindowData *data = window->internal;
115 Atom _NET_WM_ALLOWED_ACTIONS = data->videodata->_NET_WM_ALLOWED_ACTIONS;
116 Atom type;
117 Display *display = data->videodata->display;
118 int form;
119 unsigned long remain;
120 unsigned long len, i;
121 Atom *list;
122 bool ret = false;
123
124 if (X11_XGetWindowProperty(display, data->xwindow, _NET_WM_ALLOWED_ACTIONS, 0, 1024, False, XA_ATOM, &type, &form, &len, &remain, (unsigned char **)&list) == Success) {
125 for (i=0; i<len; ++i) {
126 if (list[i] == action) {
127 ret = true;
128 break;
129 }
130 }
131 X11_XFree(list);
132 }
133 return ret;
134}
135#endif // 0
136
137static void X11_FlushPendingEvents(SDL_VideoDevice *_this, SDL_Window *window)
138{
139 // Serialize and restore the pending flags, as they may be overwritten while flushing.
140 const bool last_position_pending = window->last_position_pending;
141 const bool last_size_pending = window->last_size_pending;
142
143 X11_SyncWindow(_this, window);
144
145 window->last_position_pending = last_position_pending;
146 window->last_size_pending = last_size_pending;
147}
148
149void X11_SetNetWMState(SDL_VideoDevice *_this, Window xwindow, SDL_WindowFlags flags)
150{
151 SDL_VideoData *videodata = _this->internal;
152 Display *display = videodata->display;
153 // !!! FIXME: just dereference videodata below instead of copying to locals.
154 Atom _NET_WM_STATE = videodata->atoms._NET_WM_STATE;
155 // Atom _NET_WM_STATE_HIDDEN = videodata->atoms._NET_WM_STATE_HIDDEN;
156 Atom _NET_WM_STATE_FOCUSED = videodata->atoms._NET_WM_STATE_FOCUSED;
157 Atom _NET_WM_STATE_MAXIMIZED_VERT = videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT;
158 Atom _NET_WM_STATE_MAXIMIZED_HORZ = videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ;
159 Atom _NET_WM_STATE_FULLSCREEN = videodata->atoms._NET_WM_STATE_FULLSCREEN;
160 Atom _NET_WM_STATE_ABOVE = videodata->atoms._NET_WM_STATE_ABOVE;
161 Atom _NET_WM_STATE_SKIP_TASKBAR = videodata->atoms._NET_WM_STATE_SKIP_TASKBAR;
162 Atom _NET_WM_STATE_SKIP_PAGER = videodata->atoms._NET_WM_STATE_SKIP_PAGER;
163 Atom _NET_WM_STATE_MODAL = videodata->atoms._NET_WM_STATE_MODAL;
164 Atom atoms[16];
165 int count = 0;
166
167 /* The window manager sets this property, we shouldn't set it.
168 If we did, this would indicate to the window manager that we don't
169 actually want to be mapped during X11_XMapRaised(), which would be bad.
170 *
171 if ((flags & SDL_WINDOW_HIDDEN) != 0) {
172 atoms[count++] = _NET_WM_STATE_HIDDEN;
173 }
174 */
175
176 if (flags & SDL_WINDOW_ALWAYS_ON_TOP) {
177 atoms[count++] = _NET_WM_STATE_ABOVE;
178 }
179 if (flags & SDL_WINDOW_UTILITY) {
180 atoms[count++] = _NET_WM_STATE_SKIP_TASKBAR;
181 atoms[count++] = _NET_WM_STATE_SKIP_PAGER;
182 }
183 if (flags & SDL_WINDOW_INPUT_FOCUS) {
184 atoms[count++] = _NET_WM_STATE_FOCUSED;
185 }
186 if (flags & SDL_WINDOW_MAXIMIZED) {
187 atoms[count++] = _NET_WM_STATE_MAXIMIZED_VERT;
188 atoms[count++] = _NET_WM_STATE_MAXIMIZED_HORZ;
189 }
190 if (flags & SDL_WINDOW_FULLSCREEN) {
191 atoms[count++] = _NET_WM_STATE_FULLSCREEN;
192 }
193 if (flags & SDL_WINDOW_MODAL) {
194 atoms[count++] = _NET_WM_STATE_MODAL;
195 }
196
197 SDL_assert(count <= SDL_arraysize(atoms));
198
199 if (count > 0) {
200 X11_XChangeProperty(display, xwindow, _NET_WM_STATE, XA_ATOM, 32,
201 PropModeReplace, (unsigned char *)atoms, count);
202 } else {
203 X11_XDeleteProperty(display, xwindow, _NET_WM_STATE);
204 }
205}
206
207static void X11_ConstrainPopup(SDL_Window *window, bool output_to_pending)
208{
209 // Clamp popup windows to the output borders
210 if (SDL_WINDOW_IS_POPUP(window)) {
211 SDL_Window *w;
212 SDL_DisplayID displayID;
213 SDL_Rect rect;
214 int abs_x = window->last_position_pending ? window->pending.x : window->floating.x;
215 int abs_y = window->last_position_pending ? window->pending.y : window->floating.y;
216 int offset_x = 0, offset_y = 0;
217
218 // Calculate the total offset from the parents
219 for (w = window->parent; SDL_WINDOW_IS_POPUP(w); w = w->parent) {
220 offset_x += w->x;
221 offset_y += w->y;
222 }
223
224 offset_x += w->x;
225 offset_y += w->y;
226 abs_x += offset_x;
227 abs_y += offset_y;
228
229 displayID = SDL_GetDisplayForWindow(w);
230
231 SDL_GetDisplayBounds(displayID, &rect);
232 if (abs_x + window->w > rect.x + rect.w) {
233 abs_x -= (abs_x + window->w) - (rect.x + rect.w);
234 }
235 if (abs_y + window->h > rect.y + rect.h) {
236 abs_y -= (abs_y + window->h) - (rect.y + rect.h);
237 }
238 abs_x = SDL_max(abs_x, rect.x);
239 abs_y = SDL_max(abs_y, rect.y);
240
241 if (output_to_pending) {
242 window->pending.x = abs_x - offset_x;
243 window->pending.y = abs_y - offset_y;
244 } else {
245 window->floating.x = window->windowed.x = abs_x - offset_x;
246 window->floating.y = window->windowed.y = abs_y - offset_y;
247 }
248 }
249}
250
251static void X11_SetKeyboardFocus(SDL_Window *window, bool set_active_focus)
252{
253 SDL_Window *toplevel = window;
254
255 // Find the toplevel parent
256 while (SDL_WINDOW_IS_POPUP(toplevel)) {
257 toplevel = toplevel->parent;
258 }
259
260 toplevel->internal->keyboard_focus = window;
261
262 if (set_active_focus && !window->is_hiding && !window->is_destroying) {
263 SDL_SetKeyboardFocus(window);
264 }
265}
266
267Uint32 X11_GetNetWMState(SDL_VideoDevice *_this, SDL_Window *window, Window xwindow)
268{
269 SDL_VideoData *videodata = _this->internal;
270 Display *display = videodata->display;
271 Atom _NET_WM_STATE = videodata->atoms._NET_WM_STATE;
272 Atom _NET_WM_STATE_HIDDEN = videodata->atoms._NET_WM_STATE_HIDDEN;
273 Atom _NET_WM_STATE_FOCUSED = videodata->atoms._NET_WM_STATE_FOCUSED;
274 Atom _NET_WM_STATE_MAXIMIZED_VERT = videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT;
275 Atom _NET_WM_STATE_MAXIMIZED_HORZ = videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ;
276 Atom _NET_WM_STATE_FULLSCREEN = videodata->atoms._NET_WM_STATE_FULLSCREEN;
277 Atom actualType;
278 int actualFormat;
279 unsigned long i, numItems, bytesAfter;
280 unsigned char *propertyValue = NULL;
281 long maxLength = 1024;
282 SDL_WindowFlags flags = 0;
283
284 if (X11_XGetWindowProperty(display, xwindow, _NET_WM_STATE,
285 0l, maxLength, False, XA_ATOM, &actualType,
286 &actualFormat, &numItems, &bytesAfter,
287 &propertyValue) == Success) {
288 Atom *atoms = (Atom *)propertyValue;
289 int maximized = 0;
290 int fullscreen = 0;
291
292 for (i = 0; i < numItems; ++i) {
293 if (atoms[i] == _NET_WM_STATE_HIDDEN) {
294 flags |= SDL_WINDOW_MINIMIZED | SDL_WINDOW_OCCLUDED;
295 } else if (atoms[i] == _NET_WM_STATE_FOCUSED) {
296 flags |= SDL_WINDOW_INPUT_FOCUS;
297 } else if (atoms[i] == _NET_WM_STATE_MAXIMIZED_VERT) {
298 maximized |= 1;
299 } else if (atoms[i] == _NET_WM_STATE_MAXIMIZED_HORZ) {
300 maximized |= 2;
301 } else if (atoms[i] == _NET_WM_STATE_FULLSCREEN) {
302 fullscreen = 1;
303 }
304 }
305
306 if (fullscreen == 1) {
307 if (window->flags & SDL_WINDOW_FULLSCREEN) {
308 // Pick whatever state the window expects
309 flags |= (window->flags & SDL_WINDOW_FULLSCREEN);
310 } else {
311 // Assume we're fullscreen desktop
312 flags |= SDL_WINDOW_FULLSCREEN;
313 }
314 }
315
316 if (maximized == 3) {
317 /* Fullscreen windows are maximized on some window managers,
318 and this is functional behavior - if maximized is removed,
319 the windows remain floating centered and not covering the
320 rest of the desktop. So we just won't change the maximize
321 state for fullscreen windows here, otherwise SDL would think
322 we're always maximized when fullscreen and not restore the
323 correct state when leaving fullscreen.
324 */
325 if (fullscreen) {
326 flags |= (window->flags & SDL_WINDOW_MAXIMIZED);
327 } else {
328 flags |= SDL_WINDOW_MAXIMIZED;
329 }
330 }
331
332 /* If the window is unmapped, numItems will be zero and _NET_WM_STATE_HIDDEN
333 * will not be set. Do an additional check to see if the window is unmapped
334 * and mark it as SDL_WINDOW_HIDDEN if it is.
335 */
336 {
337 XWindowAttributes attr;
338 SDL_memset(&attr, 0, sizeof(attr));
339 X11_XGetWindowAttributes(videodata->display, xwindow, &attr);
340 if (attr.map_state == IsUnmapped) {
341 flags |= SDL_WINDOW_HIDDEN;
342 }
343 }
344 X11_XFree(propertyValue);
345 }
346
347 // FIXME, check the size hints for resizable
348 // flags |= SDL_WINDOW_RESIZABLE;
349
350 return flags;
351}
352
353static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, Window w)
354{
355 SDL_VideoData *videodata = _this->internal;
356 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
357 SDL_WindowData *data;
358 int numwindows = videodata->numwindows;
359 int windowlistlength = videodata->windowlistlength;
360 SDL_WindowData **windowlist = videodata->windowlist;
361
362 // Allocate the window data
363 data = (SDL_WindowData *)SDL_calloc(1, sizeof(*data));
364 if (!data) {
365 return false;
366 }
367 data->videodata = videodata;
368 data->window = window;
369 data->xwindow = w;
370 data->hit_test_result = SDL_HITTEST_NORMAL;
371
372 X11_CreateInputContext(data);
373
374 // Associate the data with the window
375
376 if (numwindows < windowlistlength) {
377 windowlist[numwindows] = data;
378 videodata->numwindows++;
379 } else {
380 SDL_WindowData ** new_windowlist = (SDL_WindowData **)SDL_realloc(windowlist, (numwindows + 1) * sizeof(*windowlist));
381 if (!new_windowlist) {
382 SDL_free(data);
383 return false;
384 }
385 windowlist = new_windowlist;
386 windowlist[numwindows] = data;
387 videodata->numwindows++;
388 videodata->windowlistlength++;
389 videodata->windowlist = windowlist;
390 }
391
392 // Fill in the SDL window with the window data
393 {
394 XWindowAttributes attrib;
395
396 X11_XGetWindowAttributes(data->videodata->display, w, &attrib);
397 if (!SDL_WINDOW_IS_POPUP(window)) {
398 window->x = window->windowed.x = window->floating.x = attrib.x;
399 window->y = window->windowed.y = window->floating.y = attrib.y - data->border_top;
400 }
401 window->w = window->windowed.w = window->floating.w = attrib.width;
402 window->h = window->windowed.h = window->floating.h = attrib.height;
403 if (attrib.map_state != IsUnmapped) {
404 window->flags &= ~SDL_WINDOW_HIDDEN;
405 } else {
406 window->flags |= SDL_WINDOW_HIDDEN;
407 }
408 data->visual = attrib.visual;
409 data->colormap = attrib.colormap;
410 }
411
412 window->flags |= X11_GetNetWMState(_this, window, w);
413
414 {
415 Window FocalWindow;
416 int RevertTo = 0;
417 X11_XGetInputFocus(data->videodata->display, &FocalWindow, &RevertTo);
418 if (FocalWindow == w) {
419 window->flags |= SDL_WINDOW_INPUT_FOCUS;
420 }
421
422 if (window->flags & SDL_WINDOW_INPUT_FOCUS) {
423 SDL_SetKeyboardFocus(data->window);
424 }
425
426 if (window->flags & SDL_WINDOW_MOUSE_GRABBED) {
427 // Tell x11 to clip mouse
428 }
429 }
430
431 if (window->flags & SDL_WINDOW_EXTERNAL) {
432 // Query the title from the existing window
433 window->title = X11_GetWindowTitle(_this, w);
434 }
435
436 SDL_PropertiesID props = SDL_GetWindowProperties(window);
437 int screen = (displaydata ? displaydata->screen : 0);
438 SDL_SetPointerProperty(props, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, data->videodata->display);
439 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_X11_SCREEN_NUMBER, screen);
440 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, data->xwindow);
441
442 // All done!
443 window->internal = data;
444 return true;
445}
446
447static void SetupWindowInput(SDL_VideoDevice *_this, SDL_Window *window)
448{
449 long fevent = 0;
450 SDL_WindowData *data = window->internal;
451 Window xwindow = data->xwindow;
452
453#ifdef X_HAVE_UTF8_STRING
454 if (SDL_X11_HAVE_UTF8 && data->ic) {
455 X11_XGetICValues(data->ic, XNFilterEvents, &fevent, NULL);
456 }
457#endif
458
459 X11_Xinput2SelectTouch(_this, window);
460
461 {
462 unsigned int x11_keyboard_events = KeyPressMask | KeyReleaseMask;
463 unsigned int x11_pointer_events = ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
464
465 X11_Xinput2SelectMouseAndKeyboard(_this, window);
466
467 // If XInput2 can handle pointer and keyboard events, we don't track them here
468 if (data->xinput2_keyboard_enabled) {
469 x11_keyboard_events = 0;
470 }
471 if (data->xinput2_mouse_enabled) {
472 x11_pointer_events = 0;
473 }
474
475 X11_XSelectInput(data->videodata->display, xwindow,
476 (FocusChangeMask | EnterWindowMask | LeaveWindowMask | ExposureMask |
477 x11_keyboard_events | x11_pointer_events |
478 PropertyChangeMask | StructureNotifyMask |
479 KeymapStateMask | fevent));
480 }
481}
482
483static void SetWindowBordered(Display *display, int screen, Window window, bool border)
484{
485 /*
486 * this code used to check for KWM_WIN_DECORATION, but KDE hasn't
487 * supported it for years and years. It now respects _MOTIF_WM_HINTS.
488 * Gnome is similar: just use the Motif atom.
489 */
490
491 Atom WM_HINTS = X11_XInternAtom(display, "_MOTIF_WM_HINTS", True);
492 if (WM_HINTS != None) {
493 // Hints used by Motif compliant window managers
494 struct
495 {
496 unsigned long flags;
497 unsigned long functions;
498 unsigned long decorations;
499 long input_mode;
500 unsigned long status;
501 } MWMHints = {
502 (1L << 1), 0, border ? 1 : 0, 0, 0
503 };
504
505 X11_XChangeProperty(display, window, WM_HINTS, WM_HINTS, 32,
506 PropModeReplace, (unsigned char *)&MWMHints,
507 sizeof(MWMHints) / sizeof(long));
508 } else { // set the transient hints instead, if necessary
509 X11_XSetTransientForHint(display, window, RootWindow(display, screen));
510 }
511}
512
513bool X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props)
514{
515 Window w = (Window)SDL_GetNumberProperty(create_props, SDL_PROP_WINDOW_CREATE_X11_WINDOW_NUMBER,
516 (Window)SDL_GetPointerProperty(create_props, "sdl2-compat.external_window", NULL));
517 if (w) {
518 window->flags |= SDL_WINDOW_EXTERNAL;
519
520 if (!SetupWindowData(_this, window, w)) {
521 return false;
522 }
523
524 SetupWindowInput(_this, window);
525 return true;
526 }
527
528 SDL_VideoData *data = _this->internal;
529 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
530 if (!displaydata) {
531 return SDL_SetError("Could not find display info");
532 }
533
534 const bool force_override_redirect = SDL_GetHintBoolean(SDL_HINT_X11_FORCE_OVERRIDE_REDIRECT, false);
535 const bool use_resize_sync = (window->flags & SDL_WINDOW_VULKAN); /* doesn't work well with Vulkan */
536 SDL_WindowData *windowdata;
537 Display *display = data->display;
538 int screen = displaydata->screen;
539 Visual *visual;
540 int depth;
541 XSetWindowAttributes xattr;
542 XSizeHints *sizehints;
543 XWMHints *wmhints;
544 XClassHint *classhints;
545 Atom _NET_WM_BYPASS_COMPOSITOR;
546 Atom _NET_WM_WINDOW_TYPE;
547 Atom wintype;
548 const char *wintype_name = NULL;
549 long compositor = 1;
550 Atom _NET_WM_PID;
551 const char *hint = NULL;
552 int win_x, win_y;
553 bool undefined_position = false;
554
555#if defined(SDL_VIDEO_OPENGL_GLX) || defined(SDL_VIDEO_OPENGL_EGL)
556 const int transparent = (window->flags & SDL_WINDOW_TRANSPARENT) ? true : false;
557 const char *forced_visual_id = SDL_GetHint(SDL_HINT_VIDEO_X11_WINDOW_VISUALID);
558 const char *display_visual_id = SDL_GetHint(SDL_HINT_VIDEO_X11_VISUALID);
559
560 if (forced_visual_id && *forced_visual_id) {
561 XVisualInfo *vi, template;
562 int nvis;
563
564 SDL_zero(template);
565 template.visualid = SDL_strtol(forced_visual_id, NULL, 0);
566 vi = X11_XGetVisualInfo(display, VisualIDMask, &template, &nvis);
567 if (vi) {
568 visual = vi->visual;
569 depth = vi->depth;
570 X11_XFree(vi);
571 } else {
572 return false;
573 }
574 } else if ((window->flags & SDL_WINDOW_OPENGL) &&
575 (!display_visual_id || !*display_visual_id)) {
576 XVisualInfo *vinfo = NULL;
577
578#ifdef SDL_VIDEO_OPENGL_EGL
579 if (((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) ||
580 SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false))
581#ifdef SDL_VIDEO_OPENGL_GLX
582 && (!_this->gl_data || X11_GL_UseEGL(_this))
583#endif
584 ) {
585 vinfo = X11_GLES_GetVisual(_this, display, screen, transparent);
586 } else
587#endif
588 {
589#ifdef SDL_VIDEO_OPENGL_GLX
590 vinfo = X11_GL_GetVisual(_this, display, screen, transparent);
591#endif
592 }
593
594 if (!vinfo) {
595 return false;
596 }
597 visual = vinfo->visual;
598 depth = vinfo->depth;
599 X11_XFree(vinfo);
600 } else
601#endif
602 {
603 visual = displaydata->visual;
604 depth = displaydata->depth;
605 }
606
607 xattr.override_redirect = ((window->flags & SDL_WINDOW_TOOLTIP) || (window->flags & SDL_WINDOW_POPUP_MENU) || force_override_redirect) ? True : False;
608 xattr.backing_store = NotUseful;
609 xattr.background_pixmap = None;
610 xattr.border_pixel = 0;
611
612 if (visual->class == DirectColor) {
613 XColor *colorcells;
614 int i;
615 int ncolors;
616 int rmax, gmax, bmax;
617 int rmask, gmask, bmask;
618 int rshift, gshift, bshift;
619
620 xattr.colormap =
621 X11_XCreateColormap(display, RootWindow(display, screen),
622 visual, AllocAll);
623
624 // If we can't create a colormap, then we must die
625 if (!xattr.colormap) {
626 return SDL_SetError("Could not create writable colormap");
627 }
628
629 // OK, we got a colormap, now fill it in as best as we can
630 colorcells = SDL_malloc(visual->map_entries * sizeof(XColor));
631 if (!colorcells) {
632 return false;
633 }
634 ncolors = visual->map_entries;
635 rmax = 0xffff;
636 gmax = 0xffff;
637 bmax = 0xffff;
638
639 rshift = 0;
640 rmask = visual->red_mask;
641 while (0 == (rmask & 1)) {
642 rshift++;
643 rmask >>= 1;
644 }
645
646 gshift = 0;
647 gmask = visual->green_mask;
648 while (0 == (gmask & 1)) {
649 gshift++;
650 gmask >>= 1;
651 }
652
653 bshift = 0;
654 bmask = visual->blue_mask;
655 while (0 == (bmask & 1)) {
656 bshift++;
657 bmask >>= 1;
658 }
659
660 // build the color table pixel values
661 for (i = 0; i < ncolors; i++) {
662 Uint32 red = (rmax * i) / (ncolors - 1);
663 Uint32 green = (gmax * i) / (ncolors - 1);
664 Uint32 blue = (bmax * i) / (ncolors - 1);
665
666 Uint32 rbits = (rmask * i) / (ncolors - 1);
667 Uint32 gbits = (gmask * i) / (ncolors - 1);
668 Uint32 bbits = (bmask * i) / (ncolors - 1);
669
670 Uint32 pix =
671 (rbits << rshift) | (gbits << gshift) | (bbits << bshift);
672
673 colorcells[i].pixel = pix;
674
675 colorcells[i].red = red;
676 colorcells[i].green = green;
677 colorcells[i].blue = blue;
678
679 colorcells[i].flags = DoRed | DoGreen | DoBlue;
680 }
681
682 X11_XStoreColors(display, xattr.colormap, colorcells, ncolors);
683
684 SDL_free(colorcells);
685 } else {
686 xattr.colormap =
687 X11_XCreateColormap(display, RootWindow(display, screen),
688 visual, AllocNone);
689 }
690
691 if (window->undefined_x && window->undefined_y &&
692 window->last_displayID == SDL_GetPrimaryDisplay()) {
693 undefined_position = true;
694 }
695
696 if (SDL_WINDOW_IS_POPUP(window)) {
697 X11_ConstrainPopup(window, false);
698 }
699 SDL_RelativeToGlobalForWindow(window,
700 window->floating.x, window->floating.y,
701 &win_x, &win_y);
702
703 /* Always create this with the window->floating.* fields; if we're creating a windowed mode window,
704 * that's fine. If we're creating a maximized or fullscreen window, the window manager will want to
705 * know these values so it can use them if we go _back_ to the base floating windowed mode. SDL manages
706 * migration to fullscreen after CreateSDLWindow returns, which will put all the SDL_Window fields and
707 * system state as expected.
708 */
709 w = X11_XCreateWindow(display, RootWindow(display, screen),
710 win_x, win_y, window->floating.w, window->floating.h,
711 0, depth, InputOutput, visual,
712 (CWOverrideRedirect | CWBackPixmap | CWBorderPixel |
713 CWBackingStore | CWColormap),
714 &xattr);
715 if (!w) {
716 return SDL_SetError("Couldn't create window");
717 }
718
719 /* Don't set the borderless flag if we're about to go fullscreen.
720 * This prevents the window manager from moving a full-screen borderless
721 * window to a different display before we actually go fullscreen.
722 */
723 if (!(window->pending_flags & SDL_WINDOW_FULLSCREEN)) {
724 SetWindowBordered(display, screen, w, !(window->flags & SDL_WINDOW_BORDERLESS));
725 }
726
727 sizehints = X11_XAllocSizeHints();
728 // Setup the normal size hints
729 sizehints->flags = 0;
730 if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
731 sizehints->min_width = sizehints->max_width = window->floating.w;
732 sizehints->min_height = sizehints->max_height = window->floating.h;
733 sizehints->flags |= (PMaxSize | PMinSize);
734 }
735 if (!undefined_position) {
736 sizehints->x = win_x;
737 sizehints->y = win_y;
738 sizehints->flags |= USPosition;
739 }
740
741 // Setup the input hints so we get keyboard input
742 wmhints = X11_XAllocWMHints();
743 wmhints->input = !(window->flags & SDL_WINDOW_NOT_FOCUSABLE) ? True : False;
744 wmhints->window_group = data->window_group;
745 wmhints->flags = InputHint | WindowGroupHint;
746
747 // Setup the class hints so we can get an icon (AfterStep)
748 classhints = X11_XAllocClassHint();
749 classhints->res_name = (char *)SDL_GetExeName();
750 classhints->res_class = (char *)SDL_GetAppID();
751
752 // Set the size, input and class hints, and define WM_CLIENT_MACHINE and WM_LOCALE_NAME
753 X11_XSetWMProperties(display, w, NULL, NULL, NULL, 0, sizehints, wmhints, classhints);
754
755 X11_XFree(sizehints);
756 X11_XFree(wmhints);
757 X11_XFree(classhints);
758 // Set the PID related to the window for the given hostname, if possible
759 if (data->pid > 0) {
760 long pid = (long)data->pid;
761 _NET_WM_PID = X11_XInternAtom(display, "_NET_WM_PID", False);
762 X11_XChangeProperty(display, w, _NET_WM_PID, XA_CARDINAL, 32, PropModeReplace,
763 (unsigned char *)&pid, 1);
764 }
765
766 // Set the window manager state
767 X11_SetNetWMState(_this, w, window->flags);
768
769 compositor = 2; // don't disable compositing except for "normal" windows
770 hint = SDL_GetHint(SDL_HINT_X11_WINDOW_TYPE);
771 if (window->flags & SDL_WINDOW_UTILITY) {
772 wintype_name = "_NET_WM_WINDOW_TYPE_UTILITY";
773 } else if (window->flags & SDL_WINDOW_TOOLTIP) {
774 wintype_name = "_NET_WM_WINDOW_TYPE_TOOLTIP";
775 } else if (window->flags & SDL_WINDOW_POPUP_MENU) {
776 wintype_name = "_NET_WM_WINDOW_TYPE_POPUP_MENU";
777 } else if (hint && *hint) {
778 wintype_name = hint;
779 } else {
780 wintype_name = "_NET_WM_WINDOW_TYPE_NORMAL";
781 compositor = 1; // disable compositing for "normal" windows
782 }
783
784 // Let the window manager know what type of window we are.
785 _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
786 wintype = X11_XInternAtom(display, wintype_name, False);
787 X11_XChangeProperty(display, w, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
788 PropModeReplace, (unsigned char *)&wintype, 1);
789 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, true)) {
790 _NET_WM_BYPASS_COMPOSITOR = X11_XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", False);
791 X11_XChangeProperty(display, w, _NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,
792 PropModeReplace,
793 (unsigned char *)&compositor, 1);
794 }
795
796 {
797 Atom protocols[4];
798 int proto_count = 0;
799
800 protocols[proto_count++] = data->atoms.WM_DELETE_WINDOW; // Allow window to be deleted by the WM
801 protocols[proto_count++] = data->atoms.WM_TAKE_FOCUS; // Since we will want to set input focus explicitly
802
803 // Default to using ping if there is no hint
804 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NET_WM_PING, true)) {
805 protocols[proto_count++] = data->atoms._NET_WM_PING; // Respond so WM knows we're alive
806 }
807
808#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
809 if (use_resize_sync) {
810 protocols[proto_count++] = data->atoms._NET_WM_SYNC_REQUEST; /* Respond after completing resize */
811 }
812#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
813
814 SDL_assert(proto_count <= sizeof(protocols) / sizeof(protocols[0]));
815
816 X11_XSetWMProtocols(display, w, protocols, proto_count);
817 }
818
819 if (!SetupWindowData(_this, window, w)) {
820 X11_XDestroyWindow(display, w);
821 return false;
822 }
823 windowdata = window->internal;
824
825 // Set the parent if this is a non-popup window.
826 if (!SDL_WINDOW_IS_POPUP(window) && window->parent) {
827 X11_XSetTransientForHint(display, w, window->parent->internal->xwindow);
828 }
829
830 // Set the flag if the borders were forced on when creating a fullscreen window for later removal.
831 windowdata->fullscreen_borders_forced_on = !!(window->pending_flags & SDL_WINDOW_FULLSCREEN) &&
832 !!(window->flags & SDL_WINDOW_BORDERLESS);
833
834#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
835 if (use_resize_sync) {
836 X11_InitResizeSync(window);
837 }
838#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
839
840#if defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2) || defined(SDL_VIDEO_OPENGL_EGL)
841 if ((window->flags & SDL_WINDOW_OPENGL) &&
842 ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) ||
843 SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false))
844#ifdef SDL_VIDEO_OPENGL_GLX
845 && (!_this->gl_data || X11_GL_UseEGL(_this))
846#endif
847 ) {
848#ifdef SDL_VIDEO_OPENGL_EGL
849 if (!_this->egl_data) {
850 return false;
851 }
852
853 // Create the GLES window surface
854 windowdata->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)w);
855
856 if (windowdata->egl_surface == EGL_NO_SURFACE) {
857 return SDL_SetError("Could not create GLES window surface");
858 }
859#else
860 return SDL_SetError("Could not create GLES window surface (EGL support not configured)");
861#endif // SDL_VIDEO_OPENGL_EGL
862 }
863#endif
864
865#ifdef SDL_VIDEO_DRIVER_X11_XSHAPE
866 // Tooltips do not receive input
867 if (window->flags & SDL_WINDOW_TOOLTIP) {
868 Region region = X11_XCreateRegion();
869 X11_XShapeCombineRegion(display, w, ShapeInput, 0, 0, region, ShapeSet);
870 X11_XDestroyRegion(region);
871 }
872#endif
873
874 SetupWindowInput(_this, window);
875
876 // For _ICC_PROFILE.
877 X11_XSelectInput(display, RootWindow(display, screen), PropertyChangeMask);
878
879 X11_XFlush(display);
880
881 return true;
882}
883
884char *X11_GetWindowTitle(SDL_VideoDevice *_this, Window xwindow)
885{
886 SDL_VideoData *data = _this->internal;
887 Display *display = data->display;
888 int status, real_format;
889 Atom real_type;
890 unsigned long items_read, items_left;
891 unsigned char *propdata;
892 char *title = NULL;
893
894 status = X11_XGetWindowProperty(display, xwindow, data->atoms._NET_WM_NAME,
895 0L, 8192L, False, data->atoms.UTF8_STRING, &real_type, &real_format,
896 &items_read, &items_left, &propdata);
897 if (status == Success && propdata) {
898 title = SDL_strdup(SDL_static_cast(char *, propdata));
899 X11_XFree(propdata);
900 } else {
901 status = X11_XGetWindowProperty(display, xwindow, XA_WM_NAME,
902 0L, 8192L, False, XA_STRING, &real_type, &real_format,
903 &items_read, &items_left, &propdata);
904 if (status == Success && propdata) {
905 title = SDL_iconv_string("UTF-8", "", SDL_static_cast(char *, propdata), items_read + 1);
906 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Failed to convert WM_NAME title expecting UTF8! Title: %s", title);
907 X11_XFree(propdata);
908 } else {
909 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Could not get any window title response from Xorg, returning empty string!");
910 title = SDL_strdup("");
911 }
912 }
913 return title;
914}
915
916void X11_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window)
917{
918 SDL_WindowData *data = window->internal;
919 Window xwindow = data->xwindow;
920 Display *display = data->videodata->display;
921 char *title = window->title ? window->title : "";
922
923 SDL_X11_SetWindowTitle(display, xwindow, title);
924}
925
926static bool caught_x11_error = false;
927static int X11_CatchAnyError(Display *d, XErrorEvent *e)
928{
929 /* this may happen during tumultuous times when we are polling anyhow,
930 so just note we had an error and return control. */
931 caught_x11_error = true;
932 return 0;
933}
934
935/* Wait a brief time, or not, to see if the window manager decided to move/resize the window.
936 * Send MOVED and RESIZED window events */
937static bool X11_SyncWindowTimeout(SDL_VideoDevice *_this, SDL_Window *window, Uint64 param_timeout)
938{
939 SDL_WindowData *data = window->internal;
940 Display *display = data->videodata->display;
941 int (*prev_handler)(Display *, XErrorEvent *);
942 Uint64 timeout = 0;
943 bool force_exit = false;
944 bool result = true;
945
946 X11_XSync(display, False);
947 prev_handler = X11_XSetErrorHandler(X11_CatchAnyError);
948
949 if (param_timeout) {
950 timeout = SDL_GetTicksNS() + param_timeout;
951 }
952
953 while (true) {
954 X11_XSync(display, False);
955 X11_PumpEvents(_this);
956
957 if ((data->pending_operation & X11_PENDING_OP_MOVE) && (window->x == data->expected.x + data->border_left && window->y == data->expected.y + data->border_top)) {
958 data->pending_operation &= ~X11_PENDING_OP_MOVE;
959 }
960 if ((data->pending_operation & X11_PENDING_OP_RESIZE) && (window->w == data->expected.w && window->h == data->expected.h)) {
961 data->pending_operation &= ~X11_PENDING_OP_RESIZE;
962 }
963
964 if (data->pending_operation == X11_PENDING_OP_NONE) {
965 if (force_exit ||
966 (window->x == data->expected.x + data->border_left && window->y == data->expected.y + data->border_top &&
967 window->w == data->expected.w && window->h == data->expected.h)) {
968 // The window is in the expected state and nothing is pending. Done.
969 break;
970 }
971
972 /* No operations are pending, but the window still isn't in the expected state.
973 * Try one more time before exiting.
974 */
975 force_exit = true;
976 }
977
978 if (SDL_GetTicksNS() >= timeout) {
979 // Timed out without the expected values. Update the requested data so future sync calls won't block.
980 data->expected.x = window->x;
981 data->expected.y = window->y;
982 data->expected.w = window->w;
983 data->expected.h = window->h;
984
985 result = false;
986 break;
987 }
988
989 SDL_Delay(10);
990 }
991
992 data->pending_operation = X11_PENDING_OP_NONE;
993
994 if (!caught_x11_error) {
995 X11_PumpEvents(_this);
996 } else {
997 result = false;
998 }
999
1000 X11_XSetErrorHandler(prev_handler);
1001 caught_x11_error = false;
1002
1003 return result;
1004}
1005
1006bool X11_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon)
1007{
1008 SDL_WindowData *data = window->internal;
1009 Display *display = data->videodata->display;
1010 Atom _NET_WM_ICON = data->videodata->atoms._NET_WM_ICON;
1011 int (*prevHandler)(Display *, XErrorEvent *) = NULL;
1012 bool result = true;
1013
1014 if (icon) {
1015 int x, y;
1016 int propsize;
1017 long *propdata;
1018 Uint32 *src;
1019 long *dst;
1020
1021 // Set the _NET_WM_ICON property
1022 SDL_assert(icon->format == SDL_PIXELFORMAT_ARGB8888);
1023 propsize = 2 + (icon->w * icon->h);
1024 propdata = SDL_malloc(propsize * sizeof(long));
1025
1026 if (!propdata) {
1027 return false;
1028 }
1029
1030 X11_XSync(display, False);
1031 prevHandler = X11_XSetErrorHandler(X11_CatchAnyError);
1032
1033 propdata[0] = icon->w;
1034 propdata[1] = icon->h;
1035 dst = &propdata[2];
1036
1037 for (y = 0; y < icon->h; ++y) {
1038 src = (Uint32 *)((Uint8 *)icon->pixels + y * icon->pitch);
1039 for (x = 0; x < icon->w; ++x) {
1040 *dst++ = *src++;
1041 }
1042 }
1043
1044 X11_XChangeProperty(display, data->xwindow, _NET_WM_ICON, XA_CARDINAL,
1045 32, PropModeReplace, (unsigned char *)propdata,
1046 propsize);
1047 SDL_free(propdata);
1048
1049 if (caught_x11_error) {
1050 result = SDL_SetError("An error occurred while trying to set the window's icon");
1051 }
1052 }
1053
1054 X11_XFlush(display);
1055
1056 if (prevHandler) {
1057 X11_XSetErrorHandler(prevHandler);
1058 caught_x11_error = false;
1059 }
1060
1061 return result;
1062}
1063
1064void X11_UpdateWindowPosition(SDL_Window *window, bool use_current_position)
1065{
1066 SDL_WindowData *data = window->internal;
1067 Display *display = data->videodata->display;
1068 const int rel_x = use_current_position ? window->x : window->pending.x;
1069 const int rel_y = use_current_position ? window->y : window->pending.y;
1070
1071 SDL_RelativeToGlobalForWindow(window,
1072 rel_x - data->border_left, rel_y - data->border_top,
1073 &data->expected.x, &data->expected.y);
1074
1075 // Attempt to move the window
1076 if (window->flags & SDL_WINDOW_HIDDEN) {
1077 window->internal->pending_position = true;
1078 } else {
1079 data->pending_operation |= X11_PENDING_OP_MOVE;
1080 X11_XMoveWindow(display, data->xwindow, data->expected.x, data->expected.y);
1081 }
1082}
1083
1084bool X11_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window)
1085{
1086 // Sync any pending fullscreen or maximize events.
1087 if (window->internal->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE)) {
1088 X11_FlushPendingEvents(_this, window);
1089 }
1090
1091 // Set the position as pending if the window is maximized with a restore pending.
1092 if (window->flags & SDL_WINDOW_MAXIMIZED) {
1093 if (window->internal->pending_operation & X11_PENDING_OP_RESTORE) {
1094 window->internal->pending_position = true;
1095 }
1096 return true;
1097 }
1098
1099 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
1100 if (SDL_WINDOW_IS_POPUP(window)) {
1101 X11_ConstrainPopup(window, true);
1102 }
1103 X11_UpdateWindowPosition(window, false);
1104 } else {
1105 SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_UPDATE, true);
1106 }
1107 return true;
1108}
1109
1110void X11_SetWindowMinMax(SDL_Window *window, bool use_current)
1111{
1112 SDL_WindowData *data = window->internal;
1113 Display *display = data->videodata->display;
1114 XSizeHints *sizehints = X11_XAllocSizeHints();
1115 long hint_flags = 0;
1116
1117 X11_XGetWMNormalHints(display, data->xwindow, sizehints, &hint_flags);
1118 sizehints->flags &= ~(PMinSize | PMaxSize | PAspect);
1119
1120 if (data->window->flags & SDL_WINDOW_RESIZABLE) {
1121 if (data->window->min_w || data->window->min_h) {
1122 sizehints->flags |= PMinSize;
1123 sizehints->min_width = data->window->min_w;
1124 sizehints->min_height = data->window->min_h;
1125 }
1126 if (data->window->max_w || data->window->max_h) {
1127 sizehints->flags |= PMaxSize;
1128 sizehints->max_width = data->window->max_w;
1129 sizehints->max_height = data->window->max_h;
1130 }
1131 if (data->window->min_aspect > 0.0f || data->window->max_aspect > 0.0f) {
1132 sizehints->flags |= PAspect;
1133 SDL_CalculateFraction(data->window->min_aspect, &sizehints->min_aspect.x, &sizehints->min_aspect.y);
1134 SDL_CalculateFraction(data->window->max_aspect, &sizehints->max_aspect.x, &sizehints->max_aspect.y);
1135 }
1136 } else {
1137 // Set the min/max to the same values to make the window non-resizable
1138 sizehints->flags |= PMinSize | PMaxSize;
1139 sizehints->min_width = sizehints->max_width = use_current ? data->window->floating.w : window->windowed.w;
1140 sizehints->min_height = sizehints->max_height = use_current ? data->window->floating.h : window->windowed.h;
1141 }
1142
1143 X11_XSetWMNormalHints(display, data->xwindow, sizehints);
1144 X11_XFree(sizehints);
1145}
1146
1147void X11_SetWindowMinimumSize(SDL_VideoDevice *_this, SDL_Window *window)
1148{
1149 if (window->internal->pending_operation & X11_PENDING_OP_FULLSCREEN) {
1150 X11_SyncWindow(_this, window);
1151 }
1152
1153 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
1154 X11_SetWindowMinMax(window, true);
1155 }
1156}
1157
1158void X11_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window)
1159{
1160 if (window->internal->pending_operation & X11_PENDING_OP_FULLSCREEN) {
1161 X11_SyncWindow(_this, window);
1162 }
1163
1164 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
1165 X11_SetWindowMinMax(window, true);
1166 }
1167}
1168
1169void X11_SetWindowAspectRatio(SDL_VideoDevice *_this, SDL_Window *window)
1170{
1171 if (window->internal->pending_operation & X11_PENDING_OP_FULLSCREEN) {
1172 X11_SyncWindow(_this, window);
1173 }
1174
1175 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
1176 X11_SetWindowMinMax(window, true);
1177 }
1178}
1179
1180void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
1181{
1182 SDL_WindowData *data = window->internal;
1183 Display *display = data->videodata->display;
1184
1185 /* Wait for pending maximize and fullscreen operations to complete, as these windows
1186 * don't get size changes.
1187 */
1188 if (data->pending_operation & (X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_FULLSCREEN)) {
1189 X11_FlushPendingEvents(_this, window);
1190 }
1191
1192 // Set the size as pending if the window is being restored.
1193 if (window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_FULLSCREEN)) {
1194 // New size will be set when the window is restored.
1195 if (data->pending_operation & X11_PENDING_OP_RESTORE) {
1196 data->pending_size = true;
1197 } else {
1198 // Can't resize the window.
1199 window->last_size_pending = false;
1200 }
1201 return;
1202 }
1203
1204 if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
1205 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
1206 /* Apparently, if the X11 Window is set to a 'non-resizable' window, you cannot resize it using the X11_XResizeWindow, thus
1207 * we must set the size hints to adjust the window size.
1208 */
1209 XSizeHints *sizehints = X11_XAllocSizeHints();
1210 long userhints;
1211 int dest_x, dest_y;
1212
1213 X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints);
1214
1215 sizehints->min_width = sizehints->max_width = window->pending.w;
1216 sizehints->min_height = sizehints->max_height = window->pending.h;
1217 sizehints->flags |= PMinSize | PMaxSize;
1218
1219 X11_XSetWMNormalHints(display, data->xwindow, sizehints);
1220
1221 /* From Pierre-Loup:
1222 WMs each have their little quirks with that. When you change the
1223 size hints, they get a ConfigureNotify event with the
1224 WM_NORMAL_SIZE_HINTS Atom. They all save the hints then, but they
1225 don't all resize the window right away to enforce the new hints.
1226
1227 Some of them resize only after:
1228 - A user-initiated move or resize
1229 - A code-initiated move or resize
1230 - Hiding & showing window (Unmap & map)
1231
1232 The following move & resize seems to help a lot of WMs that didn't
1233 properly update after the hints were changed. We don't do a
1234 hide/show, because there are supposedly subtle problems with doing so
1235 and transitioning from windowed to fullscreen in Unity.
1236 */
1237 X11_XResizeWindow(display, data->xwindow, window->pending.w, window->pending.h);
1238 const int x = window->last_position_pending ? window->pending.x : window->x;
1239 const int y = window->last_position_pending ? window->pending.y : window->y;
1240 SDL_RelativeToGlobalForWindow(window,
1241 x - data->border_left,
1242 y - data->border_top,
1243 &dest_x, &dest_y);
1244 X11_XMoveWindow(display, data->xwindow, dest_x, dest_y);
1245 X11_XRaiseWindow(display, data->xwindow);
1246
1247 X11_XFree(sizehints);
1248 }
1249 } else {
1250 data->expected.w = window->pending.w;
1251 data->expected.h = window->pending.h;
1252 data->pending_operation |= X11_PENDING_OP_RESIZE;
1253 X11_XResizeWindow(display, data->xwindow, data->expected.w, data->expected.h);
1254 }
1255}
1256
1257bool X11_GetWindowBordersSize(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right)
1258{
1259 SDL_WindowData *data = window->internal;
1260
1261 *left = data->border_left;
1262 *right = data->border_right;
1263 *top = data->border_top;
1264 *bottom = data->border_bottom;
1265
1266 return true;
1267}
1268
1269bool X11_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity)
1270{
1271 SDL_WindowData *data = window->internal;
1272 Display *display = data->videodata->display;
1273 Atom _NET_WM_WINDOW_OPACITY = data->videodata->atoms._NET_WM_WINDOW_OPACITY;
1274
1275 if (opacity == 1.0f) {
1276 X11_XDeleteProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY);
1277 } else {
1278 const Uint32 FullyOpaque = 0xFFFFFFFF;
1279 const long alpha = (long)((double)opacity * (double)FullyOpaque);
1280 X11_XChangeProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
1281 PropModeReplace, (unsigned char *)&alpha, 1);
1282 }
1283
1284 return true;
1285}
1286
1287bool X11_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent)
1288{
1289 SDL_WindowData *data = window->internal;
1290 SDL_WindowData *parent_data = parent ? parent->internal : NULL;
1291 SDL_VideoData *video_data = _this->internal;
1292 Display *display = video_data->display;
1293
1294 if (parent_data) {
1295 X11_XSetTransientForHint(display, data->xwindow, parent_data->xwindow);
1296 } else {
1297 X11_XDeleteProperty(display, data->xwindow, video_data->atoms.WM_TRANSIENT_FOR);
1298 }
1299
1300 return true;
1301}
1302
1303bool X11_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal)
1304{
1305 SDL_WindowData *data = window->internal;
1306 SDL_VideoData *video_data = _this->internal;
1307 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
1308 Display *display = video_data->display;
1309 Uint32 flags = window->flags;
1310 Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE;
1311 Atom _NET_WM_STATE_MODAL = data->videodata->atoms._NET_WM_STATE_MODAL;
1312
1313 if (modal) {
1314 flags |= SDL_WINDOW_MODAL;
1315 } else {
1316 flags &= ~SDL_WINDOW_MODAL;
1317 X11_XDeleteProperty(display, data->xwindow, video_data->atoms.WM_TRANSIENT_FOR);
1318 }
1319
1320 if (X11_IsWindowMapped(_this, window)) {
1321 XEvent e;
1322
1323 SDL_zero(e);
1324 e.xany.type = ClientMessage;
1325 e.xclient.message_type = _NET_WM_STATE;
1326 e.xclient.format = 32;
1327 e.xclient.window = data->xwindow;
1328 e.xclient.data.l[0] = modal ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
1329 e.xclient.data.l[1] = _NET_WM_STATE_MODAL;
1330 e.xclient.data.l[3] = 0l;
1331
1332 X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
1333 SubstructureNotifyMask | SubstructureRedirectMask, &e);
1334 } else {
1335 X11_SetNetWMState(_this, data->xwindow, flags);
1336 }
1337
1338 X11_XFlush(display);
1339
1340 return true;
1341}
1342
1343void X11_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, bool bordered)
1344{
1345 const bool focused = (window->flags & SDL_WINDOW_INPUT_FOCUS) ? true : false;
1346 const bool visible = (!(window->flags & SDL_WINDOW_HIDDEN)) ? true : false;
1347 SDL_WindowData *data = window->internal;
1348 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
1349 Display *display = data->videodata->display;
1350 XEvent event;
1351
1352 if (data->pending_operation & X11_PENDING_OP_FULLSCREEN) {
1353 X11_SyncWindow(_this, window);
1354 }
1355
1356 // If the window is fullscreen, the resize capability will be set/cleared when it is returned to windowed mode.
1357 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
1358 SetWindowBordered(display, displaydata->screen, data->xwindow, bordered);
1359 X11_XFlush(display);
1360
1361 if (visible) {
1362 XWindowAttributes attr;
1363 do {
1364 X11_XSync(display, False);
1365 X11_XGetWindowAttributes(display, data->xwindow, &attr);
1366 } while (attr.map_state != IsViewable);
1367
1368 if (focused) {
1369 X11_XSetInputFocus(display, data->xwindow, RevertToParent, CurrentTime);
1370 }
1371 }
1372
1373 // make sure these don't make it to the real event queue if they fired here.
1374 X11_XSync(display, False);
1375 X11_XCheckIfEvent(display, &event, &isUnmapNotify, (XPointer)&data->xwindow);
1376 X11_XCheckIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow);
1377
1378 // Turning the borders off doesn't send an extent event, so they must be cleared here.
1379 X11_GetBorderValues(data);
1380
1381 // Make sure the window manager didn't resize our window for the difference.
1382 X11_XResizeWindow(display, data->xwindow, window->floating.w, window->floating.h);
1383 X11_XSync(display, False);
1384 } else {
1385 // If fullscreen, set a flag to toggle the borders when returning to windowed mode.
1386 data->toggle_borders = true;
1387 data->fullscreen_borders_forced_on = false;
1388 }
1389}
1390
1391void X11_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, bool resizable)
1392{
1393 SDL_WindowData *data = window->internal;
1394
1395 if (data->pending_operation & X11_PENDING_OP_FULLSCREEN) {
1396 X11_SyncWindow(_this, window);
1397 }
1398
1399 // If the window is fullscreen, the resize capability will be set/cleared when it is returned to windowed mode.
1400 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
1401 X11_SetWindowMinMax(window, true);
1402 }
1403}
1404
1405void X11_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, bool on_top)
1406{
1407 SDL_WindowData *data = window->internal;
1408 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
1409 Display *display = data->videodata->display;
1410 Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE;
1411 Atom _NET_WM_STATE_ABOVE = data->videodata->atoms._NET_WM_STATE_ABOVE;
1412
1413 if (X11_IsWindowMapped(_this, window)) {
1414 XEvent e;
1415
1416 SDL_zero(e);
1417 e.xany.type = ClientMessage;
1418 e.xclient.message_type = _NET_WM_STATE;
1419 e.xclient.format = 32;
1420 e.xclient.window = data->xwindow;
1421 e.xclient.data.l[0] =
1422 on_top ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
1423 e.xclient.data.l[1] = _NET_WM_STATE_ABOVE;
1424 e.xclient.data.l[3] = 0l;
1425
1426 X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
1427 SubstructureNotifyMask | SubstructureRedirectMask, &e);
1428 } else {
1429 X11_SetNetWMState(_this, data->xwindow, window->flags);
1430 }
1431 X11_XFlush(display);
1432}
1433
1434void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
1435{
1436 SDL_WindowData *data = window->internal;
1437 Display *display = data->videodata->display;
1438 bool bActivate = SDL_GetHintBoolean(SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN, true);
1439 bool position_is_absolute = false;
1440 bool set_position = false;
1441 XEvent event;
1442
1443 if (SDL_WINDOW_IS_POPUP(window)) {
1444 // Update the position in case the parent moved while we were hidden
1445 X11_ConstrainPopup(window, true);
1446 data->pending_position = true;
1447
1448 // Coordinates after X11_ConstrainPopup() are already in the global space.
1449 position_is_absolute = true;
1450 set_position = true;
1451 }
1452
1453 /* Whether XMapRaised focuses the window is based on the window type and it is
1454 * wm specific. There isn't much we can do here */
1455 (void)bActivate;
1456
1457 if (!X11_IsWindowMapped(_this, window)) {
1458 X11_XMapRaised(display, data->xwindow);
1459 /* Blocking wait for "MapNotify" event.
1460 * We use X11_XIfEvent because pXWindowEvent takes a mask rather than a type,
1461 * and XCheckTypedWindowEvent doesn't block */
1462 if (!(window->flags & SDL_WINDOW_EXTERNAL) && X11_IsDisplayOk(display)) {
1463 X11_XIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow);
1464 }
1465 X11_XFlush(display);
1466 set_position = data->pending_position ||
1467 (!(window->flags & SDL_WINDOW_BORDERLESS) && !window->undefined_x && !window->undefined_y);
1468 }
1469
1470 if (!data->videodata->net_wm) {
1471 // no WM means no FocusIn event, which confuses us. Force it.
1472 X11_XSync(display, False);
1473 X11_XSetInputFocus(display, data->xwindow, RevertToNone, CurrentTime);
1474 X11_XFlush(display);
1475 }
1476
1477 // Popup menus grab the keyboard
1478 if (window->flags & SDL_WINDOW_POPUP_MENU) {
1479 X11_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
1480 }
1481
1482 // Get some valid border values, if we haven't received them yet
1483 if (data->border_left == 0 && data->border_right == 0 && data->border_top == 0 && data->border_bottom == 0) {
1484 X11_GetBorderValues(data);
1485 }
1486
1487 if (set_position) {
1488 // Apply the window position, accounting for offsets due to the borders appearing.
1489 const int tx = data->pending_position ? window->pending.x : window->x;
1490 const int ty = data->pending_position ? window->pending.y : window->y;
1491 int x, y;
1492 if (position_is_absolute) {
1493 x = tx;
1494 y = ty;
1495 } else {
1496 SDL_RelativeToGlobalForWindow(window,
1497 tx - data->border_left, ty - data->border_top,
1498 &x, &y);
1499 }
1500 data->pending_position = false;
1501 X11_XMoveWindow(display, data->xwindow, x, y);
1502 }
1503
1504 /* Some window managers can send garbage coordinates while mapping the window, so don't emit size and position
1505 * events during the initial configure events.
1506 */
1507 data->size_move_event_flags = X11_SIZE_MOVE_EVENTS_DISABLE;
1508 X11_XSync(display, False);
1509 X11_PumpEvents(_this);
1510 data->size_move_event_flags = 0;
1511
1512 // If a configure event was received (type is non-zero), send the final window size and coordinates.
1513 if (data->last_xconfigure.type) {
1514 int x, y;
1515 SDL_GlobalToRelativeForWindow(data->window, data->last_xconfigure.x, data->last_xconfigure.y, &x, &y);
1516 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, data->last_xconfigure.width, data->last_xconfigure.height);
1517 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y);
1518 }
1519}
1520
1521void X11_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
1522{
1523 SDL_WindowData *data = window->internal;
1524 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
1525 int screen = (displaydata ? displaydata->screen : 0);
1526 Display *display = data->videodata->display;
1527 XEvent event;
1528
1529 if (X11_IsWindowMapped(_this, window)) {
1530 X11_XWithdrawWindow(display, data->xwindow, screen);
1531 // Blocking wait for "UnmapNotify" event
1532 if (!(window->flags & SDL_WINDOW_EXTERNAL) && X11_IsDisplayOk(display)) {
1533 X11_XIfEvent(display, &event, &isUnmapNotify, (XPointer)&data->xwindow);
1534 }
1535 X11_XFlush(display);
1536 }
1537
1538 // Transfer keyboard focus back to the parent
1539 if (window->flags & SDL_WINDOW_POPUP_MENU) {
1540 SDL_Window *new_focus = window->parent;
1541 bool set_focus = window == SDL_GetKeyboardFocus();
1542
1543 // Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed.
1544 while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) {
1545 new_focus = new_focus->parent;
1546
1547 // If some window in the chain currently had focus, set it to the new lowest-level window.
1548 if (!set_focus) {
1549 set_focus = new_focus == SDL_GetKeyboardFocus();
1550 }
1551 }
1552
1553 X11_SetKeyboardFocus(new_focus, set_focus);
1554 }
1555
1556 X11_XSync(display, False);
1557 X11_PumpEvents(_this);
1558}
1559
1560static bool X11_SetWindowActive(SDL_VideoDevice *_this, SDL_Window *window)
1561{
1562 CHECK_WINDOW_DATA(window);
1563
1564 SDL_WindowData *data = window->internal;
1565 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
1566 Display *display = data->videodata->display;
1567 Atom _NET_ACTIVE_WINDOW = data->videodata->atoms._NET_ACTIVE_WINDOW;
1568
1569 if (X11_IsWindowMapped(_this, window)) {
1570 XEvent e;
1571
1572 // printf("SDL Window %p: sending _NET_ACTIVE_WINDOW with timestamp %lu\n", window, data->user_time);
1573
1574 SDL_zero(e);
1575 e.xany.type = ClientMessage;
1576 e.xclient.message_type = _NET_ACTIVE_WINDOW;
1577 e.xclient.format = 32;
1578 e.xclient.window = data->xwindow;
1579 e.xclient.data.l[0] = 1; // source indication. 1 = application
1580 e.xclient.data.l[1] = data->user_time;
1581 e.xclient.data.l[2] = 0;
1582
1583 X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
1584 SubstructureNotifyMask | SubstructureRedirectMask, &e);
1585
1586 X11_XFlush(display);
1587 }
1588 return true;
1589}
1590
1591void X11_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window)
1592{
1593 SDL_WindowData *data = window->internal;
1594 Display *display = data->videodata->display;
1595 bool bActivate = SDL_GetHintBoolean(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, true);
1596
1597 X11_XRaiseWindow(display, data->xwindow);
1598 if (bActivate) {
1599 X11_SetWindowActive(_this, window);
1600 }
1601 X11_XFlush(display);
1602}
1603
1604static bool X11_SetWindowMaximized(SDL_VideoDevice *_this, SDL_Window *window, bool maximized)
1605{
1606 CHECK_WINDOW_DATA(window);
1607
1608 SDL_WindowData *data = window->internal;
1609 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
1610 Display *display = data->videodata->display;
1611 Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE;
1612 Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT;
1613 Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ;
1614
1615 if (window->flags & SDL_WINDOW_FULLSCREEN) {
1616 /* Fullscreen windows are maximized on some window managers,
1617 and this is functional behavior, so don't remove that state
1618 now, we'll take care of it when we leave fullscreen mode.
1619 */
1620 return true;
1621 }
1622
1623 if (X11_IsWindowMapped(_this, window)) {
1624 XEvent e;
1625
1626 SDL_zero(e);
1627 e.xany.type = ClientMessage;
1628 e.xclient.message_type = _NET_WM_STATE;
1629 e.xclient.format = 32;
1630 e.xclient.window = data->xwindow;
1631 e.xclient.data.l[0] =
1632 maximized ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
1633 e.xclient.data.l[1] = _NET_WM_STATE_MAXIMIZED_VERT;
1634 e.xclient.data.l[2] = _NET_WM_STATE_MAXIMIZED_HORZ;
1635 e.xclient.data.l[3] = 0l;
1636
1637 if (maximized) {
1638 SDL_DisplayID displayID = SDL_GetDisplayForWindow(window);
1639 SDL_Rect bounds;
1640
1641 SDL_zero(bounds);
1642 SDL_GetDisplayUsableBounds(displayID, &bounds);
1643
1644 data->expected.x = bounds.x + data->border_left;
1645 data->expected.y = bounds.y + data->border_top;
1646 data->expected.w = bounds.w - (data->border_left + data->border_right);
1647 data->expected.h = bounds.h - (data->border_top + data->border_bottom);
1648 } else {
1649 data->expected.x = window->floating.x;
1650 data->expected.y = window->floating.y;
1651 data->expected.w = window->floating.w;
1652 data->expected.h = window->floating.h;
1653 }
1654
1655 X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
1656 SubstructureNotifyMask | SubstructureRedirectMask, &e);
1657 } else {
1658 X11_SetNetWMState(_this, data->xwindow, window->flags);
1659 }
1660 X11_XFlush(display);
1661
1662 return true;
1663}
1664
1665void X11_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
1666{
1667 if (window->internal->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MINIMIZE)) {
1668 SDL_SyncWindow(window);
1669 }
1670
1671 if (window->flags & SDL_WINDOW_FULLSCREEN) {
1672 // If fullscreen, just toggle the restored state.
1673 window->internal->window_was_maximized = true;
1674 return;
1675 }
1676
1677 if (!(window->flags & SDL_WINDOW_MINIMIZED)) {
1678 window->internal->pending_operation |= X11_PENDING_OP_MAXIMIZE;
1679 X11_SetWindowMaximized(_this, window, true);
1680 }
1681}
1682
1683void X11_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
1684{
1685 SDL_WindowData *data = window->internal;
1686 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
1687 Display *display = data->videodata->display;
1688
1689 if (data->pending_operation & SDL_WINDOW_FULLSCREEN) {
1690 SDL_SyncWindow(window);
1691 }
1692
1693 data->pending_operation |= X11_PENDING_OP_MINIMIZE;
1694 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
1695 data->window_was_maximized = !!(window->flags & SDL_WINDOW_MAXIMIZED);
1696 }
1697 X11_XIconifyWindow(display, data->xwindow, displaydata->screen);
1698 X11_XFlush(display);
1699}
1700
1701void X11_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
1702{
1703 if (window->internal->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_MINIMIZE)) {
1704 SDL_SyncWindow(window);
1705 }
1706
1707 if ((window->flags & SDL_WINDOW_FULLSCREEN) && !(window->flags & SDL_WINDOW_MINIMIZED)) {
1708 // If fullscreen and not minimized, just toggle the restored state.
1709 window->internal->window_was_maximized = false;
1710 return;
1711 }
1712
1713 if (window->flags & (SDL_WINDOW_MINIMIZED | SDL_WINDOW_MAXIMIZED) ||
1714 (window->internal->pending_operation & X11_PENDING_OP_MINIMIZE)) {
1715 window->internal->pending_operation |= X11_PENDING_OP_RESTORE;
1716 }
1717
1718 // If the window was minimized while maximized, restore as maximized.
1719 const bool maximize = !!(window->flags & SDL_WINDOW_MINIMIZED) && window->internal->window_was_maximized;
1720 X11_SetWindowMaximized(_this, window, maximize);
1721 X11_ShowWindow(_this, window);
1722 X11_SetWindowActive(_this, window);
1723}
1724
1725// This asks the Window Manager to handle fullscreen for us. This is the modern way.
1726static SDL_FullscreenResult X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *_display, SDL_FullscreenOp fullscreen)
1727{
1728 CHECK_WINDOW_DATA(window);
1729 CHECK_DISPLAY_DATA(_display);
1730
1731 SDL_WindowData *data = window->internal;
1732 SDL_DisplayData *displaydata = _display->internal;
1733 Display *display = data->videodata->display;
1734 Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE;
1735 Atom _NET_WM_STATE_FULLSCREEN = data->videodata->atoms._NET_WM_STATE_FULLSCREEN;
1736
1737 if (X11_IsWindowMapped(_this, window)) {
1738 XEvent e;
1739
1740 // Flush any pending fullscreen events.
1741 if (data->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_MOVE)) {
1742 X11_SyncWindow(_this, window);
1743 }
1744
1745 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
1746 if (fullscreen == SDL_FULLSCREEN_OP_UPDATE) {
1747 // Request was out of date; set -1 to signal the video core to undo a mode switch.
1748 return SDL_FULLSCREEN_FAILED;
1749 } else if (fullscreen == SDL_FULLSCREEN_OP_LEAVE) {
1750 // Nothing to do.
1751 return SDL_FULLSCREEN_SUCCEEDED;
1752 }
1753 }
1754
1755 if (fullscreen && !(window->flags & SDL_WINDOW_RESIZABLE)) {
1756 /* Compiz refuses fullscreen toggle if we're not resizable, so update the hints so we
1757 can be resized to the fullscreen resolution (or reset so we're not resizable again) */
1758 XSizeHints *sizehints = X11_XAllocSizeHints();
1759 long flags = 0;
1760 X11_XGetWMNormalHints(display, data->xwindow, sizehints, &flags);
1761 // we are going fullscreen so turn the flags off
1762 sizehints->flags &= ~(PMinSize | PMaxSize | PAspect);
1763 X11_XSetWMNormalHints(display, data->xwindow, sizehints);
1764 X11_XFree(sizehints);
1765 }
1766
1767 SDL_zero(e);
1768 e.xany.type = ClientMessage;
1769 e.xclient.message_type = _NET_WM_STATE;
1770 e.xclient.format = 32;
1771 e.xclient.window = data->xwindow;
1772 e.xclient.data.l[0] =
1773 fullscreen ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
1774 e.xclient.data.l[1] = _NET_WM_STATE_FULLSCREEN;
1775 e.xclient.data.l[3] = 0l;
1776
1777 X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
1778 SubstructureNotifyMask | SubstructureRedirectMask, &e);
1779
1780 if (!!(window->flags & SDL_WINDOW_FULLSCREEN) != fullscreen) {
1781 data->pending_operation |= X11_PENDING_OP_FULLSCREEN;
1782 }
1783
1784 // Set the position so the window will be on the target display
1785 if (fullscreen) {
1786 SDL_DisplayID current = SDL_GetDisplayForWindowPosition(window);
1787 SDL_copyp(&data->requested_fullscreen_mode, &window->current_fullscreen_mode);
1788 if (fullscreen != !!(window->flags & SDL_WINDOW_FULLSCREEN)) {
1789 data->window_was_maximized = !!(window->flags & SDL_WINDOW_MAXIMIZED);
1790 }
1791 data->expected.x = displaydata->x;
1792 data->expected.y = displaydata->y;
1793 data->expected.w = _display->current_mode->w;
1794 data->expected.h = _display->current_mode->h;
1795
1796 // Only move the window if it isn't fullscreen or already on the target display.
1797 if (!(window->flags & SDL_WINDOW_FULLSCREEN) || (!current || current != _display->id)) {
1798 X11_XMoveWindow(display, data->xwindow, displaydata->x, displaydata->y);
1799 data->pending_operation |= X11_PENDING_OP_MOVE;
1800 }
1801 } else {
1802 SDL_zero(data->requested_fullscreen_mode);
1803
1804 /* Fullscreen windows sometimes end up being marked maximized by
1805 * window managers. Force it back to how we expect it to be.
1806 */
1807 SDL_zero(e);
1808 e.xany.type = ClientMessage;
1809 e.xclient.message_type = _NET_WM_STATE;
1810 e.xclient.format = 32;
1811 e.xclient.window = data->xwindow;
1812 if (data->window_was_maximized) {
1813 e.xclient.data.l[0] = _NET_WM_STATE_ADD;
1814 } else {
1815 e.xclient.data.l[0] = _NET_WM_STATE_REMOVE;
1816 }
1817 e.xclient.data.l[1] = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT;
1818 e.xclient.data.l[2] = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ;
1819 e.xclient.data.l[3] = 0l;
1820 X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
1821 SubstructureNotifyMask | SubstructureRedirectMask, &e);
1822 }
1823 } else {
1824 SDL_WindowFlags flags;
1825
1826 flags = window->flags;
1827 if (fullscreen) {
1828 flags |= SDL_WINDOW_FULLSCREEN;
1829 } else {
1830 flags &= ~SDL_WINDOW_FULLSCREEN;
1831 }
1832 X11_SetNetWMState(_this, data->xwindow, flags);
1833 }
1834
1835 if (data->visual->class == DirectColor) {
1836 if (fullscreen) {
1837 X11_XInstallColormap(display, data->colormap);
1838 } else {
1839 X11_XUninstallColormap(display, data->colormap);
1840 }
1841 }
1842
1843 return SDL_FULLSCREEN_PENDING;
1844}
1845
1846SDL_FullscreenResult X11_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *_display, SDL_FullscreenOp fullscreen)
1847{
1848 return X11_SetWindowFullscreenViaWM(_this, window, _display, fullscreen);
1849}
1850
1851typedef struct
1852{
1853 unsigned char *data;
1854 int format, count;
1855 Atom type;
1856} SDL_x11Prop;
1857
1858/* Reads property
1859 Must call X11_XFree on results
1860 */
1861static void X11_ReadProperty(SDL_x11Prop *p, Display *disp, Window w, Atom prop)
1862{
1863 unsigned char *ret = NULL;
1864 Atom type;
1865 int fmt;
1866 unsigned long count;
1867 unsigned long bytes_left;
1868 int bytes_fetch = 0;
1869
1870 do {
1871 if (ret) {
1872 X11_XFree(ret);
1873 }
1874 X11_XGetWindowProperty(disp, w, prop, 0, bytes_fetch, False, AnyPropertyType, &type, &fmt, &count, &bytes_left, &ret);
1875 bytes_fetch += bytes_left;
1876 } while (bytes_left != 0);
1877
1878 p->data = ret;
1879 p->format = fmt;
1880 p->count = count;
1881 p->type = type;
1882}
1883
1884void *X11_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size)
1885{
1886 SDL_WindowData *data = window->internal;
1887 Display *display = data->videodata->display;
1888 XWindowAttributes attributes;
1889 Atom icc_profile_atom;
1890 char icc_atom_string[sizeof("_ICC_PROFILE_") + 12];
1891 void *ret_icc_profile_data = NULL;
1892 CARD8 *icc_profile_data;
1893 int real_format;
1894 unsigned long real_nitems;
1895 SDL_x11Prop atomProp;
1896
1897 X11_XGetWindowAttributes(display, data->xwindow, &attributes);
1898 if (X11_XScreenNumberOfScreen(attributes.screen) > 0) {
1899 (void)SDL_snprintf(icc_atom_string, sizeof("_ICC_PROFILE_") + 12, "%s%d", "_ICC_PROFILE_", X11_XScreenNumberOfScreen(attributes.screen));
1900 } else {
1901 SDL_strlcpy(icc_atom_string, "_ICC_PROFILE", sizeof("_ICC_PROFILE"));
1902 }
1903 X11_XGetWindowAttributes(display, RootWindowOfScreen(attributes.screen), &attributes);
1904
1905 icc_profile_atom = X11_XInternAtom(display, icc_atom_string, True);
1906 if (icc_profile_atom == None) {
1907 SDL_SetError("Screen is not calibrated.");
1908 return NULL;
1909 }
1910
1911 X11_ReadProperty(&atomProp, display, RootWindowOfScreen(attributes.screen), icc_profile_atom);
1912 real_format = atomProp.format;
1913 real_nitems = atomProp.count;
1914 icc_profile_data = atomProp.data;
1915 if (real_format == None) {
1916 SDL_SetError("Screen is not calibrated.");
1917 return NULL;
1918 }
1919
1920 ret_icc_profile_data = SDL_malloc(real_nitems);
1921 if (!ret_icc_profile_data) {
1922 return NULL;
1923 }
1924
1925 SDL_memcpy(ret_icc_profile_data, icc_profile_data, real_nitems);
1926 *size = real_nitems;
1927 X11_XFree(icc_profile_data);
1928
1929 return ret_icc_profile_data;
1930}
1931
1932bool X11_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed)
1933{
1934 SDL_WindowData *data = window->internal;
1935 Display *display;
1936
1937 if (!data) {
1938 return SDL_SetError("Invalid window data");
1939 }
1940 data->mouse_grabbed = false;
1941
1942 display = data->videodata->display;
1943
1944 if (grabbed) {
1945 /* If the window is unmapped, XGrab calls return GrabNotViewable,
1946 so when we get a MapNotify later, we'll try to update the grab as
1947 appropriate. */
1948 if (window->flags & SDL_WINDOW_HIDDEN) {
1949 return true;
1950 }
1951
1952 /* If XInput2 is enabled, it will grab the pointer on button presses,
1953 * which results in XGrabPointer returning AlreadyGrabbed. If buttons
1954 * are currently pressed, clear any existing grabs before attempting
1955 * the confinement grab.
1956 */
1957 if (data->xinput2_mouse_enabled && SDL_GetMouseState(NULL, NULL)) {
1958 X11_XUngrabPointer(display, CurrentTime);
1959 }
1960
1961 // Try to grab the mouse
1962 if (!data->videodata->broken_pointer_grab) {
1963 const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
1964 int attempts;
1965 int result = 0;
1966
1967 // Try for up to 5000ms (5s) to grab. If it still fails, stop trying.
1968 for (attempts = 0; attempts < 100; attempts++) {
1969 result = X11_XGrabPointer(display, data->xwindow, False, mask, GrabModeAsync,
1970 GrabModeAsync, data->xwindow, None, CurrentTime);
1971 if (result == GrabSuccess) {
1972 data->mouse_grabbed = true;
1973 break;
1974 }
1975 SDL_Delay(50);
1976 }
1977
1978 if (result != GrabSuccess) {
1979 data->videodata->broken_pointer_grab = true; // don't try again.
1980 }
1981 }
1982
1983 X11_Xinput2GrabTouch(_this, window);
1984
1985 // Raise the window if we grab the mouse
1986 X11_XRaiseWindow(display, data->xwindow);
1987 } else {
1988 X11_XUngrabPointer(display, CurrentTime);
1989
1990 X11_Xinput2UngrabTouch(_this, window);
1991 }
1992 X11_XSync(display, False);
1993
1994 if (!data->videodata->broken_pointer_grab) {
1995 return true;
1996 } else {
1997 return SDL_SetError("The X server refused to let us grab the mouse. You might experience input bugs.");
1998 }
1999}
2000
2001bool X11_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed)
2002{
2003 SDL_WindowData *data = window->internal;
2004 Display *display;
2005
2006 if (!data) {
2007 return SDL_SetError("Invalid window data");
2008 }
2009
2010 display = data->videodata->display;
2011
2012 if (grabbed) {
2013 /* If the window is unmapped, XGrab calls return GrabNotViewable,
2014 so when we get a MapNotify later, we'll try to update the grab as
2015 appropriate. */
2016 if (window->flags & SDL_WINDOW_HIDDEN) {
2017 return true;
2018 }
2019
2020 X11_XGrabKeyboard(display, data->xwindow, True, GrabModeAsync,
2021 GrabModeAsync, CurrentTime);
2022 } else {
2023 X11_XUngrabKeyboard(display, CurrentTime);
2024 }
2025 X11_XSync(display, False);
2026
2027 return true;
2028}
2029
2030void X11_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
2031{
2032 SDL_WindowData *data = window->internal;
2033
2034 if (data) {
2035 SDL_VideoData *videodata = data->videodata;
2036 Display *display = videodata->display;
2037 int numwindows = videodata->numwindows;
2038 SDL_WindowData **windowlist = videodata->windowlist;
2039 int i;
2040
2041 if (windowlist) {
2042 for (i = 0; i < numwindows; ++i) {
2043 if (windowlist[i] && (windowlist[i]->window == window)) {
2044 windowlist[i] = windowlist[numwindows - 1];
2045 windowlist[numwindows - 1] = NULL;
2046 videodata->numwindows--;
2047 break;
2048 }
2049 }
2050 }
2051#ifdef X_HAVE_UTF8_STRING
2052 if (data->ic) {
2053 X11_XDestroyIC(data->ic);
2054 SDL_free(data->preedit_text);
2055 SDL_free(data->preedit_feedback);
2056 }
2057#endif
2058
2059#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
2060 X11_TermResizeSync(window);
2061#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
2062
2063 if (!(window->flags & SDL_WINDOW_EXTERNAL)) {
2064 X11_XDestroyWindow(display, data->xwindow);
2065 X11_XFlush(display);
2066 }
2067 SDL_free(data);
2068
2069#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
2070 // If the pointer barriers are active for this, deactivate it.
2071 if (videodata->active_cursor_confined_window == window) {
2072 X11_DestroyPointerBarrier(_this, window);
2073 }
2074#endif // SDL_VIDEO_DRIVER_X11_XFIXES
2075 }
2076 window->internal = NULL;
2077}
2078
2079bool X11_SetWindowHitTest(SDL_Window *window, bool enabled)
2080{
2081 return true; // just succeed, the real work is done elsewhere.
2082}
2083
2084void X11_AcceptDragAndDrop(SDL_Window *window, bool accept)
2085{
2086 SDL_WindowData *data = window->internal;
2087 Display *display = data->videodata->display;
2088 Atom XdndAware = data->videodata->atoms.XdndAware;
2089
2090 if (accept) {
2091 Atom xdnd_version = 5;
2092 X11_XChangeProperty(display, data->xwindow, XdndAware, XA_ATOM, 32,
2093 PropModeReplace, (unsigned char *)&xdnd_version, 1);
2094 } else {
2095 X11_XDeleteProperty(display, data->xwindow, XdndAware);
2096 }
2097}
2098
2099bool X11_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation)
2100{
2101 SDL_WindowData *data = window->internal;
2102 Display *display = data->videodata->display;
2103 XWMHints *wmhints;
2104
2105 wmhints = X11_XGetWMHints(display, data->xwindow);
2106 if (!wmhints) {
2107 return SDL_SetError("Couldn't get WM hints");
2108 }
2109
2110 wmhints->flags &= ~XUrgencyHint;
2111 data->flashing_window = false;
2112 data->flash_cancel_time = 0;
2113
2114 switch (operation) {
2115 case SDL_FLASH_CANCEL:
2116 // Taken care of above
2117 break;
2118 case SDL_FLASH_BRIEFLY:
2119 if (!(window->flags & SDL_WINDOW_INPUT_FOCUS)) {
2120 wmhints->flags |= XUrgencyHint;
2121 data->flashing_window = true;
2122 // On Ubuntu 21.04 this causes a dialog to pop up, so leave it up for a full second so users can see it
2123 data->flash_cancel_time = SDL_GetTicks() + 1000;
2124 }
2125 break;
2126 case SDL_FLASH_UNTIL_FOCUSED:
2127 if (!(window->flags & SDL_WINDOW_INPUT_FOCUS)) {
2128 wmhints->flags |= XUrgencyHint;
2129 data->flashing_window = true;
2130 }
2131 break;
2132 default:
2133 break;
2134 }
2135
2136 X11_XSetWMHints(display, data->xwindow, wmhints);
2137 X11_XFree(wmhints);
2138 return true;
2139}
2140
2141bool SDL_X11_SetWindowTitle(Display *display, Window xwindow, char *title)
2142{
2143 Atom _NET_WM_NAME = X11_XInternAtom(display, "_NET_WM_NAME", False);
2144 XTextProperty titleprop;
2145 int conv = X11_XmbTextListToTextProperty(display, &title, 1, XTextStyle, &titleprop);
2146 Status status;
2147
2148 if (X11_XSupportsLocale() != True) {
2149 return SDL_SetError("Current locale not supported by X server, cannot continue.");
2150 }
2151
2152 if (conv == 0) {
2153 X11_XSetTextProperty(display, xwindow, &titleprop, XA_WM_NAME);
2154 X11_XFree(titleprop.value);
2155 // we know this can't be a locale error as we checked X locale validity
2156 } else if (conv < 0) {
2157 return SDL_OutOfMemory();
2158 } else { // conv > 0
2159 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "%d characters were not convertible to the current locale!", conv);
2160 return true;
2161 }
2162
2163#ifdef X_HAVE_UTF8_STRING
2164 status = X11_Xutf8TextListToTextProperty(display, &title, 1, XUTF8StringStyle, &titleprop);
2165 if (status == Success) {
2166 X11_XSetTextProperty(display, xwindow, &titleprop, _NET_WM_NAME);
2167 X11_XFree(titleprop.value);
2168 } else {
2169 return SDL_SetError("Failed to convert title to UTF8! Bad encoding, or bad Xorg encoding? Window title: «%s»", title);
2170 }
2171#endif
2172
2173 X11_XFlush(display);
2174 return true;
2175}
2176
2177void X11_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
2178{
2179 SDL_WindowData *data = window->internal;
2180 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
2181 Display *display = data->videodata->display;
2182 Window root = RootWindow(display, displaydata->screen);
2183 XClientMessageEvent e;
2184 Window childReturn;
2185 int wx, wy;
2186
2187 SDL_zero(e);
2188 X11_XTranslateCoordinates(display, data->xwindow, root, x, y, &wx, &wy, &childReturn);
2189
2190 e.type = ClientMessage;
2191 e.window = data->xwindow;
2192 e.message_type = X11_XInternAtom(display, "_GTK_SHOW_WINDOW_MENU", 0);
2193 e.data.l[0] = 0; // GTK device ID (unused)
2194 e.data.l[1] = wx; // X coordinate relative to root
2195 e.data.l[2] = wy; // Y coordinate relative to root
2196 e.format = 32;
2197
2198 X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&e);
2199 X11_XFlush(display);
2200}
2201
2202bool X11_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window)
2203{
2204 const Uint64 current_time = SDL_GetTicksNS();
2205 Uint64 timeout = 0;
2206
2207 // Allow time for any pending mode switches to complete.
2208 for (int i = 0; i < _this->num_displays; ++i) {
2209 if (_this->displays[i]->internal->mode_switch_deadline_ns &&
2210 current_time < _this->displays[i]->internal->mode_switch_deadline_ns) {
2211 timeout = SDL_max(_this->displays[i]->internal->mode_switch_deadline_ns - current_time, timeout);
2212 }
2213 }
2214
2215 /* 100ms is fine for most cases, but, for some reason, maximizing
2216 * a window can take a very long time.
2217 */
2218 timeout += window->internal->pending_operation & X11_PENDING_OP_MAXIMIZE ? SDL_MS_TO_NS(1000) : SDL_MS_TO_NS(100);
2219
2220 return X11_SyncWindowTimeout(_this, window, timeout);
2221}
2222
2223bool X11_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable)
2224{
2225 SDL_WindowData *data = window->internal;
2226 Display *display = data->videodata->display;
2227 XWMHints *wmhints;
2228
2229 wmhints = X11_XGetWMHints(display, data->xwindow);
2230 if (!wmhints) {
2231 return SDL_SetError("Couldn't get WM hints");
2232 }
2233
2234 wmhints->input = focusable ? True : False;
2235 wmhints->flags |= InputHint;
2236
2237 X11_XSetWMHints(display, data->xwindow, wmhints);
2238 X11_XFree(wmhints);
2239
2240 return true;
2241}
2242
2243#endif // SDL_VIDEO_DRIVER_X11
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11window.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11window.h
new file mode 100644
index 0000000..f1a73ab
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11window.h
@@ -0,0 +1,169 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifndef SDL_x11window_h_
24#define SDL_x11window_h_
25
26/* We need to queue the focus in/out changes because they may occur during
27 video mode changes and we can respond to them by triggering more mode
28 changes.
29*/
30#define PENDING_FOCUS_TIME 200
31
32#ifdef SDL_VIDEO_OPENGL_EGL
33#include <EGL/egl.h>
34#endif
35
36typedef enum
37{
38 PENDING_FOCUS_NONE,
39 PENDING_FOCUS_IN,
40 PENDING_FOCUS_OUT
41} PendingFocusEnum;
42
43struct SDL_WindowData
44{
45 SDL_Window *window;
46 Window xwindow;
47 Visual *visual;
48 Colormap colormap;
49#ifndef NO_SHARED_MEMORY
50 // MIT shared memory extension information
51 bool use_mitshm;
52 XShmSegmentInfo shminfo;
53#endif
54 XImage *ximage;
55 GC gc;
56 XIC ic;
57 bool created;
58 int border_left;
59 int border_right;
60 int border_top;
61 int border_bottom;
62 bool xinput2_mouse_enabled;
63 bool xinput2_keyboard_enabled;
64 bool mouse_grabbed;
65 Uint64 last_focus_event_time;
66 PendingFocusEnum pending_focus;
67 Uint64 pending_focus_time;
68 bool pending_move;
69 SDL_Point pending_move_point;
70 XConfigureEvent last_xconfigure;
71 struct SDL_VideoData *videodata;
72 unsigned long user_time;
73 Atom xdnd_req;
74 Window xdnd_source;
75 bool flashing_window;
76 Uint64 flash_cancel_time;
77 SDL_Window *keyboard_focus;
78#ifdef SDL_VIDEO_OPENGL_EGL
79 EGLSurface egl_surface;
80#endif
81#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
82 bool pointer_barrier_active;
83 PointerBarrier barrier[4];
84 SDL_Rect barrier_rect;
85#endif // SDL_VIDEO_DRIVER_X11_XFIXES
86#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
87 XSyncCounter resize_counter;
88 XSyncValue resize_id;
89 bool resize_in_progress;
90#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
91
92 SDL_Rect expected;
93 SDL_DisplayMode requested_fullscreen_mode;
94
95 enum
96 {
97 X11_PENDING_OP_NONE = 0x00,
98 X11_PENDING_OP_RESTORE = 0x01,
99 X11_PENDING_OP_MINIMIZE = 0x02,
100 X11_PENDING_OP_MAXIMIZE = 0x04,
101 X11_PENDING_OP_FULLSCREEN = 0x08,
102 X11_PENDING_OP_MOVE = 0x10,
103 X11_PENDING_OP_RESIZE = 0x20
104 } pending_operation;
105
106 enum
107 {
108 X11_SIZE_MOVE_EVENTS_DISABLE = 0x01, // Events are completely disabled.
109 X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS = 0x02, // Events are disabled until a _NET_FRAME_EXTENTS event arrives.
110 } size_move_event_flags;
111
112 bool pending_size;
113 bool pending_position;
114 bool window_was_maximized;
115 bool previous_borders_nonzero;
116 bool toggle_borders;
117 bool fullscreen_borders_forced_on;
118 SDL_HitTestResult hit_test_result;
119
120 XPoint xim_spot;
121 char *preedit_text;
122 XIMFeedback *preedit_feedback;
123 int preedit_length;
124 int preedit_cursor;
125 bool ime_needs_clear_composition;
126};
127
128extern void X11_SetNetWMState(SDL_VideoDevice *_this, Window xwindow, SDL_WindowFlags flags);
129extern Uint32 X11_GetNetWMState(SDL_VideoDevice *_this, SDL_Window *window, Window xwindow);
130
131extern bool X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
132extern char *X11_GetWindowTitle(SDL_VideoDevice *_this, Window xwindow);
133extern void X11_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
134extern bool X11_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon);
135extern bool X11_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window);
136extern void X11_SetWindowMinimumSize(SDL_VideoDevice *_this, SDL_Window *window);
137extern void X11_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window);
138extern void X11_SetWindowAspectRatio(SDL_VideoDevice *_this, SDL_Window *window);
139extern bool X11_GetWindowBordersSize(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right);
140extern bool X11_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity);
141extern bool X11_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent);
142extern bool X11_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal);
143extern void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window);
144extern void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window);
145extern void X11_HideWindow(SDL_VideoDevice *_this, SDL_Window *window);
146extern void X11_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window);
147extern void X11_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
148extern void X11_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
149extern void X11_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window);
150extern void X11_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, bool bordered);
151extern void X11_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, bool resizable);
152extern void X11_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, bool on_top);
153extern SDL_FullscreenResult X11_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen);
154extern void *X11_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size);
155extern bool X11_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed);
156extern bool X11_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed);
157extern void X11_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
158extern bool X11_SetWindowHitTest(SDL_Window *window, bool enabled);
159extern void X11_AcceptDragAndDrop(SDL_Window *window, bool accept);
160extern bool X11_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
161extern void X11_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
162extern bool X11_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window);
163extern bool X11_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable);
164
165extern bool SDL_X11_SetWindowTitle(Display *display, Window xwindow, char *title);
166extern void X11_UpdateWindowPosition(SDL_Window *window, bool use_current_position);
167extern void X11_SetWindowMinMax(SDL_Window *window, bool use_current);
168
169#endif // SDL_x11window_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11xfixes.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xfixes.c
new file mode 100644
index 0000000..517ebc8
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xfixes.c
@@ -0,0 +1,214 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22#include "SDL_internal.h"
23
24#if defined(SDL_VIDEO_DRIVER_X11) && defined(SDL_VIDEO_DRIVER_X11_XFIXES)
25
26#include "SDL_x11video.h"
27#include "SDL_x11xfixes.h"
28#include "../../events/SDL_mouse_c.h"
29#include "../../events/SDL_touch_c.h"
30
31static bool xfixes_initialized = true;
32static int xfixes_selection_notify_event = 0;
33
34static int query_xfixes_version(Display *display, int major, int minor)
35{
36 // We don't care if this fails, so long as it sets major/minor on it's way out the door.
37 X11_XFixesQueryVersion(display, &major, &minor);
38 return (major * 1000) + minor;
39}
40
41static bool xfixes_version_atleast(const int version, const int wantmajor, const int wantminor)
42{
43 return version >= ((wantmajor * 1000) + wantminor);
44}
45
46void X11_InitXfixes(SDL_VideoDevice *_this)
47{
48 SDL_VideoData *data = _this->internal;
49
50 int version = 0;
51 int event, error;
52 int fixes_opcode;
53
54 Atom XA_CLIPBOARD = data->atoms.CLIPBOARD;
55
56 if (!SDL_X11_HAVE_XFIXES ||
57 !X11_XQueryExtension(data->display, "XFIXES", &fixes_opcode, &event, &error)) {
58 return;
59 }
60
61 // Selection tracking is available in all versions of XFixes
62 xfixes_selection_notify_event = event + XFixesSelectionNotify;
63 X11_XFixesSelectSelectionInput(data->display, DefaultRootWindow(data->display),
64 XA_CLIPBOARD, XFixesSetSelectionOwnerNotifyMask);
65 X11_XFixesSelectSelectionInput(data->display, DefaultRootWindow(data->display),
66 XA_PRIMARY, XFixesSetSelectionOwnerNotifyMask);
67
68 // We need at least 5.0 for barriers.
69 version = query_xfixes_version(data->display, 5, 0);
70 if (!xfixes_version_atleast(version, 5, 0)) {
71 return; // X server does not support the version we want at all.
72 }
73
74 xfixes_initialized = 1;
75}
76
77bool X11_XfixesIsInitialized(void)
78{
79 return xfixes_initialized;
80}
81
82int X11_GetXFixesSelectionNotifyEvent(void)
83{
84 return xfixes_selection_notify_event;
85}
86
87bool X11_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window)
88{
89 if (SDL_RectEmpty(&window->mouse_rect)) {
90 X11_ConfineCursorWithFlags(_this, window, NULL, 0);
91 } else {
92 if (window->flags & SDL_WINDOW_INPUT_FOCUS) {
93 X11_ConfineCursorWithFlags(_this, window, &window->mouse_rect, 0);
94 } else {
95 // Save the state for when we get focus again
96 SDL_WindowData *wdata = window->internal;
97
98 SDL_memcpy(&wdata->barrier_rect, &window->mouse_rect, sizeof(wdata->barrier_rect));
99
100 wdata->pointer_barrier_active = true;
101 }
102 }
103
104 return true;
105}
106
107bool X11_ConfineCursorWithFlags(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rect, int flags)
108{
109 /* Yaakuro: For some reason Xfixes when confining inside a rect where the
110 * edges exactly match, a rectangle the cursor 'slips' out of the barrier.
111 * To prevent that the lines for the barriers will span the whole screen.
112 */
113 SDL_VideoData *data = _this->internal;
114 SDL_WindowData *wdata;
115
116 if (!X11_XfixesIsInitialized()) {
117 return SDL_Unsupported();
118 }
119
120 // If there is already a set of barriers active, disable them.
121 if (data->active_cursor_confined_window) {
122 X11_DestroyPointerBarrier(_this, data->active_cursor_confined_window);
123 }
124
125 SDL_assert(window != NULL);
126 wdata = window->internal;
127
128 /* If user did not specify an area to confine, destroy the barrier that was/is assigned to
129 * this window it was assigned */
130 if (rect) {
131 int x1, y1, x2, y2;
132 SDL_Rect bounds;
133 SDL_GetWindowPosition(window, &bounds.x, &bounds.y);
134 SDL_GetWindowSize(window, &bounds.w, &bounds.h);
135
136 /** Negative values are not allowed. Clip values relative to the specified window. */
137 x1 = bounds.x + SDL_max(rect->x, 0);
138 y1 = bounds.y + SDL_max(rect->y, 0);
139 x2 = SDL_min(bounds.x + rect->x + rect->w, bounds.x + bounds.w);
140 y2 = SDL_min(bounds.y + rect->y + rect->h, bounds.y + bounds.h);
141
142 if ((wdata->barrier_rect.x != rect->x) ||
143 (wdata->barrier_rect.y != rect->y) ||
144 (wdata->barrier_rect.w != rect->w) ||
145 (wdata->barrier_rect.h != rect->h)) {
146 wdata->barrier_rect = *rect;
147 }
148
149 // Use the display bounds to ensure the barriers don't have corner gaps
150 SDL_GetDisplayBounds(SDL_GetDisplayForWindow(window), &bounds);
151
152 /** Create the left barrier */
153 wdata->barrier[0] = X11_XFixesCreatePointerBarrier(data->display, wdata->xwindow,
154 x1, bounds.y,
155 x1, bounds.y + bounds.h,
156 BarrierPositiveX,
157 0, NULL);
158 /** Create the right barrier */
159 wdata->barrier[1] = X11_XFixesCreatePointerBarrier(data->display, wdata->xwindow,
160 x2, bounds.y,
161 x2, bounds.y + bounds.h,
162 BarrierNegativeX,
163 0, NULL);
164 /** Create the top barrier */
165 wdata->barrier[2] = X11_XFixesCreatePointerBarrier(data->display, wdata->xwindow,
166 bounds.x, y1,
167 bounds.x + bounds.w, y1,
168 BarrierPositiveY,
169 0, NULL);
170 /** Create the bottom barrier */
171 wdata->barrier[3] = X11_XFixesCreatePointerBarrier(data->display, wdata->xwindow,
172 bounds.x, y2,
173 bounds.x + bounds.w, y2,
174 BarrierNegativeY,
175 0, NULL);
176
177 X11_XFlush(data->display);
178
179 // Lets remember current active confined window.
180 data->active_cursor_confined_window = window;
181
182 /* User activated the confinement for this window. We use this later to reactivate
183 * the confinement if it got deactivated by FocusOut or UnmapNotify */
184 wdata->pointer_barrier_active = true;
185 } else {
186 X11_DestroyPointerBarrier(_this, window);
187
188 // Only set barrier inactive when user specified NULL and not handled by focus out.
189 if (flags != X11_BARRIER_HANDLED_BY_EVENT) {
190 wdata->pointer_barrier_active = false;
191 }
192 }
193 return true;
194}
195
196void X11_DestroyPointerBarrier(SDL_VideoDevice *_this, SDL_Window *window)
197{
198 int i;
199 SDL_VideoData *data = _this->internal;
200 if (window) {
201 SDL_WindowData *wdata = window->internal;
202
203 for (i = 0; i < 4; i++) {
204 if (wdata->barrier[i] > 0) {
205 X11_XFixesDestroyPointerBarrier(data->display, wdata->barrier[i]);
206 wdata->barrier[i] = 0;
207 }
208 }
209 X11_XFlush(data->display);
210 }
211 data->active_cursor_confined_window = NULL;
212}
213
214#endif // SDL_VIDEO_DRIVER_X11 && SDL_VIDEO_DRIVER_X11_XFIXES
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11xfixes.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xfixes.h
new file mode 100644
index 0000000..bd8e437
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xfixes.h
@@ -0,0 +1,39 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22#include "SDL_internal.h"
23
24#ifndef SDL_x11xfixes_h_
25#define SDL_x11xfixes_h_
26
27#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
28
29#define X11_BARRIER_HANDLED_BY_EVENT 1
30
31extern void X11_InitXfixes(SDL_VideoDevice *_this);
32extern bool X11_XfixesIsInitialized(void);
33extern bool X11_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window);
34extern bool X11_ConfineCursorWithFlags(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rect, int flags);
35extern void X11_DestroyPointerBarrier(SDL_VideoDevice *_this, SDL_Window *window);
36extern int X11_GetXFixesSelectionNotifyEvent(void);
37#endif // SDL_VIDEO_DRIVER_X11_XFIXES
38
39#endif // SDL_x11xfixes_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11xinput2.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xinput2.c
new file mode 100644
index 0000000..afe4a7c
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xinput2.c
@@ -0,0 +1,829 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_DRIVER_X11
24
25#include "SDL_x11pen.h"
26#include "SDL_x11video.h"
27#include "SDL_x11xinput2.h"
28#include "../../events/SDL_events_c.h"
29#include "../../events/SDL_mouse_c.h"
30#include "../../events/SDL_pen_c.h"
31#include "../../events/SDL_touch_c.h"
32
33#define MAX_AXIS 16
34
35#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
36static bool xinput2_initialized;
37
38#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
39static bool xinput2_multitouch_supported;
40#endif
41
42/* Opcode returned X11_XQueryExtension
43 * It will be used in event processing
44 * to know that the event came from
45 * this extension */
46static int xinput2_opcode;
47
48static void parse_valuators(const double *input_values, const unsigned char *mask, int mask_len,
49 double *output_values, int output_values_len)
50{
51 int i = 0, z = 0;
52 int top = mask_len * 8;
53 if (top > MAX_AXIS) {
54 top = MAX_AXIS;
55 }
56
57 SDL_memset(output_values, 0, output_values_len * sizeof(double));
58 for (; i < top && z < output_values_len; i++) {
59 if (XIMaskIsSet(mask, i)) {
60 const int value = (int)*input_values;
61 output_values[z] = value;
62 input_values++;
63 }
64 z++;
65 }
66}
67
68static int query_xinput2_version(Display *display, int major, int minor)
69{
70 // We don't care if this fails, so long as it sets major/minor on it's way out the door.
71 X11_XIQueryVersion(display, &major, &minor);
72 return (major * 1000) + minor;
73}
74
75static bool xinput2_version_atleast(const int version, const int wantmajor, const int wantminor)
76{
77 return version >= ((wantmajor * 1000) + wantminor);
78}
79
80static SDL_WindowData *xinput2_get_sdlwindowdata(SDL_VideoData *videodata, Window window)
81{
82 int i;
83 for (i = 0; i < videodata->numwindows; i++) {
84 SDL_WindowData *d = videodata->windowlist[i];
85 if (d->xwindow == window) {
86 return d;
87 }
88 }
89 return NULL;
90}
91
92static SDL_Window *xinput2_get_sdlwindow(SDL_VideoData *videodata, Window window)
93{
94 const SDL_WindowData *windowdata = xinput2_get_sdlwindowdata(videodata, window);
95 return windowdata ? windowdata->window : NULL;
96}
97
98#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
99static void xinput2_normalize_touch_coordinates(SDL_Window *window, double in_x, double in_y, float *out_x, float *out_y)
100{
101 if (window) {
102 if (window->w == 1) {
103 *out_x = 0.5f;
104 } else {
105 *out_x = (float)in_x / (window->w - 1);
106 }
107 if (window->h == 1) {
108 *out_y = 0.5f;
109 } else {
110 *out_y = (float)in_y / (window->h - 1);
111 }
112 } else {
113 // couldn't find the window...
114 *out_x = (float)in_x;
115 *out_y = (float)in_y;
116 }
117}
118#endif // SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
119
120#endif // SDL_VIDEO_DRIVER_X11_XINPUT2
121
122bool X11_InitXinput2(SDL_VideoDevice *_this)
123{
124#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
125 SDL_VideoData *data = _this->internal;
126
127 int version = 0;
128 XIEventMask eventmask;
129 unsigned char mask[4] = { 0, 0, 0, 0 };
130 int event, err;
131
132 /* XInput2 is required for relative mouse mode, so you probably want to leave this enabled */
133 if (!SDL_GetHintBoolean("SDL_VIDEO_X11_XINPUT2", true)) {
134 return false;
135 }
136
137 /*
138 * Initialize XInput 2
139 * According to http://who-t.blogspot.com/2009/05/xi2-recipes-part-1.html its better
140 * to inform Xserver what version of Xinput we support.The server will store the version we support.
141 * "As XI2 progresses it becomes important that you use this call as the server may treat the client
142 * differently depending on the supported version".
143 *
144 * FIXME:event and err are not needed but if not passed X11_XQueryExtension returns SegmentationFault
145 */
146 if (!SDL_X11_HAVE_XINPUT2 ||
147 !X11_XQueryExtension(data->display, "XInputExtension", &xinput2_opcode, &event, &err)) {
148 return false; // X server does not have XInput at all
149 }
150
151 // We need at least 2.2 for Multitouch, 2.0 otherwise.
152 version = query_xinput2_version(data->display, 2, 2);
153 if (!xinput2_version_atleast(version, 2, 0)) {
154 return false; // X server does not support the version we want at all.
155 }
156
157 xinput2_initialized = true;
158
159#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH // Multitouch needs XInput 2.2
160 xinput2_multitouch_supported = xinput2_version_atleast(version, 2, 2);
161#endif
162
163 // Enable raw motion events for this display
164 SDL_zero(eventmask);
165 SDL_zeroa(mask);
166 eventmask.deviceid = XIAllMasterDevices;
167 eventmask.mask_len = sizeof(mask);
168 eventmask.mask = mask;
169
170 XISetMask(mask, XI_RawMotion);
171 XISetMask(mask, XI_RawButtonPress);
172 XISetMask(mask, XI_RawButtonRelease);
173
174#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
175 // Enable raw touch events if supported
176 if (X11_Xinput2IsMultitouchSupported()) {
177 XISetMask(mask, XI_RawTouchBegin);
178 XISetMask(mask, XI_RawTouchUpdate);
179 XISetMask(mask, XI_RawTouchEnd);
180 }
181#endif
182
183 X11_XISelectEvents(data->display, DefaultRootWindow(data->display), &eventmask, 1);
184
185 SDL_zero(eventmask);
186 SDL_zeroa(mask);
187 eventmask.deviceid = XIAllDevices;
188 eventmask.mask_len = sizeof(mask);
189 eventmask.mask = mask;
190
191 XISetMask(mask, XI_HierarchyChanged);
192 X11_XISelectEvents(data->display, DefaultRootWindow(data->display), &eventmask, 1);
193
194 X11_Xinput2UpdateDevices(_this, true);
195
196 return true;
197#else
198 return false;
199#endif
200}
201
202#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
203// xi2 device went away? take it out of the list.
204static void xinput2_remove_device_info(SDL_VideoData *videodata, const int device_id)
205{
206 SDL_XInput2DeviceInfo *prev = NULL;
207 SDL_XInput2DeviceInfo *devinfo;
208
209 for (devinfo = videodata->mouse_device_info; devinfo; devinfo = devinfo->next) {
210 if (devinfo->device_id == device_id) {
211 SDL_assert((devinfo == videodata->mouse_device_info) == (prev == NULL));
212 if (!prev) {
213 videodata->mouse_device_info = devinfo->next;
214 } else {
215 prev->next = devinfo->next;
216 }
217 SDL_free(devinfo);
218 return;
219 }
220 prev = devinfo;
221 }
222}
223
224static SDL_XInput2DeviceInfo *xinput2_get_device_info(SDL_VideoData *videodata, const int device_id)
225{
226 // cache device info as we see new devices.
227 SDL_XInput2DeviceInfo *prev = NULL;
228 SDL_XInput2DeviceInfo *devinfo;
229 XIDeviceInfo *xidevinfo;
230 int axis = 0;
231 int i;
232
233 for (devinfo = videodata->mouse_device_info; devinfo; devinfo = devinfo->next) {
234 if (devinfo->device_id == device_id) {
235 SDL_assert((devinfo == videodata->mouse_device_info) == (prev == NULL));
236 if (prev) { // move this to the front of the list, assuming we'll get more from this one.
237 prev->next = devinfo->next;
238 devinfo->next = videodata->mouse_device_info;
239 videodata->mouse_device_info = devinfo;
240 }
241 return devinfo;
242 }
243 prev = devinfo;
244 }
245
246 // don't know about this device yet, query and cache it.
247 devinfo = (SDL_XInput2DeviceInfo *)SDL_calloc(1, sizeof(SDL_XInput2DeviceInfo));
248 if (!devinfo) {
249 return NULL;
250 }
251
252 xidevinfo = X11_XIQueryDevice(videodata->display, device_id, &i);
253 if (!xidevinfo) {
254 SDL_free(devinfo);
255 return NULL;
256 }
257
258 devinfo->device_id = device_id;
259
260 /* !!! FIXME: this is sort of hacky because we only care about the first two axes we see, but any given
261 !!! FIXME: axis could be relative or absolute, and they might not even be the X and Y axes!
262 !!! FIXME: But we go on, for now. Maybe we need a more robust mouse API in SDL3... */
263 for (i = 0; i < xidevinfo->num_classes; i++) {
264 const XIValuatorClassInfo *v = (const XIValuatorClassInfo *)xidevinfo->classes[i];
265 if (v->type == XIValuatorClass) {
266 devinfo->relative[axis] = (v->mode == XIModeRelative);
267 devinfo->minval[axis] = v->min;
268 devinfo->maxval[axis] = v->max;
269 if (++axis >= 2) {
270 break;
271 }
272 }
273 }
274
275 X11_XIFreeDeviceInfo(xidevinfo);
276
277 devinfo->next = videodata->mouse_device_info;
278 videodata->mouse_device_info = devinfo;
279
280 return devinfo;
281}
282#endif
283
284void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
285{
286#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
287 SDL_VideoData *videodata = _this->internal;
288
289 if (cookie->extension != xinput2_opcode) {
290 return;
291 }
292
293 switch (cookie->evtype) {
294 case XI_HierarchyChanged:
295 {
296 const XIHierarchyEvent *hierev = (const XIHierarchyEvent *)cookie->data;
297 int i;
298 for (i = 0; i < hierev->num_info; i++) {
299 // pen stuff...
300 if ((hierev->info[i].flags & (XISlaveRemoved | XIDeviceDisabled)) != 0) {
301 X11_RemovePenByDeviceID(hierev->info[i].deviceid); // it's okay if this thing isn't actually a pen, it'll handle it.
302 } else if ((hierev->info[i].flags & (XISlaveAdded | XIDeviceEnabled)) != 0) {
303 X11_MaybeAddPenByDeviceID(_this, hierev->info[i].deviceid); // this will do more checks to make sure this is valid.
304 }
305
306 // not pen stuff...
307 if (hierev->info[i].flags & XISlaveRemoved) {
308 xinput2_remove_device_info(videodata, hierev->info[i].deviceid);
309 }
310 }
311 videodata->xinput_hierarchy_changed = true;
312 } break;
313
314 // !!! FIXME: the pen code used to rescan all devices here, but we can do this device-by-device with XI_HierarchyChanged. When do these events fire and why?
315 //case XI_PropertyEvent:
316 //case XI_DeviceChanged:
317
318 case XI_RawMotion:
319 {
320 const XIRawEvent *rawev = (const XIRawEvent *)cookie->data;
321 const bool is_pen = X11_FindPenByDeviceID(rawev->sourceid) != NULL;
322 SDL_Mouse *mouse = SDL_GetMouse();
323 SDL_XInput2DeviceInfo *devinfo;
324 double coords[2];
325 double processed_coords[2];
326 int i;
327 Uint64 timestamp = X11_GetEventTimestamp(rawev->time);
328
329 videodata->global_mouse_changed = true;
330 if (is_pen) {
331 break; // Pens check for XI_Motion instead
332 }
333
334 devinfo = xinput2_get_device_info(videodata, rawev->deviceid);
335 if (!devinfo) {
336 break; // oh well.
337 }
338
339 parse_valuators(rawev->raw_values, rawev->valuators.mask,
340 rawev->valuators.mask_len, coords, 2);
341
342 for (i = 0; i < 2; i++) {
343 if (devinfo->relative[i]) {
344 processed_coords[i] = coords[i];
345 } else {
346 processed_coords[i] = devinfo->prev_coords[i] - coords[i]; // convert absolute to relative
347 }
348 }
349
350 // Relative mouse motion is delivered to the window with keyboard focus
351 if (mouse->relative_mode && SDL_GetKeyboardFocus()) {
352 SDL_SendMouseMotion(timestamp, mouse->focus, (SDL_MouseID)rawev->sourceid, true, (float)processed_coords[0], (float)processed_coords[1]);
353 }
354
355 devinfo->prev_coords[0] = coords[0];
356 devinfo->prev_coords[1] = coords[1];
357 } break;
358
359 case XI_KeyPress:
360 case XI_KeyRelease:
361 {
362 const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
363 SDL_WindowData *windowdata = X11_FindWindow(_this, xev->event);
364 XEvent xevent;
365
366 if (xev->deviceid != xev->sourceid) {
367 // Discard events from "Master" devices to avoid duplicates.
368 break;
369 }
370
371 if (cookie->evtype == XI_KeyPress) {
372 xevent.type = KeyPress;
373 } else {
374 xevent.type = KeyRelease;
375 }
376 xevent.xkey.serial = xev->serial;
377 xevent.xkey.send_event = xev->send_event;
378 xevent.xkey.display = xev->display;
379 xevent.xkey.window = xev->event;
380 xevent.xkey.root = xev->root;
381 xevent.xkey.subwindow = xev->child;
382 xevent.xkey.time = xev->time;
383 xevent.xkey.x = (int)xev->event_x;
384 xevent.xkey.y = (int)xev->event_y;
385 xevent.xkey.x_root = (int)xev->root_x;
386 xevent.xkey.y_root = (int)xev->root_y;
387 xevent.xkey.state = xev->mods.effective;
388 xevent.xkey.keycode = xev->detail;
389 xevent.xkey.same_screen = 1;
390
391 X11_HandleKeyEvent(_this, windowdata, (SDL_KeyboardID)xev->sourceid, &xevent);
392 } break;
393
394 case XI_RawButtonPress:
395 case XI_RawButtonRelease:
396#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
397 case XI_RawTouchBegin:
398 case XI_RawTouchUpdate:
399 case XI_RawTouchEnd:
400#endif
401 {
402 videodata->global_mouse_changed = true;
403 } break;
404
405 case XI_ButtonPress:
406 case XI_ButtonRelease:
407 {
408 const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
409 X11_PenHandle *pen = X11_FindPenByDeviceID(xev->deviceid);
410 const int button = xev->detail;
411 const bool down = (cookie->evtype == XI_ButtonPress);
412
413 if (pen) {
414 // Only report button event; if there was also pen movement / pressure changes, we expect an XI_Motion event first anyway.
415 SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
416 if (button == 1) { // button 1 is the pen tip
417 SDL_SendPenTouch(0, pen->pen, window, pen->is_eraser, down);
418 } else {
419 SDL_SendPenButton(0, pen->pen, window, button - 1, down);
420 }
421 } else {
422 // Otherwise assume a regular mouse
423 SDL_WindowData *windowdata = xinput2_get_sdlwindowdata(videodata, xev->event);
424
425 if (xev->deviceid != xev->sourceid) {
426 // Discard events from "Master" devices to avoid duplicates.
427 break;
428 }
429
430 if (down) {
431 X11_HandleButtonPress(_this, windowdata, (SDL_MouseID)xev->sourceid, button,
432 (float)xev->event_x, (float)xev->event_y, xev->time);
433 } else {
434 X11_HandleButtonRelease(_this, windowdata, (SDL_MouseID)xev->sourceid, button, xev->time);
435 }
436 }
437 } break;
438
439 /* Register to receive XI_Motion (which deactivates MotionNotify), so that we can distinguish
440 real mouse motions from synthetic ones, for multitouch and pen support. */
441 case XI_Motion:
442 {
443 const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
444#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
445 bool pointer_emulated = ((xev->flags & XIPointerEmulated) != 0);
446#else
447 bool pointer_emulated = false;
448#endif
449
450 videodata->global_mouse_changed = true;
451
452 X11_PenHandle *pen = X11_FindPenByDeviceID(xev->deviceid);
453 if (pen) {
454 if (xev->deviceid != xev->sourceid) {
455 // Discard events from "Master" devices to avoid duplicates.
456 break;
457 }
458
459 SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
460 SDL_SendPenMotion(0, pen->pen, window, (float) xev->event_x, (float) xev->event_y);
461
462 float axes[SDL_PEN_AXIS_COUNT];
463 X11_PenAxesFromValuators(pen, xev->valuators.values, xev->valuators.mask, xev->valuators.mask_len, axes);
464
465 for (int i = 0; i < SDL_arraysize(axes); i++) {
466 if (pen->valuator_for_axis[i] != SDL_X11_PEN_AXIS_VALUATOR_MISSING) {
467 SDL_SendPenAxis(0, pen->pen, window, (SDL_PenAxis) i, axes[i]);
468 }
469 }
470 } else if (!pointer_emulated && xev->deviceid == videodata->xinput_master_pointer_device) {
471 // Use the master device for non-relative motion, as the slave devices can seemingly lag behind.
472 SDL_Mouse *mouse = SDL_GetMouse();
473 if (!mouse->relative_mode) {
474 SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
475 if (window) {
476 X11_ProcessHitTest(_this, window->internal, (float)xev->event_x, (float)xev->event_y, false);
477 SDL_SendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, (float)xev->event_x, (float)xev->event_y);
478 }
479 }
480 }
481 } break;
482
483#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
484 case XI_TouchBegin:
485 {
486 const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
487 float x, y;
488 SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
489 xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y);
490 SDL_SendTouch(0, xev->sourceid, xev->detail, window, SDL_EVENT_FINGER_DOWN, x, y, 1.0);
491 } break;
492
493 case XI_TouchEnd:
494 {
495 const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
496 float x, y;
497 SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
498 xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y);
499 SDL_SendTouch(0, xev->sourceid, xev->detail, window, SDL_EVENT_FINGER_UP, x, y, 1.0);
500 } break;
501
502 case XI_TouchUpdate:
503 {
504 const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
505 float x, y;
506 SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
507 xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y);
508 SDL_SendTouchMotion(0, xev->sourceid, xev->detail, window, x, y, 1.0);
509 } break;
510#endif // SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
511 }
512#endif // SDL_VIDEO_DRIVER_X11_XINPUT2
513}
514
515void X11_InitXinput2Multitouch(SDL_VideoDevice *_this)
516{
517}
518
519void X11_Xinput2SelectTouch(SDL_VideoDevice *_this, SDL_Window *window)
520{
521#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
522 SDL_VideoData *data = NULL;
523 XIEventMask eventmask;
524 unsigned char mask[4] = { 0, 0, 0, 0 };
525 SDL_WindowData *window_data = NULL;
526
527 if (!X11_Xinput2IsMultitouchSupported()) {
528 return;
529 }
530
531 data = _this->internal;
532 window_data = window->internal;
533
534 eventmask.deviceid = XIAllMasterDevices;
535 eventmask.mask_len = sizeof(mask);
536 eventmask.mask = mask;
537
538 XISetMask(mask, XI_TouchBegin);
539 XISetMask(mask, XI_TouchUpdate);
540 XISetMask(mask, XI_TouchEnd);
541 XISetMask(mask, XI_Motion);
542
543 X11_XISelectEvents(data->display, window_data->xwindow, &eventmask, 1);
544#endif
545}
546
547bool X11_Xinput2IsInitialized(void)
548{
549#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
550 return xinput2_initialized;
551#else
552 return false;
553#endif
554}
555
556bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
557{
558 SDL_WindowData *windowdata = window->internal;
559
560#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
561 const SDL_VideoData *data = _this->internal;
562
563 if (X11_Xinput2IsInitialized()) {
564 XIEventMask eventmask;
565 unsigned char mask[4] = { 0, 0, 0, 0 };
566
567 eventmask.mask_len = sizeof(mask);
568 eventmask.mask = mask;
569 eventmask.deviceid = XIAllDevices;
570
571// This is not enabled by default because these events are only delivered to the window with mouse focus, not keyboard focus
572#ifdef USE_XINPUT2_KEYBOARD
573 XISetMask(mask, XI_KeyPress);
574 XISetMask(mask, XI_KeyRelease);
575 windowdata->xinput2_keyboard_enabled = true;
576#endif
577
578 XISetMask(mask, XI_ButtonPress);
579 XISetMask(mask, XI_ButtonRelease);
580 XISetMask(mask, XI_Motion);
581 windowdata->xinput2_mouse_enabled = true;
582
583 XISetMask(mask, XI_Enter);
584 XISetMask(mask, XI_Leave);
585
586 // Hotplugging:
587 XISetMask(mask, XI_DeviceChanged);
588 XISetMask(mask, XI_HierarchyChanged);
589 XISetMask(mask, XI_PropertyEvent); // E.g., when swapping tablet pens
590
591 if (X11_XISelectEvents(data->display, windowdata->xwindow, &eventmask, 1) != Success) {
592 SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Could not enable XInput2 event handling");
593 windowdata->xinput2_keyboard_enabled = false;
594 windowdata->xinput2_mouse_enabled = false;
595 }
596 }
597#endif
598
599 if (windowdata->xinput2_keyboard_enabled || windowdata->xinput2_mouse_enabled) {
600 return true;
601 }
602 return false;
603}
604
605bool X11_Xinput2IsMultitouchSupported(void)
606{
607#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
608 return xinput2_initialized && xinput2_multitouch_supported;
609#else
610 return true;
611#endif
612}
613
614void X11_Xinput2GrabTouch(SDL_VideoDevice *_this, SDL_Window *window)
615{
616#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
617 SDL_WindowData *data = window->internal;
618 Display *display = data->videodata->display;
619
620 unsigned char mask[4] = { 0, 0, 0, 0 };
621 XIGrabModifiers mods;
622 XIEventMask eventmask;
623
624 if (!X11_Xinput2IsMultitouchSupported()) {
625 return;
626 }
627
628 mods.modifiers = XIAnyModifier;
629 mods.status = 0;
630
631 eventmask.deviceid = XIAllDevices;
632 eventmask.mask_len = sizeof(mask);
633 eventmask.mask = mask;
634
635 XISetMask(eventmask.mask, XI_TouchBegin);
636 XISetMask(eventmask.mask, XI_TouchUpdate);
637 XISetMask(eventmask.mask, XI_TouchEnd);
638 XISetMask(eventmask.mask, XI_Motion);
639
640 X11_XIGrabTouchBegin(display, XIAllDevices, data->xwindow, True, &eventmask, 1, &mods);
641#endif
642}
643
644void X11_Xinput2UngrabTouch(SDL_VideoDevice *_this, SDL_Window *window)
645{
646#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
647 SDL_WindowData *data = window->internal;
648 Display *display = data->videodata->display;
649
650 XIGrabModifiers mods;
651
652 if (!X11_Xinput2IsMultitouchSupported()) {
653 return;
654 }
655
656 mods.modifiers = XIAnyModifier;
657 mods.status = 0;
658
659 X11_XIUngrabTouchBegin(display, XIAllDevices, data->xwindow, 1, &mods);
660#endif
661}
662
663#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
664
665static void AddDeviceID(Uint32 deviceID, Uint32 **list, int *count)
666{
667 int new_count = (*count + 1);
668 Uint32 *new_list = (Uint32 *)SDL_realloc(*list, new_count * sizeof(*new_list));
669 if (!new_list) {
670 // Oh well, we'll drop this one
671 return;
672 }
673 new_list[new_count - 1] = deviceID;
674
675 *count = new_count;
676 *list = new_list;
677}
678
679static bool HasDeviceID(Uint32 deviceID, const Uint32 *list, int count)
680{
681 for (int i = 0; i < count; ++i) {
682 if (deviceID == list[i]) {
683 return true;
684 }
685 }
686 return false;
687}
688
689static void AddDeviceID64(Uint64 deviceID, Uint64 **list, int *count)
690{
691 int new_count = (*count + 1);
692 Uint64 *new_list = (Uint64 *)SDL_realloc(*list, new_count * sizeof(*new_list));
693 if (!new_list) {
694 // Oh well, we'll drop this one
695 return;
696 }
697 new_list[new_count - 1] = deviceID;
698
699 *count = new_count;
700 *list = new_list;
701}
702
703static bool HasDeviceID64(Uint64 deviceID, const Uint64 *list, int count)
704{
705 for (int i = 0; i < count; ++i) {
706 if (deviceID == list[i]) {
707 return true;
708 }
709 }
710 return false;
711}
712
713#endif // SDL_VIDEO_DRIVER_X11_XINPUT2
714
715void X11_Xinput2UpdateDevices(SDL_VideoDevice *_this, bool initial_check)
716{
717#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
718 SDL_VideoData *data = _this->internal;
719 XIDeviceInfo *info;
720 int ndevices;
721 int old_keyboard_count = 0;
722 SDL_KeyboardID *old_keyboards = NULL;
723 int new_keyboard_count = 0;
724 SDL_KeyboardID *new_keyboards = NULL;
725 int old_mouse_count = 0;
726 SDL_MouseID *old_mice = NULL;
727 int new_mouse_count = 0;
728 SDL_MouseID *new_mice = NULL;
729 int old_touch_count = 0;
730 Uint64 *old_touch_devices = NULL;
731 int new_touch_count = 0;
732 Uint64 *new_touch_devices = NULL;
733 bool send_event = !initial_check;
734
735 SDL_assert(X11_Xinput2IsInitialized());
736
737 info = X11_XIQueryDevice(data->display, XIAllDevices, &ndevices);
738
739 old_keyboards = SDL_GetKeyboards(&old_keyboard_count);
740 old_mice = SDL_GetMice(&old_mouse_count);
741 old_touch_devices = SDL_GetTouchDevices(&old_touch_count);
742
743 for (int i = 0; i < ndevices; i++) {
744 XIDeviceInfo *dev = &info[i];
745
746 switch (dev->use) {
747 case XIMasterKeyboard:
748 case XISlaveKeyboard:
749 {
750 SDL_KeyboardID keyboardID = (SDL_KeyboardID)dev->deviceid;
751 AddDeviceID(keyboardID, &new_keyboards, &new_keyboard_count);
752 if (!HasDeviceID(keyboardID, old_keyboards, old_keyboard_count)) {
753 SDL_AddKeyboard(keyboardID, dev->name, send_event);
754 }
755 }
756 break;
757 case XIMasterPointer:
758 data->xinput_master_pointer_device = dev->deviceid;
759 SDL_FALLTHROUGH;
760 case XISlavePointer:
761 {
762 SDL_MouseID mouseID = (SDL_MouseID)dev->deviceid;
763 AddDeviceID(mouseID, &new_mice, &new_mouse_count);
764 if (!HasDeviceID(mouseID, old_mice, old_mouse_count)) {
765 SDL_AddMouse(mouseID, dev->name, send_event);
766 }
767 }
768 break;
769 default:
770 break;
771 }
772
773#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
774 for (int j = 0; j < dev->num_classes; j++) {
775 Uint64 touchID;
776 SDL_TouchDeviceType touchType;
777 XIAnyClassInfo *class = dev->classes[j];
778 XITouchClassInfo *t = (XITouchClassInfo *)class;
779
780 // Only touch devices
781 if (class->type != XITouchClass) {
782 continue;
783 }
784
785 touchID = (Uint64)t->sourceid;
786 AddDeviceID64(touchID, &new_touch_devices, &new_touch_count);
787 if (!HasDeviceID64(touchID, old_touch_devices, old_touch_count)) {
788 if (t->mode == XIDependentTouch) {
789 touchType = SDL_TOUCH_DEVICE_INDIRECT_RELATIVE;
790 } else { // XIDirectTouch
791 touchType = SDL_TOUCH_DEVICE_DIRECT;
792 }
793 SDL_AddTouch(touchID, touchType, dev->name);
794 }
795 }
796#endif // SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
797 }
798
799 for (int i = old_keyboard_count; i--;) {
800 if (!HasDeviceID(old_keyboards[i], new_keyboards, new_keyboard_count)) {
801 SDL_RemoveKeyboard(old_keyboards[i], send_event);
802 }
803 }
804
805 for (int i = old_mouse_count; i--;) {
806 if (!HasDeviceID(old_mice[i], new_mice, new_mouse_count)) {
807 SDL_RemoveMouse(old_mice[i], send_event);
808 }
809 }
810
811 for (int i = old_touch_count; i--;) {
812 if (!HasDeviceID64(old_touch_devices[i], new_touch_devices, new_touch_count)) {
813 SDL_DelTouch(old_touch_devices[i]);
814 }
815 }
816
817 SDL_free(old_keyboards);
818 SDL_free(new_keyboards);
819 SDL_free(old_mice);
820 SDL_free(new_mice);
821 SDL_free(old_touch_devices);
822 SDL_free(new_touch_devices);
823
824 X11_XIFreeDeviceInfo(info);
825
826#endif // SDL_VIDEO_DRIVER_X11_XINPUT2
827}
828
829#endif // SDL_VIDEO_DRIVER_X11
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11xinput2.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xinput2.h
new file mode 100644
index 0000000..c96c020
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xinput2.h
@@ -0,0 +1,44 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifndef SDL_x11xinput2_h_
24#define SDL_x11xinput2_h_
25
26#ifndef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
27/* Define XGenericEventCookie as forward declaration when
28 *xinput2 is not available in order to compile */
29struct XGenericEventCookie;
30typedef struct XGenericEventCookie XGenericEventCookie;
31#endif
32
33extern bool X11_InitXinput2(SDL_VideoDevice *_this);
34extern void X11_InitXinput2Multitouch(SDL_VideoDevice *_this);
35extern void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie);
36extern bool X11_Xinput2IsInitialized(void);
37extern bool X11_Xinput2IsMultitouchSupported(void);
38extern void X11_Xinput2SelectTouch(SDL_VideoDevice *_this, SDL_Window *window);
39extern void X11_Xinput2GrabTouch(SDL_VideoDevice *_this, SDL_Window *window);
40extern void X11_Xinput2UngrabTouch(SDL_VideoDevice *_this, SDL_Window *window);
41extern bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *window);
42extern void X11_Xinput2UpdateDevices(SDL_VideoDevice *_this, bool initial_check);
43
44#endif // SDL_x11xinput2_h_
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11xsync.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xsync.c
new file mode 100644
index 0000000..5310d67
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xsync.c
@@ -0,0 +1,148 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22#include "SDL_internal.h"
23
24#if defined(SDL_VIDEO_DRIVER_X11) && defined(SDL_VIDEO_DRIVER_X11_XSYNC)
25
26#include "SDL_x11video.h"
27#include "SDL_x11xsync.h"
28
29static int xsync_initialized = 0;
30
31static int query_xsync_version(Display *display, int major, int minor)
32{
33 /* We don't care if this fails, so long as it sets major/minor on it's way out the door. */
34 X11_XSyncInitialize(display, &major, &minor);
35 return (major * 1000) + minor;
36}
37
38static bool xsync_version_atleast(const int version, const int wantmajor, const int wantminor)
39{
40 return version >= ((wantmajor * 1000) + wantminor);
41}
42
43void X11_InitXsync(SDL_VideoDevice *_this)
44{
45 SDL_VideoData *data = _this->internal;
46
47 int version = 0;
48 int event, error;
49 int sync_opcode;
50
51 if (!SDL_X11_HAVE_XSYNC ||
52 !X11_XQueryExtension(data->display, "SYNC", &sync_opcode, &event, &error)) {
53 return;
54 }
55
56 /* We need at least 5.0 for barriers. */
57 version = query_xsync_version(data->display, 5, 0);
58 if (!xsync_version_atleast(version, 3, 0)) {
59 return; /* X server does not support the version we want at all. */
60 }
61
62 xsync_initialized = 1;
63}
64
65int X11_XsyncIsInitialized(void)
66{
67 return xsync_initialized;
68}
69
70int X11_InitResizeSync(SDL_Window *window)
71{
72 SDL_assert(window != NULL);
73 SDL_WindowData *data = window->internal;
74 Display *display = data->videodata->display;
75 Atom counter_prop = data->videodata->atoms._NET_WM_SYNC_REQUEST_COUNTER;
76 XSyncCounter counter;
77 CARD32 counter_id;
78
79 if (!X11_XsyncIsInitialized()){
80 return SDL_Unsupported();
81 }
82
83 counter = X11_XSyncCreateCounter(display, (XSyncValue){0, 0});
84 data->resize_counter = counter;
85 data->resize_id.lo = 0;
86 data->resize_id.hi = 0;
87 data->resize_in_progress = false;
88
89 if (counter == None){
90 return SDL_Unsupported();
91 }
92
93 counter_id = counter;
94 X11_XChangeProperty(display, data->xwindow, counter_prop, XA_CARDINAL, 32,
95 PropModeReplace, (unsigned char *)&counter_id, 1);
96
97 return 0;
98}
99
100void X11_TermResizeSync(SDL_Window *window)
101{
102 SDL_WindowData *data = window->internal;
103 Display *display = data->videodata->display;
104 Atom counter_prop = data->videodata->atoms._NET_WM_SYNC_REQUEST_COUNTER;
105 XSyncCounter counter = data->resize_counter;
106
107 X11_XDeleteProperty(display, data->xwindow, counter_prop);
108 if (counter != None) {
109 X11_XSyncDestroyCounter(display, counter);
110 }
111}
112
113void X11_HandleSyncRequest(SDL_Window *window, XClientMessageEvent *event)
114{
115 SDL_WindowData *data = window->internal;
116
117 data->resize_id.lo = event->data.l[2];
118 data->resize_id.hi = event->data.l[3];
119 data->resize_in_progress = false;
120}
121
122void X11_HandleConfigure(SDL_Window *window, XConfigureEvent *event)
123{
124 SDL_WindowData *data = window->internal;
125
126 if (data->resize_id.lo || data->resize_id.hi) {
127 data->resize_in_progress = true;
128 }
129}
130
131void X11_HandlePresent(SDL_Window *window)
132{
133 SDL_WindowData *data = window->internal;
134 Display *display = data->videodata->display;
135 XSyncCounter counter = data->resize_counter;
136
137 if ((counter == None) || (!data->resize_in_progress)) {
138 return;
139 }
140
141 X11_XSyncSetCounter(display, counter, data->resize_id);
142
143 data->resize_id.lo = 0;
144 data->resize_id.hi = 0;
145 data->resize_in_progress = false;
146}
147
148#endif /* SDL_VIDEO_DRIVER_X11 && SDL_VIDEO_DRIVER_X11_XSYNC */
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11xsync.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xsync.h
new file mode 100644
index 0000000..bc747c1
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xsync.h
@@ -0,0 +1,39 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22#include "SDL_internal.h"
23
24#ifndef SDL_x11xsync_h_
25#define SDL_x11xsync_h_
26
27#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
28
29extern void X11_InitXsync(SDL_VideoDevice *_this);
30extern int X11_XsyncIsInitialized(void);
31int X11_InitResizeSync(SDL_Window *window);
32void X11_TermResizeSync(SDL_Window *window);
33void X11_HandleSyncRequest(SDL_Window *window, XClientMessageEvent *event);
34void X11_HandleConfigure(SDL_Window *window, XConfigureEvent *event);
35void X11_HandlePresent(SDL_Window *window);
36
37#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
38
39#endif /* SDL_x11xsync_h_ */
diff --git a/contrib/SDL-3.2.8/src/video/x11/edid-parse.c b/contrib/SDL-3.2.8/src/video/x11/edid-parse.c
new file mode 100644
index 0000000..eca187b
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/edid-parse.c
@@ -0,0 +1,753 @@
1/*
2 * Copyright 2007 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23/* Author: Soren Sandmann <sandmann@redhat.com> */
24#include "SDL_internal.h"
25
26#include "edid.h"
27#include <stdlib.h>
28#include <string.h>
29#include <math.h>
30#include <stdio.h>
31
32#define TRUE 1
33#define FALSE 0
34
35static int
36get_bit (int in, int bit)
37{
38 return (in & (1 << bit)) >> bit;
39}
40
41static int
42get_bits (int in, int begin, int end)
43{
44 int mask = (1 << (end - begin + 1)) - 1;
45
46 return (in >> begin) & mask;
47}
48
49static int
50decode_header (const uchar *edid)
51{
52 if (SDL_memcmp (edid, "\x00\xff\xff\xff\xff\xff\xff\x00", 8) == 0)
53 return TRUE;
54 return FALSE;
55}
56
57static int
58decode_vendor_and_product_identification (const uchar *edid, MonitorInfo *info)
59{
60 int is_model_year;
61
62 /* Manufacturer Code */
63 info->manufacturer_code[0] = get_bits (edid[0x08], 2, 6);
64 info->manufacturer_code[1] = get_bits (edid[0x08], 0, 1) << 3;
65 info->manufacturer_code[1] |= get_bits (edid[0x09], 5, 7);
66 info->manufacturer_code[2] = get_bits (edid[0x09], 0, 4);
67 info->manufacturer_code[3] = '\0';
68
69 info->manufacturer_code[0] += 'A' - 1;
70 info->manufacturer_code[1] += 'A' - 1;
71 info->manufacturer_code[2] += 'A' - 1;
72
73 /* Product Code */
74 info->product_code = edid[0x0b] << 8 | edid[0x0a];
75
76 /* Serial Number */
77 info->serial_number =
78 edid[0x0c] | edid[0x0d] << 8 | edid[0x0e] << 16 | (Uint32)edid[0x0f] << 24;
79
80 /* Week and Year */
81 is_model_year = FALSE;
82 switch (edid[0x10])
83 {
84 case 0x00:
85 info->production_week = -1;
86 break;
87
88 case 0xff:
89 info->production_week = -1;
90 is_model_year = TRUE;
91 break;
92
93 default:
94 info->production_week = edid[0x10];
95 break;
96 }
97
98 if (is_model_year)
99 {
100 info->production_year = -1;
101 info->model_year = 1990 + edid[0x11];
102 }
103 else
104 {
105 info->production_year = 1990 + edid[0x11];
106 info->model_year = -1;
107 }
108
109 return TRUE;
110}
111
112static int
113decode_edid_version (const uchar *edid, MonitorInfo *info)
114{
115 info->major_version = edid[0x12];
116 info->minor_version = edid[0x13];
117
118 return TRUE;
119}
120
121static int
122decode_display_parameters (const uchar *edid, MonitorInfo *info)
123{
124 /* Digital vs Analog */
125 info->is_digital = get_bit (edid[0x14], 7);
126
127 if (info->is_digital)
128 {
129 int bits;
130
131 static const int bit_depth[8] =
132 {
133 -1, 6, 8, 10, 12, 14, 16, -1
134 };
135
136 static const Interface interfaces[6] =
137 {
138 UNDEFINED, DVI, HDMI_A, HDMI_B, MDDI, DISPLAY_PORT
139 };
140
141 bits = get_bits (edid[0x14], 4, 6);
142 info->ad.digital.bits_per_primary = bit_depth[bits];
143
144 bits = get_bits (edid[0x14], 0, 3);
145
146 if (bits <= 5)
147 info->ad.digital.interface = interfaces[bits];
148 else
149 info->ad.digital.interface = UNDEFINED;
150 }
151 else
152 {
153 int bits = get_bits (edid[0x14], 5, 6);
154
155 static const double levels[][3] =
156 {
157 { 0.7, 0.3, 1.0 },
158 { 0.714, 0.286, 1.0 },
159 { 1.0, 0.4, 1.4 },
160 { 0.7, 0.0, 0.7 },
161 };
162
163 info->ad.analog.video_signal_level = levels[bits][0];
164 info->ad.analog.sync_signal_level = levels[bits][1];
165 info->ad.analog.total_signal_level = levels[bits][2];
166
167 info->ad.analog.blank_to_black = get_bit (edid[0x14], 4);
168
169 info->ad.analog.separate_hv_sync = get_bit (edid[0x14], 3);
170 info->ad.analog.composite_sync_on_h = get_bit (edid[0x14], 2);
171 info->ad.analog.composite_sync_on_green = get_bit (edid[0x14], 1);
172
173 info->ad.analog.serration_on_vsync = get_bit (edid[0x14], 0);
174 }
175
176 /* Screen Size / Aspect Ratio */
177 if (edid[0x15] == 0 && edid[0x16] == 0)
178 {
179 info->width_mm = -1;
180 info->height_mm = -1;
181 info->aspect_ratio = -1.0;
182 }
183 else if (edid[0x16] == 0)
184 {
185 info->width_mm = -1;
186 info->height_mm = -1;
187 info->aspect_ratio = 100.0 / (edid[0x15] + 99);
188 }
189 else if (edid[0x15] == 0)
190 {
191 info->width_mm = -1;
192 info->height_mm = -1;
193 info->aspect_ratio = 100.0 / (edid[0x16] + 99);
194 info->aspect_ratio = 1/info->aspect_ratio; /* portrait */
195 }
196 else
197 {
198 info->width_mm = 10 * edid[0x15];
199 info->height_mm = 10 * edid[0x16];
200 }
201
202 /* Gamma */
203 if (edid[0x17] == 0xFF)
204 info->gamma = -1.0;
205 else
206 info->gamma = (edid[0x17] + 100.0) / 100.0;
207
208 /* Features */
209 info->standby = get_bit (edid[0x18], 7);
210 info->suspend = get_bit (edid[0x18], 6);
211 info->active_off = get_bit (edid[0x18], 5);
212
213 if (info->is_digital)
214 {
215 info->ad.digital.rgb444 = TRUE;
216 if (get_bit (edid[0x18], 3))
217 info->ad.digital.ycrcb444 = 1;
218 if (get_bit (edid[0x18], 4))
219 info->ad.digital.ycrcb422 = 1;
220 }
221 else
222 {
223 int bits = get_bits (edid[0x18], 3, 4);
224 ColorType color_type[4] =
225 {
226 MONOCHROME, RGB, OTHER_COLOR, UNDEFINED_COLOR
227 };
228
229 info->ad.analog.color_type = color_type[bits];
230 }
231
232 info->srgb_is_standard = get_bit (edid[0x18], 2);
233
234 /* In 1.3 this is called "has preferred timing" */
235 info->preferred_timing_includes_native = get_bit (edid[0x18], 1);
236
237 /* FIXME: In 1.3 this indicates whether the monitor accepts GTF */
238 info->continuous_frequency = get_bit (edid[0x18], 0);
239 return TRUE;
240}
241
242static double
243decode_fraction (int high, int low)
244{
245 double result = 0.0;
246 int i;
247
248 high = (high << 2) | low;
249
250 for (i = 0; i < 10; ++i)
251 result += get_bit (high, i) * SDL_pow (2, i - 10);
252
253 return result;
254}
255
256static int
257decode_color_characteristics (const uchar *edid, MonitorInfo *info)
258{
259 info->red_x = decode_fraction (edid[0x1b], get_bits (edid[0x19], 6, 7));
260 info->red_y = decode_fraction (edid[0x1c], get_bits (edid[0x19], 5, 4));
261 info->green_x = decode_fraction (edid[0x1d], get_bits (edid[0x19], 2, 3));
262 info->green_y = decode_fraction (edid[0x1e], get_bits (edid[0x19], 0, 1));
263 info->blue_x = decode_fraction (edid[0x1f], get_bits (edid[0x1a], 6, 7));
264 info->blue_y = decode_fraction (edid[0x20], get_bits (edid[0x1a], 4, 5));
265 info->white_x = decode_fraction (edid[0x21], get_bits (edid[0x1a], 2, 3));
266 info->white_y = decode_fraction (edid[0x22], get_bits (edid[0x1a], 0, 1));
267
268 return TRUE;
269}
270
271static int
272decode_established_timings (const uchar *edid, MonitorInfo *info)
273{
274 static const Timing established[][8] =
275 {
276 {
277 { 800, 600, 60 },
278 { 800, 600, 56 },
279 { 640, 480, 75 },
280 { 640, 480, 72 },
281 { 640, 480, 67 },
282 { 640, 480, 60 },
283 { 720, 400, 88 },
284 { 720, 400, 70 }
285 },
286 {
287 { 1280, 1024, 75 },
288 { 1024, 768, 75 },
289 { 1024, 768, 70 },
290 { 1024, 768, 60 },
291 { 1024, 768, 87 },
292 { 832, 624, 75 },
293 { 800, 600, 75 },
294 { 800, 600, 72 }
295 },
296 {
297 { 0, 0, 0 },
298 { 0, 0, 0 },
299 { 0, 0, 0 },
300 { 0, 0, 0 },
301 { 0, 0, 0 },
302 { 0, 0, 0 },
303 { 0, 0, 0 },
304 { 1152, 870, 75 }
305 },
306 };
307
308 int i, j, idx;
309
310 idx = 0;
311 for (i = 0; i < 3; ++i)
312 {
313 for (j = 0; j < 8; ++j)
314 {
315 int byte = edid[0x23 + i];
316
317 if (get_bit (byte, j) && established[i][j].frequency != 0)
318 info->established[idx++] = established[i][j];
319 }
320 }
321 return TRUE;
322}
323
324static int
325decode_standard_timings (const uchar *edid, MonitorInfo *info)
326{
327 int i;
328
329 for (i = 0; i < 8; i++)
330 {
331 int first = edid[0x26 + 2 * i];
332 int second = edid[0x27 + 2 * i];
333
334 if (first != 0x01 && second != 0x01)
335 {
336 int w = 8 * (first + 31);
337 int h = 0;
338
339 switch (get_bits (second, 6, 7))
340 {
341 case 0x00: h = (w / 16) * 10; break;
342 case 0x01: h = (w / 4) * 3; break;
343 case 0x02: h = (w / 5) * 4; break;
344 case 0x03: h = (w / 16) * 9; break;
345 }
346
347 info->standard[i].width = w;
348 info->standard[i].height = h;
349 info->standard[i].frequency = get_bits (second, 0, 5) + 60;
350 }
351 }
352
353 return TRUE;
354}
355
356static void
357decode_lf_string (const uchar *s, int n_chars, char *result)
358{
359 int i;
360 for (i = 0; i < n_chars; ++i)
361 {
362 if (s[i] == 0x0a)
363 {
364 *result++ = '\0';
365 break;
366 }
367 else if (s[i] == 0x00)
368 {
369 /* Convert embedded 0's to spaces */
370 *result++ = ' ';
371 }
372 else
373 {
374 *result++ = s[i];
375 }
376 }
377}
378
379static void
380decode_display_descriptor (const uchar *desc,
381 MonitorInfo *info)
382{
383 switch (desc[0x03])
384 {
385 case 0xFC:
386 decode_lf_string (desc + 5, 13, info->dsc_product_name);
387 break;
388 case 0xFF:
389 decode_lf_string (desc + 5, 13, info->dsc_serial_number);
390 break;
391 case 0xFE:
392 decode_lf_string (desc + 5, 13, info->dsc_string);
393 break;
394 case 0xFD:
395 /* Range Limits */
396 break;
397 case 0xFB:
398 /* Color Point */
399 break;
400 case 0xFA:
401 /* Timing Identifications */
402 break;
403 case 0xF9:
404 /* Color Management */
405 break;
406 case 0xF8:
407 /* Timing Codes */
408 break;
409 case 0xF7:
410 /* Established Timings */
411 break;
412 case 0x10:
413 break;
414 }
415}
416
417static void
418decode_detailed_timing (const uchar *timing,
419 DetailedTiming *detailed)
420{
421 int bits;
422 StereoType stereo[] =
423 {
424 NO_STEREO, NO_STEREO, FIELD_RIGHT, FIELD_LEFT,
425 TWO_WAY_RIGHT_ON_EVEN, TWO_WAY_LEFT_ON_EVEN,
426 FOUR_WAY_INTERLEAVED, SIDE_BY_SIDE
427 };
428
429 detailed->pixel_clock = (timing[0x00] | timing[0x01] << 8) * 10000;
430 detailed->h_addr = timing[0x02] | ((timing[0x04] & 0xf0) << 4);
431 detailed->h_blank = timing[0x03] | ((timing[0x04] & 0x0f) << 8);
432 detailed->v_addr = timing[0x05] | ((timing[0x07] & 0xf0) << 4);
433 detailed->v_blank = timing[0x06] | ((timing[0x07] & 0x0f) << 8);
434 detailed->h_front_porch = timing[0x08] | get_bits (timing[0x0b], 6, 7) << 8;
435 detailed->h_sync = timing[0x09] | get_bits (timing[0x0b], 4, 5) << 8;
436 detailed->v_front_porch =
437 get_bits (timing[0x0a], 4, 7) | get_bits (timing[0x0b], 2, 3) << 4;
438 detailed->v_sync =
439 get_bits (timing[0x0a], 0, 3) | get_bits (timing[0x0b], 0, 1) << 4;
440 detailed->width_mm = timing[0x0c] | get_bits (timing[0x0e], 4, 7) << 8;
441 detailed->height_mm = timing[0x0d] | get_bits (timing[0x0e], 0, 3) << 8;
442 detailed->right_border = timing[0x0f];
443 detailed->top_border = timing[0x10];
444
445 detailed->interlaced = get_bit (timing[0x11], 7);
446
447 /* Stereo */
448 bits = get_bits (timing[0x11], 5, 6) << 1 | get_bit (timing[0x11], 0);
449 detailed->stereo = stereo[bits];
450
451 /* Sync */
452 bits = timing[0x11];
453
454 detailed->digital_sync = get_bit (bits, 4);
455 if (detailed->digital_sync)
456 {
457 detailed->ad.digital.composite = !get_bit (bits, 3);
458
459 if (detailed->ad.digital.composite)
460 {
461 detailed->ad.digital.serrations = get_bit (bits, 2);
462 detailed->ad.digital.negative_vsync = FALSE;
463 }
464 else
465 {
466 detailed->ad.digital.serrations = FALSE;
467 detailed->ad.digital.negative_vsync = !get_bit (bits, 2);
468 }
469
470 detailed->ad.digital.negative_hsync = !get_bit (bits, 0);
471 }
472 else
473 {
474 detailed->ad.analog.bipolar = get_bit (bits, 3);
475 detailed->ad.analog.serrations = get_bit (bits, 2);
476 detailed->ad.analog.sync_on_green = !get_bit (bits, 1);
477 }
478}
479
480static int
481decode_descriptors (const uchar *edid, MonitorInfo *info)
482{
483 int i;
484 int timing_idx;
485
486 timing_idx = 0;
487
488 for (i = 0; i < 4; ++i)
489 {
490 int index = 0x36 + i * 18;
491
492 if (edid[index + 0] == 0x00 && edid[index + 1] == 0x00)
493 {
494 decode_display_descriptor (edid + index, info);
495 }
496 else
497 {
498 decode_detailed_timing (
499 edid + index, &(info->detailed_timings[timing_idx++]));
500 }
501 }
502
503 info->n_detailed_timings = timing_idx;
504
505 return TRUE;
506}
507
508static void
509decode_check_sum (const uchar *edid,
510 MonitorInfo *info)
511{
512 int i;
513 uchar check = 0;
514
515 for (i = 0; i < 128; ++i)
516 check += edid[i];
517
518 info->checksum = check;
519}
520
521MonitorInfo *
522decode_edid (const uchar *edid)
523{
524 MonitorInfo *info = SDL_calloc (1, sizeof (MonitorInfo));
525
526 decode_check_sum (edid, info);
527
528 if (!decode_header (edid) ||
529 !decode_vendor_and_product_identification (edid, info) ||
530 !decode_edid_version (edid, info) ||
531 !decode_display_parameters (edid, info) ||
532 !decode_color_characteristics (edid, info) ||
533 !decode_established_timings (edid, info) ||
534 !decode_standard_timings (edid, info) ||
535 !decode_descriptors (edid, info)) {
536 SDL_free(info);
537 return NULL;
538 }
539
540 return info;
541}
542
543static const char *
544yesno (int v)
545{
546 return v? "yes" : "no";
547}
548
549void
550dump_monitor_info (MonitorInfo *info)
551{
552 int i;
553
554 printf ("Checksum: %d (%s)\n",
555 info->checksum, info->checksum? "incorrect" : "correct");
556 printf ("Manufacturer Code: %s\n", info->manufacturer_code);
557 printf ("Product Code: 0x%x\n", info->product_code);
558 printf ("Serial Number: %u\n", info->serial_number);
559
560 if (info->production_week != -1)
561 printf ("Production Week: %d\n", info->production_week);
562 else
563 printf ("Production Week: unspecified\n");
564
565 if (info->production_year != -1)
566 printf ("Production Year: %d\n", info->production_year);
567 else
568 printf ("Production Year: unspecified\n");
569
570 if (info->model_year != -1)
571 printf ("Model Year: %d\n", info->model_year);
572 else
573 printf ("Model Year: unspecified\n");
574
575 printf ("EDID revision: %d.%d\n", info->major_version, info->minor_version);
576
577 printf ("Display is %s\n", info->is_digital? "digital" : "analog");
578 if (info->is_digital)
579 {
580 const char *interface;
581 if (info->ad.digital.bits_per_primary != -1)
582 printf ("Bits Per Primary: %d\n", info->ad.digital.bits_per_primary);
583 else
584 printf ("Bits Per Primary: undefined\n");
585
586 switch (info->ad.digital.interface)
587 {
588 case DVI: interface = "DVI"; break;
589 case HDMI_A: interface = "HDMI-a"; break;
590 case HDMI_B: interface = "HDMI-b"; break;
591 case MDDI: interface = "MDDI"; break;
592 case DISPLAY_PORT: interface = "DisplayPort"; break;
593 case UNDEFINED: interface = "undefined"; break;
594 default: interface = "unknown"; break;
595 }
596 printf ("Interface: %s\n", interface);
597
598 printf ("RGB 4:4:4: %s\n", yesno (info->ad.digital.rgb444));
599 printf ("YCrCb 4:4:4: %s\n", yesno (info->ad.digital.ycrcb444));
600 printf ("YCrCb 4:2:2: %s\n", yesno (info->ad.digital.ycrcb422));
601 }
602 else
603 {
604 const char *s;
605 printf ("Video Signal Level: %f\n", info->ad.analog.video_signal_level);
606 printf ("Sync Signal Level: %f\n", info->ad.analog.sync_signal_level);
607 printf ("Total Signal Level: %f\n", info->ad.analog.total_signal_level);
608
609 printf ("Blank to Black: %s\n",
610 yesno (info->ad.analog.blank_to_black));
611 printf ("Separate HV Sync: %s\n",
612 yesno (info->ad.analog.separate_hv_sync));
613 printf ("Composite Sync on H: %s\n",
614 yesno (info->ad.analog.composite_sync_on_h));
615 printf ("Serration on VSync: %s\n",
616 yesno (info->ad.analog.serration_on_vsync));
617
618 switch (info->ad.analog.color_type)
619 {
620 case UNDEFINED_COLOR: s = "undefined"; break;
621 case MONOCHROME: s = "monochrome"; break;
622 case RGB: s = "rgb"; break;
623 case OTHER_COLOR: s = "other color"; break;
624 default: s = "unknown"; break;
625 }
626
627 printf ("Color: %s\n", s);
628 }
629
630 if (info->width_mm == -1)
631 printf ("Width: undefined\n");
632 else
633 printf ("Width: %d mm\n", info->width_mm);
634
635 if (info->height_mm == -1)
636 printf ("Height: undefined\n");
637 else
638 printf ("Height: %d mm\n", info->height_mm);
639
640 if (info->aspect_ratio > 0)
641 printf ("Aspect Ratio: %f\n", info->aspect_ratio);
642 else
643 printf ("Aspect Ratio: undefined\n");
644
645 if (info->gamma >= 0)
646 printf ("Gamma: %f\n", info->gamma);
647 else
648 printf ("Gamma: undefined\n");
649
650 printf ("Standby: %s\n", yesno (info->standby));
651 printf ("Suspend: %s\n", yesno (info->suspend));
652 printf ("Active Off: %s\n", yesno (info->active_off));
653
654 printf ("SRGB is Standard: %s\n", yesno (info->srgb_is_standard));
655 printf ("Preferred Timing Includes Native: %s\n",
656 yesno (info->preferred_timing_includes_native));
657 printf ("Continuous Frequency: %s\n", yesno (info->continuous_frequency));
658
659 printf ("Red X: %f\n", info->red_x);
660 printf ("Red Y: %f\n", info->red_y);
661 printf ("Green X: %f\n", info->green_x);
662 printf ("Green Y: %f\n", info->green_y);
663 printf ("Blue X: %f\n", info->blue_x);
664 printf ("Blue Y: %f\n", info->blue_y);
665 printf ("White X: %f\n", info->white_x);
666 printf ("White Y: %f\n", info->white_y);
667
668 printf ("Established Timings:\n");
669
670 for (i = 0; i < 24; ++i)
671 {
672 Timing *timing = &(info->established[i]);
673
674 if (timing->frequency == 0)
675 break;
676
677 printf (" %d x %d @ %d Hz\n",
678 timing->width, timing->height, timing->frequency);
679
680 }
681
682 printf ("Standard Timings:\n");
683 for (i = 0; i < 8; ++i)
684 {
685 Timing *timing = &(info->standard[i]);
686
687 if (timing->frequency == 0)
688 break;
689
690 printf (" %d x %d @ %d Hz\n",
691 timing->width, timing->height, timing->frequency);
692 }
693
694 for (i = 0; i < info->n_detailed_timings; ++i)
695 {
696 DetailedTiming *timing = &(info->detailed_timings[i]);
697 const char *s;
698
699 printf ("Timing%s: \n",
700 (i == 0 && info->preferred_timing_includes_native)?
701 " (Preferred)" : "");
702 printf (" Pixel Clock: %d\n", timing->pixel_clock);
703 printf (" H Addressable: %d\n", timing->h_addr);
704 printf (" H Blank: %d\n", timing->h_blank);
705 printf (" H Front Porch: %d\n", timing->h_front_porch);
706 printf (" H Sync: %d\n", timing->h_sync);
707 printf (" V Addressable: %d\n", timing->v_addr);
708 printf (" V Blank: %d\n", timing->v_blank);
709 printf (" V Front Porch: %d\n", timing->v_front_porch);
710 printf (" V Sync: %d\n", timing->v_sync);
711 printf (" Width: %d mm\n", timing->width_mm);
712 printf (" Height: %d mm\n", timing->height_mm);
713 printf (" Right Border: %d\n", timing->right_border);
714 printf (" Top Border: %d\n", timing->top_border);
715 switch (timing->stereo)
716 {
717 default:
718 case NO_STEREO: s = "No Stereo"; break;
719 case FIELD_RIGHT: s = "Field Sequential, Right on Sync"; break;
720 case FIELD_LEFT: s = "Field Sequential, Left on Sync"; break;
721 case TWO_WAY_RIGHT_ON_EVEN: s = "Two-way, Right on Even"; break;
722 case TWO_WAY_LEFT_ON_EVEN: s = "Two-way, Left on Even"; break;
723 case FOUR_WAY_INTERLEAVED: s = "Four-way Interleaved"; break;
724 case SIDE_BY_SIDE: s = "Side-by-Side"; break;
725 }
726 printf (" Stereo: %s\n", s);
727
728 if (timing->digital_sync)
729 {
730 printf (" Digital Sync:\n");
731 printf (" composite: %s\n", yesno (timing->ad.digital.composite));
732 printf (" serrations: %s\n", yesno (timing->ad.digital.serrations));
733 printf (" negative vsync: %s\n",
734 yesno (timing->ad.digital.negative_vsync));
735 printf (" negative hsync: %s\n",
736 yesno (timing->ad.digital.negative_hsync));
737 }
738 else
739 {
740 printf (" Analog Sync:\n");
741 printf (" bipolar: %s\n", yesno (timing->ad.analog.bipolar));
742 printf (" serrations: %s\n", yesno (timing->ad.analog.serrations));
743 printf (" sync on green: %s\n", yesno (
744 timing->ad.analog.sync_on_green));
745 }
746 }
747
748 printf ("Detailed Product information:\n");
749 printf (" Product Name: %s\n", info->dsc_product_name);
750 printf (" Serial Number: %s\n", info->dsc_serial_number);
751 printf (" Unspecified String: %s\n", info->dsc_string);
752}
753
diff --git a/contrib/SDL-3.2.8/src/video/x11/edid.h b/contrib/SDL-3.2.8/src/video/x11/edid.h
new file mode 100644
index 0000000..4581291
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/edid.h
@@ -0,0 +1,191 @@
1/*
2 * Copyright 2007 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23/* Author: Soren Sandmann <sandmann@redhat.com> */
24
25typedef unsigned char uchar;
26typedef struct MonitorInfo MonitorInfo;
27typedef struct Timing Timing;
28typedef struct DetailedTiming DetailedTiming;
29
30typedef enum
31{
32 UNDEFINED,
33 DVI,
34 HDMI_A,
35 HDMI_B,
36 MDDI,
37 DISPLAY_PORT
38} Interface;
39
40typedef enum
41{
42 UNDEFINED_COLOR,
43 MONOCHROME,
44 RGB,
45 OTHER_COLOR
46} ColorType;
47
48typedef enum
49{
50 NO_STEREO,
51 FIELD_RIGHT,
52 FIELD_LEFT,
53 TWO_WAY_RIGHT_ON_EVEN,
54 TWO_WAY_LEFT_ON_EVEN,
55 FOUR_WAY_INTERLEAVED,
56 SIDE_BY_SIDE
57} StereoType;
58
59struct Timing
60{
61 int width;
62 int height;
63 int frequency;
64};
65
66struct DetailedTiming
67{
68 int pixel_clock;
69 int h_addr;
70 int h_blank;
71 int h_sync;
72 int h_front_porch;
73 int v_addr;
74 int v_blank;
75 int v_sync;
76 int v_front_porch;
77 int width_mm;
78 int height_mm;
79 int right_border;
80 int top_border;
81 int interlaced;
82 StereoType stereo;
83
84 int digital_sync;
85 union
86 {
87 struct
88 {
89 int bipolar;
90 int serrations;
91 int sync_on_green;
92 } analog;
93
94 struct
95 {
96 int composite;
97 int serrations;
98 int negative_vsync;
99 int negative_hsync;
100 } digital;
101 } ad;
102};
103
104struct MonitorInfo
105{
106 int checksum;
107 char manufacturer_code[4];
108 int product_code;
109 unsigned int serial_number;
110
111 int production_week; // -1 if not specified
112 int production_year; // -1 if not specified
113 int model_year; // -1 if not specified
114
115 int major_version;
116 int minor_version;
117
118 int is_digital;
119
120 union
121 {
122 struct
123 {
124 int bits_per_primary;
125 Interface interface;
126 int rgb444;
127 int ycrcb444;
128 int ycrcb422;
129 } digital;
130
131 struct
132 {
133 double video_signal_level;
134 double sync_signal_level;
135 double total_signal_level;
136
137 int blank_to_black;
138
139 int separate_hv_sync;
140 int composite_sync_on_h;
141 int composite_sync_on_green;
142 int serration_on_vsync;
143 ColorType color_type;
144 } analog;
145 } ad;
146
147 int width_mm; // -1 if not specified
148 int height_mm; // -1 if not specified
149 double aspect_ratio; // -1.0 if not specififed
150
151 double gamma; // -1.0 if not specified
152
153 int standby;
154 int suspend;
155 int active_off;
156
157 int srgb_is_standard;
158 int preferred_timing_includes_native;
159 int continuous_frequency;
160
161 double red_x;
162 double red_y;
163 double green_x;
164 double green_y;
165 double blue_x;
166 double blue_y;
167 double white_x;
168 double white_y;
169
170 Timing established[24]; // Terminated by 0x0x0
171 Timing standard[8];
172
173 int n_detailed_timings;
174 DetailedTiming detailed_timings[4]; /* If monitor has a preferred
175 * mode, it is the first one
176 * (whether it has, is
177 * determined by the
178 * preferred_timing_includes
179 * bit.
180 */
181
182 // Optional product description
183 char dsc_serial_number[14];
184 char dsc_product_name[14];
185 char dsc_string[14]; // Unspecified ASCII data
186};
187
188MonitorInfo *decode_edid(const uchar *data);
189void dump_monitor_info(MonitorInfo *info);
190char *make_display_name(const char *output_name,
191 const MonitorInfo *info);
diff --git a/contrib/SDL-3.2.8/src/video/x11/xsettings-client.c b/contrib/SDL-3.2.8/src/video/x11/xsettings-client.c
new file mode 100644
index 0000000..8fb1cd3
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/xsettings-client.c
@@ -0,0 +1,859 @@
1/*
2 * Copyright © 2001, 2007 Red Hat, Inc.
3 * Copyright 2024 Igalia S.L.
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the name of Red Hat not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission. Red Hat makes no representations about the
12 * suitability of this software for any purpose. It is provided "as is"
13 * without express or implied warranty.
14 *
15 * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT
17 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
19 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 *
22 * Author: Owen Taylor, Red Hat, Inc.
23 */
24
25#include "SDL_internal.h"
26
27#ifdef SDL_VIDEO_DRIVER_X11
28
29#include "SDL_x11video.h"
30
31#include <limits.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35
36#include "xsettings-client.h"
37
38struct _XSettingsClient
39{
40 Display *display;
41 int screen;
42 XSettingsNotifyFunc notify;
43 XSettingsWatchFunc watch;
44 void *cb_data;
45
46 XSettingsGrabFunc grab;
47 XSettingsGrabFunc ungrab;
48
49 Window manager_window;
50 Atom manager_atom;
51 Atom selection_atom;
52 Atom xsettings_atom;
53
54 XSettingsList *settings;
55};
56
57static void
58notify_changes (XSettingsClient *client,
59 XSettingsList *old_list)
60{
61 XSettingsList *old_iter = old_list;
62 XSettingsList *new_iter = client->settings;
63
64 if (!client->notify)
65 return;
66
67 while (old_iter || new_iter)
68 {
69 int cmp;
70
71 if (old_iter && new_iter)
72 cmp = strcmp (old_iter->setting->name, new_iter->setting->name);
73 else if (old_iter)
74 cmp = -1;
75 else
76 cmp = 1;
77
78 if (cmp < 0)
79 {
80 client->notify (old_iter->setting->name,
81 XSETTINGS_ACTION_DELETED,
82 NULL,
83 client->cb_data);
84 }
85 else if (cmp == 0)
86 {
87 if (!xsettings_setting_equal (old_iter->setting,
88 new_iter->setting))
89 client->notify (old_iter->setting->name,
90 XSETTINGS_ACTION_CHANGED,
91 new_iter->setting,
92 client->cb_data);
93 }
94 else
95 {
96 client->notify (new_iter->setting->name,
97 XSETTINGS_ACTION_NEW,
98 new_iter->setting,
99 client->cb_data);
100 }
101
102 if (old_iter)
103 old_iter = old_iter->next;
104 if (new_iter)
105 new_iter = new_iter->next;
106 }
107}
108
109static int
110ignore_errors (Display *display, XErrorEvent *event)
111{
112 return True;
113}
114
115static char local_byte_order = '\0';
116
117#define BYTES_LEFT(buffer) ((buffer)->data + (buffer)->len - (buffer)->pos)
118
119static XSettingsResult
120fetch_card16 (XSettingsBuffer *buffer,
121 CARD16 *result)
122{
123 CARD16 x;
124
125 if (BYTES_LEFT (buffer) < 2)
126 return XSETTINGS_ACCESS;
127
128 x = *(CARD16 *)buffer->pos;
129 buffer->pos += 2;
130
131 if (buffer->byte_order == local_byte_order)
132 *result = x;
133 else
134 *result = (x << 8) | (x >> 8);
135
136 return XSETTINGS_SUCCESS;
137}
138
139static XSettingsResult
140fetch_ushort (XSettingsBuffer *buffer,
141 unsigned short *result)
142{
143 CARD16 x;
144 XSettingsResult r;
145
146 r = fetch_card16 (buffer, &x);
147 if (r == XSETTINGS_SUCCESS)
148 *result = x;
149
150 return r;
151}
152
153static XSettingsResult
154fetch_card32 (XSettingsBuffer *buffer,
155 CARD32 *result)
156{
157 CARD32 x;
158
159 if (BYTES_LEFT (buffer) < 4)
160 return XSETTINGS_ACCESS;
161
162 x = *(CARD32 *)buffer->pos;
163 buffer->pos += 4;
164
165 if (buffer->byte_order == local_byte_order)
166 *result = x;
167 else
168 *result = (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);
169
170 return XSETTINGS_SUCCESS;
171}
172
173static XSettingsResult
174fetch_card8 (XSettingsBuffer *buffer,
175 CARD8 *result)
176{
177 if (BYTES_LEFT (buffer) < 1)
178 return XSETTINGS_ACCESS;
179
180 *result = *(CARD8 *)buffer->pos;
181 buffer->pos += 1;
182
183 return XSETTINGS_SUCCESS;
184}
185
186#define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1)))
187
188static XSettingsList *
189parse_settings (unsigned char *data,
190 size_t len)
191{
192 XSettingsBuffer buffer;
193 XSettingsResult result = XSETTINGS_SUCCESS;
194 XSettingsList *settings = NULL;
195 CARD32 serial;
196 CARD32 n_entries;
197 CARD32 i;
198 XSettingsSetting *setting = NULL;
199 char buffer_byte_order = '\0';
200
201 local_byte_order = xsettings_byte_order ();
202
203 buffer.pos = buffer.data = data;
204 buffer.len = len;
205 buffer.byte_order = '\0';
206
207 result = fetch_card8 (&buffer, (unsigned char *) &buffer_byte_order);
208 if (buffer_byte_order != MSBFirst &&
209 buffer_byte_order != LSBFirst)
210 {
211 fprintf (stderr, "Invalid byte order in XSETTINGS property\n");
212 result = XSETTINGS_FAILED;
213 goto out;
214 }
215
216 buffer.byte_order = buffer_byte_order;
217 buffer.pos += 3;
218
219 result = fetch_card32 (&buffer, &serial);
220 if (result != XSETTINGS_SUCCESS)
221 goto out;
222
223 result = fetch_card32 (&buffer, &n_entries);
224 if (result != XSETTINGS_SUCCESS)
225 goto out;
226
227 for (i = 0; i < n_entries; i++)
228 {
229 CARD8 type;
230 CARD16 name_len;
231 CARD32 v_int;
232 size_t pad_len;
233
234 result = fetch_card8 (&buffer, &type);
235 if (result != XSETTINGS_SUCCESS)
236 goto out;
237
238 buffer.pos += 1;
239
240 result = fetch_card16 (&buffer, &name_len);
241 if (result != XSETTINGS_SUCCESS)
242 goto out;
243
244 pad_len = XSETTINGS_PAD(name_len, 4);
245 if (BYTES_LEFT (&buffer) < pad_len)
246 {
247 result = XSETTINGS_ACCESS;
248 goto out;
249 }
250
251 setting = malloc (sizeof *setting);
252 if (!setting)
253 {
254 result = XSETTINGS_NO_MEM;
255 goto out;
256 }
257 setting->type = XSETTINGS_TYPE_INT; /* No allocated memory */
258
259 setting->name = malloc (name_len + 1);
260 if (!setting->name)
261 {
262 result = XSETTINGS_NO_MEM;
263 goto out;
264 }
265
266 memcpy (setting->name, buffer.pos, name_len);
267 setting->name[name_len] = '\0';
268 buffer.pos += pad_len;
269
270 result = fetch_card32 (&buffer, &v_int);
271 if (result != XSETTINGS_SUCCESS)
272 goto out;
273 setting->last_change_serial = v_int;
274
275 switch (type)
276 {
277 case XSETTINGS_TYPE_INT:
278 result = fetch_card32 (&buffer, &v_int);
279 if (result != XSETTINGS_SUCCESS)
280 goto out;
281
282 setting->data.v_int = (INT32)v_int;
283 break;
284 case XSETTINGS_TYPE_STRING:
285 result = fetch_card32 (&buffer, &v_int);
286 if (result != XSETTINGS_SUCCESS)
287 goto out;
288
289 pad_len = XSETTINGS_PAD (v_int, 4);
290 if (v_int + 1 == 0 || /* Guard against wrap-around */
291 BYTES_LEFT (&buffer) < pad_len)
292 {
293 result = XSETTINGS_ACCESS;
294 goto out;
295 }
296
297 setting->data.v_string = malloc (v_int + 1);
298 if (!setting->data.v_string)
299 {
300 result = XSETTINGS_NO_MEM;
301 goto out;
302 }
303
304 memcpy (setting->data.v_string, buffer.pos, v_int);
305 setting->data.v_string[v_int] = '\0';
306 buffer.pos += pad_len;
307
308 break;
309 case XSETTINGS_TYPE_COLOR:
310 result = fetch_ushort (&buffer, &setting->data.v_color.red);
311 if (result != XSETTINGS_SUCCESS)
312 goto out;
313 result = fetch_ushort (&buffer, &setting->data.v_color.green);
314 if (result != XSETTINGS_SUCCESS)
315 goto out;
316 result = fetch_ushort (&buffer, &setting->data.v_color.blue);
317 if (result != XSETTINGS_SUCCESS)
318 goto out;
319 result = fetch_ushort (&buffer, &setting->data.v_color.alpha);
320 if (result != XSETTINGS_SUCCESS)
321 goto out;
322
323 break;
324 default:
325 /* Quietly ignore unknown types */
326 break;
327 }
328
329 setting->type = type;
330
331 result = xsettings_list_insert (&settings, setting);
332 if (result != XSETTINGS_SUCCESS)
333 goto out;
334
335 setting = NULL;
336 }
337
338 out:
339
340 if (result != XSETTINGS_SUCCESS)
341 {
342 switch (result)
343 {
344 case XSETTINGS_NO_MEM:
345 fprintf(stderr, "Out of memory reading XSETTINGS property\n");
346 break;
347 case XSETTINGS_ACCESS:
348 fprintf(stderr, "Invalid XSETTINGS property (read off end)\n");
349 break;
350 case XSETTINGS_DUPLICATE_ENTRY:
351 fprintf (stderr, "Duplicate XSETTINGS entry for '%s'\n", setting->name);
352 SDL_FALLTHROUGH;
353 case XSETTINGS_FAILED:
354 SDL_FALLTHROUGH;
355 case XSETTINGS_SUCCESS:
356 SDL_FALLTHROUGH;
357 case XSETTINGS_NO_ENTRY:
358 break;
359 }
360
361 if (setting)
362 xsettings_setting_free (setting);
363
364 xsettings_list_free (settings);
365 settings = NULL;
366
367 }
368
369 return settings;
370}
371
372static void
373read_settings (XSettingsClient *client)
374{
375 Atom type;
376 int format;
377 unsigned long n_items;
378 unsigned long bytes_after;
379 unsigned char *data;
380 int result;
381
382 int (*old_handler) (Display *, XErrorEvent *);
383
384 XSettingsList *old_list = client->settings;
385
386 client->settings = NULL;
387
388 if (client->manager_window)
389 {
390 old_handler = X11_XSetErrorHandler (ignore_errors);
391 result = X11_XGetWindowProperty (client->display, client->manager_window,
392 client->xsettings_atom, 0, LONG_MAX,
393 False, client->xsettings_atom,
394 &type, &format, &n_items, &bytes_after, &data);
395 X11_XSetErrorHandler (old_handler);
396
397 if (result == Success && type != None)
398 {
399 if (type != client->xsettings_atom)
400 {
401 fprintf (stderr, "Invalid type for XSETTINGS property");
402 }
403 else if (format != 8)
404 {
405 fprintf (stderr, "Invalid format for XSETTINGS property %d", format);
406 }
407 else
408 client->settings = parse_settings (data, n_items);
409
410 X11_XFree (data);
411 }
412 }
413
414 notify_changes (client, old_list);
415 xsettings_list_free (old_list);
416}
417
418static void
419add_events (Display *display,
420 Window window,
421 long mask)
422{
423 XWindowAttributes attr;
424
425 X11_XGetWindowAttributes (display, window, &attr);
426 X11_XSelectInput (display, window, attr.your_event_mask | mask);
427}
428
429static void
430check_manager_window (XSettingsClient *client)
431{
432 if (client->manager_window && client->watch)
433 client->watch (client->manager_window, False, 0, client->cb_data);
434
435 if (client->grab)
436 client->grab (client->display);
437 else
438 X11_XGrabServer (client->display);
439
440 client->manager_window = X11_XGetSelectionOwner (client->display,
441 client->selection_atom);
442 if (client->manager_window)
443 X11_XSelectInput (client->display, client->manager_window,
444 PropertyChangeMask | StructureNotifyMask);
445
446 if (client->ungrab)
447 client->ungrab (client->display);
448 else
449 X11_XUngrabServer (client->display);
450
451 X11_XFlush (client->display);
452
453 if (client->manager_window && client->watch)
454 {
455 if (!client->watch (client->manager_window, True,
456 PropertyChangeMask | StructureNotifyMask,
457 client->cb_data))
458 {
459 /* Inability to watch the window probably means that it was destroyed
460 * after we ungrabbed
461 */
462 client->manager_window = None;
463 return;
464 }
465 }
466
467
468 read_settings (client);
469}
470
471XSettingsClient *
472xsettings_client_new (Display *display,
473 int screen,
474 XSettingsNotifyFunc notify,
475 XSettingsWatchFunc watch,
476 void *cb_data)
477{
478 return xsettings_client_new_with_grab_funcs (display, screen, notify, watch, cb_data,
479 NULL, NULL);
480}
481
482XSettingsClient *
483xsettings_client_new_with_grab_funcs (Display *display,
484 int screen,
485 XSettingsNotifyFunc notify,
486 XSettingsWatchFunc watch,
487 void *cb_data,
488 XSettingsGrabFunc grab,
489 XSettingsGrabFunc ungrab)
490{
491 XSettingsClient *client;
492 char buffer[256];
493 char *atom_names[3];
494 Atom atoms[3];
495
496 client = malloc (sizeof *client);
497 if (!client)
498 return NULL;
499
500 client->display = display;
501 client->screen = screen;
502 client->notify = notify;
503 client->watch = watch;
504 client->cb_data = cb_data;
505 client->grab = grab;
506 client->ungrab = ungrab;
507
508 client->manager_window = None;
509 client->settings = NULL;
510
511 sprintf(buffer, "_XSETTINGS_S%d", screen);
512 atom_names[0] = buffer;
513 atom_names[1] = "_XSETTINGS_SETTINGS";
514 atom_names[2] = "MANAGER";
515
516#ifdef HAVE_XINTERNATOMS
517 XInternAtoms (display, atom_names, 3, False, atoms);
518#else
519 atoms[0] = X11_XInternAtom (display, atom_names[0], False);
520 atoms[1] = X11_XInternAtom (display, atom_names[1], False);
521 atoms[2] = X11_XInternAtom (display, atom_names[2], False);
522#endif
523
524 client->selection_atom = atoms[0];
525 client->xsettings_atom = atoms[1];
526 client->manager_atom = atoms[2];
527
528 /* Select on StructureNotify so we get MANAGER events
529 */
530 add_events (display, RootWindow (display, screen), StructureNotifyMask);
531
532 if (client->watch)
533 client->watch (RootWindow (display, screen), True, StructureNotifyMask,
534 client->cb_data);
535
536 check_manager_window (client);
537
538 return client;
539}
540
541
542void
543xsettings_client_set_grab_func (XSettingsClient *client,
544 XSettingsGrabFunc grab)
545{
546 client->grab = grab;
547}
548
549void
550xsettings_client_set_ungrab_func (XSettingsClient *client,
551 XSettingsGrabFunc ungrab)
552{
553 client->ungrab = ungrab;
554}
555
556void
557xsettings_client_destroy (XSettingsClient *client)
558{
559 if (client->watch)
560 client->watch (RootWindow (client->display, client->screen),
561 False, 0, client->cb_data);
562 if (client->manager_window && client->watch)
563 client->watch (client->manager_window, False, 0, client->cb_data);
564
565 xsettings_list_free (client->settings);
566 free (client);
567}
568
569XSettingsResult
570xsettings_client_get_setting (XSettingsClient *client,
571 const char *name,
572 XSettingsSetting **setting)
573{
574 XSettingsSetting *search = xsettings_list_lookup (client->settings, name);
575 if (search)
576 {
577 *setting = xsettings_setting_copy (search);
578 return *setting ? XSETTINGS_SUCCESS : XSETTINGS_NO_MEM;
579 }
580 else
581 return XSETTINGS_NO_ENTRY;
582}
583
584Bool
585xsettings_client_process_event (XSettingsClient *client,
586 const XEvent *xev)
587{
588 /* The checks here will not unlikely cause us to reread
589 * the properties from the manager window a number of
590 * times when the manager changes from A->B. But manager changes
591 * are going to be pretty rare.
592 */
593 if (xev->xany.window == RootWindow (client->display, client->screen))
594 {
595 if (xev->xany.type == ClientMessage &&
596 xev->xclient.message_type == client->manager_atom &&
597 xev->xclient.data.l[1] == client->selection_atom)
598 {
599 check_manager_window (client);
600 return True;
601 }
602 }
603 else if (xev->xany.window == client->manager_window)
604 {
605 if (xev->xany.type == DestroyNotify)
606 {
607 check_manager_window (client);
608 return False;
609 }
610 else if (xev->xany.type == PropertyNotify)
611 {
612 read_settings (client);
613 return True;
614 }
615 }
616
617 return False;
618}
619
620XSettingsSetting *
621xsettings_setting_copy (XSettingsSetting *setting)
622{
623 XSettingsSetting *result;
624 size_t str_len;
625
626 result = malloc (sizeof *result);
627 if (!result)
628 return NULL;
629
630 str_len = strlen (setting->name);
631 result->name = malloc (str_len + 1);
632 if (!result->name)
633 goto err;
634
635 memcpy (result->name, setting->name, str_len + 1);
636
637 result->type = setting->type;
638
639 switch (setting->type)
640 {
641 case XSETTINGS_TYPE_INT:
642 result->data.v_int = setting->data.v_int;
643 break;
644 case XSETTINGS_TYPE_COLOR:
645 result->data.v_color = setting->data.v_color;
646 break;
647 case XSETTINGS_TYPE_STRING:
648 str_len = strlen (setting->data.v_string);
649 result->data.v_string = malloc (str_len + 1);
650 if (!result->data.v_string)
651 goto err;
652
653 memcpy (result->data.v_string, setting->data.v_string, str_len + 1);
654 break;
655 }
656
657 result->last_change_serial = setting->last_change_serial;
658
659 return result;
660
661 err:
662 if (result->name)
663 free (result->name);
664 free (result);
665
666 return NULL;
667}
668
669XSettingsList *
670xsettings_list_copy (XSettingsList *list)
671{
672 XSettingsList *new = NULL;
673 XSettingsList *old_iter = list;
674 XSettingsList *new_iter = NULL;
675
676 while (old_iter)
677 {
678 XSettingsList *new_node;
679
680 new_node = malloc (sizeof *new_node);
681 if (!new_node)
682 goto error;
683
684 new_node->setting = xsettings_setting_copy (old_iter->setting);
685 if (!new_node->setting)
686 {
687 free (new_node);
688 goto error;
689 }
690
691 if (new_iter)
692 new_iter->next = new_node;
693 else
694 {
695 new = new_node;
696 new->next = NULL;
697 }
698
699
700 new_iter = new_node;
701
702 old_iter = old_iter->next;
703 }
704
705 return new;
706
707 error:
708 xsettings_list_free (new);
709 return NULL;
710}
711
712int
713xsettings_setting_equal (XSettingsSetting *setting_a,
714 XSettingsSetting *setting_b)
715{
716 if (setting_a->type != setting_b->type)
717 return 0;
718
719 if (strcmp (setting_a->name, setting_b->name) != 0)
720 return 0;
721
722 switch (setting_a->type)
723 {
724 case XSETTINGS_TYPE_INT:
725 return setting_a->data.v_int == setting_b->data.v_int;
726 case XSETTINGS_TYPE_COLOR:
727 return (setting_a->data.v_color.red == setting_b->data.v_color.red &&
728 setting_a->data.v_color.green == setting_b->data.v_color.green &&
729 setting_a->data.v_color.blue == setting_b->data.v_color.blue &&
730 setting_a->data.v_color.alpha == setting_b->data.v_color.alpha);
731 case XSETTINGS_TYPE_STRING:
732 return strcmp (setting_a->data.v_string, setting_b->data.v_string) == 0;
733 }
734
735 return 0;
736}
737
738void
739xsettings_setting_free (XSettingsSetting *setting)
740{
741 if (setting->type == XSETTINGS_TYPE_STRING)
742 free (setting->data.v_string);
743
744 if (setting->name)
745 free (setting->name);
746
747 free (setting);
748}
749
750void
751xsettings_list_free (XSettingsList *list)
752{
753 while (list)
754 {
755 XSettingsList *next = list->next;
756
757 xsettings_setting_free (list->setting);
758 free (list);
759
760 list = next;
761 }
762}
763
764XSettingsResult
765xsettings_list_insert (XSettingsList **list,
766 XSettingsSetting *setting)
767{
768 XSettingsList *node;
769 XSettingsList *iter;
770 XSettingsList *last = NULL;
771
772 node = malloc (sizeof *node);
773 if (!node)
774 return XSETTINGS_NO_MEM;
775 node->setting = setting;
776
777 iter = *list;
778 while (iter)
779 {
780 int cmp = strcmp (setting->name, iter->setting->name);
781
782 if (cmp < 0)
783 break;
784 else if (cmp == 0)
785 {
786 free (node);
787 return XSETTINGS_DUPLICATE_ENTRY;
788 }
789
790 last = iter;
791 iter = iter->next;
792 }
793
794 if (last)
795 last->next = node;
796 else
797 *list = node;
798
799 node->next = iter;
800
801 return XSETTINGS_SUCCESS;
802}
803
804XSettingsResult
805xsettings_list_delete (XSettingsList **list,
806 const char *name)
807{
808 XSettingsList *iter;
809 XSettingsList *last = NULL;
810
811 iter = *list;
812 while (iter)
813 {
814 if (strcmp (name, iter->setting->name) == 0)
815 {
816 if (last)
817 last->next = iter->next;
818 else
819 *list = iter->next;
820
821 xsettings_setting_free (iter->setting);
822 free (iter);
823
824 return XSETTINGS_SUCCESS;
825 }
826
827 last = iter;
828 iter = iter->next;
829 }
830
831 return XSETTINGS_FAILED;
832}
833
834XSettingsSetting *
835xsettings_list_lookup (XSettingsList *list,
836 const char *name)
837{
838 XSettingsList *iter;
839
840 iter = list;
841 while (iter)
842 {
843 if (strcmp (name, iter->setting->name) == 0)
844 return iter->setting;
845
846 iter = iter->next;
847 }
848
849 return NULL;
850}
851
852char
853xsettings_byte_order (void)
854{
855 CARD32 myint = 0x01020304;
856 return (*(char *)&myint == 1) ? MSBFirst : LSBFirst;
857}
858
859#endif /* SDL_VIDEO_DRIVER_X11 */
diff --git a/contrib/SDL-3.2.8/src/video/x11/xsettings-client.h b/contrib/SDL-3.2.8/src/video/x11/xsettings-client.h
new file mode 100644
index 0000000..863c6f9
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/x11/xsettings-client.h
@@ -0,0 +1,153 @@
1/*
2 * Copyright © 2001, 2007 Red Hat, Inc.
3 * Copyright 2024 Igalia S.L.
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the name of Red Hat not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission. Red Hat makes no representations about the
12 * suitability of this software for any purpose. It is provided "as is"
13 * without express or implied warranty.
14 *
15 * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT
17 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
19 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 *
22 * Author: Owen Taylor, Red Hat, Inc.
23 */
24#ifndef XSETTINGS_CLIENT_H
25#define XSETTINGS_CLIENT_H
26
27#ifdef __cplusplus
28extern "C" {
29#endif /* __cplusplus */
30
31typedef struct _XSettingsBuffer XSettingsBuffer;
32typedef struct _XSettingsColor XSettingsColor;
33typedef struct _XSettingsList XSettingsList;
34typedef struct _XSettingsSetting XSettingsSetting;
35
36/* Types of settings possible. Enum values correspond to
37 * protocol values.
38 */
39typedef enum
40{
41 XSETTINGS_TYPE_INT = 0,
42 XSETTINGS_TYPE_STRING = 1,
43 XSETTINGS_TYPE_COLOR = 2
44} XSettingsType;
45
46typedef enum
47{
48 XSETTINGS_SUCCESS,
49 XSETTINGS_NO_MEM,
50 XSETTINGS_ACCESS,
51 XSETTINGS_FAILED,
52 XSETTINGS_NO_ENTRY,
53 XSETTINGS_DUPLICATE_ENTRY
54} XSettingsResult;
55
56struct _XSettingsBuffer
57{
58 char byte_order;
59 size_t len;
60 unsigned char *data;
61 unsigned char *pos;
62};
63
64struct _XSettingsColor
65{
66 unsigned short red, green, blue, alpha;
67};
68
69struct _XSettingsList
70{
71 XSettingsSetting *setting;
72 XSettingsList *next;
73};
74
75struct _XSettingsSetting
76{
77 char *name;
78 XSettingsType type;
79
80 union {
81 int v_int;
82 char *v_string;
83 XSettingsColor v_color;
84 } data;
85
86 unsigned long last_change_serial;
87};
88
89XSettingsSetting *xsettings_setting_copy (XSettingsSetting *setting);
90void xsettings_setting_free (XSettingsSetting *setting);
91int xsettings_setting_equal (XSettingsSetting *setting_a,
92 XSettingsSetting *setting_b);
93
94void xsettings_list_free (XSettingsList *list);
95XSettingsList *xsettings_list_copy (XSettingsList *list);
96XSettingsResult xsettings_list_insert (XSettingsList **list,
97 XSettingsSetting *setting);
98XSettingsSetting *xsettings_list_lookup (XSettingsList *list,
99 const char *name);
100XSettingsResult xsettings_list_delete (XSettingsList **list,
101 const char *name);
102
103char xsettings_byte_order (void);
104
105#define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1)))
106
107typedef struct _XSettingsClient XSettingsClient;
108
109typedef enum
110{
111 XSETTINGS_ACTION_NEW,
112 XSETTINGS_ACTION_CHANGED,
113 XSETTINGS_ACTION_DELETED
114} XSettingsAction;
115
116typedef void (*XSettingsNotifyFunc) (const char *name,
117 XSettingsAction action,
118 XSettingsSetting *setting,
119 void *cb_data);
120typedef Bool (*XSettingsWatchFunc) (Window window,
121 Bool is_start,
122 long mask,
123 void *cb_data);
124typedef void (*XSettingsGrabFunc) (Display *display);
125
126XSettingsClient *xsettings_client_new (Display *display,
127 int screen,
128 XSettingsNotifyFunc notify,
129 XSettingsWatchFunc watch,
130 void *cb_data);
131XSettingsClient *xsettings_client_new_with_grab_funcs (Display *display,
132 int screen,
133 XSettingsNotifyFunc notify,
134 XSettingsWatchFunc watch,
135 void *cb_data,
136 XSettingsGrabFunc grab,
137 XSettingsGrabFunc ungrab);
138void xsettings_client_set_grab_func (XSettingsClient *client,
139 XSettingsGrabFunc grab);
140void xsettings_client_set_ungrab_func (XSettingsClient *client,
141 XSettingsGrabFunc ungrab);
142void xsettings_client_destroy (XSettingsClient *client);
143Bool xsettings_client_process_event (XSettingsClient *client,
144 const XEvent *xev);
145XSettingsResult xsettings_client_get_setting (XSettingsClient *client,
146 const char *name,
147 XSettingsSetting **setting);
148
149#ifdef __cplusplus
150}
151#endif /* __cplusplus */
152
153#endif /* XSETTINGS_CLIENT_H */