| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189 |
- /*
- Simple DirectMedia Layer
- Copyright (C) 1997-2022 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"
- #if SDL_VIDEO_DRIVER_WAYLAND
- #include "../../events/SDL_events_c.h"
- #include "SDL_waylandvideo.h"
- #include "SDL_waylandevents_c.h"
- #include "SDL_waylandwindow.h"
- #include "SDL_waylandopengles.h"
- #include "SDL_waylandmouse.h"
- #include "SDL_waylandkeyboard.h"
- #include "SDL_waylandtouch.h"
- #include "SDL_waylandclipboard.h"
- #include "SDL_waylandvulkan.h"
- #include <sys/types.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <xkbcommon/xkbcommon.h>
- #include <wayland-util.h>
- #include "xdg-shell-client-protocol.h"
- #include "xdg-decoration-unstable-v1-client-protocol.h"
- #include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
- #include "idle-inhibit-unstable-v1-client-protocol.h"
- #include "xdg-activation-v1-client-protocol.h"
- #include "text-input-unstable-v3-client-protocol.h"
- #include "tablet-unstable-v2-client-protocol.h"
- #include "xdg-output-unstable-v1-client-protocol.h"
- #include "viewporter-client-protocol.h"
- #include "primary-selection-unstable-v1-client-protocol.h"
- #include "fractional-scale-v1-client-protocol.h"
- #include "input-timestamps-unstable-v1-client-protocol.h"
- #ifdef HAVE_LIBDECOR_H
- #include <libdecor.h>
- #endif
- #define WAYLANDVID_DRIVER_NAME "wayland"
- static void display_handle_done(void *data, struct wl_output *output);
- /* Initialization/Query functions */
- static int Wayland_VideoInit(_THIS);
- static int Wayland_GetDisplayBounds(_THIS, SDL_VideoDisplay *display, SDL_Rect *rect);
- static int Wayland_GetDisplayDPI(_THIS, SDL_VideoDisplay *sdl_display, float *ddpi, float *hdpi, float *vdpi);
- static void Wayland_VideoQuit(_THIS);
- /* Find out what class name we should use
- * Based on src/video/x11/SDL_x11video.c */
- static char *get_classname()
- {
- /* !!! FIXME: this is probably wrong, albeit harmless in many common cases. From protocol spec:
- "The surface class identifies the general class of applications
- to which the surface belongs. A common convention is to use the
- file name (or the full path if it is a non-standard location) of
- the application's .desktop file as the class." */
- 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_WAYLAND_WMCLASS");
- if (spot) {
- return SDL_strdup(spot);
- } else {
- /* Fallback to the "old" envvar */
- 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");
- }
- static const char *SDL_WAYLAND_surface_tag = "sdl-window";
- static const char *SDL_WAYLAND_output_tag = "sdl-output";
- void SDL_WAYLAND_register_surface(struct wl_surface *surface)
- {
- wl_proxy_set_tag((struct wl_proxy *)surface, &SDL_WAYLAND_surface_tag);
- }
- void SDL_WAYLAND_register_output(struct wl_output *output)
- {
- wl_proxy_set_tag((struct wl_proxy *)output, &SDL_WAYLAND_output_tag);
- }
- SDL_bool SDL_WAYLAND_own_surface(struct wl_surface *surface)
- {
- return wl_proxy_get_tag((struct wl_proxy *)surface) == &SDL_WAYLAND_surface_tag;
- }
- SDL_bool SDL_WAYLAND_own_output(struct wl_output *output)
- {
- return wl_proxy_get_tag((struct wl_proxy *)output) == &SDL_WAYLAND_output_tag;
- }
- static void Wayland_DeleteDevice(SDL_VideoDevice *device)
- {
- SDL_VideoData *data = (SDL_VideoData *)device->driverdata;
- if (data->display) {
- WAYLAND_wl_display_flush(data->display);
- WAYLAND_wl_display_disconnect(data->display);
- }
- if (device->wakeup_lock) {
- SDL_DestroyMutex(device->wakeup_lock);
- }
- SDL_free(data);
- SDL_free(device);
- SDL_WAYLAND_UnloadSymbols();
- }
- static SDL_VideoDevice *Wayland_CreateDevice(void)
- {
- SDL_VideoDevice *device;
- SDL_VideoData *data;
- struct wl_display *display;
- if (!SDL_WAYLAND_LoadSymbols()) {
- return NULL;
- }
- display = WAYLAND_wl_display_connect(NULL);
- if (display == NULL) {
- SDL_WAYLAND_UnloadSymbols();
- return NULL;
- }
- data = SDL_calloc(1, sizeof(*data));
- if (data == NULL) {
- WAYLAND_wl_display_disconnect(display);
- SDL_WAYLAND_UnloadSymbols();
- SDL_OutOfMemory();
- return NULL;
- }
- data->initializing = SDL_TRUE;
- data->display = display;
- /* Initialize all variables that we clean on shutdown */
- device = SDL_calloc(1, sizeof(SDL_VideoDevice));
- if (device == NULL) {
- SDL_free(data);
- WAYLAND_wl_display_disconnect(display);
- SDL_WAYLAND_UnloadSymbols();
- SDL_OutOfMemory();
- return NULL;
- }
- device->driverdata = data;
- device->wakeup_lock = SDL_CreateMutex();
- /* Set the function pointers */
- device->VideoInit = Wayland_VideoInit;
- device->VideoQuit = Wayland_VideoQuit;
- device->GetDisplayBounds = Wayland_GetDisplayBounds;
- device->GetDisplayDPI = Wayland_GetDisplayDPI;
- device->GetWindowWMInfo = Wayland_GetWindowWMInfo;
- device->SuspendScreenSaver = Wayland_SuspendScreenSaver;
- device->PumpEvents = Wayland_PumpEvents;
- device->WaitEventTimeout = Wayland_WaitEventTimeout;
- device->SendWakeupEvent = Wayland_SendWakeupEvent;
- #if SDL_VIDEO_OPENGL_EGL
- device->GL_SwapWindow = Wayland_GLES_SwapWindow;
- device->GL_GetSwapInterval = Wayland_GLES_GetSwapInterval;
- device->GL_SetSwapInterval = Wayland_GLES_SetSwapInterval;
- device->GL_MakeCurrent = Wayland_GLES_MakeCurrent;
- device->GL_CreateContext = Wayland_GLES_CreateContext;
- device->GL_LoadLibrary = Wayland_GLES_LoadLibrary;
- device->GL_UnloadLibrary = Wayland_GLES_UnloadLibrary;
- device->GL_GetProcAddress = Wayland_GLES_GetProcAddress;
- device->GL_DeleteContext = Wayland_GLES_DeleteContext;
- device->GL_GetEGLSurface = Wayland_GLES_GetEGLSurface;
- #endif
- device->CreateSDLWindow = Wayland_CreateWindow;
- device->ShowWindow = Wayland_ShowWindow;
- device->HideWindow = Wayland_HideWindow;
- device->RaiseWindow = Wayland_RaiseWindow;
- device->SetWindowFullscreen = Wayland_SetWindowFullscreen;
- device->MaximizeWindow = Wayland_MaximizeWindow;
- device->MinimizeWindow = Wayland_MinimizeWindow;
- device->SetWindowMouseRect = Wayland_SetWindowMouseRect;
- device->SetWindowMouseGrab = Wayland_SetWindowMouseGrab;
- device->SetWindowKeyboardGrab = Wayland_SetWindowKeyboardGrab;
- device->RestoreWindow = Wayland_RestoreWindow;
- device->SetWindowBordered = Wayland_SetWindowBordered;
- device->SetWindowResizable = Wayland_SetWindowResizable;
- device->SetWindowSize = Wayland_SetWindowSize;
- device->SetWindowMinimumSize = Wayland_SetWindowMinimumSize;
- device->SetWindowMaximumSize = Wayland_SetWindowMaximumSize;
- device->SetWindowModalFor = Wayland_SetWindowModalFor;
- device->SetWindowTitle = Wayland_SetWindowTitle;
- device->GetWindowSizeInPixels = Wayland_GetWindowSizeInPixels;
- device->DestroyWindow = Wayland_DestroyWindow;
- device->SetWindowHitTest = Wayland_SetWindowHitTest;
- device->FlashWindow = Wayland_FlashWindow;
- device->HasScreenKeyboardSupport = Wayland_HasScreenKeyboardSupport;
- device->SetClipboardText = Wayland_SetClipboardText;
- device->GetClipboardText = Wayland_GetClipboardText;
- device->HasClipboardText = Wayland_HasClipboardText;
- device->SetPrimarySelectionText = Wayland_SetPrimarySelectionText;
- device->GetPrimarySelectionText = Wayland_GetPrimarySelectionText;
- device->HasPrimarySelectionText = Wayland_HasPrimarySelectionText;
- device->StartTextInput = Wayland_StartTextInput;
- device->StopTextInput = Wayland_StopTextInput;
- device->SetTextInputRect = Wayland_SetTextInputRect;
- #if SDL_VIDEO_VULKAN
- device->Vulkan_LoadLibrary = Wayland_Vulkan_LoadLibrary;
- device->Vulkan_UnloadLibrary = Wayland_Vulkan_UnloadLibrary;
- device->Vulkan_GetInstanceExtensions = Wayland_Vulkan_GetInstanceExtensions;
- device->Vulkan_CreateSurface = Wayland_Vulkan_CreateSurface;
- #endif
- device->free = Wayland_DeleteDevice;
- device->quirk_flags = VIDEO_DEVICE_QUIRK_DISABLE_DISPLAY_MODE_SWITCHING |
- VIDEO_DEVICE_QUIRK_DISABLE_UNSET_FULLSCREEN_ON_MINIMIZE;
- return device;
- }
- VideoBootStrap Wayland_bootstrap = {
- WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver",
- Wayland_CreateDevice
- };
- static void xdg_output_handle_logical_position(void *data, struct zxdg_output_v1 *xdg_output,
- int32_t x, int32_t y)
- {
- SDL_WaylandOutputData *driverdata = data;
- driverdata->x = x;
- driverdata->y = y;
- driverdata->has_logical_position = SDL_TRUE;
- }
- static void xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output,
- int32_t width, int32_t height)
- {
- SDL_WaylandOutputData *driverdata = data;
- if (driverdata->width != 0 && driverdata->height != 0) {
- /* FIXME: GNOME has a bug where the logical size does not account for
- * scale, resulting in bogus viewport sizes.
- *
- * Until this is fixed, validate that _some_ kind of scaling is being
- * done (we can't match exactly because fractional scaling can't be
- * detected otherwise), then override if necessary.
- * -flibit
- */
- const float scale = (float)driverdata->width / (float)width;
- if ((scale == 1.0f) && (driverdata->scale_factor != 1.0f)) {
- SDL_LogWarn(
- SDL_LOG_CATEGORY_VIDEO,
- "xdg_output scale did not match, overriding with wl_output scale");
- return;
- }
- }
- driverdata->width = width;
- driverdata->height = height;
- driverdata->has_logical_size = SDL_TRUE;
- }
- static void xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output)
- {
- SDL_WaylandOutputData *driverdata = data;
- /*
- * xdg-output.done events are deprecated and only apply below version 3 of the protocol.
- * A wl-output.done event will be emitted in version 3 or higher.
- */
- if (zxdg_output_v1_get_version(driverdata->xdg_output) < 3) {
- display_handle_done(data, driverdata->output);
- }
- }
- static void xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output,
- const char *name)
- {
- }
- static void xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output,
- const char *description)
- {
- SDL_WaylandOutputData *driverdata = data;
- if (driverdata->index == -1) {
- /* xdg-output descriptions, if available, supersede wl-output model names. */
- if (driverdata->placeholder.name != NULL) {
- SDL_free(driverdata->placeholder.name);
- }
- driverdata->placeholder.name = SDL_strdup(description);
- }
- }
- static const struct zxdg_output_v1_listener xdg_output_listener = {
- xdg_output_handle_logical_position,
- xdg_output_handle_logical_size,
- xdg_output_handle_done,
- xdg_output_handle_name,
- xdg_output_handle_description,
- };
- static void AddEmulatedModes(SDL_VideoDisplay *dpy, SDL_bool rot_90)
- {
- struct EmulatedMode
- {
- int w;
- int h;
- };
- /* Resolution lists courtesy of XWayland */
- const struct EmulatedMode mode_list[] = {
- /* 16:9 (1.77) */
- { 7680, 4320 },
- { 6144, 3160 },
- { 5120, 2880 },
- { 4096, 2304 },
- { 3840, 2160 },
- { 3200, 1800 },
- { 2880, 1620 },
- { 2560, 1440 },
- { 2048, 1152 },
- { 1920, 1080 },
- { 1600, 900 },
- { 1368, 768 },
- { 1280, 720 },
- { 864, 486 },
- /* 16:10 (1.6) */
- { 2560, 1600 },
- { 1920, 1200 },
- { 1680, 1050 },
- { 1440, 900 },
- { 1280, 800 },
- /* 3:2 (1.5) */
- { 720, 480 },
- /* 4:3 (1.33) */
- { 2048, 1536 },
- { 1920, 1440 },
- { 1600, 1200 },
- { 1440, 1080 },
- { 1400, 1050 },
- { 1280, 1024 },
- { 1280, 960 },
- { 1152, 864 },
- { 1024, 768 },
- { 800, 600 },
- { 640, 480 }
- };
- int i;
- SDL_DisplayMode mode;
- const int native_width = dpy->display_modes->w;
- const int native_height = dpy->display_modes->h;
- for (i = 0; i < SDL_arraysize(mode_list); ++i) {
- mode = *dpy->display_modes;
- if (rot_90) {
- mode.w = mode_list[i].h;
- mode.h = mode_list[i].w;
- } else {
- mode.w = mode_list[i].w;
- mode.h = mode_list[i].h;
- }
- /* Only add modes that are smaller than the native mode. */
- if ((mode.w < native_width && mode.h < native_height) ||
- (mode.w < native_width && mode.h == native_height) ||
- (mode.w == native_width && mode.h < native_height)) {
- SDL_AddDisplayMode(dpy, &mode);
- }
- }
- }
- static void display_handle_geometry(void *data,
- struct wl_output *output,
- int x, int y,
- int physical_width,
- int physical_height,
- int subpixel,
- const char *make,
- const char *model,
- int transform)
- {
- SDL_WaylandOutputData *driverdata = data;
- SDL_VideoDisplay *display;
- int i;
- if (driverdata->wl_output_done_count) {
- /* Clear the wl_output ref so Reset doesn't free it */
- display = SDL_GetDisplay(driverdata->index);
- for (i = 0; i < display->num_display_modes; i += 1) {
- display->display_modes[i].driverdata = NULL;
- }
- /* Okay, now it's safe to reset */
- SDL_ResetDisplayModes(driverdata->index);
- /* The display has officially started over. */
- driverdata->wl_output_done_count = 0;
- }
- /* Apply the change from wl-output only if xdg-output is not supported */
- if (!driverdata->has_logical_position) {
- driverdata->x = x;
- driverdata->y = y;
- }
- driverdata->physical_width = physical_width;
- driverdata->physical_height = physical_height;
- /* The output name is only set if xdg-output hasn't provided a description. */
- if (driverdata->index == -1 && driverdata->placeholder.name == NULL) {
- driverdata->placeholder.name = SDL_strdup(model);
- }
- driverdata->transform = transform;
- #define TF_CASE(in, out) \
- case WL_OUTPUT_TRANSFORM_##in: \
- driverdata->orientation = SDL_ORIENTATION_##out; \
- break;
- if (driverdata->physical_width >= driverdata->physical_height) {
- switch (transform) {
- TF_CASE(NORMAL, LANDSCAPE)
- TF_CASE(90, PORTRAIT)
- TF_CASE(180, LANDSCAPE_FLIPPED)
- TF_CASE(270, PORTRAIT_FLIPPED)
- TF_CASE(FLIPPED, LANDSCAPE_FLIPPED)
- TF_CASE(FLIPPED_90, PORTRAIT_FLIPPED)
- TF_CASE(FLIPPED_180, LANDSCAPE)
- TF_CASE(FLIPPED_270, PORTRAIT)
- }
- } else {
- switch (transform) {
- TF_CASE(NORMAL, PORTRAIT)
- TF_CASE(90, LANDSCAPE)
- TF_CASE(180, PORTRAIT_FLIPPED)
- TF_CASE(270, LANDSCAPE_FLIPPED)
- TF_CASE(FLIPPED, PORTRAIT_FLIPPED)
- TF_CASE(FLIPPED_90, LANDSCAPE_FLIPPED)
- TF_CASE(FLIPPED_180, PORTRAIT)
- TF_CASE(FLIPPED_270, LANDSCAPE)
- }
- }
- #undef TF_CASE
- }
- static void display_handle_mode(void *data,
- struct wl_output *output,
- uint32_t flags,
- int width,
- int height,
- int refresh)
- {
- SDL_WaylandOutputData *driverdata = data;
- if (flags & WL_OUTPUT_MODE_CURRENT) {
- driverdata->native_width = width;
- driverdata->native_height = height;
- /*
- * Don't rotate this yet, wl-output coordinates are transformed in
- * handle_done and xdg-output coordinates are pre-transformed.
- */
- if (!driverdata->has_logical_size) {
- driverdata->width = width;
- driverdata->height = height;
- }
- driverdata->refresh = refresh;
- }
- }
- static void display_handle_done(void *data,
- struct wl_output *output)
- {
- SDL_WaylandOutputData *driverdata = data;
- SDL_VideoData *video = driverdata->videodata;
- SDL_DisplayMode native_mode, desktop_mode;
- SDL_VideoDisplay *dpy;
- const SDL_bool mode_emulation_enabled = SDL_GetHintBoolean(SDL_HINT_VIDEO_WAYLAND_MODE_EMULATION, SDL_TRUE);
- /*
- * When using xdg-output, two wl-output.done events will be emitted:
- * one at the completion of wl-display and one at the completion of xdg-output.
- *
- * All required events must be received before proceeding.
- */
- const int event_await_count = 1 + (driverdata->xdg_output != NULL);
- driverdata->wl_output_done_count = SDL_min(driverdata->wl_output_done_count + 1, event_await_count + 1);
- if (driverdata->wl_output_done_count != event_await_count) {
- return;
- }
- /* The native display resolution */
- SDL_zero(native_mode);
- native_mode.format = SDL_PIXELFORMAT_RGB888;
- if (driverdata->transform & WL_OUTPUT_TRANSFORM_90) {
- native_mode.w = driverdata->native_height;
- native_mode.h = driverdata->native_width;
- } else {
- native_mode.w = driverdata->native_width;
- native_mode.h = driverdata->native_height;
- }
- native_mode.refresh_rate = (int)SDL_round(driverdata->refresh / 1000.0); /* mHz to Hz */
- native_mode.driverdata = driverdata->output;
- /* The scaled desktop mode */
- SDL_zero(desktop_mode);
- desktop_mode.format = SDL_PIXELFORMAT_RGB888;
- if (driverdata->has_logical_size) { /* If xdg-output is present, calculate the true scale of the desktop */
- driverdata->scale_factor = (float)native_mode.w / (float)driverdata->width;
- } else { /* Scale the desktop coordinates, if xdg-output isn't present */
- driverdata->width /= driverdata->scale_factor;
- driverdata->height /= driverdata->scale_factor;
- }
- /* xdg-output dimensions are already transformed, so no need to rotate. */
- if (driverdata->has_logical_size || !(driverdata->transform & WL_OUTPUT_TRANSFORM_90)) {
- desktop_mode.w = driverdata->width;
- desktop_mode.h = driverdata->height;
- } else {
- desktop_mode.w = driverdata->height;
- desktop_mode.h = driverdata->width;
- }
- desktop_mode.refresh_rate = (int)SDL_round(driverdata->refresh / 1000.0); /* mHz to Hz */
- desktop_mode.driverdata = driverdata->output;
- /*
- * The native display mode is only exposed separately from the desktop size if the
- * desktop is scaled and the wp_viewporter protocol is supported.
- */
- if (driverdata->scale_factor > 1.0f && video->viewporter != NULL) {
- if (driverdata->index > -1) {
- SDL_AddDisplayMode(SDL_GetDisplay(driverdata->index), &native_mode);
- } else {
- SDL_AddDisplayMode(&driverdata->placeholder, &native_mode);
- }
- }
- /* Calculate the display DPI */
- if (driverdata->transform & WL_OUTPUT_TRANSFORM_90) {
- driverdata->hdpi = driverdata->physical_height ? (((float)driverdata->height) * 25.4f / driverdata->physical_height) : 0.0f;
- driverdata->vdpi = driverdata->physical_width ? (((float)driverdata->width) * 25.4f / driverdata->physical_width) : 0.0f;
- driverdata->ddpi = SDL_ComputeDiagonalDPI(driverdata->height,
- driverdata->width,
- ((float)driverdata->physical_height) / 25.4f,
- ((float)driverdata->physical_width) / 25.4f);
- } else {
- driverdata->hdpi = driverdata->physical_width ? (((float)driverdata->width) * 25.4f / driverdata->physical_width) : 0.0f;
- driverdata->vdpi = driverdata->physical_height ? (((float)driverdata->height) * 25.4f / driverdata->physical_height) : 0.0f;
- driverdata->ddpi = SDL_ComputeDiagonalDPI(driverdata->width,
- driverdata->height,
- ((float)driverdata->physical_width) / 25.4f,
- ((float)driverdata->physical_height) / 25.4f);
- }
- if (driverdata->index > -1) {
- dpy = SDL_GetDisplay(driverdata->index);
- } else {
- dpy = &driverdata->placeholder;
- }
- SDL_AddDisplayMode(dpy, &desktop_mode);
- SDL_SetCurrentDisplayMode(dpy, &desktop_mode);
- SDL_SetDesktopDisplayMode(dpy, &desktop_mode);
- /* Add emulated modes if wp_viewporter is supported and mode emulation is enabled. */
- if (video->viewporter && mode_emulation_enabled) {
- const SDL_bool rot_90 = ((driverdata->transform & WL_OUTPUT_TRANSFORM_90) != 0) ||
- (driverdata->width < driverdata->height);
- AddEmulatedModes(dpy, rot_90);
- }
- if (driverdata->index == -1) {
- /* First time getting display info, create the VideoDisplay */
- SDL_bool send_event = driverdata->videodata->initializing ? SDL_FALSE : SDL_TRUE;
- driverdata->placeholder.orientation = driverdata->orientation;
- driverdata->placeholder.driverdata = driverdata;
- driverdata->index = SDL_AddVideoDisplay(&driverdata->placeholder, send_event);
- SDL_free(driverdata->placeholder.name);
- SDL_zero(driverdata->placeholder);
- } else {
- SDL_SendDisplayEvent(dpy, SDL_DISPLAYEVENT_ORIENTATION, driverdata->orientation);
- }
- }
- static void display_handle_scale(void *data,
- struct wl_output *output,
- int32_t factor)
- {
- SDL_WaylandOutputData *driverdata = data;
- driverdata->scale_factor = factor;
- }
- static const struct wl_output_listener output_listener = {
- display_handle_geometry,
- display_handle_mode,
- display_handle_done,
- display_handle_scale
- };
- static void Wayland_add_display(SDL_VideoData *d, uint32_t id)
- {
- struct wl_output *output;
- SDL_WaylandOutputData *data;
- output = wl_registry_bind(d->registry, id, &wl_output_interface, 2);
- if (output == NULL) {
- SDL_SetError("Failed to retrieve output.");
- return;
- }
- data = SDL_malloc(sizeof *data);
- SDL_zerop(data);
- data->videodata = d;
- data->output = output;
- data->registry_id = id;
- data->scale_factor = 1.0f;
- data->index = -1;
- wl_output_add_listener(output, &output_listener, data);
- SDL_WAYLAND_register_output(output);
- /* Keep a list of outputs for deferred xdg-output initialization. */
- if (d->output_list != NULL) {
- SDL_WaylandOutputData *node = d->output_list;
- while (node->next != NULL) {
- node = node->next;
- }
- node->next = (struct SDL_WaylandOutputData *)data;
- } else {
- d->output_list = (struct SDL_WaylandOutputData *)data;
- }
- if (data->videodata->xdg_output_manager) {
- data->xdg_output = zxdg_output_manager_v1_get_xdg_output(data->videodata->xdg_output_manager, output);
- zxdg_output_v1_add_listener(data->xdg_output, &xdg_output_listener, data);
- }
- }
- static void Wayland_free_display(SDL_VideoData *d, uint32_t id)
- {
- int num_displays = SDL_GetNumVideoDisplays();
- SDL_VideoDisplay *display;
- SDL_WaylandOutputData *data;
- int i;
- for (i = 0; i < num_displays; i += 1) {
- display = SDL_GetDisplay(i);
- data = (SDL_WaylandOutputData *)display->driverdata;
- if (data->registry_id == id) {
- if (d->output_list != NULL) {
- SDL_WaylandOutputData *node = d->output_list;
- if (node == data) {
- d->output_list = node->next;
- } else {
- while (node->next != data && node->next != NULL) {
- node = node->next;
- }
- if (node->next != NULL) {
- node->next = node->next->next;
- }
- }
- }
- SDL_DelVideoDisplay(i);
- if (data->xdg_output) {
- zxdg_output_v1_destroy(data->xdg_output);
- }
- wl_output_destroy(data->output);
- SDL_free(data);
- /* Update the index for all remaining displays */
- num_displays -= 1;
- for (; i < num_displays; i += 1) {
- display = SDL_GetDisplay(i);
- data = (SDL_WaylandOutputData *)display->driverdata;
- data->index -= 1;
- }
- return;
- }
- }
- }
- static void Wayland_init_xdg_output(SDL_VideoData *d)
- {
- SDL_WaylandOutputData *node;
- for (node = d->output_list; node != NULL; node = node->next) {
- node->xdg_output = zxdg_output_manager_v1_get_xdg_output(node->videodata->xdg_output_manager, node->output);
- zxdg_output_v1_add_listener(node->xdg_output, &xdg_output_listener, node);
- }
- }
- #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
- static void windowmanager_hints(void *data, struct qt_windowmanager *qt_windowmanager,
- int32_t show_is_fullscreen)
- {
- }
- static void windowmanager_quit(void *data, struct qt_windowmanager *qt_windowmanager)
- {
- SDL_SendQuit();
- }
- static const struct qt_windowmanager_listener windowmanager_listener = {
- windowmanager_hints,
- windowmanager_quit,
- };
- #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
- static void handle_ping_xdg_wm_base(void *data, struct xdg_wm_base *xdg, uint32_t serial)
- {
- xdg_wm_base_pong(xdg, serial);
- }
- static const struct xdg_wm_base_listener shell_listener_xdg = {
- handle_ping_xdg_wm_base
- };
- #ifdef HAVE_LIBDECOR_H
- static void libdecor_error(struct libdecor *context,
- enum libdecor_error error,
- const char *message)
- {
- SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "libdecor error (%d): %s\n", error, message);
- }
- static struct libdecor_interface libdecor_interface = {
- libdecor_error,
- };
- #endif
- static void display_handle_global(void *data, struct wl_registry *registry, uint32_t id,
- const char *interface, uint32_t version)
- {
- SDL_VideoData *d = data;
- /*printf("WAYLAND INTERFACE: %s\n", interface);*/
- if (SDL_strcmp(interface, "wl_compositor") == 0) {
- d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, SDL_min(4, version));
- } else if (SDL_strcmp(interface, "wl_output") == 0) {
- Wayland_add_display(d, id);
- } else if (SDL_strcmp(interface, "wl_seat") == 0) {
- Wayland_display_add_input(d, id, version);
- } else if (SDL_strcmp(interface, "xdg_wm_base") == 0) {
- d->shell.xdg = wl_registry_bind(d->registry, id, &xdg_wm_base_interface, SDL_min(version, 3));
- xdg_wm_base_add_listener(d->shell.xdg, &shell_listener_xdg, NULL);
- } else if (SDL_strcmp(interface, "wl_shm") == 0) {
- d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
- } else if (SDL_strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) {
- Wayland_display_add_relative_pointer_manager(d, id);
- } else if (SDL_strcmp(interface, "zwp_pointer_constraints_v1") == 0) {
- Wayland_display_add_pointer_constraints(d, id);
- } else if (SDL_strcmp(interface, "zwp_keyboard_shortcuts_inhibit_manager_v1") == 0) {
- d->key_inhibitor_manager = wl_registry_bind(d->registry, id, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1);
- } else if (SDL_strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0) {
- d->idle_inhibit_manager = wl_registry_bind(d->registry, id, &zwp_idle_inhibit_manager_v1_interface, 1);
- } else if (SDL_strcmp(interface, "xdg_activation_v1") == 0) {
- d->activation_manager = wl_registry_bind(d->registry, id, &xdg_activation_v1_interface, 1);
- } else if (SDL_strcmp(interface, "zwp_text_input_manager_v3") == 0) {
- Wayland_add_text_input_manager(d, id, version);
- } else if (SDL_strcmp(interface, "wl_data_device_manager") == 0) {
- Wayland_add_data_device_manager(d, id, version);
- } else if (SDL_strcmp(interface, "zwp_primary_selection_device_manager_v1") == 0) {
- Wayland_add_primary_selection_device_manager(d, id, version);
- } else if (SDL_strcmp(interface, "zxdg_decoration_manager_v1") == 0) {
- d->decoration_manager = wl_registry_bind(d->registry, id, &zxdg_decoration_manager_v1_interface, 1);
- } else if (SDL_strcmp(interface, "zwp_tablet_manager_v2") == 0) {
- d->tablet_manager = wl_registry_bind(d->registry, id, &zwp_tablet_manager_v2_interface, 1);
- if (d->input) {
- Wayland_input_add_tablet(d->input, d->tablet_manager);
- }
- } else if (SDL_strcmp(interface, "zxdg_output_manager_v1") == 0) {
- version = SDL_min(version, 3); /* Versions 1 through 3 are supported. */
- d->xdg_output_manager = wl_registry_bind(d->registry, id, &zxdg_output_manager_v1_interface, version);
- Wayland_init_xdg_output(d);
- } else if (SDL_strcmp(interface, "wp_viewporter") == 0) {
- d->viewporter = wl_registry_bind(d->registry, id, &wp_viewporter_interface, 1);
- } else if (SDL_strcmp(interface, "wp_fractional_scale_manager_v1") == 0) {
- d->fractional_scale_manager = wl_registry_bind(d->registry, id, &wp_fractional_scale_manager_v1_interface, 1);
- } else if (SDL_strcmp(interface, "zwp_input_timestamps_manager_v1") == 0) {
- d->input_timestamps_manager = wl_registry_bind(d->registry, id, &zwp_input_timestamps_manager_v1_interface, 1);
- if (d->input) {
- Wayland_RegisterTimestampListeners(d->input);
- }
- #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
- } else if (SDL_strcmp(interface, "qt_touch_extension") == 0) {
- Wayland_touch_create(d, id);
- } else if (SDL_strcmp(interface, "qt_surface_extension") == 0) {
- d->surface_extension = wl_registry_bind(registry, id,
- &qt_surface_extension_interface, 1);
- } else if (SDL_strcmp(interface, "qt_windowmanager") == 0) {
- d->windowmanager = wl_registry_bind(registry, id,
- &qt_windowmanager_interface, 1);
- qt_windowmanager_add_listener(d->windowmanager, &windowmanager_listener, d);
- #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
- }
- }
- static void display_remove_global(void *data, struct wl_registry *registry, uint32_t id)
- {
- SDL_VideoData *d = data;
- /* We don't get an interface, just an ID, so assume it's a wl_output :shrug: */
- Wayland_free_display(d, id);
- }
- static const struct wl_registry_listener registry_listener = {
- display_handle_global,
- display_remove_global
- };
- #ifdef HAVE_LIBDECOR_H
- static SDL_bool should_use_libdecor(SDL_VideoData *data, SDL_bool ignore_xdg)
- {
- if (!SDL_WAYLAND_HAVE_WAYLAND_LIBDECOR) {
- return SDL_FALSE;
- }
- if (!SDL_GetHintBoolean(SDL_HINT_VIDEO_WAYLAND_ALLOW_LIBDECOR, SDL_TRUE)) {
- return SDL_FALSE;
- }
- if (SDL_GetHintBoolean(SDL_HINT_VIDEO_WAYLAND_PREFER_LIBDECOR, SDL_FALSE)) {
- return SDL_TRUE;
- }
- if (ignore_xdg) {
- return SDL_TRUE;
- }
- if (data->decoration_manager) {
- return SDL_FALSE;
- }
- return SDL_TRUE;
- }
- #endif
- SDL_bool Wayland_LoadLibdecor(SDL_VideoData *data, SDL_bool ignore_xdg)
- {
- #ifdef HAVE_LIBDECOR_H
- if (data->shell.libdecor != NULL) {
- return SDL_TRUE; /* Already loaded! */
- }
- if (should_use_libdecor(data, ignore_xdg)) {
- data->shell.libdecor = libdecor_new(data->display, &libdecor_interface);
- return data->shell.libdecor != NULL;
- }
- #endif
- return SDL_FALSE;
- }
- int Wayland_VideoInit(_THIS)
- {
- SDL_VideoData *data = (SDL_VideoData *)_this->driverdata;
- data->xkb_context = WAYLAND_xkb_context_new(0);
- if (!data->xkb_context) {
- return SDL_SetError("Failed to create XKB context");
- }
- data->registry = wl_display_get_registry(data->display);
- if (data->registry == NULL) {
- return SDL_SetError("Failed to get the Wayland registry");
- }
- wl_registry_add_listener(data->registry, ®istry_listener, data);
- // First roundtrip to receive all registry objects.
- WAYLAND_wl_display_roundtrip(data->display);
- /* Now that we have all the protocols, load libdecor if applicable */
- Wayland_LoadLibdecor(data, SDL_FALSE);
- // Second roundtrip to receive all output events.
- WAYLAND_wl_display_roundtrip(data->display);
- Wayland_InitMouse();
- /* Get the surface class name, usually the name of the application */
- data->classname = get_classname();
- WAYLAND_wl_display_flush(data->display);
- Wayland_InitKeyboard(_this);
- Wayland_InitWin(data);
- data->initializing = SDL_FALSE;
- return 0;
- }
- static int Wayland_GetDisplayBounds(_THIS, SDL_VideoDisplay *display, SDL_Rect *rect)
- {
- SDL_WaylandOutputData *driverdata = (SDL_WaylandOutputData *)display->driverdata;
- rect->x = driverdata->x;
- rect->y = driverdata->y;
- rect->w = display->current_mode.w;
- rect->h = display->current_mode.h;
- return 0;
- }
- static int Wayland_GetDisplayDPI(_THIS, SDL_VideoDisplay *sdl_display, float *ddpi, float *hdpi, float *vdpi)
- {
- SDL_WaylandOutputData *driverdata = (SDL_WaylandOutputData *)sdl_display->driverdata;
- if (ddpi) {
- *ddpi = driverdata->ddpi;
- }
- if (hdpi) {
- *hdpi = driverdata->hdpi;
- }
- if (vdpi) {
- *vdpi = driverdata->vdpi;
- }
- return driverdata->ddpi != 0.0f ? 0 : SDL_SetError("Couldn't get DPI");
- }
- static void Wayland_VideoCleanup(_THIS)
- {
- SDL_VideoData *data = _this->driverdata;
- int i, j;
- Wayland_QuitWin(data);
- Wayland_FiniMouse(data);
- for (i = _this->num_displays - 1; i >= 0; --i) {
- SDL_VideoDisplay *display = &_this->displays[i];
- if (((SDL_WaylandOutputData *)display->driverdata)->xdg_output) {
- zxdg_output_v1_destroy(((SDL_WaylandOutputData *)display->driverdata)->xdg_output);
- }
- wl_output_destroy(((SDL_WaylandOutputData *)display->driverdata)->output);
- SDL_free(display->driverdata);
- display->driverdata = NULL;
- for (j = display->num_display_modes; j--;) {
- display->display_modes[j].driverdata = NULL;
- }
- display->desktop_mode.driverdata = NULL;
- SDL_DelVideoDisplay(i);
- }
- data->output_list = NULL;
- Wayland_display_destroy_input(data);
- Wayland_display_destroy_pointer_constraints(data);
- Wayland_display_destroy_relative_pointer_manager(data);
- if (data->activation_manager) {
- xdg_activation_v1_destroy(data->activation_manager);
- data->activation_manager = NULL;
- }
- if (data->idle_inhibit_manager) {
- zwp_idle_inhibit_manager_v1_destroy(data->idle_inhibit_manager);
- data->idle_inhibit_manager = NULL;
- }
- if (data->key_inhibitor_manager) {
- zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(data->key_inhibitor_manager);
- data->key_inhibitor_manager = NULL;
- }
- Wayland_QuitKeyboard(_this);
- if (data->text_input_manager) {
- zwp_text_input_manager_v3_destroy(data->text_input_manager);
- data->text_input_manager = NULL;
- }
- if (data->xkb_context) {
- WAYLAND_xkb_context_unref(data->xkb_context);
- data->xkb_context = NULL;
- }
- #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
- if (data->windowmanager) {
- qt_windowmanager_destroy(data->windowmanager);
- data->windowmanager = NULL;
- }
- if (data->surface_extension) {
- qt_surface_extension_destroy(data->surface_extension);
- data->surface_extension = NULL;
- }
- Wayland_touch_destroy(data);
- #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
- if (data->tablet_manager) {
- zwp_tablet_manager_v2_destroy((struct zwp_tablet_manager_v2 *)data->tablet_manager);
- data->tablet_manager = NULL;
- }
- if (data->data_device_manager) {
- wl_data_device_manager_destroy(data->data_device_manager);
- data->data_device_manager = NULL;
- }
- if (data->shm) {
- wl_shm_destroy(data->shm);
- data->shm = NULL;
- }
- if (data->shell.xdg) {
- xdg_wm_base_destroy(data->shell.xdg);
- data->shell.xdg = NULL;
- }
- if (data->decoration_manager) {
- zxdg_decoration_manager_v1_destroy(data->decoration_manager);
- data->decoration_manager = NULL;
- }
- if (data->xdg_output_manager) {
- zxdg_output_manager_v1_destroy(data->xdg_output_manager);
- data->xdg_output_manager = NULL;
- }
- if (data->viewporter) {
- wp_viewporter_destroy(data->viewporter);
- data->viewporter = NULL;
- }
- if (data->primary_selection_device_manager) {
- zwp_primary_selection_device_manager_v1_destroy(data->primary_selection_device_manager);
- data->primary_selection_device_manager = NULL;
- }
- if (data->fractional_scale_manager) {
- wp_fractional_scale_manager_v1_destroy(data->fractional_scale_manager);
- data->fractional_scale_manager = NULL;
- }
- if (data->input_timestamps_manager) {
- zwp_input_timestamps_manager_v1_destroy(data->input_timestamps_manager);
- data->input_timestamps_manager = NULL;
- }
- if (data->compositor) {
- wl_compositor_destroy(data->compositor);
- data->compositor = NULL;
- }
- if (data->registry) {
- wl_registry_destroy(data->registry);
- data->registry = NULL;
- }
- }
- SDL_bool Wayland_VideoReconnect(_THIS)
- {
- #if 0 /* TODO RECONNECT: Uncomment all when https://invent.kde.org/plasma/kwin/-/wikis/Restarting is completed */
- SDL_VideoData *data = _this->driverdata;
- SDL_Window *window = NULL;
- SDL_GLContext current_ctx = SDL_GL_GetCurrentContext();
- SDL_Window *current_window = SDL_GL_GetCurrentWindow();
- SDL_GL_MakeCurrent(NULL, NULL);
- Wayland_VideoCleanup(_this);
- SDL_ResetKeyboard();
- SDL_ResetMouse();
- if (WAYLAND_wl_display_reconnect(data->display) < 0) {
- return SDL_FALSE;
- }
- Wayland_VideoInit(_this);
- window = _this->windows;
- while (window) {
- /* We're going to cheat _just_ for a second and strip the OpenGL flag.
- * The Wayland driver actually forces it in CreateWindow, and
- * RecreateWindow does a bunch of unloading/loading of libGL, so just
- * strip the flag so RecreateWindow doesn't mess with the GL context,
- * and CreateWindow will add it right back!
- * -flibit
- */
- window->flags &= ~SDL_WINDOW_OPENGL;
- SDL_RecreateWindow(window, window->flags);
- window = window->next;
- }
- Wayland_RecreateCursors();
- if (current_window && current_ctx) {
- SDL_GL_MakeCurrent (current_window, current_ctx);
- }
- return SDL_TRUE;
- #else
- return SDL_FALSE;
- #endif /* 0 */
- }
- void Wayland_VideoQuit(_THIS)
- {
- SDL_VideoData *data = _this->driverdata;
- Wayland_VideoCleanup(_this);
- #ifdef HAVE_LIBDECOR_H
- if (data->shell.libdecor) {
- libdecor_unref(data->shell.libdecor);
- data->shell.libdecor = NULL;
- }
- #endif
- SDL_free(data->classname);
- }
- #endif /* SDL_VIDEO_DRIVER_WAYLAND */
- /* vi: set ts=4 sw=4 expandtab: */
|