Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Manuel Barrio Linares 2026-01-08 22:56:59 -03:00
commit 25f4b8432b
20 changed files with 354 additions and 233 deletions

View file

@ -281,14 +281,18 @@ jobs:
LABWC_RUNS=2 scripts/ci/smoke-test.sh build-gcc-gdb
' | $TARGET
- name: Build with gcc - catch no font installed case
if: matrix.name == 'Void-musl'
run: |
echo '
cd "$GITHUB_WORKSPACE"
xbps-remove -y dejavu-fonts-ttf
export CC=gcc
meson setup build-gcc-nofont -Dxwayland=enabled --werror
meson compile -C build-gcc-nofont
LABWC_EXPECT_RETURNCODE=1 scripts/ci/smoke-test.sh build-gcc-nofont
' | $TARGET
# Void made the foot package depend on the font.
# As this test uses both and the feature itself
# seems to work well lets just skip it for now
#
#- name: Build with gcc - catch no font installed case
# if: matrix.name == 'Void-musl'
# run: |
# echo '
# cd "$GITHUB_WORKSPACE"
# xbps-remove -y dejavu-fonts-ttf
# export CC=gcc
# meson setup build-gcc-nofont -Dxwayland=enabled --werror
# meson compile -C build-gcc-nofont
# LABWC_EXPECT_RETURNCODE=1 scripts/ci/smoke-test.sh build-gcc-nofont
# ' | $TARGET

View file

