## 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
<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>
<!--
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;
/* window snapping */
int snap_edge_range;
bool snap_top_maximize;
+ enum tiling_events_mode snap_tiling_events_mode;
enum resize_indicator_mode resize_indicator;
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
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")) {
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_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)
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
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);
}
}
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);
}
}
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;
.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,