Skip to content

Commit edd0877

Browse files
committed
wayland: Add xdg_toplevel v7 edge constraint support
If a window isn't resizable from specific directions, the compositor can inform clients of the current edge constraints, so they don't display resize cursors for non-resizable edges.
1 parent 113475a commit edd0877

File tree

5 files changed

+150
-17
lines changed

5 files changed

+150
-17
lines changed

src/video/wayland/SDL_waylandevents.c

+67
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,73 @@ static void pointer_handle_motion(void *data, struct wl_pointer *pointer,
599599

600600
if (window->hit_test) {
601601
SDL_HitTestResult rc = window->hit_test(window, &seat->pointer.last_motion, window->hit_test_data);
602+
603+
// Apply the toplevel constraints if the window isn't resizable from those directions.
604+
switch (rc) {
605+
case SDL_HITTEST_RESIZE_TOPLEFT:
606+
if ((window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_TOP) &&
607+
(window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_LEFT)) {
608+
rc = SDL_HITTEST_NORMAL;
609+
} else if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_TOP) {
610+
rc = SDL_HITTEST_RESIZE_LEFT;
611+
} else if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_LEFT) {
612+
rc = SDL_HITTEST_RESIZE_TOP;
613+
}
614+
break;
615+
case SDL_HITTEST_RESIZE_TOP:
616+
if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_TOP) {
617+
rc = SDL_HITTEST_NORMAL;
618+
}
619+
break;
620+
case SDL_HITTEST_RESIZE_TOPRIGHT:
621+
if ((window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_TOP) &&
622+
(window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_RIGHT)) {
623+
rc = SDL_HITTEST_NORMAL;
624+
} else if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_TOP) {
625+
rc = SDL_HITTEST_RESIZE_RIGHT;
626+
} else if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_RIGHT) {
627+
rc = SDL_HITTEST_RESIZE_TOP;
628+
}
629+
break;
630+
case SDL_HITTEST_RESIZE_RIGHT:
631+
if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_RIGHT) {
632+
rc = SDL_HITTEST_NORMAL;
633+
}
634+
break;
635+
case SDL_HITTEST_RESIZE_BOTTOMRIGHT:
636+
if ((window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_BOTTOM) &&
637+
(window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_RIGHT)) {
638+
rc = SDL_HITTEST_NORMAL;
639+
} else if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_BOTTOM) {
640+
rc = SDL_HITTEST_RESIZE_RIGHT;
641+
} else if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_RIGHT) {
642+
rc = SDL_HITTEST_RESIZE_BOTTOM;
643+
}
644+
break;
645+
case SDL_HITTEST_RESIZE_BOTTOM:
646+
if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_BOTTOM) {
647+
rc = SDL_HITTEST_NORMAL;
648+
}
649+
break;
650+
case SDL_HITTEST_RESIZE_BOTTOMLEFT:
651+
if ((window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_BOTTOM) &&
652+
(window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_LEFT)) {
653+
rc = SDL_HITTEST_NORMAL;
654+
} else if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_BOTTOM) {
655+
rc = SDL_HITTEST_RESIZE_LEFT;
656+
} else if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_LEFT) {
657+
rc = SDL_HITTEST_RESIZE_BOTTOM;
658+
}
659+
break;
660+
case SDL_HITTEST_RESIZE_LEFT:
661+
if (window_data->toplevel_constraints & WAYLAND_TOPLEVEL_CONSTRAINED_LEFT) {
662+
rc = SDL_HITTEST_NORMAL;
663+
}
664+
break;
665+
default:
666+
break;
667+
}
668+
602669
if (rc != window_data->hit_test_result) {
603670
window_data->hit_test_result = rc;
604671
Wayland_SeatUpdateCursor(seat);

src/video/wayland/SDL_waylandvideo.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1253,7 +1253,7 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint
12531253
struct wl_seat *seat = wl_registry_bind(d->registry, id, &wl_seat_interface, SDL_min(SDL_WL_SEAT_VERSION, version));
12541254
Wayland_DisplayCreateSeat(d, seat, id);
12551255
} else if (SDL_strcmp(interface, "xdg_wm_base") == 0) {
1256-
d->shell.xdg = wl_registry_bind(d->registry, id, &xdg_wm_base_interface, SDL_min(version, 6));
1256+
d->shell.xdg = wl_registry_bind(d->registry, id, &xdg_wm_base_interface, SDL_min(version, 7));
12571257
xdg_wm_base_add_listener(d->shell.xdg, &shell_listener_xdg, NULL);
12581258
} else if (SDL_strcmp(interface, "wl_shm") == 0) {
12591259
d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);

