mirror of
https://github.com/labwc/labwc.git
synced 2025-10-29 05:40:24 -04:00
edges: do not apply resistance to invisible edges
This commit is contained in:
parent
551feaca0a
commit
29a26d5ff7
6 changed files with 235 additions and 37 deletions
|
|
@ -3,11 +3,15 @@
|
|||
#define LABWC_EDGES_H
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "common/macros.h"
|
||||
|
||||
struct border;
|
||||
struct output;
|
||||
struct server;
|
||||
struct view;
|
||||
struct wlr_box;
|
||||
|
||||
static inline int
|
||||
clipped_add(int a, int b)
|
||||
|
|
@ -102,7 +106,7 @@ void edges_adjust_geom(struct view *view, struct border edges,
|
|||
|
||||
void edges_find_neighbors(struct border *nearest_edges, struct view *view,
|
||||
struct wlr_box target, struct output *output,
|
||||
edge_validator_t validator, bool use_pending);
|
||||
edge_validator_t validator, bool use_pending, bool ignore_hidden);
|
||||
|
||||
void edges_find_outputs(struct border *nearest_edges, struct view *view,
|
||||
struct wlr_box target, struct output *output,
|
||||
|
|
@ -116,4 +120,5 @@ void edges_adjust_resize_geom(struct view *view, struct border edges,
|
|||
|
||||
bool edges_traverse_edge(struct edge current, struct edge target, struct edge edge);
|
||||
|
||||
void edges_calculate_visibility(struct server *server, struct view *ignored_view);
|
||||
#endif /* LABWC_EDGES_H */
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ struct view {
|
|||
bool tearing_hint;
|
||||
bool visible_on_all_workspaces;
|
||||
enum view_edge tiled;
|
||||
uint32_t edges_visible; /* enum wlr_edges bitset */
|
||||
bool inhibits_keybinds;
|
||||
xkb_layout_index_t keyboard_layout;
|
||||
|
||||
|
|
|
|||
235
src/edges.c
235
src/edges.c
|
|
@ -1,6 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <pixman.h>
|
||||
#include <wlr/util/edges.h>
|
||||
#include <wlr/util/box.h>
|
||||
#include "common/border.h"
|
||||
#include "common/macros.h"
|
||||
|
|
@ -8,6 +10,7 @@
|
|||
#include "edges.h"
|
||||
#include "labwc.h"
|
||||
#include "view.h"
|
||||
#include "node.h"
|
||||
|
||||
static void
|
||||
edges_for_target_geometry(struct border *edges, struct view *view,
|
||||
|
|
@ -35,34 +38,54 @@ edges_initialize(struct border *edges)
|
|||
}
|
||||
|
||||
static inline struct edge
|
||||
build_edge(struct border region, enum view_edge direction, int pad)
|
||||
build_edge(struct border region, enum wlr_edges direction, int pad)
|
||||
{
|
||||
struct edge edge = { 0 };
|
||||
|
||||
switch (direction) {
|
||||
case VIEW_EDGE_LEFT:
|
||||
case WLR_EDGE_LEFT:
|
||||
edge.offset = clipped_sub(region.left, pad);
|
||||
edge.min = region.top;
|
||||
edge.max = region.bottom;
|
||||
break;
|
||||
case VIEW_EDGE_RIGHT:
|
||||
case WLR_EDGE_RIGHT:
|
||||
edge.offset = clipped_add(region.right, pad);
|
||||
edge.min = region.top;
|
||||
edge.max = region.bottom;
|
||||
break;
|
||||
case VIEW_EDGE_UP:
|
||||
case WLR_EDGE_TOP:
|
||||
edge.offset = clipped_sub(region.top, pad);
|
||||
edge.min = region.left;
|
||||
edge.max = region.right;
|
||||
break;
|
||||
case VIEW_EDGE_DOWN:
|
||||
case WLR_EDGE_BOTTOM:
|
||||
edge.offset = clipped_add(region.bottom, pad);
|
||||
edge.min = region.left;
|
||||
edge.max = region.right;
|
||||
break;
|
||||
default:
|
||||
case WLR_EDGE_NONE:
|
||||
/* Should never be reached */
|
||||
assert(false);
|
||||
wlr_log(WLR_ERROR, "invalid direction");
|
||||
abort();
|
||||
}
|
||||
|
||||
return edge;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
is_lesser(enum wlr_edges direction)
|
||||
{
|
||||
return direction == WLR_EDGE_LEFT || direction == WLR_EDGE_TOP;
|
||||
}
|
||||
|
||||
static inline struct edge
|
||||
build_visible_edge(struct border region, enum wlr_edges direction,
|
||||
int pad, uint32_t edges_visible)
|
||||
{
|
||||
struct edge edge = build_edge(region, direction, pad);
|
||||
|
||||
if (!(edges_visible & direction)) {
|
||||
edge.offset = is_lesser(direction) ? INT_MIN : INT_MAX;
|
||||
}
|
||||
|
||||
return edge;
|
||||
|
|
@ -72,7 +95,7 @@ static void
|
|||
validate_single_region_edge(int *valid_edge,
|
||||
struct border view, struct border target,
|
||||
struct border region, edge_validator_t validator,
|
||||
enum view_edge direction)
|
||||
enum wlr_edges direction, uint32_t edges_visible)
|
||||
{
|
||||
/*
|
||||
* When a view snaps to another while moving to its target, it can do
|
||||
|
|
@ -90,42 +113,63 @@ validate_single_region_edge(int *valid_edge,
|
|||
* the region borders for aligned edges only.
|
||||
*/
|
||||
|
||||
bool lesser = direction == VIEW_EDGE_LEFT || direction == VIEW_EDGE_UP;
|
||||
enum wlr_edges opposing = WLR_EDGE_NONE;
|
||||
|
||||
switch (direction) {
|
||||
case WLR_EDGE_TOP:
|
||||
opposing = WLR_EDGE_BOTTOM;
|
||||
break;
|
||||
case WLR_EDGE_BOTTOM:
|
||||
opposing = WLR_EDGE_TOP;
|
||||
break;
|
||||
case WLR_EDGE_LEFT:
|
||||
opposing = WLR_EDGE_RIGHT;
|
||||
break;
|
||||
case WLR_EDGE_RIGHT:
|
||||
opposing = WLR_EDGE_LEFT;
|
||||
break;
|
||||
case WLR_EDGE_NONE:
|
||||
/* Should never be reached */
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
validator(valid_edge,
|
||||
build_edge(view, direction, 0),
|
||||
build_edge(target, direction, 0),
|
||||
build_edge(region, view_edge_invert(direction), 0),
|
||||
build_edge(region, direction, rc.gap), lesser);
|
||||
build_visible_edge(region, opposing, 0, edges_visible),
|
||||
build_visible_edge(region, direction, rc.gap, edges_visible),
|
||||
is_lesser(direction));
|
||||
}
|
||||
|
||||
static void
|
||||
validate_edges(struct border *valid_edges,
|
||||
struct border view, struct border target,
|
||||
struct border region, edge_validator_t validator)
|
||||
struct border region, uint32_t edges_visible,
|
||||
edge_validator_t validator)
|
||||
{
|
||||
/* Check for edges encountered during movement of left edge */
|
||||
validate_single_region_edge(&valid_edges->left,
|
||||
view, target, region, validator, VIEW_EDGE_LEFT);
|
||||
view, target, region, validator, WLR_EDGE_LEFT, edges_visible);
|
||||
|
||||
/* Check for edges encountered during movement of right edge */
|
||||
validate_single_region_edge(&valid_edges->right,
|
||||
view, target, region, validator, VIEW_EDGE_RIGHT);
|
||||
view, target, region, validator, WLR_EDGE_RIGHT, edges_visible);
|
||||
|
||||
/* Check for edges encountered during movement of top edge */
|
||||
validate_single_region_edge(&valid_edges->top,
|
||||
view, target, region, validator, VIEW_EDGE_UP);
|
||||
view, target, region, validator, WLR_EDGE_TOP, edges_visible);
|
||||
|
||||
/* Check for edges encountered during movement of bottom edge */
|
||||
validate_single_region_edge(&valid_edges->bottom,
|
||||
view, target, region, validator, VIEW_EDGE_DOWN);
|
||||
view, target, region, validator, WLR_EDGE_BOTTOM, edges_visible);
|
||||
}
|
||||
|
||||
static void
|
||||
validate_single_output_edge(int *valid_edge,
|
||||
struct border view, struct border target,
|
||||
struct border region, edge_validator_t validator,
|
||||
enum view_edge direction)
|
||||
enum wlr_edges direction)
|
||||
{
|
||||
static struct border unbounded = {
|
||||
.top = INT_MIN,
|
||||
|
|
@ -134,13 +178,11 @@ validate_single_output_edge(int *valid_edge,
|
|||
.left = INT_MIN,
|
||||
};
|
||||
|
||||
bool lesser = direction == VIEW_EDGE_LEFT || direction == VIEW_EDGE_UP;
|
||||
|
||||
validator(valid_edge,
|
||||
build_edge(view, direction, 0),
|
||||
build_edge(target, direction, 0),
|
||||
build_edge(region, direction, 0),
|
||||
build_edge(unbounded, direction, 0), lesser);
|
||||
build_edge(unbounded, direction, 0), is_lesser(direction));
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -182,27 +224,160 @@ validate_output_edges(struct border *valid_edges,
|
|||
/* Left edge encounters a half-infinite region to the left of the output */
|
||||
|
||||
validate_single_output_edge(&valid_edges->left,
|
||||
view, target, output, validator, VIEW_EDGE_LEFT);
|
||||
view, target, output, validator, WLR_EDGE_LEFT);
|
||||
|
||||
/* Right edge encounters a half-infinite region to the right of the output */
|
||||
|
||||
validate_single_output_edge(&valid_edges->right,
|
||||
view, target, output, validator, VIEW_EDGE_RIGHT);
|
||||
view, target, output, validator, WLR_EDGE_RIGHT);
|
||||
|
||||
/* Top edge encounters a half-infinite region above the output */
|
||||
|
||||
validate_single_output_edge(&valid_edges->top,
|
||||
view, target, output, validator, VIEW_EDGE_UP);
|
||||
view, target, output, validator, WLR_EDGE_TOP);
|
||||
|
||||
/* Bottom edge encounters a half-infinite region below the output */
|
||||
validate_single_output_edge(&valid_edges->bottom,
|
||||
view, target, output, validator, VIEW_EDGE_DOWN);
|
||||
view, target, output, validator, WLR_EDGE_BOTTOM);
|
||||
}
|
||||
|
||||
/* Test if parts of the current view is covered by the remaining space in the region */
|
||||
static void
|
||||
subtract_view_from_space(struct view *view, pixman_region32_t *available)
|
||||
{
|
||||
struct wlr_box view_size = ssd_max_extents(view);
|
||||
pixman_box32_t view_rect = {
|
||||
.x1 = view_size.x,
|
||||
.x2 = view_size.x + view_size.width,
|
||||
.y1 = view_size.y,
|
||||
.y2 = view_size.y + view_size.height
|
||||
};
|
||||
|
||||
pixman_region_overlap_t overlap =
|
||||
pixman_region32_contains_rectangle(available, &view_rect);
|
||||
|
||||
switch (overlap) {
|
||||
case PIXMAN_REGION_IN:
|
||||
view->edges_visible = WLR_EDGE_TOP | WLR_EDGE_RIGHT
|
||||
| WLR_EDGE_BOTTOM | WLR_EDGE_LEFT;
|
||||
break;
|
||||
case PIXMAN_REGION_OUT:
|
||||
view->edges_visible = 0;
|
||||
return;
|
||||
case PIXMAN_REGION_PART:
|
||||
; /* works around "a label can only be part of a statement" */
|
||||
pixman_region32_t intersection;
|
||||
pixman_region32_init(&intersection);
|
||||
pixman_region32_intersect_rect(&intersection, available,
|
||||
view_size.x, view_size.y,
|
||||
view_size.width, view_size.height);
|
||||
|
||||
int nrects;
|
||||
const pixman_box32_t *rects =
|
||||
pixman_region32_rectangles(&intersection, &nrects);
|
||||
|
||||
view->edges_visible = 0;
|
||||
for (int i = 0; i < nrects; i++) {
|
||||
if (rects[i].x1 == view_rect.x1) {
|
||||
view->edges_visible |= WLR_EDGE_LEFT;
|
||||
}
|
||||
if (rects[i].y1 == view_rect.y1) {
|
||||
view->edges_visible |= WLR_EDGE_TOP;
|
||||
}
|
||||
if (rects[i].x2 == view_rect.x2) {
|
||||
view->edges_visible |= WLR_EDGE_RIGHT;
|
||||
}
|
||||
if (rects[i].y2 == view_rect.y2) {
|
||||
view->edges_visible |= WLR_EDGE_BOTTOM;
|
||||
}
|
||||
}
|
||||
pixman_region32_fini(&intersection);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Subtract the view geometry from the available region for the next check */
|
||||
pixman_region32_t view_region;
|
||||
pixman_region32_init_rects(&view_region, &view_rect, 1);
|
||||
pixman_region32_subtract(available, available, &view_region);
|
||||
pixman_region32_fini(&view_region);
|
||||
}
|
||||
|
||||
static void
|
||||
subtract_node_tree(struct wlr_scene_tree *tree, pixman_region32_t *available,
|
||||
struct view *ignored_view)
|
||||
{
|
||||
struct view *view;
|
||||
struct wlr_scene_node *node;
|
||||
struct node_descriptor *node_desc;
|
||||
wl_list_for_each_reverse(node, &tree->children, link) {
|
||||
if (!node->enabled) {
|
||||
/*
|
||||
* This skips everything that is not being
|
||||
* rendered, including minimized / unmapped
|
||||
* windows and workspaces other than the
|
||||
* current one.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
|
||||
node_desc = node->data;
|
||||
if (node_desc && node_desc->type == LAB_NODE_DESC_VIEW) {
|
||||
view = node_view_from_node(node);
|
||||
if (view != ignored_view) {
|
||||
subtract_view_from_space(view, available);
|
||||
}
|
||||
} else if (node->type == WLR_SCENE_NODE_TREE) {
|
||||
subtract_node_tree(wlr_scene_tree_from_node(node),
|
||||
available, ignored_view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
edges_calculate_visibility(struct server *server, struct view *ignored_view)
|
||||
{
|
||||
/*
|
||||
* The region stores the available output layout space
|
||||
* and subtracts the window geometries in reverse rendering
|
||||
* order, e.g. a window rendered on top is subtracted first.
|
||||
*
|
||||
* This allows to detect if a window is actually visible.
|
||||
* If there is no overlap of its geometry and the remaining
|
||||
* region it must be completely covered by other windows.
|
||||
*
|
||||
*/
|
||||
pixman_region32_t region;
|
||||
pixman_region32_init(®ion);
|
||||
|
||||
/*
|
||||
* Initialize the region with each individual output.
|
||||
*
|
||||
* If we were to use NULL for the reference output we
|
||||
* would get a single combined wlr_box of the whole
|
||||
* layout which could cover actual invisible areas
|
||||
* in case the output resolutions differ.
|
||||
*/
|
||||
struct output *output;
|
||||
struct wlr_box layout_box;
|
||||
wl_list_for_each(output, &server->outputs, link) {
|
||||
if (!output_is_usable(output)) {
|
||||
continue;
|
||||
}
|
||||
wlr_output_layout_get_box(server->output_layout,
|
||||
output->wlr_output, &layout_box);
|
||||
pixman_region32_union_rect(®ion, ®ion,
|
||||
layout_box.x, layout_box.y, layout_box.width, layout_box.height);
|
||||
}
|
||||
|
||||
subtract_node_tree(&server->scene->tree, ®ion, ignored_view);
|
||||
|
||||
pixman_region32_fini(®ion);
|
||||
}
|
||||
|
||||
void
|
||||
edges_find_neighbors(struct border *nearest_edges, struct view *view,
|
||||
struct wlr_box target, struct output *output,
|
||||
edge_validator_t validator, bool use_pending)
|
||||
edge_validator_t validator, bool use_pending, bool ignore_hidden)
|
||||
{
|
||||
assert(view);
|
||||
assert(validator);
|
||||
|
|
@ -223,6 +398,14 @@ edges_find_neighbors(struct border *nearest_edges, struct view *view,
|
|||
continue;
|
||||
}
|
||||
|
||||
uint32_t edges_visible = ignore_hidden ? v->edges_visible :
|
||||
WLR_EDGE_TOP | WLR_EDGE_LEFT
|
||||
| WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT;
|
||||
|
||||
if (edges_visible == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (output && v->output != output) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -252,7 +435,7 @@ edges_find_neighbors(struct border *nearest_edges, struct view *view,
|
|||
};
|
||||
|
||||
validate_edges(nearest_edges, view_edges,
|
||||
target_edges, win_edges, validator);
|
||||
target_edges, win_edges, edges_visible, validator);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include "edges.h"
|
||||
#include "input/keyboard.h"
|
||||
#include "labwc.h"
|
||||
#include "regions.h"
|
||||
|
|
@ -123,6 +124,9 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
|
|||
if (rc.resize_indicator) {
|
||||
resize_indicator_show(view);
|
||||
}
|
||||
if (rc.window_edge_strength) {
|
||||
edges_calculate_visibility(server, view);
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if view was snapped to any edge */
|
||||
|
|
|
|||
|
|
@ -114,8 +114,9 @@ resistance_move_apply(struct view *view, double *x, double *y)
|
|||
|
||||
if (rc.window_edge_strength != 0) {
|
||||
/* Find any relevant window edges encountered by this move */
|
||||
edges_find_neighbors(&next_edges, view, target, NULL,
|
||||
check_edge_window, /* use_pending */ false);
|
||||
edges_find_neighbors(&next_edges,
|
||||
view, target, NULL, check_edge_window,
|
||||
/* use_pending */ false, /* ignore_hidden */ true);
|
||||
}
|
||||
|
||||
/* If any "best" edges were encountered during this move, snap motion */
|
||||
|
|
@ -143,8 +144,9 @@ resistance_resize_apply(struct view *view, struct wlr_box *new_geom)
|
|||
|
||||
if (rc.window_edge_strength != 0) {
|
||||
/* Find any relevant window edges encountered by this move */
|
||||
edges_find_neighbors(&next_edges, view, *new_geom, NULL,
|
||||
check_edge_window, /* use_pending */ false);
|
||||
edges_find_neighbors(&next_edges,
|
||||
view, *new_geom, NULL, check_edge_window,
|
||||
/* use_pending */ false, /* ignore_hidden */ true);
|
||||
}
|
||||
|
||||
/* If any "best" edges were encountered during this move, snap motion */
|
||||
|
|
|
|||
15
src/snap.c
15
src/snap.c
|
|
@ -121,8 +121,9 @@ snap_move_to_edge(struct view *view, enum view_edge direction,
|
|||
struct border next_edges;
|
||||
edges_initialize(&next_edges);
|
||||
|
||||
edges_find_neighbors(&next_edges, view, target,
|
||||
output, check_edge, /* use_pending */ true);
|
||||
edges_find_neighbors(&next_edges,
|
||||
view, target, output, check_edge,
|
||||
/* use_pending */ true, /* ignore_hidden */ false);
|
||||
|
||||
/* If any "best" edges were encountered, limit motion */
|
||||
edges_adjust_move_coords(view, next_edges,
|
||||
|
|
@ -196,8 +197,9 @@ snap_grow_to_next_edge(struct view *view, enum view_edge direction,
|
|||
edges_initialize(&next_edges);
|
||||
|
||||
/* Limit motion to any intervening edge of other views on this output */
|
||||
edges_find_neighbors(&next_edges, view, *geo,
|
||||
output, check_edge, /* use_pending */ true);
|
||||
edges_find_neighbors(&next_edges,
|
||||
view, *geo, output, check_edge,
|
||||
/* use_pending */ true, /* ignore_hidden */ false);
|
||||
edges_adjust_resize_geom(view, next_edges,
|
||||
resize_edges, geo, /* use_pending */ true);
|
||||
}
|
||||
|
|
@ -255,8 +257,9 @@ snap_shrink_to_next_edge(struct view *view, enum view_edge direction,
|
|||
view->output, check_edge, /* use_pending */ true);
|
||||
|
||||
/* Limit motion to any intervening edge of ther views on this output */
|
||||
edges_find_neighbors(&next_edges, view, *geo,
|
||||
view->output, check_edge, /* use_pending */ true);
|
||||
edges_find_neighbors(&next_edges,
|
||||
view, *geo, view->output, check_edge,
|
||||
/* use_pending */ true, /* ignore_hidden */ false);
|
||||
|
||||
edges_adjust_resize_geom(view, next_edges,
|
||||
resize_edges, geo, /* use_pending */ true);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue