diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 10b22842..feddedb3 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -249,6 +249,22 @@ Therefore, where multiple objects of the same kind are required (for example Specify the number of pixels to reserve at the edges of an output. New, maximized and tiled windows will not be placed in these areas. +## RESIZE + +** [Never|Always|Nonpixel] + Show a small indicator on top of the window when resizing or moving. + When the application sets size-hints (usually X11 terminal emulators), + the indicator will show the dimensions divided by size hints instead. + In the case of terminal emulators this usually means columns x rows. + + The different values mean: + - *Never* Do not render the indicator + - *Always* Render the indicator while moving and resizing windows + - *Nonpixel* Only render the indicator during resize for windows using + size-hints + + Default is Never. + ## KEYBOARD ** diff --git a/docs/rc.xml.all b/docs/rc.xml.all index 445b61dc..ce7020de 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -52,6 +52,9 @@ 20 + + + no yes diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 62a06ab6..10d74a97 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -10,6 +10,7 @@ #include "common/buf.h" #include "common/font.h" #include "config/libinput.h" +#include "resize_indicator.h" #include "theme.h" enum window_switcher_field_content { @@ -78,6 +79,8 @@ struct rcxml { int snap_edge_range; bool snap_top_maximize; + enum resize_indicator_mode resize_indicator; + struct { int popuptime; int min_nr_workspaces; diff --git a/include/resize_indicator.h b/include/resize_indicator.h new file mode 100644 index 00000000..36e7b0d1 --- /dev/null +++ b/include/resize_indicator.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_RESIZE_INDICATOR_H +#define LABWC_RESIZE_INDICATOR_H + +struct server; +struct view; + +enum resize_indicator_mode { + LAB_RESIZE_INDICATOR_NEVER = 0, + LAB_RESIZE_INDICATOR_ALWAYS, + LAB_RESIZE_INDICATOR_NON_PIXEL +}; + +void resize_indicator_reconfigure(struct server *server); +void resize_indicator_show(struct view *view); +void resize_indicator_update(struct view *view); +void resize_indicator_hide(struct view *view); + +#endif /* LABWC_RESIZE_INDICATOR_H */ diff --git a/include/view.h b/include/view.h index 01468745..064200f8 100644 --- a/include/view.h +++ b/include/view.h @@ -128,6 +128,13 @@ struct view { struct wl_event_source *pending_configure_timeout; struct ssd *ssd; + struct resize_indicator { + int width, height; + struct wlr_scene_tree *tree; + struct wlr_scene_rect *border; + struct wlr_scene_rect *background; + struct scaled_font_buffer *text; + } resize_indicator; struct foreign_toplevel { struct wlr_foreign_toplevel_handle_v1 *handle; diff --git a/src/common/font.c b/src/common/font.c index 091863b6..e173db7a 100644 --- a/src/common/font.c +++ b/src/common/font.c @@ -51,6 +51,7 @@ font_extents(struct font *font, const char *string) pango_extents_to_pixels(&rect, NULL); /* we put a 2 px edge on each side - because Openbox does it :) */ + /* TODO: remove the 4 pixel addition and always do the padding by the caller */ rect.width += 4; cairo_destroy(c); diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 735afeb3..d0d24d33 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -657,6 +657,16 @@ entry(xmlNode *node, char *nodename, char *content) rc.workspace_config.popuptime = atoi(content); } else if (!strcasecmp(nodename, "number.desktops")) { rc.workspace_config.min_nr_workspaces = MAX(1, atoi(content)); + } else if (!strcasecmp(nodename, "popupShow.resize")) { + if (!strcasecmp(content, "Always")) { + rc.resize_indicator = LAB_RESIZE_INDICATOR_ALWAYS; + } else if (!strcasecmp(content, "Never")) { + rc.resize_indicator = LAB_RESIZE_INDICATOR_NEVER; + } else if (!strcasecmp(content, "Nonpixel")) { + rc.resize_indicator = LAB_RESIZE_INDICATOR_NON_PIXEL; + } else { + wlr_log(WLR_ERROR, "Invalid value for "); + } } } @@ -806,6 +816,8 @@ rcxml_init(void) rc.window_switcher.preview = true; rc.window_switcher.outlines = true; + rc.resize_indicator = LAB_RESIZE_INDICATOR_NEVER; + rc.workspace_config.popuptime = INT_MIN; rc.workspace_config.min_nr_workspaces = 1; } diff --git a/src/interactive.c b/src/interactive.c index 9a009d93..3caac79e 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include "labwc.h" #include "regions.h" +#include "resize_indicator.h" #include "view.h" static int @@ -96,6 +97,9 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) server->grab_y = seat->cursor->y; server->grab_box = geometry; server->resize_edges = edges; + if (rc.resize_indicator) { + resize_indicator_show(view); + } } /* Returns true if view was snapped to any edge */ @@ -172,6 +176,7 @@ interactive_finish(struct view *view) } } } + resize_indicator_hide(view); view->server->input_mode = LAB_INPUT_STATE_PASSTHROUGH; view->server->grabbed_view = NULL; @@ -190,6 +195,7 @@ void interactive_cancel(struct view *view) { if (view->server->grabbed_view == view) { + resize_indicator_hide(view); view->server->input_mode = LAB_INPUT_STATE_PASSTHROUGH; view->server->grabbed_view = NULL; /* Update focus/cursor image */ diff --git a/src/server.c b/src/server.c index c781f53d..227b641f 100644 --- a/src/server.c +++ b/src/server.c @@ -24,6 +24,7 @@ #include "layers.h" #include "menu/menu.h" #include "regions.h" +#include "resize_indicator.h" #include "theme.h" #include "view.h" #include "workspaces.h" @@ -54,6 +55,7 @@ reload_config_and_theme(void) menu_reconfigure(g_server); seat_reconfigure(g_server); regions_reconfigure(g_server); + resize_indicator_reconfigure(g_server); kde_server_decoration_update_default(); } diff --git a/src/ssd/meson.build b/src/ssd/meson.build index 95395419..b1a2a2fc 100644 --- a/src/ssd/meson.build +++ b/src/ssd/meson.build @@ -1,4 +1,5 @@ labwc_sources += files( + 'resize_indicator.c', 'ssd.c', 'ssd_part.c', 'ssd_titlebar.c', diff --git a/src/ssd/resize_indicator.c b/src/ssd/resize_indicator.c new file mode 100644 index 00000000..3a41323d --- /dev/null +++ b/src/ssd/resize_indicator.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include "common/scaled_font_buffer.h" +#include "labwc.h" +#include "resize_indicator.h" +#include "view.h" + +static void +resize_indicator_reconfigure_view(struct resize_indicator *indicator) +{ + assert(indicator->tree); + + struct theme *theme = rc.theme; + indicator->height = font_height(&rc.font_osd) + + 2 * theme->osd_window_switcher_padding + + 2 * theme->osd_border_width; + + /* Static positions */ + wlr_scene_node_set_position(&indicator->background->node, + theme->osd_border_width, theme->osd_border_width); + + wlr_scene_node_set_position(&indicator->text->scene_buffer->node, + theme->osd_border_width + theme->osd_window_switcher_padding, + theme->osd_border_width + theme->osd_window_switcher_padding); + + /* Colors */ + wlr_scene_rect_set_color(indicator->border, theme->osd_border_color); + wlr_scene_rect_set_color(indicator->background, theme->osd_bg_color); +} + +static void +resize_indicator_init(struct view *view) +{ + assert(view); + struct resize_indicator *indicator = &view->resize_indicator; + assert(!indicator->tree); + + indicator->tree = wlr_scene_tree_create(view->scene_tree); + indicator->border = wlr_scene_rect_create( + indicator->tree, 0, 0, rc.theme->osd_border_color); + indicator->background = wlr_scene_rect_create( + indicator->tree, 0, 0, rc.theme->osd_bg_color); + indicator->text = scaled_font_buffer_create(indicator->tree); + + wlr_scene_node_set_enabled(&indicator->tree->node, false); + resize_indicator_reconfigure_view(indicator); +} + +static struct wlr_box +get_size_hints(struct view *view) +{ + assert(view); + + struct wlr_box hints = { 0 }; + if (view->impl->fill_size_hints) { + view->impl->fill_size_hints(view, &hints); + } + return hints; +} + +static bool +wants_indicator(struct view *view) +{ + assert(view); + + if (rc.resize_indicator == LAB_RESIZE_INDICATOR_NON_PIXEL) { + if (view->server->input_mode != LAB_INPUT_STATE_RESIZE) { + return false; + } + struct wlr_box size_hints = get_size_hints(view); + if (size_hints.width && size_hints.height) { + return true; + } + } + return rc.resize_indicator == LAB_RESIZE_INDICATOR_ALWAYS; +} + +void +resize_indicator_reconfigure(struct server *server) +{ + struct view *view; + wl_list_for_each(view, &server->views, link) { + struct resize_indicator *indicator = &view->resize_indicator; + if (indicator->tree) { + resize_indicator_reconfigure_view(indicator); + } + if (view != server->grabbed_view) { + continue; + } + + /* This view is currently in an interactive move/resize operation */ + if (indicator->tree && indicator->tree->node.enabled) { + /* Indicator was active while reloading the config */ + if (wants_indicator(view)) { + /* Apply new font setting */ + resize_indicator_update(view); + } else { + /* Indicator was disabled in config */ + resize_indicator_hide(view); + } + } else if (wants_indicator(view)) { + /* Indicator not yet active */ + resize_indicator_show(view); + } + } +} + +static void +resize_indicator_set_size(struct resize_indicator *indicator, int width) +{ + assert(indicator->tree); + + /* We are not using a width-cache-early-out here to allow for theme changes */ + indicator->width = width + + 2 * rc.theme->osd_window_switcher_padding + + 2 * rc.theme->osd_border_width; + + wlr_scene_rect_set_size(indicator->border, indicator->width, indicator->height); + wlr_scene_rect_set_size(indicator->background, + indicator->width - 2 * rc.theme->osd_border_width, + indicator->height - 2 * rc.theme->osd_border_width); +} + +void +resize_indicator_show(struct view *view) +{ + assert(view); + + if (!wants_indicator(view)) { + return; + } + + struct resize_indicator *indicator = &view->resize_indicator; + if (!indicator->tree) { + /* Lazy initialize */ + resize_indicator_init(view); + } + + wlr_scene_node_raise_to_top(&indicator->tree->node); + wlr_scene_node_set_enabled(&indicator->tree->node, true); + resize_indicator_update(view); +} + +void +resize_indicator_update(struct view *view) +{ + assert(view); + assert(view == view->server->grabbed_view); + + if (!wants_indicator(view)) { + return; + } + + struct resize_indicator *indicator = &view->resize_indicator; + if (!indicator->tree) { + /* + * Future-proofs this routine: + * + * This can only happen when either src/interactive.c + * stops calling resize_indicator_show(), there is a + * bug in this file or resize_indicator_reconfigure() + * gets changed. + */ + wlr_log(WLR_INFO, "Warning: resize_indicator has to use a fallback path"); + resize_indicator_show(view); + } + + char text[32]; /* 12345 x 12345 would be 13 chars + 1 null byte */ + + switch (view->server->input_mode) { + case LAB_INPUT_STATE_RESIZE: + ; /* works around "a label can only be part of a statement" */ + struct wlr_box size_hints = get_size_hints(view); + snprintf(text, sizeof(text), "%d x %d", + view->current.width / MAX(1, size_hints.width), + view->current.height / MAX(1, size_hints.height)); + break; + case LAB_INPUT_STATE_MOVE: + ; /* works around "a label can only be part of a statement" */ + struct border margin = ssd_get_margin(view->ssd); + snprintf(text, sizeof(text), "%d , %d", + view->current.x - margin.left, + view->current.y - margin.top); + break; + default: + wlr_log(WLR_ERROR, "Invalid input mode for indicator update %u", + view->server->input_mode); + return; + } + + /* Let the indicator change width as required by the content */ + int width = font_width(&rc.font_osd, text); + + /* font_extents() adds 4 pixels to the calculated width */ + width -= 4; + + resize_indicator_set_size(indicator, width); + + /* Center the indicator in the window */ + wlr_scene_node_set_position(&indicator->tree->node, + (view->current.width - indicator->width) / 2, + (view->current.height - indicator->height) / 2); + + scaled_font_buffer_update(indicator->text, text, width, &rc.font_osd, + rc.theme->osd_label_text_color, NULL /* const char *arrow */); +} + +void +resize_indicator_hide(struct view *view) +{ + assert(view); + + struct resize_indicator *indicator = &view->resize_indicator; + if (!indicator->tree) { + return; + } + + wlr_scene_node_set_enabled(&indicator->tree->node, false); +} diff --git a/src/view.c b/src/view.c index 729cedb2..d85723e4 100644 --- a/src/view.c +++ b/src/view.c @@ -7,6 +7,7 @@ #include "labwc.h" #include "menu/menu.h" #include "regions.h" +#include "resize_indicator.h" #include "ssd.h" #include "view.h" #include "window-rules.h" @@ -182,6 +183,9 @@ view_moved(struct view *view) if (view->toplevel.handle) { foreign_toplevel_update_outputs(view); } + if (rc.resize_indicator && view->server->grabbed_view == view) { + resize_indicator_update(view); + } } void