Atlas - SDL_x11video.c
Home / ext / SDL2 / src / video / x11 Lines: 3 | Size: 15639 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 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#if SDL_VIDEO_DRIVER_X11 24 25#include <unistd.h> /* For getpid() and readlink() */ 26 27#include "SDL_video.h" 28#include "SDL_mouse.h" 29#include "SDL_timer.h" 30#include "../SDL_sysvideo.h" 31#include "../SDL_pixels_c.h" 32 33#include "SDL_x11video.h" 34#include "SDL_x11framebuffer.h" 35#include "SDL_x11shape.h" 36#include "SDL_x11touch.h" 37#include "SDL_x11xinput2.h" 38 39#if SDL_VIDEO_OPENGL_EGL 40#include "SDL_x11opengles.h" 41#endif 42 43#include "SDL_x11vulkan.h" 44 45/* Initialization/Query functions */ 46static int X11_VideoInit(_THIS); 47static void X11_VideoQuit(_THIS); 48 49/* Find out what class name we should use */ 50static char * 51get_classname() 52{ 53 char *spot; 54#if defined(__LINUX__) || defined(__FREEBSD__) 55 char procfile[1024]; 56 char linkfile[1024]; 57 int linksize; 58#endif 59 60 /* First allow environment variable override */ 61 spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS"); 62 if (spot) { 63 return SDL_strdup(spot); 64 } 65 66 /* Next look at the application's executable name */ 67#if defined(__LINUX__) || defined(__FREEBSD__) 68#if defined(__LINUX__) 69 SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/exe", getpid()); 70#elif defined(__FREEBSD__) 71 SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/file", 72 getpid()); 73#else 74#error Where can we find the executable name? 75#endif 76 linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1); 77 if (linksize > 0) { 78 linkfile[linksize] = '\0'; 79 spot = SDL_strrchr(linkfile, '/'); 80 if (spot) { 81 return SDL_strdup(spot + 1); 82 } else { 83 return SDL_strdup(linkfile); 84 } 85 } 86#endif /* __LINUX__ || __FREEBSD__ */ 87 88 /* Finally use the default we've used forever */ 89 return SDL_strdup("SDL_App"); 90} 91 92/* X11 driver bootstrap functions */ 93 94static int 95X11_Available(void) 96{ 97 Display *display = NULL; 98 if (SDL_X11_LoadSymbols()) { 99 display = X11_XOpenDisplay(NULL); 100 if (display != NULL) { 101 X11_XCloseDisplay(display); 102 } 103 SDL_X11_UnloadSymbols(); 104 } 105 return (display != NULL); 106} 107 108static void 109X11_DeleteDevice(SDL_VideoDevice * device) 110{ 111 SDL_VideoData *data = (SDL_VideoData *) device->driverdata; 112 if (device->vulkan_config.loader_handle) { 113 device->Vulkan_UnloadLibrary(device); 114 } 115 if (data->display) { 116 X11_XCloseDisplay(data->display); 117 } 118 SDL_free(data->windowlist); 119 SDL_free(device->driverdata); 120 SDL_free(device); 121 122 SDL_X11_UnloadSymbols(); 123} 124 125/* An error handler to reset the vidmode and then call the default handler. */ 126static SDL_bool safety_net_triggered = SDL_FALSE; 127static int (*orig_x11_errhandler) (Display *, XErrorEvent *) = NULL; 128static int 129X11_SafetyNetErrHandler(Display * d, XErrorEvent * e) 130{ 131 SDL_VideoDevice *device = NULL; 132 /* if we trigger an error in our error handler, don't try again. */ 133 if (!safety_net_triggered) { 134 safety_net_triggered = SDL_TRUE; 135 device = SDL_GetVideoDevice(); 136 if (device != NULL) { 137 int i; 138 for (i = 0; i < device->num_displays; i++) { 139 SDL_VideoDisplay *display = &device->displays[i]; 140 if (SDL_memcmp(&display->current_mode, &display->desktop_mode, 141 sizeof (SDL_DisplayMode)) != 0) { 142 X11_SetDisplayMode(device, display, &display->desktop_mode); 143 } 144 } 145 } 146 } 147 148 if (orig_x11_errhandler != NULL) { 149 return orig_x11_errhandler(d, e); /* probably terminate. */ 150 } 151 152 return 0; 153} 154 155static SDL_VideoDevice * 156X11_CreateDevice(int devindex) 157{ 158 SDL_VideoDevice *device; 159 SDL_VideoData *data; 160 const char *display = NULL; /* Use the DISPLAY environment variable */ 161 162 if (!SDL_X11_LoadSymbols()) { 163 return NULL; 164 } 165 166 /* Need for threading gl calls. This is also required for the proprietary 167 nVidia driver to be threaded. */ 168 X11_XInitThreads(); 169 170 /* Initialize all variables that we clean on shutdown */ 171 device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice)); 172 if (!device) { 173 SDL_OutOfMemory(); 174 return NULL; 175 } 176 data = (struct SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData)); 177 if (!data) { 178 SDL_free(device); 179 SDL_OutOfMemory(); 180 return NULL; 181 } 182 device->driverdata = data; 183 184 data->global_mouse_changed = SDL_TRUE; 185 186 /* FIXME: Do we need this? 187 if ( (SDL_strncmp(X11_XDisplayName(display), ":", 1) == 0) || 188 (SDL_strncmp(X11_XDisplayName(display), "unix:", 5) == 0) ) { 189 local_X11 = 1; 190 } else { 191 local_X11 = 0; 192 } 193 */ 194 data->display = X11_XOpenDisplay(display); 195#ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC 196 /* On some systems if linking without -lX11, it fails and you get following message. 197 * Xlib: connection to ":0.0" refused by server 198 * Xlib: XDM authorization key matches an existing client! 199 * 200 * It succeeds if retrying 1 second later 201 * or if running xhost +localhost on shell. 202 */ 203 if (data->display == NULL) { 204 SDL_Delay(1000); 205 data->display = X11_XOpenDisplay(display); 206 } 207#endif 208 if (data->display == NULL) { 209 SDL_free(device->driverdata); 210 SDL_free(device); 211 SDL_SetError("Couldn't open X11 display"); 212 return NULL; 213 } 214#ifdef X11_DEBUG 215 X11_XSynchronize(data->display, True); 216#endif 217 218 /* Hook up an X11 error handler to recover the desktop resolution. */ 219 safety_net_triggered = SDL_FALSE; 220 orig_x11_errhandler = X11_XSetErrorHandler(X11_SafetyNetErrHandler); 221 222 /* Set the function pointers */ 223 device->VideoInit = X11_VideoInit; 224 device->VideoQuit = X11_VideoQuit; 225 device->ResetTouch = X11_ResetTouch; 226 device->GetDisplayModes = X11_GetDisplayModes; 227 device->GetDisplayBounds = X11_GetDisplayBounds; 228 device->GetDisplayUsableBounds = X11_GetDisplayUsableBounds; 229 device->GetDisplayDPI = X11_GetDisplayDPI; 230 device->SetDisplayMode = X11_SetDisplayMode; 231 device->SuspendScreenSaver = X11_SuspendScreenSaver; 232 device->PumpEvents = X11_PumpEvents; 233 234 device->CreateSDLWindow = X11_CreateWindow; 235 device->CreateSDLWindowFrom = X11_CreateWindowFrom; 236 device->SetWindowTitle = X11_SetWindowTitle; 237 device->SetWindowIcon = X11_SetWindowIcon; 238 device->SetWindowPosition = X11_SetWindowPosition; 239 device->SetWindowSize = X11_SetWindowSize; 240 device->SetWindowMinimumSize = X11_SetWindowMinimumSize; 241 device->SetWindowMaximumSize = X11_SetWindowMaximumSize; 242 device->GetWindowBordersSize = X11_GetWindowBordersSize; 243 device->SetWindowOpacity = X11_SetWindowOpacity; 244 device->SetWindowModalFor = X11_SetWindowModalFor; 245 device->SetWindowInputFocus = X11_SetWindowInputFocus; 246 device->ShowWindow = X11_ShowWindow; 247 device->HideWindow = X11_HideWindow; 248 device->RaiseWindow = X11_RaiseWindow; 249 device->MaximizeWindow = X11_MaximizeWindow; 250 device->MinimizeWindow = X11_MinimizeWindow; 251 device->RestoreWindow = X11_RestoreWindow; 252 device->SetWindowBordered = X11_SetWindowBordered; 253 device->SetWindowResizable = X11_SetWindowResizable; 254 device->SetWindowFullscreen = X11_SetWindowFullscreen; 255 device->SetWindowGammaRamp = X11_SetWindowGammaRamp; 256 device->SetWindowGrab = X11_SetWindowGrab; 257 device->DestroyWindow = X11_DestroyWindow; 258 device->CreateWindowFramebuffer = X11_CreateWindowFramebuffer; 259 device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer; 260 device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer; 261 device->GetWindowWMInfo = X11_GetWindowWMInfo; 262 device->SetWindowHitTest = X11_SetWindowHitTest; 263 device->AcceptDragAndDrop = X11_AcceptDragAndDrop; 264 265 device->shape_driver.CreateShaper = X11_CreateShaper; 266 device->shape_driver.SetWindowShape = X11_SetWindowShape; 267 device->shape_driver.ResizeWindowShape = X11_ResizeWindowShape; 268 269#if SDL_VIDEO_OPENGL_GLX 270 device->GL_LoadLibrary = X11_GL_LoadLibrary; 271 device->GL_GetProcAddress = X11_GL_GetProcAddress; 272 device->GL_UnloadLibrary = X11_GL_UnloadLibrary; 273 device->GL_CreateContext = X11_GL_CreateContext; 274 device->GL_MakeCurrent = X11_GL_MakeCurrent; 275 device->GL_SetSwapInterval = X11_GL_SetSwapInterval; 276 device->GL_GetSwapInterval = X11_GL_GetSwapInterval; 277 device->GL_SwapWindow = X11_GL_SwapWindow; 278 device->GL_DeleteContext = X11_GL_DeleteContext; 279#elif SDL_VIDEO_OPENGL_EGL 280 device->GL_LoadLibrary = X11_GLES_LoadLibrary; 281 device->GL_GetProcAddress = X11_GLES_GetProcAddress; 282 device->GL_UnloadLibrary = X11_GLES_UnloadLibrary; 283 device->GL_CreateContext = X11_GLES_CreateContext; 284 device->GL_MakeCurrent = X11_GLES_MakeCurrent; 285 device->GL_SetSwapInterval = X11_GLES_SetSwapInterval; 286 device->GL_GetSwapInterval = X11_GLES_GetSwapInterval; 287 device->GL_SwapWindow = X11_GLES_SwapWindow; 288 device->GL_DeleteContext = X11_GLES_DeleteContext; 289#endif 290 291 device->SetClipboardText = X11_SetClipboardText; 292 device->GetClipboardText = X11_GetClipboardText; 293 device->HasClipboardText = X11_HasClipboardText; 294 device->StartTextInput = X11_StartTextInput; 295 device->StopTextInput = X11_StopTextInput; 296 device->SetTextInputRect = X11_SetTextInputRect; 297 298 device->free = X11_DeleteDevice; 299 300#if SDL_VIDEO_VULKAN 301 device->Vulkan_LoadLibrary = X11_Vulkan_LoadLibrary; 302 device->Vulkan_UnloadLibrary = X11_Vulkan_UnloadLibrary; 303 device->Vulkan_GetInstanceExtensions = X11_Vulkan_GetInstanceExtensions; 304 device->Vulkan_CreateSurface = X11_Vulkan_CreateSurface; 305#endif 306 307 return device; 308} 309 310VideoBootStrap X11_bootstrap = { 311 "x11", "SDL X11 video driver", 312 X11_Available, X11_CreateDevice 313}; 314 315static int (*handler) (Display *, XErrorEvent *) = NULL; 316static int 317X11_CheckWindowManagerErrorHandler(Display * d, XErrorEvent * e) 318{ 319 if (e->error_code == BadWindow) { 320 return (0); 321 } else { 322 return (handler(d, e)); 323 } 324} 325 326static void 327X11_CheckWindowManager(_THIS) 328{ 329 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; 330 Display *display = data->display; 331 Atom _NET_SUPPORTING_WM_CHECK; 332 int status, real_format; 333 Atom real_type; 334 unsigned long items_read = 0, items_left = 0; 335 unsigned char *propdata = NULL; 336 Window wm_window = 0; 337#ifdef DEBUG_WINDOW_MANAGER 338 char *wm_name; 339#endif 340 341 /* Set up a handler to gracefully catch errors */ 342 X11_XSync(display, False); 343 handler = X11_XSetErrorHandler(X11_CheckWindowManagerErrorHandler); 344 345 _NET_SUPPORTING_WM_CHECK = X11_XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False); 346 status = X11_XGetWindowProperty(display, DefaultRootWindow(display), _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata); 347 if (status == Success) { 348 if (items_read) { 349 wm_window = ((Window*)propdata)[0]; 350 } 351 if (propdata) { 352 X11_XFree(propdata); 353 propdata = NULL; 354 } 355 } 356 357 if (wm_window) { 358 status = X11_XGetWindowProperty(display, wm_window, _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata); 359 if (status != Success || !items_read || wm_window != ((Window*)propdata)[0]) { 360 wm_window = None; 361 } 362 if (status == Success && propdata) { 363 X11_XFree(propdata); 364 propdata = NULL; 365 } 366 } 367 368 /* Reset the error handler, we're done checking */ 369 X11_XSync(display, False); 370 X11_XSetErrorHandler(handler); 371 372 if (!wm_window) { 373#ifdef DEBUG_WINDOW_MANAGER 374 printf("Couldn't get _NET_SUPPORTING_WM_CHECK property\n"); 375#endif 376 return; 377 } 378 data->net_wm = SDL_TRUE; 379 380#ifdef DEBUG_WINDOW_MANAGER 381 wm_name = X11_GetWindowTitle(_this, wm_window); 382 printf("Window manager: %s\n", wm_name); 383 SDL_free(wm_name); 384#endif 385} 386 387 388int 389X11_VideoInit(_THIS) 390{ 391 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; 392 393 /* Get the window class name, usually the name of the application */ 394 data->classname = get_classname(); 395 396 /* Get the process PID to be associated to the window */ 397 data->pid = getpid(); 398 399 /* I have no idea how random this actually is, or has to be. */ 400 data->window_group = (XID) (((size_t) data->pid) ^ ((size_t) _this)); 401 402 /* Look up some useful Atoms */ 403#define GET_ATOM(X) data->X = X11_XInternAtom(data->display, #X, False) 404 GET_ATOM(WM_PROTOCOLS); 405 GET_ATOM(WM_DELETE_WINDOW); 406 GET_ATOM(WM_TAKE_FOCUS); 407 GET_ATOM(_NET_WM_STATE); 408 GET_ATOM(_NET_WM_STATE_HIDDEN); 409 GET_ATOM(_NET_WM_STATE_FOCUSED); 410 GET_ATOM(_NET_WM_STATE_MAXIMIZED_VERT); 411 GET_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ); 412 GET_ATOM(_NET_WM_STATE_FULLSCREEN); 413 GET_ATOM(_NET_WM_STATE_ABOVE); 414 GET_ATOM(_NET_WM_STATE_SKIP_TASKBAR); 415 GET_ATOM(_NET_WM_STATE_SKIP_PAGER); 416 GET_ATOM(_NET_WM_ALLOWED_ACTIONS); 417 GET_ATOM(_NET_WM_ACTION_FULLSCREEN); 418 GET_ATOM(_NET_WM_NAME); 419 GET_ATOM(_NET_WM_ICON_NAME); 420 GET_ATOM(_NET_WM_ICON); 421 GET_ATOM(_NET_WM_PING); 422 GET_ATOM(_NET_WM_WINDOW_OPACITY); 423 GET_ATOM(_NET_WM_USER_TIME); 424 GET_ATOM(_NET_ACTIVE_WINDOW); 425 GET_ATOM(_NET_FRAME_EXTENTS); 426 GET_ATOM(UTF8_STRING); 427 GET_ATOM(PRIMARY); 428 GET_ATOM(XdndEnter); 429 GET_ATOM(XdndPosition); 430 GET_ATOM(XdndStatus); 431 GET_ATOM(XdndTypeList); 432 GET_ATOM(XdndActionCopy); 433 GET_ATOM(XdndDrop); 434 GET_ATOM(XdndFinished); 435 GET_ATOM(XdndSelection); 436 GET_ATOM(XKLAVIER_STATE); 437 438 /* Detect the window manager */ 439 X11_CheckWindowManager(_this); 440 441 if (X11_InitModes(_this) < 0) { 442 return -1; 443 } 444 445 X11_InitXinput2(_this); 446 447 if (X11_InitKeyboard(_this) != 0) { 448 return -1; 449 } 450 X11_InitMouse(_this); 451 452 X11_InitTouch(_this); 453 454#if SDL_USE_LIBDBUS 455 SDL_DBus_Init(); 456#endif 457 458 return 0; 459} 460 461void 462X11_VideoQuit(_THIS) 463{ 464 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; 465 466 if (data->clipboard_window) { 467 X11_XDestroyWindow(data->display, data->clipboard_window); 468 } 469 470 SDL_free(data->classname); 471#ifdef X_HAVE_UTF8_STRING 472 if (data->im) { 473 X11_XCloseIM(data->im); 474 } 475#endif 476 477 X11_QuitModes(_this); 478 X11_QuitKeyboard(_this); 479 X11_QuitMouse(_this); 480 X11_QuitTouch(_this); 481 482/* !!! FIXME: other subsystems use D-Bus, so we shouldn't quit it here; 483 have SDL.c do this at a higher level, or add refcounting. */ 484#if SDL_USE_LIBDBUS 485 SDL_DBus_Quit(); 486#endif 487} 488 489SDL_bool 490X11_UseDirectColorVisuals(void) 491{ 492 return SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ? SDL_FALSE : SDL_TRUE; 493} 494 495#endif /* SDL_VIDEO_DRIVER_X11 */ 496 497/* vim: set ts=4 sw=4 expandtab: */ 498[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.