desktop: don't use LAB_NODE_LAYER_SUBSURFACE node type
Instead, set ctx.type = LAB_NODE_LAYER_SURFACE for both layer-surfaces
and layer-subsurfaces.
This patch preserves the existing behaviors:
- Pressing a subsurface of an on-demand layer-surface gives pointer
focus to the subsurface, but gives keyboard focus to the parent
layer-surface (related: a5fcbfaf).
- Pressing a subsurface of a layer-surface doesn't close a popup
(related: a89bcc3c).
Attaching LAB_NODE_NONE node-descriptor to ssd->tree looks strange, this
patch uses new LAB_NODE_SSD_ROOT instead. The node-descriptor attached to
ssd->tree is needed for get_cursor_context() to detect cursor hovering on
borders/extents.
I also updated get_cursor_context() to make my intent clearer.
desktop.c: don't print errors when cursor is on resize-indicator
In f347a81, I thought there are only window contents and SSD under
`view->scene_tree` and forgot about the resize-indicator.
I also refactored the logic around it:
- Remove `ret.node->type == WLR_SCENE_NODE_BUFFER` check since it's
already done by `lab_wlr_surface_from_node()`
- Eliminate duplicated call to `lab_wlr_surface_from_node()`
John Lindgren [Wed, 3 Sep 2025 09:32:44 +0000 (05:32 -0400)]
ssd: unify struct ssd_part with struct node_descriptor
struct ssd_part and struct node_descriptor seem to have essentially the
same purpose: tag a wlr_scene_node with some extra data indicating what
we're using it for.
Also, as with enum ssd_part_type (now lab_node_type), ssd_part is used
for several types of nodes that are not part of SSD.
So instead of the current chaining (node_descriptor -> ssd_part), let's
flatten/unify the two structs.
In detail:
- First, merge node_descriptor_type into lab_node_type.
- Add a separate view pointer in node_descriptor, since in the case of
SSD buttons we need separate view and button data pointers.
- Rename ssd_part_button to simply ssd_button. It no longer contains
an ssd_part as base.
- Add node_try_ssd_button_from_node() which replaces
node_ssd_part_from_node() + button_try_from_ssd_part().
- Factor out ssd_button_free() to be called in node descriptor destroy.
- Finally, get_cursor_context() needs a little reorganization to handle
the unified structs.
Overall, this simplifies the code a bit, and in my opinion makes it
easier to understand. No functional change intended.
Fixes up f347a81. This mistake didn't cause misbehaviors because the
ssd_part bound to ssd->tree is just a placeholder to let
get_cursor_context() call ssd_get_resizing_type() when the cursor is on
border/extents.
- Rename `scaled_scene_buffer` to `scaled_buffer`. This makes it clear
that `scaled_{font,img,icon}_buffers` are implementations of it.
- Move the files from `src/common` to `src/scaled-buffer` as
`scaled_icon_buffer` heavily depends on `server` and `view` etc.
John Lindgren [Mon, 1 Sep 2025 16:07:52 +0000 (12:07 -0400)]
interactive: un-maximize only axes that are being resized
When resizing in only one axis (horizontal/vertical), there's no reason
to un-maximize the other axis.
Supporting logic was landed in the previous commit and in ebd39dfe0d1d
(which fixed the client-initiated resize case), so all that remains is
to make a small change in interactive_begin().
John Lindgren [Mon, 1 Sep 2025 15:49:24 +0000 (11:49 -0400)]
view: expose view_set_maximized() instead of view_restore_to()
view_restore_to() (which is just set_maximized() + view_move_resize())
hasn't aged well and doesn't line up with typical usage anymore:
- it's missing view_set_untiled(), which has to be called separately
- it always forces view_move_resize() even when that's not needed
- it doesn't allow un-maximizing only one axis (see next commit)
- the fullscreen check is unnecessary (already checked in callers)
Eliminate it and just expose view_set_maximized() instead.
view: restore initially-maximized window position after unplug/plug
`update_last_layout_geometry()` stores `view->natural_geometry` in
`view->last_layout_geometry`, but it's empty for initially-maximized
windows, so their positions were not restored after outputs are
unplugged and plugged (also when VT switching in wlroots 0.19.0).
This commit sets the fallback natural geometry (at the center of the
output) so that initially-maximized windows reappears in the same output.
tokyo4j [Sat, 30 Aug 2025 19:41:08 +0000 (04:41 +0900)]
overlay: refactor
- Use a single `lab_scene_rect` for both overlay background and outlines,
like I described in the TODO comment in ffd4005.
- Simplify the resource management by destroying the overlay tree when
it's hidden. I think its overhead is pretty minimal.
- Share a single `lab_scene_rect` for both region/edge overlays.
tokyo4j [Sat, 30 Aug 2025 05:13:59 +0000 (14:13 +0900)]
scaled-icon-buffer: fix large client-side icon not being loaded
This commit fixes that client-side icons were not loaded when the rendered
icon size is larger than icon sizes from the client. This bug has become
more likely to happen due to the new thumnail-style window switcher.
The cause was `abs(INT_MIN)` becomes `INT_MIN` due to integer overflow.
tokyo4j [Wed, 13 Aug 2025 12:00:11 +0000 (21:00 +0900)]
ssd: clean up scene management
Our codebase for ssd scenes has grown with a lot of technical debts:
- We needed to call `ssd_get_part()` everywhere to get the scene node of a
ssd part. We then needed to cast it to `wlr_scene_rect` and
`wlr_scene_buffer`. This bloated our codebase and even blocked
duplicated button types in `<titlebar><layout>`.
- `ssd_get_part_type()` was a dirty hack. It compared parent, grandparent
and grandgrandparent of a node with each subtree in the ssd to get the
part type of the node.
To resolve this issues, this commit changes how ssd scenes are managed:
- Access scene rects and scene buffers just as a member of `struct ssd`.
- `ssd_part` is now a attachment to a scene node that can be accessed via
node_descriptor->data, with a new node-descriptor type
`LAB_NODE_DESC_SSD_PART`. `LAB_NODE_DESC_SSD_BUTTON` is unified into it.
Now the scene graph under ssd->tree looks like below. The parentheses
indicate the type of ssd_part attached to the node:
When hovering on SSD, `get_cursor_context()` traverses this scene node
from the leaf. If it finds a `ssd_part` attached to the node, it returns
`ssd_part_type` that represents the resizing direction, button types or
`Title`/`Titlebar`.
John Lindgren [Wed, 27 Aug 2025 17:04:52 +0000 (13:04 -0400)]
seat: improve debug logging when configuring input devices
I needed to debug an input configuration issue and found the debug
output not-super-helpful, so I made some improvements:
- Print the name and "sysname" (e.g. event11) of the device being
configured. Note that the name alone isn't enough since there can
be multiple identically-named devices.
- Print the config category matched for each device.
- Print the config values (if any) being applied. For enums, only the
numeric value is printed since I'm lazy.
- Don't print "pointer acceleration configured" if neither a pointer
speed nor acceleration profile is configured (it's confusing).
John Lindgren [Wed, 27 Aug 2025 03:48:05 +0000 (23:48 -0400)]
tree-wide: use enum types/constants where appropriate
- add LAB_WINDOW_TYPE_INVALID in place of literal -1
- document more clearly that enum lab_view_criteria is a bitset
- other one-off replacements of integer values/types for consistency
Note: variables of type enum lab_view_criteria are already used
extensively throughout the code to contain combinations of the declared
enum values. I am not introducing any new usage here, just changing the
single uint32_t to be consistent with all the other usages.
John Lindgren [Wed, 27 Aug 2025 00:27:34 +0000 (20:27 -0400)]
common: flesh out enum lab_edge and prefer over wlr_edges/wlr_direction
I like the new common/edge.h. I don't like how inconsistently we use it.
Current situation:
- enum wlr_edges and wlr_direction are designed to be used as bitset,
and are defined compatibly
- enum lab_edge is *also* designed to be used as bitset, but
incompatible with the others (LEFT/RIGHT come before UP/DOWN)
- we use an inconsistent mix of all three *AND* uint32_t (usually with
the WLR_EDGE constants rather than the LAB_EDGE constants), and
convert between them on an ad-hoc basis, sometimes implicitly
Let's clean this up:
- reorder enum lab_edge to be compatible with the two wlr enums
(check this by static_assert)
- use TOP/BOTTOM naming rather than UP/DOWN (matches wlr_edges)
- add constants for the remaining possible combinations of the 4 edges
- use lab_edge for all internal edge/direction fields, consistently
- add lab_edge_is_cardinal() as a sanity check before casting to
enum wlr_direction, and then eliminate all of direction.c/h
Instead of "enum wlr_edges direction", we now have
"enum lab_edge direction" which is not that much better. At least we
are now clear that we're overloading one enum with two meanings.
tokyo4j [Mon, 25 Aug 2025 09:54:22 +0000 (18:54 +0900)]
window-rules: fix window rules not being applied
In 943f5751, I initialized heap-allocated `view_query` used for
`If` actions with `decoration=LAB_SSD_MODE_INVALID`, but I forgot to do
that for stack-allocated `view_query` used for window rules.
John Lindgren [Sat, 23 Aug 2025 14:44:26 +0000 (10:44 -0400)]
view: respect client-initiated resize of non-maximized axis
When implementing single-axis maximize some time ago, I made the
simplifying assumption that a view couldn't be resized while maximized
(even in only one axis). And indeed for compositor-initiated resize,
we always unmaximize the view first.
However, I didn't account for the client resizing the non-maximized
axis, which we can't (and shouldn't) prevent. When this happens, we
should also update the natural geometry of that single axis so that we
don't undo the resize when un-maximizing.
P.S. xdg-shell clients resizing the *maximized* axis is still an
unsolved problem, exacerbated by the fact that xdg-shell protocol
doesn't allow clients to even know about single-axis maximize.
P.P.S. the view_invalidate_last_layout_geometry() logic may need
similar updates, I'm not sure.
John Lindgren [Sat, 16 Aug 2025 17:17:36 +0000 (13:17 -0400)]
keybind: refactor update_keycodes_iter() to reduce nesting
update_keycodes_iter() currently has 4(!) levels of nested loops, which
makes the logic (especially the break/continue statements) difficult to
understand. The logic also appears to continue looping uselessly after
a given keycode has already been added to a keybind.
John Lindgren [Fri, 15 Aug 2025 05:29:15 +0000 (01:29 -0400)]
menu: fix use-after-free at exit with sub-menu selected
Sequence of events:
- menu_finish() frees the sub-menu first
- the selection.menu of the parent menu is now dangling
- menu_finish() frees the parent menu
- menu_free() calls menu_close_root() on the parent menu
- menu_close_root() tries to close the (freed) sub-menu
- boom
Extending nullify_item_pointing_to_this_menu() avoids the crash.
- Some functional changes including:
- Disable exclusive-zone by default (Written-by: @Consolatis) and add
command line option -x|--exclusive-zone
- Add close timeout (Written-by: @Consolatis) and -t|--timeout option
- Use index of button (from right-to-left) for exit code
- Disable reading from config file and remove associated --type option
- Refactoring including:
- Use wlr_log() instead of the log.{c,h} functions
- Use wl_list instead of sway's list.c implementation
- In the pango wrapper functions, use glib's g_strdup_vprintf() rather
than the original stringop.c functions
- Align with labwc coding style to pass checkpatch.pl
- Re-licenced from MIT to GPL-2.0, and add Copyright notices for original
authors
v2
- Remove option -s|--dismiss-button and the default "X" button. To get
such a button, "-Z X :"
- Remove options -b and -z because there is no requirement to run
in a terminal.
- Remove *-no-terminal from options --button and --button-dismiss because
commands are now always run directly without a terminal.
v3
- Allow -B/-Z options without action-argument
- Invert button order of -B/-Z so that `labnag -m foo -Z x -Z y -Z z`
results in three buttons with "x" furthest to the left, and "z" on the
right (rather than the other way around).
- Use signalfd() to prevent race conditions on SIGTERM
v4
- Limit number of stdin lines to 200 to avoid hogging CPU
When IME (e.g. fcitx5) is killed, relay->input_method is destroyed and
then relay->input_method->keyboard_grab is destroyed, which causes null
pointer dereference and crashes labwc.
Possible solutions are:
- Let wlroots keep emitting keyboard grab as `data` from keyboard grab's
destroy handler just like before
- Let wlroots destroy keyboard grab before input method
- Let compositor store keyboard grab as relay->keyboard_grab
But let's just revert the change in e530f43 for now.
John Lindgren [Thu, 7 Aug 2025 19:27:12 +0000 (15:27 -0400)]
foreign-toplevel: disconnect internal signals from handle_handle_destroy()
If the handle gets destroyed from the wlroots side before the view
is destroyed, the internal signals (emitted from the view) are not
disconnected and will assert() if invoked.
John Lindgren [Tue, 5 Aug 2025 00:28:25 +0000 (20:28 -0400)]
output: make autoEnableOutputs=no apply only to drm outputs
It is not really useful for other output backends and just results
in no outputs being enabled at all. (This is mainly an annoyance for
developers normally running with drm but occasionally nested.)
tokyo4j [Mon, 4 Aug 2025 03:55:13 +0000 (12:55 +0900)]
menu: refactor parser
...with the same approach as rcxml.c
- `If` actions now works for menus
- `name` argument no longer have to be the first argument of <action>
- `label` argument no longer have to be the first argument of <item>
tokyo4j [Sat, 2 Aug 2025 12:35:51 +0000 (21:35 +0900)]
interactive: allow snapping to corner edges
In addition to <snapping><range>, <snapping><cornerRange> configures the
distance from the screen corner to trigger quater window snapping.
Also, new values "up-left", "up-right", "down-left" and "down-right" are
allowed for <action name="(Toggle)SnapToEdge" direction="[value]"> and
<query tiled="[value]">.