src/video/wayland/SDL_waylandwindow.c

+14
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,7 @@ static void handle_configure_xdg_toplevel(void *data,
774774
bool active = false;
775775
bool resizing = false;
776776
bool suspended = false;
777+
wind->toplevel_constraints = 0;
777778
wl_array_for_each (state, states) {
778779
switch (*state) {
779780
case XDG_TOPLEVEL_STATE_FULLSCREEN:
@@ -800,6 +801,18 @@ static void handle_configure_xdg_toplevel(void *data,
800801
case XDG_TOPLEVEL_STATE_SUSPENDED:
801802
suspended = true;
802803
break;
804+
case XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT:
805+
wind->toplevel_constraints |= WAYLAND_TOPLEVEL_CONSTRAINED_LEFT;
806+
break;
807+
case XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT:
808+
wind->toplevel_constraints |= WAYLAND_TOPLEVEL_CONSTRAINED_RIGHT;
809+
break;
810+
case XDG_TOPLEVEL_STATE_CONSTRAINED_TOP:
811+
wind->toplevel_constraints |= WAYLAND_TOPLEVEL_CONSTRAINED_TOP;
812+
break;
813+
case XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM:
814+
wind->toplevel_constraints |= WAYLAND_TOPLEVEL_CONSTRAINED_BOTTOM;
815+
break;
803816
default:
804817
break;
805818
}
@@ -1205,6 +1218,7 @@ static void decoration_frame_configure(struct libdecor_frame *frame,
12051218
#if SDL_LIBDECOR_CHECK_VERSION(0, 3, 0)
12061219
resizing = (window_state & LIBDECOR_WINDOW_STATE_RESIZING) != 0;
12071220
#endif
1221+
// TODO: Toplevel constraint passthrough is waiting on upstream libdecor changes.
12081222
}
12091223
const bool floating = !(fullscreen || maximized || tiled);
12101224

src/video/wayland/SDL_waylandwindow.h

+7
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,13 @@ struct SDL_WindowData
9595
WAYLAND_WM_CAPS_FULLSCREEN |
9696
WAYLAND_WM_CAPS_MINIMIZE
9797
} wm_caps;
98+
enum
99+
{
100+
WAYLAND_TOPLEVEL_CONSTRAINED_LEFT = 0x01,
101+
WAYLAND_TOPLEVEL_CONSTRAINED_RIGHT = 0x02,
102+
WAYLAND_TOPLEVEL_CONSTRAINED_TOP = 0x04,
103+
WAYLAND_TOPLEVEL_CONSTRAINED_BOTTOM = 0x08
104+
} toplevel_constraints;
98105

99106
struct wl_egl_window *egl_window;
100107
#ifdef SDL_VIDEO_OPENGL_EGL

wayland-protocols/xdg-shell.xml

+61-16
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
DEALINGS IN THE SOFTWARE.
3030
</copyright>
3131

32-
<interface name="xdg_wm_base" version="6">
32+
<interface name="xdg_wm_base" version="7">
3333
<description summary="create desktop-style surfaces">
3434
The xdg_wm_base interface is exposed as a global object enabling clients
3535
to turn their wl_surfaces into windows in a desktop environment. It
@@ -122,7 +122,7 @@
122122
</event>
123123
</interface>
124124

125-
<interface name="xdg_positioner" version="6">
125+
<interface name="xdg_positioner" version="7">
126126
<description summary="child surface positioner">
127127
The xdg_positioner provides a collection of rules for the placement of a
128128
child surface relative to a parent surface. Rules can be defined to ensure
@@ -344,7 +344,7 @@
344344

345345
The default adjustment is none.
346346
</description>
347-
<arg name="constraint_adjustment" type="uint"
347+
<arg name="constraint_adjustment" type="uint" enum="constraint_adjustment"
348348
summary="bit mask of constraint adjustments"/>
349349
</request>
350350

@@ -407,7 +407,7 @@
407407
</request>
408408
</interface>
409409

410-
<interface name="xdg_surface" version="6">
410+
<interface name="xdg_surface" version="7">
411411
<description summary="desktop user interface surface base interface">
412412
An interface that may be implemented by a wl_surface, for
413413
implementations that provide a desktop-style user interface.
@@ -434,7 +434,8 @@
434434
manipulate a buffer prior to the first xdg_surface.configure call must
435435
also be treated as errors.
436436

437-
After creating a role-specific object and setting it up, the client must
437+
After creating a role-specific object and setting it up (e.g. by sending
438+
the title, app ID, size constraints, parent, etc), the client must
438439
perform an initial commit without any buffer attached. The compositor
439440
will reply with initial wl_surface state such as
440441
wl_surface.preferred_buffer_scale followed by an xdg_surface.configure
@@ -515,8 +516,7 @@
515516
portions like drop-shadows which should be ignored for the
516517
purposes of aligning, placing and constraining windows.
517518

518-
The window geometry is double buffered, and will be applied at the
519-
time wl_surface.commit of the corresponding wl_surface is called.
519+
The window geometry is double-buffered state, see wl_surface.commit.
520520

521521
When maintaining a position, the compositor should treat the (x, y)
522522
coordinate of the window geometry as the top left corner of the window.
@@ -617,21 +617,25 @@
617617

618618
</interface>
619619

620-
<interface name="xdg_toplevel" version="6">
620+
<interface name="xdg_toplevel" version="7">
621621
<description summary="toplevel surface">
622622
This interface defines an xdg_surface role which allows a surface to,
623623
among other things, set window-like properties such as maximize,
624624
fullscreen, and minimize, set application-specific metadata like title and
625625
id, and well as trigger user interactive operations such as interactive
626626
resize and move.
627627

628+
A xdg_toplevel by default is responsible for providing the full intended
629+
visual representation of the toplevel, which depending on the window
630+
state, may mean things like a title bar, window controls and drop shadow.
631+
628632
Unmapping an xdg_toplevel means that the surface cannot be shown
629633
by the compositor until it is explicitly mapped again.
630634
All active operations (e.g., move, resize) are canceled and all
631635
attributes (e.g. title, state, stacking, ...) are discarded for
632636
an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to
633637
the state it had right after xdg_surface.get_toplevel. The client
634-
can re-map the toplevel by perfoming a commit without any buffer
638+
can re-map the toplevel by performing a commit without any buffer
635639
attached, waiting for a configure event and handling it as usual (see
636640
xdg_surface description).
637641

@@ -828,8 +832,7 @@
828832
configure event to ensure that both the client and the compositor
829833
setting the state can be synchronized.
830834

831-
States set in this way are double-buffered. They will get applied on
832-
the next commit.
835+
States set in this way are double-buffered, see wl_surface.commit.
833836
</description>
834837
<entry name="maximized" value="1" summary="the surface is maximized">
835838
<description summary="the surface is maximized">
@@ -869,24 +872,36 @@
869872
<description summary="the surface’s left edge is tiled">
870873
The window is currently in a tiled layout and the left edge is
871874
considered to be adjacent to another part of the tiling grid.
875+
876+
The client should draw without shadow or other decoration outside of
877+
the window geometry on the left edge.
872878
</description>
873879
</entry>
874880
<entry name="tiled_right" value="6" since="2">
875881
<description summary="the surface’s right edge is tiled">
876882
The window is currently in a tiled layout and the right edge is
877883
considered to be adjacent to another part of the tiling grid.
884+
885+
The client should draw without shadow or other decoration outside of
886+
the window geometry on the right edge.
878887
</description>
879888
</entry>
880889
<entry name="tiled_top" value="7" since="2">
881890
<description summary="the surface’s top edge is tiled">
882891
The window is currently in a tiled layout and the top edge is
883892
considered to be adjacent to another part of the tiling grid.
893+
894+
The client should draw without shadow or other decoration outside of
895+
the window geometry on the top edge.
884896
</description>
885897
</entry>
886898
<entry name="tiled_bottom" value="8" since="2">
887899
<description summary="the surface’s bottom edge is tiled">
888900
The window is currently in a tiled layout and the bottom edge is
889901
considered to be adjacent to another part of the tiling grid.
902+
903+
The client should draw without shadow or other decoration outside of
904+
the window geometry on the bottom edge.
890905
</description>
891906
</entry>
892907
<entry name="suspended" value="9" since="6">
@@ -896,6 +911,38 @@
896911
outputs are switched off due to screen locking.
897912
</description>
898913
</entry>
914+
<entry name="constrained_left" value="10" since="7">
915+
<description summary="the surface’s left edge is constrained">
916+
The left edge of the window is currently constrained, meaning it
917+
shouldn't attempt to resize from that edge. It can for example mean
918+
it's tiled next to a monitor edge on the constrained side of the
919+
window.
920+
</description>
921+
</entry>
922+
<entry name="constrained_right" value="11" since="7">
923+
<description summary="the surface’s right edge is constrained">
924+
The right edge of the window is currently constrained, meaning it
925+
shouldn't attempt to resize from that edge. It can for example mean
926+
it's tiled next to a monitor edge on the constrained side of the
927+
window.
928+
</description>
929+
</entry>
930+
<entry name="constrained_top" value="12" since="7">
931+
<description summary="the surface’s top edge is constrained">
932+
The top edge of the window is currently constrained, meaning it
933+
shouldn't attempt to resize from that edge. It can for example mean
934+
it's tiled next to a monitor edge on the constrained side of the
935+
window.
936+
</description>
937+
</entry>
938+
<entry name="constrained_bottom" value="13" since="7">
939+
<description summary="the surface’s bottom edge is tiled">
940+
The bottom edge of the window is currently constrained, meaning it
941+
shouldn't attempt to resize from that edge. It can for example mean
942+
it's tiled next to a monitor edge on the constrained side of the
943+
window.
944+
</description>
945+
</entry>
899946
</enum>
900947

901948
<request name="set_max_size">
@@ -908,8 +955,7 @@
908955
The width and height arguments are in window geometry coordinates.
909956
See xdg_surface.set_window_geometry.
910957

911-
Values set in this way are double-buffered. They will get applied
912-
on the next commit.
958+
Values set in this way are double-buffered, see wl_surface.commit.
913959

914960
The compositor can use this information to allow or disallow
915961
different states like maximize or fullscreen and draw accurate
@@ -949,8 +995,7 @@
949995
The width and height arguments are in window geometry coordinates.
950996
See xdg_surface.set_window_geometry.
951997

952-
Values set in this way are double-buffered. They will get applied
953-
on the next commit.
998+
Values set in this way are double-buffered, see wl_surface.commit.
954999

9551000
The compositor can use this information to allow or disallow
9561001
different states like maximize or fullscreen and draw accurate
@@ -1194,7 +1239,7 @@
11941239
</event>
11951240
</interface>
11961241

1197-
<interface name="xdg_popup" version="6">
1242+
<interface name="xdg_popup" version="7">
11981243
<description summary="short-lived, popup surfaces for menus">
11991244
A popup surface is a short-lived, temporary surface. It can be used to
12001245
implement for example menus, popovers, tooltips and other similar user

0 commit comments

Comments
 (0)