sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

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: */