Merge branch 'master' into snap-range

This commit is contained in:
elviosak 2025-12-04 09:12:58 -03:00
commit cb6b2d9cda
38 changed files with 930 additions and 790 deletions

View file

@ -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 another window or screen edge. If set to "no", only move to
the next screen edge. Default is yes. the next screen edge. Default is yes.
*<action name="Resize" />* *<action name="Resize" direction="value" />*
Begin interactive resize of window under cursor. 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.
*<action name="ResizeRelative" left="" right="" top="" bottom="" />* *<action name="ResizeRelative" left="" right="" top="" bottom="" />*
Resize window relative to its current size. Values of left, right, Resize window relative to its current size. Values of left, right,
top or bottom tell how much to resize on that edge of window, top or bottom tell how much to resize on that edge of window,

View file

@ -363,7 +363,7 @@ this is for compatibility with Openbox.
*unshade* [yes|no] Temporarily unshade windows when switching between *unshade* [yes|no] Temporarily unshade windows when switching between
them and permanently unshade on the final selection. Default is yes. them and permanently unshade on the final selection. Default is yes.
*<osd show="" style="" output="" thumbnailLabelFormat="" />* *<windowSwitcher><osd show="" style="" output="" thumbnailLabelFormat="" />*
*show* [yes|no] Draw the OnScreenDisplay when switching between *show* [yes|no] Draw the OnScreenDisplay when switching between
windows. Default is yes. 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. "classic" displays window information like icons and titles in a vertical list.
"thumbnail" shows window thumbnail, icon and title in grids. "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. "all" displays the OSD on all monitors.
"pointer" displays the OSD on the monitor containing the mouse pointer. "focused" displays the OSD on the monitor with keyboard focus.
"keyboard" displays the OSD on the monitor with keyboard focus. "cursor" displays the OSD on the monitor containing the mouse pointer.
Default is "all". Default is "all".
*thumbnailLabelFormat* Format to be used for the thumbnail label according to *custom* *thumbnailLabelFormat* Format to be used for the thumbnail label according to *custom*

View file

@ -33,8 +33,8 @@
<!-- <font><theme> can be defined without an attribute to set all places --> <!-- <font><theme> can be defined without an attribute to set all places -->
<theme> <theme>
<name></name> <!-- <name>Numix</name> -->
<icon></icon> <!-- <icon>breeze</icon> -->
<fallbackAppIcon>labwc</fallbackAppIcon> <fallbackAppIcon>labwc</fallbackAppIcon>
<titlebar> <titlebar>
<layout>icon:iconify,max,close</layout> <layout>icon:iconify,max,close</layout>
@ -218,9 +218,9 @@
space automatically, so <margin> is only intended for other, specialist space automatically, so <margin> is only intended for other, specialist
cases. 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.
<margin top="" bottom="" left="" right="" output="" /> <margin top="10" bottom="10" left="10" right="10" output="HDMI-A-1" />
--> -->
<!-- Percent based regions based on output usable area, % char is required --> <!-- Percent based regions based on output usable area, % char is required -->
@ -525,7 +525,11 @@
If mouseEmulation is enabled, all touch up/down/motion events are If mouseEmulation is enabled, all touch up/down/motion events are
translated to mouse button and motion events. translated to mouse button and motion events.
--> -->
<touch deviceName="" mapToOutput="" mouseEmulation="no"/> <touch>
<!-- <deviceName>ELAN2514:00 04F3:2AF1<deviceName> -->
<!-- <mapToOutput>HDMI-A-1</mapToOutput> -->
<mouseEmulation>no</mouseEmulation>
</touch>
<!-- <!--
The tablet cursor movement can be restricted to a single output. The tablet cursor movement can be restricted to a single output.
@ -552,7 +556,8 @@
When using mouse emulation, the pen tip [tip] and the stylus buttons When using mouse emulation, the pen tip [tip] and the stylus buttons
can be set to any available mouse button [Left|Right|Middle|..|Task]. can be set to any available mouse button [Left|Right|Middle|..|Task].
--> -->
<tablet mapToOutput="" rotate="0" mouseEmulation="no"> <tablet rotate="0" mouseEmulation="no">
<!-- <mapToOutput>HDMI-A-1</mapToOutput> -->
<!-- Active area dimensions are in mm --> <!-- Active area dimensions are in mm -->
<area top="0.0" left="0.0" width="0.0" height="0.0" /> <area top="0.0" left="0.0" width="0.0" height="0.0" />
<map button="Tip" to="Left" /> <map button="Tip" to="Left" />
@ -595,21 +600,21 @@
<libinput> <libinput>
<device category="default"> <device category="default">
<naturalScroll></naturalScroll> <!-- <naturalScroll>no</naturalScroll> -->
<leftHanded></leftHanded> <!-- <leftHanded>no</leftHanded> -->
<pointerSpeed></pointerSpeed> <!-- <pointerSpeed>0.0</pointerSpeed> -->
<accelProfile></accelProfile> <!-- <accelProfile>adaptive</accelProfile> -->
<tap>yes</tap> <tap>yes</tap>
<tapButtonMap></tapButtonMap> <!-- <tapButtonMap>lrm</tapButtonMap> -->
<tapAndDrag></tapAndDrag> <!-- <tapAndDrag>yes</tapAndDrag> -->
<dragLock></dragLock> <!-- <dragLock>yes</dragLock> -->
<threeFingerDrag></threeFingerDrag> <!-- <threeFingerDrag>yes</threeFingerDrag> -->
<middleEmulation></middleEmulation> <!-- <middleEmulation>no</middleEmulation> -->
<disableWhileTyping></disableWhileTyping> <!-- <disableWhileTyping>yes</disableWhileTyping> -->
<clickMethod></clickMethod> <!-- <clickMethod>buttonAreas</clickMethod> -->
<scrollMethod></scrollMethod> <!-- <scrollMethod>twofinger</scrollMethod> -->
<sendEventsMode></sendEventsMode> <!-- <sendEventsMode>yes</sendEventsMode> -->
<calibrationMatrix></calibrationMatrix> <!-- <calibrationMatrix>1 0 0 0 1 0</calibrationMatrix> -->
<scrollFactor>1.0</scrollFactor> <scrollFactor>1.0</scrollFactor>
</device> </device>
</libinput> </libinput>
@ -678,7 +683,7 @@
<height>400</height> <height>400</height>
<initScale>2.0</initScale> <initScale>2.0</initScale>
<increment>0.2</increment> <increment>0.2</increment>
<useFilter>true</useFilter> <useFilter>yes</useFilter>
</magnifier> </magnifier>
</labwc_config> </labwc_config>

View file

@ -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, void box_union(struct wlr_box *box_dest, struct wlr_box *box_a,
struct wlr_box *box_b); 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. * Fits and centers a content box (width & height) within a bounding box.
* The content box is downscaled if necessary (preserving aspect ratio) but * The content box is downscaled if necessary (preserving aspect ratio) but

View file

@ -47,7 +47,7 @@ enum lab_node_type {
LAB_NODE_FRAME, LAB_NODE_FRAME,
LAB_NODE_ROOT, LAB_NODE_ROOT,
LAB_NODE_MENUITEM, LAB_NODE_MENUITEM,
LAB_NODE_OSD_ITEM, LAB_NODE_CYCLE_OSD_ITEM,
LAB_NODE_LAYER_SURFACE, LAB_NODE_LAYER_SURFACE,
LAB_NODE_UNMANAGED, LAB_NODE_UNMANAGED,
LAB_NODE_ALL, LAB_NODE_ALL,

View file

@ -183,8 +183,8 @@ struct rcxml {
bool unshade; bool unshade;
enum lab_view_criteria criteria; enum lab_view_criteria criteria;
struct wl_list fields; /* struct window_switcher_field.link */ struct wl_list fields; /* struct window_switcher_field.link */
enum window_switcher_style style; enum cycle_osd_style style;
enum osd_output_criteria output_criteria; enum cycle_osd_output_criteria output_criteria;
char *thumbnail_label_format; char *thumbnail_label_format;
} window_switcher; } window_switcher;

View file

@ -107,15 +107,15 @@ enum lab_window_type {
LAB_WINDOW_TYPE_LEN LAB_WINDOW_TYPE_LEN
}; };
enum window_switcher_style { enum cycle_osd_style {
WINDOW_SWITCHER_CLASSIC, CYCLE_OSD_STYLE_CLASSIC,
WINDOW_SWITCHER_THUMBNAIL, CYCLE_OSD_STYLE_THUMBNAIL,
}; };
enum osd_output_criteria { enum cycle_osd_output_criteria {
OSD_OUTPUT_ALL, CYCLE_OSD_OUTPUT_ALL,
OSD_OUTPUT_POINTER, CYCLE_OSD_OUTPUT_CURSOR,
OSD_OUTPUT_KEYBOARD, CYCLE_OSD_OUTPUT_FOCUSED,
}; };
#endif /* LABWC_CONFIG_TYPES_H */ #endif /* LABWC_CONFIG_TYPES_H */

