summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/video/x11/SDL_x11video.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/x11/SDL_x11video.c')
-rw-r--r--contrib/SDL-3.2.8/src/video/x11/SDL_x11video.c505
1 files changed, 505 insertions, 0 deletions
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