diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd
index 4a76a374..96e44cb6 100644
--- a/docs/labwc-actions.5.scd
+++ b/docs/labwc-actions.5.scd
@@ -51,9 +51,13 @@ Actions are used in menus and keyboard/mouse bindings.
another window or screen edge. If set to "no", only move to
the next screen edge. Default is yes.
-**
+**
Begin interactive resize of window under cursor.
+ *direction* [up|down|left|right|up-left|up-right|down-left|down-right]
+ Edge or corner from which to start resizing. If this is not provided,
+ the direction is inferred from the cursor position.
+
**
Resize window relative to its current size. Values of left, right,
top or bottom tell how much to resize on that edge of window,
diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd
index 427659af..74442362 100644
--- a/docs/labwc-config.5.scd
+++ b/docs/labwc-config.5.scd
@@ -363,7 +363,7 @@ this is for compatibility with Openbox.
*unshade* [yes|no] Temporarily unshade windows when switching between
them and permanently unshade on the final selection. Default is yes.
-**
+**
*show* [yes|no] Draw the OnScreenDisplay when switching between
windows. Default is yes.
@@ -371,10 +371,10 @@ this is for compatibility with Openbox.
"classic" displays window information like icons and titles in a vertical list.
"thumbnail" shows window thumbnail, icon and title in grids.
- *output* [all|pointer|keyboard] Configures which monitor(s) show the OSD.
+ *output* [all|focused|cursor] Configures which monitor(s) show the OSD.
"all" displays the OSD on all monitors.
- "pointer" displays the OSD on the monitor containing the mouse pointer.
- "keyboard" displays the OSD on the monitor with keyboard focus.
+ "focused" displays the OSD on the monitor with keyboard focus.
+ "cursor" displays the OSD on the monitor containing the mouse pointer.
Default is "all".
*thumbnailLabelFormat* Format to be used for the thumbnail label according to *custom*
diff --git a/docs/rc.xml.all b/docs/rc.xml.all
index f0865a49..c91f0fc7 100644
--- a/docs/rc.xml.all
+++ b/docs/rc.xml.all
@@ -33,8 +33,8 @@
-
-
+
+
labwc
icon:iconify,max,close
@@ -218,9 +218,9 @@
space automatically, so is only intended for other, specialist
cases.
- If output is left empty, the margin will be applied to all outputs.
+ If 'output' is not provided, the margin will be applied to all outputs.
-
+
-->
@@ -525,7 +525,11 @@
If mouseEmulation is enabled, all touch up/down/motion events are
translated to mouse button and motion events.
-->
-
+
+
+
+ no
+
-
+
+
@@ -595,21 +600,21 @@
-
-
-
-
+
+
+
+
yes
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
1.0
@@ -678,7 +683,7 @@
400
2.0
0.2
- true
+ yes
diff --git a/include/common/box.h b/include/common/box.h
index 45c0fc4d..9f18b45e 100644
--- a/include/common/box.h
+++ b/include/common/box.h
@@ -10,6 +10,17 @@ bool box_intersects(struct wlr_box *box_a, struct wlr_box *box_b);
void box_union(struct wlr_box *box_dest, struct wlr_box *box_a,
struct wlr_box *box_b);
+/*
+ * Centers a content box (width & height) within a reference box,
+ * limiting it (if possible) to not extend outside a bounding box.
+ *
+ * The reference box and bounding box are often the same but could be
+ * different (e.g. when centering a view within its parent but limiting
+ * to usable output area).
+ */
+void box_center(int width, int height, const struct wlr_box *ref,
+ const struct wlr_box *bound, int *x, int *y);
+
/*
* Fits and centers a content box (width & height) within a bounding box.
* The content box is downscaled if necessary (preserving aspect ratio) but
diff --git a/include/common/node-type.h b/include/common/node-type.h
index 9987af38..52fff3b0 100644
--- a/include/common/node-type.h
+++ b/include/common/node-type.h
@@ -47,7 +47,7 @@ enum lab_node_type {
LAB_NODE_FRAME,
LAB_NODE_ROOT,
LAB_NODE_MENUITEM,
- LAB_NODE_OSD_ITEM,
+ LAB_NODE_CYCLE_OSD_ITEM,
LAB_NODE_LAYER_SURFACE,
LAB_NODE_UNMANAGED,
LAB_NODE_ALL,
diff --git a/include/config/rcxml.h b/include/config/rcxml.h
index 8273ba5e..3e4f15a2 100644
--- a/include/config/rcxml.h
+++ b/include/config/rcxml.h
@@ -183,8 +183,8 @@ struct rcxml {
bool unshade;
enum lab_view_criteria criteria;
struct wl_list fields; /* struct window_switcher_field.link */
- enum window_switcher_style style;
- enum osd_output_criteria output_criteria;
+ enum cycle_osd_style style;
+ enum cycle_osd_output_criteria output_criteria;
char *thumbnail_label_format;
} window_switcher;
diff --git a/include/config/types.h b/include/config/types.h
index 76e699a4..757796a6 100644
--- a/include/config/types.h
+++ b/include/config/types.h
@@ -107,15 +107,15 @@ enum lab_window_type {
LAB_WINDOW_TYPE_LEN
};
-enum window_switcher_style {
- WINDOW_SWITCHER_CLASSIC,
- WINDOW_SWITCHER_THUMBNAIL,
+enum cycle_osd_style {
+ CYCLE_OSD_STYLE_CLASSIC,
+ CYCLE_OSD_STYLE_THUMBNAIL,
};
-enum osd_output_criteria {
- OSD_OUTPUT_ALL,
- OSD_OUTPUT_POINTER,
- OSD_OUTPUT_KEYBOARD,
+enum cycle_osd_output_criteria {
+ CYCLE_OSD_OUTPUT_ALL,
+ CYCLE_OSD_OUTPUT_CURSOR,
+ CYCLE_OSD_OUTPUT_FOCUSED,
};
#endif /* LABWC_CONFIG_TYPES_H */
diff --git a/include/osd.h b/include/cycle.h
similarity index 54%
rename from include/osd.h
rename to include/cycle.h
index 2ecaa202..aaecff50 100644
--- a/include/osd.h
+++ b/include/cycle.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-#ifndef LABWC_OSD_H
-#define LABWC_OSD_H
+#ifndef LABWC_CYCLE_H
+#define LABWC_CYCLE_H
#include
#include
@@ -14,7 +14,7 @@ enum lab_cycle_dir {
};
/* TODO: add field with keyboard layout? */
-enum window_switcher_field_content {
+enum cycle_osd_field_content {
LAB_FIELD_NONE = 0,
LAB_FIELD_TYPE,
LAB_FIELD_TYPE_SHORT,
@@ -35,8 +35,8 @@ enum window_switcher_field_content {
LAB_FIELD_COUNT
};
-struct window_switcher_field {
- enum window_switcher_field_content content;
+struct cycle_osd_field {
+ enum cycle_osd_field_content content;
int width;
char *format;
struct wl_list link; /* struct rcxml.window_switcher.fields */
@@ -48,54 +48,54 @@ struct server;
struct wlr_scene_node;
/* Begin window switcher */
-void osd_begin(struct server *server, enum lab_cycle_dir direction);
+void cycle_begin(struct server *server, enum lab_cycle_dir direction);
/* Cycle the selected view in the window switcher */
-void osd_cycle(struct server *server, enum lab_cycle_dir direction);
+void cycle_step(struct server *server, enum lab_cycle_dir direction);
/* Closes the OSD */
-void osd_finish(struct server *server, bool switch_focus);
+void cycle_finish(struct server *server, bool switch_focus);
-/* Notify OSD about a destroying view */
-void osd_on_view_destroy(struct view *view);
+/* Re-initialize the window switcher */
+void cycle_reinitialize(struct server *server);
/* Focus the clicked window and close OSD */
-void osd_on_cursor_release(struct server *server, struct wlr_scene_node *node);
+void cycle_on_cursor_release(struct server *server, struct wlr_scene_node *node);
/* Used by osd.c internally to render window switcher fields */
-void osd_field_get_content(struct window_switcher_field *field,
+void cycle_osd_field_get_content(struct cycle_osd_field *field,
struct buf *buf, struct view *view);
/* Sets view info to buf according to format */
-void osd_field_set_custom(struct buf *buf, struct view *view,
+void cycle_osd_field_set_custom(struct buf *buf, struct view *view,
const char *format);
/* Used by rcxml.c when parsing the config */
-void osd_field_arg_from_xml_node(struct window_switcher_field *field,
+void cycle_osd_field_arg_from_xml_node(struct cycle_osd_field *field,
const char *nodename, const char *content);
-bool osd_field_is_valid(struct window_switcher_field *field);
-void osd_field_free(struct window_switcher_field *field);
+bool cycle_osd_field_is_valid(struct cycle_osd_field *field);
+void cycle_osd_field_free(struct cycle_osd_field *field);
/* Internal API */
-struct osd_item {
+struct cycle_osd_item {
struct view *view;
struct wlr_scene_tree *tree;
struct wl_list link;
};
-struct osd_impl {
+struct cycle_osd_impl {
/*
* Create a scene-tree of OSD for an output.
- * This sets output->osd_scene.{items,tree}.
+ * This sets output->cycle_osd.{items,tree}.
*/
- void (*create)(struct output *output, struct wl_array *views);
+ void (*create)(struct output *output);
/*
- * Update output->osd_scene.tree to highlight
- * server->osd_state.cycle_view.
+ * Update output->cycle_osd.tree to highlight
+ * server->cycle_state.selected_view.
*/
void (*update)(struct output *output);
};
-extern struct osd_impl osd_classic_impl;
-extern struct osd_impl osd_thumbnail_impl;
+extern struct cycle_osd_impl cycle_osd_classic_impl;
+extern struct cycle_osd_impl cycle_osd_thumbnail_impl;
-#endif // LABWC_OSD_H
+#endif // LABWC_CYCLE_H
diff --git a/include/input/cursor.h b/include/input/cursor.h
index 13ab72d1..12b96aac 100644
--- a/include/input/cursor.h
+++ b/include/input/cursor.h
@@ -38,6 +38,14 @@ struct cursor_context {
double sx, sy;
};
+/* Used to persistently store cursor context (e.g. in seat->pressed) */
+struct cursor_context_saved {
+ struct cursor_context ctx;
+ struct wl_listener view_destroy;
+ struct wl_listener node_destroy;
+ struct wl_listener surface_destroy;
+};
+
/**
* get_cursor_context - find view, surface and scene_node at cursor
*
@@ -65,6 +73,13 @@ void cursor_set(struct seat *seat, enum lab_cursors cursor);
void cursor_set_visible(struct seat *seat, bool visible);
+/*
+ * Safely store a cursor context to saved_ctx. saved_ctx is cleared when either
+ * of its node, surface and view is destroyed.
+ */
+void cursor_context_save(struct cursor_context_saved *saved_ctx,
+ const struct cursor_context *ctx);
+
/**
* cursor_get_resize_edges - calculate resize edge based on cursor position
* @cursor - the current cursor (usually server->seat.cursor)
diff --git a/include/labwc.h b/include/labwc.h
index 160c42f4..40bff876 100644
--- a/include/labwc.h
+++ b/include/labwc.h
@@ -18,7 +18,7 @@ enum input_mode {
LAB_INPUT_STATE_MOVE,
LAB_INPUT_STATE_RESIZE,
LAB_INPUT_STATE_MENU,
- LAB_INPUT_STATE_WINDOW_SWITCHER,
+ LAB_INPUT_STATE_CYCLE, /* a.k.a. window switching */
};
struct seat {
@@ -65,8 +65,7 @@ struct seat {
struct input_method_relay *input_method_relay;
/**
- * This is usually zeroed and is only set on button press while the
- * mouse is over a view or surface, and zeroed on button release.
+ * Cursor context saved when a mouse button is pressed on a view/surface.
* It is used to send cursor motion events to a surface even though
* the cursor has left the surface in the meantime.
*
@@ -76,10 +75,11 @@ struct seat {
* It is also used to:
* - determine the target view for action in "Drag" mousebind
* - validate view move/resize requests from CSD clients
- *
- * Both (view && !surface) and (surface && !view) are possible.
*/
- struct cursor_context pressed;
+ struct cursor_context_saved pressed;
+
+ /* Cursor context of the last cursor motion */
+ struct cursor_context_saved last_cursor_ctx;
struct lab_set bound_buttons;
@@ -139,7 +139,6 @@ struct seat {
struct wl_list tablet_pads;
struct wl_listener constraint_commit;
- struct wl_listener pressed_surface_destroy;
struct wlr_virtual_pointer_manager_v1 *virtual_pointer;
struct wl_listener new_virtual_pointer;
@@ -302,15 +301,15 @@ struct server {
struct wlr_security_context_manager_v1 *security_context_manager_v1;
/* Set when in cycle (alt-tab) mode */
- struct osd_state {
- struct view *cycle_view;
+ 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_tree *preview_parent;
- struct wlr_scene_node *preview_anchor;
+ struct wlr_scene_node *preview_dummy;
struct lab_scene_rect *preview_outline;
- } osd_state;
+ } cycle;
struct theme *theme;
@@ -392,8 +391,6 @@ void seat_pointer_end_grab(struct seat *seat, struct wlr_surface *surface);
void seat_focus_lock_surface(struct seat *seat, struct wlr_surface *surface);
void seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer);
-void seat_set_pressed(struct seat *seat, struct cursor_context *ctx);
-void seat_reset_pressed(struct seat *seat);
void seat_output_layout_changed(struct seat *seat);
/*
diff --git a/include/node.h b/include/node.h
index ad21f313..7b4936d6 100644
--- a/include/node.h
+++ b/include/node.h
@@ -53,10 +53,10 @@ struct menuitem *node_menuitem_from_node(
struct wlr_scene_node *wlr_scene_node);
/**
- * node_osd_item_from_node - return osd item struct from node
+ * node_cycle_osd_item_from_node - return cycle OSD item struct from node
* @wlr_scene_node: wlr_scene_node from which to return data
*/
-struct osd_item *node_osd_item_from_node(
+struct cycle_osd_item *node_cycle_osd_item_from_node(
struct wlr_scene_node *wlr_scene_node);
/**
diff --git a/include/output.h b/include/output.h
index 3226c964..25001247 100644
--- a/include/output.h
+++ b/include/output.h
@@ -15,14 +15,14 @@ struct output {
struct wlr_scene_output *scene_output;
struct wlr_scene_tree *layer_tree[LAB_NR_LAYERS];
struct wlr_scene_tree *layer_popup_tree;
- struct wlr_scene_tree *osd_tree;
+ struct wlr_scene_tree *cycle_osd_tree;
struct wlr_scene_tree *session_lock_tree;
struct wlr_scene_buffer *workspace_osd;
- struct osd_scene {
- struct wl_list items; /* struct osd_item */
+ struct cycle_osd_scene {
+ struct wl_list items; /* struct cycle_osd_item */
struct wlr_scene_tree *tree;
- } osd_scene;
+ } cycle_osd;
/* In output-relative scene coordinates */
struct wlr_box usable_area;
diff --git a/include/view.h b/include/view.h
index 9ad11cd9..2f8aac85 100644
--- a/include/view.h
+++ b/include/view.h
@@ -134,6 +134,9 @@ struct view {
const struct view_impl *impl;
struct wl_list link;
+ /* This is cleared when the view is not in the cycle list */
+ struct wl_list cycle_link;
+
/*
* The primary output that the view is displayed on. Specifically:
*
@@ -293,6 +296,9 @@ struct xdg_toplevel_view {
struct view base;
struct wlr_xdg_surface *xdg_surface;
+ /* Optional black background fill behind fullscreen view */
+ struct wlr_scene_rect *fullscreen_bg;
+
/* Events unique to xdg-toplevel views */
struct wl_listener set_app_id;
struct wl_listener request_show_window_menu;
@@ -384,15 +390,6 @@ struct view *view_next(struct wl_list *head, struct view *view,
struct view *view_prev(struct wl_list *head, struct view *view,
enum lab_view_criteria criteria);
-/*
- * Same as `view_next()` except that they iterate one whole cycle rather than
- * stopping at the list-head
- */
-struct view *view_next_no_head_stop(struct wl_list *head, struct view *from,
- enum lab_view_criteria criteria);
-struct view *view_prev_no_head_stop(struct wl_list *head, struct view *from,
- enum lab_view_criteria criteria);
-
/**
* view_array_append() - Append views that match criteria to array
* @server: server context
diff --git a/include/xwayland.h b/include/xwayland.h
index 5fa20e11..bbb9fa1c 100644
--- a/include/xwayland.h
+++ b/include/xwayland.h
@@ -77,5 +77,7 @@ void xwayland_update_workarea(struct server *server);
void xwayland_reset_cursor(struct server *server);
+void xwayland_flush(struct server *server);
+
#endif /* HAVE_XWAYLAND */
#endif /* LABWC_XWAYLAND_H */
diff --git a/src/action.c b/src/action.c
index a351160d..7ddba0be 100644
--- a/src/action.c
+++ b/src/action.c
@@ -18,12 +18,12 @@
#include "common/spawn.h"
#include "common/string-helpers.h"
#include "config/rcxml.h"
+#include "cycle.h"
#include "debug.h"
#include "input/keyboard.h"
#include "labwc.h"
#include "magnifier.h"
#include "menu/menu.h"
-#include "osd.h"
#include "output.h"
#include "output-virtual.h"
#include "regions.h"
@@ -414,6 +414,20 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
goto cleanup;
}
break;
+ case ACTION_TYPE_RESIZE:
+ if (!strcmp(argument, "direction")) {
+ enum lab_edge edge = lab_edge_parse(content,
+ /*tiled*/ true, /*any*/ false);
+ if (edge == LAB_EDGE_NONE || edge == LAB_EDGE_CENTER) {
+ wlr_log(WLR_ERROR,
+ "Invalid argument for action %s: '%s' (%s)",
+ action_names[action->type], argument, content);
+ } else {
+ action_arg_add_int(action, argument, edge);
+ }
+ goto cleanup;
+ }
+ break;
case ACTION_TYPE_RESIZE_RELATIVE:
if (!strcmp(argument, "left") || !strcmp(argument, "right") ||
!strcmp(argument, "top") || !strcmp(argument, "bottom")) {
@@ -1112,17 +1126,17 @@ run_action(struct view *view, struct server *server, struct action *action,
}
break;
case ACTION_TYPE_NEXT_WINDOW:
- if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
- osd_cycle(server, LAB_CYCLE_DIR_FORWARD);
+ if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
+ cycle_step(server, LAB_CYCLE_DIR_FORWARD);
} else {
- osd_begin(server, LAB_CYCLE_DIR_FORWARD);
+ cycle_begin(server, LAB_CYCLE_DIR_FORWARD);
}
break;
case ACTION_TYPE_PREVIOUS_WINDOW:
- if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
- osd_cycle(server, LAB_CYCLE_DIR_BACKWARD);
+ if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
+ cycle_step(server, LAB_CYCLE_DIR_BACKWARD);
} else {
- osd_begin(server, LAB_CYCLE_DIR_BACKWARD);
+ cycle_begin(server, LAB_CYCLE_DIR_BACKWARD);
}
break;
case ACTION_TYPE_RECONFIGURE:
@@ -1223,8 +1237,17 @@ run_action(struct view *view, struct server *server, struct action *action,
break;
case ACTION_TYPE_RESIZE:
if (view) {
- enum lab_edge resize_edges = cursor_get_resize_edges(
- server->seat.cursor, ctx);
+ /*
+ * If a direction was specified in the config, honour it.
+ * Otherwise, fall back to determining the resize edges from
+ * the current cursor position (existing behaviour).
+ */
+ enum lab_edge resize_edges =
+ action_get_int(action, "direction", LAB_EDGE_NONE);
+ if (resize_edges == LAB_EDGE_NONE) {
+ resize_edges = cursor_get_resize_edges(
+ server->seat.cursor, ctx);
+ }
interactive_begin(view, LAB_INPUT_STATE_RESIZE,
resize_edges);
}
@@ -1569,7 +1592,7 @@ actions_run(struct view *activator, struct server *server,
struct action *action;
wl_list_for_each(action, actions, link) {
- if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER
+ if (server->input_mode == LAB_INPUT_STATE_CYCLE
&& action->type != ACTION_TYPE_NEXT_WINDOW
&& action->type != ACTION_TYPE_PREVIOUS_WINDOW) {
wlr_log(WLR_INFO, "Only NextWindow or PreviousWindow "
diff --git a/src/common/box.c b/src/common/box.c
index 2520f733..5cc91bf3 100644
--- a/src/common/box.c
+++ b/src/common/box.c
@@ -35,6 +35,25 @@ box_union(struct wlr_box *box_dest, struct wlr_box *box_a, struct wlr_box *box_b
box_dest->height = y2 - y1;
}
+void
+box_center(int width, int height, const struct wlr_box *ref,
+ const struct wlr_box *bound, int *x, int *y)
+{
+ *x = ref->x + (ref->width - width) / 2;
+ *y = ref->y + (ref->height - height) / 2;
+
+ if (*x < bound->x) {
+ *x = bound->x;
+ } else if (*x + width > bound->x + bound->width) {
+ *x = bound->x + bound->width - width;
+ }
+ if (*y < bound->y) {
+ *y = bound->y;
+ } else if (*y + height > bound->y + bound->height) {
+ *y = bound->y + bound->height - height;
+ }
+}
+
struct wlr_box
box_fit_within(int width, int height, struct wlr_box *bound)
{
diff --git a/src/config/rcxml.c b/src/config/rcxml.c
index ff7a8e16..aa8657a2 100644
--- a/src/config/rcxml.c
+++ b/src/config/rcxml.c
@@ -29,8 +29,8 @@
#include "config/tablet.h"
#include "config/tablet-tool.h"
#include "config/touch.h"
+#include "cycle.h"
#include "labwc.h"
-#include "osd.h"
#include "regions.h"
#include "ssd.h"
#include "translate.h"
@@ -323,23 +323,23 @@ fill_window_rules(xmlNode *node)
static void
clear_window_switcher_fields(void)
{
- struct window_switcher_field *field, *field_tmp;
+ struct cycle_osd_field *field, *field_tmp;
wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.fields, link) {
wl_list_remove(&field->link);
- osd_field_free(field);
+ cycle_osd_field_free(field);
}
}
static void
fill_window_switcher_field(xmlNode *node)
{
- struct window_switcher_field *field = znew(*field);
+ struct cycle_osd_field *field = znew(*field);
wl_list_append(&rc.window_switcher.fields, &field->link);
xmlNode *child;
char *key, *content;
LAB_XML_FOR_EACH(node, child, key, content) {
- osd_field_arg_from_xml_node(field, key, content);
+ cycle_osd_field_arg_from_xml_node(field, key, content);
}
}
@@ -714,6 +714,8 @@ fill_libinput_category(xmlNode *node)
char *key, *content;
LAB_XML_FOR_EACH(node, child, key, content) {
if (string_null_or_empty(content)) {
+ wlr_log(WLR_ERROR, "Empty string is not allowed for "
+ "<%s>. Ignoring.", key);
continue;
}
if (!strcmp(key, "category")) {
@@ -1076,7 +1078,8 @@ entry(xmlNode *node, char *nodename, char *content)
return true;
} else if (str_space_only(content)) {
- /* ignore empty leaf nodes other than above */
+ wlr_log(WLR_ERROR, "Empty string is not allowed for %s. "
+ "Ignoring.", nodename);
/* handle non-empty leaf nodes */
} else if (!strcmp(nodename, "decoration.core")) {
@@ -1217,38 +1220,38 @@ entry(xmlNode *node, char *nodename, char *content)
set_bool(content, &rc.window_switcher.show);
} else if (!strcasecmp(nodename, "style.osd.windowSwitcher")) {
if (!strcasecmp(content, "classic")) {
- rc.window_switcher.style = WINDOW_SWITCHER_CLASSIC;
+ rc.window_switcher.style = CYCLE_OSD_STYLE_CLASSIC;
} else if (!strcasecmp(content, "thumbnail")) {
- rc.window_switcher.style = WINDOW_SWITCHER_THUMBNAIL;
+ rc.window_switcher.style = CYCLE_OSD_STYLE_THUMBNAIL;
} else {
wlr_log(WLR_ERROR, "Invalid windowSwitcher style %s: "
"should be one of classic|thumbnail", content);
}
} else if (!strcasecmp(nodename, "output.osd.windowSwitcher")) {
if (!strcasecmp(content, "all")) {
- rc.window_switcher.output_criteria = OSD_OUTPUT_ALL;
- } else if (!strcasecmp(content, "pointer")) {
- rc.window_switcher.output_criteria = OSD_OUTPUT_POINTER;
- } else if (!strcasecmp(content, "keyboard")) {
- rc.window_switcher.output_criteria = OSD_OUTPUT_KEYBOARD;
+ rc.window_switcher.output_criteria = CYCLE_OSD_OUTPUT_ALL;
+ } else if (!strcasecmp(content, "cursor")) {
+ rc.window_switcher.output_criteria = CYCLE_OSD_OUTPUT_CURSOR;
+ } else if (!strcasecmp(content, "focused")) {
+ rc.window_switcher.output_criteria = CYCLE_OSD_OUTPUT_FOCUSED;
} else {
wlr_log(WLR_ERROR, "Invalid windowSwitcher output %s: "
- "should be one of all|pointer|keyboard", content);
+ "should be one of all|focused|cursor", content);
}
/* The following two are for backward compatibility only. */
} else if (!strcasecmp(nodename, "show.windowSwitcher")) {
set_bool(content, &rc.window_switcher.show);
wlr_log(WLR_ERROR, " is deprecated."
- " Use ");
+ " Use ");
} else if (!strcasecmp(nodename, "style.windowSwitcher")) {
if (!strcasecmp(content, "classic")) {
- rc.window_switcher.style = WINDOW_SWITCHER_CLASSIC;
+ rc.window_switcher.style = CYCLE_OSD_STYLE_CLASSIC;
} else if (!strcasecmp(content, "thumbnail")) {
- rc.window_switcher.style = WINDOW_SWITCHER_THUMBNAIL;
+ rc.window_switcher.style = CYCLE_OSD_STYLE_THUMBNAIL;
}
wlr_log(WLR_ERROR, " is deprecated."
- " Use ");
+ " Use ");
} else if (!strcasecmp(nodename, "preview.windowSwitcher")) {
set_bool(content, &rc.window_switcher.preview);
@@ -1471,8 +1474,8 @@ rcxml_init(void)
rc.snap_tiling_events_mode = LAB_TILING_EVENTS_ALWAYS;
rc.window_switcher.show = true;
- rc.window_switcher.style = WINDOW_SWITCHER_CLASSIC;
- rc.window_switcher.output_criteria = OSD_OUTPUT_ALL;
+ rc.window_switcher.style = CYCLE_OSD_STYLE_CLASSIC;
+ rc.window_switcher.output_criteria = CYCLE_OSD_OUTPUT_ALL;
rc.window_switcher.thumbnail_label_format = xstrdup("%T");
rc.window_switcher.preview = true;
rc.window_switcher.outlines = true;
@@ -1641,7 +1644,7 @@ static void
load_default_window_switcher_fields(void)
{
static const struct {
- enum window_switcher_field_content content;
+ enum cycle_osd_field_content content;
int width;
} fields[] = {
#if HAVE_LIBSFDO
@@ -1654,7 +1657,7 @@ load_default_window_switcher_fields(void)
#endif
};
- struct window_switcher_field *field;
+ struct cycle_osd_field *field;
for (size_t i = 0; i < ARRAY_SIZE(fields); i++) {
field = znew(*field);
field->content = fields[i].content;
@@ -1873,13 +1876,13 @@ validate(void)
/* OSD fields */
int field_width_sum = 0;
- struct window_switcher_field *field, *field_tmp;
+ struct cycle_osd_field *field, *field_tmp;
wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.fields, link) {
field_width_sum += field->width;
- if (!osd_field_is_valid(field) || field_width_sum > 100) {
+ if (!cycle_osd_field_is_valid(field) || field_width_sum > 100) {
wlr_log(WLR_ERROR, "Deleting invalid window switcher field %p", field);
wl_list_remove(&field->link);
- osd_field_free(field);
+ cycle_osd_field_free(field);
}
}
}
diff --git a/src/cycle/cycle.c b/src/cycle/cycle.c
new file mode 100644
index 00000000..8eabaf6c
--- /dev/null
+++ b/src/cycle/cycle.c
@@ -0,0 +1,379 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include "cycle.h"
+#include
+#include
+#include
+#include
+#include "common/lab-scene-rect.h"
+#include "common/list.h"
+#include "common/scene-helpers.h"
+#include "config/rcxml.h"
+#include "labwc.h"
+#include "node.h"
+#include "output.h"
+#include "scaled-buffer/scaled-font-buffer.h"
+#include "scaled-buffer/scaled-icon-buffer.h"
+#include "ssd.h"
+#include "theme.h"
+#include "view.h"
+
+static bool init_cycle(struct server *server);
+static void update_cycle(struct server *server);
+static void destroy_cycle(struct server *server);
+
+static void
+update_preview_outlines(struct view *view)
+{
+ /* Create / Update preview outline tree */
+ struct server *server = view->server;
+ struct theme *theme = server->theme;
+ struct lab_scene_rect *rect = view->server->cycle.preview_outline;
+ if (!rect) {
+ struct lab_scene_rect_options opts = {
+ .border_colors = (float *[3]) {
+ theme->osd_window_switcher_preview_border_color[0],
+ theme->osd_window_switcher_preview_border_color[1],
+ theme->osd_window_switcher_preview_border_color[2],
+ },
+ .nr_borders = 3,
+ .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);
+ server->cycle.preview_outline = rect;
+ }
+
+ struct wlr_box geo = ssd_max_extents(view);
+ lab_scene_rect_set_size(rect, geo.width, geo.height);
+ wlr_scene_node_set_position(&rect->tree->node, geo.x, geo.y);
+}
+
+/* Returns the view to select next in the window switcher. */
+static struct view *
+get_next_selected_view(struct server *server, enum lab_cycle_dir dir)
+{
+ struct cycle_state *cycle = &server->cycle;
+ assert(cycle->selected_view);
+ assert(!wl_list_empty(&server->cycle.views));
+
+ struct wl_list *link;
+ if (dir == LAB_CYCLE_DIR_FORWARD) {
+ link = cycle->selected_view->cycle_link.next;
+ if (link == &server->cycle.views) {
+ link = link->next;
+ }
+ } else {
+ link = cycle->selected_view->cycle_link.prev;
+ if (link == &server->cycle.views) {
+ link = link->prev;
+ }
+ }
+ struct view *view = wl_container_of(link, view, cycle_link);
+ return view;
+}
+
+static struct view *
+get_first_view(struct wl_list *views)
+{
+ assert(!wl_list_empty(views));
+ struct view *view = wl_container_of(views->next, view, cycle_link);
+ return view;
+}
+
+void
+cycle_reinitialize(struct server *server)
+{
+ struct cycle_state *cycle = &server->cycle;
+
+ if (server->input_mode != LAB_INPUT_STATE_CYCLE) {
+ /* OSD not active, no need for clean up */
+ return;
+ }
+
+ struct view *selected_view = cycle->selected_view;
+ struct view *selected_view_prev =
+ get_next_selected_view(server, LAB_CYCLE_DIR_BACKWARD);
+
+ destroy_cycle(server);
+ if (init_cycle(server)) {
+ /*
+ * Preserve the selected view (or its previous view) if it's
+ * still in the cycle list
+ */
+ if (selected_view->cycle_link.next) {
+ cycle->selected_view = selected_view;
+ } else if (selected_view_prev->cycle_link.next) {
+ cycle->selected_view = selected_view_prev;
+ } else {
+ /* should be unreachable */
+ wlr_log(WLR_ERROR, "could not find view to select");
+ cycle->selected_view = get_first_view(&server->cycle.views);
+ }
+ update_cycle(server);
+ } else {
+ /* Failed to re-init window switcher, exit */
+ cycle_finish(server, /*switch_focus*/ false);
+ }
+}
+
+void
+cycle_on_cursor_release(struct server *server, struct wlr_scene_node *node)
+{
+ assert(server->input_mode == LAB_INPUT_STATE_CYCLE);
+
+ struct cycle_osd_item *item = node_cycle_osd_item_from_node(node);
+ server->cycle.selected_view = item->view;
+ cycle_finish(server, /*switch_focus*/ true);
+}
+
+static void
+restore_preview_node(struct server *server)
+{
+ if (server->cycle.preview_node) {
+ wlr_scene_node_reparent(server->cycle.preview_node,
+ server->cycle.preview_dummy->parent);
+ wlr_scene_node_place_above(server->cycle.preview_node,
+ server->cycle.preview_dummy);
+ wlr_scene_node_destroy(server->cycle.preview_dummy);
+
+ /* Node was disabled / minimized before, disable again */
+ if (!server->cycle.preview_was_enabled) {
+ wlr_scene_node_set_enabled(server->cycle.preview_node, false);
+ }
+ if (server->cycle.preview_was_shaded) {
+ struct view *view = node_view_from_node(server->cycle.preview_node);
+ view_set_shade(view, true);
+ }
+ server->cycle.preview_node = NULL;
+ server->cycle.preview_dummy = NULL;
+ server->cycle.preview_was_enabled = false;
+ server->cycle.preview_was_shaded = false;
+ }
+}
+
+void
+cycle_begin(struct server *server, enum lab_cycle_dir direction)
+{
+ if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
+ return;
+ }
+
+ if (!init_cycle(server)) {
+ return;
+ }
+
+ struct view *active_view = server->active_view;
+ if (active_view && active_view->cycle_link.next) {
+ /* Select the active view it's in the cycle list */
+ server->cycle.selected_view = active_view;
+ } else {
+ /* Otherwise, select the first view in the cycle list */
+ server->cycle.selected_view = get_first_view(&server->cycle.views);
+ }
+ /* Pre-select the next view in the given direction */
+ server->cycle.selected_view = get_next_selected_view(server, direction);
+
+ seat_focus_override_begin(&server->seat,
+ LAB_INPUT_STATE_CYCLE, LAB_CURSOR_DEFAULT);
+ update_cycle(server);
+
+ /* Update cursor, in case it is within the area covered by OSD */
+ cursor_update_focus(server);
+}
+
+void
+cycle_step(struct server *server, enum lab_cycle_dir direction)
+{
+ assert(server->input_mode == LAB_INPUT_STATE_CYCLE);
+
+ server->cycle.selected_view = get_next_selected_view(server, direction);
+ update_cycle(server);
+}
+
+void
+cycle_finish(struct server *server, bool switch_focus)
+{
+ if (server->input_mode != LAB_INPUT_STATE_CYCLE) {
+ return;
+ }
+
+ struct view *selected_view = server->cycle.selected_view;
+ destroy_cycle(server);
+
+ /* FIXME: this sets focus to the old surface even with switch_focus=true */
+ seat_focus_override_end(&server->seat);
+
+ /* Hiding OSD may need a cursor change */
+ cursor_update_focus(server);
+
+ if (switch_focus && selected_view) {
+ if (rc.window_switcher.unshade) {
+ view_set_shade(selected_view, false);
+ }
+ desktop_focus_view(selected_view, /*raise*/ true);
+ }
+}
+
+static void
+preview_selected_view(struct view *view)
+{
+ assert(view);
+ assert(view->scene_tree);
+ struct server *server = view->server;
+ struct cycle_state *cycle = &server->cycle;
+
+ /* Move previous selected node back to its original place */
+ restore_preview_node(server);
+
+ cycle->preview_node = &view->scene_tree->node;
+
+ /* Create a dummy node at the original place of the previewed window */
+ struct wlr_scene_rect *dummy_rect = wlr_scene_rect_create(
+ cycle->preview_node->parent, 0, 0, (float [4]) {0});
+ wlr_scene_node_place_below(&dummy_rect->node, cycle->preview_node);
+ wlr_scene_node_set_enabled(&dummy_rect->node, false);
+ cycle->preview_dummy = &dummy_rect->node;
+
+ /* Store node enabled / minimized state and force-enable if disabled */
+ cycle->preview_was_enabled = cycle->preview_node->enabled;
+ wlr_scene_node_set_enabled(cycle->preview_node, true);
+ if (rc.window_switcher.unshade && view->shaded) {
+ view_set_shade(view, false);
+ 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);
+
+ /* Finally raise selected node to the top */
+ wlr_scene_node_raise_to_top(cycle->preview_node);
+}
+
+static struct cycle_osd_impl *
+get_osd_impl(void)
+{
+ switch (rc.window_switcher.style) {
+ case CYCLE_OSD_STYLE_CLASSIC:
+ return &cycle_osd_classic_impl;
+ case CYCLE_OSD_STYLE_THUMBNAIL:
+ return &cycle_osd_thumbnail_impl;
+ }
+ return NULL;
+}
+
+static void
+create_osd_on_output(struct output *output)
+{
+ if (!output_is_usable(output)) {
+ return;
+ }
+ get_osd_impl()->create(output);
+ assert(output->cycle_osd.tree);
+}
+
+/* Return false on failure */
+static bool
+init_cycle(struct server *server)
+{
+ struct view *view;
+ for_each_view(view, &server->views, rc.window_switcher.criteria) {
+ wl_list_append(&server->cycle.views, &view->cycle_link);
+ }
+ if (wl_list_empty(&server->cycle.views)) {
+ wlr_log(WLR_DEBUG, "no views to switch between");
+ return false;
+ }
+
+ if (rc.window_switcher.show) {
+ /* Create OSD */
+ switch (rc.window_switcher.output_criteria) {
+ case CYCLE_OSD_OUTPUT_ALL: {
+ struct output *output;
+ wl_list_for_each(output, &server->outputs, link) {
+ create_osd_on_output(output);
+ }
+ break;
+ }
+ case CYCLE_OSD_OUTPUT_CURSOR:
+ create_osd_on_output(output_nearest_to_cursor(server));
+ break;
+ case CYCLE_OSD_OUTPUT_FOCUSED: {
+ struct output *output;
+ if (server->active_view) {
+ output = server->active_view->output;
+ } else {
+ /* Fallback to pointer, if there is no active_view */
+ output = output_nearest_to_cursor(server);
+ }
+ create_osd_on_output(output);
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+static void
+update_cycle(struct server *server)
+{
+ struct cycle_state *cycle = &server->cycle;
+
+ if (rc.window_switcher.show) {
+ struct output *output;
+ wl_list_for_each(output, &server->outputs, link) {
+ if (output->cycle_osd.tree) {
+ get_osd_impl()->update(output);
+ }
+ }
+ }
+
+ if (rc.window_switcher.preview) {
+ preview_selected_view(cycle->selected_view);
+ }
+
+ /* Outline current window */
+ if (rc.window_switcher.outlines) {
+ if (view_is_focusable(server->cycle.selected_view)) {
+ update_preview_outlines(server->cycle.selected_view);
+ }
+ }
+}
+
+/* Resets all the states in server->cycle */
+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;
+ }
+ }
+
+ 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) {
+ wl_list_remove(&view->cycle_link);
+ view->cycle_link = (struct wl_list){0};
+ }
+
+ server->cycle.selected_view = NULL;
+}
diff --git a/src/osd/meson.build b/src/cycle/meson.build
similarity index 86%
rename from src/osd/meson.build
rename to src/cycle/meson.build
index 17b4bf51..07e9f7aa 100644
--- a/src/osd/meson.build
+++ b/src/cycle/meson.build
@@ -1,5 +1,5 @@
labwc_sources += files(
- 'osd.c',
+ 'cycle.c',
'osd-classic.c',
'osd-field.c',
'osd-thumbnail.c',
diff --git a/src/osd/osd-classic.c b/src/cycle/osd-classic.c
similarity index 81%
rename from src/osd/osd-classic.c
rename to src/cycle/osd-classic.c
index 0c80bd79..67cfb8db 100644
--- a/src/osd/osd-classic.c
+++ b/src/cycle/osd-classic.c
@@ -4,24 +4,25 @@
#include
#include
#include
-#include "common/array.h"
#include "common/buf.h"
#include "common/font.h"
#include "common/lab-scene-rect.h"
#include "common/list.h"
+#include "common/mem.h"
#include "common/string-helpers.h"
#include "config/rcxml.h"
+#include "cycle.h"
#include "labwc.h"
#include "node.h"
-#include "osd.h"
#include "output.h"
#include "scaled-buffer/scaled-font-buffer.h"
#include "scaled-buffer/scaled-icon-buffer.h"
#include "theme.h"
+#include "view.h"
#include "workspaces.h"
-struct osd_classic_item {
- struct osd_item base;
+struct cycle_osd_classic_item {
+ struct cycle_osd_item base;
struct wlr_scene_tree *normal_tree, *active_tree;
};
@@ -34,7 +35,7 @@ create_fields_scene(struct server *server, struct view *view,
struct window_switcher_classic_theme *switcher_theme =
&theme->osd_window_switcher_classic;
- struct window_switcher_field *field;
+ struct cycle_osd_field *field;
wl_list_for_each(field, &rc.window_switcher.fields, link) {
int field_width = field_widths_sum * field->width / 100.0;
struct wlr_scene_node *node = NULL;
@@ -51,7 +52,7 @@ create_fields_scene(struct server *server, struct view *view,
height = icon_size;
} else {
struct buf buf = BUF_INIT;
- osd_field_get_content(field, &buf, view);
+ cycle_osd_field_get_content(field, &buf, view);
if (!string_null_or_empty(buf.data)) {
struct scaled_font_buffer *font_buffer =
@@ -76,9 +77,9 @@ create_fields_scene(struct server *server, struct view *view,
}
static void
-osd_classic_create(struct output *output, struct wl_array *views)
+cycle_osd_classic_create(struct output *output)
{
- assert(!output->osd_scene.tree && wl_list_empty(&output->osd_scene.items));
+ assert(!output->cycle_osd.tree && wl_list_empty(&output->cycle_osd.items));
struct server *server = output->server;
struct theme *theme = server->theme;
@@ -87,6 +88,7 @@ osd_classic_create(struct output *output, struct wl_array *views)
int padding = theme->osd_border_width + switcher_theme->padding;
bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1;
const char *workspace_name = server->workspaces.current->name;
+ int nr_views = wl_list_length(&server->cycle.views);
struct wlr_box output_box;
wlr_output_layout_get_box(server->output_layout, output->wlr_output,
@@ -96,13 +98,13 @@ osd_classic_create(struct output *output, struct wl_array *views)
if (switcher_theme->width_is_percent) {
w = output_box.width * switcher_theme->width / 100;
}
- int h = wl_array_len(views) * switcher_theme->item_height + 2 * padding;
+ int h = nr_views * switcher_theme->item_height + 2 * padding;
if (show_workspace) {
/* workspace indicator */
h += switcher_theme->item_height;
}
- output->osd_scene.tree = wlr_scene_tree_create(output->osd_tree);
+ output->cycle_osd.tree = wlr_scene_tree_create(output->cycle_osd_tree);
float *text_color = theme->osd_label_text_color;
float *bg_color = theme->osd_bg_color;
@@ -116,7 +118,7 @@ osd_classic_create(struct output *output, struct wl_array *views)
.width = w,
.height = h,
};
- lab_scene_rect_create(output->osd_scene.tree, &bg_opts);
+ lab_scene_rect_create(output->cycle_osd.tree, &bg_opts);
int y = padding;
@@ -134,7 +136,7 @@ osd_classic_create(struct output *output, struct wl_array *views)
}
struct scaled_font_buffer *font_buffer =
- scaled_font_buffer_create(output->osd_scene.tree);
+ scaled_font_buffer_create(output->cycle_osd.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,14 +157,14 @@ osd_classic_create(struct output *output, struct wl_array *views)
}
/* Draw text for each node */
- struct view **view;
- wl_array_for_each(view, views) {
- struct osd_classic_item *item = znew(*item);
- wl_list_append(&output->osd_scene.items, &item->base.link);
- item->base.view = *view;
- item->base.tree = wlr_scene_tree_create(output->osd_scene.tree);
+ 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);
+ item->base.view = view;
+ item->base.tree = wlr_scene_tree_create(output->cycle_osd.tree);
node_descriptor_create(&item->base.tree->node,
- LAB_NODE_OSD_ITEM, NULL, item);
+ LAB_NODE_CYCLE_OSD_ITEM, NULL, item);
/*
* OSD border
* +---------------------------------+
@@ -207,9 +209,9 @@ osd_classic_create(struct output *output, struct wl_array *views)
w - 2 * padding, switcher_theme->item_height, (float[4]) {0});
wlr_scene_node_set_position(&hitbox->node, padding, y);
- create_fields_scene(server, *view, item->normal_tree,
+ create_fields_scene(server, view, item->normal_tree,
text_color, bg_color, field_widths_sum, x, y);
- create_fields_scene(server, *view, item->active_tree,
+ create_fields_scene(server, view, item->active_tree,
text_color, active_bg_color, field_widths_sum, x, y);
y += switcher_theme->item_height;
@@ -218,23 +220,23 @@ osd_classic_create(struct output *output, struct wl_array *views)
error:;
/* Center OSD */
- wlr_scene_node_set_position(&output->osd_scene.tree->node,
+ wlr_scene_node_set_position(&output->cycle_osd.tree->node,
output_box.x + (output_box.width - w) / 2,
output_box.y + (output_box.height - h) / 2);
}
static void
-osd_classic_update(struct output *output)
+cycle_osd_classic_update(struct output *output)
{
- struct osd_classic_item *item;
- wl_list_for_each(item, &output->osd_scene.items, base.link) {
- bool active = item->base.view == output->server->osd_state.cycle_view;
+ 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;
wlr_scene_node_set_enabled(&item->normal_tree->node, !active);
wlr_scene_node_set_enabled(&item->active_tree->node, active);
}
}
-struct osd_impl osd_classic_impl = {
- .create = osd_classic_create,
- .update = osd_classic_update,
+struct cycle_osd_impl cycle_osd_classic_impl = {
+ .create = cycle_osd_classic_create,
+ .update = cycle_osd_classic_update,
};
diff --git a/src/osd/osd-field.c b/src/cycle/osd-field.c
similarity index 96%
rename from src/osd/osd-field.c
rename to src/cycle/osd-field.c
index c84cce46..cfc32811 100644
--- a/src/osd/osd-field.c
+++ b/src/cycle/osd-field.c
@@ -5,11 +5,11 @@
#include "common/buf.h"
#include "common/mem.h"
#include "config/rcxml.h"
+#include "cycle.h"
#include "view.h"
#include "workspaces.h"
#include "labwc.h"
#include "desktop-entry.h"
-#include "osd.h"
#include "output.h"
/* includes '%', terminating 's' and NULL byte, 8 is enough for %-9999s */
@@ -204,11 +204,11 @@ static const struct field_converter field_converter[LAB_FIELD_COUNT] = {
[LAB_FIELD_TITLE] = { 'T', field_set_title },
[LAB_FIELD_TITLE_SHORT] = { 't', field_set_title_short },
/* fmt_char can never be matched so prevents LAB_FIELD_CUSTOM recursion */
- [LAB_FIELD_CUSTOM] = { '\0', osd_field_set_custom },
+ [LAB_FIELD_CUSTOM] = { '\0', cycle_osd_field_set_custom },
};
void
-osd_field_set_custom(struct buf *buf, struct view *view, const char *format)
+cycle_osd_field_set_custom(struct buf *buf, struct view *view, const char *format)
{
if (!format) {
wlr_log(WLR_ERROR, "Missing format for custom window switcher field");
@@ -286,7 +286,7 @@ reset_format:
}
void
-osd_field_arg_from_xml_node(struct window_switcher_field *field,
+cycle_osd_field_arg_from_xml_node(struct cycle_osd_field *field,
const char *nodename, const char *content)
{
if (!strcmp(nodename, "content")) {
@@ -332,7 +332,7 @@ osd_field_arg_from_xml_node(struct window_switcher_field *field,
}
bool
-osd_field_is_valid(struct window_switcher_field *field)
+cycle_osd_field_is_valid(struct cycle_osd_field *field)
{
if (field->content == LAB_FIELD_NONE) {
wlr_log(WLR_ERROR, "Invalid OSD field: no content set");
@@ -350,7 +350,7 @@ osd_field_is_valid(struct window_switcher_field *field)
}
void
-osd_field_get_content(struct window_switcher_field *field,
+cycle_osd_field_get_content(struct cycle_osd_field *field,
struct buf *buf, struct view *view)
{
if (field->content == LAB_FIELD_NONE) {
@@ -363,7 +363,7 @@ osd_field_get_content(struct window_switcher_field *field,
}
void
-osd_field_free(struct window_switcher_field *field)
+cycle_osd_field_free(struct cycle_osd_field *field)
{
zfree(field->format);
zfree(field);
diff --git a/src/osd/osd-thumbnail.c b/src/cycle/osd-thumbnail.c
similarity index 86%
rename from src/osd/osd-thumbnail.c
rename to src/cycle/osd-thumbnail.c
index 68610896..2245d1ad 100644
--- a/src/osd/osd-thumbnail.c
+++ b/src/cycle/osd-thumbnail.c
@@ -5,22 +5,22 @@
#include
#include
#include "config/rcxml.h"
-#include "common/array.h"
#include "common/box.h"
#include "common/buf.h"
#include "common/lab-scene-rect.h"
#include "common/list.h"
+#include "common/mem.h"
+#include "cycle.h"
#include "labwc.h"
#include "node.h"
-#include "osd.h"
#include "output.h"
#include "scaled-buffer/scaled-font-buffer.h"
#include "scaled-buffer/scaled-icon-buffer.h"
#include "theme.h"
#include "view.h"
-struct osd_thumbnail_item {
- struct osd_item base;
+struct cycle_osd_thumbnail_item {
+ struct cycle_osd_item base;
struct scaled_font_buffer *normal_label;
struct scaled_font_buffer *active_label;
struct lab_scene_rect *active_bg;
@@ -102,7 +102,7 @@ create_label(struct wlr_scene_tree *parent, struct view *view,
const float *text_color, const float *bg_color, int y)
{
struct buf buf = BUF_INIT;
- osd_field_set_custom(&buf, view,
+ cycle_osd_field_set_custom(&buf, view,
rc.window_switcher.thumbnail_label_format);
struct scaled_font_buffer *buffer =
scaled_font_buffer_create(parent);
@@ -115,7 +115,7 @@ create_label(struct wlr_scene_tree *parent, struct view *view,
return buffer;
}
-static struct osd_thumbnail_item *
+static struct cycle_osd_thumbnail_item *
create_item_scene(struct wlr_scene_tree *parent, struct view *view,
struct output *output)
{
@@ -136,10 +136,10 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view,
return NULL;
}
- struct osd_thumbnail_item *item = znew(*item);
- wl_list_append(&output->osd_scene.items, &item->base.link);
+ struct cycle_osd_thumbnail_item *item = znew(*item);
+ wl_list_append(&output->cycle_osd.items, &item->base.link);
struct wlr_scene_tree *tree = wlr_scene_tree_create(parent);
- node_descriptor_create(&tree->node, LAB_NODE_OSD_ITEM, NULL, item);
+ node_descriptor_create(&tree->node, LAB_NODE_CYCLE_OSD_ITEM, NULL, item);
item->base.tree = tree;
item->base.view = view;
@@ -226,9 +226,9 @@ get_items_geometry(struct output *output, struct theme *theme,
}
static void
-osd_thumbnail_create(struct output *output, struct wl_array *views)
+cycle_osd_thumbnail_create(struct output *output)
{
- assert(!output->osd_scene.tree && wl_list_empty(&output->osd_scene.items));
+ assert(!output->cycle_osd.tree && wl_list_empty(&output->cycle_osd.items));
struct server *server = output->server;
struct theme *theme = server->theme;
@@ -236,19 +236,19 @@ osd_thumbnail_create(struct output *output, struct wl_array *views)
&theme->osd_window_switcher_thumbnail;
int padding = theme->osd_border_width + switcher_theme->padding;
- output->osd_scene.tree = wlr_scene_tree_create(output->osd_tree);
+ output->cycle_osd.tree = wlr_scene_tree_create(output->cycle_osd_tree);
- int nr_views = wl_array_len(views);
+ 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);
/* items */
- struct view **view;
+ struct view *view;
int index = 0;
- wl_array_for_each(view, views) {
- struct osd_thumbnail_item *item = create_item_scene(
- output->osd_scene.tree, *view, output);
+ 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);
if (!item) {
break;
}
@@ -268,7 +268,7 @@ osd_thumbnail_create(struct output *output, struct wl_array *views)
.height = nr_rows * switcher_theme->item_height + 2 * padding,
};
struct lab_scene_rect *bg =
- lab_scene_rect_create(output->osd_scene.tree, &bg_opts);
+ lab_scene_rect_create(output->cycle_osd.tree, &bg_opts);
wlr_scene_node_lower_to_bottom(&bg->tree->node);
/* center */
@@ -277,15 +277,15 @@ osd_thumbnail_create(struct output *output, struct wl_array *views)
&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->osd_scene.tree->node, lx, ly);
+ wlr_scene_node_set_position(&output->cycle_osd.tree->node, lx, ly);
}
static void
-osd_thumbnail_update(struct output *output)
+cycle_osd_thumbnail_update(struct output *output)
{
- struct osd_thumbnail_item *item;
- wl_list_for_each(item, &output->osd_scene.items, base.link) {
- bool active = (item->base.view == output->server->osd_state.cycle_view);
+ 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);
wlr_scene_node_set_enabled(&item->active_bg->tree->node, active);
wlr_scene_node_set_enabled(
&item->active_label->scene_buffer->node, active);
@@ -294,7 +294,7 @@ osd_thumbnail_update(struct output *output)
}
}
-struct osd_impl osd_thumbnail_impl = {
- .create = osd_thumbnail_create,
- .update = osd_thumbnail_update,
+struct cycle_osd_impl cycle_osd_thumbnail_impl = {
+ .create = cycle_osd_thumbnail_create,
+ .update = cycle_osd_thumbnail_update,
};
diff --git a/src/debug.c b/src/debug.c
index cd2ce3d2..7a39824f 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -21,7 +21,7 @@
#define IGNORE_SSD true
#define IGNORE_MENU true
-#define IGNORE_OSD_PREVIEW_OUTLINE true
+#define IGNORE_CYCLE_PREVIEW_OUTLINE true
#define IGNORE_SNAPPING_OVERLAY true
static struct view *last_view;
@@ -118,7 +118,7 @@ get_special(struct server *server, struct wlr_scene_node *node)
if (node->parent == &server->scene->tree) {
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
- if (node == &output->osd_tree->node) {
+ if (node == &output->cycle_osd_tree->node) {
return "output->osd_tree";
}
if (node == &output->layer_popup_tree->node) {
@@ -150,10 +150,10 @@ get_special(struct server *server, struct wlr_scene_node *node)
/* Created on-demand */
return "seat->im_relay->popup_tree";
}
- if (server->osd_state.preview_outline
- && node == &server->osd_state.preview_outline->tree->node) {
+ if (server->cycle.preview_outline
+ && node == &server->cycle.preview_outline->tree->node) {
/* Created on-demand */
- return "osd_state->preview_outline";
+ return "cycle_state->preview_outline";
}
#if HAVE_XWAYLAND
if (node == &server->unmanaged_tree->node) {
@@ -216,13 +216,11 @@ dump_tree(struct server *server, struct wlr_scene_node *node,
}
printf("%.*s %*c %4d %4d [%p]\n", max_width - 1, type, padding, ' ', x, y, node);
- struct lab_scene_rect *osd_preview_outline =
- server->osd_state.preview_outline;
if ((IGNORE_MENU && node == &server->menu_tree->node)
|| (IGNORE_SSD && last_view
&& ssd_debug_is_root_node(last_view->ssd, node))
- || (IGNORE_OSD_PREVIEW_OUTLINE && osd_preview_outline
- && node == &osd_preview_outline->tree->node)
+ || (IGNORE_CYCLE_PREVIEW_OUTLINE && server->cycle.preview_outline
+ && node == &server->cycle.preview_outline->tree->node)
|| (IGNORE_SNAPPING_OVERLAY && server->seat.overlay.rect
&& node == &server->seat.overlay.rect->tree->node)) {
printf("%*c%s\n", pos + 4 + INDENT_SIZE, ' ', "");
diff --git a/src/desktop.c b/src/desktop.c
index 76c3275e..abff9c13 100644
--- a/src/desktop.c
+++ b/src/desktop.c
@@ -74,7 +74,7 @@ desktop_focus_view(struct view *view, bool raise)
return;
}
- if (view->server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
+ if (view->server->input_mode == LAB_INPUT_STATE_CYCLE) {
wlr_log(WLR_DEBUG, "not focusing window while window switching");
return;
}
@@ -340,10 +340,10 @@ get_cursor_context(struct server *server)
ret.node = node;
ret.type = LAB_NODE_MENUITEM;
return ret;
- case LAB_NODE_OSD_ITEM:
+ case LAB_NODE_CYCLE_OSD_ITEM:
/* Always return the top scene node for osd items */
ret.node = node;
- ret.type = LAB_NODE_OSD_ITEM;
+ ret.type = LAB_NODE_CYCLE_OSD_ITEM;
return ret;
case LAB_NODE_BUTTON_FIRST...LAB_NODE_BUTTON_LAST:
case LAB_NODE_SSD_ROOT:
diff --git a/src/dnd.c b/src/dnd.c
index 8ed7c311..ff65cffa 100644
--- a/src/dnd.c
+++ b/src/dnd.c
@@ -34,7 +34,7 @@ handle_drag_start(struct wl_listener *listener, void *data)
struct wlr_drag *drag = data;
seat->drag.active = true;
- seat_reset_pressed(seat);
+ cursor_context_save(&seat->pressed, NULL);
if (drag->icon) {
/* Cleans up automatically on drag->icon->events.destroy */
wlr_scene_drag_icon_create(seat->drag.icons, drag->icon);
diff --git a/src/input/cursor.c b/src/input/cursor.c
index bf46f175..2a681a6e 100644
--- a/src/input/cursor.c
+++ b/src/input/cursor.c
@@ -21,6 +21,7 @@
#include "common/mem.h"
#include "config/mousebind.h"
#include "config/rcxml.h"
+#include "cycle.h"
#include "dnd.h"
#include "idle.h"
#include "input/gestures.h"
@@ -30,7 +31,6 @@
#include "labwc.h"
#include "layers.h"
#include "menu/menu.h"
-#include "osd.h"
#include "output.h"
#include "resistance.h"
#include "resize-outlines.h"
@@ -437,8 +437,72 @@ cursor_update_image(struct seat *seat)
cursor_names[cursor]);
}
+static void
+clear_cursor_context(struct cursor_context_saved *saved_ctx)
+{
+ if (saved_ctx->node_destroy.notify) {
+ wl_list_remove(&saved_ctx->node_destroy.link);
+ }
+ if (saved_ctx->surface_destroy.notify) {
+ wl_list_remove(&saved_ctx->surface_destroy.link);
+ }
+ if (saved_ctx->view_destroy.notify) {
+ wl_list_remove(&saved_ctx->view_destroy.link);
+ }
+ *saved_ctx = (struct cursor_context_saved) {0};
+}
+
+static void
+handle_ctx_node_destroy(struct wl_listener *listener, void *data)
+{
+ struct cursor_context_saved *saved_ctx =
+ wl_container_of(listener, saved_ctx, node_destroy);
+ clear_cursor_context(saved_ctx);
+}
+
+static void
+handle_ctx_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct cursor_context_saved *saved_ctx =
+ wl_container_of(listener, saved_ctx, surface_destroy);
+ clear_cursor_context(saved_ctx);
+}
+
+static void
+handle_ctx_view_destroy(struct wl_listener *listener, void *data)
+{
+ struct cursor_context_saved *saved_ctx =
+ wl_container_of(listener, saved_ctx, view_destroy);
+ clear_cursor_context(saved_ctx);
+}
+
+void
+cursor_context_save(struct cursor_context_saved *saved_ctx,
+ const struct cursor_context *ctx)
+{
+ assert(saved_ctx);
+
+ clear_cursor_context(saved_ctx);
+ if (!ctx) {
+ return;
+ }
+ saved_ctx->ctx = *ctx;
+ if (ctx->node) {
+ saved_ctx->node_destroy.notify = handle_ctx_node_destroy;
+ wl_signal_add(&ctx->node->events.destroy, &saved_ctx->node_destroy);
+ }
+ if (ctx->surface) {
+ saved_ctx->surface_destroy.notify = handle_ctx_surface_destroy;
+ wl_signal_add(&ctx->surface->events.destroy, &saved_ctx->surface_destroy);
+ }
+ if (ctx->view) {
+ saved_ctx->view_destroy.notify = handle_ctx_view_destroy;
+ wl_signal_add(&ctx->view->events.destroy, &saved_ctx->view_destroy);
+ }
+}
+
static bool
-update_pressed_surface(struct seat *seat, struct cursor_context *ctx)
+update_pressed_surface(struct seat *seat, const struct cursor_context *ctx)
{
/*
* In most cases, we don't want to leave one surface and enter
@@ -454,10 +518,10 @@ update_pressed_surface(struct seat *seat, struct cursor_context *ctx)
if (!wlr_seat_pointer_has_grab(seat->seat)) {
return false;
}
- if (seat->pressed.surface && ctx->surface != seat->pressed.surface) {
+ if (seat->pressed.ctx.surface && ctx->surface != seat->pressed.ctx.surface) {
struct wlr_surface *toplevel = get_toplevel(ctx->surface);
- if (toplevel && toplevel == get_toplevel(seat->pressed.surface)) {
- seat_set_pressed(seat, ctx);
+ if (toplevel && toplevel == get_toplevel(seat->pressed.ctx.surface)) {
+ cursor_context_save(&seat->pressed, ctx);
return true;
}
}
@@ -466,11 +530,11 @@ update_pressed_surface(struct seat *seat, struct cursor_context *ctx)
/*
* Common logic shared by cursor_update_focus(), process_cursor_motion()
- * and cursor_axis()
+ * and process_cursor_axis()
*/
-static bool
-cursor_update_common(struct server *server, struct cursor_context *ctx,
- bool cursor_has_moved, double *sx, double *sy)
+static void
+cursor_update_common(struct server *server, const struct cursor_context *ctx,
+ struct cursor_context *notified_ctx)
{
struct seat *seat = &server->seat;
struct wlr_seat *wlr_seat = seat->seat;
@@ -483,14 +547,14 @@ cursor_update_common(struct server *server, struct cursor_context *ctx,
* interactive move/resize, window switcher and
* menu interaction.
*/
- return false;
+ return;
}
/* TODO: verify drag_icon logic */
- if (seat->pressed.surface && ctx->surface != seat->pressed.surface
+ if (seat->pressed.ctx.surface && ctx->surface != seat->pressed.ctx.surface
&& !update_pressed_surface(seat, ctx)
&& !seat->drag.active) {
- if (cursor_has_moved) {
+ if (notified_ctx) {
/*
* Button has been pressed while over another
* surface and is still held down. Just send
@@ -499,12 +563,16 @@ cursor_update_common(struct server *server, struct cursor_context *ctx,
* if the cursor moves outside of the surface.
*/
int lx, ly;
- wlr_scene_node_coords(seat->pressed.node, &lx, &ly);
- *sx = server->seat.cursor->x - lx;
- *sy = server->seat.cursor->y - ly;
- return true;
+ wlr_scene_node_coords(seat->pressed.ctx.node, &lx, &ly);
+ *notified_ctx = seat->pressed.ctx;
+ notified_ctx->sx = server->seat.cursor->x - lx;
+ notified_ctx->sy = server->seat.cursor->y - ly;
}
- return false;
+ return;
+ }
+
+ if (notified_ctx) {
+ *notified_ctx = *ctx;
}
if (ctx->surface) {
@@ -516,11 +584,6 @@ cursor_update_common(struct server *server, struct cursor_context *ctx,
wlr_seat_pointer_notify_enter(wlr_seat, ctx->surface,
ctx->sx, ctx->sy);
seat->server_cursor = LAB_CURSOR_CLIENT;
- if (cursor_has_moved) {
- *sx = ctx->sx;
- *sy = ctx->sy;
- return true;
- }
} else {
/*
* Cursor is over a server (labwc) surface. Clear focus
@@ -538,7 +601,6 @@ cursor_update_common(struct server *server, struct cursor_context *ctx,
cursor_set(seat, cursor);
}
}
- return false;
}
enum lab_edge
@@ -597,32 +659,45 @@ cursor_process_motion(struct server *server, uint32_t time, double *sx, double *
* moving/resizing the wrong view
*/
mousebind->pressed_in_context = false;
- actions_run(seat->pressed.view, server,
- &mousebind->actions, &seat->pressed);
+ actions_run(seat->pressed.ctx.view, server,
+ &mousebind->actions, &seat->pressed.ctx);
}
}
- struct wlr_surface *old_focused_surface =
- seat->seat->pointer_state.focused_surface;
+ /*
+ * Cursor context that is actually interacting with cursor and should
+ * be notified to the client. E.g. it is cleared when menu is open,
+ * and the pressed view is set while out-of-surface dragging.
+ */
+ struct cursor_context notified_ctx = {0};
+ cursor_update_common(server, &ctx, ¬ified_ctx);
- bool notify = cursor_update_common(server, &ctx,
- /* cursor_has_moved */ true, sx, sy);
-
- struct wlr_surface *new_focused_surface =
- seat->seat->pointer_state.focused_surface;
-
- if (rc.focus_follow_mouse && new_focused_surface
- && old_focused_surface != new_focused_surface) {
+ if (rc.focus_follow_mouse) {
/*
- * If followMouse=yes, update the keyboard focus when the
- * cursor enters a surface
+ * If followMouse=yes, entering a surface or view updates
+ * keyboard focus. Note that moving the cursor between a
+ * surface and a SSD within the same view doesn't update
+ * keyboard focus, and that entering a surface/view doesn't
+ * update keyboard focus if implicit grab is active.
*/
- desktop_focus_view_or_surface(seat,
- view_from_wlr_surface(new_focused_surface),
- new_focused_surface, rc.raise_on_focus);
+ bool entering = false;
+ if (notified_ctx.view) {
+ entering = notified_ctx.view
+ != seat->last_cursor_ctx.ctx.view;
+ } else if (notified_ctx.surface) {
+ entering = notified_ctx.surface
+ != seat->last_cursor_ctx.ctx.surface;
+ }
+ if (entering) {
+ desktop_focus_view_or_surface(seat, notified_ctx.view,
+ notified_ctx.surface, rc.raise_on_focus);
+ }
}
+ cursor_context_save(&seat->last_cursor_ctx, ¬ified_ctx);
- return notify;
+ *sx = notified_ctx.sx;
+ *sy = notified_ctx.sy;
+ return notified_ctx.surface;
}
static void
@@ -641,8 +716,7 @@ _cursor_update_focus(struct server *server)
ctx.surface, rc.raise_on_focus);
}
- double sx, sy;
- cursor_update_common(server, &ctx, /*cursor_has_moved*/ false, &sx, &sy);
+ cursor_update_common(server, &ctx, NULL);
}
void
@@ -920,7 +994,7 @@ static void
process_release_mousebinding(struct server *server,
struct cursor_context *ctx, uint32_t button)
{
- if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
+ if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
return;
}
@@ -989,7 +1063,7 @@ static bool
process_press_mousebinding(struct server *server, struct cursor_context *ctx,
uint32_t button)
{
- if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
+ if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
return false;
}
@@ -1073,7 +1147,7 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms
if (ctx.view || ctx.surface) {
/* Store cursor context for later action processing */
- seat_set_pressed(seat, &ctx);
+ cursor_context_save(&seat->pressed, &ctx);
}
if (server->input_mode == LAB_INPUT_STATE_MENU) {
@@ -1138,12 +1212,12 @@ cursor_process_button_release(struct seat *seat, uint32_t button,
{
struct server *server = seat->server;
struct cursor_context ctx = get_cursor_context(server);
- struct wlr_surface *pressed_surface = seat->pressed.surface;
+ struct wlr_surface *pressed_surface = seat->pressed.ctx.surface;
/* Always notify button release event when it's not bound */
const bool notify = !lab_set_contains(&seat->bound_buttons, button);
- seat_reset_pressed(seat);
+ cursor_context_save(&seat->pressed, NULL);
if (server->input_mode == LAB_INPUT_STATE_MENU) {
/* TODO: take into account overflow of time_msec */
@@ -1157,9 +1231,9 @@ cursor_process_button_release(struct seat *seat, uint32_t button,
}
return notify;
}
- if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
- if (ctx.type == LAB_NODE_OSD_ITEM) {
- osd_on_cursor_release(server, ctx.node);
+ if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
+ if (ctx.type == LAB_NODE_CYCLE_OSD_ITEM) {
+ cycle_on_cursor_release(server, ctx.node);
}
return notify;
}
@@ -1347,8 +1421,7 @@ process_cursor_axis(struct server *server, enum wl_pointer_axis orientation,
/* Bindings swallow mouse events if activated */
if (ctx.surface && !consumed) {
/* Make sure we are sending the events to the surface under the cursor */
- double sx, sy;
- cursor_update_common(server, &ctx, /*cursor_has_moved*/ false, &sx, &sy);
+ cursor_update_common(server, &ctx, NULL);
return true;
}
diff --git a/src/input/keyboard.c b/src/input/keyboard.c
index f6b0114c..0171dcd0 100644
--- a/src/input/keyboard.c
+++ b/src/input/keyboard.c
@@ -11,12 +11,12 @@
#include "common/macros.h"
#include "config/keybind.h"
#include "config/rcxml.h"
+#include "cycle.h"
#include "idle.h"
#include "input/ime.h"
#include "input/key-state.h"
#include "labwc.h"
#include "menu/menu.h"
-#include "osd.h"
#include "session-lock.h"
#include "view.h"
#include "workspaces.h"
@@ -141,17 +141,16 @@ handle_modifiers(struct wl_listener *listener, void *data)
overlay_update(seat);
}
- bool window_switcher_active = server->input_mode
- == LAB_INPUT_STATE_WINDOW_SWITCHER;
+ bool cycling = server->input_mode == LAB_INPUT_STATE_CYCLE;
- if ((window_switcher_active || seat->workspace_osd_shown_by_modifier)
+ if ((cycling || seat->workspace_osd_shown_by_modifier)
&& !keyboard_get_all_modifiers(seat)) {
- if (window_switcher_active) {
+ if (cycling) {
if (key_state_nr_bound_keys()) {
should_cancel_cycling_on_next_key_release = true;
} else {
should_cancel_cycling_on_next_key_release = false;
- osd_finish(server, /*switch_focus*/ true);
+ cycle_finish(server, /*switch_focus*/ true);
}
}
if (seat->workspace_osd_shown_by_modifier) {
@@ -388,7 +387,7 @@ handle_key_release(struct server *server, uint32_t evdev_keycode)
*/
if (should_cancel_cycling_on_next_key_release) {
should_cancel_cycling_on_next_key_release = false;
- osd_finish(server, /*switch_focus*/ true);
+ cycle_finish(server, /*switch_focus*/ true);
}
/*
@@ -461,19 +460,19 @@ handle_cycle_view_key(struct server *server, struct keyinfo *keyinfo)
for (int i = 0; i < keyinfo->translated.nr_syms; i++) {
if (keyinfo->translated.syms[i] == XKB_KEY_Escape) {
/* Esc deactivates window switcher */
- osd_finish(server, /*switch_focus*/ false);
+ cycle_finish(server, /*switch_focus*/ false);
return true;
}
if (keyinfo->translated.syms[i] == XKB_KEY_Up
|| keyinfo->translated.syms[i] == XKB_KEY_Left) {
/* Up/Left cycles the window backward */
- osd_cycle(server, LAB_CYCLE_DIR_BACKWARD);
+ cycle_step(server, LAB_CYCLE_DIR_BACKWARD);
return true;
}
if (keyinfo->translated.syms[i] == XKB_KEY_Down
|| keyinfo->translated.syms[i] == XKB_KEY_Right) {
/* Down/Right cycles the window forward */
- osd_cycle(server, LAB_CYCLE_DIR_FORWARD);
+ cycle_step(server, LAB_CYCLE_DIR_FORWARD);
return true;
}
}
@@ -523,7 +522,7 @@ handle_compositor_keybindings(struct keyboard *keyboard,
key_state_store_pressed_key_as_bound(event->keycode);
handle_menu_keys(server, &keyinfo.translated);
return LAB_KEY_HANDLED_TRUE;
- } else if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
+ } else if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
if (handle_cycle_view_key(server, &keyinfo)) {
key_state_store_pressed_key_as_bound(event->keycode);
return LAB_KEY_HANDLED_TRUE;
diff --git a/src/interactive.c b/src/interactive.c
index 49b95250..5a27bb06 100644
--- a/src/interactive.c
+++ b/src/interactive.c
@@ -42,7 +42,7 @@ interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo)
if (wlr_box_empty(geo)) {
return;
}
- /* Resize grab_box while anchoring it to grab_box.{x,y} */
+ /* Resize grab_box while anchoring it to grab_{x,y} */
server->grab_box.x = max_move_scale(server->grab_x, server->grab_box.x,
server->grab_box.width, geo->width);
server->grab_box.y = max_move_scale(server->grab_y, server->grab_box.y,
diff --git a/src/meson.build b/src/meson.build
index 330b5daf..a9afdc4f 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -49,12 +49,12 @@ endif
subdir('common')
subdir('config')
+subdir('cycle')
subdir('decorations')
subdir('foreign-toplevel')
subdir('img')
subdir('input')
subdir('menu')
-subdir('osd')
subdir('protocols')
subdir('scaled-buffer')
subdir('ssd')
diff --git a/src/node.c b/src/node.c
index 1b3dce99..19b4b5ae 100644
--- a/src/node.c
+++ b/src/node.c
@@ -59,13 +59,13 @@ node_menuitem_from_node(struct wlr_scene_node *wlr_scene_node)
return (struct menuitem *)node_descriptor->data;
}
-struct osd_item *
-node_osd_item_from_node(struct wlr_scene_node *wlr_scene_node)
+struct cycle_osd_item *
+node_cycle_osd_item_from_node(struct wlr_scene_node *wlr_scene_node)
{
assert(wlr_scene_node->data);
struct node_descriptor *node_descriptor = wlr_scene_node->data;
- assert(node_descriptor->type == LAB_NODE_OSD_ITEM);
- return (struct osd_item *)node_descriptor->data;
+ assert(node_descriptor->type == LAB_NODE_CYCLE_OSD_ITEM);
+ return (struct cycle_osd_item *)node_descriptor->data;
}
struct ssd_button *
diff --git a/src/osd/osd.c b/src/osd/osd.c
deleted file mode 100644
index 149f8d42..00000000
--- a/src/osd/osd.c
+++ /dev/null
@@ -1,374 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-#include "osd.h"
-#include
-#include
-#include
-#include
-#include "common/array.h"
-#include "common/lab-scene-rect.h"
-#include "common/scene-helpers.h"
-#include "config/rcxml.h"
-#include "labwc.h"
-#include "node.h"
-#include "output.h"
-#include "scaled-buffer/scaled-font-buffer.h"
-#include "scaled-buffer/scaled-icon-buffer.h"
-#include "ssd.h"
-#include "theme.h"
-#include "view.h"
-
-static void update_osd(struct server *server);
-
-static void
-destroy_osd_scenes(struct server *server)
-{
- struct output *output;
- wl_list_for_each(output, &server->outputs, link) {
- struct osd_item *item, *tmp;
- wl_list_for_each_safe(item, tmp, &output->osd_scene.items, link) {
- wl_list_remove(&item->link);
- free(item);
- }
- if (output->osd_scene.tree) {
- wlr_scene_node_destroy(&output->osd_scene.tree->node);
- output->osd_scene.tree = NULL;
- }
- }
-}
-
-static void
-osd_update_preview_outlines(struct view *view)
-{
- /* Create / Update preview outline tree */
- struct server *server = view->server;
- struct theme *theme = server->theme;
- struct lab_scene_rect *rect = view->server->osd_state.preview_outline;
- if (!rect) {
- struct lab_scene_rect_options opts = {
- .border_colors = (float *[3]) {
- theme->osd_window_switcher_preview_border_color[0],
- theme->osd_window_switcher_preview_border_color[1],
- theme->osd_window_switcher_preview_border_color[2],
- },
- .nr_borders = 3,
- .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);
- server->osd_state.preview_outline = rect;
- }
-
- struct wlr_box geo = ssd_max_extents(view);
- lab_scene_rect_set_size(rect, geo.width, geo.height);
- wlr_scene_node_set_position(&rect->tree->node, geo.x, geo.y);
-}
-
-/*
- * 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);
-}
-
-void
-osd_on_view_destroy(struct view *view)
-{
- assert(view);
- struct osd_state *osd_state = &view->server->osd_state;
-
- if (view->server->input_mode != LAB_INPUT_STATE_WINDOW_SWITCHER) {
- /* 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.
- */
-
- /* Also resets preview node */
- osd_state->cycle_view = get_next_cycle_view(view->server,
- 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, /*switch_focus*/ false);
- }
- }
-
- if (osd_state->cycle_view) {
- /* Recreate the OSD to reflect the view has now gone. */
- destroy_osd_scenes(view->server);
- update_osd(view->server);
- }
-
- 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);
- }
- }
-}
-
-void
-osd_on_cursor_release(struct server *server, struct wlr_scene_node *node)
-{
- assert(server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER);
-
- struct osd_item *item = node_osd_item_from_node(node);
- server->osd_state.cycle_view = item->view;
- osd_finish(server, /*switch_focus*/ true);
-}
-
-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);
- }
- if (osd_state->preview_was_shaded) {
- struct view *view = node_view_from_node(osd_state->preview_node);
- view_set_shade(view, true);
- }
- osd_state->preview_node = NULL;
- osd_state->preview_parent = NULL;
- osd_state->preview_anchor = NULL;
- osd_state->preview_was_shaded = false;
- }
-}
-
-void
-osd_begin(struct server *server, enum lab_cycle_dir direction)
-{
- if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
- return;
- }
-
- server->osd_state.cycle_view = get_next_cycle_view(server,
- server->osd_state.cycle_view, direction);
-
- seat_focus_override_begin(&server->seat,
- LAB_INPUT_STATE_WINDOW_SWITCHER, LAB_CURSOR_DEFAULT);
- update_osd(server);
-
- /* Update cursor, in case it is within the area covered by OSD */
- cursor_update_focus(server);
-}
-
-void
-osd_cycle(struct server *server, enum lab_cycle_dir direction)
-{
- assert(server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER);
-
- server->osd_state.cycle_view = get_next_cycle_view(server,
- server->osd_state.cycle_view, direction);
- update_osd(server);
-}
-
-void
-osd_finish(struct server *server, bool switch_focus)
-{
- if (server->input_mode != LAB_INPUT_STATE_WINDOW_SWITCHER) {
- return;
- }
-
- restore_preview_node(server);
- /* FIXME: this sets focus to the old surface even with switch_focus=true */
- seat_focus_override_end(&server->seat);
-
- struct view *cycle_view = server->osd_state.cycle_view;
- server->osd_state.preview_node = NULL;
- server->osd_state.preview_anchor = NULL;
- server->osd_state.cycle_view = NULL;
- server->osd_state.preview_was_shaded = false;
-
- destroy_osd_scenes(server);
-
- if (server->osd_state.preview_outline) {
- /* Destroy the whole multi_rect so we can easily react to new themes */
- wlr_scene_node_destroy(&server->osd_state.preview_outline->tree->node);
- server->osd_state.preview_outline = NULL;
- }
-
- /* Hiding OSD may need a cursor change */
- cursor_update_focus(server);
-
- if (switch_focus && cycle_view) {
- if (rc.window_switcher.unshade) {
- view_set_shade(cycle_view, false);
- }
- desktop_focus_view(cycle_view, /*raise*/ true);
- }
-}
-
-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 */
- restore_preview_node(view->server);
-
- /* Store some pointers so we can reset the preview later on */
- osd_state->preview_node = &view->scene_tree->node;
- osd_state->preview_parent = view->scene_tree->node.parent;
-
- /* Remember the sibling right before the selected node */
- osd_state->preview_anchor = lab_wlr_scene_get_prev_node(
- osd_state->preview_node);
- 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);
- }
-
- /* 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);
- }
- if (rc.window_switcher.unshade && view->shaded) {
- view_set_shade(view, false);
- osd_state->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(osd_state->preview_node,
- view->server->view_tree_always_on_top);
-
- /* Finally raise selected node to the top */
- wlr_scene_node_raise_to_top(osd_state->preview_node);
-}
-
-static void
-update_osd_on_output(struct server *server, struct output *output,
- struct osd_impl *osd_impl, struct wl_array *views)
-{
- if (!output_is_usable(output)) {
- return;
- }
- if (!output->osd_scene.tree) {
- osd_impl->create(output, views);
- assert(output->osd_scene.tree);
- }
- osd_impl->update(output);
-}
-
-static void
-update_osd(struct server *server)
-{
- struct wl_array views;
- wl_array_init(&views);
- view_array_append(server, &views, rc.window_switcher.criteria);
-
- struct osd_impl *osd_impl = NULL;
- switch (rc.window_switcher.style) {
- case WINDOW_SWITCHER_CLASSIC:
- osd_impl = &osd_classic_impl;
- break;
- case WINDOW_SWITCHER_THUMBNAIL:
- osd_impl = &osd_thumbnail_impl;
- break;
- }
-
- if (!wl_array_len(&views) || !server->osd_state.cycle_view) {
- osd_finish(server, /*switch_focus*/ false);
- goto out;
- }
-
- if (rc.window_switcher.show) {
- /* Display the actual OSD */
- switch (rc.window_switcher.output_criteria) {
- case OSD_OUTPUT_ALL: {
- struct output *output;
- wl_list_for_each(output, &server->outputs, link) {
- update_osd_on_output(server, output, osd_impl, &views);
- }
- break;
- }
- case OSD_OUTPUT_POINTER:
- update_osd_on_output(server,
- output_nearest_to_cursor(server), osd_impl, &views);
- break;
- case OSD_OUTPUT_KEYBOARD: {
- struct output *output;
- if (server->active_view) {
- output = server->active_view->output;
- } else {
- /* Fallback to pointer, if there is no active_view */
- output = output_nearest_to_cursor(server);
- }
- update_osd_on_output(server, output, osd_impl, &views);
- break;
- }
- }
- }
-
- if (rc.window_switcher.preview) {
- preview_cycled_view(server->osd_state.cycle_view);
- }
-
- /* Outline current window */
- if (rc.window_switcher.outlines) {
- if (view_is_focusable(server->osd_state.cycle_view)) {
- osd_update_preview_outlines(server->osd_state.cycle_view);
- }
- }
-
-out:
- wl_array_release(&views);
-}
diff --git a/src/output.c b/src/output.c
index 3859ee15..f8c19c56 100644
--- a/src/output.c
+++ b/src/output.c
@@ -182,7 +182,7 @@ handle_output_destroy(struct wl_listener *listener, void *data)
wlr_scene_node_destroy(&output->layer_tree[i]->node);
}
wlr_scene_node_destroy(&output->layer_popup_tree->node);
- wlr_scene_node_destroy(&output->osd_tree->node);
+ wlr_scene_node_destroy(&output->cycle_osd_tree->node);
wlr_scene_node_destroy(&output->session_lock_tree->node);
if (output->workspace_osd) {
wlr_scene_node_destroy(&output->workspace_osd->node);
@@ -542,7 +542,7 @@ 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->osd_scene.items);
+ wl_list_init(&output->cycle_osd.items);
/*
* Create layer-trees (background, bottom, top and overlay) and
@@ -553,7 +553,7 @@ handle_new_output(struct wl_listener *listener, void *data)
wlr_scene_tree_create(&server->scene->tree);
}
output->layer_popup_tree = wlr_scene_tree_create(&server->scene->tree);
- output->osd_tree = wlr_scene_tree_create(&server->scene->tree);
+ output->cycle_osd_tree = wlr_scene_tree_create(&server->scene->tree);
output->session_lock_tree = wlr_scene_tree_create(&server->scene->tree);
/*
@@ -577,7 +577,7 @@ handle_new_output(struct wl_listener *listener, void *data)
wlr_scene_node_place_below(&output->layer_tree[3]->node, menu_node);
wlr_scene_node_place_below(&output->layer_popup_tree->node, menu_node);
- wlr_scene_node_raise_to_top(&output->osd_tree->node);
+ wlr_scene_node_raise_to_top(&output->cycle_osd_tree->node);
wlr_scene_node_raise_to_top(&output->session_lock_tree->node);
/*
diff --git a/src/seat.c b/src/seat.c
index 239155f9..a5cdf3ee 100644
--- a/src/seat.c
+++ b/src/seat.c
@@ -848,46 +848,6 @@ seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer)
seat->focused_layer = layer;
}
-static void
-pressed_surface_destroy(struct wl_listener *listener, void *data)
-{
- struct seat *seat = wl_container_of(listener, seat,
- pressed_surface_destroy);
-
- /*
- * Using data directly prevents 'unused variable'
- * warning when compiling without asserts
- */
- assert(data == seat->pressed.surface);
-
- seat_reset_pressed(seat);
-}
-
-void
-seat_set_pressed(struct seat *seat, struct cursor_context *ctx)
-{
- assert(ctx);
- assert(ctx->view || ctx->surface);
- seat_reset_pressed(seat);
-
- seat->pressed = *ctx;
-
- if (ctx->surface) {
- seat->pressed_surface_destroy.notify = pressed_surface_destroy;
- wl_signal_add(&ctx->surface->events.destroy,
- &seat->pressed_surface_destroy);
- }
-}
-
-void
-seat_reset_pressed(struct seat *seat)
-{
- if (seat->pressed.surface) {
- wl_list_remove(&seat->pressed_surface_destroy.link);
- }
- seat->pressed = (struct cursor_context){0};
-}
-
void
seat_output_layout_changed(struct seat *seat)
{
diff --git a/src/server.c b/src/server.c
index abaaaf0b..9f271b10 100644
--- a/src/server.c
+++ b/src/server.c
@@ -549,6 +549,7 @@ server_init(struct server *server)
wl_list_init(&server->views);
wl_list_init(&server->unmanaged_surfaces);
+ wl_list_init(&server->cycle.views);
server->scene = wlr_scene_create();
if (!server->scene) {
@@ -562,21 +563,21 @@ 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
- * | osd | 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
+ * | 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
*/
server->view_tree_always_on_bottom = wlr_scene_tree_create(&server->scene->tree);
diff --git a/src/view-impl-common.c b/src/view-impl-common.c
index 8f4e2795..e5e10878 100644
--- a/src/view-impl-common.c
+++ b/src/view-impl-common.c
@@ -65,7 +65,6 @@ view_impl_apply_geometry(struct view *view, int w, int h)
{
struct wlr_box *current = &view->current;
struct wlr_box *pending = &view->pending;
- struct wlr_box old = *current;
/*
* Anchor right edge if resizing via left edge.
@@ -100,8 +99,4 @@ view_impl_apply_geometry(struct view *view, int w, int h)
current->width = w;
current->height = h;
-
- if (!wlr_box_equal(current, &old)) {
- view_moved(view);
- }
}
diff --git a/src/view.c b/src/view.c
index 250ac1ab..c439b443 100644
--- a/src/view.c
+++ b/src/view.c
@@ -15,11 +15,11 @@
#include "common/match.h"
#include "common/mem.h"
#include "config/rcxml.h"
+#include "cycle.h"
#include "foreign-toplevel/foreign.h"
#include "input/keyboard.h"
#include "labwc.h"
#include "menu/menu.h"
-#include "osd.h"
#include "output.h"
#include "placement.h"
#include "regions.h"
@@ -35,6 +35,7 @@
#if HAVE_XWAYLAND
#include
+#include "xwayland.h"
#endif
struct view *
@@ -345,48 +346,6 @@ view_prev(struct wl_list *head, struct view *view, enum lab_view_criteria criter
return NULL;
}
-struct view *
-view_next_no_head_stop(struct wl_list *head, struct view *from,
- enum lab_view_criteria criteria)
-{
- assert(head);
-
- struct wl_list *elm = from ? &from->link : head;
-
- struct wl_list *end = elm;
- for (elm = elm->next; elm != end; elm = elm->next) {
- if (elm == head) {
- continue;
- }
- struct view *view = wl_container_of(elm, view, link);
- if (matches_criteria(view, criteria)) {
- return view;
- }
- }
- return from;
-}
-
-struct view *
-view_prev_no_head_stop(struct wl_list *head, struct view *from,
- enum lab_view_criteria criteria)
-{
- assert(head);
-
- struct wl_list *elm = from ? &from->link : head;
-
- struct wl_list *end = elm;
- for (elm = elm->prev; elm != end; elm = elm->prev) {
- if (elm == head) {
- continue;
- }
- struct view *view = wl_container_of(elm, view, link);
- if (matches_criteria(view, criteria)) {
- return view;
- }
- }
- return from;
-}
-
void
view_array_append(struct server *server, struct wl_array *views,
enum lab_view_criteria criteria)
@@ -823,7 +782,7 @@ view_minimize(struct view *view, bool minimized)
{
assert(view);
- if (view->server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
+ if (view->server->input_mode == LAB_INPUT_STATE_CYCLE) {
wlr_log(WLR_ERROR, "not minimizing window while window switching");
return;
}
@@ -858,23 +817,7 @@ view_compute_centered_position(struct view *view, const struct wlr_box *ref,
int height = h + margin.top + margin.bottom;
/* If reference box is NULL then center to usable area */
- if (!ref) {
- ref = &usable;
- }
- *x = ref->x + (ref->width - width) / 2;
- *y = ref->y + (ref->height - height) / 2;
-
- /* Fit the view within the usable area */
- if (*x < usable.x) {
- *x = usable.x;
- } else if (*x + width > usable.x + usable.width) {
- *x = usable.x + usable.width - width;
- }
- if (*y < usable.y) {
- *y = usable.y;
- } else if (*y + height > usable.y + usable.height) {
- *y = usable.y + usable.height - height;
- }
+ box_center(width, height, ref ? ref : &usable, &usable, x, y);
*x += margin.left;
*y += margin.top;
@@ -1281,13 +1224,8 @@ view_apply_fullscreen_geometry(struct view *view)
assert(output_is_usable(view->output));
struct wlr_box box = { 0 };
- wlr_output_effective_resolution(view->output->wlr_output,
- &box.width, &box.height);
- double ox = 0, oy = 0;
- wlr_output_layout_output_coords(view->server->output_layout,
- view->output->wlr_output, &ox, &oy);
- box.x -= ox;
- box.y -= oy;
+ wlr_output_layout_get_box(view->server->output_layout,
+ view->output->wlr_output, &box);
view_move_resize(view, box);
}
@@ -1762,6 +1700,12 @@ view_set_fullscreen(struct view *view, bool fullscreen)
view_apply_special_geometry(view);
}
output_set_has_fullscreen_view(view->output, view->fullscreen);
+ /*
+ * Entering/leaving fullscreen might result in a different
+ * scene node ending up under the cursor even if view_moved()
+ * isn't called. Update cursor focus explicitly for that case.
+ */
+ cursor_update_focus(view->server);
}
static bool
@@ -2304,6 +2248,17 @@ view_move_to_front(struct view *view)
move_to_front(view);
}
+#if HAVE_XWAYLAND
+ /*
+ * view_move_to_front() is typically called on each mouse press
+ * via Raise action. This means we are restacking windows just
+ * about at the same time we send the mouse press input to the
+ * X server, and creates a race where the mouse press could go
+ * to an incorrect X window depending on timing. To mitigate the
+ * race, perform an explicit flush after restacking.
+ */
+ xwayland_flush(view->server);
+#endif
cursor_update_focus(view->server);
desktop_update_top_layer_visibility(view->server);
}
@@ -2612,15 +2567,13 @@ view_destroy(struct view *view)
server->session_lock_manager->last_active_view = NULL;
}
- if (server->seat.pressed.view == view) {
- seat_reset_pressed(&server->seat);
- }
-
if (view->tiled_region_evacuate) {
zfree(view->tiled_region_evacuate);
}
- osd_on_view_destroy(view);
+ /* TODO: call this on map/unmap instead */
+ cycle_reinitialize(server);
+
undecorate(view);
view_set_icon(view, NULL, NULL);
diff --git a/src/xdg.c b/src/xdg.c
index f11d522c..668dfe45 100644
--- a/src/xdg.c
+++ b/src/xdg.c
@@ -10,6 +10,7 @@
#include
#include "buffer.h"
#include "common/array.h"
+#include "common/box.h"
#include "common/macros.h"
#include "common/mem.h"
#include "config/rcxml.h"
@@ -129,6 +130,58 @@ do_late_positioning(struct view *view)
}
}
+static void
+disable_fullscreen_bg(struct view *view)
+{
+ struct xdg_toplevel_view *xdg_view = xdg_toplevel_view_from_view(view);
+ if (xdg_view->fullscreen_bg) {
+ wlr_scene_node_set_enabled(&xdg_view->fullscreen_bg->node, false);
+ }
+}
+
+/*
+ * Centers any fullscreen view smaller than the full output size.
+ * This should be called immediately before view_moved().
+ */
+static void
+center_fullscreen_if_needed(struct view *view)
+{
+ if (!view->fullscreen || !output_is_usable(view->output)) {
+ disable_fullscreen_bg(view);
+ return;
+ }
+
+ struct wlr_box output_box = {0};
+ wlr_output_layout_get_box(view->server->output_layout,
+ view->output->wlr_output, &output_box);
+ box_center(view->current.width, view->current.height, &output_box,
+ &output_box, &view->current.x, &view->current.y);
+
+ /* Reset pending x/y to computed position also */
+ view->pending.x = view->current.x;
+ view->pending.y = view->current.y;
+
+ if (view->current.width >= output_box.width
+ && view->current.width >= output_box.height) {
+ disable_fullscreen_bg(view);
+ return;
+ }
+
+ struct xdg_toplevel_view *xdg_view = xdg_toplevel_view_from_view(view);
+ if (!xdg_view->fullscreen_bg) {
+ const float black[4] = {0, 0, 0, 1};
+ xdg_view->fullscreen_bg =
+ wlr_scene_rect_create(view->scene_tree, 0, 0, black);
+ wlr_scene_node_lower_to_bottom(&xdg_view->fullscreen_bg->node);
+ }
+
+ wlr_scene_node_set_position(&xdg_view->fullscreen_bg->node,
+ output_box.x - view->current.x, output_box.y - view->current.y);
+ wlr_scene_rect_set_size(xdg_view->fullscreen_bg,
+ output_box.width, output_box.height);
+ wlr_scene_node_set_enabled(&xdg_view->fullscreen_bg->node, true);
+}
+
/* TODO: reorder so this forward declaration isn't needed */
static void set_pending_configure_serial(struct view *view, uint32_t serial);
@@ -238,6 +291,8 @@ handle_commit(struct wl_listener *listener, void *data)
if (update_required) {
view_impl_apply_geometry(view, size.width, size.height);
+ center_fullscreen_if_needed(view);
+ view_moved(view);
/*
* Some views (e.g., terminals that scale as multiples of rows
@@ -335,9 +390,11 @@ handle_configure_timeout(void *data)
}
view->current.x = view->pending.x;
view->current.y = view->pending.y;
- view_moved(view);
}
+ center_fullscreen_if_needed(view);
+ view_moved(view);
+
/* Re-sync pending view with current state */
snap_constraints_update(view);
view->pending = view->current;
@@ -400,7 +457,7 @@ handle_request_move(struct wl_listener *listener, void *data)
* want.
*/
struct view *view = wl_container_of(listener, view, request_move);
- if (view == view->server->seat.pressed.view) {
+ if (view == view->server->seat.pressed.ctx.view) {
interactive_begin(view, LAB_INPUT_STATE_MOVE, LAB_EDGE_NONE);
}
}
@@ -418,7 +475,7 @@ handle_request_resize(struct wl_listener *listener, void *data)
*/
struct wlr_xdg_toplevel_resize_event *event = data;
struct view *view = wl_container_of(listener, view, request_resize);
- if (view == view->server->seat.pressed.view) {
+ if (view == view->server->seat.pressed.ctx.view) {
interactive_begin(view, LAB_INPUT_STATE_RESIZE, event->edges);
}
}
@@ -545,6 +602,12 @@ xdg_toplevel_view_configure(struct view *view, struct wlr_box geo)
} else if (view->pending_configure_serial == 0) {
view->current.x = geo.x;
view->current.y = geo.y;
+ /*
+ * It's a bit difficult to think of a corner case where
+ * center_fullscreen_if_needed() would actually be needed
+ * here, but including it anyway for completeness.
+ */
+ center_fullscreen_if_needed(view);
view_moved(view);
}
}
@@ -663,6 +726,10 @@ xdg_toplevel_view_set_fullscreen(struct view *view, bool fullscreen)
if (serial > 0) {
set_pending_configure_serial(view, serial);
}
+ /* Disable background fill immediately on leaving fullscreen */
+ if (!fullscreen) {
+ disable_fullscreen_bg(view);
+ }
}
static void
@@ -785,6 +852,9 @@ handle_map(struct wl_listener *listener, void *data)
set_initial_position(view);
}
+ /* Disable background fill at map (paranoid?) */
+ disable_fullscreen_bg(view);
+
/*
* Set initial "current" position directly before
* calling view_moved() to reduce flicker
@@ -929,8 +999,6 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data)
assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
- wlr_xdg_surface_ping(xdg_surface);
-
struct xdg_toplevel_view *xdg_toplevel_view = znew(*xdg_toplevel_view);
struct view *view = &xdg_toplevel_view->base;
diff --git a/src/xwayland.c b/src/xwayland.c
index efd8d8be..564fa3e2 100644
--- a/src/xwayland.c
+++ b/src/xwayland.c
@@ -274,6 +274,7 @@ handle_commit(struct wl_listener *listener, void *data)
*/
if (current->width != state->width || current->height != state->height) {
view_impl_apply_geometry(view, state->width, state->height);
+ view_moved(view);
}
}
@@ -289,7 +290,7 @@ handle_request_move(struct wl_listener *listener, void *data)
* want.
*/
struct view *view = wl_container_of(listener, view, request_move);
- if (view == view->server->seat.pressed.view) {
+ if (view == view->server->seat.pressed.ctx.view) {
interactive_begin(view, LAB_INPUT_STATE_MOVE, LAB_EDGE_NONE);
}
}
@@ -307,7 +308,7 @@ handle_request_resize(struct wl_listener *listener, void *data)
*/
struct wlr_xwayland_resize_event *event = data;
struct view *view = wl_container_of(listener, view, request_resize);
- if (view == view->server->seat.pressed.view) {
+ if (view == view->server->seat.pressed.ctx.view) {
interactive_begin(view, LAB_INPUT_STATE_RESIZE, event->edges);
}
}
@@ -1050,7 +1051,6 @@ handle_new_surface(struct wl_listener *listener, void *data)
struct server *server =
wl_container_of(listener, server, xwayland_new_surface);
struct wlr_xwayland_surface *xsurface = data;
- wlr_xwayland_surface_ping(xsurface);
/*
* We do not create 'views' for xwayland override_redirect surfaces,
@@ -1438,3 +1438,13 @@ xwayland_update_workarea(struct server *server)
};
wlr_xwayland_set_workareas(server->xwayland, &workarea, 1);
}
+
+void
+xwayland_flush(struct server *server)
+{
+ if (!server->xwayland || !server->xwayland->xwm) {
+ return;
+ }
+
+ xcb_flush(wlr_xwayland_get_xwm_connection(server->xwayland));
+}