Add full handle/grip assembly to the bottom of SSD window frames,
following the Openbox themerc specification for geometry and theming.
Theme parsing:
- Parse window.handle.width (handle bar height, default 6)
- Parse window.grip.width (corner grip width, default 20)
- Parse window.[active|inactive].handle.bg with Solid/Gradient support
- Parse window.[active|inactive].grip.bg (inherits from handle if unset)
- Pre-render 1px-wide fill buffers and cairo patterns for handle/grip
Scene graph (new ssd-handle.c):
- Handle assembly replaces bottom border when active, with its own
left/right/top borders and three-segment bottom border
- Grips at left/right corners for diagonal resize (sw/se-resize)
- Center handle for vertical resize (s-resize)
- Vertical separator lines between grips and handle using border color
- Per Openbox spec, handle_width is content-only height with borders
drawn around it (total assembly height = 2*border_width + handle_width)
Interactive visual states (grips only):
- Hover: 20% black overlay on grip content area
- Pressed: 40% black overlay with 1px inset shadow (dark top/left,
light bottom/right) for a pushed-in 3D effect
- Dragging: 20% overlay with inset shadow maintained
- Global hover tracking (server.hovered_handle_ssd/element) ensures
proper cleanup when cursor moves across views or to desktop
Decoration toggle cycle (ToggleDecorations action):
- New LAB_SSD_MODE_BORDER_HANDLE between BORDER and FULL
- keepBorder=true: full -> border+handle -> border -> none -> full
- keepBorder=false: full -> none -> full (unchanged)
Node types and input:
- New LAB_NODE_HANDLE, LAB_NODE_GRIP_LEFT, LAB_NODE_GRIP_RIGHT
- Integrated into LAB_NODE_BORDER/BORDER_BOTTOM containment so
existing Border context mousebinds (Resize) work automatically
- Handle/grip descriptors resolved directly in get_cursor_context()
bypassing ssd_get_resizing_type() for precise cursor shapes
Visibility rules:
- Hidden when maximized, shaded, or handle_width is 0
- Hidden in LAB_SSD_MODE_BORDER and LAB_SSD_MODE_NONE states
- Bottom border in ssd-border.c disabled when handle is active
Documentation:
- labwc-theme.5.scd: document all handle/grip theme properties
- labwc-actions.5.scd: update ToggleDecorations to 4-state cycle
- docs/themerc: add handle/grip default values
When -t or --window-title is supplied, its required argument is treated as a
format string where `%o` is replaced by the `wlr-output`'s `name` when we set
the window title.
This uses glib to split and join the format string because our own
`string-helpers` library only has `str_join`. Otherwise using `string-helpers`
would have been preferred.
v2: Switch XRGB to XBGR
v3: Rewrite HDR mode checking and setting
v4: Restructure HDR support detection
v5: Fix code style
v6: Fix old style function declaration
v7: This function should be declared static
v8: This helper function can accept a const struct on input
v9: Rebase now that 0.20 is merged
v10: Rewrite with multiple color format attempts
v11: Add in the parts that accidentally got left in my
original color-management-v1 patch
v12: Add missing function prototype
v13: Apply suggested changes
v14: Changed HDR application setup in new output
v15: Rewrite configure_new_output to use lab_wlr_scene_output_commit
v16: Fixed application of HDR on external mode or output config change
v17: Fixed it for real this time instead of crashing
v18: Moved the effective resolution collection, plus one style change.
Menu accelerators are one-letter mnemonics to quickly select/exec
items from the current menu. For each menu item, the accelerator is
defined as the first character of the item label, converted to
lowercase. A different accelerator can be explicitly defined in
menu.xml with the special '_' character before the target letter.
- Add a field `accelerator` to the `menuitem` struct
- Implement `menu_item_select_by_accelerator()`
Example:
The accelerator for an item with the label "e_macs" is 'm'.
This function behaves identically to `scaled_font_buffer_update()`
but allows setting the text as pango markup, supporting further
customization like underscores.
This fixes a warning in gcc16 below:
../src/img/img-xpm.c: In function ‘xpm_load_to_surface’:
../src/img/img-xpm.c:354:33: warning: variable ‘xcnt’ set but not used [-Wunused-but-set-variable=]
354 | for (int n = 0, xcnt = 0; n < wbytes; n += cpp, xcnt++) {
| ^~~~
dbus-update-activation-environment and systemctl --user import-environment
were fired asynchronously via spawn_async_no_shell, racing with the autostart
script. Any systemd user service started from autostart (e.g. via
labwc-session.target) could start before the import completed, leaving
WAYLAND_DISPLAY and related variables absent from its environment and those
of any apps it launches.
Run both commands synchronously via a new spawn_sync_no_shell helper so
the import is guaranteed to complete before the autostart script executes.
Each scroll button draws its own full border, so placing the up and down
buttons flush produced a divider twice the intended thickness. Shift
button_down up by one border width and extend its height accordingly so
its top border overlaps button_up's bottom border.
Also drop a stray \`+ border\` offset from the scroll button text Y-position
that was pushing the label below the button's center.
Previously the scroll button group height was shorter than intended as it was
calculated using details.height instead of the border dimensions. Calculate
button heights using border_rect_height to properly fill the bordered region.
Use details-specific border thickness and color config options instead of regular button options. Adjust padding for a more compact look on details scroll buttons.
The raise_on_focus_delay is meant to dampen z-order churn from
focus-follows-mouse cursor passes. Applying it to every focus
change meant explicit actions (alt-tab cycle finish, Focus action,
xdg/xwayland activation, view map, etc.) also waited for the delay
before raising, which felt laggy.
Route all non-sloppy-focus callers through an immediate raise and
keep the timer-based raise only for desktop_focus_view_or_surface(),
which is the sloppy-focus entry point.
If the user disables raiseOnFocus or lowers raiseOnFocusDelay while
a raise is queued, the queued raise should not fire against the new
config. Cancel it in reload_config_and_theme() before rereading the
rc.xml.
The pending_auto_raise_view pointer would become dangling if the
view it references is destroyed before the timer fires. Clear it
in view_destroy() alongside the existing active_view cleanup.
When raiseOnFocus is enabled and raiseOnFocusDelay is > 0, defer
view_move_to_front() until a wl_event_loop timer elapses instead of
raising immediately. The pending view/timer pair lives on struct
server so that at most one raise can be pending per compositor.
Semantics:
delay_ms == 0 : immediate raise (preserves prior behavior)
delay_ms > 0, hover A : schedule raise of A after delay_ms
another hover before : timer is reset and the new view becomes
expiry the pending one (brief flyby hovers do
not stack up raises)
focus change, raise=false: pending raise is cancelled
This is most useful with followMouse to avoid brief passes of the
cursor stacking up z-order changes.
Add two fields to struct server:
struct view *pending_auto_raise_view;
struct wl_event_source *pending_auto_raise_timer;
and forward-declare desktop_cancel_pending_auto_raise() in labwc.h.
The state is a single 'slot' (at most one view/timer pending) since
a new focus change supersedes any previous pending raise.
This commit just reserves the state and the public API; the
behaviour is implemented in the following commit.
Add a new <focus><raiseOnFocusDelay> element accepting an integer in
milliseconds. The default of 0 preserves the current behavior (raise
immediately when raiseOnFocus is enabled).
The new field is carried as uint32_t raise_on_focus_delay_ms on
struct rcxml. This commit only adds the parser and default; the
actual delay logic follows in a subsequent commit.
Let distributors opt out of installing labwc-session.target at configure
time. Default is 'auto' (install if the systemd pkg-config file is
present), 'enabled' forces it on, 'disabled' skips it entirely.
Describe the shipped labwc-session.target in labwc(1) SESSION MANAGEMENT
and add commented-out systemctl start/stop lines to the example
autostart and shutdown files. Users on systemd-based distros can
uncomment these to pull in graphical-session.target when labwc starts
and tear it down cleanly on exit, without labwc itself mandating any
specific init system.
Add a small systemd user target modelled on miracle-wm-session.target.
It binds to graphical-session.target and orders after
graphical-session-pre.target, so systemd user services declaring
WantedBy=graphical-session.target (panels, portals, notification
daemons, ...) start and stop in sync with a labwc session.
Installed into $systemduserunitdir when the systemd dependency is
available at configure time; on systems without systemd the install
is skipped and labwc's runtime activation of the target fails
gracefully.
Modifier+right-drag resize was silently ignored on fully maximized views
because of an early-return guard in interactive_begin(). The axis-specific
un-maximization logic introduced in #3043 already handles partial
maximization correctly; extend that to the VIEW_AXIS_BOTH case so both
axes are cleared while keeping the current geometry as the starting point
of the resize.
Move already permits dragging maximized views, so this also removes an
asymmetry between the move and resize paths.
Fixes: #3524