4747
4848#include "alpha-modifier-v1-client-protocol.h"
4949#include "cursor-shape-v1-client-protocol.h"
50+ #include "ext-zones-v1-client-protocol.h"
5051#include "fractional-scale-v1-client-protocol.h"
5152#include "frog-color-management-v1-client-protocol.h"
5253#include "idle-inhibit-unstable-v1-client-protocol.h"
@@ -400,6 +401,7 @@ static void handle_wl_output_done(void *data, struct wl_output *output);
400401// Initialization/Query functions
401402static bool Wayland_VideoInit (SDL_VideoDevice * _this );
402403static bool Wayland_GetDisplayBounds (SDL_VideoDevice * _this , SDL_VideoDisplay * display , SDL_Rect * rect );
404+ static bool Wayland_GetDisplayUsableBounds (SDL_VideoDevice * _this , SDL_VideoDisplay * display , SDL_Rect * rect );
403405static void Wayland_VideoQuit (SDL_VideoDevice * _this );
404406
405407static const char * SDL_WAYLAND_surface_tag = "sdl-window" ;
@@ -617,6 +619,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols)
617619 device -> VideoInit = Wayland_VideoInit ;
618620 device -> VideoQuit = Wayland_VideoQuit ;
619621 device -> GetDisplayBounds = Wayland_GetDisplayBounds ;
622+ device -> GetDisplayUsableBounds = Wayland_GetDisplayUsableBounds ;
620623 device -> SuspendScreenSaver = Wayland_SuspendScreenSaver ;
621624
622625 device -> PumpEvents = Wayland_PumpEvents ;
@@ -661,6 +664,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols)
661664 device -> GetWindowSizeInPixels = Wayland_GetWindowSizeInPixels ;
662665 device -> GetWindowContentScale = Wayland_GetWindowContentScale ;
663666 device -> GetWindowICCProfile = Wayland_GetWindowICCProfile ;
667+ device -> GetWindowBordersSize = Wayland_GetWindowBorderSize ;
664668 device -> GetDisplayForWindow = Wayland_GetDisplayForWindow ;
665669 device -> DestroyWindow = Wayland_DestroyWindow ;
666670 device -> SetWindowHitTest = Wayland_SetWindowHitTest ;
@@ -673,6 +677,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols)
673677 device -> SyncWindow = Wayland_SyncWindow ;
674678 device -> SetWindowFocusable = Wayland_SetWindowFocusable ;
675679 device -> ReconfigureWindow = Wayland_ReconfigureWindow ;
680+ device -> SetWindowAlwaysOnTop = Wayland_SetWindowAlwaysOnTop ;
676681
677682#ifdef SDL_USE_LIBDBUS
678683 if (SDL_SystemTheme_Init ())
@@ -731,7 +736,86 @@ VideoBootStrap Wayland_bootstrap = {
731736 false
732737};
733738
734- static void handle_xdg_output_logical_position (void * data , struct zxdg_output_v1 * xdg_output , int32_t x , int32_t y )
739+ static void handle_zone_size (void * data , struct ext_zone_v1 * ext_zone_v1 , int32_t width , int32_t height )
740+ {
741+ SDL_DisplayData * disp = (SDL_DisplayData * )data ;
742+
743+ // Negative size values mean that zone creation on this output was denied.
744+ if (width < 0 || height < 0 ) {
745+ ext_zone_v1_destroy (disp -> ext_zone_v1 );
746+ disp -> ext_zone_v1 = NULL ;
747+ return ;
748+ }
749+
750+ disp -> zone_width = width ? width : SDL_MAX_SINT32 ;
751+ disp -> zone_height = height ? height : SDL_MAX_SINT32 ;
752+
753+ if (disp -> display ) {
754+ SDL_VideoDisplay * display = SDL_GetVideoDisplay (disp -> display );
755+ if (display ) {
756+ SDL_SendDisplayEvent (display , SDL_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED , width , height );
757+ }
758+ }
759+ }
760+
761+ static void handle_zone_handle (void * data , struct ext_zone_v1 * ext_zone_v1 , const char * handle )
762+ {
763+ // NOP
764+ }
765+
766+ static void handle_zone_done (void * data , struct ext_zone_v1 * ext_zone_v1 )
767+ {
768+ // NOP
769+ }
770+
771+ static void handle_zone_item_blocked (void * data , struct ext_zone_v1 * ext_zone_v1 , struct ext_zone_item_v1 * item )
772+ {
773+ SDL_WindowData * wind = (SDL_WindowData * )ext_zone_item_v1_get_user_data (item );
774+ wind -> entering_new_zone = false;
775+ }
776+
777+ static void handle_zone_item_entered (void * data , struct ext_zone_v1 * ext_zone_v1 , struct ext_zone_item_v1 * item )
778+ {
779+ if (item ) {
780+ SDL_WindowData * wind = (SDL_WindowData * )ext_zone_item_v1_get_user_data (item );
781+ wind -> current_ext_zone_v1 = ext_zone_v1 ;
782+
783+ if (wind -> entering_new_zone ) {
784+ Wayland_ApplyZoneItemPosition (wind );
785+ }
786+
787+ wind -> entering_new_zone = false;
788+ }
789+ }
790+
791+ static void handle_zone_item_left (void * data , struct ext_zone_v1 * ext_zone_v1 , struct ext_zone_item_v1 * item )
792+ {
793+ if (item ) {
794+ SDL_WindowData * wind = (SDL_WindowData * )ext_zone_item_v1_get_user_data (item );
795+ wind -> current_ext_zone_v1 = NULL ;
796+
797+ if (!wind -> entering_new_zone ) {
798+ // If leaving a zone without a new join pending, find a new zone to join.
799+ const SDL_DisplayData * display = Wayland_GetDisplayForWindowZone (wind );
800+
801+ if (display ) {
802+ ext_zone_v1_add_item (display -> ext_zone_v1 , item );
803+ }
804+ }
805+ }
806+ }
807+
808+ static const struct ext_zone_v1_listener zone_listener = {
809+ handle_zone_size ,
810+ handle_zone_handle ,
811+ handle_zone_done ,
812+ handle_zone_item_blocked ,
813+ handle_zone_item_entered ,
814+ handle_zone_item_left
815+ };
816+
817+ static void handle_xdg_output_logical_position (void * data , struct zxdg_output_v1 * xdg_output ,
818+ int32_t x , int32_t y )
735819{
736820 SDL_DisplayData * internal = (SDL_DisplayData * )data ;
737821
@@ -1071,6 +1155,13 @@ static void handle_wl_output_done(void *data, struct wl_output *output)
10711155
10721156 SDL_SetDisplayHDRProperties (dpy , & internal -> HDR );
10731157
1158+ // Get the zone for this output, if the extension is available.
1159+ if (video -> ext_zone_manager_v1 && !internal -> ext_zone_v1 ) {
1160+ internal -> ext_zone_v1 = ext_zone_manager_v1_get_zone (video -> ext_zone_manager_v1 , internal -> output );
1161+ ext_zone_v1_set_user_data (internal -> ext_zone_v1 , internal );
1162+ ext_zone_v1_add_listener (internal -> ext_zone_v1 , & zone_listener , internal );
1163+ }
1164+
10741165 if (internal -> display == 0 ) {
10751166 // First time getting display info, initialize the VideoDisplay
10761167 if (internal -> physical_width_mm >= internal -> physical_height_mm ) {
@@ -1197,6 +1288,10 @@ static void Wayland_free_display(SDL_VideoDisplay *display, bool send_event)
11971288 wp_color_management_output_v1_destroy (display_data -> wp_color_management_output );
11981289 }
11991290
1291+ if (display_data -> ext_zone_v1 ) {
1292+ ext_zone_v1_destroy (display_data -> ext_zone_v1 );
1293+ }
1294+
12001295 if (display_data -> xdg_output ) {
12011296 zxdg_output_v1_destroy (display_data -> xdg_output );
12021297 }
@@ -1327,6 +1422,10 @@ static void handle_registry_global(void *data, struct wl_registry *registry, uin
13271422 d -> wp_alpha_modifier_v1 = wl_registry_bind (d -> registry , id , & wp_alpha_modifier_v1_interface , 1 );
13281423 } else if (SDL_strcmp (interface , "xdg_toplevel_icon_manager_v1" ) == 0 ) {
13291424 d -> xdg_toplevel_icon_manager_v1 = wl_registry_bind (d -> registry , id , & xdg_toplevel_icon_manager_v1_interface , 1 );
1425+ } else if (SDL_strcmp (interface , "ext_zone_manager_v1" ) == 0 ) {
1426+ if (SDL_GetHintBoolean (SDL_HINT_VIDEO_WAYLAND_ENABLE_ZONES , false)) {
1427+ d -> ext_zone_manager_v1 = wl_registry_bind (d -> registry , id , & ext_zone_manager_v1_interface , 1 );
1428+ }
13301429 } else if (SDL_strcmp (interface , "frog_color_management_factory_v1" ) == 0 ) {
13311430 d -> frog_color_management_factory_v1 = wl_registry_bind (d -> registry , id , & frog_color_management_factory_v1_interface , 1 );
13321431 } else if (SDL_strcmp (interface , "wp_color_manager_v1" ) == 0 ) {
@@ -1462,11 +1561,18 @@ bool Wayland_VideoInit(SDL_VideoDevice *_this)
14621561 // Second roundtrip to receive all output events.
14631562 WAYLAND_wl_display_roundtrip (data -> display );
14641563
1564+ // Third roundtrip, if necessary, to retrieve zone info.
1565+ if (data -> ext_zone_manager_v1 ) {
1566+ WAYLAND_wl_display_roundtrip (data -> display );
1567+ }
1568+
14651569 Wayland_FinalizeDisplays (data );
14661570
14671571 Wayland_InitMouse (data );
14681572 Wayland_InitKeyboard (_this );
14691573
1574+ SDL_SetBooleanProperty (SDL_GetGlobalProperties (), SDL_PROP_GLOBAL_VIDEO_WAYLAND_HAS_ZONES_BOOLEAN , !!data -> ext_zone_manager_v1 );
1575+
14701576 if (data -> primary_selection_device_manager ) {
14711577 _this -> SetPrimarySelectionText = Wayland_SetPrimarySelectionText ;
14721578 _this -> GetPrimarySelectionText = Wayland_GetPrimarySelectionText ;
@@ -1508,6 +1614,22 @@ static bool Wayland_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *d
15081614 return true;
15091615}
15101616
1617+ static bool Wayland_GetDisplayUsableBounds (SDL_VideoDevice * _this , SDL_VideoDisplay * display , SDL_Rect * rect )
1618+ {
1619+ SDL_DisplayData * internal = display -> internal ;
1620+
1621+ if (internal -> ext_zone_v1 ) {
1622+ rect -> x = internal -> x ;
1623+ rect -> y = internal -> y ;
1624+ rect -> w = internal -> zone_width ;
1625+ rect -> h = internal -> zone_height ;
1626+
1627+ return true;
1628+ }
1629+
1630+ return Wayland_GetDisplayBounds (_this , display , rect );
1631+ }
1632+
15111633static void Wayland_VideoCleanup (SDL_VideoDevice * _this )
15121634{
15131635 SDL_VideoData * data = _this -> internal ;
@@ -1642,6 +1764,11 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this)
16421764 data -> xdg_toplevel_icon_manager_v1 = NULL ;
16431765 }
16441766
1767+ if (data -> ext_zone_manager_v1 ) {
1768+ ext_zone_manager_v1_destroy (data -> ext_zone_manager_v1 );
1769+ data -> ext_zone_manager_v1 = NULL ;
1770+ }
1771+
16451772 if (data -> frog_color_management_factory_v1 ) {
16461773 frog_color_management_factory_v1_destroy (data -> frog_color_management_factory_v1 );
16471774 data -> frog_color_management_factory_v1 = NULL ;
0 commit comments