src/video/x11/SDL_x11video.c
author Manuel Alfayate Corchete
Thu, 22 Oct 2020 19:51:57 +0200
changeset 14191 7bb6793cd70f
parent 14037 e0220c4bc306
permissions -rw-r--r--
kmsdrm: Add comment about KMSDRM_VideoQuit() changes.
     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: */