| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896 |
- /*
- 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_WAYLAND
- #include "../SDL_sysvideo.h"
- #include "../../events/SDL_events_c.h"
- #include "../../core/unix/SDL_appid.h"
- #include "../SDL_egl_c.h"
- #include "SDL_waylandevents_c.h"
- #include "SDL_waylandwindow.h"
- #include "SDL_waylandvideo.h"
- #include "../../SDL_hints_c.h"
- #include "alpha-modifier-v1-client-protocol.h"
- #include "xdg-shell-client-protocol.h"
- #include "xdg-decoration-unstable-v1-client-protocol.h"
- #include "idle-inhibit-unstable-v1-client-protocol.h"
- #include "xdg-activation-v1-client-protocol.h"
- #include "viewporter-client-protocol.h"
- #include "fractional-scale-v1-client-protocol.h"
- #include "xdg-foreign-unstable-v2-client-protocol.h"
- #include "xdg-dialog-v1-client-protocol.h"
- #include "frog-color-management-v1-client-protocol.h"
- #include "xdg-toplevel-icon-v1-client-protocol.h"
- #ifdef HAVE_LIBDECOR_H
- #include <libdecor.h>
- #endif
- static double GetWindowScale(SDL_Window *window)
- {
- return (window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) || window->internal->scale_to_display ? window->internal->scale_factor : 1.0;
- }
- // These are point->pixel->point round trip safe; the inverse is not round trip safe due to rounding.
- static int PointToPixel(SDL_Window *window, int point)
- {
- /* Rounds halfway away from zero as per the Wayland fractional scaling protocol spec.
- * Wayland scale units are in units of 1/120, so the offset is required to correct for
- * rounding errors when using certain scale values.
- */
- return SDL_max((int)SDL_lround((double)point * GetWindowScale(window) + 1e-6), 1);
- }
- static int PixelToPoint(SDL_Window *window, int pixel)
- {
- return SDL_max((int)SDL_lround((double)pixel / GetWindowScale(window)), 1);
- }
- /* According to the Wayland spec:
- *
- * "If the [fullscreen] surface doesn't cover the whole output, the compositor will
- * position the surface in the center of the output and compensate with border fill
- * covering the rest of the output. The content of the border fill is undefined, but
- * should be assumed to be in some way that attempts to blend into the surrounding area
- * (e.g. solid black)."
- *
- * - KDE, as of 5.27, still doesn't do this
- * - GNOME prior to 43 didn't do this (older versions are still found in many LTS distros)
- *
- * Default to 'stretch' for now, until things have moved forward enough that the default
- * can be changed to 'aspect'.
- */
- enum WaylandModeScale
- {
- WAYLAND_MODE_SCALE_UNDEFINED,
- WAYLAND_MODE_SCALE_ASPECT,
- WAYLAND_MODE_SCALE_STRETCH,
- WAYLAND_MODE_SCALE_NONE
- };
- static enum WaylandModeScale GetModeScaleMethod(void)
- {
- static enum WaylandModeScale scale_mode = WAYLAND_MODE_SCALE_UNDEFINED;
- if (scale_mode == WAYLAND_MODE_SCALE_UNDEFINED) {
- const char *scale_hint = SDL_GetHint(SDL_HINT_VIDEO_WAYLAND_MODE_SCALING);
- if (scale_hint) {
- if (!SDL_strcasecmp(scale_hint, "aspect")) {
- scale_mode = WAYLAND_MODE_SCALE_ASPECT;
- } else if (!SDL_strcasecmp(scale_hint, "none")) {
- scale_mode = WAYLAND_MODE_SCALE_NONE;
- } else {
- scale_mode = WAYLAND_MODE_SCALE_STRETCH;
- }
- } else {
- scale_mode = WAYLAND_MODE_SCALE_STRETCH;
- }
- }
- return scale_mode;
- }
- static void GetBufferSize(SDL_Window *window, int *width, int *height)
- {
- SDL_WindowData *data = window->internal;
- int buf_width;
- int buf_height;
- // Exclusive fullscreen modes always have a pixel density of 1
- if (data->is_fullscreen && window->fullscreen_exclusive) {
- buf_width = window->current_fullscreen_mode.w;
- buf_height = window->current_fullscreen_mode.h;
- } else if (!data->scale_to_display) {
- // Round fractional backbuffer sizes halfway away from zero.
- buf_width = PointToPixel(window, data->requested.logical_width);
- buf_height = PointToPixel(window, data->requested.logical_height);
- } else {
- buf_width = data->requested.pixel_width;
- buf_height = data->requested.pixel_height;
- }
- if (width) {
- *width = buf_width;
- }
- if (height) {
- *height = buf_height;
- }
- }
- static void SetMinMaxDimensions(SDL_Window *window)
- {
- SDL_WindowData *wind = window->internal;
- int min_width, min_height, max_width, max_height;
- if ((window->flags & SDL_WINDOW_FULLSCREEN) || wind->fullscreen_deadline_count) {
- min_width = 0;
- min_height = 0;
- max_width = 0;
- max_height = 0;
- } else if (window->flags & SDL_WINDOW_RESIZABLE) {
- int adj_w = SDL_max(window->min_w, wind->system_limits.min_width);
- int adj_h = SDL_max(window->min_h, wind->system_limits.min_height);
- if (wind->scale_to_display) {
- adj_w = PixelToPoint(window, adj_w);
- adj_h = PixelToPoint(window, adj_h);
- }
- min_width = adj_w;
- min_height = adj_h;
- adj_w = window->max_w ? SDL_max(window->max_w, wind->system_limits.min_width) : 0;
- adj_h = window->max_h ? SDL_max(window->max_h, wind->system_limits.min_height) : 0;
- if (wind->scale_to_display) {
- adj_w = PixelToPoint(window, adj_w);
- adj_h = PixelToPoint(window, adj_h);
- }
- max_width = adj_w;
- max_height = adj_h;
- } else {
- min_width = wind->current.logical_width;
- min_height = wind->current.logical_height;
- max_width = wind->current.logical_width;
- max_height = wind->current.logical_height;
- }
- #ifdef HAVE_LIBDECOR_H
- if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
- if (!wind->shell_surface.libdecor.initial_configure_seen || !wind->shell_surface.libdecor.frame) {
- return; // Can't do anything yet, wait for ShowWindow
- }
- /* No need to change these values if the window is non-resizable,
- * as libdecor will just overwrite them internally.
- */
- if (libdecor_frame_has_capability(wind->shell_surface.libdecor.frame, LIBDECOR_ACTION_RESIZE)) {
- libdecor_frame_set_min_content_size(wind->shell_surface.libdecor.frame,
- min_width,
- min_height);
- libdecor_frame_set_max_content_size(wind->shell_surface.libdecor.frame,
- max_width,
- max_height);
- }
- } else
- #endif
- if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL) {
- if (wind->shell_surface.xdg.roleobj.toplevel == NULL) {
- return; // Can't do anything yet, wait for ShowWindow
- }
- xdg_toplevel_set_min_size(wind->shell_surface.xdg.roleobj.toplevel,
- min_width,
- min_height);
- xdg_toplevel_set_max_size(wind->shell_surface.xdg.roleobj.toplevel,
- max_width,
- max_height);
- }
- }
- static void EnsurePopupPositionIsValid(SDL_Window *window, int *x, int *y)
- {
- int adj_count = 0;
- /* Per the xdg-positioner spec, child popup windows must intersect or at
- * least be partially adjacent to the parent window.
- *
- * Failure to ensure this on a compositor that enforces this restriction
- * can result in behavior ranging from the window being spuriously closed
- * to a protocol violation.
- */
- if (*x + window->w < 0) {
- *x = -window->w;
- ++adj_count;
- }
- if (*y + window->h < 0) {
- *y = -window->h;
- ++adj_count;
- }
- if (*x > window->parent->w) {
- *x = window->parent->w;
- ++adj_count;
- }
- if (*y > window->parent->h) {
- *y = window->parent->h;
- ++adj_count;
- }
- /* If adjustment was required on the x and y axes, the popup is aligned with
- * the parent corner-to-corner and is neither overlapping nor adjacent, so it
- * must be nudged by 1 to be considered adjacent.
- */
- if (adj_count > 1) {
- *x += *x < 0 ? 1 : -1;
- }
- }
- static void AdjustPopupOffset(SDL_Window *popup, int *x, int *y)
- {
- // Adjust the popup positioning, if necessary
- #ifdef HAVE_LIBDECOR_H
- if (popup->parent->internal->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
- int adj_x, adj_y;
- libdecor_frame_translate_coordinate(popup->parent->internal->shell_surface.libdecor.frame,
- *x, *y, &adj_x, &adj_y);
- *x = adj_x;
- *y = adj_y;
- }
- #endif
- }
- static void RepositionPopup(SDL_Window *window, bool use_current_position)
- {
- SDL_WindowData *wind = window->internal;
- if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP &&
- wind->shell_surface.xdg.roleobj.popup.positioner &&
- xdg_popup_get_version(wind->shell_surface.xdg.roleobj.popup.popup) >= XDG_POPUP_REPOSITION_SINCE_VERSION) {
- int x = use_current_position ? window->x : window->floating.x;
- int y = use_current_position ? window->y : window->floating.y;
- EnsurePopupPositionIsValid(window, &x, &y);
- if (wind->scale_to_display) {
- x = PixelToPoint(window->parent, x);
- y = PixelToPoint(window->parent, y);
- }
- AdjustPopupOffset(window, &x, &y);
- xdg_positioner_set_anchor_rect(wind->shell_surface.xdg.roleobj.popup.positioner, 0, 0, window->parent->internal->current.logical_width, window->parent->internal->current.logical_height);
- xdg_positioner_set_size(wind->shell_surface.xdg.roleobj.popup.positioner, wind->current.logical_width, wind->current.logical_height);
- xdg_positioner_set_offset(wind->shell_surface.xdg.roleobj.popup.positioner, x, y);
- xdg_popup_reposition(wind->shell_surface.xdg.roleobj.popup.popup,
- wind->shell_surface.xdg.roleobj.popup.positioner,
- 0);
- }
- }
- static void SetSurfaceOpaqueRegion(SDL_WindowData *wind, bool is_opaque)
- {
- SDL_VideoData *viddata = wind->waylandData;
- if (is_opaque) {
- struct wl_region *region = wl_compositor_create_region(viddata->compositor);
- wl_region_add(region, 0, 0,
- wind->current.logical_width, wind->current.logical_height);
- wl_surface_set_opaque_region(wind->surface, region);
- wl_region_destroy(region);
- } else {
- wl_surface_set_opaque_region(wind->surface, NULL);
- }
- }
- static void ConfigureWindowGeometry(SDL_Window *window)
- {
- SDL_WindowData *data = window->internal;
- const double scale_factor = GetWindowScale(window);
- const int old_pixel_width = data->current.pixel_width;
- const int old_pixel_height = data->current.pixel_height;
- int window_width, window_height;
- bool window_size_changed;
- // Set the drawable backbuffer size.
- GetBufferSize(window, &data->current.pixel_width, &data->current.pixel_height);
- const bool buffer_size_changed = data->current.pixel_width != old_pixel_width ||
- data->current.pixel_height != old_pixel_height;
- if (data->egl_window && buffer_size_changed) {
- WAYLAND_wl_egl_window_resize(data->egl_window,
- data->current.pixel_width,
- data->current.pixel_height,
- 0, 0);
- }
- if (data->is_fullscreen && window->fullscreen_exclusive) {
- int output_width;
- int output_height;
- window_width = window->current_fullscreen_mode.w;
- window_height = window->current_fullscreen_mode.h;
- output_width = data->requested.logical_width;
- output_height = data->requested.logical_height;
- switch (GetModeScaleMethod()) {
- case WAYLAND_MODE_SCALE_NONE:
- /* The Wayland spec states that the advertised fullscreen dimensions are a maximum.
- * Windows can request a smaller size, but exceeding these dimensions is a protocol violation,
- * thus, modes that exceed the output size still need to be scaled with a viewport.
- */
- if (window_width <= output_width && window_height <= output_height) {
- output_width = window_width;
- output_height = window_height;
- break;
- }
- SDL_FALLTHROUGH;
- case WAYLAND_MODE_SCALE_ASPECT:
- {
- const float output_ratio = (float)output_width / (float)output_height;
- const float mode_ratio = (float)window_width / (float)window_height;
- if (output_ratio > mode_ratio) {
- output_width = SDL_lroundf((float)window_width * ((float)output_height / (float)window_height));
- } else if (output_ratio < mode_ratio) {
- output_height = SDL_lroundf((float)window_height * ((float)output_width / (float)window_width));
- }
- } break;
- default:
- break;
- }
- window_size_changed = window_width != window->w || window_height != window->h ||
- data->current.logical_width != output_width || data->current.logical_height != output_height;
- if (window_size_changed || buffer_size_changed) {
- if (data->viewport) {
- wp_viewport_set_destination(data->viewport, output_width, output_height);
- data->current.logical_width = output_width;
- data->current.logical_height = output_height;
- } else {
- // Calculate the integer scale from the mode and output.
- const int32_t int_scale = SDL_max(window->current_fullscreen_mode.w / output_width, 1);
- wl_surface_set_buffer_scale(data->surface, int_scale);
- data->current.logical_width = window->current_fullscreen_mode.w;
- data->current.logical_height = window->current_fullscreen_mode.h;
- }
- data->pointer_scale.x = (double)window_width / (double)data->current.logical_width;
- data->pointer_scale.y = (double)window_height / (double)data->current.logical_height;
- }
- } else {
- window_width = data->requested.logical_width;
- window_height = data->requested.logical_height;
- window_size_changed = window_width != data->current.logical_width || window_height != data->current.logical_height;
- if (window_size_changed || buffer_size_changed) {
- if (data->viewport) {
- wp_viewport_set_destination(data->viewport, window_width, window_height);
- } else if (window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) {
- // Don't change this if the DPI awareness flag is unset, as an application may have set this manually on a custom or external surface.
- wl_surface_set_buffer_scale(data->surface, (int32_t)scale_factor);
- }
- // Clamp the physical window size to the system minimum required size.
- data->current.logical_width = SDL_max(window_width, data->system_limits.min_width);
- data->current.logical_height = SDL_max(window_height, data->system_limits.min_height);
- if (!data->scale_to_display) {
- data->pointer_scale.x = 1.0;
- data->pointer_scale.y = 1.0;
- } else {
- data->pointer_scale.x = scale_factor;
- data->pointer_scale.y = scale_factor;
- }
- }
- }
- /*
- * The surface geometry, opaque region and pointer confinement region only
- * need to be recalculated if the output size has changed.
- */
- if (window_size_changed) {
- /* XXX: This is a hack and only set on the xdg-toplevel path when viewports
- * aren't supported to avoid a potential protocol violation if a buffer
- * with an old size is committed.
- */
- if (!data->viewport && data->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL && data->shell_surface.xdg.surface) {
- xdg_surface_set_window_geometry(data->shell_surface.xdg.surface, 0, 0, data->current.logical_width, data->current.logical_height);
- }
- SetSurfaceOpaqueRegion(data, !(window->flags & SDL_WINDOW_TRANSPARENT) && window->opacity == 1.0f);
- // Ensure that child popup windows are still in bounds.
- for (SDL_Window *child = window->first_child; child; child = child->next_sibling) {
- RepositionPopup(child, true);
- }
- }
- /* Update the min/max dimensions, primarily if the state was changed, and for non-resizable
- * xdg-toplevel windows where the limits should match the window size.
- */
- SetMinMaxDimensions(window);
- // Unconditionally send the window and drawable size, the video core will deduplicate when required.
- if (!data->scale_to_display) {
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window_width, window_height);
- } else {
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, data->current.pixel_width, data->current.pixel_height);
- }
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED, data->current.pixel_width, data->current.pixel_height);
- /* Send an exposure event if the window is in the shown state and the size has changed,
- * even if the window is occluded, as the client needs to commit a new frame for the
- * changes to take effect.
- *
- * The occlusion state is immediately set again afterward, if necessary.
- */
- if (data->surface_status == WAYLAND_SURFACE_STATUS_SHOWN) {
- if ((buffer_size_changed || window_size_changed) ||
- (!data->suspended && (window->flags & SDL_WINDOW_OCCLUDED))) {
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_EXPOSED, 0, 0);
- }
- if (data->suspended) {
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_OCCLUDED, 0, 0);
- }
- }
- }
- static void CommitLibdecorFrame(SDL_Window *window)
- {
- #ifdef HAVE_LIBDECOR_H
- SDL_WindowData *wind = window->internal;
- if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR && wind->shell_surface.libdecor.frame) {
- struct libdecor_state *state = libdecor_state_new(wind->current.logical_width, wind->current.logical_height);
- libdecor_frame_commit(wind->shell_surface.libdecor.frame, state, NULL);
- libdecor_state_free(state);
- }
- #endif
- }
- static void fullscreen_deadline_handler(void *data, struct wl_callback *callback, uint32_t callback_data)
- {
- // Get the window from the ID as it may have been destroyed
- SDL_WindowID windowID = (SDL_WindowID)((uintptr_t)data);
- SDL_Window *window = SDL_GetWindowFromID(windowID);
- if (window && window->internal) {
- window->internal->fullscreen_deadline_count--;
- }
- wl_callback_destroy(callback);
- }
- static struct wl_callback_listener fullscreen_deadline_listener = {
- fullscreen_deadline_handler
- };
- static void maximized_deadline_handler(void *data, struct wl_callback *callback, uint32_t callback_data)
- {
- // Get the window from the ID as it may have been destroyed
- SDL_WindowID windowID = (SDL_WindowID)((uintptr_t)data);
- SDL_Window *window = SDL_GetWindowFromID(windowID);
- if (window && window->internal) {
- window->internal->maximized_deadline_count--;
- }
- wl_callback_destroy(callback);
- }
- static struct wl_callback_listener maximized_deadline_listener = {
- maximized_deadline_handler
- };
- static void FlushPendingEvents(SDL_Window *window)
- {
- while (window->internal->fullscreen_deadline_count || window->internal->maximized_deadline_count) {
- WAYLAND_wl_display_roundtrip(window->internal->waylandData->display);
- }
- }
- /* While we can't get window position from the compositor, we do at least know
- * what monitor we're on, so let's send move events that put the window at the
- * center of the whatever display the wl_surface_listener events give us.
- */
- static void Wayland_move_window(SDL_Window *window)
- {
- SDL_WindowData *wind = window->internal;
- SDL_DisplayData *display;
- SDL_DisplayID *displays;
- if (wind->outputs && wind->num_outputs) {
- display = wind->outputs[wind->num_outputs - 1];
- } else {
- // A window may not be on any displays if minimized.
- return;
- }
- displays = SDL_GetDisplays(NULL);
- if (displays) {
- for (int i = 0; displays[i]; ++i) {
- if (SDL_GetDisplayDriverData(displays[i]) == display) {
- /* We want to send a very very specific combination here:
- *
- * 1. A coordinate that tells the application what display we're on
- * 2. Exactly (0, 0)
- *
- * Part 1 is useful information but is also really important for
- * ensuring we end up on the right display for fullscreen, while
- * part 2 is important because numerous applications use a specific
- * combination of GetWindowPosition and GetGlobalMouseState, and of
- * course neither are supported by Wayland. Since global mouse will
- * fall back to just GetMouseState, we need the window position to
- * be zero so the cursor math works without it going off in some
- * random direction. See UE5 Editor for a notable example of this!
- *
- * This may be an issue some day if we're ever able to implement
- * SDL_GetDisplayUsableBounds!
- *
- * -flibit
- */
- if (wind->last_displayID != displays[i]) {
- wind->last_displayID = displays[i];
- if (wind->shell_surface_type != WAYLAND_SURFACE_XDG_POPUP) {
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, display->x, display->y);
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_DISPLAY_CHANGED, wind->last_displayID, 0);
- }
- }
- break;
- }
- }
- SDL_free(displays);
- }
- }
- static void SetFullscreen(SDL_Window *window, struct wl_output *output)
- {
- SDL_WindowData *wind = window->internal;
- SDL_VideoData *viddata = wind->waylandData;
- #ifdef HAVE_LIBDECOR_H
- if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
- if (!wind->shell_surface.libdecor.frame) {
- return; // Can't do anything yet, wait for ShowWindow
- }
- wind->fullscreen_exclusive = output ? window->fullscreen_exclusive : false;
- ++wind->fullscreen_deadline_count;
- if (output) {
- Wayland_SetWindowResizable(SDL_GetVideoDevice(), window, true);
- wl_surface_commit(wind->surface);
- libdecor_frame_set_fullscreen(wind->shell_surface.libdecor.frame, output);
- } else {
- libdecor_frame_unset_fullscreen(wind->shell_surface.libdecor.frame);
- }
- } else
- #endif
- if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL) {
- if (wind->shell_surface.xdg.roleobj.toplevel == NULL) {
- return; // Can't do anything yet, wait for ShowWindow
- }
- wind->fullscreen_exclusive = output ? window->fullscreen_exclusive : false;
- ++wind->fullscreen_deadline_count;
- if (output) {
- Wayland_SetWindowResizable(SDL_GetVideoDevice(), window, true);
- wl_surface_commit(wind->surface);
- xdg_toplevel_set_fullscreen(wind->shell_surface.xdg.roleobj.toplevel, output);
- } else {
- xdg_toplevel_unset_fullscreen(wind->shell_surface.xdg.roleobj.toplevel);
- }
- }
- // Queue a deadline event
- struct wl_callback *cb = wl_display_sync(viddata->display);
- wl_callback_add_listener(cb, &fullscreen_deadline_listener, (void *)((uintptr_t)window->id));
- }
- static void UpdateWindowFullscreen(SDL_Window *window, bool fullscreen)
- {
- SDL_WindowData *wind = window->internal;
- wind->is_fullscreen = fullscreen;
- if (fullscreen) {
- if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
- SDL_copyp(&window->current_fullscreen_mode, &window->requested_fullscreen_mode);
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0);
- SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_ENTER, false);
- /* Set the output for exclusive fullscreen windows when entering fullscreen from a
- * compositor event, or if the fullscreen parameters were changed between the initial
- * fullscreen request and now, to ensure that the window is on the correct output,
- * as requested by the client.
- */
- if (window->fullscreen_exclusive && (!wind->fullscreen_exclusive || !wind->fullscreen_was_positioned)) {
- SDL_VideoDisplay *disp = SDL_GetVideoDisplay(window->current_fullscreen_mode.displayID);
- if (disp) {
- wind->fullscreen_was_positioned = true;
- SetFullscreen(window, disp->internal->output);
- }
- }
- }
- } else {
- // Don't change the fullscreen flags if the window is hidden or being hidden.
- if ((window->flags & SDL_WINDOW_FULLSCREEN) && !window->is_hiding && !(window->flags & SDL_WINDOW_HIDDEN)) {
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0);
- SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_LEAVE, false);
- wind->fullscreen_was_positioned = false;
- /* Send a move event, in case it was deferred while the fullscreen window was moving and
- * on multiple outputs.
- */
- Wayland_move_window(window);
- }
- }
- }
- static const struct wl_callback_listener surface_frame_listener;
- static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time)
- {
- SDL_WindowData *wind = (SDL_WindowData *)data;
- /* XXX: This is needed to work around an Nvidia egl-wayland bug due to buffer coordinates
- * being used with wl_surface_damage, which causes part of the output to not be
- * updated when using a viewport with an output region larger than the source region.
- */
- if (wl_compositor_get_version(wind->waylandData->compositor) >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) {
- wl_surface_damage_buffer(wind->surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32);
- } else {
- wl_surface_damage(wind->surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32);
- }
- if (wind->surface_status == WAYLAND_SURFACE_STATUS_WAITING_FOR_FRAME) {
- wind->surface_status = WAYLAND_SURFACE_STATUS_SHOWN;
- // If any child windows are waiting on this window to be shown, show them now
- for (SDL_Window *w = wind->sdlwindow->first_child; w; w = w->next_sibling) {
- if (w->internal->surface_status == WAYLAND_SURFACE_STATUS_SHOW_PENDING) {
- Wayland_ShowWindow(SDL_GetVideoDevice(), w);
- } else if (w->internal->reparenting_required) {
- Wayland_SetWindowParent(SDL_GetVideoDevice(), w, w->parent);
- if (w->flags & SDL_WINDOW_MODAL) {
- Wayland_SetWindowModal(SDL_GetVideoDevice(), w, true);
- }
- }
- }
- /* If the window was initially set to the suspended state, send the occluded event now,
- * as we don't want to mark the window as occluded until at least one frame has been submitted.
- */
- if (wind->suspended) {
- SDL_SendWindowEvent(wind->sdlwindow, SDL_EVENT_WINDOW_OCCLUDED, 0, 0);
- }
- }
- wl_callback_destroy(cb);
- wind->surface_frame_callback = wl_surface_frame(wind->surface);
- wl_callback_add_listener(wind->surface_frame_callback, &surface_frame_listener, data);
- }
- static const struct wl_callback_listener surface_frame_listener = {
- surface_frame_done
- };
- static const struct wl_callback_listener gles_swap_frame_listener;
- static void gles_swap_frame_done(void *data, struct wl_callback *cb, uint32_t time)
- {
- SDL_WindowData *wind = (SDL_WindowData *)data;
- SDL_SetAtomicInt(&wind->swap_interval_ready, 1); // mark window as ready to present again.
- // reset this callback to fire again once a new frame was presented and compositor wants the next one.
- wind->gles_swap_frame_callback = wl_surface_frame(wind->gles_swap_frame_surface_wrapper);
- wl_callback_destroy(cb);
- wl_callback_add_listener(wind->gles_swap_frame_callback, &gles_swap_frame_listener, data);
- }
- static const struct wl_callback_listener gles_swap_frame_listener = {
- gles_swap_frame_done
- };
- static void handle_configure_xdg_shell_surface(void *data, struct xdg_surface *xdg, uint32_t serial)
- {
- SDL_WindowData *wind = (SDL_WindowData *)data;
- SDL_Window *window = wind->sdlwindow;
- ConfigureWindowGeometry(window);
- xdg_surface_ack_configure(xdg, serial);
- wind->shell_surface.xdg.initial_configure_seen = true;
- }
- static const struct xdg_surface_listener shell_surface_listener_xdg = {
- handle_configure_xdg_shell_surface
- };
- static void handle_configure_xdg_toplevel(void *data,
- struct xdg_toplevel *xdg_toplevel,
- int32_t width,
- int32_t height,
- struct wl_array *states)
- {
- SDL_WindowData *wind = (SDL_WindowData *)data;
- SDL_Window *window = wind->sdlwindow;
- enum xdg_toplevel_state *state;
- bool fullscreen = false;
- bool maximized = false;
- bool floating = true;
- bool tiled = false;
- bool active = false;
- bool suspended = false;
- wl_array_for_each (state, states) {
- switch (*state) {
- case XDG_TOPLEVEL_STATE_FULLSCREEN:
- fullscreen = true;
- floating = false;
- break;
- case XDG_TOPLEVEL_STATE_MAXIMIZED:
- maximized = true;
- floating = false;
- break;
- case XDG_TOPLEVEL_STATE_ACTIVATED:
- active = true;
- break;
- case XDG_TOPLEVEL_STATE_TILED_LEFT:
- case XDG_TOPLEVEL_STATE_TILED_RIGHT:
- case XDG_TOPLEVEL_STATE_TILED_TOP:
- case XDG_TOPLEVEL_STATE_TILED_BOTTOM:
- tiled = true;
- floating = false;
- break;
- case XDG_TOPLEVEL_STATE_SUSPENDED:
- suspended = true;
- break;
- default:
- break;
- }
- }
- UpdateWindowFullscreen(window, fullscreen);
- /* Always send a maximized/restore event; if the event is redundant it will
- * automatically be discarded (see src/events/SDL_windowevents.c)
- *
- * No, we do not get minimize events from xdg-shell, however, the minimized
- * state can be programmatically set. The meaning of 'minimized' is compositor
- * dependent, but in general, we can assume that the flag should remain set until
- * the next focused configure event occurs.
- */
- if (active || !(window->flags & SDL_WINDOW_MINIMIZED)) {
- if (window->flags & SDL_WINDOW_MINIMIZED) {
- // If we were minimized, send a restored event before possibly sending maximized.
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
- }
- SDL_SendWindowEvent(window,
- (maximized && !fullscreen) ? SDL_EVENT_WINDOW_MAXIMIZED : SDL_EVENT_WINDOW_RESTORED,
- 0, 0);
- }
- if (!fullscreen) {
- /* xdg_toplevel spec states that this is a suggestion.
- * Ignore if less than or greater than max/min size.
- */
- if (window->flags & SDL_WINDOW_RESIZABLE) {
- if ((floating && wind->pending_restored_size) ||
- width == 0 || height == 0) {
- /* This happens when we're being restored from a non-floating state
- * with a pending floating client size, or the compositor indicates
- * that the size is up to the client, so use the cached window size here.
- */
- if (floating) {
- wind->pending_restored_size = false;
- width = window->floating.w;
- height = window->floating.h;
- } else {
- width = window->windowed.w;
- height = window->windowed.h;
- }
- if (!wind->scale_to_display) {
- wind->requested.logical_width = width;
- wind->requested.logical_height = height;
- } else {
- wind->requested.pixel_width = width;
- wind->requested.pixel_height = height;
- width = wind->requested.logical_width = PixelToPoint(window, width);
- height = wind->requested.logical_height = PixelToPoint(window, height);
- }
- } else {
- /* Don't apply the supplied dimensions if they haven't changed from the last configuration
- * event, or a newer size set programmatically can be overwritten by old data.
- */
- if (width != wind->last_configure.width || height != wind->last_configure.height) {
- wind->requested.logical_width = width;
- wind->requested.logical_height = height;
- if (wind->scale_to_display) {
- wind->requested.pixel_width = PointToPixel(window, width);
- wind->requested.pixel_height = PointToPixel(window, height);
- }
- }
- }
- } else {
- /* If we're a fixed-size window, we know our size for sure.
- * Always assume the configure is wrong.
- */
- if (!wind->scale_to_display) {
- width = wind->requested.logical_width = window->floating.w;
- height = wind->requested.logical_height = window->floating.h;
- } else {
- wind->requested.pixel_width = window->floating.w;
- wind->requested.pixel_height = window->floating.h;
- width = wind->requested.logical_width = PixelToPoint(window, window->floating.w);
- height = wind->requested.logical_height = PixelToPoint(window, window->floating.h);
- }
- }
- /* Notes on the spec:
- *
- * - The content limits are only a hint, which the compositor is free to ignore,
- * so apply them manually when appropriate.
- *
- * - Maximized windows must have their exact dimensions respected, thus they must
- * not be resized, or a protocol violation can occur.
- *
- * - When resizing a window, the width/height are maximum values, so aspect ratio
- * correction can't resize beyond the existing dimensions, or a protocol violation
- * can occur. In practice, nothing seems to kill clients that do this, but doing
- * so causes GNOME to glitch out.
- */
- if (!maximized) {
- if (!wind->scale_to_display) {
- if (window->max_w > 0) {
- wind->requested.logical_width = SDL_min(wind->requested.logical_width, window->max_w);
- }
- wind->requested.logical_width = SDL_max(wind->requested.logical_width, window->min_w);
- if (window->max_h > 0) {
- wind->requested.logical_height = SDL_min(wind->requested.logical_height, window->max_h);
- }
- wind->requested.logical_height = SDL_max(wind->requested.logical_height, window->min_h);
- // Aspect correction.
- const float aspect = (float)wind->requested.logical_width / (float)wind->requested.logical_height;
- if (window->min_aspect != 0.f && aspect < window->min_aspect) {
- wind->requested.logical_height = SDL_lroundf((float)wind->requested.logical_width / window->min_aspect);
- } else if (window->max_aspect != 0.f && aspect > window->max_aspect) {
- wind->requested.logical_width = SDL_lroundf((float)wind->requested.logical_height * window->max_aspect);
- }
- } else {
- if (window->max_w > 0) {
- wind->requested.pixel_width = SDL_min(wind->requested.pixel_width, window->max_w);
- }
- wind->requested.pixel_width = SDL_max(wind->requested.pixel_width, window->min_w);
- if (window->max_h > 0) {
- wind->requested.pixel_height = SDL_min(wind->requested.pixel_height, window->max_h);
- }
- wind->requested.pixel_height = SDL_max(wind->requested.pixel_height, window->min_h);
- // Aspect correction.
- const float aspect = (float)wind->requested.pixel_width / (float)wind->requested.pixel_height;
- if (window->min_aspect != 0.f && aspect < window->min_aspect) {
- wind->requested.pixel_height = SDL_lroundf((float)wind->requested.pixel_width / window->min_aspect);
- } else if (window->max_aspect != 0.f && aspect > window->max_aspect) {
- wind->requested.pixel_width = SDL_lroundf((float)wind->requested.pixel_height * window->max_aspect);
- }
- wind->requested.logical_width = PixelToPoint(window, wind->requested.pixel_width);
- wind->requested.logical_height = PixelToPoint(window, wind->requested.pixel_height);
- }
- }
- } else {
- // Fullscreen windows know their exact size.
- if (width == 0 || height == 0) {
- width = wind->requested.logical_width;
- height = wind->requested.logical_height;
- } else {
- wind->requested.logical_width = width;
- wind->requested.logical_height = height;
- }
- if (wind->scale_to_display) {
- wind->requested.pixel_width = PointToPixel(window, width);
- wind->requested.pixel_height = PointToPixel(window, height);
- }
- }
- wind->last_configure.width = width;
- wind->last_configure.height = height;
- wind->floating = floating;
- wind->suspended = suspended;
- wind->active = active;
- window->tiled = tiled;
- if (wind->surface_status == WAYLAND_SURFACE_STATUS_WAITING_FOR_CONFIGURE) {
- wind->surface_status = WAYLAND_SURFACE_STATUS_WAITING_FOR_FRAME;
- }
- }
- static void handle_close_xdg_toplevel(void *data, struct xdg_toplevel *xdg_toplevel)
- {
- SDL_WindowData *window = (SDL_WindowData *)data;
- SDL_SendWindowEvent(window->sdlwindow, SDL_EVENT_WINDOW_CLOSE_REQUESTED, 0, 0);
- }
- static void handle_xdg_configure_toplevel_bounds(void *data,
- struct xdg_toplevel *xdg_toplevel,
- int32_t width, int32_t height)
- {
- // NOP
- }
- void handle_xdg_toplevel_wm_capabilities(void *data,
- struct xdg_toplevel *xdg_toplevel,
- struct wl_array *capabilities)
- {
- // NOP
- }
- static const struct xdg_toplevel_listener toplevel_listener_xdg = {
- handle_configure_xdg_toplevel,
- handle_close_xdg_toplevel,
- handle_xdg_configure_toplevel_bounds, // Version 4
- handle_xdg_toplevel_wm_capabilities // Version 5
- };
- static void handle_configure_xdg_popup(void *data,
- struct xdg_popup *xdg_popup,
- int32_t x,
- int32_t y,
- int32_t width,
- int32_t height)
- {
- SDL_WindowData *wind = (SDL_WindowData *)data;
- int offset_x = 0, offset_y = 0;
- // Adjust the position if it was offset for libdecor
- AdjustPopupOffset(wind->sdlwindow, &offset_x, &offset_y);
- x -= offset_x;
- y -= offset_y;
- wind->requested.logical_width = width;
- wind->requested.logical_height = height;
- if (wind->scale_to_display) {
- x = PointToPixel(wind->sdlwindow->parent, x);
- y = PointToPixel(wind->sdlwindow->parent, y);
- wind->requested.pixel_width = PointToPixel(wind->sdlwindow, width);
- wind->requested.pixel_height = PointToPixel(wind->sdlwindow, height);
- }
- SDL_SendWindowEvent(wind->sdlwindow, SDL_EVENT_WINDOW_MOVED, x, y);
- if (wind->surface_status == WAYLAND_SURFACE_STATUS_WAITING_FOR_CONFIGURE) {
- wind->surface_status = WAYLAND_SURFACE_STATUS_WAITING_FOR_FRAME;
- }
- }
- static void handle_done_xdg_popup(void *data, struct xdg_popup *xdg_popup)
- {
- SDL_WindowData *window = (SDL_WindowData *)data;
- SDL_SendWindowEvent(window->sdlwindow, SDL_EVENT_WINDOW_CLOSE_REQUESTED, 0, 0);
- }
- static void handle_repositioned_xdg_popup(void *data,
- struct xdg_popup *xdg_popup,
- uint32_t token)
- {
- // No-op, configure does all the work we care about
- }
- static const struct xdg_popup_listener popup_listener_xdg = {
- handle_configure_xdg_popup,
- handle_done_xdg_popup,
- handle_repositioned_xdg_popup
- };
- static void handle_configure_zxdg_decoration(void *data,
- struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1,
- uint32_t mode)
- {
- SDL_Window *window = (SDL_Window *)data;
- SDL_WindowData *internal = window->internal;
- SDL_VideoDevice *device = SDL_GetVideoDevice();
- /* If the compositor tries to force CSD anyway, bail on direct XDG support
- * and fall back to libdecor, it will handle these events from then on.
- *
- * To do this we have to fully unmap, then map with libdecor loaded.
- */
- if (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE) {
- if (window->flags & SDL_WINDOW_BORDERLESS) {
- // borderless windows do request CSD, so we got what we wanted
- return;
- }
- if (!Wayland_LoadLibdecor(internal->waylandData, true)) {
- // libdecor isn't available, so no borders for you... oh well
- return;
- }
- WAYLAND_wl_display_roundtrip(internal->waylandData->display);
- Wayland_HideWindow(device, window);
- SDL_zero(internal->shell_surface);
- internal->shell_surface_type = WAYLAND_SURFACE_LIBDECOR;
- Wayland_ShowWindow(device, window);
- }
- }
- static const struct zxdg_toplevel_decoration_v1_listener decoration_listener = {
- handle_configure_zxdg_decoration
- };
- #ifdef HAVE_LIBDECOR_H
- /*
- * XXX: Hack for older versions of libdecor that lack the function to query the
- * minimum content size limit. The internal limits must always be overridden
- * to ensure that very small windows don't cause errors or crashes.
- *
- * On libdecor >= 0.1.2, which exposes the function to get the minimum content
- * size limit, this function is a no-op.
- *
- * Can be removed if the minimum required version of libdecor is raised to
- * 0.1.2 or higher.
- */
- static void OverrideLibdecorLimits(SDL_Window *window)
- {
- #ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR
- if (!libdecor_frame_get_min_content_size) {
- libdecor_frame_set_min_content_size(window->internal->shell_surface.libdecor.frame, window->min_w, window->min_h);
- }
- #elif !SDL_LIBDECOR_CHECK_VERSION(0, 2, 0)
- libdecor_frame_set_min_content_size(window->internal->shell_surface.libdecor.frame, window->min_w, window->min_h);
- #endif
- }
- /*
- * NOTE: Retrieves the minimum content size limits, if the function for doing so is available.
- * On versions of libdecor that lack the minimum content size retrieval function, this
- * function is a no-op.
- *
- * Can be replaced with a direct call if the minimum required version of libdecor is raised
- * to 0.1.2 or higher.
- */
- static void LibdecorGetMinContentSize(struct libdecor_frame *frame, int *min_w, int *min_h)
- {
- #ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR
- if (libdecor_frame_get_min_content_size != NULL) {
- libdecor_frame_get_min_content_size(frame, min_w, min_h);
- }
- #elif SDL_LIBDECOR_CHECK_VERSION(0, 2, 0)
- libdecor_frame_get_min_content_size(frame, min_w, min_h);
- #endif
- }
- static void decoration_frame_configure(struct libdecor_frame *frame,
- struct libdecor_configuration *configuration,
- void *user_data)
- {
- SDL_WindowData *wind = (SDL_WindowData *)user_data;
- SDL_Window *window = wind->sdlwindow;
- struct libdecor_state *state;
- enum libdecor_window_state window_state;
- int width, height;
- bool prev_fullscreen = wind->is_fullscreen;
- bool active = false;
- bool fullscreen = false;
- bool maximized = false;
- bool tiled = false;
- bool suspended = false;
- bool floating;
- static const enum libdecor_window_state tiled_states = (LIBDECOR_WINDOW_STATE_TILED_LEFT | LIBDECOR_WINDOW_STATE_TILED_RIGHT |
- LIBDECOR_WINDOW_STATE_TILED_TOP | LIBDECOR_WINDOW_STATE_TILED_BOTTOM);
- // Window State
- if (libdecor_configuration_get_window_state(configuration, &window_state)) {
- fullscreen = (window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN) != 0;
- maximized = (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) != 0;
- active = (window_state & LIBDECOR_WINDOW_STATE_ACTIVE) != 0;
- tiled = (window_state & tiled_states) != 0;
- #if SDL_LIBDECOR_CHECK_VERSION(0, 2, 0)
- suspended = (window_state & LIBDECOR_WINDOW_STATE_SUSPENDED) != 0;
- #endif
- }
- floating = !(fullscreen || maximized || tiled);
- UpdateWindowFullscreen(window, fullscreen);
- /* Always send a maximized/restore event; if the event is redundant it will
- * automatically be discarded (see src/events/SDL_windowevents.c)
- *
- * No, we do not get minimize events from libdecor, however, the minimized
- * state can be programmatically set. The meaning of 'minimized' is compositor
- * dependent, but in general, we can assume that the flag should remain set until
- * the next focused configure event occurs.
- */
- if (active || !(window->flags & SDL_WINDOW_MINIMIZED)) {
- if (window->flags & SDL_WINDOW_MINIMIZED) {
- // If we were minimized, send a restored event before possibly sending maximized.
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
- }
- SDL_SendWindowEvent(window,
- (maximized && !fullscreen) ? SDL_EVENT_WINDOW_MAXIMIZED : SDL_EVENT_WINDOW_RESTORED,
- 0, 0);
- }
- /* For fullscreen or fixed-size windows we know our size.
- * Always assume the configure is wrong.
- */
- if (fullscreen) {
- if (!libdecor_configuration_get_content_size(configuration, frame, &width, &height)) {
- width = wind->requested.logical_width;
- height = wind->requested.logical_height;
- } else {
- // Fullscreen windows know their exact size.
- wind->requested.logical_width = width;
- wind->requested.logical_height = height;
- if (wind->scale_to_display) {
- wind->requested.pixel_width = PointToPixel(window, width);
- wind->requested.pixel_height = PointToPixel(window, height);
- }
- }
- } else {
- if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
- /* If we're a fixed-size window, we know our size for sure.
- * Always assume the configure is wrong.
- */
- if (!wind->scale_to_display) {
- width = wind->requested.logical_width = window->floating.w;
- height = wind->requested.logical_height = window->floating.h;
- } else {
- wind->requested.pixel_width = window->floating.w;
- wind->requested.pixel_height = window->floating.h;
- width = wind->requested.logical_width = PixelToPoint(window, window->floating.w);
- height = wind->requested.logical_height = PixelToPoint(window, window->floating.h);
- }
- OverrideLibdecorLimits(window);
- } else {
- /* XXX: The libdecor cairo plugin sends bogus content sizes that add the
- * height of the title bar when transitioning from a fixed-size to
- * floating state. Ignore the sent window dimensions in this case,
- * in favor of the cached value to avoid the window increasing in
- * size after every state transition.
- *
- * https://gitlab.freedesktop.org/libdecor/libdecor/-/issues/34
- */
- if ((floating && (wind->pending_restored_size || (!wind->floating && !(window->flags & SDL_WINDOW_BORDERLESS)))) ||
- !libdecor_configuration_get_content_size(configuration, frame, &width, &height)) {
- /* This happens when we're being restored from a non-floating state,
- * or the compositor indicates that the size is up to the client, so
- * used the cached window size here.
- */
- if (floating) {
- wind->pending_restored_size = false;
- width = window->floating.w;
- height = window->floating.h;
- } else {
- width = window->windowed.w;
- height = window->windowed.h;
- }
- if (!wind->scale_to_display) {
- wind->requested.logical_width = width;
- wind->requested.logical_height = height;
- } else {
- wind->requested.pixel_width = width;
- wind->requested.pixel_height = height;
- width = wind->requested.logical_width = PixelToPoint(window, width);
- height = wind->requested.logical_height = PixelToPoint(window, height);
- }
- } else {
- /* Don't apply the supplied dimensions if they haven't changed from the last configuration
- * event, or a newer size set programmatically can be overwritten by old data.
- */
- if (width != wind->last_configure.width || height != wind->last_configure.height) {
- wind->requested.logical_width = width;
- wind->requested.logical_height = height;
- if (wind->scale_to_display) {
- wind->requested.pixel_width = PointToPixel(window, width);
- wind->requested.pixel_height = PointToPixel(window, height);
- }
- }
- }
- }
- /* Notes on the spec:
- *
- * - The content limits are only a hint, which the compositor is free to ignore,
- * so apply them manually when appropriate.
- *
- * - Maximized windows must have their exact dimensions respected, thus they must
- * not be resized, or a protocol violation can occur.
- *
- * - When resizing a window, the width/height are maximum values, so aspect ratio
- * correction can't resize beyond the existing dimensions, or a protocol violation
- * can occur. In practice, nothing seems to kill clients that do this, but doing
- * so causes GNOME to glitch out.
- */
- if (!maximized) {
- if (!wind->scale_to_display) {
- if (window->max_w > 0) {
- wind->requested.logical_width = SDL_min(wind->requested.logical_width, window->max_w);
- }
- wind->requested.logical_width = SDL_max(wind->requested.logical_width, window->min_w);
- if (window->max_h > 0) {
- wind->requested.logical_height = SDL_min(wind->requested.logical_height, window->max_h);
- }
- wind->requested.logical_height = SDL_max(wind->requested.logical_height, window->min_h);
- // Aspect correction.
- const float aspect = (float)wind->requested.logical_width / (float)wind->requested.logical_height;
- if (window->min_aspect != 0.f && aspect < window->min_aspect) {
- wind->requested.logical_height = SDL_lroundf((float)wind->requested.logical_width / window->min_aspect);
- } else if (window->max_aspect != 0.f && aspect > window->max_aspect) {
- wind->requested.logical_width = SDL_lroundf((float)wind->requested.logical_height * window->max_aspect);
- }
- } else {
- if (window->max_w > 0) {
- wind->requested.pixel_width = SDL_min(wind->requested.pixel_width, window->max_w);
- }
- wind->requested.pixel_width = SDL_max(wind->requested.pixel_width, window->min_w);
- if (window->max_h > 0) {
- wind->requested.pixel_height = SDL_min(wind->requested.pixel_height, window->max_h);
- }
- wind->requested.pixel_height = SDL_max(wind->requested.pixel_height, window->min_h);
- // Aspect correction.
- const float aspect = (float)wind->requested.pixel_width / (float)wind->requested.pixel_height;
- if (window->min_aspect != 0.f && aspect < window->min_aspect) {
- wind->requested.pixel_height = SDL_lroundf((float)wind->requested.pixel_width / window->min_aspect);
- } else if (window->max_aspect != 0.f && aspect > window->max_aspect) {
- wind->requested.pixel_width = SDL_lroundf((float)wind->requested.pixel_height * window->max_aspect);
- }
- wind->requested.logical_width = PixelToPoint(window, wind->requested.pixel_width);
- wind->requested.logical_height = PixelToPoint(window, wind->requested.pixel_height);
- }
- }
- }
- // Store the new state.
- wind->last_configure.width = width;
- wind->last_configure.height = height;
- wind->floating = floating;
- wind->suspended = suspended;
- wind->active = active;
- window->tiled = tiled;
- // Calculate the new window geometry
- ConfigureWindowGeometry(window);
- // ... then commit the changes on the libdecor side.
- state = libdecor_state_new(wind->current.logical_width, wind->current.logical_height);
- libdecor_frame_commit(frame, state, configuration);
- libdecor_state_free(state);
- if (!wind->shell_surface.libdecor.initial_configure_seen) {
- LibdecorGetMinContentSize(frame, &wind->system_limits.min_width, &wind->system_limits.min_height);
- wind->shell_surface.libdecor.initial_configure_seen = true;
- }
- if (wind->surface_status == WAYLAND_SURFACE_STATUS_WAITING_FOR_CONFIGURE) {
- wind->surface_status = WAYLAND_SURFACE_STATUS_WAITING_FOR_FRAME;
- }
- /* Update the resize capability if this config event was the result of the
- * compositor taking a window out of fullscreen. Since this will change the
- * capabilities and commit a new frame state with the last known content
- * dimension, this has to be called after the new state has been committed
- * and the new content dimensions were updated.
- */
- if (prev_fullscreen && !wind->is_fullscreen) {
- Wayland_SetWindowResizable(SDL_GetVideoDevice(), window,
- !!(window->flags & SDL_WINDOW_RESIZABLE));
- }
- }
- static void decoration_frame_close(struct libdecor_frame *frame, void *user_data)
- {
- SDL_SendWindowEvent(((SDL_WindowData *)user_data)->sdlwindow, SDL_EVENT_WINDOW_CLOSE_REQUESTED, 0, 0);
- }
- static void decoration_frame_commit(struct libdecor_frame *frame, void *user_data)
- {
- /* libdecor decoration subsurfaces are synchronous, so the client needs to
- * commit a frame to trigger an update of the decoration surfaces.
- */
- SDL_WindowData *wind = (SDL_WindowData *)user_data;
- if (!wind->suspended && wind->surface_status == WAYLAND_SURFACE_STATUS_SHOWN) {
- SDL_SendWindowEvent(wind->sdlwindow, SDL_EVENT_WINDOW_EXPOSED, 0, 0);
- }
- }
- static struct libdecor_frame_interface libdecor_frame_interface = {
- decoration_frame_configure,
- decoration_frame_close,
- decoration_frame_commit,
- };
- #endif
- static void Wayland_HandlePreferredScaleChanged(SDL_WindowData *window_data, double factor)
- {
- const double old_factor = window_data->scale_factor;
- // Round the scale factor if viewports aren't available.
- if (!window_data->viewport) {
- factor = SDL_ceil(factor);
- }
- if (factor != old_factor) {
- window_data->scale_factor = factor;
- if (window_data->scale_to_display) {
- /* If the window is in the floating state with a user/application specified size, calculate the new
- * logical size from the backbuffer size. Otherwise, use the fixed underlying logical size to calculate
- * the new backbuffer dimensions.
- */
- if (window_data->floating) {
- window_data->requested.logical_width = PixelToPoint(window_data->sdlwindow, window_data->requested.pixel_width);
- window_data->requested.logical_height = PixelToPoint(window_data->sdlwindow, window_data->requested.pixel_height);
- } else {
- window_data->requested.pixel_width = PointToPixel(window_data->sdlwindow, window_data->requested.logical_width);
- window_data->requested.pixel_height = PointToPixel(window_data->sdlwindow, window_data->requested.logical_height);
- }
- }
- if (window_data->sdlwindow->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY || window_data->scale_to_display) {
- ConfigureWindowGeometry(window_data->sdlwindow);
- CommitLibdecorFrame(window_data->sdlwindow);
- }
- }
- }
- static void Wayland_MaybeUpdateScaleFactor(SDL_WindowData *window)
- {
- double factor;
- int i;
- /* If the fractional scale protocol is present or the core protocol supports the
- * preferred buffer scale event, the compositor will explicitly tell the application
- * what scale it wants via these events, so don't try to determine the scale factor
- * from which displays the surface has entered.
- */
- if (window->fractional_scale || wl_surface_get_version(window->surface) >= WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION) {
- return;
- }
- if (window->num_outputs != 0) {
- // Check every display's factor, use the highest
- factor = 0.0;
- for (i = 0; i < window->num_outputs; i++) {
- SDL_DisplayData *internal = window->outputs[i];
- factor = SDL_max(factor, internal->scale_factor);
- }
- } else {
- // All outputs removed, just fall back.
- factor = window->scale_factor;
- }
- Wayland_HandlePreferredScaleChanged(window, factor);
- }
- void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, SDL_DisplayData *display_data)
- {
- for (int i = 0; i < window->num_outputs; i++) {
- if (window->outputs[i] == display_data) { // remove this one
- if (i == (window->num_outputs - 1)) {
- window->outputs[i] = NULL;
- } else {
- SDL_memmove(&window->outputs[i],
- &window->outputs[i + 1],
- sizeof(SDL_DisplayData *) * ((window->num_outputs - i) - 1));
- }
- window->num_outputs--;
- i--;
- }
- }
- if (window->num_outputs == 0) {
- SDL_free(window->outputs);
- window->outputs = NULL;
- } else if (!window->is_fullscreen || window->num_outputs == 1) {
- Wayland_move_window(window->sdlwindow);
- Wayland_MaybeUpdateScaleFactor(window);
- }
- }
- static void handle_surface_enter(void *data, struct wl_surface *surface, struct wl_output *output)
- {
- SDL_WindowData *window = data;
- SDL_DisplayData *internal = wl_output_get_user_data(output);
- SDL_DisplayData **new_outputs;
- if (!SDL_WAYLAND_own_output(output) || !SDL_WAYLAND_own_surface(surface)) {
- return;
- }
- new_outputs = SDL_realloc(window->outputs,
- sizeof(SDL_DisplayData *) * (window->num_outputs + 1));
- if (!new_outputs) {
- return;
- }
- window->outputs = new_outputs;
- window->outputs[window->num_outputs++] = internal;
- // Update the scale factor after the move so that fullscreen outputs are updated.
- if (!window->is_fullscreen || window->num_outputs == 1) {
- Wayland_move_window(window->sdlwindow);
- Wayland_MaybeUpdateScaleFactor(window);
- }
- }
- static void handle_surface_leave(void *data, struct wl_surface *surface, struct wl_output *output)
- {
- SDL_WindowData *window = (SDL_WindowData *)data;
- if (!SDL_WAYLAND_own_output(output) || !SDL_WAYLAND_own_surface(surface)) {
- return;
- }
- Wayland_RemoveOutputFromWindow(window, (SDL_DisplayData *)wl_output_get_user_data(output));
- }
- static void handle_preferred_buffer_scale(void *data, struct wl_surface *wl_surface, int32_t factor)
- {
- SDL_WindowData *wind = data;
- /* The spec is unclear on how this interacts with the fractional scaling protocol,
- * so, for now, assume that the fractional scaling protocol takes priority and
- * only listen to this event if the fractional scaling protocol is not present.
- */
- if (!wind->fractional_scale) {
- Wayland_HandlePreferredScaleChanged(data, (double)factor);
- }
- }
- static void handle_preferred_buffer_transform(void *data, struct wl_surface *wl_surface, uint32_t transform)
- {
- // Nothing to do here.
- }
- static const struct wl_surface_listener surface_listener = {
- handle_surface_enter,
- handle_surface_leave,
- handle_preferred_buffer_scale,
- handle_preferred_buffer_transform
- };
- static void handle_preferred_fractional_scale(void *data, struct wp_fractional_scale_v1 *wp_fractional_scale_v1, uint32_t scale)
- {
- const double factor = (double)scale / 120.; // 120 is a magic number defined in the spec as a common denominator
- Wayland_HandlePreferredScaleChanged(data, factor);
- }
- static const struct wp_fractional_scale_v1_listener fractional_scale_listener = {
- handle_preferred_fractional_scale
- };
- static void frog_preferred_metadata_handler(void *data, struct frog_color_managed_surface *frog_color_managed_surface, uint32_t transfer_function,
- uint32_t output_display_primary_red_x, uint32_t output_display_primary_red_y,
- uint32_t output_display_primary_green_x, uint32_t output_display_primary_green_y,
- uint32_t output_display_primary_blue_x, uint32_t output_display_primary_blue_y,
- uint32_t output_white_point_x, uint32_t output_white_point_y,
- uint32_t max_luminance, uint32_t min_luminance,
- uint32_t max_full_frame_luminance)
- {
- SDL_WindowData *wind = (SDL_WindowData *)data;
- SDL_HDROutputProperties HDR;
- SDL_zero(HDR);
- switch (transfer_function) {
- case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ:
- /* ITU-R BT.2408-7 (Sept 2023) has the reference PQ white level at 203 nits,
- * while older Dolby documentation claims a reference level of 100 nits.
- *
- * Use 203 nits for now.
- */
- HDR.HDR_headroom = max_luminance / 203.0f;
- break;
- case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SCRGB_LINEAR:
- HDR.HDR_headroom = max_luminance / 80.0f;
- break;
- case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_UNDEFINED:
- case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SRGB:
- case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_GAMMA_22:
- default:
- HDR.HDR_headroom = 1.0f;
- break;
- }
- HDR.SDR_white_level = 1.0f;
- SDL_SetWindowHDRProperties(wind->sdlwindow, &HDR, true);
- }
- static const struct frog_color_managed_surface_listener frog_surface_listener = {
- frog_preferred_metadata_handler
- };
- static void SetKeyboardFocus(SDL_Window *window)
- {
- SDL_Window *topmost = window;
- // Find the topmost parent
- while (topmost->parent) {
- topmost = topmost->parent;
- }
- topmost->internal->keyboard_focus = window;
- SDL_SetKeyboardFocus(window);
- }
- bool Wayland_SetWindowHitTest(SDL_Window *window, bool enabled)
- {
- return true; // just succeed, the real work is done elsewhere.
- }
- static struct xdg_toplevel *GetToplevelForWindow(SDL_WindowData *wind)
- {
- if (wind) {
- /* Libdecor crashes on attempts to unset the parent by passing null, which is allowed by the
- * toplevel spec, so just use the raw xdg-toplevel instead (that's what libdecor does
- * internally anyways).
- */
- #ifdef HAVE_LIBDECOR_H
- if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR && wind->shell_surface.libdecor.frame) {
- return libdecor_frame_get_xdg_toplevel(wind->shell_surface.libdecor.frame);
- } else
- #endif
- if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL && wind->shell_surface.xdg.roleobj.toplevel) {
- return wind->shell_surface.xdg.roleobj.toplevel;
- }
- }
- return NULL;
- }
- bool Wayland_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent_window)
- {
- SDL_WindowData *child_data = window->internal;
- SDL_WindowData *parent_data = parent_window ? parent_window->internal : NULL;
- child_data->reparenting_required = false;
- if (parent_data && parent_data->surface_status != WAYLAND_SURFACE_STATUS_SHOWN) {
- // Need to wait for the parent to become mapped, or it's the same as setting a null parent.
- child_data->reparenting_required = true;
- return true;
- }
- struct xdg_toplevel *child_toplevel = GetToplevelForWindow(child_data);
- struct xdg_toplevel *parent_toplevel = GetToplevelForWindow(parent_data);
- if (child_toplevel) {
- xdg_toplevel_set_parent(child_toplevel, parent_toplevel);
- }
- return true;
- }
- bool Wayland_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal)
- {
- SDL_VideoData *viddata = _this->internal;
- SDL_WindowData *data = window->internal;
- SDL_WindowData *parent_data = window->parent->internal;
- if (parent_data->surface_status != WAYLAND_SURFACE_STATUS_SHOWN) {
- // Need to wait for the parent to become mapped before changing modal status.
- data->reparenting_required = true;
- return true;
- } else {
- data->reparenting_required = false;
- }
- struct xdg_toplevel *toplevel = GetToplevelForWindow(data);
- if (toplevel) {
- if (viddata->xdg_wm_dialog_v1) {
- if (modal) {
- if (!data->xdg_dialog_v1) {
- data->xdg_dialog_v1 = xdg_wm_dialog_v1_get_xdg_dialog(viddata->xdg_wm_dialog_v1, toplevel);
- }
- xdg_dialog_v1_set_modal(data->xdg_dialog_v1);
- } else if (data->xdg_dialog_v1) {
- xdg_dialog_v1_unset_modal(data->xdg_dialog_v1);
- }
- }
- }
- return true;
- }
- static void show_hide_sync_handler(void *data, struct wl_callback *callback, uint32_t callback_data)
- {
- // Get the window from the ID as it may have been destroyed
- SDL_WindowID windowID = (SDL_WindowID)((uintptr_t)data);
- SDL_Window *window = SDL_GetWindowFromID(windowID);
- if (window && window->internal) {
- SDL_WindowData *wind = window->internal;
- wind->show_hide_sync_required = false;
- }
- wl_callback_destroy(callback);
- }
- static struct wl_callback_listener show_hide_sync_listener = {
- show_hide_sync_handler
- };
- static void exported_handle_handler(void *data, struct zxdg_exported_v2 *zxdg_exported_v2, const char *handle)
- {
- SDL_WindowData *wind = (SDL_WindowData*)data;
- SDL_PropertiesID props = SDL_GetWindowProperties(wind->sdlwindow);
- SDL_SetStringProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_EXPORT_HANDLE_STRING, handle);
- }
- static struct zxdg_exported_v2_listener exported_v2_listener = {
- exported_handle_handler
- };
- void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
- {
- SDL_VideoData *c = _this->internal;
- SDL_WindowData *data = window->internal;
- SDL_PropertiesID props = SDL_GetWindowProperties(window);
- // Custom surfaces don't get toplevels and are always considered 'shown'; nothing to do here.
- if (data->shell_surface_type == WAYLAND_SURFACE_CUSTOM) {
- return;
- }
- /* If this is a child window, the parent *must* be in the final shown state,
- * meaning that it has received a configure event, followed by a frame callback.
- * If not, a race condition can result, with effects ranging from the child
- * window to spuriously closing to protocol errors.
- *
- * If waiting on the parent window, set the pending status and the window will
- * be shown when the parent is in the shown state.
- */
- if (window->parent) {
- if (window->parent->internal->surface_status != WAYLAND_SURFACE_STATUS_SHOWN) {
- data->surface_status = WAYLAND_SURFACE_STATUS_SHOW_PENDING;
- return;
- }
- }
- /* The window was hidden, but the sync point hasn't yet been reached.
- * Pump events to avoid a possible protocol violation.
- */
- if (data->show_hide_sync_required) {
- WAYLAND_wl_display_roundtrip(c->display);
- }
- data->surface_status = WAYLAND_SURFACE_STATUS_WAITING_FOR_CONFIGURE;
- /* Detach any previous buffers before resetting everything, otherwise when
- * calling this a second time you'll get an annoying protocol error!
- *
- * FIXME: This was originally moved to HideWindow, which _should_ make
- * sense, but for whatever reason UE5's popups require that this actually
- * be in both places at once? Possibly from renderers making commits? I can't
- * fully remember if this location caused crashes or if I was fixing a pair
- * of Hide/Show calls. In any case, UE gives us a pretty good test and having
- * both detach calls passes. This bug may be relevant if I'm wrong:
- *
- * https://bugs.kde.org/show_bug.cgi?id=448856
- *
- * -flibit
- */
- wl_surface_attach(data->surface, NULL, 0, 0);
- wl_surface_commit(data->surface);
- // Create the shell surface and map the toplevel/popup
- #ifdef HAVE_LIBDECOR_H
- if (data->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
- data->shell_surface.libdecor.frame = libdecor_decorate(c->shell.libdecor,
- data->surface,
- &libdecor_frame_interface,
- data);
- if (!data->shell_surface.libdecor.frame) {
- SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Failed to create libdecor frame!");
- } else {
- libdecor_frame_set_app_id(data->shell_surface.libdecor.frame, data->app_id);
- libdecor_frame_map(data->shell_surface.libdecor.frame);
- if (c->zxdg_exporter_v2) {
- data->exported = zxdg_exporter_v2_export_toplevel(c->zxdg_exporter_v2, data->surface);
- zxdg_exported_v2_add_listener(data->exported, &exported_v2_listener, data);
- }
- if (c->xdg_toplevel_icon_manager_v1 && data->xdg_toplevel_icon_v1) {
- xdg_toplevel_icon_manager_v1_set_icon(_this->internal->xdg_toplevel_icon_manager_v1,
- libdecor_frame_get_xdg_toplevel(data->shell_surface.libdecor.frame),
- data->xdg_toplevel_icon_v1);
- }
- SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER, libdecor_frame_get_xdg_surface(data->shell_surface.libdecor.frame));
- SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, libdecor_frame_get_xdg_toplevel(data->shell_surface.libdecor.frame));
- }
- } else
- #endif
- if (data->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL || data->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP) {
- data->shell_surface.xdg.surface = xdg_wm_base_get_xdg_surface(c->shell.xdg, data->surface);
- xdg_surface_set_user_data(data->shell_surface.xdg.surface, data);
- xdg_surface_add_listener(data->shell_surface.xdg.surface, &shell_surface_listener_xdg, data);
- SDL_SetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER, data->shell_surface.xdg.surface);
- if (data->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP) {
- SDL_Window *parent = window->parent;
- SDL_WindowData *parent_data = parent->internal;
- struct xdg_surface *parent_xdg_surface = NULL;
- int position_x = 0, position_y = 0;
- // Configure the popup parameters
- #ifdef HAVE_LIBDECOR_H
- if (parent_data->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
- parent_xdg_surface = libdecor_frame_get_xdg_surface(parent_data->shell_surface.libdecor.frame);
- } else
- #endif
- if (parent_data->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL ||
- parent_data->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP) {
- parent_xdg_surface = parent_data->shell_surface.xdg.surface;
- }
- // Set up the positioner for the popup and configure the constraints
- data->shell_surface.xdg.roleobj.popup.positioner = xdg_wm_base_create_positioner(c->shell.xdg);
- xdg_positioner_set_anchor(data->shell_surface.xdg.roleobj.popup.positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT);
- xdg_positioner_set_anchor_rect(data->shell_surface.xdg.roleobj.popup.positioner, 0, 0, parent->internal->current.logical_width, parent->internal->current.logical_width);
- xdg_positioner_set_constraint_adjustment(data->shell_surface.xdg.roleobj.popup.positioner,
- XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y);
- xdg_positioner_set_gravity(data->shell_surface.xdg.roleobj.popup.positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
- xdg_positioner_set_size(data->shell_surface.xdg.roleobj.popup.positioner, data->current.logical_width, data->current.logical_height);
- // Set the popup initial position
- position_x = window->x;
- position_y = window->y;
- EnsurePopupPositionIsValid(window, &position_x, &position_y);
- if (data->scale_to_display) {
- position_x = PixelToPoint(window->parent, position_x);
- position_y = PixelToPoint(window->parent, position_y);
- }
- AdjustPopupOffset(window, &position_x, &position_y);
- xdg_positioner_set_offset(data->shell_surface.xdg.roleobj.popup.positioner, position_x, position_y);
- // Assign the popup role
- data->shell_surface.xdg.roleobj.popup.popup = xdg_surface_get_popup(data->shell_surface.xdg.surface,
- parent_xdg_surface,
- data->shell_surface.xdg.roleobj.popup.positioner);
- xdg_popup_add_listener(data->shell_surface.xdg.roleobj.popup.popup, &popup_listener_xdg, data);
- if (window->flags & SDL_WINDOW_TOOLTIP) {
- struct wl_region *region;
- // Tooltips can't be interacted with, so turn off the input region to avoid blocking anything behind them
- region = wl_compositor_create_region(c->compositor);
- wl_region_add(region, 0, 0, 0, 0);
- wl_surface_set_input_region(data->surface, region);
- wl_region_destroy(region);
- } else if (window->flags & SDL_WINDOW_POPUP_MENU) {
- if (window->parent == SDL_GetKeyboardFocus()) {
- SetKeyboardFocus(window);
- }
- }
- SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_POPUP_POINTER, data->shell_surface.xdg.roleobj.popup.popup);
- SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_POSITIONER_POINTER, data->shell_surface.xdg.roleobj.popup.positioner);
- } else {
- data->shell_surface.xdg.roleobj.toplevel = xdg_surface_get_toplevel(data->shell_surface.xdg.surface);
- xdg_toplevel_set_app_id(data->shell_surface.xdg.roleobj.toplevel, data->app_id);
- xdg_toplevel_add_listener(data->shell_surface.xdg.roleobj.toplevel, &toplevel_listener_xdg, data);
- if (c->zxdg_exporter_v2) {
- data->exported = zxdg_exporter_v2_export_toplevel(c->zxdg_exporter_v2, data->surface);
- zxdg_exported_v2_add_listener(data->exported, &exported_v2_listener, data);
- }
- if (c->xdg_toplevel_icon_manager_v1 && data->xdg_toplevel_icon_v1) {
- xdg_toplevel_icon_manager_v1_set_icon(_this->internal->xdg_toplevel_icon_manager_v1,
- data->shell_surface.xdg.roleobj.toplevel,
- data->xdg_toplevel_icon_v1);
- }
- SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, data->shell_surface.xdg.roleobj.toplevel);
- }
- }
- // Restore state that was set prior to this call
- Wayland_SetWindowParent(_this, window, window->parent);
- if (window->flags & SDL_WINDOW_MODAL) {
- Wayland_SetWindowModal(_this, window, true);
- }
- Wayland_SetWindowTitle(_this, window);
- /* We have to wait until the surface gets a "configure" event, or use of
- * this surface will fail. This is a new rule for xdg_shell.
- */
- #ifdef HAVE_LIBDECOR_H
- if (data->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
- if (data->shell_surface.libdecor.frame) {
- while (!data->shell_surface.libdecor.initial_configure_seen) {
- WAYLAND_wl_display_flush(c->display);
- WAYLAND_wl_display_dispatch(c->display);
- }
- }
- } else
- #endif
- if (data->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP || data->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL) {
- /* Unlike libdecor we need to call this explicitly to prevent a deadlock.
- * libdecor will call this as part of their configure event!
- * -flibit
- */
- wl_surface_commit(data->surface);
- if (data->shell_surface.xdg.surface) {
- while (!data->shell_surface.xdg.initial_configure_seen) {
- WAYLAND_wl_display_flush(c->display);
- WAYLAND_wl_display_dispatch(c->display);
- }
- }
- // Create the window decorations
- if (data->shell_surface_type != WAYLAND_SURFACE_XDG_POPUP && data->shell_surface.xdg.roleobj.toplevel && c->decoration_manager) {
- data->server_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(c->decoration_manager, data->shell_surface.xdg.roleobj.toplevel);
- zxdg_toplevel_decoration_v1_add_listener(data->server_decoration,
- &decoration_listener,
- window);
- }
- } else {
- // Nothing to see here, just commit.
- wl_surface_commit(data->surface);
- }
- // Make sure the window can't be resized to 0 or it can be spuriously closed by the window manager.
- data->system_limits.min_width = SDL_max(data->system_limits.min_width, 1);
- data->system_limits.min_height = SDL_max(data->system_limits.min_height, 1);
- /* Unlike the rest of window state we have to set this _after_ flushing the
- * display, because we need to create the decorations before possibly hiding
- * them immediately afterward.
- */
- #ifdef HAVE_LIBDECOR_H
- if (data->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
- // Libdecor plugins can enforce minimum window sizes, so adjust if the initial window size is too small.
- if (window->windowed.w < data->system_limits.min_width ||
- window->windowed.h < data->system_limits.min_height) {
- // Warn if the window frame will be larger than the content surface.
- SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO,
- "Window dimensions (%i, %i) are smaller than the system enforced minimum (%i, %i); window borders will be larger than the content surface.",
- window->windowed.w, window->windowed.h, data->system_limits.min_width, data->system_limits.min_height);
- data->current.logical_width = SDL_max(window->windowed.w, data->system_limits.min_width);
- data->current.logical_height = SDL_max(window->windowed.h, data->system_limits.min_height);
- CommitLibdecorFrame(window);
- }
- }
- #endif
- Wayland_SetWindowResizable(_this, window, !!(window->flags & SDL_WINDOW_RESIZABLE));
- Wayland_SetWindowBordered(_this, window, !(window->flags & SDL_WINDOW_BORDERLESS));
- // We're finally done putting the window together, raise if possible
- if (c->activation_manager) {
- /* Note that we don't check for empty strings, as that is still
- * considered a valid activation token!
- */
- const char *activation_token = SDL_getenv("XDG_ACTIVATION_TOKEN");
- if (activation_token) {
- xdg_activation_v1_activate(c->activation_manager,
- activation_token,
- data->surface);
- // Clear this variable, per the protocol's request
- SDL_unsetenv_unsafe("XDG_ACTIVATION_TOKEN");
- }
- }
- data->show_hide_sync_required = true;
- struct wl_callback *cb = wl_display_sync(_this->internal->display);
- wl_callback_add_listener(cb, &show_hide_sync_listener, (void*)((uintptr_t)window->id));
- // Send an exposure event to signal that the client should draw.
- if (data->surface_status == WAYLAND_SURFACE_STATUS_WAITING_FOR_FRAME) {
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_EXPOSED, 0, 0);
- }
- }
- static void Wayland_ReleasePopup(SDL_VideoDevice *_this, SDL_Window *popup)
- {
- SDL_WindowData *popupdata;
- // Basic sanity checks to weed out the weird popup closures
- if (!SDL_ObjectValid(popup, SDL_OBJECT_TYPE_WINDOW)) {
- return;
- }
- popupdata = popup->internal;
- if (!popupdata) {
- return;
- }
- // This may already be freed by a parent popup!
- if (popupdata->shell_surface.xdg.roleobj.popup.popup == NULL) {
- return;
- }
- if (popup->flags & SDL_WINDOW_POPUP_MENU) {
- if (popup == SDL_GetKeyboardFocus()) {
- SDL_Window *new_focus = popup->parent;
- // Find the highest level window that isn't being hidden or destroyed.
- while (new_focus->parent && (new_focus->is_hiding || new_focus->is_destroying)) {
- new_focus = new_focus->parent;
- }
- SetKeyboardFocus(new_focus);
- }
- }
- xdg_popup_destroy(popupdata->shell_surface.xdg.roleobj.popup.popup);
- xdg_positioner_destroy(popupdata->shell_surface.xdg.roleobj.popup.positioner);
- popupdata->shell_surface.xdg.roleobj.popup.popup = NULL;
- popupdata->shell_surface.xdg.roleobj.popup.positioner = NULL;
- SDL_PropertiesID props = SDL_GetWindowProperties(popup);
- SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_POPUP_POINTER, NULL);
- SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_POSITIONER_POINTER, NULL);
- }
- void Wayland_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
- {
- SDL_VideoData *data = _this->internal;
- SDL_WindowData *wind = window->internal;
- SDL_PropertiesID props = SDL_GetWindowProperties(window);
- // Custom surfaces have nothing to destroy and are always considered to be 'shown'; nothing to do here.
- if (wind->shell_surface_type == WAYLAND_SURFACE_CUSTOM) {
- return;
- }
- /* The window was shown, but the sync point hasn't yet been reached.
- * Pump events to avoid a possible protocol violation.
- */
- if (wind->show_hide_sync_required) {
- WAYLAND_wl_display_roundtrip(data->display);
- }
- wind->surface_status = WAYLAND_SURFACE_STATUS_HIDDEN;
- if (wind->server_decoration) {
- zxdg_toplevel_decoration_v1_destroy(wind->server_decoration);
- wind->server_decoration = NULL;
- }
- // Be sure to detach after this is done, otherwise ShowWindow crashes!
- if (wind->shell_surface_type != WAYLAND_SURFACE_XDG_POPUP) {
- wl_surface_attach(wind->surface, NULL, 0, 0);
- wl_surface_commit(wind->surface);
- }
- // Clean up the export handle.
- if (wind->exported) {
- zxdg_exported_v2_destroy(wind->exported);
- wind->exported = NULL;
- SDL_SetStringProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_EXPORT_HANDLE_STRING, NULL);
- }
- if (wind->xdg_dialog_v1) {
- xdg_dialog_v1_destroy(wind->xdg_dialog_v1);
- wind->xdg_dialog_v1 = NULL;
- }
- #ifdef HAVE_LIBDECOR_H
- if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
- if (wind->shell_surface.libdecor.frame) {
- libdecor_frame_unref(wind->shell_surface.libdecor.frame);
- wind->shell_surface.libdecor.frame = NULL;
- SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER, NULL);
- SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, NULL);
- }
- } else
- #endif
- if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP) {
- Wayland_ReleasePopup(_this, window);
- } else if (wind->shell_surface.xdg.roleobj.toplevel) {
- xdg_toplevel_destroy(wind->shell_surface.xdg.roleobj.toplevel);
- wind->shell_surface.xdg.roleobj.toplevel = NULL;
- SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, NULL);
- }
- if (wind->shell_surface.xdg.surface) {
- xdg_surface_destroy(wind->shell_surface.xdg.surface);
- wind->shell_surface.xdg.surface = NULL;
- SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER, NULL);
- }
- wind->show_hide_sync_required = true;
- struct wl_callback *cb = wl_display_sync(_this->internal->display);
- wl_callback_add_listener(cb, &show_hide_sync_listener, (void*)((uintptr_t)window->id));
- }
- static void handle_xdg_activation_done(void *data,
- struct xdg_activation_token_v1 *xdg_activation_token_v1,
- const char *token)
- {
- SDL_WindowData *window = data;
- if (xdg_activation_token_v1 == window->activation_token) {
- xdg_activation_v1_activate(window->waylandData->activation_manager,
- token,
- window->surface);
- xdg_activation_token_v1_destroy(window->activation_token);
- window->activation_token = NULL;
- }
- }
- static const struct xdg_activation_token_v1_listener activation_listener_xdg = {
- handle_xdg_activation_done
- };
- /* The xdg-activation protocol considers "activation" to be one of two things:
- *
- * 1: Raising a window to the top and flashing the titlebar
- * 2: Flashing the titlebar while keeping the window where it is
- *
- * As you might expect from Wayland, the general policy is to go with #2 unless
- * the client can prove to the compositor beyond a reasonable doubt that raising
- * the window will not be malicuous behavior.
- *
- * For SDL this means RaiseWindow and FlashWindow both use the same protocol,
- * but in different ways: RaiseWindow will provide as _much_ information as
- * possible while FlashWindow will provide as _little_ information as possible,
- * to nudge the compositor into doing what we want.
- *
- * This isn't _strictly_ what the protocol says will happen, but this is what
- * current implementations are doing (as of writing, YMMV in the far distant
- * future).
- *
- * -flibit
- */
- static void Wayland_activate_window(SDL_VideoData *data, SDL_WindowData *target_wind, bool set_serial)
- {
- struct SDL_WaylandInput * input = data->input;
- SDL_Window *focus = SDL_GetKeyboardFocus();
- struct wl_surface *requesting_surface = focus ? focus->internal->surface : NULL;
- if (data->activation_manager) {
- if (target_wind->activation_token) {
- // We're about to overwrite this with a new request
- xdg_activation_token_v1_destroy(target_wind->activation_token);
- }
- target_wind->activation_token = xdg_activation_v1_get_activation_token(data->activation_manager);
- xdg_activation_token_v1_add_listener(target_wind->activation_token,
- &activation_listener_xdg,
- target_wind);
- /* Note that we are not setting the app_id here.
- *
- * Hypothetically we could set the app_id from data->classname, but
- * that part of the API is for _external_ programs, not ourselves.
- *
- * -flibit
- */
- if (requesting_surface) {
- // This specifies the surface from which the activation request is originating, not the activation target surface.
- xdg_activation_token_v1_set_surface(target_wind->activation_token, requesting_surface);
- }
- if (set_serial && input && input->seat) {
- xdg_activation_token_v1_set_serial(target_wind->activation_token, input->last_implicit_grab_serial, input->seat);
- }
- xdg_activation_token_v1_commit(target_wind->activation_token);
- }
- }
- void Wayland_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window)
- {
- Wayland_activate_window(_this->internal, window->internal, true);
- }
- bool Wayland_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation)
- {
- /* Not setting the serial will specify 'urgency' without switching focus as per
- * https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/9#note_854977
- */
- Wayland_activate_window(_this->internal, window->internal, false);
- return true;
- }
- SDL_FullscreenResult Wayland_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window,
- SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen)
- {
- SDL_WindowData *wind = window->internal;
- struct wl_output *output = display->internal->output;
- // Custom surfaces have no toplevel to make fullscreen.
- if (wind->shell_surface_type == WAYLAND_SURFACE_CUSTOM) {
- return SDL_FULLSCREEN_FAILED;
- }
- if (wind->show_hide_sync_required) {
- WAYLAND_wl_display_roundtrip(_this->internal->display);
- }
- // Flushing old events pending a new one, ignore this request.
- if (wind->drop_fullscreen_requests) {
- return SDL_FULLSCREEN_SUCCEEDED;
- }
- wind->drop_fullscreen_requests = true;
- FlushPendingEvents(window);
- wind->drop_fullscreen_requests = false;
- // Nothing to do if the window is not fullscreen, and this isn't an explicit enter request.
- if (!wind->is_fullscreen) {
- if (fullscreen == SDL_FULLSCREEN_OP_UPDATE) {
- // Request was out of date; signal the video core not to update any state.
- return SDL_FULLSCREEN_PENDING;
- } else if (fullscreen == SDL_FULLSCREEN_OP_LEAVE) {
- // Already not fullscreen; nothing to do.
- return SDL_FULLSCREEN_SUCCEEDED;
- }
- }
- // Don't send redundant fullscreen set/unset events.
- if (fullscreen != wind->is_fullscreen) {
- wind->fullscreen_was_positioned = fullscreen;
- SetFullscreen(window, fullscreen ? output : NULL);
- } else if (wind->is_fullscreen) {
- /*
- * If the window is already fullscreen, this is likely a request to switch between
- * fullscreen and fullscreen desktop, change outputs, or change the video mode.
- *
- * If the window is already positioned on the target output, just update the
- * window geometry.
- */
- if (wind->last_displayID != display->id) {
- wind->fullscreen_was_positioned = true;
- SetFullscreen(window, output);
- } else {
- ConfigureWindowGeometry(window);
- CommitLibdecorFrame(window);
- return SDL_FULLSCREEN_SUCCEEDED;
- }
- }
- return SDL_FULLSCREEN_PENDING;
- }
- void Wayland_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
- {
- SDL_WindowData *wind = window->internal;
- #ifdef HAVE_LIBDECOR_H
- if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
- if (!wind->shell_surface.libdecor.frame) {
- return; // Can't do anything yet, wait for ShowWindow
- }
- libdecor_frame_unset_maximized(wind->shell_surface.libdecor.frame);
- } else
- #endif
- // Note that xdg-shell does NOT provide a way to unset minimize!
- if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL) {
- if (wind->shell_surface.xdg.roleobj.toplevel == NULL) {
- return; // Can't do anything yet, wait for ShowWindow
- }
- xdg_toplevel_unset_maximized(wind->shell_surface.xdg.roleobj.toplevel);
- }
- }
- void Wayland_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, bool bordered)
- {
- SDL_WindowData *wind = window->internal;
- const SDL_VideoData *viddata = (const SDL_VideoData *)_this->internal;
- #ifdef HAVE_LIBDECOR_H
- if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
- if (wind->shell_surface.libdecor.frame) {
- libdecor_frame_set_visibility(wind->shell_surface.libdecor.frame, bordered);
- }
- } else
- #endif
- if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL) {
- if ((viddata->decoration_manager) && (wind->server_decoration)) {
- const enum zxdg_toplevel_decoration_v1_mode mode = bordered ? ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE : ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
- zxdg_toplevel_decoration_v1_set_mode(wind->server_decoration, mode);
- }
- }
- }
- void Wayland_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, bool resizable)
- {
- #ifdef HAVE_LIBDECOR_H
- const SDL_WindowData *wind = window->internal;
- if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
- if (!wind->shell_surface.libdecor.frame) {
- return; // Can't do anything yet, wait for ShowWindow
- }
- if (libdecor_frame_has_capability(wind->shell_surface.libdecor.frame, LIBDECOR_ACTION_RESIZE)) {
- if (!resizable) {
- libdecor_frame_unset_capabilities(wind->shell_surface.libdecor.frame, LIBDECOR_ACTION_RESIZE);
- }
- } else if (resizable) {
- libdecor_frame_set_capabilities(wind->shell_surface.libdecor.frame, LIBDECOR_ACTION_RESIZE);
- }
- }
- #endif
- /* When changing the resize capability on libdecor windows, the limits must always
- * be reapplied, as when libdecor changes states, it overwrites the values internally.
- */
- SetMinMaxDimensions(window);
- CommitLibdecorFrame(window);
- }
- void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
- {
- SDL_VideoData *viddata = _this->internal;
- SDL_WindowData *wind = window->internal;
- if (wind->show_hide_sync_required) {
- WAYLAND_wl_display_roundtrip(_this->internal->display);
- }
- #ifdef HAVE_LIBDECOR_H
- if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
- if (!wind->shell_surface.libdecor.frame) {
- return; // Can't do anything yet, wait for ShowWindow
- }
- // Commit to preserve any pending size data.
- wl_surface_commit(wind->surface);
- libdecor_frame_set_maximized(wind->shell_surface.libdecor.frame);
- } else
- #endif
- if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL) {
- if (wind->shell_surface.xdg.roleobj.toplevel == NULL) {
- return; // Can't do anything yet, wait for ShowWindow
- }
- // Commit to preserve any pending size data.
- wl_surface_commit(wind->surface);
- xdg_toplevel_set_maximized(wind->shell_surface.xdg.roleobj.toplevel);
- }
- ++wind->maximized_deadline_count;
- struct wl_callback *cb = wl_display_sync(viddata->display);
- wl_callback_add_listener(cb, &maximized_deadline_listener, (void *)((uintptr_t)window->id));
- }
- void Wayland_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
- {
- SDL_WindowData *wind = window->internal;
- // TODO: Check compositor capabilities to see if minimizing is supported
- #ifdef HAVE_LIBDECOR_H
- if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
- if (!wind->shell_surface.libdecor.frame) {
- return; // Can't do anything yet, wait for ShowWindow
- }
- libdecor_frame_set_minimized(wind->shell_surface.libdecor.frame);
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
- } else
- #endif
- if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL) {
- if (wind->shell_surface.xdg.roleobj.toplevel == NULL) {
- return; // Can't do anything yet, wait for ShowWindow
- }
- xdg_toplevel_set_minimized(wind->shell_surface.xdg.roleobj.toplevel);
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
- }
- }
- bool Wayland_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window)
- {
- SDL_VideoData *data = _this->internal;
- /* This may look suspiciously like SetWindowGrab, despite SetMouseRect not
- * implicitly doing a grab. And you're right! Wayland doesn't let us mess
- * around with mouse focus whatsoever, so it just happens to be that the
- * work that we can do in these two functions ends up being the same.
- *
- * Just know that this call lets you confine with a rect, SetWindowGrab
- * lets you confine without a rect.
- */
- if (SDL_RectEmpty(&window->mouse_rect) && !(window->flags & SDL_WINDOW_MOUSE_GRABBED)) {
- return Wayland_input_unconfine_pointer(data->input, window);
- } else {
- return Wayland_input_confine_pointer(data->input, window);
- }
- }
- bool Wayland_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed)
- {
- SDL_VideoData *data = _this->internal;
- if (grabbed) {
- return Wayland_input_confine_pointer(data->input, window);
- } else if (SDL_RectEmpty(&window->mouse_rect)) {
- return Wayland_input_unconfine_pointer(data->input, window);
- }
- return true;
- }
- bool Wayland_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed)
- {
- SDL_VideoData *data = _this->internal;
- if (grabbed) {
- return Wayland_input_grab_keyboard(window, data->input);
- } else {
- return Wayland_input_ungrab_keyboard(window);
- }
- }
- bool Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props)
- {
- SDL_WindowData *data;
- SDL_VideoData *c = _this->internal;
- struct wl_surface *external_surface = (struct wl_surface *)SDL_GetPointerProperty(create_props, SDL_PROP_WINDOW_CREATE_WAYLAND_WL_SURFACE_POINTER,
- (struct wl_surface *)SDL_GetPointerProperty(create_props, "sdl2-compat.external_window", NULL));
- const bool custom_surface_role = (external_surface != NULL) || SDL_GetBooleanProperty(create_props, SDL_PROP_WINDOW_CREATE_WAYLAND_SURFACE_ROLE_CUSTOM_BOOLEAN, false);
- const bool create_egl_window = !!(window->flags & SDL_WINDOW_OPENGL) ||
- SDL_GetBooleanProperty(create_props, SDL_PROP_WINDOW_CREATE_WAYLAND_CREATE_EGL_WINDOW_BOOLEAN, false);
- data = SDL_calloc(1, sizeof(*data));
- if (!data) {
- return false;
- }
- window->internal = data;
- if (window->x == SDL_WINDOWPOS_UNDEFINED) {
- window->x = 0;
- }
- if (window->y == SDL_WINDOWPOS_UNDEFINED) {
- window->y = 0;
- }
- data->waylandData = c;
- data->sdlwindow = window;
- data->scale_factor = 1.0;
- if (SDL_WINDOW_IS_POPUP(window)) {
- data->scale_to_display = window->parent->internal->scale_to_display;
- data->scale_factor = window->parent->internal->scale_factor;
- EnsurePopupPositionIsValid(window, &window->x, &window->y);
- } else {
- for (int i = 0; i < _this->num_displays; i++) {
- data->scale_factor = SDL_max(data->scale_factor, _this->displays[i]->internal->scale_factor);
- }
- }
- data->outputs = NULL;
- data->num_outputs = 0;
- data->scale_to_display = c->scale_to_display_enabled;
- // Cache the app_id at creation time, as it may change before the window is mapped.
- data->app_id = SDL_strdup(SDL_GetAppID());
- if (!data->scale_to_display) {
- data->requested.logical_width = window->floating.w;
- data->requested.logical_height = window->floating.h;
- } else {
- data->requested.logical_width = PixelToPoint(window, window->floating.w);
- data->requested.logical_height = PixelToPoint(window, window->floating.h);
- data->requested.pixel_width = window->floating.w;
- data->requested.pixel_height = window->floating.h;
- }
- if (!external_surface) {
- data->surface = wl_compositor_create_surface(c->compositor);
- wl_surface_add_listener(data->surface, &surface_listener, data);
- wl_surface_set_user_data(data->surface, data);
- SDL_WAYLAND_register_surface(data->surface);
- } else {
- window->flags |= SDL_WINDOW_EXTERNAL;
- data->surface = external_surface;
- /* External surfaces are registered by being put in a list, as changing tags or userdata
- * can cause problems with external toolkits.
- */
- Wayland_AddWindowDataToExternalList(data);
- }
- /* Always attach a viewport and fractional scale manager if available and the surface is not custom/external,
- * or the custom/external surface was explicitly flagged as high pixel density aware, which signals that the
- * application wants SDL to handle scaling.
- */
- if (!custom_surface_role || (window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY)) {
- if (c->viewporter) {
- data->viewport = wp_viewporter_get_viewport(c->viewporter, data->surface);
- // The viewport always uses the entire buffer.
- wp_viewport_set_source(data->viewport,
- wl_fixed_from_int(-1), wl_fixed_from_int(-1),
- wl_fixed_from_int(-1), wl_fixed_from_int(-1));
- }
- if (c->fractional_scale_manager) {
- data->fractional_scale = wp_fractional_scale_manager_v1_get_fractional_scale(c->fractional_scale_manager, data->surface);
- wp_fractional_scale_v1_add_listener(data->fractional_scale, &fractional_scale_listener, data);
- }
- }
- if (!custom_surface_role) {
- if (c->frog_color_management_factory_v1) {
- data->frog_color_managed_surface = frog_color_management_factory_v1_get_color_managed_surface(c->frog_color_management_factory_v1, data->surface);
- frog_color_managed_surface_add_listener(data->frog_color_managed_surface, &frog_surface_listener, data);
- }
- if (c->wp_alpha_modifier_v1) {
- data->wp_alpha_modifier_surface_v1 = wp_alpha_modifier_v1_get_surface(c->wp_alpha_modifier_v1, data->surface);
- wp_alpha_modifier_surface_v1_set_multiplier(data->wp_alpha_modifier_surface_v1, SDL_MAX_UINT32);
- }
- }
- // Must be called before EGL configuration to set the drawable backbuffer size.
- ConfigureWindowGeometry(window);
- /* Fire a callback when the compositor wants a new frame rendered.
- * Right now this only matters for OpenGL; we use this callback to add a
- * wait timeout that avoids getting deadlocked by the compositor when the
- * window isn't visible.
- */
- if (window->flags & SDL_WINDOW_OPENGL) {
- data->gles_swap_frame_event_queue = WAYLAND_wl_display_create_queue(data->waylandData->display);
- data->gles_swap_frame_surface_wrapper = WAYLAND_wl_proxy_create_wrapper(data->surface);
- WAYLAND_wl_proxy_set_queue((struct wl_proxy *)data->gles_swap_frame_surface_wrapper, data->gles_swap_frame_event_queue);
- data->gles_swap_frame_callback = wl_surface_frame(data->gles_swap_frame_surface_wrapper);
- wl_callback_add_listener(data->gles_swap_frame_callback, &gles_swap_frame_listener, data);
- }
- // No frame callback on external surfaces as it may already have one attached.
- if (!external_surface) {
- // Fire a callback when the compositor wants a new frame to set the surface damage region.
- data->surface_frame_callback = wl_surface_frame(data->surface);
- wl_callback_add_listener(data->surface_frame_callback, &surface_frame_listener, data);
- }
- if (window->flags & SDL_WINDOW_TRANSPARENT) {
- if (_this->gl_config.alpha_size == 0) {
- _this->gl_config.alpha_size = 8;
- }
- }
- if (create_egl_window) {
- data->egl_window = WAYLAND_wl_egl_window_create(data->surface, data->current.pixel_width, data->current.pixel_height);
- }
- #ifdef SDL_VIDEO_OPENGL_EGL
- if (window->flags & SDL_WINDOW_OPENGL) {
- // Create the GLES window surface
- data->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)data->egl_window);
- if (data->egl_surface == EGL_NO_SURFACE) {
- return false; // SDL_EGL_CreateSurface should have set error
- }
- }
- #endif
- if (c->relative_mouse_mode) {
- Wayland_input_enable_relative_pointer(c->input);
- }
- // We may need to create an idle inhibitor for this new window
- Wayland_SuspendScreenSaver(_this);
- if (!custom_surface_role) {
- #ifdef HAVE_LIBDECOR_H
- if (c->shell.libdecor && !SDL_WINDOW_IS_POPUP(window)) {
- data->shell_surface_type = WAYLAND_SURFACE_LIBDECOR;
- } else
- #endif
- if (c->shell.xdg) {
- if (SDL_WINDOW_IS_POPUP(window)) {
- data->shell_surface_type = WAYLAND_SURFACE_XDG_POPUP;
- } else {
- data->shell_surface_type = WAYLAND_SURFACE_XDG_TOPLEVEL;
- }
- } // All other cases will be WAYLAND_SURFACE_UNKNOWN
- } else {
- // Roleless and external surfaces are always considered to be in the shown state by the backend.
- data->shell_surface_type = WAYLAND_SURFACE_CUSTOM;
- data->surface_status = WAYLAND_SURFACE_STATUS_SHOWN;
- }
- if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, false)) {
- data->double_buffer = true;
- }
- SDL_PropertiesID props = SDL_GetWindowProperties(window);
- SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, data->waylandData->display);
- SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, data->surface);
- SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_EGL_WINDOW_POINTER, data->egl_window);
- data->hit_test_result = SDL_HITTEST_NORMAL;
- return true;
- }
- void Wayland_SetWindowMinimumSize(SDL_VideoDevice *_this, SDL_Window *window)
- {
- // Will be committed when Wayland_SetWindowSize() is called by the video core.
- SetMinMaxDimensions(window);
- }
- void Wayland_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window)
- {
- // Will be committed when Wayland_SetWindowSize() is called by the video core.
- SetMinMaxDimensions(window);
- }
- bool Wayland_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window)
- {
- SDL_WindowData *wind = window->internal;
- // Only popup windows can be positioned relative to the parent.
- if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP) {
- if (wind->shell_surface.xdg.roleobj.popup.popup &&
- xdg_popup_get_version(wind->shell_surface.xdg.roleobj.popup.popup) < XDG_POPUP_REPOSITION_SINCE_VERSION) {
- return SDL_Unsupported();
- }
- RepositionPopup(window, false);
- return true;
- } else if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR || wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL) {
- const int x = window->floating.x;
- const int y = window->floating.y;
- /* Catch up on any pending state before attempting to change the fullscreen window
- * display via a set fullscreen call to make sure the window doesn't have a pending
- * leave fullscreen event that it might override.
- */
- FlushPendingEvents(window);
- /* XXX: Need to restore this after the roundtrip, as the requested coordinates might
- * have been overwritten by the 'real' coordinates if a display enter/leave event
- * occurred.
- *
- * The common pattern:
- *
- * SDL_SetWindowPosition();
- * SDL_SetWindowFullscreen();
- *
- * for positioning a desktop fullscreen window won't work without this.
- */
- window->floating.x = x;
- window->floating.y = y;
- if (wind->is_fullscreen) {
- SDL_VideoDisplay *display = SDL_GetVideoDisplayForFullscreenWindow(window);
- if (display && wind->last_displayID != display->id) {
- struct wl_output *output = display->internal->output;
- SetFullscreen(window, output);
- return true;
- }
- }
- }
- return SDL_SetError("wayland cannot position non-popup windows");
- }
- void Wayland_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
- {
- SDL_WindowData *wind = window->internal;
- /* Fullscreen windows do not get explicitly resized, and not strictly
- * obeying the size of maximized windows is a protocol violation, so
- * it is necessary to flush any of these pending state operations.
- *
- * Calling this on a custom surface is informative, so the size must
- * always be passed through.
- */
- FlushPendingEvents(window);
- if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) ||
- wind->shell_surface_type == WAYLAND_SURFACE_CUSTOM) {
- if (!wind->scale_to_display) {
- wind->requested.logical_width = window->floating.w;
- wind->requested.logical_height = window->floating.h;
- } else {
- wind->requested.logical_width = PixelToPoint(window, window->floating.w);
- wind->requested.logical_height = PixelToPoint(window, window->floating.h);
- wind->requested.pixel_width = window->floating.w;
- wind->requested.pixel_height = window->floating.h;
- }
- ConfigureWindowGeometry(window);
- } else {
- wind->pending_restored_size = true;
- }
- // Always commit, as this may be in response to a min/max limit change.
- CommitLibdecorFrame(window);
- }
- void Wayland_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h)
- {
- SDL_WindowData *data = window->internal;
- *w = data->current.pixel_width;
- *h = data->current.pixel_height;
- }
- SDL_DisplayID Wayland_GetDisplayForWindow(SDL_VideoDevice *_this, SDL_Window *window)
- {
- SDL_WindowData *wind = window->internal;
- if (wind) {
- return wind->last_displayID;
- }
- return 0;
- }
- bool Wayland_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity)
- {
- SDL_WindowData *wind = window->internal;
- if (wind->wp_alpha_modifier_surface_v1) {
- SetSurfaceOpaqueRegion(wind, !(window->flags & SDL_WINDOW_TRANSPARENT) && opacity == 1.0f);
- wp_alpha_modifier_surface_v1_set_multiplier(wind->wp_alpha_modifier_surface_v1, (Uint32)((double)SDL_MAX_UINT32 * (double)opacity));
- return true;
- }
- return SDL_SetError("wayland: set window opacity failed; compositor lacks support for the required wp_alpha_modifier_v1 protocol");
- }
- void Wayland_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window)
- {
- SDL_WindowData *wind = window->internal;
- const char *title = window->title ? window->title : "";
- #ifdef HAVE_LIBDECOR_H
- if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR && wind->shell_surface.libdecor.frame) {
- libdecor_frame_set_title(wind->shell_surface.libdecor.frame, title);
- } else
- #endif
- if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL && wind->shell_surface.xdg.roleobj.toplevel) {
- xdg_toplevel_set_title(wind->shell_surface.xdg.roleobj.toplevel, title);
- }
- }
- bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon)
- {
- SDL_WindowData *wind = window->internal;
- struct xdg_toplevel *toplevel = NULL;
- if (!_this->internal->xdg_toplevel_icon_manager_v1) {
- return SDL_SetError("wayland: cannot set icon; xdg_toplevel_icon_v1 protocol not supported");
- }
- if (icon->w != icon->h) {
- return SDL_SetError("wayland: icon width and height must be equal, got %ix%i", icon->w, icon->h);
- }
- if (wind->xdg_toplevel_icon_v1) {
- xdg_toplevel_icon_v1_destroy(wind->xdg_toplevel_icon_v1);
- wind->xdg_toplevel_icon_v1 = NULL;
- }
- // TODO: Add high-DPI icon support
- Wayland_ReleaseSHMBuffer(&wind->icon);
- if (Wayland_AllocSHMBuffer(icon->w, icon->h, &wind->icon) != 0) {
- return SDL_SetError("wayland: failed to allocate SHM buffer for the icon");
- }
- SDL_PremultiplyAlpha(icon->w, icon->h, icon->format, icon->pixels, icon->pitch, SDL_PIXELFORMAT_ARGB8888, wind->icon.shm_data, icon->w * 4, true);
- wind->xdg_toplevel_icon_v1 = xdg_toplevel_icon_manager_v1_create_icon(_this->internal->xdg_toplevel_icon_manager_v1);
- xdg_toplevel_icon_v1_add_buffer(wind->xdg_toplevel_icon_v1, wind->icon.wl_buffer, 1);
- #ifdef HAVE_LIBDECOR_H
- if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR && wind->shell_surface.libdecor.frame) {
- toplevel = libdecor_frame_get_xdg_toplevel(wind->shell_surface.libdecor.frame);
- } else
- #endif
- if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL && wind->shell_surface.xdg.roleobj.toplevel) {
- toplevel = wind->shell_surface.xdg.roleobj.toplevel;
- }
- if (toplevel) {
- xdg_toplevel_icon_manager_v1_set_icon(_this->internal->xdg_toplevel_icon_manager_v1, toplevel, wind->xdg_toplevel_icon_v1);
- }
- return true;
- }
- bool Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window)
- {
- SDL_WindowData *wind = window->internal;
- do {
- WAYLAND_wl_display_roundtrip(_this->internal->display);
- } while (wind->fullscreen_deadline_count || wind->maximized_deadline_count);
- return true;
- }
- void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
- {
- SDL_WindowData *wind = window->internal;
- if (wind->scale_to_display) {
- x = PixelToPoint(window, x);
- y = PixelToPoint(window, y);
- }
- #ifdef HAVE_LIBDECOR_H
- if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
- if (wind->shell_surface.libdecor.frame) {
- libdecor_frame_show_window_menu(wind->shell_surface.libdecor.frame, wind->waylandData->input->seat, wind->waylandData->input->last_implicit_grab_serial, x, y);
- }
- } else
- #endif
- if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL) {
- if (wind->shell_surface.xdg.roleobj.toplevel) {
- xdg_toplevel_show_window_menu(wind->shell_surface.xdg.roleobj.toplevel, wind->waylandData->input->seat, wind->waylandData->input->last_implicit_grab_serial, x, y);
- }
- }
- }
- bool Wayland_SuspendScreenSaver(SDL_VideoDevice *_this)
- {
- SDL_VideoData *data = _this->internal;
- #ifdef SDL_USE_LIBDBUS
- if (SDL_DBus_ScreensaverInhibit(_this->suspend_screensaver)) {
- return true;
- }
- #endif
- /* The idle_inhibit_unstable_v1 protocol suspends the screensaver
- on a per wl_surface basis, but SDL assumes that suspending
- the screensaver can be done independently of any window.
- To reconcile these differences, we propagate the idle inhibit
- state to each window. If there is no window active, we will
- be able to inhibit idle once the first window is created.
- */
- if (data->idle_inhibit_manager) {
- SDL_Window *window = _this->windows;
- while (window) {
- SDL_WindowData *win_data = window->internal;
- if (_this->suspend_screensaver && !win_data->idle_inhibitor) {
- win_data->idle_inhibitor =
- zwp_idle_inhibit_manager_v1_create_inhibitor(data->idle_inhibit_manager,
- win_data->surface);
- } else if (!_this->suspend_screensaver && win_data->idle_inhibitor) {
- zwp_idle_inhibitor_v1_destroy(win_data->idle_inhibitor);
- win_data->idle_inhibitor = NULL;
- }
- window = window->next;
- }
- }
- return true;
- }
- void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
- {
- SDL_VideoData *data = _this->internal;
- SDL_WindowData *wind = window->internal;
- if (data && wind) {
- /* Roundtrip before destroying the window to make sure that it has received input leave events, so that
- * no internal structures are left pointing to the destroyed window.
- */
- if (wind->show_hide_sync_required) {
- WAYLAND_wl_display_roundtrip(data->display);
- }
- #ifdef SDL_VIDEO_OPENGL_EGL
- if (wind->egl_surface) {
- SDL_EGL_DestroySurface(_this, wind->egl_surface);
- }
- #endif
- if (wind->egl_window) {
- WAYLAND_wl_egl_window_destroy(wind->egl_window);
- }
- if (wind->idle_inhibitor) {
- zwp_idle_inhibitor_v1_destroy(wind->idle_inhibitor);
- }
- if (wind->activation_token) {
- xdg_activation_token_v1_destroy(wind->activation_token);
- }
- if (wind->viewport) {
- wp_viewport_destroy(wind->viewport);
- }
- if (wind->fractional_scale) {
- wp_fractional_scale_v1_destroy(wind->fractional_scale);
- }
- if (wind->wp_alpha_modifier_surface_v1) {
- wp_alpha_modifier_surface_v1_destroy(wind->wp_alpha_modifier_surface_v1);
- }
- if (wind->frog_color_managed_surface) {
- frog_color_managed_surface_destroy(wind->frog_color_managed_surface);
- }
- SDL_free(wind->outputs);
- SDL_free(wind->app_id);
- if (wind->gles_swap_frame_callback) {
- wl_callback_destroy(wind->gles_swap_frame_callback);
- WAYLAND_wl_proxy_wrapper_destroy(wind->gles_swap_frame_surface_wrapper);
- WAYLAND_wl_event_queue_destroy(wind->gles_swap_frame_event_queue);
- }
- if (wind->surface_frame_callback) {
- wl_callback_destroy(wind->surface_frame_callback);
- }
- if (!(window->flags & SDL_WINDOW_EXTERNAL)) {
- wl_surface_destroy(wind->surface);
- } else {
- Wayland_RemoveWindowDataFromExternalList(wind);
- }
- if (wind->xdg_toplevel_icon_v1) {
- xdg_toplevel_icon_v1_destroy(wind->xdg_toplevel_icon_v1);
- }
- Wayland_ReleaseSHMBuffer(&wind->icon);
- SDL_free(wind);
- WAYLAND_wl_display_flush(data->display);
- }
- window->internal = NULL;
- }
- #endif // SDL_VIDEO_DRIVER_WAYLAND
|