| /* | 
 |   Simple DirectMedia Layer | 
 |   Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org> | 
 |  | 
 |   This software is provided 'as-is', without any express or implied | 
 |   warranty.  In no event will the authors be held liable for any damages | 
 |   arising from the use of this software. | 
 |  | 
 |   Permission is granted to anyone to use this software for any purpose, | 
 |   including commercial applications, and to alter it and redistribute it | 
 |   freely, subject to the following restrictions: | 
 |  | 
 |   1. The origin of this software must not be misrepresented; you must not | 
 |      claim that you wrote the original software. If you use this software | 
 |      in a product, an acknowledgment in the product documentation would be | 
 |      appreciated but is not required. | 
 |   2. Altered source versions must be plainly marked as such, and must not be | 
 |      misrepresented as being the original software. | 
 |   3. This notice may not be removed or altered from any source distribution. | 
 | */ | 
 | #include "../../SDL_internal.h" | 
 |  | 
 | #ifdef SDL_VIDEO_DRIVER_X11 | 
 |  | 
 | #include <unistd.h> /* For getpid() and readlink() */ | 
 |  | 
 | #include "SDL_video.h" | 
 | #include "SDL_mouse.h" | 
 | #include "SDL_timer.h" | 
 | #include "SDL_hints.h" | 
 | #include "../SDL_sysvideo.h" | 
 | #include "../SDL_pixels_c.h" | 
 |  | 
 | #include "SDL_x11video.h" | 
 | #include "SDL_x11framebuffer.h" | 
 | #include "SDL_x11shape.h" | 
 | #include "SDL_x11touch.h" | 
 | #include "SDL_x11xinput2.h" | 
 | #include "SDL_x11xfixes.h" | 
 | #include "SDL_x11messagebox.h" | 
 |  | 
 | #ifdef SDL_VIDEO_OPENGL_EGL | 
 | #include "SDL_x11opengles.h" | 
 | #endif | 
 |  | 
 | #include "SDL_x11vulkan.h" | 
 |  | 
 | /* Initialization/Query functions */ | 
 | static int X11_VideoInit(_THIS); | 
 | static void X11_VideoQuit(_THIS); | 
 |  | 
 | /* Find out what class name we should use */ | 
 | static char *get_classname() | 
 | { | 
 |     char *spot; | 
 | #if defined(__LINUX__) || defined(__FREEBSD__) | 
 |     char procfile[1024]; | 
 |     char linkfile[1024]; | 
 |     int linksize; | 
 | #endif | 
 |  | 
 |     /* First allow environment variable override */ | 
 |     spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS"); | 
 |     if (spot) { | 
 |         return SDL_strdup(spot); | 
 |     } | 
 |  | 
 |     /* Next look at the application's executable name */ | 
 | #if defined(__LINUX__) || defined(__FREEBSD__) | 
 | #if defined(__LINUX__) | 
 |     (void)SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/exe", getpid()); | 
 | #elif defined(__FREEBSD__) | 
 |     (void)SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/file", getpid()); | 
 | #else | 
 | #error Where can we find the executable name? | 
 | #endif | 
 |     linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1); | 
 |     if (linksize > 0) { | 
 |         linkfile[linksize] = '\0'; | 
 |         spot = SDL_strrchr(linkfile, '/'); | 
 |         if (spot) { | 
 |             return SDL_strdup(spot + 1); | 
 |         } else { | 
 |             return SDL_strdup(linkfile); | 
 |         } | 
 |     } | 
 | #endif /* __LINUX__ || __FREEBSD__ */ | 
 |  | 
 |     /* Finally use the default we've used forever */ | 
 |     return SDL_strdup("SDL_App"); | 
 | } | 
 |  | 
 | /* X11 driver bootstrap functions */ | 
 |  | 
 | static int (*orig_x11_errhandler)(Display *, XErrorEvent *) = NULL; | 
 |  | 
 | static void X11_DeleteDevice(SDL_VideoDevice *device) | 
 | { | 
 |     SDL_VideoData *data = (SDL_VideoData *)device->driverdata; | 
 |     if (device->vulkan_config.loader_handle) { | 
 |         device->Vulkan_UnloadLibrary(device); | 
 |     } | 
 |     if (data->display) { | 
 |         X11_XSetErrorHandler(orig_x11_errhandler); | 
 |         X11_XCloseDisplay(data->display); | 
 |     } | 
 |     if (data->request_display) { | 
 |         X11_XCloseDisplay(data->request_display); | 
 |     } | 
 |     SDL_free(data->windowlist); | 
 |     if (device->wakeup_lock) { | 
 |         SDL_DestroyMutex(device->wakeup_lock); | 
 |     } | 
 |     SDL_free(device->driverdata); | 
 |     SDL_free(device); | 
 |  | 
 |     SDL_X11_UnloadSymbols(); | 
 | } | 
 |  | 
 | /* An error handler to reset the vidmode and then call the default handler. */ | 
 | static SDL_bool safety_net_triggered = SDL_FALSE; | 
 | static int X11_SafetyNetErrHandler(Display *d, XErrorEvent *e) | 
 | { | 
 |     SDL_VideoDevice *device = NULL; | 
 |     /* if we trigger an error in our error handler, don't try again. */ | 
 |     if (!safety_net_triggered) { | 
 |         safety_net_triggered = SDL_TRUE; | 
 |         device = SDL_GetVideoDevice(); | 
 |         if (device) { | 
 |             int i; | 
 |             for (i = 0; i < device->num_displays; i++) { | 
 |                 SDL_VideoDisplay *display = &device->displays[i]; | 
 |                 if (SDL_memcmp(&display->current_mode, &display->desktop_mode, | 
 |                                sizeof(SDL_DisplayMode)) != 0) { | 
 |                     X11_SetDisplayMode(device, display, &display->desktop_mode); | 
 |                 } | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     if (orig_x11_errhandler) { | 
 |         return orig_x11_errhandler(d, e); /* probably terminate. */ | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | static SDL_bool X11_IsXWayland(Display *d) | 
 | { | 
 |     int opcode, event, error; | 
 |     return X11_XQueryExtension(d, "XWAYLAND", &opcode, &event, &error) == True; | 
 | } | 
 |  | 
 | static SDL_VideoDevice *X11_CreateDevice(void) | 
 | { | 
 |     SDL_VideoDevice *device; | 
 |     SDL_VideoData *data; | 
 |     const char *display = NULL; /* Use the DISPLAY environment variable */ | 
 |     Display *x11_display = NULL; | 
 |  | 
 |     if (!SDL_X11_LoadSymbols()) { | 
 |         return NULL; | 
 |     } | 
 |  | 
 |     /* Need for threading gl calls. This is also required for the proprietary | 
 |         nVidia driver to be threaded. */ | 
 |     X11_XInitThreads(); | 
 |  | 
 |     /* Open the display first to be sure that X11 is available */ | 
 |     x11_display = X11_XOpenDisplay(display); | 
 |  | 
 |     if (!x11_display) { | 
 |         SDL_X11_UnloadSymbols(); | 
 |         return NULL; | 
 |     } | 
 |  | 
 |     /* Initialize all variables that we clean on shutdown */ | 
 |     device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice)); | 
 |     if (!device) { | 
 |         SDL_OutOfMemory(); | 
 |         return NULL; | 
 |     } | 
 |     data = (struct SDL_VideoData *)SDL_calloc(1, sizeof(SDL_VideoData)); | 
 |     if (!data) { | 
 |         SDL_free(device); | 
 |         SDL_OutOfMemory(); | 
 |         return NULL; | 
 |     } | 
 |     device->driverdata = data; | 
 |  | 
 |     data->global_mouse_changed = SDL_TRUE; | 
 |  | 
 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | 
 |     data->active_cursor_confined_window = NULL; | 
 | #endif /* SDL_VIDEO_DRIVER_X11_XFIXES */ | 
 |  | 
 |     data->display = x11_display; | 
 |     data->request_display = X11_XOpenDisplay(display); | 
 |     if (!data->request_display) { | 
 |         X11_XCloseDisplay(data->display); | 
 |         SDL_free(device->driverdata); | 
 |         SDL_free(device); | 
 |         SDL_X11_UnloadSymbols(); | 
 |         return NULL; | 
 |     } | 
 |  | 
 |     device->wakeup_lock = SDL_CreateMutex(); | 
 |  | 
 | #ifdef X11_DEBUG | 
 |     X11_XSynchronize(data->display, True); | 
 | #endif | 
 |  | 
 |     /* Hook up an X11 error handler to recover the desktop resolution. */ | 
 |     safety_net_triggered = SDL_FALSE; | 
 |     orig_x11_errhandler = X11_XSetErrorHandler(X11_SafetyNetErrHandler); | 
 |  | 
 |     /* Steam Deck will have an on-screen keyboard, so check their environment | 
 |      * variable so we can make use of SDL_StartTextInput. | 
 |      */ | 
 |     data->is_steam_deck = SDL_GetHintBoolean("SteamDeck", SDL_FALSE); | 
 |  | 
 |     /* Set the function pointers */ | 
 |     device->VideoInit = X11_VideoInit; | 
 |     device->VideoQuit = X11_VideoQuit; | 
 |     device->ResetTouch = X11_ResetTouch; | 
 |     device->GetDisplayModes = X11_GetDisplayModes; | 
 |     device->GetDisplayBounds = X11_GetDisplayBounds; | 
 |     device->GetDisplayUsableBounds = X11_GetDisplayUsableBounds; | 
 |     device->GetDisplayDPI = X11_GetDisplayDPI; | 
 |     device->GetWindowICCProfile = X11_GetWindowICCProfile; | 
 |     device->SetDisplayMode = X11_SetDisplayMode; | 
 |     device->SuspendScreenSaver = X11_SuspendScreenSaver; | 
 |     device->PumpEvents = X11_PumpEvents; | 
 |     device->WaitEventTimeout = X11_WaitEventTimeout; | 
 |     device->SendWakeupEvent = X11_SendWakeupEvent; | 
 |  | 
 |     device->CreateSDLWindow = X11_CreateWindow; | 
 |     device->CreateSDLWindowFrom = X11_CreateWindowFrom; | 
 |     device->SetWindowTitle = X11_SetWindowTitle; | 
 |     device->SetWindowIcon = X11_SetWindowIcon; | 
 |     device->SetWindowPosition = X11_SetWindowPosition; | 
 |     device->SetWindowSize = X11_SetWindowSize; | 
 |     device->SetWindowMinimumSize = X11_SetWindowMinimumSize; | 
 |     device->SetWindowMaximumSize = X11_SetWindowMaximumSize; | 
 |     device->GetWindowBordersSize = X11_GetWindowBordersSize; | 
 |     device->SetWindowOpacity = X11_SetWindowOpacity; | 
 |     device->SetWindowModalFor = X11_SetWindowModalFor; | 
 |     device->SetWindowInputFocus = X11_SetWindowInputFocus; | 
 |     device->ShowWindow = X11_ShowWindow; | 
 |     device->HideWindow = X11_HideWindow; | 
 |     device->RaiseWindow = X11_RaiseWindow; | 
 |     device->MaximizeWindow = X11_MaximizeWindow; | 
 |     device->MinimizeWindow = X11_MinimizeWindow; | 
 |     device->RestoreWindow = X11_RestoreWindow; | 
 |     device->SetWindowBordered = X11_SetWindowBordered; | 
 |     device->SetWindowResizable = X11_SetWindowResizable; | 
 |     device->SetWindowAlwaysOnTop = X11_SetWindowAlwaysOnTop; | 
 |     device->SetWindowFullscreen = X11_SetWindowFullscreen; | 
 |     device->SetWindowGammaRamp = X11_SetWindowGammaRamp; | 
 |     device->SetWindowMouseGrab = X11_SetWindowMouseGrab; | 
 |     device->SetWindowKeyboardGrab = X11_SetWindowKeyboardGrab; | 
 |     device->DestroyWindow = X11_DestroyWindow; | 
 |     device->CreateWindowFramebuffer = X11_CreateWindowFramebuffer; | 
 |     device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer; | 
 |     device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer; | 
 |     device->GetWindowWMInfo = X11_GetWindowWMInfo; | 
 |     device->SetWindowHitTest = X11_SetWindowHitTest; | 
 |     device->AcceptDragAndDrop = X11_AcceptDragAndDrop; | 
 |     device->FlashWindow = X11_FlashWindow; | 
 |  | 
 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | 
 |     device->SetWindowMouseRect = X11_SetWindowMouseRect; | 
 | #endif /* SDL_VIDEO_DRIVER_X11_XFIXES */ | 
 |  | 
 |     device->shape_driver.CreateShaper = X11_CreateShaper; | 
 |     device->shape_driver.SetWindowShape = X11_SetWindowShape; | 
 |     device->shape_driver.ResizeWindowShape = X11_ResizeWindowShape; | 
 |  | 
 | #ifdef SDL_VIDEO_OPENGL_GLX | 
 |     device->GL_LoadLibrary = X11_GL_LoadLibrary; | 
 |     device->GL_GetProcAddress = X11_GL_GetProcAddress; | 
 |     device->GL_UnloadLibrary = X11_GL_UnloadLibrary; | 
 |     device->GL_CreateContext = X11_GL_CreateContext; | 
 |     device->GL_MakeCurrent = X11_GL_MakeCurrent; | 
 |     device->GL_SetSwapInterval = X11_GL_SetSwapInterval; | 
 |     device->GL_GetSwapInterval = X11_GL_GetSwapInterval; | 
 |     device->GL_SwapWindow = X11_GL_SwapWindow; | 
 |     device->GL_DeleteContext = X11_GL_DeleteContext; | 
 | #endif | 
 | #ifdef SDL_VIDEO_OPENGL_EGL | 
 | #ifdef SDL_VIDEO_OPENGL_GLX | 
 |     if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_FORCE_EGL, SDL_FALSE)) { | 
 | #endif | 
 |         device->GL_LoadLibrary = X11_GLES_LoadLibrary; | 
 |         device->GL_GetProcAddress = X11_GLES_GetProcAddress; | 
 |         device->GL_UnloadLibrary = X11_GLES_UnloadLibrary; | 
 |         device->GL_CreateContext = X11_GLES_CreateContext; | 
 |         device->GL_MakeCurrent = X11_GLES_MakeCurrent; | 
 |         device->GL_SetSwapInterval = X11_GLES_SetSwapInterval; | 
 |         device->GL_GetSwapInterval = X11_GLES_GetSwapInterval; | 
 |         device->GL_SwapWindow = X11_GLES_SwapWindow; | 
 |         device->GL_DeleteContext = X11_GLES_DeleteContext; | 
 | #ifdef SDL_VIDEO_OPENGL_GLX | 
 |     } | 
 | #endif | 
 | #endif | 
 |  | 
 |     device->SetClipboardText = X11_SetClipboardText; | 
 |     device->GetClipboardText = X11_GetClipboardText; | 
 |     device->HasClipboardText = X11_HasClipboardText; | 
 |     device->SetPrimarySelectionText = X11_SetPrimarySelectionText; | 
 |     device->GetPrimarySelectionText = X11_GetPrimarySelectionText; | 
 |     device->HasPrimarySelectionText = X11_HasPrimarySelectionText; | 
 |     device->StartTextInput = X11_StartTextInput; | 
 |     device->StopTextInput = X11_StopTextInput; | 
 |     device->SetTextInputRect = X11_SetTextInputRect; | 
 |     device->HasScreenKeyboardSupport = X11_HasScreenKeyboardSupport; | 
 |     device->ShowScreenKeyboard = X11_ShowScreenKeyboard; | 
 |     device->HideScreenKeyboard = X11_HideScreenKeyboard; | 
 |     device->IsScreenKeyboardShown = X11_IsScreenKeyboardShown; | 
 |  | 
 |     device->free = X11_DeleteDevice; | 
 |  | 
 | #ifdef SDL_VIDEO_VULKAN | 
 |     device->Vulkan_LoadLibrary = X11_Vulkan_LoadLibrary; | 
 |     device->Vulkan_UnloadLibrary = X11_Vulkan_UnloadLibrary; | 
 |     device->Vulkan_GetInstanceExtensions = X11_Vulkan_GetInstanceExtensions; | 
 |     device->Vulkan_CreateSurface = X11_Vulkan_CreateSurface; | 
 | #endif | 
 |  | 
 |     data->is_xwayland = X11_IsXWayland(x11_display); | 
 |  | 
 |     return device; | 
 | } | 
 |  | 
 | VideoBootStrap X11_bootstrap = { | 
 |     "x11", "SDL X11 video driver", | 
 |     X11_CreateDevice, | 
 |     X11_ShowMessageBox | 
 | }; | 
 |  | 
 | static int (*handler)(Display *, XErrorEvent *) = NULL; | 
 | static int X11_CheckWindowManagerErrorHandler(Display *d, XErrorEvent *e) | 
 | { | 
 |     if (e->error_code == BadWindow) { | 
 |         return 0; | 
 |     } else { | 
 |         return handler(d, e); | 
 |     } | 
 | } | 
 |  | 
 | static void X11_CheckWindowManager(_THIS) | 
 | { | 
 |     SDL_VideoData *data = (SDL_VideoData *)_this->driverdata; | 
 |     Display *display = data->display; | 
 |     Atom _NET_SUPPORTING_WM_CHECK; | 
 |     int status, real_format; | 
 |     Atom real_type; | 
 |     unsigned long items_read = 0, items_left = 0; | 
 |     unsigned char *propdata = NULL; | 
 |     Window wm_window = 0; | 
 | #ifdef DEBUG_WINDOW_MANAGER | 
 |     char *wm_name; | 
 | #endif | 
 |  | 
 |     /* Set up a handler to gracefully catch errors */ | 
 |     X11_XSync(display, False); | 
 |     handler = X11_XSetErrorHandler(X11_CheckWindowManagerErrorHandler); | 
 |  | 
 |     _NET_SUPPORTING_WM_CHECK = X11_XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False); | 
 |     status = X11_XGetWindowProperty(display, DefaultRootWindow(display), _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata); | 
 |     if (status == Success) { | 
 |         if (items_read) { | 
 |             wm_window = ((Window *)propdata)[0]; | 
 |         } | 
 |         if (propdata) { | 
 |             X11_XFree(propdata); | 
 |             propdata = NULL; | 
 |         } | 
 |     } | 
 |  | 
 |     if (wm_window) { | 
 |         status = X11_XGetWindowProperty(display, wm_window, _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata); | 
 |         if (status != Success || !items_read || wm_window != ((Window *)propdata)[0]) { | 
 |             wm_window = None; | 
 |         } | 
 |         if (status == Success && propdata) { | 
 |             X11_XFree(propdata); | 
 |             propdata = NULL; | 
 |         } | 
 |     } | 
 |  | 
 |     /* Reset the error handler, we're done checking */ | 
 |     X11_XSync(display, False); | 
 |     X11_XSetErrorHandler(handler); | 
 |  | 
 |     if (!wm_window) { | 
 | #ifdef DEBUG_WINDOW_MANAGER | 
 |         printf("Couldn't get _NET_SUPPORTING_WM_CHECK property\n"); | 
 | #endif | 
 |         return; | 
 |     } | 
 |     data->net_wm = SDL_TRUE; | 
 |  | 
 | #ifdef DEBUG_WINDOW_MANAGER | 
 |     wm_name = X11_GetWindowTitle(_this, wm_window); | 
 |     printf("Window manager: %s\n", wm_name); | 
 |     SDL_free(wm_name); | 
 | #endif | 
 | } | 
 |  | 
 | int X11_VideoInit(_THIS) | 
 | { | 
 |     SDL_VideoData *data = (SDL_VideoData *)_this->driverdata; | 
 |  | 
 |     /* Get the window class name, usually the name of the application */ | 
 |     data->classname = get_classname(); | 
 |  | 
 |     /* Get the process PID to be associated to the window */ | 
 |     data->pid = getpid(); | 
 |  | 
 |     /* I have no idea how random this actually is, or has to be. */ | 
 |     data->window_group = (XID)(((size_t)data->pid) ^ ((size_t)_this)); | 
 |  | 
 |     /* Look up some useful Atoms */ | 
 | #define GET_ATOM(X) data->X = X11_XInternAtom(data->display, #X, False) | 
 |     GET_ATOM(WM_PROTOCOLS); | 
 |     GET_ATOM(WM_DELETE_WINDOW); | 
 |     GET_ATOM(WM_TAKE_FOCUS); | 
 |     GET_ATOM(WM_NAME); | 
 |     GET_ATOM(_NET_WM_STATE); | 
 |     GET_ATOM(_NET_WM_STATE_HIDDEN); | 
 |     GET_ATOM(_NET_WM_STATE_FOCUSED); | 
 |     GET_ATOM(_NET_WM_STATE_MAXIMIZED_VERT); | 
 |     GET_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ); | 
 |     GET_ATOM(_NET_WM_STATE_FULLSCREEN); | 
 |     GET_ATOM(_NET_WM_STATE_ABOVE); | 
 |     GET_ATOM(_NET_WM_STATE_SKIP_TASKBAR); | 
 |     GET_ATOM(_NET_WM_STATE_SKIP_PAGER); | 
 |     GET_ATOM(_NET_WM_ALLOWED_ACTIONS); | 
 |     GET_ATOM(_NET_WM_ACTION_FULLSCREEN); | 
 |     GET_ATOM(_NET_WM_NAME); | 
 |     GET_ATOM(_NET_WM_ICON_NAME); | 
 |     GET_ATOM(_NET_WM_ICON); | 
 |     GET_ATOM(_NET_WM_PING); | 
 |     GET_ATOM(_NET_WM_WINDOW_OPACITY); | 
 |     GET_ATOM(_NET_WM_USER_TIME); | 
 |     GET_ATOM(_NET_ACTIVE_WINDOW); | 
 |     GET_ATOM(_NET_FRAME_EXTENTS); | 
 |     GET_ATOM(_SDL_WAKEUP); | 
 |     GET_ATOM(UTF8_STRING); | 
 |     GET_ATOM(PRIMARY); | 
 |     GET_ATOM(XdndEnter); | 
 |     GET_ATOM(XdndPosition); | 
 |     GET_ATOM(XdndStatus); | 
 |     GET_ATOM(XdndTypeList); | 
 |     GET_ATOM(XdndActionCopy); | 
 |     GET_ATOM(XdndDrop); | 
 |     GET_ATOM(XdndFinished); | 
 |     GET_ATOM(XdndSelection); | 
 |     GET_ATOM(XKLAVIER_STATE); | 
 |  | 
 |     /* Detect the window manager */ | 
 |     X11_CheckWindowManager(_this); | 
 |  | 
 |     if (X11_InitModes(_this) < 0) { | 
 |         return -1; | 
 |     } | 
 |  | 
 |     X11_InitXinput2(_this); | 
 |  | 
 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | 
 |     X11_InitXfixes(_this); | 
 | #endif /* SDL_VIDEO_DRIVER_X11_XFIXES */ | 
 |  | 
 | #ifndef X_HAVE_UTF8_STRING | 
 | #warning X server does not support UTF8_STRING, a feature introduced in 2000! This is likely to become a hard error in a future libSDL2. | 
 | #endif | 
 |  | 
 |     if (X11_InitKeyboard(_this) != 0) { | 
 |         return -1; | 
 |     } | 
 |     X11_InitMouse(_this); | 
 |  | 
 |     X11_InitTouch(_this); | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | void X11_VideoQuit(_THIS) | 
 | { | 
 |     SDL_VideoData *data = (SDL_VideoData *)_this->driverdata; | 
 |  | 
 |     if (data->clipboard_window) { | 
 |         X11_XDestroyWindow(data->display, data->clipboard_window); | 
 |     } | 
 |  | 
 |     SDL_free(data->classname); | 
 | #ifdef X_HAVE_UTF8_STRING | 
 |     if (data->im) { | 
 |         X11_XCloseIM(data->im); | 
 |     } | 
 | #endif | 
 |  | 
 |     X11_QuitModes(_this); | 
 |     X11_QuitKeyboard(_this); | 
 |     X11_QuitMouse(_this); | 
 |     X11_QuitTouch(_this); | 
 | } | 
 |  | 
 | SDL_bool X11_UseDirectColorVisuals(void) | 
 | { | 
 |     return SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ? SDL_FALSE : SDL_TRUE; | 
 | } | 
 |  | 
 | #endif /* SDL_VIDEO_DRIVER_X11 */ | 
 |  | 
 | /* vim: set ts=4 sw=4 expandtab: */ |