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