@ -4,9 +4,11 @@
#include <stdbool.h>
#include <wayland-server-core.h>
#include <wlr/util/box.h>
#include "config/types.h"
struct output;
struct wlr_box;
enum lab_cycle_dir {
LAB_CYCLE_DIR_NONE,
@ -49,6 +51,40 @@ struct cycle_filter {
enum cycle_app_id_filter app_id;
};
struct cycle_state {
struct view *selected_view;
struct wl_list views;
struct wl_list osd_outputs; /* struct cycle_osd_output.link */
bool preview_was_shaded;
bool preview_was_enabled;
struct wlr_scene_node *preview_node;
struct wlr_scene_node *preview_dummy;
struct lab_scene_rect *preview_outline;
struct cycle_filter filter;
};
struct cycle_osd_output {
struct wl_list link; /* struct cycle_state.osd_outputs */
struct output *output;
struct wl_listener tree_destroy;
/* set by cycle_osd_impl->init() */
struct wl_list items; /* struct cycle_osd_item.link */
struct wlr_scene_tree *tree;
/* set by cycle_osd_impl->init() and moved by cycle_osd_scroll_update() */
struct wlr_scene_tree *items_tree;
/* used in osd-scroll.c */
struct cycle_osd_scroll_context {
int top_row_idx;
int nr_rows, nr_cols, nr_visible_rows;
int delta_y;
struct wlr_box bar_area;
struct wlr_scene_tree *bar_tree;
struct lab_scene_rect *bar;
} scroll;
};
struct buf;
struct view;
struct server;
@ -92,17 +128,38 @@ struct cycle_osd_item {
struct cycle_osd_impl {
/*
* Create a scene-tree of OSD for an output.
* This sets output->cycle_osd.{items,tree}.
* Create a scene-tree of OSD for an output and fill
* osd_output->items.
*/
void (*create)(struct output *output);
void (*init)(struct cycle_osd_output *osd_output);
/*
* Update output->cycle_osd.tree to highlight
* server->cycle_state.selected_view.
* Update the OSD to highlight server->cycle.selected_view.
*/
void (*update)(struct output *output);
void (*update)(struct cycle_osd_output *osd_output);
};
#define SCROLLBAR_W 10
/**
* Initialize the context and scene for scrolling OSD items.
*
* @output: Output of the OSD
* @bar_area: Area where the scrollbar is drawn
* @delta_y: The vertical delta by which items are scrolled (usually item height)
* @nr_cols: Number of columns in the OSD
* @nr_rows: Number of rows in the OSD
* @nr_visible_rows: Number of visible rows in the OSD
* @border_color: Border color of the scrollbar
* @bg_color: Background color of the scrollbar
*/
void cycle_osd_scroll_init(struct cycle_osd_output *osd_output,
struct wlr_box bar_area, int delta_y,
int nr_cols, int nr_rows, int nr_visible_rows,
float *border_color, float *bg_color);
/* Scroll the OSD to show server->cycle.selected_view if needed */
void cycle_osd_scroll_update(struct cycle_osd_output *osd_output);
extern struct cycle_osd_impl cycle_osd_classic_impl;
extern struct cycle_osd_impl cycle_osd_thumbnail_impl;

View file

@ -238,6 +238,7 @@ struct server {
/* Tree for unmanaged xsurfaces without initialized view (usually popups) */
struct wlr_scene_tree *unmanaged_tree;
#endif
struct wlr_scene_tree *cycle_preview_tree;
/* Tree for built in menu */
struct wlr_scene_tree *menu_tree;
@ -303,16 +304,7 @@ struct server {
struct wlr_security_context_manager_v1 *security_context_manager_v1;
/* Set when in cycle (alt-tab) mode */
struct cycle_state {
struct view *selected_view;
struct wl_list views;
bool preview_was_shaded;
bool preview_was_enabled;
struct wlr_scene_node *preview_node;
struct wlr_scene_node *preview_dummy;
struct lab_scene_rect *preview_outline;
struct cycle_filter filter;
} cycle;
struct cycle_state cycle;
struct theme *theme;

View file

@ -19,11 +19,6 @@ struct output {
struct wlr_scene_tree *session_lock_tree;
struct wlr_scene_buffer *workspace_osd;
struct cycle_osd_scene {
struct wl_list items; /* struct cycle_osd_item */
struct wlr_scene_tree *tree;
} cycle_osd;
/* In output-relative scene coordinates */
struct wlr_box usable_area;

View file

@ -512,8 +512,7 @@ void view_constrain_size_to_that_of_usable_area(struct view *view);
void view_set_maximized(struct view *view, enum view_axis maximized);
void view_set_untiled(struct view *view);
void view_maximize(struct view *view, enum view_axis axis,
bool store_natural_geometry);
void view_maximize(struct view *view, enum view_axis axis);
void view_set_fullscreen(struct view *view, bool fullscreen);
void view_toggle_maximize(struct view *view, enum view_axis axis);
bool view_wants_decorations(struct view *view);
@ -540,8 +539,8 @@ void view_move_to_edge(struct view *view, enum lab_edge direction, bool snap_to_
void view_grow_to_edge(struct view *view, enum lab_edge direction);
void view_shrink_to_edge(struct view *view, enum lab_edge direction);
void view_snap_to_edge(struct view *view, enum lab_edge direction,
bool across_outputs, bool combine, bool store_natural_geometry);
void view_snap_to_region(struct view *view, struct region *region, bool store_natural_geometry);
bool across_outputs, bool combine);
void view_snap_to_region(struct view *view, struct region *region);
void view_move_to_output(struct view *view, struct output *output);
void view_move_to_front(struct view *view);

View file

@ -28,7 +28,6 @@ server_protocols = [
wl_protocol_dir / 'staging/color-management/color-management-v1.xml',
'cosmic-workspace-unstable-v1.xml',
'wlr-layer-shell-unstable-v1.xml',
'wlr-input-inhibitor-unstable-v1.xml',
'wlr-output-power-management-unstable-v1.xml',
]

View file

@ -1,67 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_input_inhibit_unstable_v1">
<copyright>
Copyright © 2018 Drew DeVault
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<interface name="zwlr_input_inhibit_manager_v1" version="1">
<description summary="inhibits input events to other clients">
Clients can use this interface to prevent input events from being sent to
any surfaces but its own, which is useful for example in lock screen
software. It is assumed that access to this interface will be locked down
to whitelisted clients by the compositor.
</description>
<request name="get_inhibitor">
<description summary="inhibit input to other clients">
Activates the input inhibitor. As long as the inhibitor is active, the
compositor will not send input events to other clients.
</description>
<arg name="id" type="new_id" interface="zwlr_input_inhibitor_v1"/>
</request>
<enum name="error">
<entry name="already_inhibited" value="0" summary="an input inhibitor is already in use on the compositor"/>
</enum>
</interface>
<interface name="zwlr_input_inhibitor_v1" version="1">
<description summary="inhibits input to other clients">
While this resource exists, input to clients other than the owner of the
inhibitor resource will not receive input events. The client that owns
this resource will receive all input events normally. The compositor will
also disable all of its own input processing (such as keyboard shortcuts)
while the inhibitor is active.
The compositor may continue to send input events to selected clients,
such as an on-screen keyboard (via the input-method protocol).
</description>
<request name="destroy" type="destructor">
<description summary="destroy the input inhibitor object">
Destroy the inhibitor and allow other clients to receive input.
</description>
</request>
</interface>
</protocol>

View file

@ -1146,7 +1146,7 @@ run_action(struct view *view, struct server *server, struct action *action,
}
bool combine = action_get_bool(action, "combine", false);
view_snap_to_edge(view, edge, /*across_outputs*/ true,
combine, /*store_natural_geometry*/ true);
combine);
}
break;
case ACTION_TYPE_GROW_TO_EDGE:
@ -1203,16 +1203,14 @@ run_action(struct view *view, struct server *server, struct action *action,
if (view) {
enum view_axis axis = action_get_int(action,
"direction", VIEW_AXIS_BOTH);
view_maximize(view, axis,
/*store_natural_geometry*/ true);
view_maximize(view, axis);
}
break;
case ACTION_TYPE_UNMAXIMIZE:
if (view) {
enum view_axis axis = action_get_int(action,
"direction", VIEW_AXIS_BOTH);
view_maximize(view, view->maximized & ~axis,
/*store_natural_geometry*/ true);
view_maximize(view, view->maximized & ~axis);
}
break;
case ACTION_TYPE_TOGGLE_FULLSCREEN:
@ -1426,8 +1424,7 @@ run_action(struct view *view, struct server *server, struct action *action,
view_apply_natural_geometry(view);
break;
}
view_snap_to_region(view, region,
/*store_natural_geometry*/ true);
view_snap_to_region(view, region);
} else {
wlr_log(WLR_ERROR, "Invalid SnapToRegion id: '%s'", region_name);
}
@ -1435,8 +1432,7 @@ run_action(struct view *view, struct server *server, struct action *action,
}
case ACTION_TYPE_UNSNAP:
if (view && !view->fullscreen && !view_is_floating(view)) {
view_maximize(view, VIEW_AXIS_NONE,
/* store_natural_geometry */ false);
view_maximize(view, VIEW_AXIS_NONE);
view_set_untiled(view);
view_apply_natural_geometry(view);
}

View file

@ -6,6 +6,7 @@
#include <wlr/util/log.h>
#include "common/lab-scene-rect.h"
#include "common/list.h"
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "labwc.h"
@ -39,7 +40,8 @@ update_preview_outlines(struct view *view)
.border_width = theme->osd_window_switcher_preview_border_width,
};
rect = lab_scene_rect_create(&server->scene->tree, &opts);
wlr_scene_node_place_above(&rect->tree->node, &server->menu_tree->node);
wlr_scene_node_place_above(&rect->tree->node,
&server->cycle_preview_tree->node);
server->cycle.preview_outline = rect;
}
@ -244,13 +246,8 @@ preview_selected_view(struct view *view)
cycle->preview_was_shaded = true;
}
/*
* FIXME: This abuses an implementation detail of the always-on-top tree.
* Create a permanent server->osd_preview_tree instead that can
* also be used as parent for the preview outlines.
*/
wlr_scene_node_reparent(cycle->preview_node,
view->server->view_tree_always_on_top);
view->server->cycle_preview_tree);
/* Finally raise selected node to the top */
wlr_scene_node_raise_to_top(cycle->preview_node);
@ -314,6 +311,21 @@ insert_view_ordered_by_age(struct wl_list *views, struct view *new_view)
wl_list_insert(link, &new_view->cycle_link);
}
static void
handle_osd_tree_destroy(struct wl_listener *listener, void *data)
{
struct cycle_osd_output *osd_output =
wl_container_of(listener, osd_output, tree_destroy);
struct cycle_osd_item *item, *tmp;
wl_list_for_each_safe(item, tmp, &osd_output->items, link) {
wl_list_remove(&item->link);
free(item);
}
wl_list_remove(&osd_output->tree_destroy.link);
wl_list_remove(&osd_output->link);
free(osd_output);
}
/* Return false on failure */
static bool
init_cycle(struct server *server, struct cycle_filter filter)
@ -366,8 +378,17 @@ init_cycle(struct server *server, struct cycle_filter filter)
if (!output_is_usable(output)) {
continue;
}
get_osd_impl()->create(output);
assert(output->cycle_osd.tree);
struct cycle_osd_output *osd_output = znew(*osd_output);
wl_list_append(&server->cycle.osd_outputs, &osd_output->link);
osd_output->output = output;
wl_list_init(&osd_output->items);
get_osd_impl()->init(osd_output);
osd_output->tree_destroy.notify = handle_osd_tree_destroy;
wl_signal_add(&osd_output->tree->node.events.destroy,
&osd_output->tree_destroy);
}
}
@ -380,11 +401,9 @@ update_cycle(struct server *server)
struct cycle_state *cycle = &server->cycle;
if (rc.window_switcher.osd.show) {
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
if (output->cycle_osd.tree) {
get_osd_impl()->update(output);
}
struct cycle_osd_output *osd_output;
wl_list_for_each(osd_output, &cycle->osd_outputs, link) {
get_osd_impl()->update(osd_output);
}
}
@ -394,8 +413,8 @@ update_cycle(struct server *server)
/* Outline current window */
if (rc.window_switcher.outlines) {
if (view_is_focusable(server->cycle.selected_view)) {
update_preview_outlines(server->cycle.selected_view);
if (view_is_focusable(cycle->selected_view)) {
update_preview_outlines(cycle->selected_view);
}
}
}
@ -404,32 +423,25 @@ update_cycle(struct server *server)
static void
destroy_cycle(struct server *server)
{
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
struct cycle_osd_item *item, *tmp;
wl_list_for_each_safe(item, tmp, &output->cycle_osd.items, link) {
wl_list_remove(&item->link);
free(item);
}
if (output->cycle_osd.tree) {
wlr_scene_node_destroy(&output->cycle_osd.tree->node);
output->cycle_osd.tree = NULL;
}
struct cycle_osd_output *osd_output, *tmp;
wl_list_for_each_safe(osd_output, tmp, &server->cycle.osd_outputs, link) {
/* calls handle_osd_tree_destroy() */
wlr_scene_node_destroy(&osd_output->tree->node);
}
restore_preview_node(server);
if (server->cycle.preview_outline) {
wlr_scene_node_destroy(&server->cycle.preview_outline->tree->node);
server->cycle.preview_outline = NULL;
}
struct view *view, *tmp;
wl_list_for_each_safe(view, tmp, &server->cycle.views, cycle_link) {
struct view *view, *tmp2;
wl_list_for_each_safe(view, tmp2, &server->cycle.views, cycle_link) {
wl_list_remove(&view->cycle_link);
view->cycle_link = (struct wl_list){0};
}
server->cycle.selected_view = NULL;
server->cycle.filter = (struct cycle_filter){0};
server->cycle = (struct cycle_state){0};
wl_list_init(&server->cycle.views);
wl_list_init(&server->cycle.osd_outputs);
}

View file

@ -2,5 +2,6 @@ labwc_sources += files(
'cycle.c',
'osd-classic.c',
'osd-field.c',
'osd-scroll.c',
'osd-thumbnail.c',
)

View file

@ -77,10 +77,9 @@ create_fields_scene(struct server *server, struct view *view,
}
static void
cycle_osd_classic_create(struct output *output)
cycle_osd_classic_init(struct cycle_osd_output *osd_output)
{
assert(!output->cycle_osd.tree && wl_list_empty(&output->cycle_osd.items));
struct output *output = osd_output->output;
struct server *server = output->server;
struct theme *theme = server->theme;
struct window_switcher_classic_theme *switcher_theme =
@ -98,13 +97,18 @@ cycle_osd_classic_create(struct output *output)
if (switcher_theme->width_is_percent) {
w = output_box.width * switcher_theme->width / 100;
}
int h = nr_views * switcher_theme->item_height + 2 * padding;
int workspace_name_h = 0;
if (show_workspace) {
/* workspace indicator */
h += switcher_theme->item_height;
workspace_name_h = switcher_theme->item_height;
}
int nr_visible_views = (output_box.height - workspace_name_h - 2 * padding)
/ switcher_theme->item_height;
nr_visible_views = MIN(nr_visible_views, nr_views);
int h = workspace_name_h + nr_visible_views * switcher_theme->item_height
+ 2 * padding;
output->cycle_osd.tree = wlr_scene_tree_create(output->cycle_osd_tree);
osd_output->tree = wlr_scene_tree_create(output->cycle_osd_tree);
float *text_color = theme->osd_label_text_color;
float *bg_color = theme->osd_bg_color;
@ -118,7 +122,7 @@ cycle_osd_classic_create(struct output *output)
.width = w,
.height = h,
};
lab_scene_rect_create(output->cycle_osd.tree, &bg_opts);
lab_scene_rect_create(osd_output->tree, &bg_opts);
int y = padding;
@ -136,7 +140,7 @@ cycle_osd_classic_create(struct output *output)
}
struct scaled_font_buffer *font_buffer =
scaled_font_buffer_create(output->cycle_osd.tree);
scaled_font_buffer_create(osd_output->tree);
wlr_scene_node_set_position(&font_buffer->scene_buffer->node,
x, y + (switcher_theme->item_height - font_height(&font)) / 2);
scaled_font_buffer_update(font_buffer, workspace_name, 0,
@ -155,13 +159,17 @@ cycle_osd_classic_create(struct output *output)
goto error;
}
float *active_bg_color = switcher_theme->item_active_bg_color;
float *active_border_color = switcher_theme->item_active_border_color;
osd_output->items_tree = wlr_scene_tree_create(osd_output->tree);
/* Draw text for each node */
struct view *view;
wl_list_for_each(view, &server->cycle.views, cycle_link) {
struct cycle_osd_classic_item *item = znew(*item);
wl_list_append(&output->cycle_osd.items, &item->base.link);
wl_list_append(&osd_output->items, &item->base.link);
item->base.view = view;
item->base.tree = wlr_scene_tree_create(output->cycle_osd.tree);
item->base.tree = wlr_scene_tree_create(osd_output->items_tree);
node_descriptor_create(&item->base.tree->node,
LAB_NODE_CYCLE_OSD_ITEM, NULL, item);
/*
@ -187,9 +195,6 @@ cycle_osd_classic_create(struct output *output)
item->active_tree = wlr_scene_tree_create(item->base.tree);
wlr_scene_node_set_enabled(&item->active_tree->node, false);
float *active_bg_color = switcher_theme->item_active_bg_color;
float *active_border_color = switcher_theme->item_active_border_color;
/* Highlight around selected window's item */
struct lab_scene_rect_options highlight_opts = {
.border_colors = (float *[1]) {active_border_color},
@ -216,25 +221,39 @@ cycle_osd_classic_create(struct output *output)
y += switcher_theme->item_height;
}
struct wlr_box scrollbar_area = {
.x = w - padding - SCROLLBAR_W,
.y = padding,
.width = SCROLLBAR_W,
.height = h - 2 * padding,
};
cycle_osd_scroll_init(osd_output, scrollbar_area,
switcher_theme->item_height,
/*nr_cols*/ 1, /*nr_rows*/ nr_views, nr_visible_views,
active_border_color, active_bg_color);
error:;
/* Center OSD */
wlr_scene_node_set_position(&output->cycle_osd.tree->node,
wlr_scene_node_set_position(&osd_output->tree->node,
output_box.x + (output_box.width - w) / 2,
output_box.y + (output_box.height - h) / 2);
}
static void
cycle_osd_classic_update(struct output *output)
cycle_osd_classic_update(struct cycle_osd_output *osd_output)
{
struct server *server = osd_output->output->server;
cycle_osd_scroll_update(osd_output);
struct cycle_osd_classic_item *item;
wl_list_for_each(item, &output->cycle_osd.items, base.link) {
bool active = item->base.view == output->server->cycle.selected_view;
wl_list_for_each(item, &osd_output->items, base.link) {
bool active = item->base.view == server->cycle.selected_view;
wlr_scene_node_set_enabled(&item->normal_tree->node, !active);
wlr_scene_node_set_enabled(&item->active_tree->node, active);
}
}
struct cycle_osd_impl cycle_osd_classic_impl = {
.create = cycle_osd_classic_create,
.init = cycle_osd_classic_init,
.update = cycle_osd_classic_update,
};

95
src/cycle/osd-scroll.c Normal file
View file

@ -0,0 +1,95 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <assert.h>
#include <wlr/types/wlr_scene.h>
#include "common/lab-scene-rect.h"
#include "labwc.h"
#include "cycle.h"
#include "output.h"
void
cycle_osd_scroll_init(struct cycle_osd_output *osd_output, struct wlr_box bar_area,
int delta_y, int nr_cols, int nr_rows, int nr_visible_rows,
float *border_color, float *bg_color)
{
if (nr_visible_rows >= nr_rows) {
/* OSD doesn't have so many windows to scroll through */
return;
}
struct cycle_osd_scroll_context *scroll = &osd_output->scroll;
scroll->nr_cols = nr_cols;
scroll->nr_rows = nr_rows;
scroll->nr_visible_rows = nr_visible_rows;
scroll->top_row_idx = 0;
scroll->bar_area = bar_area;
scroll->delta_y = delta_y;
scroll->bar_tree = wlr_scene_tree_create(osd_output->tree);
wlr_scene_node_set_position(&scroll->bar_tree->node,
bar_area.x, bar_area.y);
struct lab_scene_rect_options scrollbar_opts = {
.border_colors = (float *[1]) { border_color },
.nr_borders = 1,
.border_width = 1,
.bg_color = bg_color,
.width = bar_area.width,
.height = bar_area.height * nr_visible_rows / nr_rows,
};
scroll->bar = lab_scene_rect_create(scroll->bar_tree, &scrollbar_opts);
}
static int
get_cycle_idx(struct cycle_osd_output *osd_output)
{
struct server *server = osd_output->output->server;
int idx = 0;
struct cycle_osd_item *item;
wl_list_for_each(item, &osd_output->items, link) {
if (item->view == server->cycle.selected_view) {
return idx;
}
idx++;
}
assert(false && "selected view not found in items");
return -1;
}
void
cycle_osd_scroll_update(struct cycle_osd_output *osd_output)
{
struct cycle_osd_scroll_context *scroll = &osd_output->scroll;
if (!scroll->bar) {
return;
}
int cycle_idx = get_cycle_idx(osd_output);
/* Update the range of visible rows */
int bottom_row_idx = scroll->top_row_idx + scroll->nr_visible_rows;
while (cycle_idx < scroll->top_row_idx * scroll->nr_cols) {
scroll->top_row_idx--;
bottom_row_idx--;
}
while (cycle_idx >= bottom_row_idx * scroll->nr_cols) {
scroll->top_row_idx++;
bottom_row_idx++;
}
/* Vertically move scrollbar by (bar height) / (# of total rows) */
wlr_scene_node_set_position(&scroll->bar->tree->node, 0,
scroll->bar_area.height * scroll->top_row_idx / scroll->nr_rows);
/* Vertically move items */
wlr_scene_node_set_position(&osd_output->items_tree->node, 0,
-scroll->delta_y * scroll->top_row_idx);
/* Hide items outside of visible area */
int idx = 0;
struct cycle_osd_item *item;
wl_list_for_each(item, &osd_output->items, link) {
bool visible = idx >= scroll->top_row_idx * scroll->nr_cols
&& idx < bottom_row_idx * scroll->nr_cols;
wlr_scene_node_set_enabled(&item->tree->node, visible);
idx++;
}
}

View file

@ -117,9 +117,9 @@ create_label(struct wlr_scene_tree *parent, struct view *view,
static struct cycle_osd_thumbnail_item *
create_item_scene(struct wlr_scene_tree *parent, struct view *view,
struct output *output)
struct cycle_osd_output *osd_output)
{
struct server *server = output->server;
struct server *server = osd_output->output->server;
struct theme *theme = server->theme;
struct window_switcher_thumbnail_theme *switcher_theme =
&theme->osd_window_switcher_thumbnail;
@ -137,7 +137,7 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view,
}
struct cycle_osd_thumbnail_item *item = znew(*item);
wl_list_append(&output->cycle_osd.items, &item->base.link);
wl_list_append(&osd_output->items, &item->base.link);
struct wlr_scene_tree *tree = wlr_scene_tree_create(parent);
node_descriptor_create(&tree->node, LAB_NODE_CYCLE_OSD_ITEM, NULL, item);
item->base.tree = tree;
@ -159,7 +159,7 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view,
switcher_theme->item_height, (float[4]) {0});
/* thumbnail */
struct wlr_buffer *thumb_buffer = render_thumb(output, view);
struct wlr_buffer *thumb_buffer = render_thumb(osd_output->output, view);
if (thumb_buffer) {
struct wlr_scene_buffer *thumb_scene_buffer =
wlr_scene_buffer_create(tree, thumb_buffer);
@ -194,9 +194,10 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view,
}
static void
get_items_geometry(struct output *output, struct theme *theme,
int nr_thumbs, int *nr_rows, int *nr_cols)
get_items_geometry(struct output *output, int nr_thumbs,
int *nr_cols, int *nr_rows, int *nr_visible_rows)
{
struct theme *theme = output->server->theme;
struct window_switcher_thumbnail_theme *switcher_theme =
&theme->osd_window_switcher_thumbnail;
int output_width, output_height;
@ -223,32 +224,35 @@ get_items_geometry(struct output *output, struct theme *theme,
(*nr_rows)++;
*nr_cols = ceilf((float)nr_thumbs / *nr_rows);
}
*nr_visible_rows = MIN(*nr_rows,
(output_height - 2 * padding) / switcher_theme->item_height);
}
static void
cycle_osd_thumbnail_create(struct output *output)
cycle_osd_thumbnail_init(struct cycle_osd_output *osd_output)
{
assert(!output->cycle_osd.tree && wl_list_empty(&output->cycle_osd.items));
struct output *output = osd_output->output;
struct server *server = output->server;
struct theme *theme = server->theme;
struct window_switcher_thumbnail_theme *switcher_theme =
&theme->osd_window_switcher_thumbnail;
int padding = theme->osd_border_width + switcher_theme->padding;
output->cycle_osd.tree = wlr_scene_tree_create(output->cycle_osd_tree);
osd_output->tree = wlr_scene_tree_create(output->cycle_osd_tree);
osd_output->items_tree = wlr_scene_tree_create(osd_output->tree);
int nr_views = wl_list_length(&server->cycle.views);
assert(nr_views > 0);
int nr_rows, nr_cols;
get_items_geometry(output, theme, nr_views, &nr_rows, &nr_cols);
int nr_cols, nr_rows, nr_visible_rows;
get_items_geometry(output, nr_views, &nr_cols, &nr_rows, &nr_visible_rows);
/* items */
struct view *view;
int index = 0;
wl_list_for_each(view, &server->cycle.views, cycle_link) {
struct cycle_osd_thumbnail_item *item = create_item_scene(
output->cycle_osd.tree, view, output);
osd_output->items_tree, view, osd_output);
if (!item) {
break;
}
@ -258,17 +262,31 @@ cycle_osd_thumbnail_create(struct output *output)
index++;
}
int items_width = switcher_theme->item_width * nr_cols;
int items_height = switcher_theme->item_height * nr_visible_rows;
struct wlr_box scrollbar_area = {
.x = padding + items_width - SCROLLBAR_W,
.y = padding,
.width = SCROLLBAR_W,
.height = items_height,
};
cycle_osd_scroll_init(osd_output, scrollbar_area,
switcher_theme->item_height, nr_cols, nr_rows, nr_visible_rows,
switcher_theme->item_active_border_color,
switcher_theme->item_active_bg_color);
/* background */
struct lab_scene_rect_options bg_opts = {
.border_colors = (float *[1]) { theme->osd_border_color },
.nr_borders = 1,
.border_width = theme->osd_border_width,
.bg_color = theme->osd_bg_color,
.width = nr_cols * switcher_theme->item_width + 2 * padding,
.height = nr_rows * switcher_theme->item_height + 2 * padding,
.width = items_width + 2 * padding,
.height = items_height + 2 * padding,
};
struct lab_scene_rect *bg =
lab_scene_rect_create(output->cycle_osd.tree, &bg_opts);
lab_scene_rect_create(osd_output->tree, &bg_opts);
wlr_scene_node_lower_to_bottom(&bg->tree->node);
/* center */
@ -277,15 +295,18 @@ cycle_osd_thumbnail_create(struct output *output)
&output_box);
int lx = output_box.x + (output_box.width - bg_opts.width) / 2;
int ly = output_box.y + (output_box.height - bg_opts.height) / 2;
wlr_scene_node_set_position(&output->cycle_osd.tree->node, lx, ly);
wlr_scene_node_set_position(&osd_output->tree->node, lx, ly);
}
static void
cycle_osd_thumbnail_update(struct output *output)
cycle_osd_thumbnail_update(struct cycle_osd_output *osd_output)
{
struct server *server = osd_output->output->server;
cycle_osd_scroll_update(osd_output);
struct cycle_osd_thumbnail_item *item;
wl_list_for_each(item, &output->cycle_osd.items, base.link) {
bool active = (item->base.view == output->server->cycle.selected_view);
wl_list_for_each(item, &osd_output->items, base.link) {
bool active = (item->base.view == server->cycle.selected_view);
wlr_scene_node_set_enabled(&item->active_bg->tree->node, active);
wlr_scene_node_set_enabled(
&item->active_label->scene_buffer->node, active);
@ -295,6 +316,6 @@ cycle_osd_thumbnail_update(struct output *output)
}
struct cycle_osd_impl cycle_osd_thumbnail_impl = {
.create = cycle_osd_thumbnail_create,
.init = cycle_osd_thumbnail_init,
.update = cycle_osd_thumbnail_update,
};

View file

@ -26,8 +26,7 @@ handle_request_maximize(struct wl_listener *listener, void *data)
struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data;
view_maximize(wlr_toplevel->view,
event->maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE,
/*store_natural_geometry*/ true);
event->maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE);
}
static void

