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