2021-09-24 21:45:48 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2020-12-30 10:29:21 +00:00
|
|
|
#include "config.h"
|
2022-04-26 23:56:27 +02:00
|
|
|
#include <assert.h>
|
2021-08-16 07:16:56 +01:00
|
|
|
#include <cairo.h>
|
|
|
|
|
#include <drm_fourcc.h>
|
|
|
|
|
#include <pango/pangocairo.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>
|
2022-02-11 23:12:45 +00:00
|
|
|
#include "buffer.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"
|
2022-08-20 20:11:58 +02:00
|
|
|
#include "common/graphic-helpers.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
|
|
|
|
2022-02-12 22:04:27 +00:00
|
|
|
static void
|
2022-04-20 17:45:10 +01:00
|
|
|
destroy_osd_nodes(struct output *output)
|
2022-02-12 22:04:27 +00:00
|
|
|
{
|
|
|
|
|
struct wlr_scene_node *child, *next;
|
2022-06-05 17:12:55 +02:00
|
|
|
struct wl_list *children = &output->osd_tree->children;
|
2022-06-05 15:55:18 +02:00
|
|
|
wl_list_for_each_safe(child, next, children, link) {
|
2022-02-12 22:04:27 +00:00
|
|
|
wlr_scene_node_destroy(child);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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) {
|
|
|
|
|
/* Update the OSD to reflect the view has now gone. */
|
|
|
|
|
osd_update(view->server);
|
|
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
|
osd_update(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);
|
|
|
|
|
osd_update(server);
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
2022-04-20 17:45:10 +01:00
|
|
|
struct output *output;
|
|
|
|
|
wl_list_for_each(output, &server->outputs, link) {
|
|
|
|
|
destroy_osd_nodes(output);
|
|
|
|
|
wlr_scene_node_set_enabled(&output->osd_tree->node, false);
|
|
|
|
|
}
|
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
|
2023-04-19 14:44:41 +01:00
|
|
|
render_osd(struct server *server, cairo_t *cairo, int w, int h,
|
2024-03-03 19:51:15 +00:00
|
|
|
bool show_workspace, const char *workspace_name,
|
|
|
|
|
struct wl_array *views)
|
2019-12-27 21:45:00 +00:00
|
|
|
{
|
2023-04-19 14:44:41 +01:00
|
|
|
struct view *cycle_view = server->osd_state.cycle_view;
|
|
|
|
|
struct theme *theme = server->theme;
|
|
|
|
|
|
2022-12-12 00:50:57 +00:00
|
|
|
cairo_surface_t *surf = cairo_get_target(cairo);
|
|
|
|
|
|
|
|
|
|
/* Draw background */
|
|
|
|
|
set_cairo_color(cairo, theme->osd_bg_color);
|
|
|
|
|
cairo_rectangle(cairo, 0, 0, w, h);
|
|
|
|
|
cairo_fill(cairo);
|
|
|
|
|
|
|
|
|
|
/* Draw border */
|
|
|
|
|
set_cairo_color(cairo, theme->osd_border_color);
|
2023-06-29 17:45:33 +01:00
|
|
|
struct wlr_fbox fbox = {
|
|
|
|
|
.width = w,
|
|
|
|
|
.height = h,
|
|
|
|
|
};
|
|
|
|
|
draw_cairo_border(cairo, fbox, theme->osd_border_width);
|
2022-12-12 00:50:57 +00:00
|
|
|
|
|
|
|
|
/* Set up text rendering */
|
|
|
|
|
set_cairo_color(cairo, theme->osd_label_text_color);
|
|
|
|
|
PangoLayout *layout = pango_cairo_create_layout(cairo);
|
2024-07-14 08:55:30 +01:00
|
|
|
pango_context_set_round_glyph_positions(pango_layout_get_context(layout), false);
|
2022-12-12 00:50:57 +00:00
|
|
|
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
|
|
|
|
|
|
|
|
|
|
PangoFontDescription *desc = font_to_pango_desc(&rc.font_osd);
|
|
|
|
|
pango_layout_set_font_description(layout, desc);
|
|
|
|
|
|
|
|
|
|
pango_cairo_update_layout(cairo, layout);
|
|
|
|
|
|
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) {
|
|
|
|
|
/* Center workspace indicator on the x axis */
|
|
|
|
|
int x = font_width(&rc.font_osd, workspace_name);
|
2024-03-26 05:12:19 -05:00
|
|
|
x = (w - x) / 2;
|
2023-07-05 19:49:56 +01:00
|
|
|
cairo_move_to(cairo, x, y + theme->osd_window_switcher_item_active_border_width);
|
2022-12-12 00:50:57 +00:00
|
|
|
PangoWeight weight = pango_font_description_get_weight(desc);
|
|
|
|
|
pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
|
|
|
|
|
pango_layout_set_font_description(layout, desc);
|
|
|
|
|
pango_layout_set_text(layout, workspace_name, -1);
|
|
|
|
|
pango_cairo_show_layout(cairo, layout);
|
|
|
|
|
pango_font_description_set_weight(desc, weight);
|
|
|
|
|
pango_layout_set_font_description(layout, desc);
|
2023-04-24 21:31:28 +01:00
|
|
|
y += theme->osd_window_switcher_item_height;
|
2022-04-20 17:45:10 +01:00
|
|
|
}
|
2022-12-12 00:50:57 +00:00
|
|
|
pango_font_description_free(desc);
|
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) {
|
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;
|
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) {
|
2024-03-16 19:03:06 +01:00
|
|
|
buf_clear(&buf);
|
2023-07-05 19:49:56 +01:00
|
|
|
cairo_move_to(cairo, x, y
|
|
|
|
|
+ theme->osd_window_switcher_item_padding_y
|
|
|
|
|
+ theme->osd_window_switcher_item_active_border_width);
|
2023-04-19 14:44:41 +01:00
|
|
|
|
2024-04-10 17:39:31 -05:00
|
|
|
osd_field_get_content(field, &buf, *view);
|
|
|
|
|
|
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;
|
2023-04-21 16:26:44 +01:00
|
|
|
pango_layout_set_width(layout, field_width * PANGO_SCALE);
|
2024-04-16 23:36:32 -04:00
|
|
|
pango_layout_set_text(layout, buf.data, -1);
|
2023-04-19 14:44:41 +01:00
|
|
|
pango_cairo_show_layout(cairo, layout);
|
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
|
|
|
}
|
|
|
|
|
|
2023-08-10 15:46:00 +01:00
|
|
|
if (*view == cycle_view) {
|
2022-12-12 00:50:57 +00:00
|
|
|
/* Highlight current window */
|
2023-06-29 17:45:33 +01:00
|
|
|
struct wlr_fbox fbox = {
|
2023-06-29 21:29:43 +01:00
|
|
|
.x = theme->osd_border_width + theme->osd_window_switcher_padding,
|
2023-07-05 19:49:56 +01:00
|
|
|
.y = y,
|
2024-03-26 05:12:19 -05:00
|
|
|
.width = w
|
2023-06-29 21:29:43 +01:00
|
|
|
- 2 * theme->osd_border_width
|
|
|
|
|
- 2 * theme->osd_window_switcher_padding,
|
2023-07-05 19:49:56 +01:00
|
|
|
.height = theme->osd_window_switcher_item_height,
|
2023-06-29 17:45:33 +01:00
|
|
|
};
|
2023-07-01 14:56:13 +02:00
|
|
|
draw_cairo_border(cairo, fbox,
|
|
|
|
|
theme->osd_window_switcher_item_active_border_width);
|
2022-12-12 00:50:57 +00:00
|
|
|
cairo_stroke(cairo);
|
|
|
|
|
}
|
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-12-12 00:50:57 +00:00
|
|
|
g_object_unref(layout);
|
2022-02-09 16:38:07 -05:00
|
|
|
|
2022-12-12 00:50:57 +00:00
|
|
|
cairo_surface_flush(surf);
|
|
|
|
|
}
|
2022-06-15 02:02:50 +02:00
|
|
|
|
2022-12-12 00:50:57 +00:00
|
|
|
static void
|
2024-03-04 20:30:31 +00:00
|
|
|
display_osd(struct output *output, struct wl_array *views)
|
2022-12-12 00:50:57 +00:00
|
|
|
{
|
|
|
|
|
struct server *server = output->server;
|
2023-04-24 21:31:28 +01:00
|
|
|
struct theme *theme = server->theme;
|
2022-12-12 00:50:57 +00:00
|
|
|
bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1;
|
2024-07-25 21:56:05 +02:00
|
|
|
const char *workspace_name = server->workspaces.current->name;
|
2022-12-12 00:50:57 +00:00
|
|
|
|
|
|
|
|
float scale = output->wlr_output->scale;
|
2023-04-24 21:31:28 +01:00
|
|
|
int w = theme->osd_window_switcher_width;
|
2024-03-26 05:12:19 -05:00
|
|
|
if (theme->osd_window_switcher_width_is_percent) {
|
|
|
|
|
w = output->wlr_output->width / output->wlr_output->scale
|
|
|
|
|
* theme->osd_window_switcher_width / 100;
|
|
|
|
|
}
|
2024-03-04 20:30:31 +00:00
|
|
|
int h = wl_array_len(views) * rc.theme->osd_window_switcher_item_height
|
2023-08-12 19:59:46 +01:00
|
|
|
+ 2 * rc.theme->osd_border_width
|
|
|
|
|
+ 2 * rc.theme->osd_window_switcher_padding;
|
2022-12-12 00:50:57 +00:00
|
|
|
if (show_workspace) {
|
|
|
|
|
/* workspace indicator */
|
2023-04-24 21:31:28 +01:00
|
|
|
h += theme->osd_window_switcher_item_height;
|
2022-12-12 00:50:57 +00:00
|
|
|
}
|
2022-06-15 02:02:50 +02:00
|
|
|
|
2024-11-12 07:23:40 +09:00
|
|
|
struct lab_data_buffer *buffer = buffer_create_cairo(w, h, scale);
|
|
|
|
|
if (!buffer) {
|
2023-08-27 21:11:04 +02:00
|
|
|
wlr_log(WLR_ERROR, "Failed to allocate cairo buffer for the window switcher");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-12-12 00:50:57 +00:00
|
|
|
|
|
|
|
|
/* Render OSD image */
|
2024-11-28 06:58:17 +09:00
|
|
|
cairo_t *cairo = cairo_create(buffer->surface);
|
2024-03-04 20:30:31 +00:00
|
|
|
render_osd(server, cairo, w, h, show_workspace, workspace_name, views);
|
2024-11-28 06:58:17 +09:00
|
|
|
cairo_destroy(cairo);
|
2022-12-12 00:50:57 +00:00
|
|
|
|
|
|
|
|
struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create(
|
2024-11-12 07:23:40 +09:00
|
|
|
output->osd_tree, &buffer->base);
|
|
|
|
|
wlr_buffer_drop(&buffer->base);
|
2022-12-12 00:50:57 +00:00
|
|
|
wlr_scene_buffer_set_dest_size(scene_buffer, w, h);
|
|
|
|
|
|
|
|
|
|
/* Center OSD */
|
|
|
|
|
struct wlr_box output_box;
|
|
|
|
|
wlr_output_layout_get_box(output->server->output_layout,
|
|
|
|
|
output->wlr_output, &output_box);
|
|
|
|
|
int lx = output->usable_area.x + output->usable_area.width / 2
|
|
|
|
|
- w / 2 + output_box.x;
|
|
|
|
|
int ly = output->usable_area.y + output->usable_area.height / 2
|
|
|
|
|
- h / 2 + output_box.y;
|
|
|
|
|
wlr_scene_node_set_position(&scene_buffer->node, lx, ly);
|
|
|
|
|
wlr_scene_node_set_enabled(&output->osd_tree->node, true);
|
|
|
|
|
}
|
2021-08-16 07:16:56 +01:00
|
|
|
|
2024-03-04 20:30:31 +00:00
|
|
|
void
|
|
|
|
|
osd_update(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) {
|
|
|
|
|
destroy_osd_nodes(output);
|
|
|
|
|
if (output_is_usable(output)) {
|
2024-03-04 20:30:31 +00:00
|
|
|
display_osd(output, &views);
|
2023-03-08 15:26:49 +01:00
|
|
|
}
|
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
|
|
|
}
|