xdg: add snapping.notifyClient option to control tiling events

This commit is contained in:
Andrew J. Hesford 2024-01-22 22:11:53 -05:00
parent 9f51384b6a
commit 3162bbb3c2
7 changed files with 100 additions and 41 deletions

View file

@ -236,16 +236,44 @@ this is for compatibility with Openbox.
## WINDOW SNAPPING
The following two options relate to triggering window actions when moving
windows using the mouse.
Windows may be "snapped" to an edge or user-defined region of an output when
activated with SnapToEdge actions or, optionally, by dragging windows to the
edges of an output. Edge snapping causes a window to occupy half of its output,
extending outward from the snapped edge.
*<snapping><range>*
The distance in pixels from the edge of an output for window Move
operations to trigger SnapToEdge. A range of 0 disables window snapping.
Default is 1.
If an interactive move ends with the cursor a maximum distance *range*,
(in pixels) from the edge of an output, the move will trigger a
SnapToEdge action for that edge. A *range* of 0 disables snapping via
interactive moves. Default is 1.
*<snapping><topMaximize>* [yes|no]
Maximize window if Move operation ends on the top edge. Default is yes.
If *yes*, an interactive move that snaps a window to the top edge will
maximize the window. If *no*, snapping will behave as it does with other
edges, causing the window to occupy the top half of an output. Default
is yes.
*<snapping><notifyClient>* [always|region|edge|never]
Snapping windows can trigger corresponding tiling events for native
Wayland clients. Clients may use these events to alter their rendering
based on knowledge that some edges of the view are confined to edges of
a snapping region or output. For example, rounded corners may become
square when tiled, or media players may letter-box or pillar-box video
rather than imposing rigid aspect ratios on windows that will violate
the constraints of window snapping.
- When *always* is specified, any window that is snapped to either an
output edge or a user-defined region will receive a tiling event.
- When *region* is specified, only windows snapped to a user-defined
region will receive an event.
- When *edge* is specified, only windows snapped to an output edge will
receive an event.
- When *never* is specified, tiling events will never be triggered.
The default is "always".
## REGIONS

View file

@ -78,10 +78,11 @@
<raiseOnFocus>no</raiseOnFocus>
</focus>
<!-- Set range to 0 to disable window snapping completely -->
<snapping>
<!-- Set range to 0 to disable window snapping completely -->
<range>1</range>
<topMaximize>yes</topMaximize>
<notifyClient>always</notifyClient>
</snapping>
<!--

View file

@ -35,6 +35,14 @@ enum adaptive_sync_mode {
LAB_ADAPTIVE_SYNC_FULLSCREEN,
};
enum tiling_events_mode {
LAB_TILING_EVENTS_NEVER = 0,
LAB_TILING_EVENTS_REGION = 1 << 0,
LAB_TILING_EVENTS_EDGE = 1 << 1,
LAB_TILING_EVENTS_ALWAYS =
(LAB_TILING_EVENTS_REGION | LAB_TILING_EVENTS_EDGE),
};
struct usable_area_override {
struct border margin;
char *output;
@ -114,6 +122,7 @@ struct rcxml {
/* window snapping */
int snap_edge_range;
bool snap_top_maximize;
enum tiling_events_mode snap_tiling_events_mode;
enum resize_indicator_mode resize_indicator;

View file

@ -94,7 +94,7 @@ struct view_impl {
void (*map)(struct view *view);
void (*set_activated)(struct view *view, bool activated);
void (*set_fullscreen)(struct view *view, bool fullscreen);
void (*set_tiled)(struct view *view);
void (*notify_tiled)(struct view *view);
/*
* client_request is true if the client unmapped its own
* surface; false if we are just minimizing the view. The two

View file

@ -835,6 +835,18 @@ entry(xmlNode *node, char *nodename, char *content)
rc.snap_edge_range = atoi(content);
} else if (!strcasecmp(nodename, "topMaximize.snapping")) {
set_bool(content, &rc.snap_top_maximize);
} else if (!strcasecmp(nodename, "notifyClient.snapping")) {
if (!strcasecmp(content, "always")) {
rc.snap_tiling_events_mode = LAB_TILING_EVENTS_ALWAYS;
} else if (!strcasecmp(content, "region")) {
rc.snap_tiling_events_mode = LAB_TILING_EVENTS_REGION;
} else if (!strcasecmp(content, "edge")) {
rc.snap_tiling_events_mode = LAB_TILING_EVENTS_EDGE;
} else if (!strcasecmp(content, "never")) {
rc.snap_tiling_events_mode = LAB_TILING_EVENTS_NEVER;
} else {
wlr_log(WLR_ERROR, "ignoring invalid value for notifyClient");
}
/* <windowSwitcher show="" preview="" outlines="" /> */
} else if (!strcasecmp(nodename, "show.windowSwitcher")) {
@ -1092,6 +1104,7 @@ rcxml_init(void)
rc.snap_edge_range = 1;
rc.snap_top_maximize = true;
rc.snap_tiling_events_mode = LAB_TILING_EVENTS_ALWAYS;
rc.window_switcher.show = true;
rc.window_switcher.preview = true;

View file

@ -1043,6 +1043,15 @@ view_is_floating(struct view *view)
|| view_is_tiled(view));
}
static void
view_notify_tiled(struct view *view)
{
assert(view);
if (view->impl->notify_tiled) {
view->impl->notify_tiled(view);
}
}
/* Reset tiled state of view without changing geometry */
void
view_set_untiled(struct view *view)
@ -1051,10 +1060,7 @@ view_set_untiled(struct view *view)
view->tiled = VIEW_EDGE_INVALID;
view->tiled_region = NULL;
zfree(view->tiled_region_evacuate);
if (view->impl->set_tiled) {
view->impl->set_tiled(view);
}
view_notify_tiled(view);
}
void
@ -1775,11 +1781,7 @@ view_snap_to_edge(struct view *view, enum view_edge edge,
view_set_untiled(view);
view_set_output(view, output);
view->tiled = edge;
if (view->impl->set_tiled) {
view->impl->set_tiled(view);
}
view_notify_tiled(view);
view_apply_tiled_geometry(view);
}
@ -1813,11 +1815,7 @@ view_snap_to_region(struct view *view, struct region *region,
}
view_set_untiled(view);
view->tiled_region = region;
if (view->impl->set_tiled) {
view->impl->set_tiled(view);
}
view_notify_tiled(view);
view_apply_region_geometry(view);
}

