diff --git a/include/labwc.h b/include/labwc.h index 83f37dca..da07a3e1 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -47,6 +47,7 @@ #include "config/rcxml.h" #include "input/cursor.h" #include "input/ime.h" +#include "overlay.h" #include "regions.h" #include "session-lock.h" #if HAVE_NLS @@ -159,9 +160,8 @@ struct seat { struct wlr_scene_tree *icons; } drag; - /* Private use by regions.c */ - struct region *region_active; - struct region_overlay region_overlay; + /* Private use by overlay.c */ + struct overlay overlay; /* Used to prevent region snapping when starting a move with A-Left */ bool region_prevent_snap; @@ -479,6 +479,8 @@ void seat_output_layout_changed(struct seat *seat); void interactive_begin(struct view *view, enum input_mode mode, uint32_t edges); void interactive_finish(struct view *view); void interactive_cancel(struct view *view); +/* Possibly returns VIEW_EDGE_CENTER if is yes */ +enum view_edge edge_from_cursor(struct seat *seat, struct output **dest_output); void output_init(struct server *server); void output_manager_init(struct server *server); diff --git a/include/overlay.h b/include/overlay.h new file mode 100644 index 00000000..e5035c7b --- /dev/null +++ b/include/overlay.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_OVERLAY_H +#define LABWC_OVERLAY_H + +#include +#include "common/graphic-helpers.h" + +struct overlay { + struct wlr_scene_tree *tree; + union { + struct wlr_scene_rect *rect; + struct multi_rect *pixman_rect; + }; + + /* For delayed overlay */ + struct view *view; + struct wlr_box box; + struct wl_event_source *timer; +}; + +/* Calls overlay_hide() internally if the view is not to be snapped */ +void overlay_show(struct seat *seat, struct view *view); +void overlay_hide(struct seat *seat); + +#endif diff --git a/include/regions.h b/include/regions.h index 6638f9f3..abe472be 100644 --- a/include/regions.h +++ b/include/regions.h @@ -25,14 +25,6 @@ struct region { } center; }; -struct region_overlay { - struct wlr_scene_tree *tree; - union { - struct wlr_scene_rect *overlay; - struct multi_rect *pixman_overlay; - }; -}; - /* Returns true if we should show the region overlay or snap to region */ bool regions_should_snap(struct server *server); @@ -73,7 +65,4 @@ void regions_destroy(struct seat *seat, struct wl_list *regions); struct region *regions_from_cursor(struct server *server); struct region *regions_from_name(const char *region_name, struct output *output); -void regions_show_overlay(struct view *view, struct seat *seat, struct region *region); -void regions_hide_overlay(struct seat *seat); - #endif /* LABWC_REGIONS_H */ diff --git a/src/debug.c b/src/debug.c index d8b9c179..3b79f7ac 100644 --- a/src/debug.c +++ b/src/debug.c @@ -136,10 +136,10 @@ get_special(struct server *server, struct wlr_scene_node *node) if (node == &server->seat.drag.icons->node) { return "seat->drag.icons"; } - if (server->seat.region_overlay.tree - && node == &server->seat.region_overlay.tree->node) { + if (server->seat.overlay.tree + && node == &server->seat.overlay.tree->node) { /* Created on-demand */ - return "seat->region_overlay"; + return "seat->overlay"; } if (server->seat.input_method_relay->popup_tree && node == &server->seat.input_method_relay->popup_tree->node) { diff --git a/src/input/cursor.c b/src/input/cursor.c index eb359e71..04334389 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -225,16 +225,7 @@ process_cursor_move(struct server *server, uint32_t time) resistance_move_apply(view, &dx, &dy); view_move(view, dx, dy); - /* Region overlay */ - if (!regions_should_snap(server)) { - return; - } - struct region *region = regions_from_cursor(server); - if (region) { - regions_show_overlay(view, &server->seat, region); - } else { - regions_hide_overlay(&server->seat); - } + overlay_show(&server->seat, view); } static void diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 0ca6d390..fb7ac204 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -82,9 +82,9 @@ keyboard_modifiers_notify(struct wl_listener *listener, void *data) seat->region_prevent_snap = false; } - if (server->osd_state.cycle_view || server->grabbed_view + if (server->osd_state.cycle_view || seat->workspace_osd_shown_by_modifier) { - if (!keyboard_any_modifiers_pressed(wlr_keyboard)) { + if (!keyboard_any_modifiers_pressed(wlr_keyboard)) { if (server->osd_state.cycle_view) { if (key_state_nr_bound_keys()) { should_cancel_cycling_on_next_key_release = true; @@ -95,11 +95,12 @@ keyboard_modifiers_notify(struct wl_listener *listener, void *data) if (seat->workspace_osd_shown_by_modifier) { workspaces_osd_hide(seat); } - if (server->grabbed_view) { - regions_hide_overlay(seat); - } } } + /* Pressing/releasing modifier key may show/hide region overlay */ + if (server->grabbed_view) { + overlay_show(seat, server->grabbed_view); + } if (!input_method_keyboard_grab_forward_modifiers(keyboard)) { wlr_seat_keyboard_notify_modifiers(seat->seat, diff --git a/src/interactive.c b/src/interactive.c index a0b4f255..243aaa6a 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -129,39 +129,53 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) } } +enum view_edge +edge_from_cursor(struct seat *seat, struct output **dest_output) +{ + int snap_range = rc.snap_edge_range; + if (!snap_range) { + return VIEW_EDGE_INVALID; + } + + struct output *output = output_nearest_to_cursor(seat->server); + if (!output_is_usable(output)) { + wlr_log(WLR_ERROR, "output at cursor is unusable"); + return VIEW_EDGE_INVALID; + } + *dest_output = output; + + /* Translate into output local coordinates */ + double cursor_x = seat->cursor->x; + double cursor_y = seat->cursor->y; + wlr_output_layout_output_coords(seat->server->output_layout, + output->wlr_output, &cursor_x, &cursor_y); + + struct wlr_box *area = &output->usable_area; + if (cursor_x <= area->x + snap_range) { + return VIEW_EDGE_LEFT; + } else if (cursor_x >= area->x + area->width - snap_range) { + return VIEW_EDGE_RIGHT; + } else if (cursor_y <= area->y + snap_range) { + if (rc.snap_top_maximize) { + return VIEW_EDGE_CENTER; + } else { + return VIEW_EDGE_UP; + } + } else if (cursor_y >= area->y + area->height - snap_range) { + return VIEW_EDGE_DOWN; + } else { + /* Not close to any edge */ + return VIEW_EDGE_INVALID; + } +} + /* Returns true if view was snapped to any edge */ static bool snap_to_edge(struct view *view) { - int snap_range = rc.snap_edge_range; - if (!snap_range) { - return false; - } - - struct output *output = output_nearest_to_cursor(view->server); - if (!output_is_usable(output)) { - wlr_log(WLR_ERROR, "output at cursor is unusable"); - return false; - } - - /* Translate into output local coordinates */ - double cursor_x = view->server->seat.cursor->x; - double cursor_y = view->server->seat.cursor->y; - wlr_output_layout_output_coords(view->server->output_layout, - output->wlr_output, &cursor_x, &cursor_y); - - struct wlr_box *area = &output->usable_area; - enum view_edge edge; - if (cursor_x <= area->x + snap_range) { - edge = VIEW_EDGE_LEFT; - } else if (cursor_x >= area->x + area->width - snap_range) { - edge = VIEW_EDGE_RIGHT; - } else if (cursor_y <= area->y + snap_range) { - edge = VIEW_EDGE_UP; - } else if (cursor_y >= area->y + area->height - snap_range) { - edge = VIEW_EDGE_DOWN; - } else { - /* Not close to any edge */ + struct output *output; + enum view_edge edge = edge_from_cursor(&view->server->seat, &output); + if (edge == VIEW_EDGE_INVALID) { return false; } @@ -170,7 +184,8 @@ snap_to_edge(struct view *view) * Don't store natural geometry here (it was * stored already in interactive_begin()) */ - if (edge == VIEW_EDGE_UP && rc.snap_top_maximize) { + if (edge == VIEW_EDGE_CENTER) { + /* */ view_maximize(view, VIEW_AXIS_BOTH, /*store_natural_geometry*/ false); } else { @@ -226,7 +241,7 @@ interactive_cancel(struct view *view) return; } - regions_hide_overlay(&view->server->seat); + overlay_hide(&view->server->seat); resize_indicator_hide(view); diff --git a/src/meson.build b/src/meson.build index a71eb06b..e08afb82 100644 --- a/src/meson.build +++ b/src/meson.build @@ -14,6 +14,7 @@ labwc_sources = files( 'osd.c', 'output.c', 'output-virtual.c', + 'overlay.c', 'placement.c', 'regions.c', 'resistance.c', diff --git a/src/overlay.c b/src/overlay.c new file mode 100644 index 00000000..c9007e69 --- /dev/null +++ b/src/overlay.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include "labwc.h" +#include "overlay.h" +#include "view.h" + +static void +create_overlay(struct seat *seat) +{ + assert(!seat->overlay.tree); + + struct server *server = seat->server; + struct wlr_scene_tree *parent = wlr_scene_tree_create(&server->scene->tree); + + seat->overlay.tree = parent; + wlr_scene_node_set_enabled(&parent->node, false); + if (!wlr_renderer_is_pixman(server->renderer)) { + /* Hardware assisted rendering: Half transparent overlay */ + float color[4] = { 0.25, 0.25, 0.35, 0.5 }; + seat->overlay.rect = wlr_scene_rect_create(parent, 0, 0, color); + } else { + /* Software rendering: Outlines */ + int line_width = server->theme->osd_border_width; + float *colors[3] = { + server->theme->osd_bg_color, + server->theme->osd_label_text_color, + server->theme->osd_bg_color + }; + seat->overlay.pixman_rect = multi_rect_create(parent, colors, line_width); + } +} + +static void +cancel_pending_overlay(struct overlay *overlay) +{ + if (!overlay->timer) { + return; + } + wl_event_source_remove(overlay->timer); + overlay->timer = NULL; + overlay->view = NULL; + overlay->box = (struct wlr_box){0}; +} + +static void +show_overlay(struct seat *seat, struct view *view, struct wlr_box *box) +{ + if (!seat->overlay.tree) { + create_overlay(seat); + } + + /* Update overlay */ + struct server *server = seat->server; + struct wlr_scene_node *node = &seat->overlay.tree->node; + if (!wlr_renderer_is_pixman(server->renderer)) { + /* Hardware assisted rendering: Half transparent overlay */ + wlr_scene_rect_set_size(seat->overlay.rect, + box->width, box->height); + } else { + /* Software rendering: Outlines */ + multi_rect_set_size(seat->overlay.pixman_rect, + box->width, box->height); + } + if (node->parent != view->scene_tree->node.parent) { + wlr_scene_node_reparent(node, view->scene_tree->node.parent); + wlr_scene_node_place_below(node, &view->scene_tree->node); + } + wlr_scene_node_set_position(node, box->x, box->y); + wlr_scene_node_set_enabled(node, true); + + cancel_pending_overlay(&seat->overlay); +} + +static int +handle_overlay_timeout(void *data) +{ + struct seat *seat = data; + show_overlay(seat, seat->overlay.view, &seat->overlay.box); + return 0; +} + +static void +show_overlay_delayed(struct seat *seat, struct view *view, struct wlr_box *box) +{ + if (seat->overlay.timer + && seat->overlay.view == view + && wlr_box_equal(box, &seat->overlay.box)) { + return; + } + cancel_pending_overlay(&seat->overlay); + seat->overlay.view = view; + seat->overlay.box = *box; + seat->overlay.timer = wl_event_loop_add_timer( + seat->server->wl_event_loop, + handle_overlay_timeout, seat); + wl_event_source_timer_update(seat->overlay.timer, 150); +} + +void +overlay_show(struct seat *seat, struct view *view) +{ + struct server *server = seat->server; + + /* + * TODO: cache return value of regions_from_cursor() or + * edge_from_cursor() to eliminate duplicated calls to show_overlay() / + * show_overlay_delayed(). + */ + if (regions_should_snap(server)) { + /* Region overlay */ + struct region *region = regions_from_cursor(server); + if (region) { + show_overlay(seat, view, ®ion->geo); + return; + } + } else { + /* Snap-to-edge overlay */ + struct output *output; + enum view_edge edge = edge_from_cursor(seat, &output); + if (edge != VIEW_EDGE_INVALID) { + /* TODO: share logic with view_get_edge_snap_box() */ + struct wlr_box box = + output_usable_area_in_layout_coords(output); + switch (edge) { + case VIEW_EDGE_RIGHT: + box.x += box.width / 2; + /* fallthrough */ + case VIEW_EDGE_LEFT: + box.width /= 2; + break; + case VIEW_EDGE_DOWN: + box.y += box.height / 2; + /* fallthrough */ + case VIEW_EDGE_UP: + box.height /= 2; + break; + case VIEW_EDGE_CENTER: + /* */ + break; + case VIEW_EDGE_INVALID: + /* not reached */ + assert(false); + } + /* + * Delay showing overlay to prevent flickering when + * dragging view across output edges in multi-monitor + * setup + */ + show_overlay_delayed(seat, view, &box); + return; + } + } + overlay_hide(seat); +} + +void +overlay_hide(struct seat *seat) +{ + cancel_pending_overlay(&seat->overlay); + + struct server *server = seat->server; + struct wlr_scene_node *node = &seat->overlay.tree->node; + if (!node) { + return; + } + + wlr_scene_node_set_enabled(node, false); + if (node->parent != &server->scene->tree) { + wlr_scene_node_reparent(node, &server->scene->tree); + } +} diff --git a/src/regions.c b/src/regions.c index f6eb6174..3add5870 100644 --- a/src/regions.c +++ b/src/regions.c @@ -5,11 +5,9 @@ #include #include #include -#include #include #include #include -#include "common/graphic-helpers.h" #include "common/list.h" #include "common/mem.h" #include "input/keyboard.h" @@ -30,32 +28,6 @@ regions_should_snap(struct server *server) return keyboard_any_modifiers_pressed(keyboard); } -static void -overlay_create(struct seat *seat) -{ - assert(!seat->region_overlay.tree); - - struct server *server = seat->server; - struct wlr_scene_tree *parent = wlr_scene_tree_create(&server->scene->tree); - - seat->region_overlay.tree = parent; - wlr_scene_node_set_enabled(&parent->node, false); - if (!wlr_renderer_is_pixman(server->renderer)) { - /* Hardware assisted rendering: Half transparent overlay */ - float color[4] = { 0.25, 0.25, 0.35, 0.5 }; - seat->region_overlay.overlay = wlr_scene_rect_create(parent, 0, 0, color); - } else { - /* Software rendering: Outlines */ - int line_width = server->theme->osd_border_width; - float *colors[3] = { - server->theme->osd_bg_color, - server->theme->osd_label_text_color, - server->theme->osd_bg_color - }; - seat->region_overlay.pixman_overlay = multi_rect_create(parent, colors, line_width); - } -} - struct region * regions_from_name(const char *region_name, struct output *output) { @@ -101,61 +73,6 @@ regions_from_cursor(struct server *server) return closest_region; } -void -regions_show_overlay(struct view *view, struct seat *seat, struct region *region) -{ - assert(view); - assert(seat); - assert(region); - - /* Don't show active region */ - if (seat->region_active == region) { - return; - } - - if (!seat->region_overlay.tree) { - overlay_create(seat); - } - - /* Update overlay */ - struct server *server = seat->server; - struct wlr_scene_node *node = &seat->region_overlay.tree->node; - if (!wlr_renderer_is_pixman(server->renderer)) { - /* Hardware assisted rendering: Half transparent overlay */ - wlr_scene_rect_set_size(seat->region_overlay.overlay, - region->geo.width, region->geo.height); - } else { - /* Software rendering: Outlines */ - multi_rect_set_size(seat->region_overlay.pixman_overlay, - region->geo.width, region->geo.height); - } - if (node->parent != view->scene_tree->node.parent) { - wlr_scene_node_reparent(node, view->scene_tree->node.parent); - wlr_scene_node_place_below(node, &view->scene_tree->node); - } - wlr_scene_node_set_position(node, region->geo.x, region->geo.y); - wlr_scene_node_set_enabled(node, true); - seat->region_active = region; -} - -void -regions_hide_overlay(struct seat *seat) -{ - assert(seat); - if (!seat->region_active) { - return; - } - - struct server *server = seat->server; - struct wlr_scene_node *node = &seat->region_overlay.tree->node; - - wlr_scene_node_set_enabled(node, false); - if (node->parent != &server->scene->tree) { - wlr_scene_node_reparent(node, &server->scene->tree); - } - seat->region_active = NULL; -} - void regions_reconfigure_output(struct output *output) { @@ -247,9 +164,6 @@ regions_destroy(struct seat *seat, struct wl_list *regions) wl_list_for_each_safe(region, region_tmp, regions, link) { wl_list_remove(®ion->link); zfree(region->name); - if (seat && seat->region_active == region) { - seat->region_active = NULL; - } zfree(region); } } diff --git a/src/view.c b/src/view.c index 066b8427..6e8a7769 100644 --- a/src/view.c +++ b/src/view.c @@ -2223,7 +2223,7 @@ view_destroy(struct view *view) /* Application got killed while moving around */ server->input_mode = LAB_INPUT_STATE_PASSTHROUGH; server->grabbed_view = NULL; - regions_hide_overlay(&server->seat); + overlay_hide(&server->seat); } if (server->active_view == view) {