View file

@ -273,17 +273,12 @@ snap_to_edge(struct view *view)
enum lab_edge edge = edge1 | edge2;
view_set_output(view, output);
/*
* Don't store natural geometry here (it was
* stored already in interactive_begin())
*/
if (edge == LAB_EDGE_TOP && rc.snap_top_maximize) {
/* <topMaximize> */
view_maximize(view, VIEW_AXIS_BOTH,
/*store_natural_geometry*/ false);
view_maximize(view, VIEW_AXIS_BOTH);
} else {
view_snap_to_edge(view, edge, /*across_outputs*/ false,
/*combine*/ false, /*store_natural_geometry*/ false);
/*combine*/ false);
}
return true;
@ -298,8 +293,7 @@ snap_to_region(struct view *view)
struct region *region = regions_from_cursor(view->server);
if (region) {
view_snap_to_region(view, region,
/*store_natural_geometry*/ false);
view_snap_to_region(view, region);
return true;
}
return false;

View file

@ -542,7 +542,6 @@ handle_new_output(struct wl_listener *listener, void *data)
wl_signal_add(&wlr_output->events.request_state, &output->request_state);
wl_list_init(&output->regions);
wl_list_init(&output->cycle_osd.items);
/*
* Create layer-trees (background, bottom, top and overlay) and

View file

@ -100,6 +100,7 @@ reload_config_and_theme(struct server *server)
view_reload_ssd(view);
}
cycle_finish(server, /*switch_focus*/ false);
menu_reconfigure(server);
seat_reconfigure(server);
regions_reconfigure(server);
@ -556,6 +557,7 @@ server_init(struct server *server)
wl_list_init(&server->views);
wl_list_init(&server->unmanaged_surfaces);
wl_list_init(&server->cycle.views);
wl_list_init(&server->cycle.osd_outputs);
server->scene = wlr_scene_create();
if (!server->scene) {
@ -569,21 +571,22 @@ server_init(struct server *server)
* z-order for nodes which cover the whole work-area. For per-output
* scene-trees, see handle_new_output() in src/output.c
*
* | Type | Scene Tree | Per Output | Example
* | ------------------- | ---------------- | ---------- | -------
* | ext-session | lock-screen | Yes | swaylock
* | window switcher OSD | cycle_osd_tree | Yes |
* | compositor-menu | menu_tree | No | root-menu
* | layer-shell | layer-popups | Yes |
* | layer-shell | overlay-layer | Yes |
* | layer-shell | top-layer | Yes | waybar
* | xwayland-OR | unmanaged | No | dmenu
* | xdg-popups | xdg-popups | No |
* | toplevels windows | always-on-top | No |
* | toplevels windows | normal | No | firefox
* | toplevels windows | always-on-bottom | No | pcmanfm-qt --desktop
* | layer-shell | bottom-layer | Yes | waybar
* | layer-shell | background-layer | Yes | swaybg
* | Scene Tree | Description
* | ---------------------------------- | -------------------------------------
* | output->session_lock_tree | session lock surfaces (e.g. swaylock)
* | output->cycle_osd_tree | window switcher's on-screen display
* | server->cycle_preview_tree | window switcher's previewed window
* | server->menu_tree | labwc's server-side menus
* | output->layer_popup_tree | xdg popups on layer surfaces
* | output->layer_tree[3] | overlay layer surfaces (e.g. rofi)
* | output->layer_tree[2] | top layer surfaces (e.g. waybar)
* | server->unmanaged_tree | unmanaged X11 surfaces (e.g. dmenu)
* | server->xdg_popup_tree | xdg popups on xdg windows
* | server->view_tree_always_on_top | always-on-top xdg/X11 windows
* | server->view_tree | normal xdg/X11 windows (e.g. firefox)
* | server->view_tree_always_on_bottom | always-on-bottom xdg/X11 windows
* | output->layer_tree[1] | bottom layer surfaces
* | output->layer_tree[0] | background layer surfaces (e.g. swaybg)
*/
if (server->renderer->features.input_color_transform) {
@ -633,6 +636,7 @@ server_init(struct server *server)
server->unmanaged_tree = wlr_scene_tree_create(&server->scene->tree);
#endif
server->menu_tree = wlr_scene_tree_create(&server->scene->tree);
server->cycle_preview_tree = wlr_scene_tree_create(&server->scene->tree);
workspaces_init(server);

View file

@ -614,7 +614,7 @@ view_move_relative(struct view *view, int x, int y)
if (view->fullscreen) {
return;
}
view_maximize(view, VIEW_AXIS_NONE, /*store_natural_geometry*/ false);
view_maximize(view, VIEW_AXIS_NONE);
if (view_is_tiled(view)) {
view_set_untiled(view);
view_move_resize(view, view->natural_geometry);
@ -632,7 +632,7 @@ view_move_to_cursor(struct view *view)
return;
}
view_set_fullscreen(view, false);
view_maximize(view, VIEW_AXIS_NONE, /*store_natural_geometry*/ false);
view_maximize(view, VIEW_AXIS_NONE);
if (view_is_tiled(view)) {
view_set_untiled(view);
view_move_resize(view, view->natural_geometry);
@ -1375,9 +1375,15 @@ view_set_untiled(struct view *view)
view_notify_tiled(view);
}
static bool
in_interactive_move(struct view *view)
{
return (view->server->input_mode == LAB_INPUT_STATE_MOVE
&& view->server->grabbed_view == view);
}
void
view_maximize(struct view *view, enum view_axis axis,
bool store_natural_geometry)
view_maximize(struct view *view, enum view_axis axis)
{
assert(view);
@ -1389,6 +1395,7 @@ view_maximize(struct view *view, enum view_axis axis,
return;
}
bool store_natural_geometry = !in_interactive_move(view);
view_set_shade(view, false);
if (axis != VIEW_AXIS_NONE) {
@ -1440,8 +1447,7 @@ view_toggle_maximize(struct view *view, enum view_axis axis)
case VIEW_AXIS_HORIZONTAL:
case VIEW_AXIS_VERTICAL:
/* Toggle one axis (XOR) */
view_maximize(view, view->maximized ^ axis,
/*store_natural_geometry*/ true);
view_maximize(view, view->maximized ^ axis);
break;
case VIEW_AXIS_BOTH:
/*
@ -1449,8 +1455,7 @@ view_toggle_maximize(struct view *view, enum view_axis axis)
* maximized, otherwise unmaximize.
*/
view_maximize(view, (view->maximized == VIEW_AXIS_BOTH) ?
VIEW_AXIS_NONE : VIEW_AXIS_BOTH,
/*store_natural_geometry*/ true);
VIEW_AXIS_NONE : VIEW_AXIS_BOTH);
break;
default:
break;
@ -2064,7 +2069,7 @@ view_placement_parse(const char *policy)
void
view_snap_to_edge(struct view *view, enum lab_edge edge,
bool across_outputs, bool combine, bool store_natural_geometry)
bool across_outputs, bool combine)
{
assert(view);
@ -2078,6 +2083,7 @@ view_snap_to_edge(struct view *view, enum lab_edge edge,
return;
}
bool store_natural_geometry = !in_interactive_move(view);
view_set_shade(view, false);
if (lab_edge_is_cardinal(edge) && view->maximized == VIEW_AXIS_NONE
@ -2124,8 +2130,7 @@ view_snap_to_edge(struct view *view, enum lab_edge edge,
if (view->maximized != VIEW_AXIS_NONE) {
/* Unmaximize + keep using existing natural_geometry */
view_maximize(view, VIEW_AXIS_NONE,
/*store_natural_geometry*/ false);
view_maximize(view, VIEW_AXIS_NONE);
} else if (store_natural_geometry) {
/* store current geometry as new natural_geometry */
view_store_natural_geometry(view);
@ -2139,8 +2144,7 @@ view_snap_to_edge(struct view *view, enum lab_edge edge,
}
void
view_snap_to_region(struct view *view, struct region *region,
bool store_natural_geometry)
view_snap_to_region(struct view *view, struct region *region)
{
assert(view);
assert(region);
@ -2155,12 +2159,12 @@ view_snap_to_region(struct view *view, struct region *region,
return;
}
bool store_natural_geometry = !in_interactive_move(view);
view_set_shade(view, false);
if (view->maximized != VIEW_AXIS_NONE) {
/* Unmaximize + keep using existing natural_geometry */
view_maximize(view, VIEW_AXIS_NONE,
/*store_natural_geometry*/ false);
view_maximize(view, VIEW_AXIS_NONE);
} else if (store_natural_geometry) {
/* store current geometry as new natural_geometry */
view_store_natural_geometry(view);
@ -2193,7 +2197,7 @@ view_move_to_output(struct view *view, struct output *output)
view_apply_tiled_geometry(view);
} else if (view->tiled_region) {
struct region *region = regions_from_name(view->tiled_region->name, output);
view_snap_to_region(view, region, /*store_natural_geometry*/ false);
view_snap_to_region(view, region);
}
}

View file

@ -225,8 +225,7 @@ handle_commit(struct wl_listener *listener, void *data)
set_fullscreen_from_request(view, &toplevel->requested);
}
if (toplevel->requested.maximized) {
view_maximize(view, VIEW_AXIS_BOTH,
/*store_natural_geometry*/ true);
view_maximize(view, VIEW_AXIS_BOTH);
}
return;
}
@ -505,8 +504,7 @@ handle_request_maximize(struct wl_listener *listener, void *data)
view_set_output(view, output_nearest_to_cursor(view->server));
}
bool maximized = toplevel->requested.maximized;
view_maximize(view, maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE,
/*store_natural_geometry*/ true);
view_maximize(view, maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE);
}
static void

View file

@ -470,7 +470,7 @@ handle_request_maximize(struct wl_listener *listener, void *data)
if (surf->maximized_horz) {
maximize |= VIEW_AXIS_HORIZONTAL;
}
view_maximize(view, maximize, /*store_natural_geometry*/ true);
view_maximize(view, maximize);
}
static void
@ -704,7 +704,7 @@ handle_map_request(struct wl_listener *listener, void *data)
if (xsurface->maximized_vert) {
axis |= VIEW_AXIS_VERTICAL;
}
view_maximize(view, axis, /*store_natural_geometry*/ true);
view_maximize(view, axis);
/*
* We could also call set_initial_position() here, but it's not
* really necessary until the view is actually mapped (and at