2021-09-24 21:45:48 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2022-04-26 23:56:27 +02:00
|
|
|
#include <assert.h>
|
2021-08-16 07:16:56 +01:00
|
|
|
#include <cairo.h>
|
2021-07-23 21:15:55 +01:00
|
|
|
#include <wlr/util/log.h>
|
2023-06-29 17:45:33 +01:00
|
|
|
#include <wlr/util/box.h>
|
2023-08-20 11:56:50 +01:00
|
|
|
#include "common/array.h"
|
2021-08-16 07:16:56 +01:00
|
|
|
#include "common/buf.h"
|
|
|
|
|
#include "common/font.h"
|
2025-03-14 16:53:40 +09:00
|
|
|
#include "common/scaled-font-buffer.h"
|
2025-03-14 19:33:28 +09:00
|
|
|
#include "common/scaled-icon-buffer.h"
|
2025-03-14 16:53:40 +09:00
|
|
|
#include "common/scaled-rect-buffer.h"
|
2022-04-26 23:56:27 +02:00
|
|
|
#include "common/scene-helpers.h"
|
2020-09-28 20:41:41 +01:00
|
|
|
#include "config/rcxml.h"
|
|
|
|
|
#include "labwc.h"
|
2022-06-15 02:02:50 +02:00
|
|
|
#include "node.h"
|
2024-04-10 17:39:31 -05:00
|
|
|
#include "osd.h"
|
|
|
|
|
#include "theme.h"
|
2022-11-21 10:10:39 -05:00
|
|
|
#include "view.h"
|
2023-05-20 10:20:36 +01:00
|
|
|
#include "window-rules.h"
|
2022-06-15 02:02:50 +02:00
|
|
|
#include "workspaces.h"
|
2020-08-20 21:13:04 +01:00
|
|
|
|
2025-03-14 16:53:40 +09:00
|
|
|
struct osd_scene_item {
|
|
|
|
|
struct view *view;
|
|
|
|
|
struct wlr_scene_node *highlight_outline;
|
|
|
|
|
};
|
|
|
|
|
|
2025-03-14 16:53:18 +09:00
|
|
|
static void update_osd(struct server *server);
|
|
|
|
|
|
2022-02-12 22:04:27 +00:00
|
|
|
static void
|
2025-03-14 16:53:40 +09:00
|
|
|
destroy_osd_scenes(struct server *server)
|
2022-02-12 22:04:27 +00:00
|
|
|
{
|
2025-03-14 16:53:40 +09:00
|
|
|
struct output *output;
|
|
|
|
|
wl_list_for_each(output, &server->outputs, link) {
|
|
|
|
|
wlr_scene_node_destroy(&output->osd_scene.tree->node);
|
|
|
|
|
output->osd_scene.tree = NULL;
|
|
|
|
|
|
|
|
|
|
wl_array_release(&output->osd_scene.items);
|
|
|
|
|
wl_array_init(&output->osd_scene.items);
|
2022-02-12 22:04:27 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-04 20:15:03 +02:00
|
|
|
static void
|
|
|
|
|
osd_update_preview_outlines(struct view *view)
|
|
|
|
|
{
|
|
|
|
|
/* Create / Update preview outline tree */
|
|
|
|
|
struct server *server = view->server;
|
2022-08-22 00:42:01 +02:00
|
|
|
struct multi_rect *rect = view->server->osd_state.preview_outline;
|
2022-07-04 20:15:03 +02:00
|
|
|
if (!rect) {
|
2024-04-15 20:22:44 +09:00
|
|
|
int line_width = server->theme->osd_window_switcher_preview_border_width;
|
2022-07-04 20:15:03 +02:00
|
|
|
float *colors[] = {
|
2024-04-15 20:22:44 +09:00
|
|
|
server->theme->osd_window_switcher_preview_border_color[0],
|
|
|
|
|
server->theme->osd_window_switcher_preview_border_color[1],
|
|
|
|
|
server->theme->osd_window_switcher_preview_border_color[2],
|
2022-07-04 20:15:03 +02:00
|
|
|
};
|
|
|
|
|
rect = multi_rect_create(&server->scene->tree, colors, line_width);
|
|
|
|
|
wlr_scene_node_place_above(&rect->tree->node, &server->menu_tree->node);
|
2022-08-22 00:42:01 +02:00
|
|
|
server->osd_state.preview_outline = rect;
|
2022-07-04 20:15:03 +02:00
|
|
|
}
|
2022-08-27 13:26:30 +01:00
|
|
|
|
2022-07-04 20:15:03 +02:00
|
|
|
struct wlr_box geo = ssd_max_extents(view);
|
2022-08-27 13:26:30 +01:00
|
|
|
multi_rect_set_size(rect, geo.width, geo.height);
|
|
|
|
|
wlr_scene_node_set_position(&rect->tree->node, geo.x, geo.y);
|
2022-07-04 20:15:03 +02:00
|
|
|
}
|
|
|
|
|
|
2024-12-30 04:57:39 +09:00
|
|
|
/*
|
|
|
|
|
* Returns the view to select next in the window switcher.
|
|
|
|
|
* If !start_view, the second focusable view is returned.
|
|
|
|
|
*/
|
|
|
|
|
static struct view *
|
|
|
|
|
get_next_cycle_view(struct server *server, struct view *start_view,
|
|
|
|
|
enum lab_cycle_dir dir)
|
|
|
|
|
{
|
|
|
|
|
struct view *(*iter)(struct wl_list *head, struct view *view,
|
|
|
|
|
enum lab_view_criteria criteria);
|
|
|
|
|
bool forwards = dir == LAB_CYCLE_DIR_FORWARD;
|
|
|
|
|
iter = forwards ? view_next_no_head_stop : view_prev_no_head_stop;
|
|
|
|
|
|
|
|
|
|
enum lab_view_criteria criteria = rc.window_switcher.criteria;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Views are listed in stacking order, topmost first. Usually the
|
|
|
|
|
* topmost view is already focused, so when iterating in the forward
|
|
|
|
|
* direction we pre-select the view second from the top:
|
|
|
|
|
*
|
|
|
|
|
* View #1 (on top, currently focused)
|
|
|
|
|
* View #2 (pre-selected)
|
|
|
|
|
* View #3
|
|
|
|
|
* ...
|
|
|
|
|
*/
|
|
|
|
|
if (!start_view && forwards) {
|
|
|
|
|
start_view = iter(&server->views, NULL, criteria);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return iter(&server->views, start_view, criteria);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-22 02:54:40 +02:00
|
|
|
void
|
|
|
|
|
osd_on_view_destroy(struct view *view)
|
|
|
|
|
{
|
|
|
|
|
assert(view);
|
|
|
|
|
struct osd_state *osd_state = &view->server->osd_state;
|
|
|
|
|
|
2024-12-28 17:42:26 +09:00
|
|
|
if (view->server->input_mode != LAB_INPUT_STATE_WINDOW_SWITCHER) {
|
2022-08-22 02:54:40 +02:00
|
|
|
/* OSD not active, no need for clean up */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (osd_state->cycle_view == view) {
|
|
|
|
|
/*
|
|
|
|
|
* If we are the current OSD selected view, cycle
|
|
|
|
|
* to the next because we are dying.
|
|
|
|
|
*/
|
2022-09-26 10:58:13 +02:00
|
|
|
|
|
|
|
|
/* Also resets preview node */
|
2024-12-30 04:57:39 +09:00
|
|
|
osd_state->cycle_view = get_next_cycle_view(view->server,
|
2022-08-22 02:54:40 +02:00
|
|
|
osd_state->cycle_view, LAB_CYCLE_DIR_BACKWARD);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If we cycled back to ourselves, then we have no more windows.
|
|
|
|
|
* Just close the OSD for good.
|
|
|
|
|
*/
|
|
|
|
|
if (osd_state->cycle_view == view || !osd_state->cycle_view) {
|
|
|
|
|
/* osd_finish() additionally resets cycle_view to NULL */
|
|
|
|
|
osd_finish(view->server);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-26 10:58:13 +02:00
|
|
|
if (osd_state->cycle_view) {
|
2025-03-14 16:53:40 +09:00
|
|
|
/* Recreate the OSD to reflect the view has now gone. */
|
|
|
|
|
destroy_osd_scenes(view->server);
|
2025-03-14 16:53:18 +09:00
|
|
|
update_osd(view->server);
|
2022-09-26 10:58:13 +02:00
|
|
|
}
|
|
|
|
|
|
2022-08-22 02:54:40 +02:00
|
|
|
if (view->scene_tree) {
|
|
|
|
|
struct wlr_scene_node *node = &view->scene_tree->node;
|
|
|
|
|
if (osd_state->preview_anchor == node) {
|
|
|
|
|
/*
|
|
|
|
|
* If we are the anchor for the current OSD selected view,
|
|
|
|
|
* replace the anchor with the node before us.
|
|
|
|
|
*/
|
|
|
|
|
osd_state->preview_anchor = lab_wlr_scene_get_prev_node(node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-30 00:44:12 +09:00
|
|
|
static void
|
|
|
|
|
restore_preview_node(struct server *server)
|
|
|
|
|
{
|
|
|
|
|
struct osd_state *osd_state = &server->osd_state;
|
|
|
|
|
if (osd_state->preview_node) {
|
|
|
|
|
wlr_scene_node_reparent(osd_state->preview_node,
|
|
|
|
|
osd_state->preview_parent);
|
|
|
|
|
|
|
|
|
|
if (osd_state->preview_anchor) {
|
|
|
|
|
wlr_scene_node_place_above(osd_state->preview_node,
|
|
|
|
|
osd_state->preview_anchor);
|
|
|
|
|
} else {
|
|
|
|
|
/* Selected view was the first node */
|
|
|
|
|
wlr_scene_node_lower_to_bottom(osd_state->preview_node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Node was disabled / minimized before, disable again */
|
|
|
|
|
if (!osd_state->preview_was_enabled) {
|
|
|
|
|
wlr_scene_node_set_enabled(osd_state->preview_node, false);
|
|
|
|
|
}
|
|
|
|
|
osd_state->preview_node = NULL;
|
|
|
|
|
osd_state->preview_parent = NULL;
|
|
|
|
|
osd_state->preview_anchor = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-30 01:05:17 +09:00
|
|
|
void
|
|
|
|
|
osd_begin(struct server *server, enum lab_cycle_dir direction)
|
|
|
|
|
{
|
|
|
|
|
if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-30 04:57:39 +09:00
|
|
|
server->osd_state.cycle_view = get_next_cycle_view(server,
|
2024-12-30 01:05:17 +09:00
|
|
|
server->osd_state.cycle_view, direction);
|
|
|
|
|
|
|
|
|
|
seat_focus_override_begin(&server->seat,
|
|
|
|
|
LAB_INPUT_STATE_WINDOW_SWITCHER, LAB_CURSOR_DEFAULT);
|
2025-03-14 16:53:18 +09:00
|
|
|
update_osd(server);
|
2025-02-22 20:38:59 +09:00
|
|
|
|
|
|
|
|
/* Update cursor, in case it is within the area covered by OSD */
|
|
|
|
|
cursor_update_focus(server);
|
2024-12-30 01:05:17 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
osd_cycle(struct server *server, enum lab_cycle_dir direction)
|
|
|
|
|
{
|
|
|
|
|
assert(server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER);
|
|
|
|
|
|
2024-12-30 04:57:39 +09:00
|
|
|
server->osd_state.cycle_view = get_next_cycle_view(server,
|
2024-12-30 01:05:17 +09:00
|
|
|
server->osd_state.cycle_view, direction);
|
2025-03-14 16:53:18 +09:00
|
|
|
update_osd(server);
|
2024-12-30 01:05:17 +09:00
|
|
|
}
|
|
|
|
|
|
2022-02-12 22:04:27 +00:00
|
|
|
void
|
|
|
|
|
osd_finish(struct server *server)
|
|
|
|
|
{
|
2024-12-30 00:44:12 +09:00
|
|
|
restore_preview_node(server);
|
Clear keyboard/pointer focus on Move/Resize, window switcher and menu
The previous revert fixed the problem of stuck modifier keys with
keybinds in Blender, but made Firefox show its menu bar with Alt-*
keybinds. This is fundamentally inevitable due to the limitation of
wayland protocol, but at least for the default Alt-Tab keybind for
window switcher, we can mitigate this problem by clearing the keyboard
focus when the window switcher is activated. This is what KWin does, and
we decided to follow that.
So in this commit, keyboard and pointer focus are temporarily cleared
while Move/Resize, window switcher and menu interactions and restored
after them. We slightly deviate from KWin as KWin doesn't clear the
keyboard focus while Move/Resize, but it solves our existing problem
that Firefox shows its menu bar after dragging it with default Alt-Drag
mousebind, and this is what Mutter does.
We considered other solutions, but they don't work well:
1. Send wl_keyboard.{leave,enter} every time keybinds/mousebinds are
triggered. This solves the Firefox's menu bar problem, but that
sounds like a workaround and sending unnecessary events every time is
not desirable.
2. Send release events for both modifiers and keys even when they are
bound to keybinds. This is what Mutter is doing, but it looks like an
implementation issue and violates wayland protocol.
2024-12-29 00:48:55 +09:00
|
|
|
seat_focus_override_end(&server->seat);
|
|
|
|
|
|
2022-04-26 23:56:27 +02:00
|
|
|
server->osd_state.preview_node = NULL;
|
|
|
|
|
server->osd_state.preview_anchor = NULL;
|
2024-12-31 13:00:32 +09:00
|
|
|
server->osd_state.cycle_view = NULL;
|
2022-04-26 22:06:22 +02:00
|
|
|
|
2025-03-14 16:53:40 +09:00
|
|
|
destroy_osd_scenes(server);
|
|
|
|
|
|
2022-08-22 00:42:01 +02:00
|
|
|
if (server->osd_state.preview_outline) {
|
2022-07-04 20:15:03 +02:00
|
|
|
/* Destroy the whole multi_rect so we can easily react to new themes */
|
2022-08-22 00:42:01 +02:00
|
|
|
wlr_scene_node_destroy(&server->osd_state.preview_outline->tree->node);
|
|
|
|
|
server->osd_state.preview_outline = NULL;
|
2022-07-04 20:15:03 +02:00
|
|
|
}
|
2022-12-12 00:56:55 +00:00
|
|
|
|
|
|
|
|
/* Hiding OSD may need a cursor change */
|
|
|
|
|
cursor_update_focus(server);
|
2022-02-12 22:04:27 +00:00
|
|
|
}
|
|
|
|
|
|
2022-04-26 23:56:27 +02:00
|
|
|
static void
|
|
|
|
|
preview_cycled_view(struct view *view)
|
|
|
|
|
{
|
|
|
|
|
assert(view);
|
|
|
|
|
assert(view->scene_tree);
|
|
|
|
|
struct osd_state *osd_state = &view->server->osd_state;
|
|
|
|
|
|
|
|
|
|
/* Move previous selected node back to its original place */
|
2024-12-30 00:44:12 +09:00
|
|
|
restore_preview_node(view->server);
|
2022-04-26 23:56:27 +02:00
|
|
|
|
2024-03-05 21:01:39 +00:00
|
|
|
/* Store some pointers so we can reset the preview later on */
|
2022-04-26 23:56:27 +02:00
|
|
|
osd_state->preview_node = &view->scene_tree->node;
|
2024-03-05 21:01:39 +00:00
|
|
|
osd_state->preview_parent = view->scene_tree->node.parent;
|
|
|
|
|
|
|
|
|
|
/* Remember the sibling right before the selected node */
|
2022-04-26 23:56:27 +02:00
|
|
|
osd_state->preview_anchor = lab_wlr_scene_get_prev_node(
|
|
|
|
|
osd_state->preview_node);
|
2022-07-06 07:19:28 +02:00
|
|
|
while (osd_state->preview_anchor && !osd_state->preview_anchor->data) {
|
|
|
|
|
/* Ignore non-view nodes */
|
|
|
|
|
osd_state->preview_anchor = lab_wlr_scene_get_prev_node(
|
|
|
|
|
osd_state->preview_anchor);
|
|
|
|
|
}
|
2022-04-26 23:56:27 +02:00
|
|
|
|
|
|
|
|
/* Store node enabled / minimized state and force-enable if disabled */
|
|
|
|
|
osd_state->preview_was_enabled = osd_state->preview_node->enabled;
|
|
|
|
|
if (!osd_state->preview_was_enabled) {
|
|
|
|
|
wlr_scene_node_set_enabled(osd_state->preview_node, true);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-05 21:01:39 +00:00
|
|
|
/*
|
|
|
|
|
* 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(osd_state->preview_node,
|
|
|
|
|
view->server->view_tree_always_on_top);
|
|
|
|
|
|
2022-04-26 23:56:27 +02:00
|
|
|
/* Finally raise selected node to the top */
|
|
|
|
|
wlr_scene_node_raise_to_top(osd_state->preview_node);
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-12 00:50:57 +00:00
|
|
|
static void
|
2025-03-14 16:53:40 +09:00
|
|
|
create_osd_scene(struct output *output, struct wl_array *views)
|
2019-12-27 21:45:00 +00:00
|
|
|
{
|
2025-03-14 16:53:40 +09:00
|
|
|
struct server *server = output->server;
|
2023-04-19 14:44:41 +01:00
|
|
|
struct theme *theme = server->theme;
|
2025-03-14 16:53:40 +09:00
|
|
|
bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1;
|
|
|
|
|
const char *workspace_name = server->workspaces.current->name;
|
2023-04-19 14:44:41 +01:00
|
|
|
|
2025-03-14 16:53:40 +09:00
|
|
|
int w = theme->osd_window_switcher_width;
|
|
|
|
|
if (theme->osd_window_switcher_width_is_percent) {
|
|
|
|
|
w = output->wlr_output->width
|
|
|
|
|
* theme->osd_window_switcher_width / 100;
|
|
|
|
|
}
|
|
|
|
|
int h = wl_array_len(views) * rc.theme->osd_window_switcher_item_height
|
|
|
|
|
+ 2 * rc.theme->osd_border_width
|
|
|
|
|
+ 2 * rc.theme->osd_window_switcher_padding;
|
|
|
|
|
if (show_workspace) {
|
|
|
|
|
/* workspace indicator */
|
|
|
|
|
h += theme->osd_window_switcher_item_height;
|
|
|
|
|
}
|
2022-12-12 00:50:57 +00:00
|
|
|
|
2025-03-14 16:53:40 +09:00
|
|
|
output->osd_scene.tree = wlr_scene_tree_create(output->osd_tree);
|
2022-12-12 00:50:57 +00:00
|
|
|
|
2025-03-14 16:53:40 +09:00
|
|
|
float *text_color = theme->osd_label_text_color;
|
|
|
|
|
float *bg_color = theme->osd_bg_color;
|
2022-12-12 00:50:57 +00:00
|
|
|
|
2025-03-14 16:53:40 +09:00
|
|
|
/* Draw background */
|
|
|
|
|
scaled_rect_buffer_create(output->osd_scene.tree, w, h,
|
|
|
|
|
theme->osd_border_width, bg_color, theme->osd_border_color);
|
2022-12-12 00:50:57 +00:00
|
|
|
|
2023-06-29 21:29:43 +01:00
|
|
|
int y = theme->osd_border_width + theme->osd_window_switcher_padding;
|
2022-12-12 00:50:57 +00:00
|
|
|
|
|
|
|
|
/* Draw workspace indicator */
|
|
|
|
|
if (show_workspace) {
|
2025-03-14 16:53:40 +09:00
|
|
|
struct font font = rc.font_osd;
|
|
|
|
|
font.weight = FONT_WEIGHT_BOLD;
|
|
|
|
|
|
2022-12-12 00:50:57 +00:00
|
|
|
/* Center workspace indicator on the x axis */
|
2025-03-14 16:53:40 +09:00
|
|
|
int x = (w - font_width(&font, workspace_name)) / 2;
|
|
|
|
|
struct scaled_font_buffer *font_buffer =
|
|
|
|
|
scaled_font_buffer_create(output->osd_scene.tree);
|
|
|
|
|
wlr_scene_node_set_position(&font_buffer->scene_buffer->node,
|
|
|
|
|
x, y + theme->osd_window_switcher_item_active_border_width);
|
|
|
|
|
scaled_font_buffer_update(font_buffer, workspace_name, 0,
|
|
|
|
|
&font, text_color, bg_color);
|
2023-04-24 21:31:28 +01:00
|
|
|
y += theme->osd_window_switcher_item_height;
|
2022-04-20 17:45:10 +01:00
|
|
|
}
|
2021-10-13 21:29:32 +01:00
|
|
|
|
2024-04-14 14:20:57 -04:00
|
|
|
struct buf buf = BUF_INIT;
|
2022-06-15 02:02:50 +02:00
|
|
|
|
2023-07-01 14:56:13 +02:00
|
|
|
/* This is the width of the area available for text fields */
|
|
|
|
|
int available_width = w - 2 * theme->osd_border_width
|
|
|
|
|
- 2 * theme->osd_window_switcher_padding
|
|
|
|
|
- 2 * theme->osd_window_switcher_item_active_border_width;
|
2023-06-29 17:45:33 +01:00
|
|
|
|
2022-12-12 00:50:57 +00:00
|
|
|
/* Draw text for each node */
|
2023-08-10 15:46:00 +01:00
|
|
|
struct view **view;
|
2023-08-12 19:59:05 +01:00
|
|
|
wl_array_for_each(view, views) {
|
2025-03-14 16:53:40 +09:00
|
|
|
struct osd_scene_item *item =
|
|
|
|
|
wl_array_add(&output->osd_scene.items, sizeof(*item));
|
|
|
|
|
item->view = *view;
|
2023-06-29 17:45:33 +01:00
|
|
|
/*
|
|
|
|
|
* OSD border
|
|
|
|
|
* +---------------------------------+
|
|
|
|
|
* | |
|
|
|
|
|
* | item border |
|
|
|
|
|
* |+-------------------------------+|
|
|
|
|
|
* || ||
|
|
|
|
|
* ||padding between each field ||
|
|
|
|
|
* ||| field-1 | field-2 | field-n |||
|
|
|
|
|
* || ||
|
|
|
|
|
* || ||
|
|
|
|
|
* |+-------------------------------+|
|
|
|
|
|
* | |
|
|
|
|
|
* | |
|
|
|
|
|
* +---------------------------------+
|
|
|
|
|
*/
|
2023-07-05 19:49:56 +01:00
|
|
|
int x = theme->osd_border_width
|
|
|
|
|
+ theme->osd_window_switcher_padding
|
|
|
|
|
+ theme->osd_window_switcher_item_active_border_width
|
|
|
|
|
+ theme->osd_window_switcher_item_padding_x;
|
2025-03-14 16:53:40 +09:00
|
|
|
struct wlr_scene_tree *item_root =
|
|
|
|
|
wlr_scene_tree_create(output->osd_scene.tree);
|
2023-06-29 17:45:33 +01:00
|
|
|
|
|
|
|
|
int nr_fields = wl_list_length(&rc.window_switcher.fields);
|
2023-04-19 14:44:41 +01:00
|
|
|
struct window_switcher_field *field;
|
|
|
|
|
wl_list_for_each(field, &rc.window_switcher.fields, link) {
|
2023-06-29 17:45:33 +01:00
|
|
|
int field_width = (available_width - (nr_fields + 1)
|
|
|
|
|
* theme->osd_window_switcher_item_padding_x)
|
|
|
|
|
* field->width / 100.0;
|
2025-03-14 19:33:28 +09:00
|
|
|
struct wlr_scene_node *node = NULL;
|
|
|
|
|
|
|
|
|
|
if (field->content == LAB_FIELD_ICON) {
|
|
|
|
|
int icon_size = font_height(&rc.font_osd);
|
|
|
|
|
struct scaled_icon_buffer *icon_buffer =
|
|
|
|
|
scaled_icon_buffer_create(item_root,
|
|
|
|
|
server, icon_size, icon_size);
|
|
|
|
|
scaled_icon_buffer_set_app_id(icon_buffer,
|
|
|
|
|
view_get_string_prop(*view, "app_id"));
|
|
|
|
|
node = &icon_buffer->scene_buffer->node;
|
|
|
|
|
} else {
|
|
|
|
|
buf_clear(&buf);
|
|
|
|
|
osd_field_get_content(field, &buf, *view);
|
|
|
|
|
|
|
|
|
|
struct scaled_font_buffer *font_buffer =
|
|
|
|
|
scaled_font_buffer_create(item_root);
|
|
|
|
|
scaled_font_buffer_update(font_buffer, buf.data, field_width,
|
|
|
|
|
&rc.font_osd, text_color, bg_color);
|
|
|
|
|
node = &font_buffer->scene_buffer->node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wlr_scene_node_set_position(node, x,
|
|
|
|
|
y + theme->osd_window_switcher_item_padding_y
|
2025-03-14 16:53:40 +09:00
|
|
|
+ theme->osd_window_switcher_item_active_border_width);
|
2023-04-24 21:31:28 +01:00
|
|
|
x += field_width + theme->osd_window_switcher_item_padding_x;
|
2022-02-13 13:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
2025-03-14 16:53:40 +09:00
|
|
|
/* Highlight around selected window's item */
|
|
|
|
|
int highlight_w = w - 2 * theme->osd_border_width
|
|
|
|
|
- 2 * theme->osd_window_switcher_padding;
|
|
|
|
|
int highlight_h = theme->osd_window_switcher_item_height;
|
|
|
|
|
int highlight_x = theme->osd_border_width
|
|
|
|
|
+ theme->osd_window_switcher_padding;
|
|
|
|
|
int border_width = theme->osd_window_switcher_item_active_border_width;
|
|
|
|
|
float transparent[4] = {0};
|
|
|
|
|
|
|
|
|
|
struct scaled_rect_buffer *highlight_buffer = scaled_rect_buffer_create(
|
|
|
|
|
output->osd_scene.tree, highlight_w, highlight_h,
|
|
|
|
|
border_width, transparent, text_color);
|
|
|
|
|
assert(highlight_buffer);
|
|
|
|
|
item->highlight_outline = &highlight_buffer->scene_buffer->node;
|
|
|
|
|
wlr_scene_node_set_position(item->highlight_outline, highlight_x, y);
|
|
|
|
|
wlr_scene_node_set_enabled(item->highlight_outline, false);
|
2022-02-09 16:38:07 -05:00
|
|
|
|
2023-07-05 19:49:56 +01:00
|
|
|
y += theme->osd_window_switcher_item_height;
|
2022-12-12 00:50:57 +00:00
|
|
|
}
|
2024-04-14 14:20:57 -04:00
|
|
|
buf_reset(&buf);
|
2022-02-09 16:38:07 -05:00
|
|
|
|
2025-03-14 16:53:40 +09:00
|
|
|
/* Center OSD */
|
|
|
|
|
struct wlr_box usable = output_usable_area_in_layout_coords(output);
|
|
|
|
|
wlr_scene_node_set_position(&output->osd_scene.tree->node,
|
|
|
|
|
usable.x + usable.width / 2 - w / 2,
|
|
|
|
|
usable.y + usable.height / 2 - h / 2);
|
2022-12-12 00:50:57 +00:00
|
|
|
}
|
2022-06-15 02:02:50 +02:00
|
|
|
|
2022-12-12 00:50:57 +00:00
|
|
|
static void
|
2025-03-14 16:53:40 +09:00
|
|
|
update_item_highlight(struct output *output)
|
2022-12-12 00:50:57 +00:00
|
|
|
{
|
2025-03-14 16:53:40 +09:00
|
|
|
struct osd_scene_item *item;
|
|
|
|
|
wl_array_for_each(item, &output->osd_scene.items) {
|
|
|
|
|
wlr_scene_node_set_enabled(item->highlight_outline,
|
|
|
|
|
item->view == output->server->osd_state.cycle_view);
|
2024-03-26 05:12:19 -05:00
|
|
|
}
|
2022-12-12 00:50:57 +00:00
|
|
|
}
|
2021-08-16 07:16:56 +01:00
|
|
|
|
2025-03-14 16:53:18 +09:00
|
|
|
static void
|
|
|
|
|
update_osd(struct server *server)
|
2024-03-03 19:51:15 +00:00
|
|
|
{
|
|
|
|
|
struct wl_array views;
|
|
|
|
|
wl_array_init(&views);
|
2024-03-03 20:03:32 +00:00
|
|
|
view_array_append(server, &views, rc.window_switcher.criteria);
|
2024-03-03 19:51:15 +00:00
|
|
|
|
2024-03-04 20:30:31 +00:00
|
|
|
if (!wl_array_len(&views) || !server->osd_state.cycle_view) {
|
2022-12-12 00:50:57 +00:00
|
|
|
osd_finish(server);
|
2024-03-04 20:30:31 +00:00
|
|
|
goto out;
|
2022-12-12 00:50:57 +00:00
|
|
|
}
|
2022-02-09 16:38:07 -05:00
|
|
|
|
2023-08-27 21:11:04 +02:00
|
|
|
if (rc.window_switcher.show && rc.theme->osd_window_switcher_width > 0) {
|
2023-03-08 15:26:49 +01:00
|
|
|
/* Display the actual OSD */
|
|
|
|
|
struct output *output;
|
|
|
|
|
wl_list_for_each(output, &server->outputs, link) {
|
2025-03-14 16:53:40 +09:00
|
|
|
if (!output_is_usable(output)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!output->osd_scene.tree) {
|
|
|
|
|
create_osd_scene(output, &views);
|
|
|
|
|
assert(output->osd_scene.tree);
|
2023-03-08 15:26:49 +01:00
|
|
|
}
|
2025-03-14 16:53:40 +09:00
|
|
|
update_item_highlight(output);
|
2022-06-15 02:02:50 +02:00
|
|
|
}
|
2022-12-12 00:50:57 +00:00
|
|
|
}
|
2019-12-27 21:45:00 +00:00
|
|
|
|
2022-12-12 00:50:57 +00:00
|
|
|
/* Outline current window */
|
2023-04-20 22:31:26 +01:00
|
|
|
if (rc.window_switcher.outlines) {
|
2023-09-25 22:42:06 -04:00
|
|
|
if (view_is_focusable(server->osd_state.cycle_view)) {
|
2022-12-12 00:50:57 +00:00
|
|
|
osd_update_preview_outlines(server->osd_state.cycle_view);
|
2022-02-09 16:38:07 -05:00
|
|
|
}
|
2020-06-18 20:39:55 +01:00
|
|
|
}
|
2022-04-26 23:56:27 +02:00
|
|
|
|
2023-04-20 22:31:26 +01:00
|
|
|
if (rc.window_switcher.preview) {
|
2022-04-26 23:56:27 +02:00
|
|
|
preview_cycled_view(server->osd_state.cycle_view);
|
|
|
|
|
}
|
2024-03-04 20:30:31 +00:00
|
|
|
out:
|
|
|
|
|
wl_array_release(&views);
|
2020-06-18 20:39:55 +01:00
|
|
|
}
|