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)); +}