Atlas - SDL_x11video.c
Home / ext / SDL / src / video / x11 Lines: 3 | Size: 17035 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#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 "../../core/linux/SDL_progressbar.h" 29#include "../../events/SDL_keyboard_c.h" 30#include "../../events/SDL_mouse_c.h" 31#include "../SDL_pixels_c.h" 32#include "../SDL_sysvideo.h" 33 34#include "SDL_x11framebuffer.h" 35#include "SDL_x11pen.h" 36#include "SDL_x11touch.h" 37#include "SDL_x11video.h" 38#include "SDL_x11xfixes.h" 39#include "SDL_x11xinput2.h" 40#include "SDL_x11messagebox.h" 41#include "SDL_x11shape.h" 42#include "SDL_x11xsync.h" 43#include "SDL_x11xtest.h" 44 45#ifdef SDL_VIDEO_OPENGL_EGL 46#include "SDL_x11opengles.h" 47#endif 48 49// Initialization/Query functions 50static bool X11_VideoInit(SDL_VideoDevice *_this); 51static void X11_VideoQuit(SDL_VideoDevice *_this); 52 53// X11 driver bootstrap functions 54 55static void X11_DeleteDevice(SDL_VideoDevice *device) 56{ 57 SDL_VideoData *data = device->internal; 58 if (device->vulkan_config.loader_handle) { 59 device->Vulkan_UnloadLibrary(device); 60 } 61 if (data->display) { 62 X11_XCloseDisplay(data->display); 63 } 64 if (data->request_display) { 65 X11_XCloseDisplay(data->request_display); 66 } 67 SDL_free(data->windowlist); 68 SDL_free(device->internal); 69 SDL_free(device); 70 71 SDL_X11_UnloadSymbols(); 72} 73 74static bool X11_IsXWayland(Display *d) 75{ 76 int opcode, event, error; 77 return X11_XQueryExtension(d, "XWAYLAND", &opcode, &event, &error) == True; 78} 79 80static bool X11_IsWSL(void) 81{ 82#ifdef SDL_PLATFORM_LINUX 83 if (SDL_GetPathInfo("/proc/sys/fs/binfmt_misc/WSLInterop", NULL) || SDL_GetPathInfo("/run/WSL", NULL)) { // if either of these exist, we're on WSL. 84 return true; 85 } 86#endif 87 return false; 88} 89 90static SDL_VideoDevice *X11_CreateDevice(void) 91{ 92 SDL_VideoDevice *device; 93 SDL_VideoData *data; 94 const char *display = NULL; // Use the DISPLAY environment variable 95 Display *x11_display = NULL; 96 97 if (!SDL_X11_LoadSymbols()) { 98 return NULL; 99 } 100 101 /* Need for threading gl calls. This is also required for the proprietary 102 nVidia driver to be threaded. */ 103 X11_XInitThreads(); 104 105 // Open the display first to be sure that X11 is available 106 x11_display = X11_XOpenDisplay(display); 107 108 if (!x11_display) { 109 SDL_X11_UnloadSymbols(); 110 111 const char *session = SDL_getenv("XDG_SESSION_TYPE"); 112 if (session && SDL_strcasecmp(session, "wayland") == 0) { 113 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Failed to connect to the X11 (XWayland) display server"); 114 } else { 115 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Failed to connect to the X11 display server"); 116 } 117 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#ifdef X11_DEBUG 150 X11_XSynchronize(data->display, True); 151#endif 152 153 /* Steam Deck will have an on-screen keyboard, so check their environment 154 * variable so we can make use of SDL_StartTextInput. 155 */ 156 data->is_steam_deck = SDL_GetHintBoolean("SteamDeck", false); 157 158 // Set the function pointers 159 device->VideoInit = X11_VideoInit; 160 device->VideoQuit = X11_VideoQuit; 161 device->ResetTouch = X11_ResetTouch; 162 device->GetDisplayModes = X11_GetDisplayModes; 163 device->GetDisplayBounds = X11_GetDisplayBounds; 164 device->GetDisplayUsableBounds = X11_GetDisplayUsableBounds; 165 device->GetWindowICCProfile = X11_GetWindowICCProfile; 166 device->SetDisplayMode = X11_SetDisplayMode; 167 device->SuspendScreenSaver = X11_SuspendScreenSaver; 168 device->PumpEvents = X11_PumpEvents; 169 device->WaitEventTimeout = X11_WaitEventTimeout; 170 device->SendWakeupEvent = X11_SendWakeupEvent; 171 172 device->CreateSDLWindow = X11_CreateWindow; 173 device->SetWindowTitle = X11_SetWindowTitle; 174 device->SetWindowIcon = X11_SetWindowIcon; 175 device->SetWindowPosition = X11_SetWindowPosition; 176 device->SetWindowSize = X11_SetWindowSize; 177 device->SetWindowMinimumSize = X11_SetWindowMinimumSize; 178 device->SetWindowMaximumSize = X11_SetWindowMaximumSize; 179 device->SetWindowAspectRatio = X11_SetWindowAspectRatio; 180 device->GetWindowBordersSize = X11_GetWindowBordersSize; 181 device->SetWindowOpacity = X11_SetWindowOpacity; 182 device->SetWindowParent = X11_SetWindowParent; 183 device->SetWindowModal = X11_SetWindowModal; 184 device->ShowWindow = X11_ShowWindow; 185 device->HideWindow = X11_HideWindow; 186 device->RaiseWindow = X11_RaiseWindow; 187 device->MaximizeWindow = X11_MaximizeWindow; 188 device->MinimizeWindow = X11_MinimizeWindow; 189 device->RestoreWindow = X11_RestoreWindow; 190 device->SetWindowBordered = X11_SetWindowBordered; 191 device->SetWindowResizable = X11_SetWindowResizable; 192 device->SetWindowAlwaysOnTop = X11_SetWindowAlwaysOnTop; 193 device->SetWindowFullscreen = X11_SetWindowFullscreen; 194 device->SetWindowMouseGrab = X11_SetWindowMouseGrab; 195 device->SetWindowKeyboardGrab = X11_SetWindowKeyboardGrab; 196 device->DestroyWindow = X11_DestroyWindow; 197 device->CreateWindowFramebuffer = X11_CreateWindowFramebuffer; 198 device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer; 199 device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer; 200 device->SetWindowHitTest = X11_SetWindowHitTest; 201 device->AcceptDragAndDrop = X11_AcceptDragAndDrop; 202 device->UpdateWindowShape = X11_UpdateWindowShape; 203 device->FlashWindow = X11_FlashWindow; 204#ifdef SDL_USE_LIBDBUS 205 device->ApplyWindowProgress = DBUS_ApplyWindowProgress; 206#endif // SDL_USE_LIBDBUS 207 device->ShowWindowSystemMenu = X11_ShowWindowSystemMenu; 208 device->SetWindowFocusable = X11_SetWindowFocusable; 209 device->SyncWindow = X11_SyncWindow; 210 211#ifdef SDL_VIDEO_DRIVER_X11_XFIXES 212 device->SetWindowMouseRect = X11_SetWindowMouseRect; 213#endif // SDL_VIDEO_DRIVER_X11_XFIXES 214 215#ifdef SDL_VIDEO_OPENGL_GLX 216 device->GL_LoadLibrary = X11_GL_LoadLibrary; 217 device->GL_GetProcAddress = X11_GL_GetProcAddress; 218 device->GL_UnloadLibrary = X11_GL_UnloadLibrary; 219 device->GL_CreateContext = X11_GL_CreateContext; 220 device->GL_MakeCurrent = X11_GL_MakeCurrent; 221 device->GL_SetSwapInterval = X11_GL_SetSwapInterval; 222 device->GL_GetSwapInterval = X11_GL_GetSwapInterval; 223 device->GL_SwapWindow = X11_GL_SwapWindow; 224 device->GL_DestroyContext = X11_GL_DestroyContext; 225 device->GL_GetEGLSurface = NULL; 226#endif 227#ifdef SDL_VIDEO_OPENGL_EGL 228#ifdef SDL_VIDEO_OPENGL_GLX 229 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) { 230#endif 231 device->GL_LoadLibrary = X11_GLES_LoadLibrary; 232 device->GL_GetProcAddress = X11_GLES_GetProcAddress; 233 device->GL_UnloadLibrary = X11_GLES_UnloadLibrary; 234 device->GL_CreateContext = X11_GLES_CreateContext; 235 device->GL_MakeCurrent = X11_GLES_MakeCurrent; 236 device->GL_SetSwapInterval = X11_GLES_SetSwapInterval; 237 device->GL_GetSwapInterval = X11_GLES_GetSwapInterval; 238 device->GL_SwapWindow = X11_GLES_SwapWindow; 239 device->GL_DestroyContext = X11_GLES_DestroyContext; 240 device->GL_GetEGLSurface = X11_GLES_GetEGLSurface; 241#ifdef SDL_VIDEO_OPENGL_GLX 242 } 243#endif 244#endif 245 246 device->GetTextMimeTypes = X11_GetTextMimeTypes; 247 device->SetClipboardData = X11_SetClipboardData; 248 device->GetClipboardData = X11_GetClipboardData; 249 device->HasClipboardData = X11_HasClipboardData; 250 device->SetPrimarySelectionText = X11_SetPrimarySelectionText; 251 device->GetPrimarySelectionText = X11_GetPrimarySelectionText; 252 device->HasPrimarySelectionText = X11_HasPrimarySelectionText; 253 device->StartTextInput = X11_StartTextInput; 254 device->StopTextInput = X11_StopTextInput; 255 device->UpdateTextInputArea = X11_UpdateTextInputArea; 256 device->HasScreenKeyboardSupport = X11_HasScreenKeyboardSupport; 257 device->ShowScreenKeyboard = X11_ShowScreenKeyboard; 258 device->HideScreenKeyboard = X11_HideScreenKeyboard; 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 VIDEO_DEVICE_CAPS_SLOW_FRAMEBUFFER; 278 279 data->is_xwayland = X11_IsXWayland(x11_display); 280 if (data->is_xwayland) { 281 SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, "Detected XWayland"); 282 283 device->device_caps |= VIDEO_DEVICE_CAPS_MODE_SWITCHING_EMULATED | 284 VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS; 285 } 286 if (X11_IsWSL()) { 287 // On WSL, direct X11 is faster than using OpenGL for window framebuffers, so try to detect WSL and avoid texture framebuffer. 288 device->device_caps &= ~VIDEO_DEVICE_CAPS_SLOW_FRAMEBUFFER; 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(WM_STATE); 389 GET_ATOM(_NET_WM_STATE); 390 GET_ATOM(_NET_WM_STATE_HIDDEN); 391 GET_ATOM(_NET_WM_STATE_FOCUSED); 392 GET_ATOM(_NET_WM_STATE_MAXIMIZED_VERT); 393 GET_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ); 394 GET_ATOM(_NET_WM_STATE_FULLSCREEN); 395 GET_ATOM(_NET_WM_STATE_ABOVE); 396 GET_ATOM(_NET_WM_STATE_SKIP_TASKBAR); 397 GET_ATOM(_NET_WM_STATE_SKIP_PAGER); 398 GET_ATOM(_NET_WM_MOVERESIZE); 399 GET_ATOM(_NET_WM_STATE_MODAL); 400 GET_ATOM(_NET_WM_ALLOWED_ACTIONS); 401 GET_ATOM(_NET_WM_ACTION_FULLSCREEN); 402 GET_ATOM(_NET_WM_NAME); 403 GET_ATOM(_NET_WM_ICON_NAME); 404 GET_ATOM(_NET_WM_ICON); 405 GET_ATOM(_NET_WM_PING); 406 GET_ATOM(_NET_WM_SYNC_REQUEST); 407 GET_ATOM(_NET_WM_SYNC_REQUEST_COUNTER); 408 GET_ATOM(_NET_WM_WINDOW_OPACITY); 409 GET_ATOM(_NET_WM_USER_TIME); 410 GET_ATOM(_NET_ACTIVE_WINDOW); 411 GET_ATOM(_NET_FRAME_EXTENTS); 412 GET_ATOM(_SDL_WAKEUP); 413 GET_ATOM(UTF8_STRING); 414 GET_ATOM(PRIMARY); 415 GET_ATOM(CLIPBOARD); 416 GET_ATOM(INCR); 417 GET_ATOM(SDL_SELECTION); 418 GET_ATOM(TARGETS); 419 GET_ATOM(SDL_FORMATS); 420 GET_ATOM(RESOURCE_MANAGER); 421 GET_ATOM(XdndAware); 422 GET_ATOM(XdndEnter); 423 GET_ATOM(XdndLeave); 424 GET_ATOM(XdndPosition); 425 GET_ATOM(XdndStatus); 426 GET_ATOM(XdndTypeList); 427 GET_ATOM(XdndActionCopy); 428 GET_ATOM(XdndDrop); 429 GET_ATOM(XdndFinished); 430 GET_ATOM(XdndSelection); 431 GET_ATOM(XKLAVIER_STATE); 432 433 // Detect the window manager 434 X11_CheckWindowManager(_this); 435 436 if (!X11_InitModes(_this)) { 437 return false; 438 } 439 440 if (!X11_InitXinput2(_this)) { 441 // Assume a mouse and keyboard are attached 442 SDL_AddKeyboard(SDL_DEFAULT_KEYBOARD_ID, NULL); 443 SDL_AddMouse(SDL_DEFAULT_MOUSE_ID, NULL); 444 } 445 446#ifdef SDL_VIDEO_DRIVER_X11_XFIXES 447 X11_InitXfixes(_this); 448#endif 449 450 X11_InitXsettings(_this); 451 452#ifdef SDL_VIDEO_DRIVER_X11_XSYNC 453 X11_InitXsync(_this); 454#endif 455 456#ifdef SDL_VIDEO_DRIVER_X11_XTEST 457 X11_InitXTest(_this); 458#endif 459 460#ifndef X_HAVE_UTF8_STRING 461#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. 462#endif 463 464 if (!X11_InitKeyboard(_this)) { 465 return false; 466 } 467 X11_InitMouse(_this); 468 469 X11_InitTouch(_this); 470 471 X11_InitPen(_this); 472 473 // Request currently available mime-types in the clipboard. 474 X11_XConvertSelection(data->display, data->atoms.CLIPBOARD, data->atoms.TARGETS, 475 data->atoms.SDL_FORMATS, GetWindow(_this), CurrentTime); 476 477 return true; 478} 479 480void X11_VideoQuit(SDL_VideoDevice *_this) 481{ 482 SDL_VideoData *data = _this->internal; 483 484 if (data->clipboard_window) { 485 X11_XDestroyWindow(data->display, data->clipboard_window); 486 } 487 488 if (data->xsettings_window) { 489 X11_XDestroyWindow(data->display, data->xsettings_window); 490 } 491 492#ifdef X_HAVE_UTF8_STRING 493 if (data->im) { 494 X11_XCloseIM(data->im); 495 } 496#endif 497 498 X11_QuitXinput2(_this); 499 X11_QuitModes(_this); 500 X11_QuitKeyboard(_this); 501 X11_QuitMouse(_this); 502 X11_QuitTouch(_this); 503 X11_QuitPen(_this); 504 X11_QuitClipboard(_this); 505 X11_QuitXsettings(_this); 506} 507 508bool X11_UseDirectColorVisuals(void) 509{ 510 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NODIRECTCOLOR, false)) { 511 return false; 512 } 513 return true; 514} 515 516#endif // SDL_VIDEO_DRIVER_X11 517[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.