sdl

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

SDL_waylandvideo.c (16838B)


      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 
     22 #include "../../SDL_internal.h"
     23 
     24 #if SDL_VIDEO_DRIVER_WAYLAND
     25 
     26 #include "SDL_video.h"
     27 #include "SDL_mouse.h"
     28 #include "SDL_stdinc.h"
     29 #include "../../events/SDL_events_c.h"
     30 
     31 #include "SDL_waylandvideo.h"
     32 #include "SDL_waylandevents_c.h"
     33 #include "SDL_waylandwindow.h"
     34 #include "SDL_waylandopengles.h"
     35 #include "SDL_waylandmouse.h"
     36 #include "SDL_waylandtouch.h"
     37 #include "SDL_waylandclipboard.h"
     38 #include "SDL_waylandvulkan.h"
     39 
     40 #include <sys/types.h>
     41 #include <unistd.h>
     42 #include <fcntl.h>
     43 #include <xkbcommon/xkbcommon.h>
     44 
     45 #include "SDL_waylanddyn.h"
     46 #include <wayland-util.h>
     47 
     48 #include "xdg-shell-client-protocol.h"
     49 #include "xdg-shell-unstable-v6-client-protocol.h"
     50 #include "xdg-decoration-unstable-v1-client-protocol.h"
     51 #include "org-kde-kwin-server-decoration-manager-client-protocol.h"
     52 
     53 #define WAYLANDVID_DRIVER_NAME "wayland"
     54 
     55 /* Initialization/Query functions */
     56 static int
     57 Wayland_VideoInit(_THIS);
     58 
     59 static void
     60 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display);
     61 static int
     62 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
     63 
     64 static void
     65 Wayland_VideoQuit(_THIS);
     66 
     67 /* Find out what class name we should use
     68  * Based on src/video/x11/SDL_x11video.c */
     69 static char *
     70 get_classname()
     71 {
     72 /* !!! FIXME: this is probably wrong, albeit harmless in many common cases. From protocol spec:
     73     "The surface class identifies the general class of applications
     74     to which the surface belongs. A common convention is to use the
     75     file name (or the full path if it is a non-standard location) of
     76     the application's .desktop file as the class." */
     77 
     78     char *spot;
     79 #if defined(__LINUX__) || defined(__FREEBSD__)
     80     char procfile[1024];
     81     char linkfile[1024];
     82     int linksize;
     83 #endif
     84 
     85     /* First allow environment variable override */
     86     spot = SDL_getenv("SDL_VIDEO_WAYLAND_WMCLASS");
     87     if (spot) {
     88         return SDL_strdup(spot);
     89     } else {
     90         /* Fallback to the "old" envvar */
     91         spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS");
     92         if (spot) {
     93             return SDL_strdup(spot);
     94         }
     95     }
     96 
     97     /* Next look at the application's executable name */
     98 #if defined(__LINUX__) || defined(__FREEBSD__)
     99 #if defined(__LINUX__)
    100     SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/exe", getpid());
    101 #elif defined(__FREEBSD__)
    102     SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/file",
    103                  getpid());
    104 #else
    105 #error Where can we find the executable name?
    106 #endif
    107     linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1);
    108     if (linksize > 0) {
    109         linkfile[linksize] = '\0';
    110         spot = SDL_strrchr(linkfile, '/');
    111         if (spot) {
    112             return SDL_strdup(spot + 1);
    113         } else {
    114             return SDL_strdup(linkfile);
    115         }
    116     }
    117 #endif /* __LINUX__ || __FREEBSD__ */
    118 
    119     /* Finally use the default we've used forever */
    120     return SDL_strdup("SDL_App");
    121 }
    122 
    123 static void
    124 Wayland_DeleteDevice(SDL_VideoDevice *device)
    125 {
    126     SDL_VideoData *data = (SDL_VideoData *)device->driverdata;
    127     if (data->display) {
    128         WAYLAND_wl_display_flush(data->display);
    129         WAYLAND_wl_display_disconnect(data->display);
    130     }
    131     SDL_free(data);
    132     SDL_free(device);
    133     SDL_WAYLAND_UnloadSymbols();
    134 }
    135 
    136 static SDL_VideoDevice *
    137 Wayland_CreateDevice(int devindex)
    138 {
    139     SDL_VideoDevice *device;
    140     SDL_VideoData *data;
    141     struct wl_display *display;
    142 
    143     if (!SDL_WAYLAND_LoadSymbols()) {
    144         return NULL;
    145     }
    146 
    147     display = WAYLAND_wl_display_connect(NULL);
    148     if (display == NULL) {
    149         SDL_WAYLAND_UnloadSymbols();
    150         return NULL;
    151     }
    152 
    153     data = SDL_calloc(1, sizeof(*data));
    154     if (data == NULL) {
    155         WAYLAND_wl_display_disconnect(display);
    156         SDL_WAYLAND_UnloadSymbols();
    157         SDL_OutOfMemory();
    158         return NULL;
    159     }
    160 
    161     data->display = display;
    162 
    163     /* Initialize all variables that we clean on shutdown */
    164     device = SDL_calloc(1, sizeof(SDL_VideoDevice));
    165     if (!device) {
    166         SDL_free(data);
    167         WAYLAND_wl_display_disconnect(display);
    168         SDL_WAYLAND_UnloadSymbols();
    169         SDL_OutOfMemory();
    170         return NULL;
    171     }
    172 
    173     device->driverdata = data;
    174 
    175     /* Set the function pointers */
    176     device->VideoInit = Wayland_VideoInit;
    177     device->VideoQuit = Wayland_VideoQuit;
    178     device->SetDisplayMode = Wayland_SetDisplayMode;
    179     device->GetDisplayModes = Wayland_GetDisplayModes;
    180     device->GetWindowWMInfo = Wayland_GetWindowWMInfo;
    181 
    182     device->PumpEvents = Wayland_PumpEvents;
    183 
    184     device->GL_SwapWindow = Wayland_GLES_SwapWindow;
    185     device->GL_GetSwapInterval = Wayland_GLES_GetSwapInterval;
    186     device->GL_SetSwapInterval = Wayland_GLES_SetSwapInterval;
    187     device->GL_GetDrawableSize = Wayland_GLES_GetDrawableSize;
    188     device->GL_MakeCurrent = Wayland_GLES_MakeCurrent;
    189     device->GL_CreateContext = Wayland_GLES_CreateContext;
    190     device->GL_LoadLibrary = Wayland_GLES_LoadLibrary;
    191     device->GL_UnloadLibrary = Wayland_GLES_UnloadLibrary;
    192     device->GL_GetProcAddress = Wayland_GLES_GetProcAddress;
    193     device->GL_DeleteContext = Wayland_GLES_DeleteContext;
    194 
    195     device->CreateSDLWindow = Wayland_CreateWindow;
    196     device->ShowWindow = Wayland_ShowWindow;
    197     device->SetWindowFullscreen = Wayland_SetWindowFullscreen;
    198     device->MaximizeWindow = Wayland_MaximizeWindow;
    199     device->SetWindowGrab = Wayland_SetWindowGrab;
    200     device->RestoreWindow = Wayland_RestoreWindow;
    201     device->SetWindowBordered = Wayland_SetWindowBordered;
    202     device->SetWindowSize = Wayland_SetWindowSize;
    203     device->SetWindowTitle = Wayland_SetWindowTitle;
    204     device->DestroyWindow = Wayland_DestroyWindow;
    205     device->SetWindowHitTest = Wayland_SetWindowHitTest;
    206 
    207     device->SetClipboardText = Wayland_SetClipboardText;
    208     device->GetClipboardText = Wayland_GetClipboardText;
    209     device->HasClipboardText = Wayland_HasClipboardText;
    210 
    211 #if SDL_VIDEO_VULKAN
    212     device->Vulkan_LoadLibrary = Wayland_Vulkan_LoadLibrary;
    213     device->Vulkan_UnloadLibrary = Wayland_Vulkan_UnloadLibrary;
    214     device->Vulkan_GetInstanceExtensions = Wayland_Vulkan_GetInstanceExtensions;
    215     device->Vulkan_CreateSurface = Wayland_Vulkan_CreateSurface;
    216     device->Vulkan_GetDrawableSize = Wayland_Vulkan_GetDrawableSize;
    217 #endif
    218 
    219     device->free = Wayland_DeleteDevice;
    220 
    221     return device;
    222 }
    223 
    224 VideoBootStrap Wayland_bootstrap = {
    225     WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver",
    226     Wayland_CreateDevice
    227 };
    228 
    229 static void
    230 display_handle_geometry(void *data,
    231                         struct wl_output *output,
    232                         int x, int y,
    233                         int physical_width,
    234                         int physical_height,
    235                         int subpixel,
    236                         const char *make,
    237                         const char *model,
    238                         int transform)
    239 
    240 {
    241     SDL_VideoDisplay *display = data;
    242 
    243     display->name = SDL_strdup(model);
    244 }
    245 
    246 static void
    247 display_handle_mode(void *data,
    248                     struct wl_output *output,
    249                     uint32_t flags,
    250                     int width,
    251                     int height,
    252                     int refresh)
    253 {
    254     SDL_DisplayMode mode;
    255     SDL_VideoDisplay *display = data;
    256 
    257     SDL_zero(mode);
    258     mode.format = SDL_PIXELFORMAT_RGB888;
    259     mode.w = width;
    260     mode.h = height;
    261     mode.refresh_rate = refresh / 1000; // mHz to Hz
    262     mode.driverdata = ((SDL_WaylandOutputData*)display->driverdata)->output;
    263     SDL_AddDisplayMode(display, &mode);
    264 
    265     if (flags & WL_OUTPUT_MODE_CURRENT) {
    266         display->current_mode = mode;
    267         display->desktop_mode = mode;
    268     }
    269 }
    270 
    271 static void
    272 display_handle_done(void *data,
    273                     struct wl_output *output)
    274 {
    275     /* !!! FIXME: this will fail on any further property changes! */
    276     SDL_VideoDisplay *display = data;
    277     SDL_AddVideoDisplay(display, SDL_FALSE);
    278     wl_output_set_user_data(output, display->driverdata);
    279     SDL_free(display->name);
    280     SDL_free(display);
    281 }
    282 
    283 static void
    284 display_handle_scale(void *data,
    285                      struct wl_output *output,
    286                      int32_t factor)
    287 {
    288     SDL_VideoDisplay *display = data;
    289     ((SDL_WaylandOutputData*)display->driverdata)->scale_factor = factor;
    290 }
    291 
    292 static const struct wl_output_listener output_listener = {
    293     display_handle_geometry,
    294     display_handle_mode,
    295     display_handle_done,
    296     display_handle_scale
    297 };
    298 
    299 static void
    300 Wayland_add_display(SDL_VideoData *d, uint32_t id)
    301 {
    302     struct wl_output *output;
    303     SDL_WaylandOutputData *data;
    304     SDL_VideoDisplay *display = SDL_malloc(sizeof *display);
    305     if (!display) {
    306         SDL_OutOfMemory();
    307         return;
    308     }
    309     SDL_zero(*display);
    310 
    311     output = wl_registry_bind(d->registry, id, &wl_output_interface, 2);
    312     if (!output) {
    313         SDL_SetError("Failed to retrieve output.");
    314         SDL_free(display);
    315         return;
    316     }
    317     data = SDL_malloc(sizeof *data);
    318     data->output = output;
    319     data->scale_factor = 1.0;
    320     display->driverdata = data;
    321 
    322     wl_output_add_listener(output, &output_listener, display);
    323 }
    324 
    325 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
    326 static void
    327 windowmanager_hints(void *data, struct qt_windowmanager *qt_windowmanager,
    328         int32_t show_is_fullscreen)
    329 {
    330 }
    331 
    332 static void
    333 windowmanager_quit(void *data, struct qt_windowmanager *qt_windowmanager)
    334 {
    335     SDL_SendQuit();
    336 }
    337 
    338 static const struct qt_windowmanager_listener windowmanager_listener = {
    339     windowmanager_hints,
    340     windowmanager_quit,
    341 };
    342 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
    343 
    344 
    345 static void
    346 handle_ping_zxdg_shell(void *data, struct zxdg_shell_v6 *zxdg, uint32_t serial)
    347 {
    348     zxdg_shell_v6_pong(zxdg, serial);
    349 }
    350 
    351 static const struct zxdg_shell_v6_listener shell_listener_zxdg = {
    352     handle_ping_zxdg_shell
    353 };
    354 
    355 
    356 static void
    357 handle_ping_xdg_wm_base(void *data, struct xdg_wm_base *xdg, uint32_t serial)
    358 {
    359     xdg_wm_base_pong(xdg, serial);
    360 }
    361 
    362 static const struct xdg_wm_base_listener shell_listener_xdg = {
    363     handle_ping_xdg_wm_base
    364 };
    365 
    366 
    367 static void
    368 display_handle_global(void *data, struct wl_registry *registry, uint32_t id,
    369                       const char *interface, uint32_t version)
    370 {
    371     SDL_VideoData *d = data;
    372 
    373     /*printf("WAYLAND INTERFACE: %s\n", interface);*/
    374 
    375     if (strcmp(interface, "wl_compositor") == 0) {
    376         d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, SDL_min(3, version));
    377     } else if (strcmp(interface, "wl_output") == 0) {
    378         Wayland_add_display(d, id);
    379     } else if (strcmp(interface, "wl_seat") == 0) {
    380         Wayland_display_add_input(d, id, version);
    381     } else if (strcmp(interface, "xdg_wm_base") == 0) {
    382         d->shell.xdg = wl_registry_bind(d->registry, id, &xdg_wm_base_interface, 1);
    383         xdg_wm_base_add_listener(d->shell.xdg, &shell_listener_xdg, NULL);
    384     } else if (strcmp(interface, "zxdg_shell_v6") == 0) {
    385         d->shell.zxdg = wl_registry_bind(d->registry, id, &zxdg_shell_v6_interface, 1);
    386         zxdg_shell_v6_add_listener(d->shell.zxdg, &shell_listener_zxdg, NULL);
    387     } else if (strcmp(interface, "wl_shell") == 0) {
    388         d->shell.wl = wl_registry_bind(d->registry, id, &wl_shell_interface, 1);
    389     } else if (strcmp(interface, "wl_shm") == 0) {
    390         d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
    391         d->cursor_theme = WAYLAND_wl_cursor_theme_load(NULL, 32, d->shm);
    392     } else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) {
    393         Wayland_display_add_relative_pointer_manager(d, id);
    394     } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) {
    395         Wayland_display_add_pointer_constraints(d, id);
    396     } else if (strcmp(interface, "wl_data_device_manager") == 0) {
    397         d->data_device_manager = wl_registry_bind(d->registry, id, &wl_data_device_manager_interface, SDL_min(3, version));
    398     } else if (strcmp(interface, "zxdg_decoration_manager_v1") == 0) {
    399         d->decoration_manager = wl_registry_bind(d->registry, id, &zxdg_decoration_manager_v1_interface, 1);
    400     } else if (strcmp(interface, "org_kde_kwin_server_decoration_manager") == 0) {
    401         d->kwin_server_decoration_manager = wl_registry_bind(d->registry, id, &org_kde_kwin_server_decoration_manager_interface, 1);
    402 
    403 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
    404     } else if (strcmp(interface, "qt_touch_extension") == 0) {
    405         Wayland_touch_create(d, id);
    406     } else if (strcmp(interface, "qt_surface_extension") == 0) {
    407         d->surface_extension = wl_registry_bind(registry, id,
    408                 &qt_surface_extension_interface, 1);
    409     } else if (strcmp(interface, "qt_windowmanager") == 0) {
    410         d->windowmanager = wl_registry_bind(registry, id,
    411                 &qt_windowmanager_interface, 1);
    412         qt_windowmanager_add_listener(d->windowmanager, &windowmanager_listener, d);
    413 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
    414     }
    415 }
    416 
    417 static void
    418 display_remove_global(void *data, struct wl_registry *registry, uint32_t id) {}
    419 
    420 static const struct wl_registry_listener registry_listener = {
    421     display_handle_global,
    422     display_remove_global
    423 };
    424 
    425 int
    426 Wayland_VideoInit(_THIS)
    427 {
    428     SDL_VideoData *data = (SDL_VideoData*)_this->driverdata;
    429 
    430     data->xkb_context = WAYLAND_xkb_context_new(0);
    431     if (!data->xkb_context) {
    432         return SDL_SetError("Failed to create XKB context");
    433     }
    434 
    435     data->registry = wl_display_get_registry(data->display);
    436     if (data->registry == NULL) {
    437         return SDL_SetError("Failed to get the Wayland registry");
    438     }
    439 
    440     wl_registry_add_listener(data->registry, &registry_listener, data);
    441 
    442     // First roundtrip to receive all registry objects.
    443     WAYLAND_wl_display_roundtrip(data->display);
    444 
    445     // Second roundtrip to receive all output events.
    446     WAYLAND_wl_display_roundtrip(data->display);
    447 
    448     Wayland_InitMouse();
    449 
    450     /* Get the surface class name, usually the name of the application */
    451     data->classname = get_classname();
    452 
    453     WAYLAND_wl_display_flush(data->display);
    454 
    455     return 0;
    456 }
    457 
    458 static void
    459 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display)
    460 {
    461     // Nothing to do here, everything was already done in the wl_output
    462     // callbacks.
    463 }
    464 
    465 static int
    466 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
    467 {
    468     return SDL_Unsupported();
    469 }
    470 
    471 void
    472 Wayland_VideoQuit(_THIS)
    473 {
    474     SDL_VideoData *data = _this->driverdata;
    475     int i, j;
    476 
    477     Wayland_FiniMouse ();
    478 
    479     for (i = 0; i < _this->num_displays; ++i) {
    480         SDL_VideoDisplay *display = &_this->displays[i];
    481 
    482         wl_output_destroy(((SDL_WaylandOutputData*)display->driverdata)->output);
    483         SDL_free(display->driverdata);
    484         display->driverdata = NULL;
    485 
    486         for (j = display->num_display_modes; j--;) {
    487             display->display_modes[j].driverdata = NULL;
    488         }
    489         display->desktop_mode.driverdata = NULL;
    490     }
    491 
    492     Wayland_display_destroy_input(data);
    493     Wayland_display_destroy_pointer_constraints(data);
    494     Wayland_display_destroy_relative_pointer_manager(data);
    495 
    496     if (data->xkb_context) {
    497         WAYLAND_xkb_context_unref(data->xkb_context);
    498         data->xkb_context = NULL;
    499     }
    500 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
    501     if (data->windowmanager)
    502         qt_windowmanager_destroy(data->windowmanager);
    503 
    504     if (data->surface_extension)
    505         qt_surface_extension_destroy(data->surface_extension);
    506 
    507     Wayland_touch_destroy(data);
    508 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
    509 
    510     if (data->shm)
    511         wl_shm_destroy(data->shm);
    512 
    513     if (data->cursor_theme)
    514         WAYLAND_wl_cursor_theme_destroy(data->cursor_theme);
    515 
    516     if (data->shell.wl)
    517         wl_shell_destroy(data->shell.wl);
    518 
    519     if (data->shell.xdg)
    520         xdg_wm_base_destroy(data->shell.xdg);
    521 
    522     if (data->shell.zxdg)
    523         zxdg_shell_v6_destroy(data->shell.zxdg);
    524 
    525     if (data->compositor)
    526         wl_compositor_destroy(data->compositor);
    527 
    528     if (data->registry)
    529         wl_registry_destroy(data->registry);
    530 
    531     SDL_free(data->classname);
    532 }
    533 
    534 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
    535 
    536 /* vi: set ts=4 sw=4 expandtab: */