View file

@ -424,32 +424,42 @@ xdg_toplevel_view_set_fullscreen(struct view *view, bool fullscreen)
}
static void
xdg_toplevel_view_set_tiled(struct view *view)
xdg_toplevel_view_notify_tiled(struct view *view)
{
enum wlr_edges edge;
/* Take no action if xdg-shell tiling is disabled */
if (rc.snap_tiling_events_mode == LAB_TILING_EVENTS_NEVER) {
return;
}
enum wlr_edges edge = WLR_EDGE_NONE;
bool want_edge = rc.snap_tiling_events_mode & LAB_TILING_EVENTS_EDGE;
bool want_region = rc.snap_tiling_events_mode & LAB_TILING_EVENTS_REGION;
/*
* Edge-snapped view are considered tiled on the snapped edge and those
* perpendicular to it.
*/
switch (view->tiled) {
case VIEW_EDGE_LEFT:
edge = WLR_EDGE_LEFT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM;
break;
case VIEW_EDGE_RIGHT:
edge = WLR_EDGE_RIGHT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM;
break;
case VIEW_EDGE_UP:
edge = WLR_EDGE_TOP | WLR_EDGE_LEFT | WLR_EDGE_RIGHT;
break;
case VIEW_EDGE_DOWN:
edge = WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT;
break;
default:
edge = WLR_EDGE_NONE;
if (want_edge) {
switch (view->tiled) {
case VIEW_EDGE_LEFT:
edge = WLR_EDGE_LEFT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM;
break;
case VIEW_EDGE_RIGHT:
edge = WLR_EDGE_RIGHT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM;
break;
case VIEW_EDGE_UP:
edge = WLR_EDGE_TOP | WLR_EDGE_LEFT | WLR_EDGE_RIGHT;
break;
case VIEW_EDGE_DOWN:
edge = WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT;
break;
default:
edge = WLR_EDGE_NONE;
}
}
if (view->tiled_region) {
if (want_region && view->tiled_region) {
/* Region-snapped views are considered tiled on all edges */
edge = WLR_EDGE_LEFT | WLR_EDGE_RIGHT |
WLR_EDGE_TOP | WLR_EDGE_BOTTOM;
@ -629,7 +639,7 @@ static const struct view_impl xdg_toplevel_view_impl = {
.map = xdg_toplevel_view_map,
.set_activated = xdg_toplevel_view_set_activated,
.set_fullscreen = xdg_toplevel_view_set_fullscreen,
.set_tiled = xdg_toplevel_view_set_tiled,
.notify_tiled = xdg_toplevel_view_notify_tiled,
.unmap = xdg_toplevel_view_unmap,
.maximize = xdg_toplevel_view_maximize,
.minimize = xdg_toplevel_view_minimize,