View file

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LABWC_OSD_H #ifndef LABWC_CYCLE_H
#define LABWC_OSD_H #define LABWC_CYCLE_H
#include <stdbool.h> #include <stdbool.h>
#include <wayland-server-core.h> #include <wayland-server-core.h>
@ -14,7 +14,7 @@ enum lab_cycle_dir {
}; };
/* TODO: add field with keyboard layout? */ /* TODO: add field with keyboard layout? */
enum window_switcher_field_content { enum cycle_osd_field_content {
LAB_FIELD_NONE = 0, LAB_FIELD_NONE = 0,
LAB_FIELD_TYPE, LAB_FIELD_TYPE,
LAB_FIELD_TYPE_SHORT, LAB_FIELD_TYPE_SHORT,
@ -35,8 +35,8 @@ enum window_switcher_field_content {
LAB_FIELD_COUNT LAB_FIELD_COUNT
}; };
struct window_switcher_field { struct cycle_osd_field {
enum window_switcher_field_content content; enum cycle_osd_field_content content;
int width; int width;
char *format; char *format;
struct wl_list link; /* struct rcxml.window_switcher.fields */ struct wl_list link; /* struct rcxml.window_switcher.fields */
@ -48,54 +48,54 @@ struct server;
struct wlr_scene_node; struct wlr_scene_node;
/* Begin window switcher */ /* 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 */ /* 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 */ /* 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 */ /* Re-initialize the window switcher */
void osd_on_view_destroy(struct view *view); void cycle_reinitialize(struct server *server);
/* Focus the clicked window and close OSD */ /* 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 */ /* 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); struct buf *buf, struct view *view);
/* Sets view info to buf according to format */ /* 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); const char *format);
/* Used by rcxml.c when parsing the config */ /* 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); const char *nodename, const char *content);
bool osd_field_is_valid(struct window_switcher_field *field); bool cycle_osd_field_is_valid(struct cycle_osd_field *field);
void osd_field_free(struct window_switcher_field *field); void cycle_osd_field_free(struct cycle_osd_field *field);
/* Internal API */ /* Internal API */
struct osd_item { struct cycle_osd_item {
struct view *view; struct view *view;
struct wlr_scene_tree *tree; struct wlr_scene_tree *tree;
struct wl_list link; struct wl_list link;
}; };
struct osd_impl { struct cycle_osd_impl {
/* /*
* Create a scene-tree of OSD for an output. * 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 * Update output->cycle_osd.tree to highlight
* server->osd_state.cycle_view. * server->cycle_state.selected_view.
*/ */
void (*update)(struct output *output); void (*update)(struct output *output);
}; };
extern struct osd_impl osd_classic_impl; extern struct cycle_osd_impl cycle_osd_classic_impl;
extern struct osd_impl osd_thumbnail_impl; extern struct cycle_osd_impl cycle_osd_thumbnail_impl;
#endif // LABWC_OSD_H #endif // LABWC_CYCLE_H

View file

@ -38,6 +38,14 @@ struct cursor_context {
double sx, sy; 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 * 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); 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_get_resize_edges - calculate resize edge based on cursor position
* @cursor - the current cursor (usually server->seat.cursor) * @cursor - the current cursor (usually server->seat.cursor)

View file

@ -18,7 +18,7 @@ enum input_mode {
LAB_INPUT_STATE_MOVE, LAB_INPUT_STATE_MOVE,
LAB_INPUT_STATE_RESIZE, LAB_INPUT_STATE_RESIZE,
LAB_INPUT_STATE_MENU, LAB_INPUT_STATE_MENU,
LAB_INPUT_STATE_WINDOW_SWITCHER, LAB_INPUT_STATE_CYCLE, /* a.k.a. window switching */
}; };
struct seat { struct seat {
@ -65,8 +65,7 @@ struct seat {
struct input_method_relay *input_method_relay; struct input_method_relay *input_method_relay;
/** /**
* This is usually zeroed and is only set on button press while the * Cursor context saved when a mouse button is pressed on a view/surface.
* mouse is over a view or surface, and zeroed on button release.
* It is used to send cursor motion events to a surface even though * It is used to send cursor motion events to a surface even though
* the cursor has left the surface in the meantime. * the cursor has left the surface in the meantime.
* *
@ -76,10 +75,11 @@ struct seat {
* It is also used to: * It is also used to:
* - determine the target view for action in "Drag" mousebind * - determine the target view for action in "Drag" mousebind
* - validate view move/resize requests from CSD clients * - 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; struct lab_set bound_buttons;
@ -139,7 +139,6 @@ struct seat {
struct wl_list tablet_pads; struct wl_list tablet_pads;
struct wl_listener constraint_commit; struct wl_listener constraint_commit;
struct wl_listener pressed_surface_destroy;
struct wlr_virtual_pointer_manager_v1 *virtual_pointer; struct wlr_virtual_pointer_manager_v1 *virtual_pointer;
struct wl_listener new_virtual_pointer; struct wl_listener new_virtual_pointer;
@ -302,15 +301,15 @@ struct server {
struct wlr_security_context_manager_v1 *security_context_manager_v1; struct wlr_security_context_manager_v1 *security_context_manager_v1;
/* Set when in cycle (alt-tab) mode */ /* Set when in cycle (alt-tab) mode */
struct osd_state { struct cycle_state {
struct view *cycle_view; struct view *selected_view;
struct wl_list views;
bool preview_was_shaded; bool preview_was_shaded;
bool preview_was_enabled; bool preview_was_enabled;
struct wlr_scene_node *preview_node; struct wlr_scene_node *preview_node;
struct wlr_scene_tree *preview_parent; struct wlr_scene_node *preview_dummy;
struct wlr_scene_node *preview_anchor;
struct lab_scene_rect *preview_outline; struct lab_scene_rect *preview_outline;
} osd_state; } cycle;
struct theme *theme; 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_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_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); void seat_output_layout_changed(struct seat *seat);
/* /*

View file

@ -53,10 +53,10 @@ struct menuitem *node_menuitem_from_node(
struct wlr_scene_node *wlr_scene_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 * @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); struct wlr_scene_node *wlr_scene_node);
/** /**

View file

@ -15,14 +15,14 @@ struct output {
struct wlr_scene_output *scene_output; struct wlr_scene_output *scene_output;
struct wlr_scene_tree *layer_tree[LAB_NR_LAYERS]; struct wlr_scene_tree *layer_tree[LAB_NR_LAYERS];
struct wlr_scene_tree *layer_popup_tree; 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_tree *session_lock_tree;
struct wlr_scene_buffer *workspace_osd; struct wlr_scene_buffer *workspace_osd;
struct osd_scene { struct cycle_osd_scene {
struct wl_list items; /* struct osd_item */ struct wl_list items; /* struct cycle_osd_item */
struct wlr_scene_tree *tree; struct wlr_scene_tree *tree;
} osd_scene; } cycle_osd;
/* In output-relative scene coordinates */ /* In output-relative scene coordinates */
struct wlr_box usable_area; struct wlr_box usable_area;

View file

@ -134,6 +134,9 @@ struct view {
const struct view_impl *impl; const struct view_impl *impl;
struct wl_list link; 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: * The primary output that the view is displayed on. Specifically:
* *
@ -293,6 +296,9 @@ struct xdg_toplevel_view {
struct view base; struct view base;
struct wlr_xdg_surface *xdg_surface; 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 */ /* Events unique to xdg-toplevel views */
struct wl_listener set_app_id; struct wl_listener set_app_id;
struct wl_listener request_show_window_menu; 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, struct view *view_prev(struct wl_list *head, struct view *view,
enum lab_view_criteria criteria); 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 * view_array_append() - Append views that match criteria to array
* @server: server context * @server: server context

View file

@ -77,5 +77,7 @@ void xwayland_update_workarea(struct server *server);
void xwayland_reset_cursor(struct server *server); void xwayland_reset_cursor(struct server *server);
void xwayland_flush(struct server *server);
#endif /* HAVE_XWAYLAND */ #endif /* HAVE_XWAYLAND */
#endif /* LABWC_XWAYLAND_H */ #endif /* LABWC_XWAYLAND_H */

View file

@ -18,12 +18,12 @@
#include "common/spawn.h" #include "common/spawn.h"
#include "common/string-helpers.h" #include "common/string-helpers.h"
#include "config/rcxml.h" #include "config/rcxml.h"
#include "cycle.h"
#include "debug.h" #include "debug.h"
#include "input/keyboard.h" #include "input/keyboard.h"
#include "labwc.h" #include "labwc.h"
#include "magnifier.h" #include "magnifier.h"
#include "menu/menu.h" #include "menu/menu.h"
#include "osd.h"
#include "output.h" #include "output.h"
#include "output-virtual.h" #include "output-virtual.h"
#include "regions.h" #include "regions.h"
@ -414,6 +414,20 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
goto cleanup; goto cleanup;
} }
break; 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: case ACTION_TYPE_RESIZE_RELATIVE:
if (!strcmp(argument, "left") || !strcmp(argument, "right") || if (!strcmp(argument, "left") || !strcmp(argument, "right") ||
!strcmp(argument, "top") || !strcmp(argument, "bottom")) { !strcmp(argument, "top") || !strcmp(argument, "bottom")) {
@ -1112,17 +1126,17 @@ run_action(struct view *view, struct server *server, struct action *action,
} }
break; break;
case ACTION_TYPE_NEXT_WINDOW: case ACTION_TYPE_NEXT_WINDOW:
if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) { if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
osd_cycle(server, LAB_CYCLE_DIR_FORWARD); cycle_step(server, LAB_CYCLE_DIR_FORWARD);
} else { } else {
osd_begin(server, LAB_CYCLE_DIR_FORWARD); cycle_begin(server, LAB_CYCLE_DIR_FORWARD);
} }
break; break;
case ACTION_TYPE_PREVIOUS_WINDOW: case ACTION_TYPE_PREVIOUS_WINDOW:
if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) { if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
osd_cycle(server, LAB_CYCLE_DIR_BACKWARD); cycle_step(server, LAB_CYCLE_DIR_BACKWARD);
} else { } else {
osd_begin(server, LAB_CYCLE_DIR_BACKWARD); cycle_begin(server, LAB_CYCLE_DIR_BACKWARD);
} }
break; break;
case ACTION_TYPE_RECONFIGURE: case ACTION_TYPE_RECONFIGURE:
@ -1223,8 +1237,17 @@ run_action(struct view *view, struct server *server, struct action *action,
break; break;
case ACTION_TYPE_RESIZE: case ACTION_TYPE_RESIZE:
if (view) { 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, interactive_begin(view, LAB_INPUT_STATE_RESIZE,
resize_edges); resize_edges);
} }
@ -1569,7 +1592,7 @@ actions_run(struct view *activator, struct server *server,
struct action *action; struct action *action;
wl_list_for_each(action, actions, link) { 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_NEXT_WINDOW
&& action->type != ACTION_TYPE_PREVIOUS_WINDOW) { && action->type != ACTION_TYPE_PREVIOUS_WINDOW) {
wlr_log(WLR_INFO, "Only NextWindow or PreviousWindow " wlr_log(WLR_INFO, "Only NextWindow or PreviousWindow "

View file

@ -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; 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 struct wlr_box
box_fit_within(int width, int height, struct wlr_box *bound) box_fit_within(int width, int height, struct wlr_box *bound)
{ {

View file

@ -29,8 +29,8 @@
#include "config/tablet.h" #include "config/tablet.h"
#include "config/tablet-tool.h" #include "config/tablet-tool.h"
#include "config/touch.h" #include "config/touch.h"
#include "cycle.h"
#include "labwc.h" #include "labwc.h"
#include "osd.h"
#include "regions.h" #include "regions.h"
#include "ssd.h" #include "ssd.h"
#include "translate.h" #include "translate.h"
@ -323,23 +323,23 @@ fill_window_rules(xmlNode *node)
static void static void
clear_window_switcher_fields(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_for_each_safe(field, field_tmp, &rc.window_switcher.fields, link) {
wl_list_remove(&field->link); wl_list_remove(&field->link);
osd_field_free(field); cycle_osd_field_free(field);
} }
} }
static void static void
fill_window_switcher_field(xmlNode *node) 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); wl_list_append(&rc.window_switcher.fields, &field->link);
xmlNode *child; xmlNode *child;
char *key, *content; char *key, *content;
LAB_XML_FOR_EACH(node, child, 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; char *key, *content;
LAB_XML_FOR_EACH(node, child, key, content) { LAB_XML_FOR_EACH(node, child, key, content) {
if (string_null_or_empty(content)) { if (string_null_or_empty(content)) {
wlr_log(WLR_ERROR, "Empty string is not allowed for "
"<libinput><device><%s>. Ignoring.", key);
continue; continue;
} }
if (!strcmp(key, "category")) { if (!strcmp(key, "category")) {
@ -1076,7 +1078,8 @@ entry(xmlNode *node, char *nodename, char *content)
return true; return true;
} else if (str_space_only(content)) { } 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 */ /* handle non-empty leaf nodes */
} else if (!strcmp(nodename, "decoration.core")) { } else if (!strcmp(nodename, "decoration.core")) {
@ -1217,38 +1220,38 @@ entry(xmlNode *node, char *nodename, char *content)
set_bool(content, &rc.window_switcher.show); set_bool(content, &rc.window_switcher.show);
} else if (!strcasecmp(nodename, "style.osd.windowSwitcher")) { } else if (!strcasecmp(nodename, "style.osd.windowSwitcher")) {
if (!strcasecmp(content, "classic")) { if (!strcasecmp(content, "classic")) {
rc.window_switcher.style = WINDOW_SWITCHER_CLASSIC; rc.window_switcher.style = CYCLE_OSD_STYLE_CLASSIC;
} else if (!strcasecmp(content, "thumbnail")) { } else if (!strcasecmp(content, "thumbnail")) {
rc.window_switcher.style = WINDOW_SWITCHER_THUMBNAIL; rc.window_switcher.style = CYCLE_OSD_STYLE_THUMBNAIL;
} else { } else {
wlr_log(WLR_ERROR, "Invalid windowSwitcher style %s: " wlr_log(WLR_ERROR, "Invalid windowSwitcher style %s: "
"should be one of classic|thumbnail", content); "should be one of classic|thumbnail", content);
} }
} else if (!strcasecmp(nodename, "output.osd.windowSwitcher")) { } else if (!strcasecmp(nodename, "output.osd.windowSwitcher")) {
if (!strcasecmp(content, "all")) { if (!strcasecmp(content, "all")) {
rc.window_switcher.output_criteria = OSD_OUTPUT_ALL; rc.window_switcher.output_criteria = CYCLE_OSD_OUTPUT_ALL;
} else if (!strcasecmp(content, "pointer")) { } else if (!strcasecmp(content, "cursor")) {
rc.window_switcher.output_criteria = OSD_OUTPUT_POINTER; rc.window_switcher.output_criteria = CYCLE_OSD_OUTPUT_CURSOR;
} else if (!strcasecmp(content, "keyboard")) { } else if (!strcasecmp(content, "focused")) {
rc.window_switcher.output_criteria = OSD_OUTPUT_KEYBOARD; rc.window_switcher.output_criteria = CYCLE_OSD_OUTPUT_FOCUSED;
} else { } else {
wlr_log(WLR_ERROR, "Invalid windowSwitcher output %s: " 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. */ /* The following two are for backward compatibility only. */
} else if (!strcasecmp(nodename, "show.windowSwitcher")) { } else if (!strcasecmp(nodename, "show.windowSwitcher")) {
set_bool(content, &rc.window_switcher.show); set_bool(content, &rc.window_switcher.show);
wlr_log(WLR_ERROR, "<windowSwitcher show=\"\" /> is deprecated." wlr_log(WLR_ERROR, "<windowSwitcher show=\"\" /> is deprecated."
" Use <osd show=\"\" />"); " Use <windowSwitcher><osd show=\"\" />");
} else if (!strcasecmp(nodename, "style.windowSwitcher")) { } else if (!strcasecmp(nodename, "style.windowSwitcher")) {
if (!strcasecmp(content, "classic")) { if (!strcasecmp(content, "classic")) {
rc.window_switcher.style = WINDOW_SWITCHER_CLASSIC; rc.window_switcher.style = CYCLE_OSD_STYLE_CLASSIC;
} else if (!strcasecmp(content, "thumbnail")) { } else if (!strcasecmp(content, "thumbnail")) {
rc.window_switcher.style = WINDOW_SWITCHER_THUMBNAIL; rc.window_switcher.style = CYCLE_OSD_STYLE_THUMBNAIL;
} }
wlr_log(WLR_ERROR, "<windowSwitcher style=\"\" /> is deprecated." wlr_log(WLR_ERROR, "<windowSwitcher style=\"\" /> is deprecated."
" Use <osd style=\"\" />"); " Use <windowSwitcher><osd style=\"\" />");
} else if (!strcasecmp(nodename, "preview.windowSwitcher")) { } else if (!strcasecmp(nodename, "preview.windowSwitcher")) {
set_bool(content, &rc.window_switcher.preview); set_bool(content, &rc.window_switcher.preview);
@ -1471,8 +1474,8 @@ rcxml_init(void)
rc.snap_tiling_events_mode = LAB_TILING_EVENTS_ALWAYS; rc.snap_tiling_events_mode = LAB_TILING_EVENTS_ALWAYS;
rc.window_switcher.show = true; rc.window_switcher.show = true;
rc.window_switcher.style = WINDOW_SWITCHER_CLASSIC; rc.window_switcher.style = CYCLE_OSD_STYLE_CLASSIC;
rc.window_switcher.output_criteria = OSD_OUTPUT_ALL; rc.window_switcher.output_criteria = CYCLE_OSD_OUTPUT_ALL;
rc.window_switcher.thumbnail_label_format = xstrdup("%T"); rc.window_switcher.thumbnail_label_format = xstrdup("%T");
rc.window_switcher.preview = true; rc.window_switcher.preview = true;
rc.window_switcher.outlines = true; rc.window_switcher.outlines = true;
@ -1641,7 +1644,7 @@ static void
load_default_window_switcher_fields(void) load_default_window_switcher_fields(void)
{ {
static const struct { static const struct {
enum window_switcher_field_content content; enum cycle_osd_field_content content;
int width; int width;
} fields[] = { } fields[] = {
#if HAVE_LIBSFDO #if HAVE_LIBSFDO
@ -1654,7 +1657,7 @@ load_default_window_switcher_fields(void)
#endif #endif
}; };
struct window_switcher_field *field; struct cycle_osd_field *field;
for (size_t i = 0; i < ARRAY_SIZE(fields); i++) { for (size_t i = 0; i < ARRAY_SIZE(fields); i++) {
field = znew(*field); field = znew(*field);
field->content = fields[i].content; field->content = fields[i].content;
@ -1873,13 +1876,13 @@ validate(void)
/* OSD fields */ /* OSD fields */
int field_width_sum = 0; 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) { wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.fields, link) {
field_width_sum += field->width; 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); wlr_log(WLR_ERROR, "Deleting invalid window switcher field %p", field);
wl_list_remove(&field->link); wl_list_remove(&field->link);
osd_field_free(field); cycle_osd_field_free(field);
} }
} }
} }

379
src/cycle/cycle.c Normal file
View file

@ -0,0 +1,379 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "cycle.h"
#include <assert.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#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;
}

View file

@ -1,5 +1,5 @@
labwc_sources += files( labwc_sources += files(
'osd.c', 'cycle.c',
'osd-classic.c', 'osd-classic.c',
'osd-field.c', 'osd-field.c',
'osd-thumbnail.c', 'osd-thumbnail.c',

View file

@ -4,24 +4,25 @@
#include <wlr/types/wlr_scene.h> #include <wlr/types/wlr_scene.h>
#include <wlr/util/box.h> #include <wlr/util/box.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "common/array.h"
#include "common/buf.h" #include "common/buf.h"
#include "common/font.h" #include "common/font.h"
#include "common/lab-scene-rect.h" #include "common/lab-scene-rect.h"
#include "common/list.h" #include "common/list.h"
#include "common/mem.h"
#include "common/string-helpers.h" #include "common/string-helpers.h"
#include "config/rcxml.h" #include "config/rcxml.h"
#include "cycle.h"
#include "labwc.h" #include "labwc.h"
#include "node.h" #include "node.h"
#include "osd.h"
#include "output.h" #include "output.h"
#include "scaled-buffer/scaled-font-buffer.h" #include "scaled-buffer/scaled-font-buffer.h"
#include "scaled-buffer/scaled-icon-buffer.h" #include "scaled-buffer/scaled-icon-buffer.h"
#include "theme.h" #include "theme.h"
#include "view.h"
#include "workspaces.h" #include "workspaces.h"
struct osd_classic_item { struct cycle_osd_classic_item {
struct osd_item base; struct cycle_osd_item base;
struct wlr_scene_tree *normal_tree, *active_tree; 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 = struct window_switcher_classic_theme *switcher_theme =
&theme->osd_window_switcher_classic; &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) { wl_list_for_each(field, &rc.window_switcher.fields, link) {
int field_width = field_widths_sum * field->width / 100.0; int field_width = field_widths_sum * field->width / 100.0;
struct wlr_scene_node *node = NULL; struct wlr_scene_node *node = NULL;
@ -51,7 +52,7 @@ create_fields_scene(struct server *server, struct view *view,
height = icon_size; height = icon_size;
} else { } else {
struct buf buf = BUF_INIT; 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)) { if (!string_null_or_empty(buf.data)) {
struct scaled_font_buffer *font_buffer = struct scaled_font_buffer *font_buffer =
@ -76,9 +77,9 @@ create_fields_scene(struct server *server, struct view *view,
} }
static void 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 server *server = output->server;
struct theme *theme = server->theme; 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; int padding = theme->osd_border_width + switcher_theme->padding;
bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1; bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1;
const char *workspace_name = server->workspaces.current->name; const char *workspace_name = server->workspaces.current->name;
int nr_views = wl_list_length(&server->cycle.views);
struct wlr_box output_box; struct wlr_box output_box;
wlr_output_layout_get_box(server->output_layout, output->wlr_output, 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) { if (switcher_theme->width_is_percent) {
w = output_box.width * switcher_theme->width / 100; 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) { if (show_workspace) {
/* workspace indicator */ /* workspace indicator */
h += switcher_theme->item_height; 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 *text_color = theme->osd_label_text_color;
float *bg_color = theme->osd_bg_color; float *bg_color = theme->osd_bg_color;
@ -116,7 +118,7 @@ osd_classic_create(struct output *output, struct wl_array *views)
.width = w, .width = w,
.height = h, .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; int y = padding;
@ -134,7 +136,7 @@ osd_classic_create(struct output *output, struct wl_array *views)
} }
struct scaled_font_buffer *font_buffer = 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, wlr_scene_node_set_position(&font_buffer->scene_buffer->node,
x, y + (switcher_theme->item_height - font_height(&font)) / 2); x, y + (switcher_theme->item_height - font_height(&font)) / 2);
scaled_font_buffer_update(font_buffer, workspace_name, 0, 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 */ /* Draw text for each node */
struct view **view; struct view *view;
wl_array_for_each(view, views) { wl_list_for_each(view, &server->cycle.views, cycle_link) {
struct osd_classic_item *item = znew(*item); struct cycle_osd_classic_item *item = znew(*item);
wl_list_append(&output->osd_scene.items, &item->base.link); wl_list_append(&output->cycle_osd.items, &item->base.link);
item->base.view = *view; item->base.view = view;
item->base.tree = wlr_scene_tree_create(output->osd_scene.tree); item->base.tree = wlr_scene_tree_create(output->cycle_osd.tree);
node_descriptor_create(&item->base.tree->node, node_descriptor_create(&item->base.tree->node,
LAB_NODE_OSD_ITEM, NULL, item); LAB_NODE_CYCLE_OSD_ITEM, NULL, item);
/* /*
* OSD border * 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}); w - 2 * padding, switcher_theme->item_height, (float[4]) {0});
wlr_scene_node_set_position(&hitbox->node, padding, y); 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); 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); text_color, active_bg_color, field_widths_sum, x, y);
y += switcher_theme->item_height; y += switcher_theme->item_height;
@ -218,23 +220,23 @@ osd_classic_create(struct output *output, struct wl_array *views)
error:; error:;
/* Center OSD */ /* 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.x + (output_box.width - w) / 2,
output_box.y + (output_box.height - h) / 2); output_box.y + (output_box.height - h) / 2);
} }
static void static void
osd_classic_update(struct output *output) cycle_osd_classic_update(struct output *output)
{ {
struct osd_classic_item *item; struct cycle_osd_classic_item *item;
wl_list_for_each(item, &output->osd_scene.items, base.link) { wl_list_for_each(item, &output->cycle_osd.items, base.link) {
bool active = item->base.view == output->server->osd_state.cycle_view; 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->normal_tree->node, !active);
wlr_scene_node_set_enabled(&item->active_tree->node, active); wlr_scene_node_set_enabled(&item->active_tree->node, active);
} }
} }
struct osd_impl osd_classic_impl = { struct cycle_osd_impl cycle_osd_classic_impl = {
.create = osd_classic_create, .create = cycle_osd_classic_create,
.update = osd_classic_update, .update = cycle_osd_classic_update,
}; };

View file

@ -5,11 +5,11 @@
#include "common/buf.h" #include "common/buf.h"
#include "common/mem.h" #include "common/mem.h"
#include "config/rcxml.h" #include "config/rcxml.h"
#include "cycle.h"
#include "view.h" #include "view.h"
#include "workspaces.h" #include "workspaces.h"
#include "labwc.h" #include "labwc.h"
#include "desktop-entry.h" #include "desktop-entry.h"
#include "osd.h"
#include "output.h" #include "output.h"
/* includes '%', terminating 's' and NULL byte, 8 is enough for %-9999s */ /* 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] = { 'T', field_set_title },
[LAB_FIELD_TITLE_SHORT] = { 't', field_set_title_short }, [LAB_FIELD_TITLE_SHORT] = { 't', field_set_title_short },
/* fmt_char can never be matched so prevents LAB_FIELD_CUSTOM recursion */ /* 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 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) { if (!format) {
wlr_log(WLR_ERROR, "Missing format for custom window switcher field"); wlr_log(WLR_ERROR, "Missing format for custom window switcher field");
@ -286,7 +286,7 @@ reset_format:
} }
void 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) const char *nodename, const char *content)
{ {
if (!strcmp(nodename, "content")) { if (!strcmp(nodename, "content")) {
@ -332,7 +332,7 @@ osd_field_arg_from_xml_node(struct window_switcher_field *field,
} }
bool 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) { if (field->content == LAB_FIELD_NONE) {
wlr_log(WLR_ERROR, "Invalid OSD field: no content set"); wlr_log(WLR_ERROR, "Invalid OSD field: no content set");
@ -350,7 +350,7 @@ osd_field_is_valid(struct window_switcher_field *field)
} }
void 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) struct buf *buf, struct view *view)
{ {
if (field->content == LAB_FIELD_NONE) { if (field->content == LAB_FIELD_NONE) {
@ -363,7 +363,7 @@ osd_field_get_content(struct window_switcher_field *field,
} }
void void
osd_field_free(struct window_switcher_field *field) cycle_osd_field_free(struct cycle_osd_field *field)
{ {
zfree(field->format); zfree(field->format);
zfree(field); zfree(field);

View file

@ -5,22 +5,22 @@
#include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_scene.h> #include <wlr/types/wlr_scene.h>
#include "config/rcxml.h" #include "config/rcxml.h"
#include "common/array.h"
#include "common/box.h" #include "common/box.h"
#include "common/buf.h" #include "common/buf.h"
#include "common/lab-scene-rect.h" #include "common/lab-scene-rect.h"
#include "common/list.h" #include "common/list.h"
#include "common/mem.h"
#include "cycle.h"
#include "labwc.h" #include "labwc.h"
#include "node.h" #include "node.h"
#include "osd.h"
#include "output.h" #include "output.h"
#include "scaled-buffer/scaled-font-buffer.h" #include "scaled-buffer/scaled-font-buffer.h"
#include "scaled-buffer/scaled-icon-buffer.h" #include "scaled-buffer/scaled-icon-buffer.h"
#include "theme.h" #include "theme.h"
#include "view.h" #include "view.h"
struct osd_thumbnail_item { struct cycle_osd_thumbnail_item {
struct osd_item base; struct cycle_osd_item base;
struct scaled_font_buffer *normal_label; struct scaled_font_buffer *normal_label;
struct scaled_font_buffer *active_label; struct scaled_font_buffer *active_label;
struct lab_scene_rect *active_bg; 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) const float *text_color, const float *bg_color, int y)
{ {
struct buf buf = BUF_INIT; struct buf buf = BUF_INIT;
osd_field_set_custom(&buf, view, cycle_osd_field_set_custom(&buf, view,
rc.window_switcher.thumbnail_label_format); rc.window_switcher.thumbnail_label_format);
struct scaled_font_buffer *buffer = struct scaled_font_buffer *buffer =
scaled_font_buffer_create(parent); scaled_font_buffer_create(parent);
@ -115,7 +115,7 @@ create_label(struct wlr_scene_tree *parent, struct view *view,
return buffer; return buffer;
} }
static struct osd_thumbnail_item * static struct cycle_osd_thumbnail_item *
create_item_scene(struct wlr_scene_tree *parent, struct view *view, create_item_scene(struct wlr_scene_tree *parent, struct view *view,
struct output *output) struct output *output)
{ {
@ -136,10 +136,10 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view,
return NULL; return NULL;
} }
struct osd_thumbnail_item *item = znew(*item); struct cycle_osd_thumbnail_item *item = znew(*item);
wl_list_append(&output->osd_scene.items, &item->base.link); wl_list_append(&output->cycle_osd.items, &item->base.link);
struct wlr_scene_tree *tree = wlr_scene_tree_create(parent); 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.tree = tree;
item->base.view = view; item->base.view = view;
@ -226,9 +226,9 @@ get_items_geometry(struct output *output, struct theme *theme,
} }
static void 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 server *server = output->server;
struct theme *theme = server->theme; struct theme *theme = server->theme;
@ -236,19 +236,19 @@ osd_thumbnail_create(struct output *output, struct wl_array *views)
&theme->osd_window_switcher_thumbnail; &theme->osd_window_switcher_thumbnail;
int padding = theme->osd_border_width + switcher_theme->padding; 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); assert(nr_views > 0);
int nr_rows, nr_cols; int nr_rows, nr_cols;
get_items_geometry(output, theme, nr_views, &nr_rows, &nr_cols); get_items_geometry(output, theme, nr_views, &nr_rows, &nr_cols);
/* items */ /* items */
struct view **view; struct view *view;
int index = 0; int index = 0;
wl_array_for_each(view, views) { wl_list_for_each(view, &server->cycle.views, cycle_link) {
struct osd_thumbnail_item *item = create_item_scene( struct cycle_osd_thumbnail_item *item = create_item_scene(
output->osd_scene.tree, *view, output); output->cycle_osd.tree, view, output);
if (!item) { if (!item) {
break; break;
} }
@ -268,7 +268,7 @@ osd_thumbnail_create(struct output *output, struct wl_array *views)
.height = nr_rows * switcher_theme->item_height + 2 * padding, .height = nr_rows * switcher_theme->item_height + 2 * padding,
}; };
struct lab_scene_rect *bg = 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); wlr_scene_node_lower_to_bottom(&bg->tree->node);
/* center */ /* center */
@ -277,15 +277,15 @@ osd_thumbnail_create(struct output *output, struct wl_array *views)
&output_box); &output_box);
int lx = output_box.x + (output_box.width - bg_opts.width) / 2; int lx = output_box.x + (output_box.width - bg_opts.width) / 2;
int ly = output_box.y + (output_box.height - bg_opts.height) / 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 static void
osd_thumbnail_update(struct output *output) cycle_osd_thumbnail_update(struct output *output)
{ {
struct osd_thumbnail_item *item; struct cycle_osd_thumbnail_item *item;
wl_list_for_each(item, &output->osd_scene.items, base.link) { wl_list_for_each(item, &output->cycle_osd.items, base.link) {
bool active = (item->base.view == output->server->osd_state.cycle_view); 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_bg->tree->node, active);
wlr_scene_node_set_enabled( wlr_scene_node_set_enabled(
&item->active_label->scene_buffer->node, active); &item->active_label->scene_buffer->node, active);
@ -294,7 +294,7 @@ osd_thumbnail_update(struct output *output)
} }
} }
struct osd_impl osd_thumbnail_impl = { struct cycle_osd_impl cycle_osd_thumbnail_impl = {
.create = osd_thumbnail_create, .create = cycle_osd_thumbnail_create,
.update = osd_thumbnail_update, .update = cycle_osd_thumbnail_update,
}; };

View file

@ -21,7 +21,7 @@
#define IGNORE_SSD true #define IGNORE_SSD true
#define IGNORE_MENU true #define IGNORE_MENU true
#define IGNORE_OSD_PREVIEW_OUTLINE true #define IGNORE_CYCLE_PREVIEW_OUTLINE true
#define IGNORE_SNAPPING_OVERLAY true #define IGNORE_SNAPPING_OVERLAY true
static struct view *last_view; 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) { if (node->parent == &server->scene->tree) {
struct output *output; struct output *output;
wl_list_for_each(output, &server->outputs, link) { 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"; return "output->osd_tree";
} }
if (node == &output->layer_popup_tree->node) { if (node == &output->layer_popup_tree->node) {
@ -150,10 +150,10 @@ get_special(struct server *server, struct wlr_scene_node *node)
/* Created on-demand */ /* Created on-demand */
return "seat->im_relay->popup_tree"; return "seat->im_relay->popup_tree";
} }
if (server->osd_state.preview_outline if (server->cycle.preview_outline
&& node == &server->osd_state.preview_outline->tree->node) { && node == &server->cycle.preview_outline->tree->node) {
/* Created on-demand */ /* Created on-demand */
return "osd_state->preview_outline"; return "cycle_state->preview_outline";
} }
#if HAVE_XWAYLAND #if HAVE_XWAYLAND
if (node == &server->unmanaged_tree->node) { 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); 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) if ((IGNORE_MENU && node == &server->menu_tree->node)
|| (IGNORE_SSD && last_view || (IGNORE_SSD && last_view
&& ssd_debug_is_root_node(last_view->ssd, node)) && ssd_debug_is_root_node(last_view->ssd, node))
|| (IGNORE_OSD_PREVIEW_OUTLINE && osd_preview_outline || (IGNORE_CYCLE_PREVIEW_OUTLINE && server->cycle.preview_outline
&& node == &osd_preview_outline->tree->node) && node == &server->cycle.preview_outline->tree->node)
|| (IGNORE_SNAPPING_OVERLAY && server->seat.overlay.rect || (IGNORE_SNAPPING_OVERLAY && server->seat.overlay.rect
&& node == &server->seat.overlay.rect->tree->node)) { && node == &server->seat.overlay.rect->tree->node)) {
printf("%*c%s\n", pos + 4 + INDENT_SIZE, ' ', "<skipping children>"); printf("%*c%s\n", pos + 4 + INDENT_SIZE, ' ', "<skipping children>");

View file

@ -74,7 +74,7 @@ desktop_focus_view(struct view *view, bool raise)
return; 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"); wlr_log(WLR_DEBUG, "not focusing window while window switching");
return; return;
} }
@ -340,10 +340,10 @@ get_cursor_context(struct server *server)
ret.node = node; ret.node = node;
ret.type = LAB_NODE_MENUITEM; ret.type = LAB_NODE_MENUITEM;
return ret; return ret;
case LAB_NODE_OSD_ITEM: case LAB_NODE_CYCLE_OSD_ITEM:
/* Always return the top scene node for osd items */ /* Always return the top scene node for osd items */
ret.node = node; ret.node = node;
ret.type = LAB_NODE_OSD_ITEM; ret.type = LAB_NODE_CYCLE_OSD_ITEM;
return ret; return ret;
case LAB_NODE_BUTTON_FIRST...LAB_NODE_BUTTON_LAST: case LAB_NODE_BUTTON_FIRST...LAB_NODE_BUTTON_LAST:
case LAB_NODE_SSD_ROOT: case LAB_NODE_SSD_ROOT:

View file

@ -34,7 +34,7 @@ handle_drag_start(struct wl_listener *listener, void *data)
struct wlr_drag *drag = data; struct wlr_drag *drag = data;
seat->drag.active = true; seat->drag.active = true;
seat_reset_pressed(seat); cursor_context_save(&seat->pressed, NULL);
if (drag->icon) { if (drag->icon) {
/* Cleans up automatically on drag->icon->events.destroy */ /* Cleans up automatically on drag->icon->events.destroy */
wlr_scene_drag_icon_create(seat->drag.icons, drag->icon); wlr_scene_drag_icon_create(seat->drag.icons, drag->icon);

View file

@ -21,6 +21,7 @@
#include "common/mem.h" #include "common/mem.h"
#include "config/mousebind.h" #include "config/mousebind.h"
#include "config/rcxml.h" #include "config/rcxml.h"
#include "cycle.h"
#include "dnd.h" #include "dnd.h"
#include "idle.h" #include "idle.h"
#include "input/gestures.h" #include "input/gestures.h"
@ -30,7 +31,6 @@
#include "labwc.h" #include "labwc.h"
#include "layers.h" #include "layers.h"
#include "menu/menu.h" #include "menu/menu.h"
#include "osd.h"
#include "output.h" #include "output.h"
#include "resistance.h" #include "resistance.h"
#include "resize-outlines.h" #include "resize-outlines.h"
@ -437,8 +437,72 @@ cursor_update_image(struct seat *seat)
cursor_names[cursor]); 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 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 * 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)) { if (!wlr_seat_pointer_has_grab(seat->seat)) {
return false; 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); struct wlr_surface *toplevel = get_toplevel(ctx->surface);
if (toplevel && toplevel == get_toplevel(seat->pressed.surface)) { if (toplevel && toplevel == get_toplevel(seat->pressed.ctx.surface)) {
seat_set_pressed(seat, ctx); cursor_context_save(&seat->pressed, ctx);
return true; 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() * Common logic shared by cursor_update_focus(), process_cursor_motion()
* and cursor_axis() * and process_cursor_axis()
*/ */
static bool static void
cursor_update_common(struct server *server, struct cursor_context *ctx, cursor_update_common(struct server *server, const struct cursor_context *ctx,
bool cursor_has_moved, double *sx, double *sy) struct cursor_context *notified_ctx)
{ {
struct seat *seat = &server->seat; struct seat *seat = &server->seat;
struct wlr_seat *wlr_seat = seat->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 * interactive move/resize, window switcher and
* menu interaction. * menu interaction.
*/ */
return false; return;
} }
/* TODO: verify drag_icon logic */ /* 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) && !update_pressed_surface(seat, ctx)
&& !seat->drag.active) { && !seat->drag.active) {
if (cursor_has_moved) { if (notified_ctx) {
/* /*
* Button has been pressed while over another * Button has been pressed while over another
* surface and is still held down. Just send * 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. * if the cursor moves outside of the surface.
*/ */
int lx, ly; int lx, ly;
wlr_scene_node_coords(seat->pressed.node, &lx, &ly); wlr_scene_node_coords(seat->pressed.ctx.node, &lx, &ly);
*sx = server->seat.cursor->x - lx; *notified_ctx = seat->pressed.ctx;
*sy = server->seat.cursor->y - ly; notified_ctx->sx = server->seat.cursor->x - lx;
return true; notified_ctx->sy = server->seat.cursor->y - ly;
} }
return false; return;
}
if (notified_ctx) {
*notified_ctx = *ctx;
} }
if (ctx->surface) { 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, wlr_seat_pointer_notify_enter(wlr_seat, ctx->surface,
ctx->sx, ctx->sy); ctx->sx, ctx->sy);
seat->server_cursor = LAB_CURSOR_CLIENT; seat->server_cursor = LAB_CURSOR_CLIENT;
if (cursor_has_moved) {
*sx = ctx->sx;
*sy = ctx->sy;
return true;
}
} else { } else {
/* /*
* Cursor is over a server (labwc) surface. Clear focus * 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); cursor_set(seat, cursor);
} }
} }
return false;
} }
enum lab_edge enum lab_edge
@ -597,32 +659,45 @@ cursor_process_motion(struct server *server, uint32_t time, double *sx, double *
* moving/resizing the wrong view * moving/resizing the wrong view
*/ */
mousebind->pressed_in_context = false; mousebind->pressed_in_context = false;
actions_run(seat->pressed.view, server, actions_run(seat->pressed.ctx.view, server,
&mousebind->actions, &seat->pressed); &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, &notified_ctx);
bool notify = cursor_update_common(server, &ctx, if (rc.focus_follow_mouse) {
/* 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 followMouse=yes, update the keyboard focus when the * If followMouse=yes, entering a surface or view updates
* cursor enters a surface * 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, bool entering = false;
view_from_wlr_surface(new_focused_surface), if (notified_ctx.view) {
new_focused_surface, rc.raise_on_focus); 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, &notified_ctx);
return notify; *sx = notified_ctx.sx;
*sy = notified_ctx.sy;
return notified_ctx.surface;
} }
static void static void
@ -641,8 +716,7 @@ _cursor_update_focus(struct server *server)
ctx.surface, rc.raise_on_focus); ctx.surface, rc.raise_on_focus);
} }
double sx, sy; cursor_update_common(server, &ctx, NULL);
cursor_update_common(server, &ctx, /*cursor_has_moved*/ false, &sx, &sy);
} }
void void
@ -920,7 +994,7 @@ static void
process_release_mousebinding(struct server *server, process_release_mousebinding(struct server *server,
struct cursor_context *ctx, uint32_t button) 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; return;
} }
@ -989,7 +1063,7 @@ static bool
process_press_mousebinding(struct server *server, struct cursor_context *ctx, process_press_mousebinding(struct server *server, struct cursor_context *ctx,
uint32_t button) uint32_t button)
{ {
if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) { if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
return false; 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) { if (ctx.view || ctx.surface) {
/* Store cursor context for later action processing */ /* 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) { 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 server *server = seat->server;
struct cursor_context ctx = get_cursor_context(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 */ /* Always notify button release event when it's not bound */
const bool notify = !lab_set_contains(&seat->bound_buttons, button); 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) { if (server->input_mode == LAB_INPUT_STATE_MENU) {
/* TODO: take into account overflow of time_msec */ /* TODO: take into account overflow of time_msec */
@ -1157,9 +1231,9 @@ cursor_process_button_release(struct seat *seat, uint32_t button,
} }
return notify; return notify;
} }
if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) { if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
if (ctx.type == LAB_NODE_OSD_ITEM) { if (ctx.type == LAB_NODE_CYCLE_OSD_ITEM) {
osd_on_cursor_release(server, ctx.node); cycle_on_cursor_release(server, ctx.node);
} }
return notify; return notify;
} }
@ -1347,8 +1421,7 @@ process_cursor_axis(struct server *server, enum wl_pointer_axis orientation,
/* Bindings swallow mouse events if activated */ /* Bindings swallow mouse events if activated */
if (ctx.surface && !consumed) { if (ctx.surface && !consumed) {
/* Make sure we are sending the events to the surface under the cursor */ /* Make sure we are sending the events to the surface under the cursor */
double sx, sy; cursor_update_common(server, &ctx, NULL);
cursor_update_common(server, &ctx, /*cursor_has_moved*/ false, &sx, &sy);
return true; return true;
} }

View file

@ -11,12 +11,12 @@
#include "common/macros.h" #include "common/macros.h"
#include "config/keybind.h" #include "config/keybind.h"
#include "config/rcxml.h" #include "config/rcxml.h"
#include "cycle.h"
#include "idle.h" #include "idle.h"
#include "input/ime.h" #include "input/ime.h"
#include "input/key-state.h" #include "input/key-state.h"
#include "labwc.h" #include "labwc.h"
#include "menu/menu.h" #include "menu/menu.h"
#include "osd.h"
#include "session-lock.h" #include "session-lock.h"
#include "view.h" #include "view.h"
#include "workspaces.h" #include "workspaces.h"
@ -141,17 +141,16 @@ handle_modifiers(struct wl_listener *listener, void *data)
overlay_update(seat); overlay_update(seat);
} }
bool window_switcher_active = server->input_mode bool cycling = server->input_mode == LAB_INPUT_STATE_CYCLE;
== LAB_INPUT_STATE_WINDOW_SWITCHER;
if ((window_switcher_active || seat->workspace_osd_shown_by_modifier) if ((cycling || seat->workspace_osd_shown_by_modifier)
&& !keyboard_get_all_modifiers(seat)) { && !keyboard_get_all_modifiers(seat)) {
if (window_switcher_active) { if (cycling) {
if (key_state_nr_bound_keys()) { if (key_state_nr_bound_keys()) {
should_cancel_cycling_on_next_key_release = true; should_cancel_cycling_on_next_key_release = true;
} else { } else {
should_cancel_cycling_on_next_key_release = false; 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) { 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) { if (should_cancel_cycling_on_next_key_release) {
should_cancel_cycling_on_next_key_release = false; 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++) { for (int i = 0; i < keyinfo->translated.nr_syms; i++) {
if (keyinfo->translated.syms[i] == XKB_KEY_Escape) { if (keyinfo->translated.syms[i] == XKB_KEY_Escape) {
/* Esc deactivates window switcher */ /* Esc deactivates window switcher */
osd_finish(server, /*switch_focus*/ false); cycle_finish(server, /*switch_focus*/ false);
return true; return true;
} }
if (keyinfo->translated.syms[i] == XKB_KEY_Up if (keyinfo->translated.syms[i] == XKB_KEY_Up
|| keyinfo->translated.syms[i] == XKB_KEY_Left) { || keyinfo->translated.syms[i] == XKB_KEY_Left) {
/* Up/Left cycles the window backward */ /* Up/Left cycles the window backward */
osd_cycle(server, LAB_CYCLE_DIR_BACKWARD); cycle_step(server, LAB_CYCLE_DIR_BACKWARD);
return true; return true;
} }
if (keyinfo->translated.syms[i] == XKB_KEY_Down if (keyinfo->translated.syms[i] == XKB_KEY_Down
|| keyinfo->translated.syms[i] == XKB_KEY_Right) { || keyinfo->translated.syms[i] == XKB_KEY_Right) {
/* Down/Right cycles the window forward */ /* Down/Right cycles the window forward */
osd_cycle(server, LAB_CYCLE_DIR_FORWARD); cycle_step(server, LAB_CYCLE_DIR_FORWARD);
return true; return true;
} }
} }
@ -523,7 +522,7 @@ handle_compositor_keybindings(struct keyboard *keyboard,
key_state_store_pressed_key_as_bound(event->keycode); key_state_store_pressed_key_as_bound(event->keycode);
handle_menu_keys(server, &keyinfo.translated); handle_menu_keys(server, &keyinfo.translated);
return LAB_KEY_HANDLED_TRUE; 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)) { if (handle_cycle_view_key(server, &keyinfo)) {
key_state_store_pressed_key_as_bound(event->keycode); key_state_store_pressed_key_as_bound(event->keycode);
return LAB_KEY_HANDLED_TRUE; return LAB_KEY_HANDLED_TRUE;

View file

@ -42,7 +42,7 @@ interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo)
if (wlr_box_empty(geo)) { if (wlr_box_empty(geo)) {
return; 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.x = max_move_scale(server->grab_x, server->grab_box.x,
server->grab_box.width, geo->width); server->grab_box.width, geo->width);
server->grab_box.y = max_move_scale(server->grab_y, server->grab_box.y, server->grab_box.y = max_move_scale(server->grab_y, server->grab_box.y,

View file

@ -49,12 +49,12 @@ endif
subdir('common') subdir('common')
subdir('config') subdir('config')
subdir('cycle')
subdir('decorations') subdir('decorations')
subdir('foreign-toplevel') subdir('foreign-toplevel')
subdir('img') subdir('img')
subdir('input') subdir('input')
subdir('menu') subdir('menu')
subdir('osd')
subdir('protocols') subdir('protocols')
subdir('scaled-buffer') subdir('scaled-buffer')
subdir('ssd') subdir('ssd')

View file

@ -59,13 +59,13 @@ node_menuitem_from_node(struct wlr_scene_node *wlr_scene_node)
return (struct menuitem *)node_descriptor->data; return (struct menuitem *)node_descriptor->data;
} }
struct osd_item * struct cycle_osd_item *
node_osd_item_from_node(struct wlr_scene_node *wlr_scene_node) node_cycle_osd_item_from_node(struct wlr_scene_node *wlr_scene_node)
{ {
assert(wlr_scene_node->data); assert(wlr_scene_node->data);
struct node_descriptor *node_descriptor = wlr_scene_node->data; struct node_descriptor *node_descriptor = wlr_scene_node->data;
assert(node_descriptor->type == LAB_NODE_OSD_ITEM); assert(node_descriptor->type == LAB_NODE_CYCLE_OSD_ITEM);
return (struct osd_item *)node_descriptor->data; return (struct cycle_osd_item *)node_descriptor->data;
} }
struct ssd_button * struct ssd_button *

View file

@ -1,374 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "osd.h"
#include <assert.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#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);
}

View file

@ -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_tree[i]->node);
} }
wlr_scene_node_destroy(&output->layer_popup_tree->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); wlr_scene_node_destroy(&output->session_lock_tree->node);
if (output->workspace_osd) { if (output->workspace_osd) {
wlr_scene_node_destroy(&output->workspace_osd->node); 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_signal_add(&wlr_output->events.request_state, &output->request_state);
wl_list_init(&output->regions); 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 * 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); wlr_scene_tree_create(&server->scene->tree);
} }
output->layer_popup_tree = 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); 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_tree[3]->node, menu_node);
wlr_scene_node_place_below(&output->layer_popup_tree->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); wlr_scene_node_raise_to_top(&output->session_lock_tree->node);
/* /*

View file

@ -848,46 +848,6 @@ seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer)
seat->focused_layer = 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 void
seat_output_layout_changed(struct seat *seat) seat_output_layout_changed(struct seat *seat)
{ {

View file

@ -549,6 +549,7 @@ server_init(struct server *server)
wl_list_init(&server->views); wl_list_init(&server->views);
wl_list_init(&server->unmanaged_surfaces); wl_list_init(&server->unmanaged_surfaces);
wl_list_init(&server->cycle.views);
server->scene = wlr_scene_create(); server->scene = wlr_scene_create();
if (!server->scene) { 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 * z-order for nodes which cover the whole work-area. For per-output
* scene-trees, see handle_new_output() in src/output.c * scene-trees, see handle_new_output() in src/output.c
* *
* | Type | Scene Tree | Per Output | Example * | Type | Scene Tree | Per Output | Example
* | ----------------- | ---------------- | ---------- | ------- * | ------------------- | ---------------- | ---------- | -------
* | ext-session | lock-screen | Yes | swaylock * | ext-session | lock-screen | Yes | swaylock
* | osd | osd_tree | Yes | * | window switcher OSD | cycle_osd_tree | Yes |
* | compositor-menu | menu_tree | No | root-menu * | compositor-menu | menu_tree | No | root-menu
* | layer-shell | layer-popups | Yes | * | layer-shell | layer-popups | Yes |
* | layer-shell | overlay-layer | Yes | * | layer-shell | overlay-layer | Yes |
* | layer-shell | top-layer | Yes | waybar * | layer-shell | top-layer | Yes | waybar
* | xwayland-OR | unmanaged | No | dmenu * | xwayland-OR | unmanaged | No | dmenu
* | xdg-popups | xdg-popups | No | * | xdg-popups | xdg-popups | No |
* | toplevels windows | always-on-top | No | * | toplevels windows | always-on-top | No |
* | toplevels windows | normal | No | firefox * | toplevels windows | normal | No | firefox
* | toplevels windows | always-on-bottom | No | pcmanfm-qt --desktop * | toplevels windows | always-on-bottom | No | pcmanfm-qt --desktop
* | layer-shell | bottom-layer | Yes | waybar * | layer-shell | bottom-layer | Yes | waybar
* | layer-shell | background-layer | Yes | swaybg * | layer-shell | background-layer | Yes | swaybg
*/ */
server->view_tree_always_on_bottom = wlr_scene_tree_create(&server->scene->tree); server->view_tree_always_on_bottom = wlr_scene_tree_create(&server->scene->tree);

View file

@ -65,7 +65,6 @@ view_impl_apply_geometry(struct view *view, int w, int h)
{ {
struct wlr_box *current = &view->current; struct wlr_box *current = &view->current;
struct wlr_box *pending = &view->pending; struct wlr_box *pending = &view->pending;
struct wlr_box old = *current;
/* /*
* Anchor right edge if resizing via left edge. * 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->width = w;
current->height = h; current->height = h;
if (!wlr_box_equal(current, &old)) {
view_moved(view);
}
} }

View file

@ -15,11 +15,11 @@
#include "common/match.h" #include "common/match.h"
#include "common/mem.h" #include "common/mem.h"
#include "config/rcxml.h" #include "config/rcxml.h"
#include "cycle.h"
#include "foreign-toplevel/foreign.h" #include "foreign-toplevel/foreign.h"
#include "input/keyboard.h" #include "input/keyboard.h"
#include "labwc.h" #include "labwc.h"
#include "menu/menu.h" #include "menu/menu.h"
#include "osd.h"
#include "output.h" #include "output.h"
#include "placement.h" #include "placement.h"
#include "regions.h" #include "regions.h"
@ -35,6 +35,7 @@
#if HAVE_XWAYLAND #if HAVE_XWAYLAND
#include <wlr/xwayland.h> #include <wlr/xwayland.h>
#include "xwayland.h"
#endif #endif
struct view * struct view *
@ -345,48 +346,6 @@ view_prev(struct wl_list *head, struct view *view, enum lab_view_criteria criter
return NULL; 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 void
view_array_append(struct server *server, struct wl_array *views, view_array_append(struct server *server, struct wl_array *views,
enum lab_view_criteria criteria) enum lab_view_criteria criteria)
@ -823,7 +782,7 @@ view_minimize(struct view *view, bool minimized)
{ {
assert(view); 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"); wlr_log(WLR_ERROR, "not minimizing window while window switching");
return; return;
} }
@ -858,23 +817,7 @@ view_compute_centered_position(struct view *view, const struct wlr_box *ref,
int height = h + margin.top + margin.bottom; int height = h + margin.top + margin.bottom;
/* If reference box is NULL then center to usable area */ /* If reference box is NULL then center to usable area */
if (!ref) { box_center(width, height, ref ? ref : &usable, &usable, x, y);
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;
}
*x += margin.left; *x += margin.left;
*y += margin.top; *y += margin.top;
@ -1281,13 +1224,8 @@ view_apply_fullscreen_geometry(struct view *view)
assert(output_is_usable(view->output)); assert(output_is_usable(view->output));
struct wlr_box box = { 0 }; struct wlr_box box = { 0 };
wlr_output_effective_resolution(view->output->wlr_output, wlr_output_layout_get_box(view->server->output_layout,
&box.width, &box.height); view->output->wlr_output, &box);
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;
view_move_resize(view, box); view_move_resize(view, box);
} }
@ -1762,6 +1700,12 @@ view_set_fullscreen(struct view *view, bool fullscreen)
view_apply_special_geometry(view); view_apply_special_geometry(view);
} }
output_set_has_fullscreen_view(view->output, view->fullscreen); 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 static bool
@ -2304,6 +2248,17 @@ view_move_to_front(struct view *view)
move_to_front(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); cursor_update_focus(view->server);
desktop_update_top_layer_visibility(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; server->session_lock_manager->last_active_view = NULL;
} }
if (server->seat.pressed.view == view) {
seat_reset_pressed(&server->seat);
}
if (view->tiled_region_evacuate) { if (view->tiled_region_evacuate) {
zfree(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); undecorate(view);
view_set_icon(view, NULL, NULL); view_set_icon(view, NULL, NULL);

View file

@ -10,6 +10,7 @@
#include <wlr/types/wlr_xdg_toplevel_icon_v1.h> #include <wlr/types/wlr_xdg_toplevel_icon_v1.h>
#include "buffer.h" #include "buffer.h"
#include "common/array.h" #include "common/array.h"
#include "common/box.h"
#include "common/macros.h" #include "common/macros.h"
#include "common/mem.h" #include "common/mem.h"
#include "config/rcxml.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 */ /* TODO: reorder so this forward declaration isn't needed */
static void set_pending_configure_serial(struct view *view, uint32_t serial); 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) { if (update_required) {
view_impl_apply_geometry(view, size.width, size.height); 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 * 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.x = view->pending.x;
view->current.y = view->pending.y; view->current.y = view->pending.y;
view_moved(view);
} }
center_fullscreen_if_needed(view);
view_moved(view);
/* Re-sync pending view with current state */ /* Re-sync pending view with current state */
snap_constraints_update(view); snap_constraints_update(view);
view->pending = view->current; view->pending = view->current;
@ -400,7 +457,7 @@ handle_request_move(struct wl_listener *listener, void *data)
* want. * want.
*/ */
struct view *view = wl_container_of(listener, view, request_move); 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); 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 wlr_xdg_toplevel_resize_event *event = data;
struct view *view = wl_container_of(listener, view, request_resize); 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); 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) { } else if (view->pending_configure_serial == 0) {
view->current.x = geo.x; view->current.x = geo.x;
view->current.y = geo.y; 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); view_moved(view);
} }
} }
@ -663,6 +726,10 @@ xdg_toplevel_view_set_fullscreen(struct view *view, bool fullscreen)
if (serial > 0) { if (serial > 0) {
set_pending_configure_serial(view, serial); set_pending_configure_serial(view, serial);
} }
/* Disable background fill immediately on leaving fullscreen */
if (!fullscreen) {
disable_fullscreen_bg(view);
}
} }
static void static void
@ -785,6 +852,9 @@ handle_map(struct wl_listener *listener, void *data)
set_initial_position(view); set_initial_position(view);
} }
/* Disable background fill at map (paranoid?) */
disable_fullscreen_bg(view);
/* /*
* Set initial "current" position directly before * Set initial "current" position directly before
* calling view_moved() to reduce flicker * 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); 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 xdg_toplevel_view *xdg_toplevel_view = znew(*xdg_toplevel_view);
struct view *view = &xdg_toplevel_view->base; struct view *view = &xdg_toplevel_view->base;

View file

@ -274,6 +274,7 @@ handle_commit(struct wl_listener *listener, void *data)
*/ */
if (current->width != state->width || current->height != state->height) { if (current->width != state->width || current->height != state->height) {
view_impl_apply_geometry(view, state->width, 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. * want.
*/ */
struct view *view = wl_container_of(listener, view, request_move); 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); 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 wlr_xwayland_resize_event *event = data;
struct view *view = wl_container_of(listener, view, request_resize); 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); 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 = struct server *server =
wl_container_of(listener, server, xwayland_new_surface); wl_container_of(listener, server, xwayland_new_surface);
struct wlr_xwayland_surface *xsurface = data; struct wlr_xwayland_surface *xsurface = data;
wlr_xwayland_surface_ping(xsurface);
/* /*
* We do not create 'views' for xwayland override_redirect surfaces, * 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); 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));
}