Compare commits

...

146 commits

Author SHA1 Message Date
Johan Malm
529fc382da build: bump version to 0.20.1 2026-06-15 19:43:52 +01:00
Johan Malm
4dab6994dc NEWS.md: add notes for 0.20.1 2026-06-15 19:32:59 +01:00
Nikolas Nyby
07e1053a8d readme: Use meson setup in example to fix meson warning
Using meson without an explicit command emits a warning.
2026-06-13 07:20:13 +01:00
Johan Malm
2135fb0d35 [fixup] minor typos 2026-06-12 21:26:14 +01:00
Johan Malm
bbfa6bbddb CONTRIBUTING.md: add section on LLM/AI Tools 2026-06-12 21:26:14 +01:00
Johan Malm
a814dd1e38 NEWS.md: add notes for 0.9.8 2026-06-11 22:23:14 +02:00
Consolatis
28d03b0893 src/buffer.c: prevent creating invalid buffers
Testcase: set title of foot to an invisible string (here LTR)

First one works and correctly sets the title, 2nd one crashes
- printf "\x1b]0;\xe2\x80\x8e%s\x07" "some title"
- printf "\x1b]0;\xe2\x80\x8e%s\x07" ""

Reported-by: Domo via IRC
2026-06-11 21:53:28 +02:00
Johan Malm
f4b9bdab65 NEWS.md: add notes on wlroots-0.20 2026-06-08 23:19:27 +02:00
Consolatis
78227f1724 CI: Debian Testing temporary labwc removal due to wlroots transition 2026-06-08 20:28:51 +02:00
Johan Malm
579e532908 xdg-shell: protect against SIGABRT when TTY switching
Ensure we have got sensible toplevel width/height before trying to set
size on configure.

This protects against an edge case experienced when TTY switching between
labwc and Xfce running on XOrg on another TTY. It is not yet clear what
causes this, but hitting wlroots assert() is not great, so let's protect
against it.

Written-by: @Tamaranch

Fixes: #3608
2026-06-04 20:05:06 +01:00
Johan Malm
d5b5b765c7 build: bump version to 0.20.0
Some checks failed
labwc.github.io / notify (push) Has been cancelled
2026-05-25 20:21:25 +01:00
Johan Malm
37434e3ea5 NEWS.md: minor fix s/labwc/wlroots/ 2026-05-25 20:17:11 +01:00
Johan Malm
52487547ca NEWS.md: update notes for 0.20.0 2026-05-25 20:07:41 +01:00
Johan Malm
7e74b82874 NEWS.md: move wlroots-0.19 notes
...to the 0.9.x section
2026-05-25 20:07:41 +01:00
Johan Malm
151acae28a Add default keybind super-d for ToggleShowDesktop 2026-05-25 20:22:59 +02:00
Consolatis
bce14a5ad7 toplevel-capture: partial initial implementation
Missing:
- xwayland child windows
- xwayland unmanaged windows, e.g. popups / menus / ...
- xdg child window positioning
- xdg subsurfaces (test-case: mate-terminal settings listboxes)
- xdg popup positioning
2026-05-20 17:49:06 +01:00
Weblate
d5eb227c77 Translation updates from weblate
Some checks failed
labwc.github.io / notify (push) Has been cancelled
Co-authored-by: Demian <Demian@gmx.co.uk>
Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/de/
Translation: Labwc/labwc
2026-05-18 21:19:21 +01:00
Tomi Ollila
80ff89c640 Typo/style fixes in docs and code comments
Codespell(1)-pointed typos in files listed by `git ls-files`
(sans checkpatch.pl and possibly some others). Removed some
extra spaces. Added a few missing trailing periods.
`Default is ...`.  Added spaces in ' />' where missing
(sans e.g. wayland protocol and t/* files).
Fit some lines in docs/*.scd to 80 colums.

Used git grep commands (to find similar cases):
$ git grep -n '\S/>'
$ git grep -nF '.  '
$ git grep -n '[^\t*'\'',{#]\t'
$ git grep -ni 'default '

No functional change. No change in *.[ch] line numbers.
2026-05-18 19:55:05 +01:00
massi
4af693a7fd window-title: cleaner string replace
Some checks failed
labwc.github.io / notify (push) Has been cancelled
2026-05-16 14:36:51 +01:00
massi
cd7be59d5f window-title: rewording, renaming, lint fixes 2026-05-16 14:36:51 +01:00
massi
0a9bd41df0 window-title: add documentation 2026-05-16 14:36:51 +01:00
massi
df6dde131f window-title: implement window-title command line argument
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.
2026-05-16 14:36:51 +01:00
massi
7a53f294a8 output.c: use env var LABWC_TITLE to set nested labwc window title
Introduces an environment variable LABWC_TITLE, which, when set, will
be used to set the title of a nested instance of labwc.
2026-05-16 14:36:51 +01:00
Tomi Ollila
8473ea4b72 main: update --version string 2026-05-13 14:47:08 +02:00
stormshadow
4b61a8a879 fix(labnag): add missing --exclusive-zone to getopt options 2026-05-11 21:03:15 +01:00
Johan Malm
bc287686ea NEWS.md: interim update 2026-05-11 21:01:55 +01:00
Standreas
477b3b1ddb Fix typo in labwc-config.5.scd
Some checks failed
labwc.github.io / notify (push) Has been cancelled
2026-05-09 13:09:29 +01:00
Consolatis
e1fdbb0611 CI: also run CI on docs changes 2026-05-08 21:02:10 +02:00
Consolatis
90b87cb882 docs/labwc-config: fix HDR comment syntax error
Fixes 'Expected ``` and a newline to begin literal block'
2026-05-08 21:02:10 +02:00
Alex Chernika
e2f3c03287
src/menu: slight clean up of menu accelerators
Slight clean up of the accelerator implementation, including use of glib for utf8-parsing
2026-05-08 20:35:40 +02:00
Consolatis
d86b2cfd0d docs: mention Vulkan requirement for HDR config 2026-05-08 19:21:51 +01:00
Christopher Snowhill
38f80d7167 Document the new HDR option
Some checks failed
labwc.github.io / notify (push) Has been cancelled
v2: Remove note which is no longer applicable.
2026-05-07 15:21:34 +02:00
Christopher Snowhill
c1b11c7821 chase wlroots: Add support for HDR10 output
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.
2026-05-07 15:21:34 +02:00
Consolatis
2189b2be1e main: include wlroots version in --version string 2026-05-07 06:24:30 +01:00
Consolatis
2480a23b19 common/macros: change WLR_VERSION macro to runtime evaluation 2026-05-07 06:24:30 +01:00
Johan Malm
0f5e4f8dd0 cycle.c: put common code in get_cycle_app_id 2026-05-07 04:42:11 +02:00
Johan Malm
949e9ffe42 cycle.c: put common code in get_view_criteria() 2026-05-07 04:42:11 +02:00
Tomi Ollila
ff2f243eb1 NEWS.md: typo and style fixes with help of perl(1) and codespell(1)
Some checks failed
labwc.github.io / notify (push) Has been cancelled
$ perl -pi.bak -e 's,\S\K/>, />,' NEWS.md
$ codespell [-i3] -L DoubleClick,inter-operability NEWS.md
2026-05-05 20:43:12 +01:00
Johan Malm
f42e1895d4 buf.c: fix -fanalyze warning
../src/common/buf.c:61:28: error: write to string literal [-Werror=analyzer-write-to-string-literal]
   61 |                         *p = '\0';
      |                         ~~~^~~~~~
2026-05-05 20:31:47 +02:00
Alex Chernika
3632c6703a menu: implement menu accelerators
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'.
2026-05-05 19:16:59 +01:00
Alex Chernika
07a0a4e59b scaled-buffer: introduce scaled_font_buffer_update_markup()
This function behaves identically to `scaled_font_buffer_update()`
but allows setting the text as pango markup, supporting further
customization like underscores.
2026-05-05 19:16:59 +01:00
Weblate
0caefa6a9e Translation updates from weblate
Co-authored-by: Tobias Si <dragoslav123@protonmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/sr_Latn/
Translation: Labwc/labwc
2026-05-05 19:10:24 +01:00
tokyo4j
0ff9af4ae0 src/img/img-xpm.c: fix unused variable warning
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++) {
      |                                 ^~~~
2026-05-04 06:21:58 +01:00
elviosak
6ce25978e7 Add Next/PreviousWindowImmediate to switch windows without OSD
Some checks failed
labwc.github.io / notify (push) Has been cancelled
Co-authored-by: @johanmalm
2026-05-02 13:37:34 +01:00
elviosak
bc7498dc6f fix chromium popup on maximized window 2026-05-01 19:49:40 +01:00
Johan Malm
df9537c809 NEWS.md: interim update 2026-05-01 16:58:56 +01:00
Jos Dehaes
def6ff22d3 session: drop goto label from spawn_sync_no_shell
Replace goto out with break since g_strfreev follows the switch directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 19:45:01 +01:00
Jos Dehaes
998fd80737 session: run activation env update synchronously
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.
2026-04-29 19:45:01 +01:00
stormshadow
7ec7a322d2 labnag: remove doubled border between scroll buttons and adjust text position
Some checks failed
labwc.github.io / notify (push) Has been cancelled
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.
2026-04-28 20:47:21 +01:00
stormshadow
ccdef5e854 labnag: fix details scroll button group height calculation
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.
2026-04-28 20:47:21 +01:00
stormshadow
e209de3eb1 labnag: separate details scroll button styling from regular buttons
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.
2026-04-28 20:47:21 +01:00
stormshadow
91d89f71ce labnag: add details border color and margin options
Adds --details-border-color and --details-margin command line options to configure the border color and margin of the details pane.
2026-04-28 20:47:21 +01:00
stormshadow
de3870246a labnag: remove +1 offset from button Y-position calculation
It essentially removes an awkward top margin from the button on the main labnag bar.
2026-04-28 20:47:21 +01:00
Jos Dehaes
e61d58e54d desktop: only apply raise-on-focus delay to sloppy-focus
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.
2026-04-28 20:33:09 +01:00
Jos Dehaes
6237e26a1d docs: document raiseOnFocusDelay
Add the new option to labwc-config(5) and the example rc.xml.all.
2026-04-28 20:33:09 +01:00
Jos Dehaes
9661ed4285 server: cancel pending auto-raise on config reload
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.
2026-04-28 20:33:09 +01:00
Jos Dehaes
ecc5565686 view: cancel pending auto-raise on view destroy
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.
2026-04-28 20:33:09 +01:00
Jos Dehaes
28908adfbe desktop: implement delayed auto-raise timer logic
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.
2026-04-28 20:33:09 +01:00
Jos Dehaes
fe6ea66b82 server: add pending auto-raise state + decl
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.
2026-04-28 20:33:09 +01:00
Jos Dehaes
7b3f37725f rcxml: add raiseOnFocusDelay option
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.
2026-04-28 20:33:09 +01:00
Jos Dehaes
70e3173f99 build: add systemd-session feature option
Some checks are pending
labwc.github.io / notify (push) Waiting to run
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.
2026-04-27 20:57:36 +01:00
Jos Dehaes
af277b09ed docs: document labwc-session.target integration
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.
2026-04-27 20:57:36 +01:00
Jos Dehaes
d8119cb354 build: ship labwc-session.target systemd user unit
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.
2026-04-27 20:57:36 +01:00
diniamo
5c7bfe3c67 Add onbutton scrollMethod, scrollButton 2026-04-27 20:55:55 +01:00
tokyo4j
7d264c907f src/cycle/cycle.c: fix typo 2026-04-26 19:07:23 +09:00
elviosak
20540d76f9 fix docs typo
Some checks failed
labwc.github.io / notify (push) Has been cancelled
2026-04-24 21:27:46 +01:00
Thorbjørn Lindeijer
5f668a82ee interactive: allow resize on fully maximized views
Some checks failed
labwc.github.io / notify (push) Has been cancelled
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
2026-04-19 13:37:13 +01:00
daniel
5c582bda09 config: migrate from amixer to pactl 2026-04-19 13:29:37 +01:00
Johan Malm
6df6a092ba NEWS.md: update notes for 0.9.7 2026-04-17 19:38:55 +01:00
Yannis Drougas
c5ea41e876 Add overrideInhibition to keyboard.keybind
Some checks failed
labwc.github.io / notify (push) Has been cancelled
2026-04-14 22:33:49 +01:00
yuiiio
4c7a9960a4 osd-thumbnail: fix failed to get client texture
Use wlr_client_buffer->texture directly instead of wlr_texture_from_buffer.
All buffers in content_tree are wlr_client_buffer.

wlr_texture_from_buffer calls client_buffer_begin_data_ptr_access which
fails when client_buffer->source == NULL (client released buffer early,
e.g. wl_shm foot-terminal).
2026-04-15 00:26:10 +09:00
Johan Malm
da37e97a45 Add action ToggleShowDesktop
Some checks are pending
labwc.github.io / notify (push) Waiting to run
2026-04-13 21:59:09 +01:00
Tobias Bengfort
8b32422b93 rcxml: allow to restrict privileged interfaces
Some checks failed
labwc.github.io / notify (push) Has been cancelled
2026-04-11 16:34:11 +02:00
Johan Malm
c8bb3381a4 Add key state OSD for debugging
Some checks are pending
labwc.github.io / notify (push) Waiting to run
Add action `DebugToggleKeyStateIndicator` to show in the top-left corner
the pressed, bound, pressed-sent keys as well as modifiers.

This should help fault find issues like #2771 and #3238

Based-on: #3262
Co-authored-by: @tokyo4j
2026-04-10 21:19:11 +01:00
Weblate
c4fd916fd3 Translation updates from weblate
Co-authored-by: ButterflyOfFire <butterflyoffire@protonmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/kab/
Translation: Labwc/labwc
2026-04-10 20:45:51 +01:00
Manuel Barrio Linares
3ec20044b5 server: add support for color-management-v1 and color-representation-manager-v1 2026-04-10 12:59:44 +02:00
daniel
afaed4af63
config: remove the underline from the XF86 keys
to match the actual key names in xkbcommon-keysyms.h
2026-04-08 10:37:35 +02:00
John Lindgren
90c38e2681 xdg: set initial position a hair earlier to fix window rules
Partially reverts: 3f223fe5b0
("xdg: unify initial positioning logic")
2026-04-07 20:05:00 +02:00
Jens Peters
a893b1ab50 input: fix tablet tool tilt motion
Tilt axis are not relative.
2026-04-06 22:10:10 +01:00
Consolatis
c9b4da2ce2 osd-thumbnail: handle buffer allocation failure
Fixes: #3489
2026-04-02 22:10:30 +02:00
tranzystorekk
e45548077e CI: Void: use wlroots0.20-devel distro package 2026-04-01 17:37:26 +02:00
Consolatis
7be58fbaba CI: add no-backends build test
Some checks failed
labwc.github.io / notify (push) Has been cancelled
2026-03-31 22:58:49 +02:00
Consolatis
3df8fcda3a meson.build: make libinput and libdrm depend on wlroots feature flags 2026-03-31 22:58:49 +02:00
Consolatis
8c6faa010f make wlroots libinput backend optional 2026-03-31 22:58:49 +02:00
Consolatis
dfbea3e156 make wlroots session backend optional 2026-03-31 22:58:49 +02:00
Consolatis
046d055ac0 make wlroots DRM backend optional 2026-03-31 22:58:49 +02:00
Johan Malm
fe1955fb27 Stop generating wayland-protocols server headers
We still need to generate wlr-protocols server headers, as well as
client headers and code.

References:
- https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5075
- https://github.com/swaywm/sway/pull/8838
2026-03-31 20:51:49 +02:00
Johan Malm
b3f3fc9084 NEWS.md: interim update following port to wlroots 0.20 2026-03-31 19:34:03 +01:00
Jens Peters
674238caa9 docs: document tablet tool pressure range 2026-03-31 19:56:07 +02:00
Jens Peters
0d049552bc input: support tablet tool pressure range configuration
Needs wlroots 0.20.0.
2026-03-31 19:56:07 +02:00
Jens Peters
9209f611d5 build: require libinput 1.26
Bump the requirement for having support for
tablet tool pressure range configuration.
2026-03-31 19:56:07 +02:00
Johan Malm
46d687ab54 clients/labnag.c: return zero on get_text_size() error
Ref: https://github.com/swaywm/sway/pull/9070
2026-03-31 02:58:39 +02:00
GlassOnTin
9a8154836c keyboard: use XKB_CONTEXT_NO_SECURE_GETENV on Android
Android's bionic libc implements secure_getenv() as a function that
always returns NULL because app processes don't have AT_SECURE set.
This prevents xkbcommon from reading XKB_DEFAULT_LAYOUT and other
environment variables when resolving keyboard layouts.

Use XKB_CONTEXT_NO_SECURE_GETENV on Android so xkbcommon falls back
to regular getenv(), which works correctly in the Android app
environment.
2026-03-30 22:25:35 +01:00
GlassOnTin
862d230ff9 Make labnag a meson feature flag
Add a 'labnag' meson option (default: auto) to allow disabling the
labnag notification daemon at build time. This is useful for
embedded/headless deployments (e.g. Android) where labnag is not
needed, and avoids building its wayland-client dependencies.

Disable with: meson setup build -Dlabnag=disabled
2026-03-30 22:22:21 +01:00
tokyo4j
3128f50a25 xwayland: remove atom synchronization
It was used to get window icon via _NET_WM_ICON, which is now
implemented by wlroots 0.20. Anyone who needs another atom can revert
this commit and add atoms in the `atoms` array.
2026-03-30 11:55:37 +02:00
tokyo4j
cdee4ba5f1 xwayland: use wlr_xwayland_surface_fetch_icon() 2026-03-30 11:55:37 +02:00
Christopher Snowhill
cee5712877 chase wlroots: wlr_scene_set_gamma_control_manager_v1
Ref: 515275ee7214bf91f8a758b660093eb4b932195a
(wlr_scene: Introduce wlr_scene_set_gamma_control_manager_v1)

This wlroots change eliminates the need for separate event tracking for gamma
control application.

v2: Fix code style
v3: Rebase now that 0.20 is merged
2026-03-30 11:18:53 +02:00
tokyo4j
647e9b7658 Drop cosmic-workspace and use wlroots implementation of ext-workspace-v1 2026-03-30 10:29:04 +02:00
Consolatis
c3fc78286a chase wlroots: track 0.20 branch
And move CI to use system wide wlroots deps where availble.
2026-03-29 15:58:13 +01:00
Manuel Barrio Linares
76f166ae42 chase wlroots: wlr_xwayland_set_cursor now takes a wlr_buffer (MR 5230)
Ref: 84d603acc06a45dd3c3a4b2cf1fd08b2933ca2b5
(xwayland: take wlr_buffer in wlr_xwayland_set_cursor())

Ref: 6ae54dca23064e897b393283887986e5719a747f
(xwayland: lock new buffer instead of the old one)

Co-Authored-By: Consolatis

This wlroots change fixes a potential UAF which we dealt with in labwc.
We can thus remove the workaround completely.
2026-03-29 15:58:13 +01:00
tokyo4j
863863fc7c chase wlroots: ime: don't use data in kb grab destroy handler (MR 5170)
Ref: 06275103f249cd2954630e59383342e102a6c1a3
(input-method-v2: Destroy keyboard grab before input method)

Background:

My MR in wlroots (!5107) stopped emitting `wlr_input_method_v2`
on its `commit`/`destroy` events, but didn't stop emitting
`wlr_input_method_keyboard_grab_v2` on its `destroy` event. That was
because `handle_keyboard_grab_destroy()` was called *after*
`handle_input_method_destroy()` for some reason, which caused segfault
when dereferencing `relay->input_method.keyboard_grab` in
`handle_keyboard_grab_destroy()`.

MR 5170 reversed this weired order of destroy handler calls, and finally
stopped emitting `wlr_input_method_keyboard_grab_v2` on its `destroy`
event.
2026-03-29 15:58:13 +01:00
Consolatis
5f8a6395a1 chase wlroots: ime: rename to new_input_method (MR 5107)
Ref: 06aacb2a6fd237a5e1062d611909432bbcf5b566
(input-method: rename input_method event to new_input_method)
2026-03-29 15:58:13 +01:00
Christopher Snowhill
511ed69c53 chase wlroots: add wl_fixes interface (MR 4685)
Ref: 812675ba34ce612e9294e8a9814b1baf4b4775d4
(fixes: add implementation)
2026-03-29 15:58:13 +01:00
Consolatis
0c24252c85 chase wlroots: ime: rename to new_text_input (MR 5032)
Ref: 536100488fc4c64528786801860f96cfa1a55765
(text-input-v3: Name new text input event correctly)
2026-03-29 15:58:13 +01:00
Consolatis
48bcccddc3 chase wlroots: increase wlroots meson dep 2026-03-29 15:58:13 +01:00
Consolatis
ea4e7e9a02 CI: allow compiling wlroots as subproject 2026-03-29 15:58:13 +01:00
John Lindgren
3ce6328f6d xwayland: set initial view state + geometry in handle_associate()
Empirically, the associate event is always seen just after map_request
but before surface map. Window properties are also read by wlroots just
before emitting associate. So after some trial and error, this seems to
be the best place to set initial view states and compute initial
placement.

Fixes initial positioning of "urxvt -g -0-0".

urxvt placement regressed in:
9903331995
("xwayland: rework setting initial geometry from surface")

map_request handler was added ~2 years ago in:
c9018da4c4
("xwayland: set initial geometry in map_request handler")

I'm not sure if the map_request handler was incorrect from day one,
or if something changed in wlroots and/or Xwayland since then.
2026-03-28 14:59:29 +00:00
Daniel Lublin
fee38bceca Handle xwayland not started (when HAVE_XWAYLAND, but lacking the binary)
Closes https://github.com/labwc/labwc/issues/434
2026-03-24 19:40:49 +00:00
Tomi Ollila
4e25347791 use X macros to fill up enum action_type and const char *action_names[]
To make it harder to have these 2 data structures go out of sync.

No functional change.

To understand X macros better, visit:
- https://en.wikipedia.org/wiki/X_macro
- https://philliptrudeau.com/blog/x-macro
2026-03-24 19:35:38 +00:00
John Lindgren
9903331995 xwayland: rework setting initial geometry from surface
The basic idea is to set the initial geometry from the surface exactly
once, just before we need it, i.e. either (1) when mapping the view or
(2) right before processing an initial maximize/fullscreen request.

I've consolidated various parts of the initial geometry setup to take
place at this point (in ensure_initial_geometry_and_output()).

The main motivation is to have a valid, adjusted floating geometry for
the view *before* saving the natural geometry when processing an initial
maximize/fullscreen request. This reduces code duplication and addresses
a FIXME in set_initial_position(), as well as fixing an issue where the
natural geometry could exceed the usable output area.

Some other minor changes:

- The initial output is now set directly from the surface geometry if
  the "application/user-set position" hint is given. This is unlikely
  to matter in practice, but theoretically an initially maximized view
  could now appear on a different (application-chosen) output.

- Floating view size is now constrained to the usable area even if a
  position hint is set. It seemed inconsistent that `xterm -g 200x200`
  was constrained but `xterm -g 200x200+0+0` was not.
2026-03-22 21:55:02 +00:00
John Lindgren
d4ad27e636 tree-wide: use rc.theme instead of server.theme
Having two global pointers to the same struct is redundant.
2026-03-22 10:16:42 +01:00
John Lindgren
9550bccef2 decorations: fix accidental rename
Fixes: 4f72e6775e
("tree-wide: rename g_server to just server")
2026-03-22 10:13:17 +01:00
John Lindgren
a2e0de7676 tree-wide: rename seat->seat to seat->wlr_seat for clarity
As we use "seat" to refer to the labwc struct, this makes it clearer
at a quick glance which code is using the wlr struct instead.
2026-03-22 00:56:49 +01:00
John Lindgren
4f72e6775e tree-wide: rename g_server to just server 2026-03-21 21:35:33 +00:00
John Lindgren
8d46da9db1 tree-wide: wrap a few extra-long lines 2026-03-21 21:35:33 +00:00
John Lindgren
cb49bddf63 tree-wide: auto-replace of (struct server *)
#!/bin/bash
    read -r -d '' EXPRS << EOF
    s/xwayland->server/xwayland->svr/g;

    s/\t*struct server \*server;\n//g;
    s/\t*struct server \*server =.*?;\n//gs;
    s/\t*.* = ([a-z_]*->)*server[;,]\n//g;
    s/\{\n\n/\{\n/g;
    s/\n\n+/\n\n/g;

    s/\(\s*struct server \*server\)/(void)/g;
    s/\(\s*struct server \*server,\s*/(/g;
    s/,\s*struct server \*server\)/)/g;
    s/,\s*struct server \*server,\s*/, /g;

    s/\(\s*([a-z_]*->)*server\)/()/g;
    s/\(\s*([a-z_]*->)*server,\s*/(/g;
    s/,\s*([a-z_]*->)*server\)/)/g;
    s/,\s*([a-z_]*->)*server,\s*/, /g;

    s/([a-z_]*->)*server->/g_server./g;

    s/xwayland->svr/xwayland->server/g;
    EOF

    find src include \( -name \*.c -o -name \*.h \) -exec \
        perl -0777 -i -pe "$EXPRS" \{\} \;
2026-03-21 21:35:33 +00:00
John Lindgren
60ac8f07bb tree-wide: add g_server global and prep for auto-replace 2026-03-21 21:35:33 +00:00
Johan Malm
50bb882cf0 NEWS.md: fix 0.9.6 release notes
...by removing the comment about crashes after suspend on some Nvidia GPUs
because this only happens after #3387 has is not yet in any release.

Ref:
- https://github.com/labwc/labwc/pull/3430#discussion_r2911781637
- https://github.com/labwc/labwc/pull/3448#discussion_r2948247530

Helped-by: @tokyo4j
2026-03-18 06:43:55 +09:00
Consolatis
17d6c29037 xwayland: sync always-on-top to X11 2026-03-16 17:46:48 +01:00
Consolatis
3b6fe26301 view: add always-on-top signal 2026-03-16 17:46:48 +01:00
John Lindgren
16c5373be5 tree-wide: use die_if_null() for wlr_scene alloc failures
Some checks failed
labwc.github.io / notify (push) Has been cancelled
wlr_scene_*_create() functions all allocate memory via calloc() and
return NULL if the allocation fails. Previously, the failures were
handled in any of 3 different ways:

 - sending a wayland protocol error
 - exiting labwc with an error
 - segfault (no NULL check at all)

Since labwc does not attempt to survive heap exhaustion in other
allocation paths (such as `znew`), it seems more consistent to use the
same die_if_null() check used in those paths to exit with an error.

For the three most common create() functions (tree, rect, buffer),
add small lab_wlr_ wrappers to common/scene-helpers.
2026-03-15 21:26:37 +00:00
John Lindgren
c4effef0ec xwayland: fix missing title and icon with override-redirect toggle
When an X11 client clears override-redirect on a window (i.e. it
becomes "managed") without an unmap/map cycle, wlroots doesn't
re-emit the set_title/class/icon events. We need to synthesize
them to re-read the values from the surface.

From what I can tell, only older/misbehaving X11 clients would
actually do this (toggle override-redirect without unmap/map), so
this is perhaps a bit pedantic, but an easy fix anyway.
2026-03-15 19:40:26 +00:00
Johan Malm
5a9ae66332 labnag.c: initialize sigset_t mask 2026-03-15 13:14:19 +00:00
Johan Malm
7c39ee30e9 NEWS.md: update notes for 0.9.6 2026-03-15 12:24:39 +00:00
Consolatis
4faab834f9 xwayland: make X11 always-on-top request opt-in
Fixes: #3441
2026-03-15 13:09:05 +01:00
tokyo4j
a3646721bc view: clear view->{title,app_id} later in view_destroy()
...as it *feels* unsafe to call functions like `undecorate()` after
clearing view->{title,app_id}, which are expected to be non-NULL.
2026-03-11 19:30:50 +01:00
tokyo4j
ab60379c7f ssd-titlebar: update title buffer when title gets empty
Before this commit, ssd_update_title() returned early and the title was
not updated when the title gets empty. Furthermore, this caused the
title buffer to be misplaced outside of the titlebar when the window is
resized.

Reproducer:
1. Build and launch https://github.com/tokyo4j/wayland-demo/tree/repro-labwc-ssd-title-not-updated
2. One second later, the window title gets empty. But the title in
   SSD is not updated.
3. Un-minimize the window. Now the title buffer is misplaced outside of
   the titlebar.
2026-03-11 19:30:50 +01:00
Consolatis
93029ca583 src/output.c: guard against wlroots not providing the X11 backend
Reported-By: Flrian via IRC
2026-03-11 15:57:44 +01:00
Consolatis
89574772d0 src/output.c: handle outputs with all modes failing
- To reset the mode request we were using wlr_output_state_set_mode(NULL) which is not
  allowed in wlroots. Replace it with removing the mode from the commit manually.

- Before this patch, we would assume that we can't configure an output if it doesn't have
  any fixed mode. This is not true for wayland backed nested outputs. Replace the fixed
  return false with wlr_output_test_state().

- Prevent configuring the output if the test fails as the current behavior was a
  workaround for the hardcoded return false in case there were no fixed modes available.

Fixes: #3428
Reported-By: kode54
2026-03-11 09:34:34 +01:00
Jonathan Marler
b36b4ab816 output: exit compositor when last nested output is destroyed
When running on the X11 or Wayland backend, each output corresponds
to a window in the parent compositor. Unlike DRM outputs, these
cannot be reconnected after being destroyed.

Terminate the compositor when the last output is destroyed on these
nested backends, matching the expected behavior when the user closes
the window.
2026-03-11 09:31:23 +01:00
Consolatis
f0589810ad src/cycle: handle views without output
Fixes: #3425
Reported-By: kode54
2026-03-10 22:34:16 +09:00
tokyo4j
3bb4ccd22d view: save view->last_placement on initial positioning
Fix a regression introduced in 3f223fe5 where output layout changes may
move xdg views outside of the output layout with the error:
`view has no last placement info`. This happens also for X11 views if
they don't specify its own geometry on map.

Recent changes introduced `view->last_placement` which is used to
restore view positions after layout changes. This is supposed to be
updated when a view is mapped or manually moved by the user.

However, 3f223fe5 replaced `view_place_by_policy()` with
`view_compute_position_by_policy()` in the xdg view map handler. Unlike
`view_place_by_policy()`, this function does not update
`view->last_placement`, leaving it unset at the time of output layout
changes.

Therefore, this patch adds explicit calls to `view_save_last_placement()`
in the map handler to ensure `view->last_placement` is always set for
mapped views.
2026-03-10 21:05:53 +09:00
Alexander Ziaee
1f776466a8 labwc-config.5: document mouse cursor variables
Some checks failed
labwc.github.io / notify (push) Has been cancelled
It was written that mouse cursor size can be set with XCURSOR_SIZE.
However, XCURSOR_THEME is also required, which exists in the context
of XCURSOR_PATH. Break them down in the ENVIRONMENT VARIABLES section.

Reported by: Consolatis (PATH)
Reported by: Jan Beich <jbeich@FreeBSD.org> (THEME)
Reported by: Thomas Dickey <dickey@invisible-island.net> (SIZE)
2026-03-08 08:08:02 +01:00
Tobias Bengfort
9de35d7b4f
src/server.c: fix name of xdg-dialog-v1 global for sandbox permission 2026-03-07 04:29:29 +01:00
Consolatis
8d8654db02 action: sync action arrays
The previous commit desynchronized the action type arrays
causing Move operations to be treated as Raise actions.

Fixes: #3398
Reported-By: Domo via IRC
2026-03-06 03:12:18 +09:00
tokyo4j
d6dab9139f action: reorder ACTION_TYPE_MOVE enum entry
This makes the code a bit easier to read by aligning ACTION_TYPE_MOVE
and ACTION_TYPE_MOVE together.
2026-03-05 19:19:01 +09:00
tokyo4j
00f63622b5 interactive: subtle refactor to interactive_set_grab_context()
- Add `server` argument to `interactive_set_grab_context()` to clarify
  it updates something in `server`.
- Add `const` qualifier to `ctx` argument of
  `interactive_set_grab_context()` and `cursor_get_resize_edges()`
2026-03-05 19:19:01 +09:00
John Lindgren
df73a97efa view: add/improve some comments 2026-03-05 19:06:59 +09:00
John Lindgren
3f223fe5b0 xdg: unify initial positioning logic 2026-03-05 19:06:59 +09:00
John Lindgren
658df83280 xdg: set initial output at initial commit
This seems to be the best point to do it, since the parent view is known
but initial maximized/fullscreen state hasn't been applied yet.
2026-03-05 19:06:59 +09:00
John Lindgren
1211c43a47 view: honor placement policy in more scenarios 2026-03-05 19:06:59 +09:00
John Lindgren
5238dcaa08 view: factor out view_compute_position_by_policy() 2026-03-05 19:06:59 +09:00
John Lindgren
6a9e6dcae1 view: factor out view_compute_cascaded_position() 2026-03-05 19:06:59 +09:00
John Lindgren
04cd38f251 view: factor out view_compute_near_cursor_position() 2026-03-05 19:06:59 +09:00
John Lindgren
7be407c8e4 view: un-fullscreen/maximize/tile before any placement policy 2026-03-05 19:06:59 +09:00
John Lindgren
d4ca1dfb69 view: make view_move_to_cursor() static 2026-03-05 19:06:59 +09:00
150 changed files with 4676 additions and 5634 deletions

View file

@ -20,6 +20,7 @@ on:
- 'clients/**'
- 't/**'
- 'scripts/**'
- 'docs/**'
- '.github/workflows/**'
jobs:
@ -82,7 +83,7 @@ jobs:
run: |
pacman-key --init
pacman -Syu --noconfirm
pacman -S --noconfirm git meson clang wlroots0.19 libdrm libinput \
pacman -S --noconfirm git meson clang wlroots0.20 libdrm libinput \
wayland-protocols cairo pango libxml2 xorg-xwayland librsvg \
libdisplay-info gdb ttf-dejavu foot libsfdo cmocka
@ -92,9 +93,15 @@ jobs:
sed -i '/^Types/ s/deb/& deb-src/' /etc/apt/sources.list.d/debian.sources
apt-get update
apt-get upgrade -y
apt-get install -y git gcc clang gdb xwayland
apt-get build-dep -y labwc
apt-get build-dep -y libwlroots-0.19-dev
apt-get install -y git gcc clang gdb xwayland meson pkgconf
apt-get build-dep -y libwlroots-0.20-dev
# Debian Testing temporarily removed labwc due to wlroots 0.20 transition
#apt-get build-dep -y labwc
# Install the deps manually
apt-get install -y libxml2-dev liblzma-dev libglib2.0-dev \
libcairo2-dev libpango1.0-dev libinput-dev libpng-dev \
librsvg2-dev wayland-protocols libxkbcommon-dev libdrm-dev \
scdoc gettext libwlroots-0.20-dev libsfdo-dev
- name: Install FreeBSD dependencies
if: matrix.name == 'FreeBSD'
@ -106,7 +113,7 @@ jobs:
pkg set -yn pkg:mesa-dri # hack to skip llvm dependency
pkg install -y git pcre2 meson gcc pkgconf cairo pango evdev-proto \
hwdata wayland-protocols libdisplay-info libepoll-shim \
wlroots019
wlroots020
run: echo "setup done"
- name: Install Void Linux dependencies
@ -120,8 +127,8 @@ jobs:
xbps-install -Syu
xbps-install -y git meson gcc clang pkg-config scdoc \
cairo-devel glib-devel libpng-devel librsvg-devel libxml2-devel \
pango-devel wlroots0.19-devel gdb bash xorg-server-xwayland \
dejavu-fonts-ttf libsfdo-devel foot
pango-devel wlroots0.20-devel gdb bash xorg-server-xwayland \
dejavu-fonts-ttf libsfdo-devel foot hwids
# These builds are executed on all runners
- name: Build with gcc
@ -209,6 +216,19 @@ jobs:
meson compile -C build-gcc-no-feature
' | $TARGET
# No backend build, run on Arch only
- name: Build with gcc - no-backends test
if: matrix.name == 'Arch'
run: |
echo '
cd "$GITHUB_WORKSPACE"
export CC=gcc
meson setup build-gcc-no-backends \
-Dwlroots:backends= -Dwlroots:session=disabled --werror \
--force-fallback-for=wlroots
meson compile -C build-gcc-no-backends
' | $TARGET
# Unit tests, run on Arch only
- name: Build with gcc - unit test
if: matrix.name == 'Arch'

View file

@ -19,7 +19,8 @@
- [6. Unit Tests](#unit-tests)
- [7. Submitting Patches](#submitting-patches)
- [8. Native Language Support](#native-language-support)
- [9. Upversion](#upversion)
- [9. Tools](#tools)
- [10. Upversion](#upversion)
# How to Contribute
@ -472,6 +473,29 @@ This generates a new pot file at `po/labwc.pot`
3. Copy the header from the original `labwc.pot` to the new one, keeping
the newly generated dates, check for sanity and commit.
# Tools
We permit LLM and AI-assisted tooling, but only on the basis that humans remain
fully accountable. Contributors must use an `Assisted-by:` tag in the commit
logs if AI tools generated or significantly assisted with any part of the code
or documentation. Humans retain full legal and technical responsibility for the
resulting work, including any defects.
We have received Pull Requests where the submitter clearly did not understand
the code, could not reason about it, and would be unable to maintain it. This
is not acceptable.
Any AI-generated code contribution must be compatible with the GPL-2.0-only
license.
While LLMs can be useful for triage, review, clean-up, API refactoring and
similar tasks, we strongly discourage AI slop. Code review effort is usually the
project's primary bottleneck, and submitting unreviewed, AI-generated garbage
wastes reviewer time, slows development, and harms the overall health of the
project. Likewise, AI-generated commit messages and AI-written text in issues
and discussions are strongly discouraged and may be treated as sabotage on this
basis.
# Upversion
It is generally only the lead-maintainer who will upversion, but in order

341
NEWS.md
View file

@ -9,6 +9,11 @@ The format is based on [Keep a Changelog]
| Date | All Changes | wlroots version | lines-of-code |
|------------|---------------|-----------------|---------------|
| 2026-06-15 | [0.20.1] | 0.20.1 | 28337 |
| 2026-05-25 | [0.20.0] | 0.20.1 | 28313 |
| 2026-06-11 | [0.9.8] | 0.19.3 | 29284 |
| 2026-04-17 | [0.9.7] | 0.19.2 | 29277 |
| 2026-03-15 | [0.9.6] | 0.19.2 | 29271 |
| 2026-03-04 | [0.9.5] | 0.19.2 | 29251 |
| 2026-02-27 | [0.9.4] | 0.19.2 | 29225 |
| 2025-12-19 | [0.9.3] | 0.19.2 | 28968 |
@ -42,6 +47,11 @@ The format is based on [Keep a Changelog]
| 2021-03-05 | [0.1.0] | 0.12.0 | 4627 |
[unreleased]: NEWS.md#unreleased
[0.20.1]: NEWS.md#0201---2026-06-15
[0.20.0]: NEWS.md#0200---2026-05-25
[0.9.8]: NEWS.md#098---2026-06-11
[0.9.7]: NEWS.md#097---2026-04-17
[0.9.6]: NEWS.md#096---2026-03-15
[0.9.5]: NEWS.md#095---2026-03-04
[0.9.4]: NEWS.md#094---2026-02-27
[0.9.3]: NEWS.md#093---2025-12-19
@ -74,6 +84,138 @@ The format is based on [Keep a Changelog]
[0.2.0]: NEWS.md#020---2021-04-15
[0.1.0]: NEWS.md#010---2021-03-05
## unreleased
[unreleased-commits]
## Notes on wlroots-0.20
There are some regression warnings worth noting for the switch to wlroots 0.20:
- lxqt-panel auto-hiding does not work with a panel size greater than 40 [#3600]
[wlroots-5392]
- Some Wine game windows disappear after alt-tab. It is not yet clear where the
bug is, but the issue manifests itself when running wlroots-0.20 [#3615]
[wlroots-4103]
[wlroots-5392]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5392
[wlroots-4103]: https://gitlab.freedesktop.org/wlroots/wlroots/-/work_items/4103
## 0.20.1 - 2026-06-15
[0.20.1-commits]
This is a small bug fix release.
### Fixed
- Handle titles with no visible characters, for example the left-to-right mark
(&lrm;) @Consolatis [#3630]
- Protect against SIGABRT when TTY switching in unusual circumstances by
ensuring that xdg-shell windows have sensible width and height before trying
to set size on configure. This protects against an edge case experienced when
switching between labwc on one TTY and Xfce on XOrg on another TTY.
@johanmalm @Tamaranch [#3617]
## 0.20.0 - 2026-05-25
[0.20.0-commits]
This is the first release using wlroots-0.20 and therefore has an increased risk
of teething issues. Many thanks to @Consolatis for leading the effort to port
across [#2956].
In terms of new features, the most noteworthy ones include: (i) the frequently
requested show-desktop action; (ii) initial toplevel capture support to
screenshot specific windows; (iii) menu accelerators/shortcuts; and (iv) HDR10
support when running with the Vulkan renderer option.
The eagle-eyed amongst you will have noticed the sudden jump from labwc `0.9.7`
to `0.20.0`. The reason for this is to align the minor number to that of the
wlroots version against which the compositor is linked.
The 0.9.x series has turned into a maintenance branch named v0.9 with bug fixes
only, for anyone preferring to build with wlroots-0.19.
Note to maintainers:
- libinput >=1.26 is required in support of tablet tool pressure range
configuration.
- wlroots >=0.20.1 is required to avoid some bugs that we do not want labwc to
ship with. For details, see:
https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5325
### Added
- Partially support toplevel-capture. Note that the following are not yet
implemented: (i) XWayland child windows; (ii) XWayland unmanaged windows
(e.g. popups); (iii) xdg child window positioning; (iv) xdg subsurfaces;
and (v) xdg popup positioning. [#2968] @Consolatis
- Add command line option -t|--title to set the labwc window title when running
nested [#3577] @mdsib
- Add support for HDR10 output @kode54 @Consolatis [#3424]
- Include wlroots version in --version string @Consolatis [#3567] [#3581]
- Implement menu accelerators (one-letter mnemonics to quickly select/exec
items from the current menu) @ch3rn1ka [#3505]
- Add Next/PreviousWindowImmediate actions @elviosak @johanmalm [#3547]
- Add labnag options `--details-border-color` and `--details-margin`
@st0rm-shad0w [#3527]
- Add config option `<focus><raiseOnFocusDelay>` to defer raise-on-focus by a
small amount when `raiseOnFocus` is enabled @joske [#3513]
- Install `labwc-session.target` systemd user unit when the systemd dependency
is available @joske [#3534]
- Add `onbutton` to config option `<libinput><device><scrollMethod>`. Also add
associated option `<libinput><device><scrollButton>`. @diniamo [#3540]
- Add `overrideInhibition` option to `<keybind>` [#3507] @drougas
- Add action `ToggleShowDesktop` to hide/unhide windows, and default keybind
`Super-d` to trigger this action [#3500] [#3595] @johanmalm
- Add `<privilegedInterfaces>` config option so that privileged protocols can be
restricted [#3493] @xi
- Add action `DebugToggleKeyStateIndicator` to show a key-state on-screen
display (OSD) for debugging. [#3499] @johanmalm @tokyo4j
- Add support for `color-management-v1` and `color-representation-manager-v1`
protocols [#3469] @ManuLinares
- Add configuration option `<tabletTool minPressure="0.0" maxPressure="1.0" />`
to enable tablet tool pressure range libinput settings [#2916] @jp7677
- Add `wl_fixes` interface [#2956] @kode54
### Fixed
- Enable labnag long option --exclusive-zone [#3576] @st0rm-shad0w
- Position chromium popup correctly when a window is maximized on a multi-
output setup @elviosak [#3547]
- Run session activation environment update synchronously to avoid a race
condition with the autostart script [#3543] @joske
- Allow interactive resize on fully maximized windows so that a resize
initiated by modifier plus right-mouse-button-drag is not ignored [#3525]
@bjorn
- Gracefully handle missing XWayland packages, so that a labwc compositor which
has been built with XWayland support (which is optional) can be run even if
XWayland is not installed. [#3401] @quite
- Rework how XWayland window initial geometry is set to ensure that the natural
geometry does not exceed the usable output area when handling initial
maximize/fullscreen requests. [#3439] @jlindgren90.
- For XWayland windows, sync always-on-top state back to X.Org Server. This
makes `wmctrl -b toggle,above` work. [#3446] @jlindgren90
- Fix missing title and icon with XWayland client override-redirect toggle.
There are no known issues with clients, so this is purely for preventative
purposes. [#3450] @jlindgren90
- Update titlebar title when set to empty and fix an associated issue causing
the title to be misplaced outside of the titlebar when the window is resized.
[#3443] @tokyo4j
- When running nested, exit compositor when last output is destroyed because
in this situation, each output corresponds to a window in the parent
compositor and, unlike DRM outputs, these cannot be reconnected after being
destroyed. [#3440] @marler8997
- Allow policy-based placement to apply when an initially-maximized/fullscreen
view is restored to floating geometry. [#3387] [#3502] @jlindgren90
### Changed
- Change the default keybinds for XF86Audio{LowerVolume,RaiseVolume,Mute} to use
pactl instead of amixer [#3484] @danielrrrr
- Drop cosmic-workspace protocol [#3031] @tokyo4j
## Notes on wlroots-0.19
There are some regression warnings worth noting for the switch to wlroots 0.19:
@ -108,9 +250,73 @@ There are some regression warnings worth noting for the switch to wlroots 0.19:
[wlroots-5098]:https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5098
[gtk-8792]: https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/8792
## unreleased
## 0.9.8 - 2026-06-11
[unreleased-commits]
[0.9.8-commits]
### Fixed
- Handle titles with no visible characters @Consolatis [#3631]
- Save window position on initial placement to fix Raspberry Pi pygame SDL
XWayland window starting off-screen. @tokyo4j [#3433] [#3625] [#3616]
## 0.9.7 - 2026-04-17
[0.9.7-commits]
This is a small bug fix release.
```
0.9.6--------0.9.7 <--- v0.9 branch with bug-fixes only
/
/
0.9.4--------0.9.5-------- <-- master (built with wlroots-0.20)
```
## Fixed
- Fix intermittent failed-to-get-texture issue with some clients (e.g. foot)
when using the window-switcher in the thumbnail mode. [#3511] @yuiiio
- Fix tablet tool tilt motion. [#3494] @jp7677
- Handle window-switcher buffer allocation failure when in 'thumbnail' mode.
This is believed to be very unlikely to happen, but has been reported by one
user and is believed to be GPU driver related. [#3490] @Consolatis
## 0.9.6 - 2026-03-15
[0.9.6-commits]
This is an earlier-than-usual release containing bug fixes only. It has been
done on a separate branch (v0.9) to avoid the inclusion of refactoring and new
features.
```
0.9.6 <--- bug-fixes only
/
/
0.9.4--------0.9.5-------- <-- master
```
### Fixed
- Disable outputs where all modes fail [#3428] [#3429] @Consolatis @kode54
- Fix regression in `0.9.4` that causes `NextWindow` action to segfault when
no outputs are connected. [#3425] [#3430] @Consolatis
- Fix typo to allow `xdg-dialog-v1` global [#3426] @xi
### Changed
- Disallow X11 window always-on-top requests by default to fix an issue whereby
Alt+Tab cannot be used to switch to other windows when using some XWayland
Wine games [#3441]. Add window-rule property `allowAlwaysOnTop` to optionally
allow this always-on-top requests. Add the snippet below to the `rc.xml` file
to restore the previous behaviour. [#3445] @Consolatis
```
<windowRules>
<windowRule identifier="*" allowAlwaysOnTop="yes" />
</windowRules>
```
## 0.9.5 - 2026-03-04
@ -139,9 +345,9 @@ Note to package maintainers: This release requires wayland version >=1.22.90
- Add theme option window.button.hover.bg.color [#3365] @johanmalm
- Implement scrollable window-switcher OSD [#3291] @tokyo4j
- Support the `NextWindow` options listed below [#3271] @tokyo4j
- `<action name="NextWindow" workspace="current|all"/>`
- `<action name="NextWindow" output="all|focused|cursor"/>`
- `<action name="NextWindow" identifier="all|current"/>`
- `<action name="NextWindow" workspace="current|all" />`
- `<action name="NextWindow" output="all|focused|cursor" />`
- `<action name="NextWindow" identifier="all|current" />`
- Add config option `*<desktops><initial>` for setting the active workspace on
startup. [#3265] @5trixs0f
@ -189,7 +395,7 @@ Note to package maintainers: This release requires wayland version >=1.22.90
and get keyboard focus so that they can be operated with the keyboard.
An example use-case is the xfce4-panel applications-menu being opened by
the command xfce4-popup-applicationsmenu. [#3165] @johanmalm
- On popup destory, return focus to whoever had it before the popop [#3165]
- On popup destroy, return focus to whoever had it before the popop [#3165]
@johanmalm @tokyo4j
- Unshade window if selected from client-list-combined-menu [#3345] @Amodio
- Show non-dialog child windows in window-switcher [#3339] @tokyo4j
@ -223,7 +429,7 @@ A big thank you to all involved in this release.
### Added
- Add `<windowSwitcher order="focus|age"/>` to optionally order windows by age
- Add `<windowSwitcher order="focus|age" />` to optionally order windows by age
rather than most recent focus. @mbroemme [#3229]
- Replace `<snapping><range>` with `<snapping><range inner="" outer="">` to
provide more granular control when configuring the size of snapping areas
@ -254,7 +460,7 @@ A big thank you to all involved in this release.
[#3134]
- labnag: add --keyboard-focus option @tokyo4j [#3120]
- Allow window switcher to temporarily unshade windows using config option
`<windowSwitcher unshade="yes|no"/>` @Amodio @Consolatis [#3124]
`<windowSwitcher unshade="yes|no" />` @Amodio @Consolatis [#3124]
- For the 'classic' style window-switcher, add the following theme options:
- `osd.window-switcher.style-classic.item.active.border.color`
- `osd.window-switcher.style-classic.item.active.bg.color`
@ -284,7 +490,7 @@ A big thank you to all involved in this release.
client surface. Fixes a regression in 885919f. @tokyo4j [#3211]
- Set all foreign-toplevel initial states correctly. This is not believed to fix
any particular user-issue, but just feels safer. @jlindgren90 [#3217]
- Update layer-shell client top layer visiblity on unmap instead of destroy
- Update layer-shell client top layer visibility on unmap instead of destroy
because it is possible for fullscreen xwayland windows to be unmapped without
being destroyed, and in this case the top layer visibility needs to be updated
to unhide other layer-shell clients like panels. @jlindgren90 [#3199]
@ -296,7 +502,7 @@ A big thank you to all involved in this release.
@elviosak [#3146] [#3168]
- Work around client-side rounding issues at right/bottom pixel. This fixes an
issue with some clients (notably Qt ones) where cursor coordinates in the
rightmost or bottom fixel are incorrectly rounded up putting them outside the
rightmost or bottom pixel are incorrectly rounded up putting them outside the
surface bounds. The issue has been particularly noticeable with layer-shell
clients like lxqt-panel. @jlindgren90 [#3157] [#2379] [#3099]
Note: This also avoids a similar server-side rounding issue with some
@ -344,7 +550,7 @@ A big thank you to all involved in this release.
when a window is using fullscreen mode. @johanmalm [#3158]
- Call labnag with on-demand keyboard interactivity by default @tokyo4j [#3120]
- Temporarily unshade windows when switching windows. Restore old behaviour with
`<windowSwitcher unshade="no"/>` @Amodio @Consolatis [#3124]
`<windowSwitcher unshade="no" />` @Amodio @Consolatis [#3124]
- In the classic style window-switcher, the default color of the selected window
item has been changed to inherit the border color but with 15% opacity
@tokyo4j [#3118]
@ -363,7 +569,7 @@ A big thank you to all involved in this release.
`<windowSwitcher style="thumbnail">`. @tokyo4j [#2981]
- Add `toggle` option to `GoToDesktop` action. This has the effect of going back
to the last desktop if already on the target. @RainerKuemmerle [#3024]
- Add `<theme maximizedDecoration="titlebar|none"/>` to allow hiding titlebar
- Add `<theme maximizedDecoration="titlebar|none" />` to allow hiding titlebar
when window is maximized. @CosmicFusion @tokyo4j [#3015]
- Use client-send-to-menu as 'Workspace' submenu in built-in client-menu
@johanmalm [#2995]
@ -414,7 +620,7 @@ A big thank you to all involved in this release.
- Change default keybind `W-<arrow>` to combine cardinal directions to support
resizing of windows to fill a quarter of an output. This only affects users
who do not use an `rc.xml` (thereby using default keybinds) or use the
`<keyboard><default/>` option. Previous behavior can be restored by setting
`<keyboard><default />` option. Previous behavior can be restored by setting
`combine="no"` as shown below. [#3081] @tokyo4j
```
@ -513,7 +719,7 @@ window.*.title.bg.colorTo.splitTo:
window rule to enable this. @Consolatis @tokyo4j [#2840]
- Add config option `<core><primarySelection>`. This enables autoscroll
(middle-click to scroll up/down) in Chromium and electron based clients
without inadvertantly pasting the primary clipboard. @johanmalm [#2832]
without inadvertently pasting the primary clipboard. @johanmalm [#2832]
- Bump `xdg_shell` version from 3 to 6 @tokyo4j [#2814]
- Bump `wl_compositor` version from 5 to 6 @tokyo4j [#2812]
- Support tablet tool mouse buttons @jp7677 [#2778]
@ -590,7 +796,7 @@ window.*.title.bg.colorTo.splitTo:
agnostic on choice of launcher.
- `A-<arrow>` for `MoveToEdge` because `Alt-` keybinds should be for clients
to use and this one results in frequent user complaints because it prevents
some common usage patterns like alt-left/right in web browers.
some common usage patterns like alt-left/right in web browsers.
- Change default titlebar menu button from a dot to an arrow @johanmalm [#2844]
- When `dragLock` is set to `yes`, the drag no longer expires after a short
delay (known as `Sticky` mode) as recommended by libinput [#2803]. The timeout
@ -625,7 +831,7 @@ release.
- Localize desktop-entry application names used by the window switcher via
`desktop_entry_name` or the `%n` specifier @tokyo4j [#2653]
- Add `HideCursor` action @jp7677 [#2633]
- Support application icons in window-switcher using `<field content="icon"/>`
- Support application icons in window-switcher using `<field content="icon" />`
and use this by default. @tokyo4j [#2621]
- Support application icons in client-list-combined-menu @tokyo4j [#2617]
- Support the use of the keypad-enter key when using menu. @zeusgoose [#2610]
@ -762,7 +968,7 @@ Notes to package maintainers:
closing a popup did not move the pointer focus to the main toplevel until the
cursor was moved. [#2443]
- Improve algorithm for menu placement with xdg-positioner [#2408]
- Do not forward IME key-release without correspinding key-press to avoid stuck
- Do not forward IME key-release without corresponding key-press to avoid stuck
keys [#2437]
### Changed
@ -824,7 +1030,7 @@ Notes to package maintainers:
```xml
<windowRules>
<windowRule identifier="blender" wantAbsorbedModifierReleaseEvents="yes"/>
<windowRule identifier="blender" wantAbsorbedModifierReleaseEvents="yes" />
</windowRules>
```
@ -841,8 +1047,8 @@ menu.border.color: #aaaaaa
```xml
<windowSwitcher>
<fields>
<field content="custom" format="%n" width="25%"/>
<field content="title" width="75%"/>
<field content="custom" format="%n" width="25%" />
<field content="title" width="75%" />
</fields>
</windowSwitcher>
```
@ -972,7 +1178,7 @@ Notes to package maintainers:
- Support the openbox style menus listed below. Written-by: @droc12345
1. `client-list-combined-menu` shows windows across all workspaces. This can
be used with a mouse/key bind using:
`<action name="ShowMenu" menu="client-list-combined-menu"/>` [#2101]
`<action name="ShowMenu" menu="client-list-combined-menu" />` [#2101]
2. `client-send-to` shows all workspaces that the current window can be sent
to. This can additional be used within a client menu using:
`<menu id="client-send-to-menu" label="Send to Workspace..." />` [#2152]
@ -1131,14 +1337,14 @@ have been attributed with a 'Written-by' against each relevant log entry.
```xml
<placement>
<policy>cascade</policy>
<cascadeOffset x="40" y="30"/>
<cascadeOffset x="40" y="30" />
</placement>
```
- Support relative tablet motion. Written-by: @jp7677 [#1962]
```xml
<tabletTool motion="absolute|relative" relativeMotionSensitivity="1.0"/>
<tabletTool motion="absolute|relative" relativeMotionSensitivity="1.0" />
```
### Fixed
@ -1222,7 +1428,7 @@ joint effort by @spl237 and @Consolatis.
- Respect `menu.overlap.x` when using pipemenus. [#1940]
- Do not try to restore windows to very small width/height on unmaximize.
This fixes a bug with Thonny (Python IDE made with Tk). [#1938]
- Conditially set squared server-side decoration (SSD) corners when a view is
- Conditionally set squared server-side decoration (SSD) corners when a view is
tiled. Written-by: @jp7677 [#1926]
- Remember initial direction when starting window-cycling with `PreviousView`.
Also make the toggling of direction when shift is pressed relative to the
@ -1246,7 +1452,7 @@ joint effort by @spl237 and @Consolatis.
Chromium and Steam. [#1861]
- Session-lock: fix flashing & update cursor shape. [#1858]
- Remove tearing-controller listeners on destroy. [#1853]
- Handle invalid `ForEach` and `If` action cofigs. [#1838]
- Handle invalid `ForEach` and `If` action configs. [#1838]
- Delay startup of applications until event loop is ready. This avoids race
conditions when using autostart scripts that trigger a labwc SIGHUP. [#1588]
- With `SendToDesktop` action follow=no option, ensure the topmost window is
@ -1263,7 +1469,7 @@ joint effort by @spl237 and @Consolatis.
- Remove subprojects/seatd.wrap as no longer needed
- Action `MoveToCursor` is deprecated in favour of:
`<action name="AutoPlace" policy="cursor"/>`.
`<action name="AutoPlace" policy="cursor" />`.
## 0.7.2 - 2024-05-10
@ -1280,7 +1486,7 @@ contributions from others as noted in the log.
### Added
- Add `<menu><ignoreButtonReleasePeriod>` to prevent clicks with small movements
from inadvertantly closing a menu or selecting a menu item. This is the
from inadvertently closing a menu or selecting a menu item. This is the
equivalent of `<menu><hideDelay>` on Openbox. [#1760]
- Support drop-shadows (disabled by default) for windows using server-side
decorations. Written-by: @cillian64
@ -1308,7 +1514,7 @@ window.inactive.shadow.color: #00000040
```xml
<action name="ForEach">
<query identifier="foo"/>
<query identifier="foo" />
<then>
<!-- carry out some action on match -->
</then>
@ -1350,7 +1556,7 @@ osd.window-switcher.width: 75%
<snapping>
<overlay>
<enabled>yes|no</enabled>
<delay inner="500" outer="500"/>
<delay inner="500" outer="500" />
</overlay>
</snapping>
```
@ -1513,7 +1719,7 @@ osd.window-switcher.preview.border.color: #ffffff,#00a2ff,#ffffff
```xml
<keybind key="A-Space">
<action name="ShowMenu" menu="client-menu" atCursor="No"/>
<action name="ShowMenu" menu="client-menu" atCursor="No" />
</keybind>
```
@ -1521,7 +1727,7 @@ osd.window-switcher.preview.border.color: #ffffff,#00a2ff,#ffffff
is already used by the action itself). [#1589]
```xml
<action name="MoveToOutput" output="HDMI-A-1"/>
<action name="MoveToOutput" output="HDMI-A-1" />
```
- Do not deactivate window when giving keyboard focus to a non-view
@ -1586,8 +1792,8 @@ osd.window-switcher.preview.border.color: #ffffff,#00a2ff,#ffffff
Written-by: @jp7677
```xml
<touch mapToOutput=""/>
<touch deviceName="" mapToOutput=""/>
<touch mapToOutput="" />
<touch deviceName="" mapToOutput="" />
```
- Add tablet support including:
@ -1800,7 +2006,7 @@ relating to surface focus and keyboard issues, amongst others.
- Allow referencing the current workspace in actions, for example:
```xml
<action name="SendToDesktop" to="current"/>
<action name="SendToDesktop" to="current" />
```
### Fixed
@ -1956,7 +2162,7 @@ relating to surface focus and keyboard issues, amongst others.
```xml
<windowSwitcher>
<fields>
<field content="identifier" width="25%"/>
<field content="identifier" width="25%" />
</fields>
</windowSwithcer>
```
@ -2023,9 +2229,9 @@ relating to surface focus and keyboard issues, amongst others.
```xml
<windowRules>
<windowRule identifier="some-application">
<action name="Maximize"/>
<action name="Maximize" />
</windowRule>
<windowRule identifier="foo*" serverDecoration="yes|no"/>
<windowRule identifier="foo*" serverDecoration="yes|no" />
</windowRules>
```
@ -2110,7 +2316,7 @@ Unless otherwise stated all contributions are by the core-devs
```xml
<keyboard>
<default/>
<default />
<keybind key="A-Left"><action name="None" /></keybind>
<keybind key="A-Right"><action name="None" /></keybind>
</keyboard>
@ -2374,7 +2580,7 @@ reported, tested and fixed issues. Particular mentions go to @bi4k8,
actions to be de-coupled from buttons. As a result, "Drag" and
"DoubleClick" actions previously defined against "TitleBar" should now
come under the "Title" context, for example:
`<mousebind button="Left" action="Drag"><action name="Move"/></mousebind>`
`<mousebind button="Left" action="Drag"><action name="Move" /></mousebind>`
- Remove default alt-escape keybind for Exit because too many people have
exited the compositor by mistake trying to get out of alt-tab cycling
or similar.
@ -2607,7 +2813,12 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
ShowMenu
[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
[unreleased-commits]: https://github.com/labwc/labwc/compare/0.9.5...HEAD
[unreleased-commits]: https://github.com/labwc/labwc/compare/0.20.1...HEAD
[0.20.1-commits]: https://github.com/labwc/labwc/compare/0.20.0..0.20.1
[0.20.0-commits]: https://github.com/labwc/labwc/compare/0.9.5..0.20.0
[0.9.8-commits]: https://github.com/labwc/labwc/compare/0.9.7...0.9.8
[0.9.7-commits]: https://github.com/labwc/labwc/compare/0.9.6...0.9.7
[0.9.6-commits]: https://github.com/labwc/labwc/compare/0.9.5...0.9.6
[0.9.5-commits]: https://github.com/labwc/labwc/compare/0.9.4...0.9.5
[0.9.4-commits]: https://github.com/labwc/labwc/compare/0.9.3...0.9.4
[0.9.3-commits]: https://github.com/labwc/labwc/compare/0.9.2...0.9.3
@ -3046,6 +3257,7 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
[#2909]: https://github.com/labwc/labwc/pull/2909
[#2910]: https://github.com/labwc/labwc/pull/2910
[#2914]: https://github.com/labwc/labwc/pull/2914
[#2916]: https://github.com/labwc/labwc/pull/2916
[#2933]: https://github.com/labwc/labwc/pull/2933
[#2937]: https://github.com/labwc/labwc/pull/2937
[#2939]: https://github.com/labwc/labwc/pull/2939
@ -3053,8 +3265,10 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
[#2943]: https://github.com/labwc/labwc/pull/2943
[#2944]: https://github.com/labwc/labwc/pull/2944
[#2948]: https://github.com/labwc/labwc/pull/2948
[#2956]: https://github.com/labwc/labwc/pull/2956
[#2965]: https://github.com/labwc/labwc/pull/2965
[#2967]: https://github.com/labwc/labwc/pull/2967
[#2968]: https://github.com/labwc/labwc/pull/2968
[#2970]: https://github.com/labwc/labwc/pull/2970
[#2971]: https://github.com/labwc/labwc/pull/2971
[#2972]: https://github.com/labwc/labwc/pull/2972
@ -3070,6 +3284,7 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
[#3020]: https://github.com/labwc/labwc/pull/3020
[#3024]: https://github.com/labwc/labwc/pull/3024
[#3028]: https://github.com/labwc/labwc/pull/3028
[#3031]: https://github.com/labwc/labwc/pull/3031
[#3033]: https://github.com/labwc/labwc/pull/3033
[#3039]: https://github.com/labwc/labwc/pull/3039
[#3042]: https://github.com/labwc/labwc/pull/3042
@ -3135,8 +3350,54 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
[#3365]: https://github.com/labwc/labwc/pull/3365
[#3372]: https://github.com/labwc/labwc/pull/3372
[#3373]: https://github.com/labwc/labwc/pull/3373
[#3387]: https://github.com/labwc/labwc/pull/3387
[#3400]: https://github.com/labwc/labwc/pull/3400
[#3401]: https://github.com/labwc/labwc/pull/3401
[#3406]: https://github.com/labwc/labwc/pull/3406
[#3410]: https://github.com/labwc/labwc/pull/3410
[#3411]: https://github.com/labwc/labwc/pull/3411
[#3412]: https://github.com/labwc/labwc/pull/3412
[#3424]: https://github.com/labwc/labwc/pull/3424
[#3425]: https://github.com/labwc/labwc/pull/3425
[#3426]: https://github.com/labwc/labwc/pull/3426
[#3428]: https://github.com/labwc/labwc/pull/3428
[#3429]: https://github.com/labwc/labwc/pull/3429
[#3430]: https://github.com/labwc/labwc/pull/3430
[#3433]: https://github.com/labwc/labwc/pull/3433
[#3439]: https://github.com/labwc/labwc/pull/3439
[#3440]: https://github.com/labwc/labwc/pull/3440
[#3441]: https://github.com/labwc/labwc/pull/3441
[#3443]: https://github.com/labwc/labwc/pull/3443
[#3445]: https://github.com/labwc/labwc/pull/3445
[#3446]: https://github.com/labwc/labwc/pull/3446
[#3450]: https://github.com/labwc/labwc/pull/3450
[#3469]: https://github.com/labwc/labwc/pull/3469
[#3484]: https://github.com/labwc/labwc/pull/3484
[#3490]: https://github.com/labwc/labwc/pull/3490
[#3493]: https://github.com/labwc/labwc/pull/3493
[#3494]: https://github.com/labwc/labwc/pull/3494
[#3499]: https://github.com/labwc/labwc/pull/3499
[#3500]: https://github.com/labwc/labwc/pull/3500
[#3502]: https://github.com/labwc/labwc/pull/3502
[#3505]: https://github.com/labwc/labwc/pull/3505
[#3507]: https://github.com/labwc/labwc/pull/3507
[#3511]: https://github.com/labwc/labwc/pull/3511
[#3513]: https://github.com/labwc/labwc/pull/3513
[#3525]: https://github.com/labwc/labwc/pull/3525
[#3527]: https://github.com/labwc/labwc/pull/3527
[#3534]: https://github.com/labwc/labwc/pull/3534
[#3540]: https://github.com/labwc/labwc/pull/3540
[#3543]: https://github.com/labwc/labwc/pull/3543
[#3547]: https://github.com/labwc/labwc/pull/3547
[#3567]: https://github.com/labwc/labwc/pull/3567
[#3576]: https://github.com/labwc/labwc/pull/3576
[#3577]: https://github.com/labwc/labwc/pull/3577
[#3581]: https://github.com/labwc/labwc/pull/3581
[#3595]: https://github.com/labwc/labwc/pull/3595
[#3600]: https://github.com/labwc/labwc/pull/3600
[#3615]: https://github.com/labwc/labwc/pull/3615
[#3616]: https://github.com/labwc/labwc/pull/3616
[#3617]: https://github.com/labwc/labwc/pull/3617
[#3625]: https://github.com/labwc/labwc/pull/3625
[#3630]: https://github.com/labwc/labwc/pull/3630
[#3631]: https://github.com/labwc/labwc/pull/3631

View file

@ -153,7 +153,7 @@ Build dependencies include:
- meson, ninja, gcc/clang
- wayland-protocols
Disable xwayland with `meson -Dxwayland=disabled build/`
Disable xwayland with `meson setup -Dxwayland=disabled build/`
For OS/distribution specific details see [wiki].
@ -213,15 +213,16 @@ If you have not created an rc.xml config file, default bindings will be:
| `super`-`return` | lab-sensible-terminal
| `alt`-`F4` | close window
| `super`-`a` | toggle maximize
| `super`-`d` | toggle show-desktop
| `super`-`mouse-left` | move window
| `super`-`mouse-right` | resize window
| `super`-`arrow` | resize window to fill half the output
| `alt`-`space` | show the window menu
| `XF86_AudioLowerVolume` | amixer sset Master 5%-
| `XF86_AudioRaiseVolume` | amixer sset Master 5%+
| `XF86_AudioMute` | amixer sset Master toggle
| `XF86_MonBrightnessUp` | brightnessctl set +10%
| `XF86_MonBrightnessDown` | brightnessctl set 10%-
| `XF86AudioLowerVolume` | pactl set-sink-volume @DEFAULT_SINK@ -5%
| `XF86AudioRaiseVolume` | pactl set-sink-volume @DEFAULT_SINK@ +5%
| `XF86AudioMute` | pactl set-sink-mute @DEFAULT_SINK@ toggle
| `XF86MonBrightnessUp` | brightnessctl set +10%
| `XF86MonBrightnessDown` | brightnessctl set 10%-
A root-menu can be opened by clicking on the desktop.

View file

@ -46,6 +46,7 @@ struct conf {
uint32_t button_text;
uint32_t button_background;
uint32_t details_background;
uint32_t details_border_color;
uint32_t background;
uint32_t text;
uint32_t button_border;
@ -60,6 +61,7 @@ struct conf {
ssize_t button_gap_close;
ssize_t button_margin_right;
ssize_t button_padding;
ssize_t details_margin;
};
struct pointer {
@ -206,6 +208,16 @@ static void
get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height,
int *baseline, double scale, bool markup, const char *fmt, ...)
{
if (width) {
*width = 0;
}
if (height) {
*height = 0;
}
if (baseline) {
*baseline = 0;
}
va_list args;
va_start(args, fmt);
gchar *buf = g_strdup_vprintf(fmt, args);
@ -290,10 +302,10 @@ render_details_scroll_button(cairo_t *cairo, struct nag *nag,
get_text_size(cairo, nag->conf->font_description, &text_width,
&text_height, NULL, 1, true, "%s", button->text);
int border = nag->conf->button_border_thickness;
int padding = nag->conf->button_padding;
int border = nag->conf->details_border_thickness;
int padding = (nag->conf->button_padding / 3) + 2;
cairo_set_source_u32(cairo, nag->conf->details_background);
cairo_set_source_u32(cairo, nag->conf->details_border_color);
cairo_rectangle(cairo, button->x, button->y,
button->width, button->height);
cairo_fill(cairo);
@ -306,7 +318,7 @@ render_details_scroll_button(cairo_t *cairo, struct nag *nag,
cairo_set_source_u32(cairo, nag->conf->button_text);
cairo_move_to(cairo, button->x + border + padding,
button->y + border + (button->height - text_height) / 2);
button->y + (button->height - text_height) / 2);
render_text(cairo, nag->conf->font_description, 1, true,
"%s", button->text);
}
@ -321,8 +333,8 @@ get_detailed_scroll_button_width(cairo_t *cairo, struct nag *nag)
NULL, 1, true, "%s", nag->details.button_down.text);
int text_width = up_width > down_width ? up_width : down_width;
int border = nag->conf->button_border_thickness;
int padding = nag->conf->button_padding;
int border = nag->conf->details_border_thickness;
int padding = (nag->conf->button_padding / 3) + 2;
return text_width + border * 2 + padding * 2;
}
@ -333,8 +345,9 @@ render_detailed(cairo_t *cairo, struct nag *nag, uint32_t y)
uint32_t width = nag->width;
int border = nag->conf->details_border_thickness;
int margin = nag->conf->details_margin;
int padding = nag->conf->message_padding;
int decor = padding + border;
int decor = margin + border;
nag->details.x = decor;
nag->details.y = y + decor;
@ -362,7 +375,7 @@ render_detailed(cairo_t *cairo, struct nag *nag, uint32_t y)
bool show_buttons = nag->details.offset > 0;
int button_width = get_detailed_scroll_button_width(cairo, nag);
if (show_buttons) {
nag->details.width -= button_width;
nag->details.width += border - button_width;
pango_layout_set_width(layout,
(nag->details.width - padding * 2) * PANGO_SCALE);
}
@ -375,7 +388,7 @@ render_detailed(cairo_t *cairo, struct nag *nag, uint32_t y)
if (!show_buttons) {
show_buttons = true;
nag->details.width -= button_width;
nag->details.width += border - button_width;
pango_layout_set_width(layout,
(nag->details.width - padding * 2) * PANGO_SCALE);
}
@ -391,21 +404,29 @@ render_detailed(cairo_t *cairo, struct nag *nag, uint32_t y)
nag->details.visible_lines = pango_layout_get_line_count(layout);
int border_rect_height = nag->details.height + 2 * border;
if (show_buttons) {
nag->details.button_up.x = nag->details.x + nag->details.width;
nag->details.button_up.y = nag->details.y;
nag->details.button_up.y = nag->details.y - border;
nag->details.button_up.width = button_width;
nag->details.button_up.height = nag->details.height / 2;
nag->details.button_up.height = (border_rect_height + border) / 2;
render_details_scroll_button(cairo, nag, &nag->details.button_up);
nag->details.button_down.x = nag->details.x + nag->details.width;
nag->details.button_down.y =
nag->details.button_up.y + nag->details.button_up.height;
nag->details.button_up.y + nag->details.button_up.height - border;
nag->details.button_down.width = button_width;
nag->details.button_down.height = nag->details.height / 2;
nag->details.button_down.height =
border_rect_height - nag->details.button_up.height + border;
render_details_scroll_button(cairo, nag, &nag->details.button_down);
}
cairo_set_source_u32(cairo, nag->conf->details_border_color);
cairo_rectangle(cairo, margin, nag->details.y - border,
nag->details.width + 2 * border, border_rect_height);
cairo_fill(cairo);
cairo_set_source_u32(cairo, nag->conf->details_background);
cairo_rectangle(cairo, nag->details.x, nag->details.y,
nag->details.width, nag->details.height);
@ -437,7 +458,7 @@ render_button(cairo_t *cairo, struct nag *nag, struct button *button,
}
button->x = *x - border - text_width - padding * 2 + 1;
button->y = (int)(ideal_height - text_height) / 2 - padding + 1;
button->y = (int)(ideal_height - text_height) / 2 - padding;
button->width = text_width + padding * 2;
button->height = text_height + padding * 2;
@ -1388,6 +1409,7 @@ nag_setup(struct nag *nag)
}
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
sigprocmask(SIG_BLOCK, &mask, NULL);
@ -1453,14 +1475,16 @@ conf_init(struct conf *conf)
conf->keyboard_focus = ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
conf->bar_border_thickness = 2;
conf->message_padding = 8;
conf->details_border_thickness = 3;
conf->button_border_thickness = 3;
conf->button_gap = 20;
conf->button_gap_close = 15;
conf->button_margin_right = 2;
conf->button_padding = 3;
conf->button_background = 0x680A0AFF;
conf->details_margin = 11;
conf->details_border_thickness = 3;
conf->details_background = 0x680A0AFF;
conf->details_border_color = 0x680A0AFF;
conf->background = 0x900000FF;
conf->text = 0xFFFFFFFF;
conf->button_text = 0xFFFFFFFF;
@ -1540,16 +1564,18 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
TO_COLOR_BORDER_BOTTOM,
TO_COLOR_BUTTON_BG,
TO_COLOR_DETAILS,
TO_COLOR_DETAILS_BORDER,
TO_COLOR_TEXT,
TO_COLOR_BUTTON_TEXT,
TO_THICK_BAR_BORDER,
TO_PADDING_MESSAGE,
TO_THICK_DET_BORDER,
TO_THICK_DETAILS_BORDER,
TO_THICK_BTN_BORDER,
TO_GAP_BTN,
TO_GAP_BTN_DISMISS,
TO_MARGIN_BTN_RIGHT,
TO_PADDING_BTN,
TO_MARGIN_DETAILS,
};
static const struct option opts[] = {
@ -1566,6 +1592,7 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
{"message", required_argument, NULL, 'm'},
{"output", required_argument, NULL, 'o'},
{"timeout", required_argument, NULL, 't'},
{"exclusive-zone", no_argument, NULL, 'x'},
{"version", no_argument, NULL, 'v'},
{"background-color", required_argument, NULL, TO_COLOR_BACKGROUND},
@ -1576,8 +1603,10 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
{"button-text-color", required_argument, NULL, TO_COLOR_BUTTON_TEXT},
{"border-bottom-size", required_argument, NULL, TO_THICK_BAR_BORDER},
{"message-padding", required_argument, NULL, TO_PADDING_MESSAGE},
{"details-border-size", required_argument, NULL, TO_THICK_DET_BORDER},
{"details-border-size", required_argument, NULL, TO_THICK_DETAILS_BORDER},
{"details-background-color", required_argument, NULL, TO_COLOR_DETAILS},
{"details-border-color", required_argument, NULL, TO_COLOR_DETAILS_BORDER},
{"details-margin", required_argument, NULL, TO_MARGIN_DETAILS},
{"button-border-size", required_argument, NULL, TO_THICK_BTN_BORDER},
{"button-gap", required_argument, NULL, TO_GAP_BTN},
{"button-dismiss-gap", required_argument, NULL, TO_GAP_BTN_DISMISS},
@ -1622,6 +1651,9 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
" --details-border-size size Thickness for the details border.\n"
" --details-background-color RRGGBB[AA]\n"
" Details background color.\n"
" --details-border-color RRGGBB[AA]\n"
" Details border color.\n"
" --details-margin margin Margin for the details.\n"
" --button-border-size size Thickness for the button border.\n"
" --button-gap gap Size of the gap between buttons\n"
" --button-dismiss-gap gap Size of the gap for dismiss button.\n"
@ -1758,6 +1790,11 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
fprintf(stderr, "Invalid details background color: %s\n", optarg);
}
break;
case TO_COLOR_DETAILS_BORDER:
if (!parse_color(optarg, &conf->details_border_color)) {
fprintf(stderr, "Invalid details border color: %s\n", optarg);
}
break;
case TO_COLOR_TEXT: /* Text color */
if (!parse_color(optarg, &conf->text)) {
fprintf(stderr, "Invalid text color: %s\n", optarg);
@ -1774,7 +1811,7 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
case TO_PADDING_MESSAGE: /* Message padding */
conf->message_padding = strtol(optarg, NULL, 0);
break;
case TO_THICK_DET_BORDER: /* Details border thickness */
case TO_THICK_DETAILS_BORDER: /* Details border thickness */
conf->details_border_thickness = strtol(optarg, NULL, 0);
break;
case TO_THICK_BTN_BORDER: /* Button border thickness */
@ -1792,6 +1829,9 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
case TO_PADDING_BTN: /* Padding for the button text */
conf->button_padding = strtol(optarg, NULL, 0);
break;
case TO_MARGIN_DETAILS:
conf->details_margin = strtol(optarg, NULL, 0);
break;
default: /* Help or unknown flag */
fprintf(c == 'h' ? stdout : stderr, "%s", usage);
return LAB_EXIT_FAILURE;

View file

@ -1,59 +1,61 @@
wayland_client = dependency('wayland-client')
wayland_cursor = dependency('wayland-cursor')
if get_option('labnag').allowed()
wayland_client = dependency('wayland-client')
wayland_cursor = dependency('wayland-cursor')
nag_sources = files(
'labnag.c',
'pool-buffer.c',
)
wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
protocols = [
wl_protocol_dir / 'stable/tablet/tablet-v2.xml',
wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
'../protocols/wlr-layer-shell-unstable-v1.xml',
]
foreach xml : protocols
nag_sources += custom_target(
xml.underscorify() + '_c',
input: xml,
output: '@BASENAME@-protocol.c',
command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
nag_sources = files(
'labnag.c',
'pool-buffer.c',
)
nag_sources += custom_target(
xml.underscorify() + '_client_h',
input: xml,
output: '@BASENAME@-client-protocol.h',
command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
)
endforeach
if host_machine.system() in ['freebsd', 'openbsd']
# For signalfd()
epoll_dep = dependency('epoll-shim')
else
epoll_dep = []
wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
protocols = [
wl_protocol_dir / 'stable/tablet/tablet-v2.xml',
wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
'../protocols/wlr-layer-shell-unstable-v1.xml',
]
foreach xml : protocols
nag_sources += custom_target(
xml.underscorify() + '_c',
input: xml,
output: '@BASENAME@-protocol.c',
command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
)
nag_sources += custom_target(
xml.underscorify() + '_client_h',
input: xml,
output: '@BASENAME@-client-protocol.h',
command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
)
endforeach
if host_machine.system() in ['freebsd', 'openbsd']
# For signalfd()
epoll_dep = dependency('epoll-shim')
else
epoll_dep = []
endif
executable(
'labnag',
nag_sources,
dependencies: [
cairo,
pangocairo,
glib,
wayland_client,
wayland_cursor,
wlroots,
server_protos,
epoll_dep,
xkbcommon,
],
include_directories: [labwc_inc],
install: true,
)
endif
executable(
'labnag',
nag_sources,
dependencies: [
cairo,
pangocairo,
glib,
wayland_client,
wayland_cursor,
wlroots,
server_protos,
epoll_dep,
xkbcommon,
],
include_directories: [labwc_inc],
install: true,
)
clients = files('lab-sensible-terminal')
install_data(clients, install_dir: get_option('bindir'))

View file

@ -0,0 +1,6 @@
[Unit]
Description=labwc session
Documentation=man:labwc(1) man:systemd.special(7)
BindsTo=graphical-session.target
Wants=graphical-session-pre.target
After=graphical-session-pre.target

View file

@ -1,10 +1,18 @@
# Example autostart file
# When running under systemd, uncomment the systemctl line below to pull in
# graphical-session.target via labwc-session.target. This lets systemd user
# services declaring WantedBy=graphical-session.target (panels, portals,
# notification daemons, etc.) start in sync with the labwc session. Enable
# individual services with: systemctl --user enable <unit>
#
# systemctl --user --no-block start labwc-session.target
# Set background color.
swaybg -c '#113344' >/dev/null 2>&1 &
# Configure output directives such as mode, position, scale and transform.
# Use wlr-randr to get your output names
# Use wlr-randr to get your output names.
# Example ~/.config/kanshi/config below:
# profile {
# output HDMI-A-1 position 1366,0

View file

@ -95,6 +95,12 @@ _labnag_ [options...]
*--details-border-size* <size>
Set the thickness for the details border.
*--details-border-color* <RRGGBB[AA]>
Set the color of the details border.
*--details-margin* <margin>
Set the margin for the details.
*--button-border-size* <size>
Set the thickness for the button border.

View file

@ -16,7 +16,7 @@ Actions are used in menus and keyboard/mouse bindings.
SIGTERM signal.
*<action name="Execute" command="value" />*
Execute command. Note that in the interest of backward compatibility,
Execute command. Note that in the interest of backward compatibility,
labwc supports <execute> as an alternative to <command> even though
openbox documentation states that it is deprecated.
Note: Tilde (~) is expanded in the command before passing to execvp().
@ -126,30 +126,34 @@ Actions are used in menus and keyboard/mouse bindings.
position if it had been maximized or tiled to a direction or region.
*<action name="NextWindow" workspace="current" output="all" identifier="all" />*++
*<action name="PreviousWindow" workspace="current" output="all" identifier="all" />*
*<action name="PreviousWindow" workspace="current" output="all" identifier="all" />*++
*<action name="NextWindowImmediate" workspace="current" output="all" identifier="all" />*++
*<action name="PreviousWindowImmediate" workspace="current" output="all" identifier="all" />*++
Cycle focus to next/previous window, respectively.
Default keybinds for NextWindow and PreviousWindow are Alt-Tab and
Shift-Alt-Tab. While cycling through windows, the arrow keys move the
selected window forwards/backwards and the escape key halts the cycling.
NextWindowImmediate and PreviousWindowImmediate skip the Window Switcher
and OSD, useful for binding to keys without modifiers.
*workspace* [all|current]
This determines whether to cycle through windows on all workspaces or the
current workspace. Default is "current".
This determines whether to cycle through windows on all workspaces or
the current workspace. Default is "current".
*output* [all|focused|cursor]
This determines whether to cycle through windows on all outputs, the focused
output, or the output under the cursor. Default is "all".
This determines whether to cycle through windows on all outputs, the
focused output, or the output under the cursor. Default is "all".
*identifier* [all|current]
This determines whether to cycle through all windows or only windows of the
same application as the currently focused window. Default is "all".
This determines whether to cycle through all windows or only windows of
the same application as the currently focused window. Default is "all".
*<action name="Reconfigure" />*
Re-load configuration and theme files.
*<action name="ShowMenu" menu="root-menu"/>*
*<action name="ShowMenu" menu="root-menu" />*
Show a menu.
```
@ -295,7 +299,7 @@ Actions are used in menus and keyboard/mouse bindings.
(if one exists).
*wrap* [yes|no] When using the direction attribute, wrap around from
right-to-left or top-to-bottom, and vice versa. Default no.
right-to-left or top-to-bottom, and vice versa. Default is no.
*<action name="FitToOutput" />*
Resizes active window size to width and height of the output when the
@ -309,10 +313,10 @@ Actions are used in menus and keyboard/mouse bindings.
workspace or its index (starting at 1) as configured in rc.xml.
*wrap* [yes|no] Wrap around from last desktop to first, and vice
versa. Default yes.
versa. Default is yes.
*toggle* [yes|no] Toggle to “last” if already on the workspace that
would be the actual destination. Default no.
would be the actual destination. Default is no.
*<action name="SendToDesktop" to="value" follow="yes" wrap="yes" />*
Send active window to workspace.
@ -320,10 +324,11 @@ Actions are used in menus and keyboard/mouse bindings.
*to* The workspace to send the window to. Supported values are the same
as for GoToDesktop.
*follow* [yes|no] Also switch to the specified workspace. Default yes.
*follow* [yes|no] Also switch to the specified workspace.
Default is yes.
*wrap* [yes|no] Wrap around from last desktop to first, and vice
versa. Default yes.
versa. Default is yes.
*<action name="VirtualOutputAdd" output_name="value" />*
Add virtual output (headless backend).
@ -341,11 +346,11 @@ Actions are used in menus and keyboard/mouse bindings.
```
<keybind key="W-v">
<action name="VirtualOutputAdd" output_name="ScreenCasting"/>
<action name="Execute" command='sh -c "wlr-randr --output ScreenCasting --pos 0,0 --scale 2 --custom-mode 3840x2110; wlr-randr --output eDP-1 --pos 0,0 --scale 2 --mode 3840x2160"'/>
<action name="VirtualOutputAdd" output_name="ScreenCasting" />
<action name="Execute" command='sh -c "wlr-randr --output ScreenCasting --pos 0,0 --scale 2 --custom-mode 3840x2110; wlr-randr --output eDP-1 --pos 0,0 --scale 2 --mode 3840x2160"' />
</keybind>
<keybind key="W-c">
<action name="VirtualOutputRemove"/>
<action name="VirtualOutputRemove" />
</keybind>
```
@ -366,7 +371,7 @@ Actions are used in menus and keyboard/mouse bindings.
*output_name* The name of virtual output. If not supplied, will remove
the last virtual output added.
*<action name="AutoPlace" policy="value"/>*
*<action name="AutoPlace" policy="value" />*
Reposition the window according to the desired placement policy.
*policy* [automatic|cursor|center|cascade] Use the specified policy,
@ -424,6 +429,13 @@ Actions are used in menus and keyboard/mouse bindings.
Toggle the screen magnifier on or off at the last magnification level
used.
*<action name="ToggleShowDesktop" />*
Minimize all windows in the current workspace so that the desktop
becomes visible. On calling the action again the hidden windows are
unminimized, provided that - since the initial `ShowDesktop` - (a) no
windows have been unminimized; (b) workspaces have not been switched;
and (c) no new applications have been started.
*<action name="ZoomIn">*++
*<action name="ZoomOut">*
Increase or decrease the magnification level for the screen magnifier.
@ -436,6 +448,10 @@ Actions are used in menus and keyboard/mouse bindings.
If used as the only action for a binding: clear an earlier defined
binding.
*<action name="DebugToggleKeyStateIndicator" />*
Toggle visibility of key-state on-screen display (OSD). Note: This is
for debugging purposes only.
# CONDITIONAL ACTIONS
Actions that execute other actions. Used in keyboard/mouse bindings.
@ -448,10 +464,10 @@ Actions that execute other actions. Used in keyboard/mouse bindings.
```
<action name="If">
<query/>
<prompt message=""/>
<then><action/></then>
<else><action/></else>
<query />
<prompt message="" />
<then><action /></then>
<else><action /></else>
</action>
```
@ -503,7 +519,7 @@ Actions that execute other actions. Used in keyboard/mouse bindings.
"right-occupied" directions will not wrap.
*tiled* [up|right|down|left|up-left|up-right|down-left|down-right|center|any]
Whether the client is tiled (snapped) along the the
Whether the client is tiled (snapped) along the
indicated screen edge.
*tiled_region*
@ -533,9 +549,9 @@ Actions that execute other actions. Used in keyboard/mouse bindings.
```
<keybind key="W-q">
<action name="If">
<prompt message="Quit?"/>
<prompt message="Quit?" />
<then>
<action name="Exit"/>
<action name="Exit" />
</then>
</action>
</keybind>

View file

@ -25,7 +25,7 @@ The XDG Base Directory Specification does not specify whether or not programs
should (a) allow the first-identified configuration file to supersede any
others, or (b) define rules for merging the information from more than one file.
By default, labwc uses option (a), reading only the first file identified. With
By default, labwc uses option (a), reading only the first file identified. With
the --merge-config option, the search order is reversed, but every configuration
file encountered is processed in turn. Thus, user-specific files will augment
system-wide configurations, with conflicts favoring the user-specific
@ -175,6 +175,7 @@ this is for compatibility with Openbox.
<adaptiveSync>no</adaptiveSync>
<allowTearing>no</allowTearing>
<autoEnableOutputs>yes</autoEnableOutputs>
<hdr>no</hdr>
<reuseOutputMode>no</reuseOutputMode>
<xwaylandPersistence>no</xwaylandPersistence>
<primarySelection>yes</primarySelection>
@ -240,6 +241,12 @@ this is for compatibility with Openbox.
'pkill kanshi ; wlopm --off \*' resume 'kanshi & wlopm --on \*'
```
*<core><hdr>* [yes|no]
Automatically enable HDR support on outputs when configuring them,
where supported by the particular output device and display. Default
is no. Additionally requires the Vulkan backend. Can be set
via `WLR_RENDERER=vulkan` in `~/.config/labwc/environment`.
*<core><reuseOutputMode>* [yes|no]
Try to re-use the existing output mode (resolution / refresh rate).
This may prevent unnecessary screenblank delays when starting labwc
@ -493,6 +500,13 @@ this is for compatibility with Openbox.
*<focus><raiseOnFocus>* [yes|no]
Raise window to top when focused. Default is no.
*<focus><raiseOnFocusDelay>* [milliseconds]
When raiseOnFocus is enabled, delay the actual raise by this many
milliseconds. Default is 0 (raise immediately). A subsequent focus
change before the timer elapses restarts or cancels the pending raise.
Useful together with followMouse to avoid brief passes of the cursor
stacking up z-order changes.
## WINDOW SNAPPING
Windows may be "snapped" to an edge or user-defined region of an output when
@ -512,7 +526,8 @@ extending outward from the snapped edge.
*<range><inner>* and *<range><outer>*, and 50 for *<cornerRange>*.
*<snapping><overlay><enabled>* [yes|no]
Show an overlay when snapping a window to an output edge. Default is yes.
Show an overlay when snapping a window to an output edge.
Default is yes.
*<snapping><overlay><delay><inner>*++
*<snapping><overlay><delay><outer>*
@ -581,7 +596,7 @@ extending outward from the snapped edge.
A setting of 0 disables the OSD. Default is 1000 ms.
*<desktops><prefix>*
Set the prefix to use when using "number" above. Default is "Workspace"
Set the prefix to use when using "number" above. Default is "Workspace".
## THEME
@ -735,7 +750,7 @@ generate gesture events, like swipe and pinch. There are some related settings
(e.g. *threeFingerDrag* and *twoFingerScroll*) in the *<libinput>* section.
In the Wayland Compositor domain, events associated with touchscreens are
sometimes simply referred to as *touch* events. Touchscreens can be configured
sometimes simply referred to as *touch* events. Touchscreens can be configured
in both the *<touch>* and *<libinput>* sections. Note that touchscreen gestures
are not interpreted by libinput, nor labwc. Any touch point is passed to the
client (application) for any interpretation of gestures.
@ -760,7 +775,8 @@ References:
Stores the keyboard layout either globally or per window and restores
it when switching back to the window. Default is global.
*<keyboard><keybind key="" layoutDependent="" onRelease="" allowWhenLocked="">*
*<keyboard><keybind key="" layoutDependent="" onRelease="" allowWhenLocked=""
overrideInhibition="">*
Define a *key* binding in the format *modifier-key*, where supported
modifiers are:
- S (shift)
@ -808,6 +824,12 @@ References:
*allowWhenLocked* [yes|no]
Make this keybind work even if the screen is locked. Default is no.
*overrideInhibition* [yes|no]
Make this keybind work even if the view inhibits keybinds.
This can be used to prevent W-Tab and similar keybinds from being
delivered to Virtual Machines, VNC clients or nested compositors.
Default is no.
*onRelease* [yes|no]
When yes, fires the keybind action when the key or key
combination is released, rather than first pressed. This is useful to
@ -820,7 +842,7 @@ References:
```
<keybind key="Super_L" onRelease="yes">
<action name="Execute" command="rofi -show drun"/>
<action name="Execute" command="rofi -show drun" />
</keybind>
```
@ -841,11 +863,12 @@ References:
W-Return - lab-sensible-terminal
A-F4 - close window
W-a - toggle maximize
W-d - toggle show-desktop
W-<arrow> - resize window to fill half or quarter of the output
A-Space - show window menu
```
Audio and MonBrightness keys are also bound to amixer and
Audio and MonBrightness keys are also bound to pactl and
brightnessctl, respectively.
*<keyboard><repeatRate>*
@ -880,7 +903,7 @@ input-devices by the Wayland protocol.
- Shade: A button that, by default, toggles window shading.
- AllDesktops: A button that, by default, toggles omnipresence of a
window.
- Close: A button that, by default, closses a window.
- Close: A button that, by default, closes a window.
- Border: The window's border including Top...BRCorner below.
- Top: The top edge of the window's border.
- Bottom: The bottom edge of the window's border.
@ -954,10 +977,10 @@ input-devices by the Wayland protocol.
```
<mouse>
<default/>
<default />
<context name="Frame">
<mousebind button="W-Left" action="Press"/>
<mousebind button="W-Left" action="Drag"/>
<mousebind button="W-Left" action="Press" />
<mousebind button="W-Left" action="Drag" />
</context>
</mouse>
```
@ -965,7 +988,7 @@ input-devices by the Wayland protocol.
*<mouse><default />*
Load default mousebinds. This is an addition to the openbox
specification and provides a way to keep config files simpler whilst
allowing user specific binds. Note that if no rc.xml is found, or if no
allowing user specific binds. Note that if no rc.xml is found, or if no
<mouse><mousebind> entries exist, the same default mousebinds will be
loaded even if the <default /> element is not provided.
@ -977,7 +1000,7 @@ Note: To rotate touch events with output rotation, use the libinput
*calibrationMatrix* setting.
```
<touch deviceName="" mapToOutput="" mouseEmulation="no"/>
<touch deviceName="" mapToOutput="" mouseEmulation="no" />
```
*<touch deviceName="" />*
@ -1090,7 +1113,8 @@ Note: To rotate touch events with output rotation, use the libinput
## TABLET TOOL
```
<tabletTool motion="absolute" relativeMotionSensitivity="1" />
<tabletTool motion="absolute" relativeMotionSensitivity="1.0"
minPressure="0.0" maxPressure="1.0" />
```
*<tabletTool motion="">* [absolute|relative]
@ -1105,6 +1129,16 @@ Note: To rotate touch events with output rotation, use the libinput
speed, using a value greater than 1.0 increases the speed of the
cursor. The default is "1.0".
*<tabletTool minPressure="">*
*<tabletTool maxPressure="">*
The pressure range of a tablet tool can be controlled by adjusting
*minPressure* and *maxPressure*. Setting the minimum pressure to
a value greater than zero requires more pressure for the tip
threshold, setting the maximum pressure to a value less than 1.0
requires less pressure for the user before the maximum is reached.
The default is 0 for the minimum pressure and 1.0 for the maximum
pressure.
## LIBINPUT
```
@ -1123,6 +1157,7 @@ Note: To rotate touch events with output rotation, use the libinput
<disableWhileTyping></disableWhileTyping>
<clickMethod></clickMethod>
<scrollMethod></scrollMethod>
<scrollButton></scrollButton>
<sendEventsMode></sendEventsMode>
<calibrationMatrix></calibrationMatrix>
<scrollFactor>1.0</scrollFactor>
@ -1227,19 +1262,26 @@ Note: To rotate touch events with output rotation, use the libinput
The default method depends on the touchpad hardware.
*<libinput><device><scrollMethod>* [none|twofinger|edge]
Configure the method by which physical movements on a touchpad are
mapped to scroll events.
*<libinput><device><scrollMethod>* [none|twofinger|edge|onbutton]
Configure the method by which physical movements are mapped to scroll
events.
The scroll methods available are:
- *twofinger* - Scroll by two fingers being placed on the surface of the
touchpad, then moving those fingers vertically or horizontally.
- *edge* - Scroll by moving a single finger along the right edge
(vertical scroll) or bottom edge (horizontal scroll).
- *onbutton* - Scroll by pressing a button.
- *none* - No scroll events will be produced.
The default method depends on the touchpad hardware.
*<libinput><device><scrollButton>* [button]
Set the button used for the *onbutton* scroll method.
*button* is the decimal form of a value
from `linux/input-event-codes.h`.
*<libinput><device><sendEventsMode>* [yes|no|disabledOnExternalMouse]
Optionally enable or disable sending any device events.
@ -1287,7 +1329,7 @@ defined as shown below.
<!-- Action -->
<windowRule identifier="" title="" type="">
<action name=""/>
<action name="" />
</windowRule>
<!-- Property -->
@ -1354,6 +1396,13 @@ situation.
*ignoreConfigureRequest* prevents a X11 window to position and size
itself.
*<windowRules><windowRule allowAlwaysOnTop="">* [yes|no|default]
*allowAlwaysOnTop* allows a X11 window to control its always-on-top
state ('above' in X11 terms).
Note: X11 window always-on-top requests are disallowed by default.
This window rule offers a means of allowing it.
*<windowRules><windowRule fixedPosition="">* [yes|no|default]
*fixedPosition* disallows interactive move/resize and prevents
re-positioning in response to changes in reserved output space, which
@ -1427,11 +1476,73 @@ situation.
Whether to apply a bilinear filter to the magnified image, or
just to use nearest-neighbour. Default is true - bilinear filtered.
## PRIVILEGED INTERFACES
Labwc supports a small set of privileged wayland interfaces. All of these
interfaces are enabled by default for applications unless they are running
via a sandbox environment supporting the security-context-v1 protocol.
Security conscious users are suggested to use a sandbox framework to run
potentially untrusted applications as it additionally limits access to the
filesystem (including labwc configuration) and other services like dbus.
In addition to that, privileged protocols can be restricted for non-sandboxed
clients by defining a `<privilegedInterfaces>` block:
```
<privilegedInterfaces>
<allow>zwlr_layer_shell_v1</allow>
<allow>zwlr_virtual_pointer_manager_v1</allow>
</privilegedInterfaces>
```
*<privilegedInterfaces><allow>*
Name of the interface that should be allowed.
This is the full list of interfaces that can be controlled with this mechanism:
- `wp_drm_lease_device_v1`
- `zwlr_gamma_control_manager_v1`
- `zwlr_output_manager_v1`
- `zwlr_output_power_manager_v1`
- `zwp_input_method_manager_v2`
- `zwlr_virtual_pointer_manager_v1`
- `zwp_virtual_keyboard_manager_v1`
- `zwlr_export_dmabuf_manager_v1`
- `zwlr_screencopy_manager_v1`
- `ext_data_control_manager_v1`
- `zwlr_data_control_manager_v1`
- `wp_security_context_manager_v1`
- `ext_idle_notifier_v1`
- `zwlr_foreign_toplevel_manager_v1`
- `ext_foreign_toplevel_list_v1`
- `ext_session_lock_manager_v1`
- `zwlr_layer_shell_v1`
- `ext_workspace_manager_v1`
- `ext_image_copy_capture_manager_v1`
- `ext_output_image_capture_source_manager_v1`
## ENVIRONMENT VARIABLES
*XCURSOR_THEME* and *XCURSOR_SIZE* are supported to set cursor theme
and size respectively. The default size is 24. System cursor themes can
typically be found with a command such as:
*XCURSOR_PATH*
Specify a colon-separated list of paths to look for mouse cursors in.
Default is
~/.local/share/icons:
~/.icons:
/usr/share/icons:
/usr/share/pixmaps:
~/.cursors:
/usr/share/cursors/xorg-x11:
/usr/X11R6/lib/X11/icons:
*XCURSOR_SIZE*
Specify an alternative mouse cursor size in pixels. Requires
XCURSOR_THEME to be set also. Default is 24.
*XCURSOR_THEME*
Specify a mouse cursor theme within XCURSOR_PATH.
System cursor themes can typically be found with a command such as:
```
find /usr/share/icons/ -type d -name "cursors"

View file

@ -12,7 +12,7 @@ Static menus are built based on the menu.xml file located at
# SYNTAX
The menu file must be entirely enclosed within <openbox_menu> and
</openbox_menu> tags. Inside these tags, menus are specified as follows:
</openbox_menu> tags. Inside these tags, menus are specified as follows:
```
<!-- A toplevel menu -->
@ -111,7 +111,7 @@ Pipe menus are menus generated dynamically based on output of scripts or
binaries. They are so-called because the output of the executable is piped to
the labwc menu.
For any *<menu id="" label="" execute="COMMAND"/>* entry in menu.xml, the
For any *<menu id="" label="" execute="COMMAND" />* entry in menu.xml, the
COMMAND will be executed the first time the item is selected (for example by
cursor or keyboard input). The XML output of the command will be parsed and
shown as a submenu. The content of pipemenus is cached until the whole menu
@ -124,7 +124,7 @@ menus, for example:
```
<openbox_pipe_menu>
<item label="Terminal">
<action name="Execute" command="xterm"/>
<action name="Execute" command="xterm" />
</item>
</openbox_pipe_menu>
```
@ -144,10 +144,25 @@ obmenu-generator with the menu generator of your choice):
```
<?xml version="1.0"?>
<openbox_menu>
<menu id="root-menu" label="" execute="obmenu-generator"/>
<menu id="root-menu" label="" execute="obmenu-generator" />
</openbox_menu>
```
# ACCELERATORS / MNEMONICS
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. Accelerators can be any unicode character and are not limited to
ASCII. A usual underscore can be shown by duplicating it.
If the menu only contains a single instance of the pressed accelerator the item
will be executed directly. Otherwise, all matching items are cycled through.
Example:
The accelerator for an item with the label "e_Macs" is 'm'.
# LOCALISATION
Available localisation for the default "client-menu" is only shown if no

View file

@ -135,7 +135,7 @@ window.*.title.bg.colorTo.splitTo: #557485
*window.active.title.bg*
Texture for the focused window's titlebar. See texture section above.
Default is *Solid*
Default is *Solid*.
*window.active.title.bg.color*
Background color for the focused window's titlebar. See texture section
@ -144,7 +144,7 @@ window.*.title.bg.colorTo.splitTo: #557485
*window.inactive.title.bg*
Texture for non-focused windows' titlebars. See texture section above.
Default is *Solid*
Default is *Solid*.
*window.inactive.title.bg.color*
Background color for non-focused windows' titlebars. See texture section
@ -470,7 +470,7 @@ all are supported.
Width of magnifier window border in pixels. Default is 1.
*magnifier.border.color*
Color of the magnfier window border. Default is #ff0000 (red).
Color of the magnifier window border. Default is #ff0000 (red).
# BUTTONS

View file

@ -66,10 +66,18 @@ the `--exit` and `--reconfigure` options use.
Manager, or the Window Manager can be launched independently first. On
Wayland, the Compositor is both Display Server and Window Manager, so
the described session management mechanisms do not work because the
Compositor needs to be running before the session can function. As some
Compositor needs to be running before the session can function. As some
session clients support both X11 and Wayland, this command line option
avoids re-writes and fragmentation.
*-t, --title* <fmtstr>
Set the window title for labwc to use when it is running in a window
(i.e. nested in a compositor). <fmtstr> is a format string to be used as
the window title, replacing `%o` with the name of the output
region. This is useful when simulating multiple screens, such as with
running labwc with the environment variable `WLR_WL_OUTPUTS=2`. In this
case, `%o` will be unique per simulated screen.
*-v, --version*
Show the version number and quit
@ -118,6 +126,25 @@ this is accomplished by setting the session variables to empty strings. For
systemd, the command `systemctl --user unset-environment` will be invoked to
actually remove the variables from the activation environment.
A systemd user unit named `labwc-session.target` is also shipped alongside
the compositor for users who want to integrate labwc with systemd. It binds
to the standard `graphical-session.target`, so systemd user services can
start and stop in sync with the labwc session when they declare a WantedBy
or PartOf relationship to that target. Labwc does not activate the target
itself; users opt in by adding lines like the following to their
*autostart* and *shutdown* files:
```
systemctl --user --no-block start labwc-session.target
systemctl --user stop graphical-session.target
```
The example *autostart* and *shutdown* files shipped with labwc include
these commented out. To have a user service automatically started with
the session, enable it so the corresponding symlink under the
graphical-session.target.wants directory exists, for example by running
"systemctl --user enable dms.service".
# ENVIRONMENT VARIABLES
Set the environment variables listed below to enable specific debug options.
@ -141,7 +168,7 @@ example: *LABWC_DEBUG_FOO=1 labwc*.
*LABWC_DEBUG_KEY_STATE*
Enable logging of press and release events for bound keys (generally
key-combinations like *Ctrl-Alt-t*)
key-combinations like *Ctrl-Alt-t*).
# SEE ALSO

View file

@ -23,7 +23,7 @@
Any menu with the id "workspaces" will be hidden
if there is only a single workspace available.
-->
<menu id="client-send-to-menu"/>
<menu id="client-send-to-menu" />
<!--
openbox default workspace selector
to use replace above workspace menu with the example below
@ -56,9 +56,9 @@
# A prompt can be used as follows:
<item label="Exit">
<action name="If">
<prompt message="Do you really want to exit the compositor?"/>
<prompt message="Do you really want to exit the compositor?" />
<then>
<action name="Exit"/>
<action name="Exit" />
</then>
</action>
</item>

View file

@ -13,6 +13,7 @@
<adaptiveSync>no</adaptiveSync>
<allowTearing>no</allowTearing>
<autoEnableOutputs>yes</autoEnableOutputs>
<hdr>no</hdr>
<reuseOutputMode>no</reuseOutputMode>
<xwaylandPersistence>no</xwaylandPersistence>
<primarySelection>yes</primarySelection>
@ -158,6 +159,8 @@
<followMouse>no</followMouse>
<followMouseRequiresMovement>yes</followMouseRequiresMovement>
<raiseOnFocus>no</raiseOnFocus>
<!-- Delay (ms) before applying raise-on-focus. 0 = immediate. -->
<raiseOnFocusDelay>0</raiseOnFocusDelay>
</focus>
<snapping>
@ -276,6 +279,9 @@
<keybind key="W-a">
<action name="ToggleMaximize" />
</keybind>
<keybind key="W-d">
<action name="ToggleShowDesktop" />
</keybind>
<keybind key="W-Left">
<action name="SnapToEdge" direction="left" combine="yes" />
</keybind>
@ -291,19 +297,19 @@
<keybind key="A-Space">
<action name="ShowMenu" menu="client-menu" atCursor="no" />
</keybind>
<keybind key="XF86_AudioLowerVolume">
<action name="Execute" command="amixer sset Master 5%-" />
<keybind key="XF86AudioLowerVolume">
<action name="Execute" command="pactl set-sink-volume @DEFAULT_SINK@ -5%" />
</keybind>
<keybind key="XF86_AudioRaiseVolume">
<action name="Execute" command="amixer sset Master 5%+" />
<keybind key="XF86AudioRaiseVolume">
<action name="Execute" command="pactl set-sink-volume @DEFAULT_SINK@ +5%" />
</keybind>
<keybind key="XF86_AudioMute">
<action name="Execute" command="amixer sset Master toggle" />
<keybind key="XF86AudioMute">
<action name="Execute" command="pactl set-sink-mute @DEFAULT_SINK@ toggle" />
</keybind>
<keybind key="XF86_MonBrightnessUp">
<keybind key="XF86MonBrightnessUp">
<action name="Execute" command="brightnessctl set +10%" />
</keybind>
<keybind key="XF86_MonBrightnessDown">
<keybind key="XF86MonBrightnessDown">
<action name="Execute" command="brightnessctl set 10%-" />
</keybind>
<!-- SnapToRegion via W-Numpad -->
@ -573,8 +579,11 @@
*relativeMotionSensitivity* controls the speed of the cursor. Using
a value lower than 1.0 decreases the speed, using a value greater than
1.0 increases the speed of the cursor.
The pressure range of a tablet tool can be controlled by adjusting
*minPressure* and *maxPressure*.
-->
<tabletTool motion="absolute" relativeMotionSensitivity="1.0" />
<tabletTool motion="absolute" relativeMotionSensitivity="1.0"
minPressure="0.0" maxPressure="1.0" />
<!--
The *category* attribute is optional and can be set to touch, touchpad,
@ -589,7 +598,7 @@
- accelProfile [flat|adaptive]
- tapButtonMap [lrm|lmr]
- clickMethod [none|buttonAreas|clickfinger]
- scrollMethod [twoFinger|edge|none]
- scrollMethod [twoFinger|edge|onbutton|none]
- sendEventsMode [yes|no|disabledOnExternalMouse]
- calibrationMatrix [six float values split by space]
- scrollFactor [float]
@ -615,6 +624,7 @@
<!-- <disableWhileTyping>yes</disableWhileTyping> -->
<!-- <clickMethod>buttonAreas</clickMethod> -->
<!-- <scrollMethod>twofinger</scrollMethod> -->
<!-- <scrollButton>274</scrollButton> -->
<!-- <sendEventsMode>yes</sendEventsMode> -->
<!-- <calibrationMatrix>1 0 0 0 1 0</calibrationMatrix> -->
<scrollFactor>1.0</scrollFactor>
@ -635,10 +645,10 @@
# string and '?' matches any single character.
<windowRules>
<windowRule identifier="*"><action name="Maximize"/></windowRule>
<windowRule identifier="foo" serverDecoration="yes"/>
<windowRule title="bar" serverDecoration="yes"/>
<windowRule identifier="baz" title="quax" serverDecoration="yes"/>
<windowRule identifier="*"><action name="Maximize" /></windowRule>
<windowRule identifier="foo" serverDecoration="yes" />
<windowRule title="bar" serverDecoration="yes" />
<windowRule identifier="baz" title="quax" serverDecoration="yes" />
</windowRules>
# Example below for `lxqt-panel` and `pcmanfm-qt \-\-desktop`
@ -649,18 +659,18 @@
<windowRule identifier="lxqt-panel" matchOnce="true">
<skipTaskbar>yes</skipTaskbar>
<action name="MoveTo" x="0" y="0" />
<action name="ToggleAlwaysOnTop"/>
<action name="ToggleAlwaysOnTop" />
</windowRule>
<windowRule title="pcmanfm-desktop*">
<skipTaskbar>yes</skipTaskbar>
<skipWindowSwitcher>yes</skipWindowSwitcher>
<fixedPosition>yes</fixedPosition>
<action name="MoveTo" x="0" y="0" />
<action name="ToggleAlwaysOnBottom"/>
<action name="ToggleAlwaysOnBottom" />
</windowRule>
<windowRule identifier="org.qutebrowser.qutebrowser">
<action name="ResizeTo" width="1024" height="800" />
<action name="AutoPlace"/>
<action name="AutoPlace" />
</windowRule>
</windowRules>
-->

View file

@ -3,3 +3,11 @@
# This file is executed as a shell script when labwc is preparing to terminate
# itself.
# For further details see labwc-config(5).
# When running under systemd, uncomment the systemctl line below to tear down
# graphical-session.target (which cascades to labwc-session.target via
# BindsTo, and to any service declaring PartOf=graphical-session.target).
# Running synchronously here ensures those services are stopped before the
# Wayland socket goes away, avoiding "Broken pipe" failures on teardown.
#
# systemctl --user stop graphical-session.target

View file

@ -47,8 +47,7 @@ bool actions_contain_toggle_keybinds(struct wl_list *action_list);
* direction of resize or the position of the window menu button for ShowMenu
* action.
*/
void actions_run(struct view *activator, struct server *server,
struct wl_list *actions, struct cursor_context *ctx);
void actions_run(struct view *activator, struct wl_list *actions, struct cursor_context *ctx);
void action_prompts_destroy(void);
bool action_check_prompt_result(pid_t pid, int exit_code);

View file

@ -4,6 +4,7 @@
#include <cairo.h>
#include <pango/pango-font.h>
#include <stdbool.h>
struct lab_data_buffer;
@ -43,10 +44,11 @@ void font_get_buffer_size(int max_width, const char *text, struct font *font,
* @font: font description
* @color: foreground color in rgba format
* @bg_pattern: background pattern
* @use_markup: flag to render pango markup
*/
void font_buffer_create(struct lab_data_buffer **buffer, int max_width,
int height, const char *text, struct font *font, const float *color,
cairo_pattern_t *bg_pattern, double scale);
cairo_pattern_t *bg_pattern, double scale, bool use_markup);
/**
* font_finish - free some font related resources

View file

@ -62,7 +62,11 @@
#define BOUNDED_INT(a) ((a) < INT_MAX && (a) > INT_MIN)
#endif
#define LAB_WLR_VERSION_AT_LEAST(major, minor, micro) \
(WLR_VERSION_NUM >= (((major) << 16) | ((minor) << 8) | (micro)))
#define _LAB_CALC_WLR_VERSION_NUM(major, minor, micro) (((major) << 16) | ((minor) << 8) | (micro))
#define LAB_WLR_VERSION_AT_LEAST(major, minor, micro) ( \
server.wlr_version >= _LAB_CALC_WLR_VERSION_NUM(major, minor, micro))
#define LAB_WLR_VERSION_LOWER(major, minor, micro) (!LAB_WLR_VERSION_AT_LEAST(major, minor, micro))
#endif /* LABWC_MACROS_H */

View file

@ -4,6 +4,7 @@
#include <stdbool.h>
struct wlr_buffer;
struct wlr_scene_node;
struct wlr_surface;
struct wlr_scene_output;
@ -11,6 +12,13 @@ struct wlr_output_state;
struct wlr_surface *lab_wlr_surface_from_node(struct wlr_scene_node *node);
/* variants of wlr_scene_*_create() functions that don't return NULL */
struct wlr_scene_tree *lab_wlr_scene_tree_create(struct wlr_scene_tree *parent);
struct wlr_scene_rect *lab_wlr_scene_rect_create(struct wlr_scene_tree *parent,
int width, int height, const float color[static 4]);
struct wlr_scene_buffer *lab_wlr_scene_buffer_create(
struct wlr_scene_tree *parent, struct wlr_buffer *buffer);
/**
* lab_get_prev_node - return previous (sibling) node
* @node: node to find the previous node from

View file

@ -16,6 +16,12 @@ pid_t spawn_primary_client(const char *command);
*/
void spawn_async_no_shell(char const *command);
/**
* spawn_sync_no_shell - execute synchronously
* @command: command to be executed
*/
void spawn_sync_no_shell(char const *command);
/**
* spawn_piped - execute asynchronously
* @command: command to be executed

View file

@ -28,6 +28,9 @@ static struct key_combos {
}, {
.binding = "W-a",
.action = "ToggleMaximize",
}, {
.binding = "W-d",
.action = "ToggleShowDesktop",
}, {
.binding = "W-Left",
.action = "SnapToEdge",
@ -84,35 +87,35 @@ static struct key_combos {
.value = "no",
},
}, {
.binding = "XF86_AudioLowerVolume",
.binding = "XF86AudioLowerVolume",
.action = "Execute",
.attributes[0] = {
.name = "command",
.value = "amixer sset Master 5%-",
.value = "pactl set-sink-volume @DEFAULT_SINK@ -5%",
},
}, {
.binding = "XF86_AudioRaiseVolume",
.binding = "XF86AudioRaiseVolume",
.action = "Execute",
.attributes[0] = {
.name = "command",
.value = "amixer sset Master 5%+",
.value = "pactl set-sink-volume @DEFAULT_SINK@ +5%",
},
}, {
.binding = "XF86_AudioMute",
.binding = "XF86AudioMute",
.action = "Execute",
.attributes[0] = {
.name = "command",
.value = "amixer sset Master toggle",
.value = "pactl set-sink-mute @DEFAULT_SINK@ toggle",
},
}, {
.binding = "XF86_MonBrightnessUp",
.binding = "XF86MonBrightnessUp",
.action = "Execute",
.attributes[0] = {
.name = "command",
.value = "brightnessctl set +10%",
},
}, {
.binding = "XF86_MonBrightnessDown",
.binding = "XF86MonBrightnessDown",
.action = "Execute",
.attributes[0] = {
.name = "command",
@ -141,14 +144,14 @@ static struct key_combos {
* <mouse>
* <context name="Maximize">
* <mousebind button="Left" action="Click">
* <action name="Focus"/>
* <action name="Raise"/>
* <action name="ToggleMaximize"/>
* <action name="Focus" />
* <action name="Raise" />
* <action name="ToggleMaximize" />
* </mousebind>
* </context>
* <context name="Root">
* <mousebind direction="Up" action="Scroll">
* <action name="GoToDesktop" to="left" wrap="yes"/>
* <action name="GoToDesktop" to="left" wrap="yes" />
* </mousebind>
* </context>
* </mouse>

View file

@ -23,6 +23,7 @@ struct keybind {
struct wl_list actions; /* struct action.link */
struct wl_list link; /* struct rcxml.keybinds */
bool on_release;
bool override_inhibition;
};
/**
@ -45,5 +46,5 @@ bool keybind_the_same(struct keybind *a, struct keybind *b);
bool keybind_contains_keycode(struct keybind *keybind, xkb_keycode_t keycode);
bool keybind_contains_keysym(struct keybind *keybind, xkb_keysym_t keysym);
void keybind_update_keycodes(struct server *server);
void keybind_update_keycodes(void);
#endif /* LABWC_KEYBIND_H */

View file

@ -31,6 +31,7 @@ struct libinput_category {
int dwt; /* -1 or libinput_config_dwt_state */
int click_method; /* -1 or libinput_config_click_method */
int scroll_method; /* -1 or libinput_config_scroll_method */
int scroll_button; /* -1 or a button from linux/input_event_codes.h */
int send_events_mode; /* -1 or libinput_config_send_events_mode */
bool have_calibration_matrix;
double scroll_factor;

View file

@ -36,6 +36,12 @@ enum tearing_mode {
LAB_TEARING_FULLSCREEN_FORCED,
};
enum render_bit_depth {
LAB_RENDER_BIT_DEPTH_DEFAULT = 0,
LAB_RENDER_BIT_DEPTH_8,
LAB_RENDER_BIT_DEPTH_10,
};
enum tiling_events_mode {
LAB_TILING_EVENTS_NEVER = 0,
LAB_TILING_EVENTS_REGION = 1 << 0,
@ -74,8 +80,10 @@ struct rcxml {
int gap;
enum adaptive_sync_mode adaptive_sync;
enum tearing_mode allow_tearing;
enum render_bit_depth target_render_depth;
bool auto_enable_outputs;
bool reuse_output_mode;
uint32_t allowed_interfaces;
bool xwayland_persistence;
bool primary_selection;
char *prompt_command;
@ -89,6 +97,7 @@ struct rcxml {
bool focus_follow_mouse;
bool focus_follow_mouse_requires_movement;
bool raise_on_focus;
uint32_t raise_on_focus_delay_ms;
/* theme */
char *theme_name;
@ -144,6 +153,8 @@ struct rcxml {
struct tablet_tool_config {
enum lab_motion motion;
double relative_motion_sensitivity;
double min_pressure;
double max_pressure;
} tablet_tool;
/* libinput */
@ -211,6 +222,7 @@ struct rcxml {
bool mag_filter;
};
/* defined in main.c */
extern struct rcxml rc;
void rcxml_read(const char *filename);
@ -222,4 +234,6 @@ void rcxml_finish(void);
*/
void append_parsed_actions(xmlNode *node, struct wl_list *list);
uint32_t parse_privileged_interface(const char *name);
#endif /* LABWC_RCXML_H */

View file

@ -21,12 +21,12 @@ void session_environment_init(void);
* session_autostart_init - run autostart file as shell script
* Note: Same as `sh ~/.config/labwc/autostart` (or equivalent XDG config dir)
*/
void session_autostart_init(struct server *server);
void session_autostart_init(void);
/**
* session_shutdown - run session shutdown file as shell script
* Note: Same as `sh ~/.config/labwc/shutdown` (or equivalent XDG config dir)
*/
void session_shutdown(struct server *server);
void session_shutdown(void);
#endif /* LABWC_SESSION_H */

View file

@ -91,20 +91,24 @@ struct server;
struct wlr_scene_node;
/* Begin window switcher */
void cycle_begin(struct server *server, enum lab_cycle_dir direction,
void cycle_begin(enum lab_cycle_dir direction,
struct cycle_filter filter);
/* Cycle the selected view in the window switcher */
void cycle_step(struct server *server, enum lab_cycle_dir direction);
void cycle_step(enum lab_cycle_dir direction);
/* Closes the OSD */
void cycle_finish(struct server *server, bool switch_focus);
void cycle_finish(bool switch_focus);
/* Re-initialize the window switcher */
void cycle_reinitialize(struct server *server);
void cycle_reinitialize(void);
/* Immediately cycle to next/previous window */
void cycle_immediate(enum lab_cycle_dir direction,
struct cycle_filter filter);
/* Focus the clicked window and close OSD */
void cycle_on_cursor_release(struct server *server, struct wlr_scene_node *node);
void cycle_on_cursor_release(struct wlr_scene_node *node);
/* Used by osd.c internally to render window switcher fields */
void cycle_osd_field_get_content(struct cycle_osd_field *field,
@ -133,7 +137,7 @@ struct cycle_osd_impl {
*/
void (*init)(struct cycle_osd_output *osd_output);
/*
* Update the OSD to highlight server->cycle.selected_view.
* Update the OSD to highlight server.cycle.selected_view.
*/
void (*update)(struct cycle_osd_output *osd_output);
};
@ -157,7 +161,7 @@ void cycle_osd_scroll_init(struct cycle_osd_output *osd_output,
int nr_cols, int nr_rows, int nr_visible_rows,
float *border_color, float *bg_color);
/* Scroll the OSD to show server->cycle.selected_view if needed */
/* Scroll the OSD to show server.cycle.selected_view if needed */
void cycle_osd_scroll_update(struct cycle_osd_output *osd_output);
extern struct cycle_osd_impl cycle_osd_classic_impl;

View file

@ -4,6 +4,6 @@
struct server;
void debug_dump_scene(struct server *server);
void debug_dump_scene(void);
#endif /* LABWC_DEBUG_H */

View file

@ -6,13 +6,13 @@ struct server;
struct view;
struct wlr_surface;
void kde_server_decoration_init(struct server *server);
void xdg_server_decoration_init(struct server *server);
void kde_server_decoration_init(void);
void xdg_server_decoration_init(void);
void kde_server_decoration_update_default(void);
void kde_server_decoration_set_view(struct view *view, struct wlr_surface *surface);
void kde_server_decoration_finish(struct server *server);
void xdg_server_decoration_finish(struct server *server);
void kde_server_decoration_finish(void);
void xdg_server_decoration_finish(void);
#endif /* LABWC_DECORATIONS_H */

View file

@ -6,14 +6,12 @@
struct server;
void desktop_entry_init(struct server *server);
void desktop_entry_finish(struct server *server);
void desktop_entry_init(void);
void desktop_entry_finish(void);
struct lab_img *desktop_entry_load_icon_from_app_id(
struct server *server, const char *app_id, int size, float scale);
struct lab_img *desktop_entry_load_icon_from_app_id(const char *app_id, int size, float scale);
struct lab_img *desktop_entry_load_icon(
struct server *server, const char *icon_name, int size, float scale);
struct lab_img *desktop_entry_load_icon(const char *icon_name, int size, float scale);
/**
* desktop_entry_name_lookup() - return the application name
@ -22,8 +20,7 @@ struct lab_img *desktop_entry_load_icon(
* The lifetime of the returned value is the same as that
* of sfdo_desktop_db (from `struct sfdo.desktop_db`)
*/
const char *desktop_entry_name_lookup(struct server *server,
const char *app_id);
const char *desktop_entry_name_lookup(const char *app_id);
#endif /* HAVE_LIBSFDO */
#endif /* LABWC_DESKTOP_ENTRY_H */

View file

@ -117,6 +117,6 @@ void edges_adjust_resize_geom(struct view *view, struct border edges,
bool edges_traverse_edge(struct edge current, struct edge target, struct edge edge);
void edges_calculate_visibility(struct server *server, struct view *ignored_view);
void edges_calculate_visibility(struct view *ignored_view);
#endif /* LABWC_EDGES_H */

View file

@ -6,6 +6,6 @@ struct wl_display;
struct wlr_seat;
void idle_manager_create(struct wl_display *display);
void idle_manager_notify_activity(struct wlr_seat *seat);
void idle_manager_notify_activity(struct wlr_seat *wlr_seat);
#endif /* LABWC_IDLE_H */

View file

@ -62,7 +62,7 @@ struct cursor_context_saved {
*
* If no node is found at cursor, ctx.type is set to ROOT.
*/
struct cursor_context get_cursor_context(struct server *server);
struct cursor_context get_cursor_context(void);
/**
* cursor_set - set cursor icon
@ -82,7 +82,7 @@ void cursor_context_save(struct cursor_context_saved *saved_ctx,
/**
* 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)
* @cursor_context - result of get_cursor_context()
*
* Calculates the resize edge combination that is most appropriate based
@ -92,7 +92,7 @@ void cursor_context_save(struct cursor_context_saved *saved_ctx,
* keyboard modifier or when using the Resize action from a keybind.
*/
enum lab_edge cursor_get_resize_edges(struct wlr_cursor *cursor,
struct cursor_context *ctx);
const struct cursor_context *ctx);
/**
* cursor_get_from_edge - translate lab_edge enum to lab_cursor enum
@ -113,7 +113,7 @@ enum lab_cursors cursor_get_from_edge(enum lab_edge resize_edges);
* or to force an update of the cursor icon by sending an exit and enter
* event to an already focused surface.
*/
void cursor_update_focus(struct server *server);
void cursor_update_focus(void);
/**
* cursor_update_image - re-set the labwc cursor image
@ -130,7 +130,7 @@ void cursor_update_image(struct seat *seat);
* should be notified. Parameters sx, sy holds the surface coordinates
* in that case.
*/
bool cursor_process_motion(struct server *server, uint32_t time, double *sx, double *sy);
bool cursor_process_motion(uint32_t time, double *sx, double *sy);
/**
* Processes cursor button press. The return value indicates if a client

View file

@ -5,12 +5,17 @@
#include <stdbool.h>
#include <stdint.h>
struct seat;
/*
* All keycodes in these functions are (Linux) libinput evdev scancodes which is
* what 'wlr_keyboard' uses (e.g. 'seat->keyboard_group->keyboard->keycodes').
* Note: These keycodes are different to XKB scancodes by a value of 8.
*/
void key_state_indicator_update(struct seat *seat);
void key_state_indicator_toggle(void);
/**
* key_state_pressed_sent_keycodes - generate array of pressed+sent keys
* Note: The array is generated by subtracting any bound keys from _all_ pressed

View file

@ -23,8 +23,7 @@ enum input_mode {
};
struct seat {
struct wlr_seat *seat;
struct server *server;
struct wlr_seat *wlr_seat;
struct wlr_keyboard_group *keyboard_group;
struct wl_list touch_points; /* struct touch_point.link */
@ -149,8 +148,15 @@ struct seat {
};
struct server {
uint32_t wlr_version;
struct wl_display *wl_display;
struct wl_event_loop *wl_event_loop; /* Can be used for timer events */
/* Pending auto-raise timer (used when rc.raise_on_focus_delay_ms > 0) */
struct view *pending_auto_raise_view;
struct wl_event_source *pending_auto_raise_timer;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wlr_backend *backend;
@ -188,6 +194,13 @@ struct server {
struct wlr_xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager;
struct wl_listener xdg_toplevel_icon_set_icon;
struct {
struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *manager;
struct {
struct wl_listener new_request;
} on;
} toplevel_capture;
/* front to back order */
struct wl_list views;
uint64_t next_view_creation_id;
@ -244,13 +257,14 @@ struct server {
struct wl_list all; /* struct workspace.link */
struct workspace *current;
struct workspace *last;
struct lab_cosmic_workspace_manager *cosmic_manager;
struct lab_cosmic_workspace_group *cosmic_group;
struct lab_ext_workspace_manager *ext_manager;
struct lab_ext_workspace_group *ext_group;
struct wlr_ext_workspace_manager_v1 *ext_manager;
struct wlr_ext_workspace_group_handle_v1 *ext_group;
struct {
struct wl_listener layout_output_added;
} on;
struct {
struct wl_listener commit;
} on_ext_manager;
} workspaces;
struct wl_list outputs;
@ -274,7 +288,6 @@ struct server {
struct wl_listener renderer_lost;
struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1;
struct wl_listener gamma_control_set_gamma;
struct session_lock_manager *session_lock_manager;
@ -303,19 +316,22 @@ struct server {
/* Set when in cycle (alt-tab) mode */
struct cycle_state cycle;
struct theme *theme;
struct menu *menu_current;
struct wl_list menus;
struct sfdo *sfdo;
pid_t primary_client_pid;
char *title_fmt;
};
/* defined in main.c */
extern struct server server;
void xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup);
void xdg_shell_init(struct server *server);
void xdg_shell_finish(struct server *server);
void xdg_shell_init(void);
void xdg_shell_finish(void);
/*
* desktop.c routines deal with a collection of views
@ -343,6 +359,13 @@ void xdg_shell_finish(struct server *server);
*/
void desktop_focus_view(struct view *view, bool raise);
/**
* desktop_cancel_pending_auto_raise() - cancel any pending delayed auto-raise
* (from raiseOnFocusDelay). Called when a view is being destroyed, on config
* reload, or when a new focus change with raise=false supersedes the pending.
*/
void desktop_cancel_pending_auto_raise(void);
/**
* desktop_focus_view_or_surface() - like desktop_focus_view() but can
* also focus other (e.g. xwayland-unmanaged) surfaces
@ -350,14 +373,14 @@ void desktop_focus_view(struct view *view, bool raise);
void desktop_focus_view_or_surface(struct seat *seat, struct view *view,
struct wlr_surface *surface, bool raise);
void desktop_arrange_all_views(struct server *server);
void desktop_arrange_all_views(void);
void desktop_focus_output(struct output *output);
/**
* Toggles the (output local) visibility of the layershell top layer
* based on the existence of a fullscreen window on the current workspace.
*/
void desktop_update_top_layer_visibility(struct server *server);
void desktop_update_top_layer_visibility(void);
/**
* desktop_focus_topmost_view() - focus the topmost view on the current
@ -367,11 +390,11 @@ void desktop_update_top_layer_visibility(struct server *server);
* This function is typically called when the focused view is hidden
* (closes, is minimized, etc.) to focus the "next" view underneath.
*/
void desktop_focus_topmost_view(struct server *server);
void desktop_focus_topmost_view(void);
void seat_init(struct server *server);
void seat_finish(struct server *server);
void seat_reconfigure(struct server *server);
void seat_init(void);
void seat_finish(void);
void seat_reconfigure(void);
void seat_force_focus_surface(struct seat *seat, struct wlr_surface *surface);
void seat_focus_surface(struct seat *seat, struct wlr_surface *surface);
@ -403,15 +426,15 @@ void seat_focus_override_end(struct seat *seat, bool restore_focus);
/**
* interactive_anchor_to_cursor() - repositions the geometry to remain
* underneath the cursor when its size changes during interactive move.
* This function also resizes server->grab_box and repositions it to remain
* underneath server->grab_{x,y}.
* This function also resizes server.grab_box and repositions it to remain
* underneath server.grab_{x,y}.
*
* geo->{width,height} are provided by the caller.
* geo->{x,y} are computed by this function.
*/
void interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo);
void interactive_anchor_to_cursor(struct wlr_box *geo);
void interactive_set_grab_context(struct cursor_context *ctx);
void interactive_set_grab_context(const struct cursor_context *ctx);
void interactive_begin(struct view *view, enum input_mode mode,
enum lab_edge edges);
void interactive_finish(struct view *view);
@ -429,12 +452,12 @@ bool edge_from_cursor(struct seat *seat, struct output **dest_output,
void handle_tearing_new_object(struct wl_listener *listener, void *data);
void server_init(struct server *server);
void server_start(struct server *server);
void server_finish(struct server *server);
void server_init(void);
void server_start(void);
void server_finish(void);
void create_constraint(struct wl_listener *listener, void *data);
void constrain_cursor(struct server *server, struct wlr_pointer_constraint_v1
void constrain_cursor(struct wlr_pointer_constraint_v1
*constraint);
#endif /* LABWC_H */

View file

@ -12,7 +12,6 @@ struct seat;
struct lab_layer_surface {
struct wlr_layer_surface_v1 *layer_surface;
struct wlr_scene_layer_surface_v1 *scene_layer_surface;
struct server *server;
bool mapped;
/* true only inside handle_unmap() */
@ -29,7 +28,6 @@ struct lab_layer_surface {
struct lab_layer_popup {
struct wlr_xdg_popup *wlr_popup;
struct wlr_scene_tree *scene_tree;
struct server *server;
bool parent_was_focused;
/* To simplify moving popup nodes from the bottom to the top layer */
@ -41,8 +39,8 @@ struct lab_layer_popup {
struct wl_listener reposition;
};
void layers_init(struct server *server);
void layers_finish(struct server *server);
void layers_init(void);
void layers_finish(void);
void layers_arrange(struct output *output);
void layer_try_set_focus(struct seat *seat,

View file

@ -14,8 +14,8 @@ enum magnify_dir {
MAGNIFY_DECREASE
};
void magnifier_toggle(struct server *server);
void magnifier_set_scale(struct server *server, enum magnify_dir dir);
void magnifier_toggle(void);
void magnifier_set_scale(enum magnify_dir dir);
bool output_wants_magnification(struct output *output);
void magnifier_draw(struct output *output, struct wlr_buffer *output_buffer,
struct wlr_box *damage);

View file

@ -23,9 +23,11 @@ struct menuitem {
char *text;
char *icon_name;
const char *arrow;
uint32_t accelerator;
struct menu *parent;
struct menu *submenu;
bool selectable;
bool use_markup;
enum menuitem_type type;
int native_width;
struct wlr_scene_tree *tree;
@ -49,7 +51,6 @@ struct menu {
int height;
} size;
struct wl_list menuitems;
struct server *server;
struct {
struct menu *menu;
struct menuitem *item;
@ -65,14 +66,27 @@ struct menu {
};
/* For keyboard support */
void menu_item_select_next(struct server *server);
void menu_item_select_previous(struct server *server);
void menu_submenu_enter(struct server *server);
void menu_submenu_leave(struct server *server);
bool menu_call_selected_actions(struct server *server);
void menu_item_select_next(void);
void menu_item_select_previous(void);
void menu_init(struct server *server);
void menu_finish(struct server *server);
/**
* menu_item_select_by_accelerator - selects the next menu item with
* a matching accelerator, starting after the current selection
*
* @accelerator a shortcut to quickly select/open an item, defined in menu.xml
* with an underscore in the item label before the target letter.
*
* Return: a boolean value that represents whether the newly selected item
* needs to be executed.
*/
bool menu_item_select_by_accelerator(uint32_t accelerator);
void menu_submenu_enter(void);
void menu_submenu_leave(void);
bool menu_call_selected_actions(void);
void menu_init(void);
void menu_finish(void);
void menu_on_view_destroy(struct view *view);
/**
@ -80,15 +94,15 @@ void menu_on_view_destroy(struct view *view);
*
* @id id string defined in menu.xml like "root-menu"
*/
struct menu *menu_get_by_id(struct server *server, const char *id);
struct menu *menu_get_by_id(const char *id);
/**
* menu_open_root - open menu on position (x, y)
*
* This function will close server->menu_current, open the
* new menu and assign @menu to server->menu_current.
* This function will close server.menu_current, open the
* new menu and assign @menu to server.menu_current.
*
* Additionally, server->input_mode will be set to LAB_INPUT_STATE_MENU.
* Additionally, server.input_mode will be set to LAB_INPUT_STATE_MENU.
*/
void menu_open_root(struct menu *menu, int x, int y);
@ -101,16 +115,16 @@ void menu_open_root(struct menu *menu, int x, int y);
void menu_process_cursor_motion(struct wlr_scene_node *node);
/**
* menu_close_root- close root menu
* menu_close_root - close root menu
*
* This function will close server->menu_current and set it to NULL.
* Asserts that server->input_mode is set to LAB_INPUT_STATE_MENU.
* This function will close server.menu_current and set it to NULL.
* Asserts that server.input_mode is set to LAB_INPUT_STATE_MENU.
*
* Additionally, server->input_mode will be set to LAB_INPUT_STATE_PASSTHROUGH.
* Additionally, server.input_mode will be set to LAB_INPUT_STATE_PASSTHROUGH.
*/
void menu_close_root(struct server *server);
void menu_close_root(void);
/* menu_reconfigure - reload theme and content */
void menu_reconfigure(struct server *server);
void menu_reconfigure(void);
#endif /* LABWC_MENU_H */

View file

@ -5,9 +5,9 @@
struct server;
struct wlr_output;
void output_virtual_add(struct server *server, const char *output_name,
void output_virtual_add(const char *output_name,
struct wlr_output **store_wlr_output);
void output_virtual_remove(struct server *server, const char *output_name);
void output_virtual_update_fallback(struct server *server);
void output_virtual_remove(const char *output_name);
void output_virtual_update_fallback(void);
#endif

View file

@ -9,7 +9,6 @@
struct output {
struct wl_list link; /* server.outputs */
struct server *server;
struct wlr_output *wlr_output;
struct wlr_output_state pending;
struct wlr_scene_output *scene_output;
@ -39,19 +38,16 @@ struct output {
* disconnected and connected again.
*/
uint64_t id_bit;
bool gamma_lut_changed;
};
#undef LAB_NR_LAYERS
void output_init(struct server *server);
void output_finish(struct server *server);
struct output *output_from_wlr_output(struct server *server,
struct wlr_output *wlr_output);
struct output *output_from_name(struct server *server, const char *name);
struct output *output_nearest_to(struct server *server, int lx, int ly);
struct output *output_nearest_to_cursor(struct server *server);
void output_init(void);
void output_finish(void);
struct output *output_from_wlr_output(struct wlr_output *wlr_output);
struct output *output_from_name(const char *name);
struct output *output_nearest_to(int lx, int ly);
struct output *output_nearest_to_cursor(void);
/**
* output_get_adjacent() - get next output, in a given direction,
@ -69,12 +65,15 @@ struct output *output_get_adjacent(struct output *output,
bool output_is_usable(struct output *output);
void output_update_usable_area(struct output *output);
void output_update_all_usable_areas(struct server *server, bool layout_changed);
void output_update_all_usable_areas(bool layout_changed);
bool output_get_tearing_allowance(struct output *output);
struct wlr_box output_usable_area_in_layout_coords(struct output *output);
void handle_output_power_manager_set_mode(struct wl_listener *listener,
void *data);
void output_enable_adaptive_sync(struct output *output, bool enabled);
void output_enable_hdr(struct output *output, struct wlr_output_state *os,
bool enabled, bool silent);
void output_state_setup_hdr(struct output *output, bool silent);
/**
* Notifies whether a fullscreen view is displayed on the given output.

View file

@ -1,24 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LABWC_PROTOCOLS_COSMIC_WORKSPACES_INTERNAL_H
#define LABWC_PROTOCOLS_COSMIC_WORKSPACES_INTERNAL_H
struct lab_cosmic_workspace;
struct lab_cosmic_workspace_group;
struct lab_cosmic_workspace_manager;
enum pending_change {
/* group events */
CW_PENDING_WS_CREATE = 1 << 0,
/* ws events*/
CW_PENDING_WS_ACTIVATE = 1 << 1,
CW_PENDING_WS_DEACTIVATE = 1 << 2,
CW_PENDING_WS_REMOVE = 1 << 3,
};
void cosmic_group_output_send_initial_state(struct lab_cosmic_workspace_group *group,
struct wl_resource *group_resource);
void cosmic_manager_schedule_done_event(struct lab_cosmic_workspace_manager *manager);
#endif /* LABWC_PROTOCOLS_COSMIC_WORKSPACES_INTERNAL_H */

View file

@ -1,94 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LABWC_PROTOCOLS_COSMIC_WORKSPACES_H
#define LABWC_PROTOCOLS_COSMIC_WORKSPACES_H
#include <stdbool.h>
#include <wayland-server-core.h>
struct wlr_output;
struct lab_cosmic_workspace_manager {
struct wl_global *global;
struct wl_list groups;
uint32_t caps;
struct wl_event_source *idle_source;
struct wl_event_loop *event_loop;
struct {
struct wl_listener display_destroy;
} on;
struct wl_list resources;
};
struct lab_cosmic_workspace_group {
struct lab_cosmic_workspace_manager *manager;
struct wl_list workspaces;
struct wl_array capabilities;
struct {
struct wl_signal create_workspace;
struct wl_signal destroy;
} events;
struct wl_list link;
struct wl_list outputs;
struct wl_list resources;
};
struct lab_cosmic_workspace {
struct lab_cosmic_workspace_group *group;
char *name;
struct wl_array coordinates;
struct wl_array capabilities;
uint32_t state; /* enum lab_cosmic_workspace_state */
uint32_t state_pending; /* enum lab_cosmic_workspace_state */
struct {
struct wl_signal activate;
struct wl_signal deactivate;
struct wl_signal remove;
struct wl_signal destroy;
} events;
struct wl_list link;
struct wl_list resources;
};
enum lab_cosmic_workspace_caps {
CW_CAP_NONE = 0,
CW_CAP_GRP_ALL = 0x000000ff,
CW_CAP_WS_ALL = 0x0000ff00,
/* group caps */
CW_CAP_GRP_WS_CREATE = 1 << 0,
/* workspace caps */
CW_CAP_WS_ACTIVATE = 1 << 8,
CW_CAP_WS_DEACTIVATE = 1 << 9,
CW_CAP_WS_REMOVE = 1 << 10,
};
struct lab_cosmic_workspace_manager *lab_cosmic_workspace_manager_create(
struct wl_display *display, uint32_t caps, uint32_t version);
struct lab_cosmic_workspace_group *lab_cosmic_workspace_group_create(
struct lab_cosmic_workspace_manager *manager);
void lab_cosmic_workspace_group_output_enter(
struct lab_cosmic_workspace_group *group, struct wlr_output *output);
void lab_cosmic_workspace_group_output_leave(
struct lab_cosmic_workspace_group *group, struct wlr_output *output);
void lab_cosmic_workspace_group_destroy(struct lab_cosmic_workspace_group *group);
struct lab_cosmic_workspace *lab_cosmic_workspace_create(struct lab_cosmic_workspace_group *group);
void lab_cosmic_workspace_set_name(struct lab_cosmic_workspace *workspace, const char *name);
void lab_cosmic_workspace_set_active(struct lab_cosmic_workspace *workspace, bool enabled);
void lab_cosmic_workspace_set_urgent(struct lab_cosmic_workspace *workspace, bool enabled);
void lab_cosmic_workspace_set_hidden(struct lab_cosmic_workspace *workspace, bool enabled);
void lab_cosmic_workspace_set_coordinates(struct lab_cosmic_workspace *workspace,
struct wl_array *coordinates);
void lab_cosmic_workspace_destroy(struct lab_cosmic_workspace *workspace);
#endif /* LABWC_PROTOCOLS_COSMIC_WORKSPACES_H */

View file

@ -1,25 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LABWC_PROTOCOLS_EXT_WORKSPACES_INTERNAL_H
#define LABWC_PROTOCOLS_EXT_WORKSPACES_INTERNAL_H
struct wl_resource;
struct lab_ext_workspace_group;
struct lab_ext_workspace_manager;
enum pending_ext_workspaces_change {
/* group events */
WS_PENDING_WS_CREATE = 1 << 0,
/* ws events*/
WS_PENDING_WS_ACTIVATE = 1 << 1,
WS_PENDING_WS_DEACTIVATE = 1 << 2,
WS_PENDING_WS_REMOVE = 1 << 3,
WS_PENDING_WS_ASSIGN = 1 << 4,
};
void ext_group_output_send_initial_state(struct lab_ext_workspace_group *group,
struct wl_resource *group_resource);
void ext_manager_schedule_done_event(struct lab_ext_workspace_manager *manager);
#endif /* LABWC_PROTOCOLS_EXT_WORKSPACES_INTERNAL_H */

View file

@ -1,109 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LABWC_PROTOCOLS_EXT_WORKSPACES_H
#define LABWC_PROTOCOLS_EXT_WORKSPACES_H
#include <stdbool.h>
#include <wayland-server-core.h>
struct wlr_output;
struct lab_ext_workspace_manager {
struct wl_global *global;
struct wl_list groups;
struct wl_list workspaces;
uint32_t caps;
struct wl_event_source *idle_source;
struct wl_event_loop *event_loop;
struct {
struct wl_listener display_destroy;
} on;
struct wl_list resources;
};
struct lab_ext_workspace_group {
struct lab_ext_workspace_manager *manager;
uint32_t capabilities;
struct {
struct wl_signal create_workspace;
struct wl_signal destroy;
} events;
struct wl_list link;
struct wl_list outputs;
struct wl_list resources;
};
struct lab_ext_workspace {
struct lab_ext_workspace_manager *manager;
struct lab_ext_workspace_group *group;
char *id;
char *name;
struct wl_array coordinates;
uint32_t capabilities;
uint32_t state; /* enum lab_ext_workspace_state */
uint32_t state_pending; /* enum lab_ext_workspace_state */
struct {
struct wl_signal activate;
struct wl_signal deactivate;
struct wl_signal remove;
struct wl_signal assign;
struct wl_signal destroy;
} events;
struct wl_list link;
struct wl_list resources;
};
enum lab_ext_workspace_caps {
WS_CAP_NONE = 0,
WS_CAP_GRP_ALL = 0x0000ffff,
WS_CAP_WS_ALL = 0xffff0000,
/* group caps */
WS_CAP_GRP_WS_CREATE = 1 << 0,
/* workspace caps */
WS_CAP_WS_ACTIVATE = 1 << 16,
WS_CAP_WS_DEACTIVATE = 1 << 17,
WS_CAP_WS_REMOVE = 1 << 18,
WS_CAP_WS_ASSIGN = 1 << 19,
};
struct lab_ext_workspace_manager *lab_ext_workspace_manager_create(
struct wl_display *display, uint32_t caps, uint32_t version);
struct lab_ext_workspace_group *lab_ext_workspace_group_create(
struct lab_ext_workspace_manager *manager);
void lab_ext_workspace_group_output_enter(
struct lab_ext_workspace_group *group, struct wlr_output *output);
void lab_ext_workspace_group_output_leave(
struct lab_ext_workspace_group *group, struct wlr_output *output);
void lab_ext_workspace_group_destroy(struct lab_ext_workspace_group *group);
/* Create a new workspace, id may be NULL */
struct lab_ext_workspace *lab_ext_workspace_create(
struct lab_ext_workspace_manager *manager, const char *id);
void lab_ext_workspace_assign_to_group(struct lab_ext_workspace *workspace,
struct lab_ext_workspace_group *group);
void lab_ext_workspace_set_name(struct lab_ext_workspace *workspace, const char *name);
void lab_ext_workspace_set_active(struct lab_ext_workspace *workspace, bool enabled);
void lab_ext_workspace_set_urgent(struct lab_ext_workspace *workspace, bool enabled);
void lab_ext_workspace_set_hidden(struct lab_ext_workspace *workspace, bool enabled);
void lab_ext_workspace_set_coordinates(struct lab_ext_workspace *workspace,
struct wl_array *coordinates);
void lab_ext_workspace_destroy(struct lab_ext_workspace *workspace);
#endif /* LABWC_PROTOCOLS_EXT_WORKSPACES_H */

View file

@ -1,84 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LABWC_PROTOCOLS_TRANSACTION_ADDON_H
#define LABWC_PROTOCOLS_TRANSACTION_ADDON_H
#include <wayland-server-core.h>
struct lab_transaction_op {
uint32_t change;
void *src;
void *data;
struct {
struct wl_signal destroy;
} events;
// Private
struct wl_list link;
};
struct lab_transaction_session_context {
int ref_count;
struct wl_list transaction_ops;
};
struct lab_wl_resource_addon {
struct lab_transaction_session_context *ctx;
void *data;
};
/*
* Creates a new addon which can be attached to a wl_resource via
* wl_resource_set_user_data() and retrieved via wl_resource_get_user_data().
*
* Usually the ctx argument should be addon->ctx of the parent wl_resource.
* If it is NULL it will be created automatically which can be used for top
* level wl_resources (when a client binds a wl_global from the registry).
*
* The context refcount is increased by one after this call.
*/
struct lab_wl_resource_addon *lab_resource_addon_create(
struct lab_transaction_session_context *ctx);
/*
* A generic transaction operation attached to
* a session context transaction operation list.
*
* All arguments other than the context are user defined.
* Use of an enum for pending_change is suggested.
*
* The client is responsible for eventually freeing the data
* passed in the void *src and *data arguments by listening
* to the events.destroy signal. The transaction operations can be
* looped through by using lab_transaction_for_each(trans_op, ctx).
*/
struct lab_transaction_op *lab_transaction_op_add(
struct lab_transaction_session_context *ctx,
uint32_t pending_change, void *src, void *data);
/*
* Removes the transaction operation from the ctx list and frees it.
*
* Does *not* free any passed in src or data arguments.
* Use the events.destroy signal for that if necessary.
*/
void lab_transaction_op_destroy(struct lab_transaction_op *transaction_op);
/*
* Destroys the addon.
*
* The context refcount is decreased by one. If it reaches
* zero the context will be free'd alongside the addon itself.
* If the context is destroyed all pending transaction operations
* are destroyed as well.
*/
void lab_resource_addon_destroy(struct lab_wl_resource_addon *addon);
/* Convenience wrappers for looping through the pending transaction ops of a ctx */
#define lab_transaction_for_each(transaction_op, ctx) \
wl_list_for_each(transaction_op, &(ctx)->transaction_ops, link)
#define lab_transaction_for_each_safe(trans_op, trans_op_tmp, ctx) \
wl_list_for_each_safe(trans_op, trans_op_tmp, &(ctx)->transaction_ops, link)
#endif /* LABWC_PROTOCOLS_TRANSACTIONS_ADDON_H */

View file

@ -26,7 +26,7 @@ struct region {
};
/* Returns true if we should show the region overlay or snap to region */
bool regions_should_snap(struct server *server);
bool regions_should_snap(void);
/**
* regions_reconfigure*() - re-initializes all regions from struct rc.
@ -36,7 +36,7 @@ bool regions_should_snap(struct server *server);
* - new output local regions are created from struct rc
* - the region geometry is re-calculated
*/
void regions_reconfigure(struct server *server);
void regions_reconfigure(void);
void regions_reconfigure_output(struct output *output);
/* re-calculate the geometry based on usable area */
@ -62,7 +62,7 @@ void regions_evacuate_output(struct output *output);
void regions_destroy(struct seat *seat, struct wl_list *regions);
/* Get output local region from cursor or name, may be NULL */
struct region *regions_from_cursor(struct server *server);
struct region *regions_from_cursor(void);
struct region *regions_from_name(const char *region_name, struct output *output);
#endif /* LABWC_REGIONS_H */

View file

@ -5,7 +5,7 @@
struct server;
struct view;
void resize_indicator_reconfigure(struct server *server);
void resize_indicator_reconfigure(void);
void resize_indicator_show(struct view *view);
void resize_indicator_update(struct view *view);
void resize_indicator_hide(struct view *view);

View file

@ -130,7 +130,7 @@ void scaled_buffer_request_update(struct scaled_buffer *self,
/**
* scaled_buffer_invalidate_sharing - clear the list of entire cached
* scaled_buffers used to share visually dupliated buffers. This should
* scaled_buffers used to share visually duplicated buffers. This should
* be called on Reconfigure to force updates of newly created
* scaled_buffers rather than reusing ones created before Reconfigure.
*/

View file

@ -15,6 +15,7 @@ struct scaled_font_buffer {
/* Private */
char *text;
bool use_markup;
int max_width;
float color[4];
float bg_color[4];
@ -69,8 +70,18 @@ scaled_font_buffer_create_for_titlebar(struct wlr_scene_tree *parent,
* bg_color is ignored for font buffers created with
* scaled_font_buffer_create_for_titlebar().
*/
void scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text,
int max_width, struct font *font, const float *color,
void scaled_font_buffer_update(struct scaled_font_buffer *self,
const char *text, int max_width, struct font *font, const float *color,
const float *bg_color);
/**
* Update an existing auto scaling font buffer allowing the use of pango markup.
*
* Behaves identically to scaled_font_buffer_update(), but allows customization
* of the `use_markup` field of the @self struct via @use_markup.
*/
void scaled_font_buffer_update_markup(struct scaled_font_buffer *self,
const char *text, int max_width, struct font *font, const float *color,
const float *bg_color, bool use_markup);
#endif /* LABWC_SCALED_FONT_BUFFER_H */

View file

@ -12,7 +12,6 @@ struct wlr_scene_buffer;
struct scaled_icon_buffer {
struct scaled_buffer *scaled_buffer;
struct wlr_scene_buffer *scene_buffer;
struct server *server;
/* for window icon */
struct view *view;
char *view_app_id;
@ -39,8 +38,7 @@ struct scaled_icon_buffer {
* wlr_scene_buffer (or one of its parents) is being destroyed.
*/
struct scaled_icon_buffer *scaled_icon_buffer_create(
struct wlr_scene_tree *parent, struct server *server,
int width, int height);
struct wlr_scene_tree *parent, int width, int height);
void scaled_icon_buffer_set_view(struct scaled_icon_buffer *self,
struct view *view);

View file

@ -8,7 +8,6 @@ struct output;
struct server;
struct session_lock_manager {
struct server *server;
struct wlr_session_lock_manager_v1 *wlr_manager;
/* View re-focused on unlock */
struct view *last_active_view;
@ -31,8 +30,8 @@ struct session_lock_manager {
struct wl_listener lock_destroy;
};
void session_lock_init(struct server *server);
void session_lock_init(void);
void session_lock_output_create(struct session_lock_manager *manager, struct output *output);
void session_lock_update_for_layout_change(struct server *server);
void session_lock_update_for_layout_change(void);
#endif /* LABWC_SESSION_LOCK_H */

8
include/show-desktop.h Normal file
View file

@ -0,0 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LABWC_SHOW_DESKTOP_H
#define LABWC_SHOW_DESKTOP_H
void show_desktop_toggle(void);
void show_desktop_reset(void);
#endif /* LABWC_SHOW_DESKTOP_H */

View file

@ -51,8 +51,7 @@ void ssd_set_titlebar(struct ssd *ssd, bool enabled);
void ssd_enable_keybind_inhibit_indicator(struct ssd *ssd, bool enable);
void ssd_enable_shade(struct ssd *ssd, bool enable);
void ssd_update_hovered_button(struct server *server,
struct wlr_scene_node *node);
void ssd_update_hovered_button(struct wlr_scene_node *node);
void ssd_button_free(struct ssd_button *button);

View file

@ -213,16 +213,13 @@ struct theme {
int mag_border_width;
};
struct server;
/**
* theme_init - read openbox theme and generate button textures
* @theme: theme data
* @server: server
* @theme_name: theme-name in <theme-dir>/<theme-name>/labwc/themerc
* Note <theme-dir> is obtained in theme-dir.c
*/
void theme_init(struct theme *theme, struct server *server, const char *theme_name);
void theme_init(struct theme *theme, const char *theme_name);
/**
* theme_finish - free button textures

View file

@ -135,7 +135,6 @@ struct view_impl {
};
struct view {
struct server *server;
enum view_type type;
const struct view_impl *impl;
struct wl_list link;
@ -178,6 +177,12 @@ struct view {
char *title;
char *app_id; /* WM_CLASS for xwayland windows */
struct {
struct wlr_scene *scene;
struct wlr_ext_image_capture_source_v1 *source;
struct wl_listener on_capture_source_destroy;
} capture;
bool mapped;
bool been_mapped;
uint64_t creation_id;
@ -185,6 +190,7 @@ struct view {
enum ssd_preference ssd_preference;
bool shaded;
bool minimized;
bool was_minimized_by_show_desktop_action;
enum view_axis maximized;
bool fullscreen;
bool tearing_hint;
@ -280,6 +286,7 @@ struct view {
struct wl_signal minimized;
struct wl_signal fullscreened;
struct wl_signal activated; /* bool *activated */
struct wl_signal always_on_top;
/*
* This is emitted when app_id, or icon set via xdg_toplevel_icon
* is updated. This is listened by scaled_icon_buffer.
@ -318,6 +325,7 @@ struct xdg_toplevel_view {
/* Events unique to xdg-toplevel views */
struct wl_listener set_app_id;
struct wl_listener request_show_window_menu;
struct wl_listener set_parent;
struct wl_listener new_popup;
};
@ -357,7 +365,7 @@ bool view_matches_query(struct view *view, struct view_query *query);
* @criteria: Criteria to match against.
* Example:
* struct view *view;
* for_each_view(view, &server->views, LAB_VIEW_CRITERIA_NONE) {
* for_each_view(view, &server.views, LAB_VIEW_CRITERIA_NONE) {
* printf("%s\n", view_get_string_prop(view, "app_id"));
* }
*/
@ -373,7 +381,7 @@ bool view_matches_query(struct view *view, struct view_query *query);
* @criteria: Criteria to match against.
* Example:
* struct view *view;
* for_each_view_reverse(view, &server->views, LAB_VIEW_CRITERIA_NONE) {
* for_each_view_reverse(view, &server.views, LAB_VIEW_CRITERIA_NONE) {
* printf("%s\n", view_get_string_prop(view, "app_id"));
* }
*/
@ -423,13 +431,13 @@ struct view *view_prev(struct wl_list *head, struct view *view,
* struct view **view;
* struct wl_array views;
* wl_array_init(&views);
* view_array_append(server, &views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE);
* view_array_append(&views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE);
* wl_array_for_each(view, &views) {
* // Do something with *view
* }
* wl_array_release(&views);
*/
void view_array_append(struct server *server, struct wl_array *views,
void view_array_append(struct wl_array *views,
enum lab_view_criteria criteria);
enum view_wants_focus view_wants_focus(struct view *view);
@ -486,7 +494,6 @@ void view_resize_relative(struct view *view,
int left, int right, int top, int bottom);
void view_move_relative(struct view *view, int x, int y);
void view_move(struct view *view, int x, int y);
void view_move_to_cursor(struct view *view);
void view_moved(struct view *view);
void view_minimize(struct view *view, bool minimized);
bool view_compute_centered_position(struct view *view,
@ -515,11 +522,30 @@ int view_effective_height(struct view *view, bool use_pending);
*/
void view_center(struct view *view, const struct wlr_box *ref);
/**
* view_compute_position_by_policy() - compute view placement
* @view: view to be placed
* @geom: floating view geometry to update (in/out)
* @allow_cursor: set to false to ignore center-on-cursor policy
* @policy: placement policy to apply
*
* Computes floating view placement according to configured strategy.
* Unlike view_place_by_policy(), this function doesn't actually move
* the view. It returns false if position could not be computed (e.g.
* if no outputs are connected). In that case, @geom is not modified.
*/
bool view_compute_position_by_policy(struct view *view, struct wlr_box *geom,
bool allow_cursor, enum lab_placement_policy policy);
/**
* view_place_by_policy - apply placement strategy to view
* @view: view to be placed
* @allow_cursor: set to false to ignore center-on-cursor policy
* @policy: placement policy to apply
*
* Places a floating view according to configured placement strategy.
* Clears any maximized/fullscreen/tiled state and restores natural
* geometry of the view before positioning.
*/
void view_place_by_policy(struct view *view, bool allow_cursor,
enum lab_placement_policy policy);
@ -546,7 +572,15 @@ bool view_titlebar_visible(struct view *view);
void view_set_ssd_mode(struct view *view, enum lab_ssd_mode mode);
void view_set_decorations(struct view *view, enum lab_ssd_mode mode, bool force_ssd);
void view_toggle_fullscreen(struct view *view);
/*
* Saves the window position in view->last_placement. This should be called
* when a view is first mapped or manually moved by the user.
*/
void view_save_last_placement(struct view *view);
/* Restores and adjusts the view's position from the view->last_placement */
void view_adjust_for_layout_change(struct view *view);
void view_move_to_edge(struct view *view, enum lab_edge direction, bool snap_to_windows);
void view_grow_to_edge(struct view *view, enum lab_edge direction);
void view_shrink_to_edge(struct view *view, enum lab_edge direction);
@ -603,4 +637,6 @@ enum lab_placement_policy view_placement_parse(const char *policy);
/* xdg.c */
struct wlr_xdg_surface *xdg_surface_from_view(struct view *view);
bool view_matches_criteria(struct view *view, enum lab_view_criteria criteria);
#endif /* LABWC_VIEW_H */

View file

@ -40,6 +40,7 @@ struct window_rule {
enum property ignore_configure_request;
enum property fixed_position;
enum property icon_prefer_client;
enum property allow_always_on_top;
struct wl_list link; /* struct rcxml.window_rules */
};

View file

@ -12,34 +12,20 @@ struct wlr_scene_tree;
struct workspace {
struct wl_list link; /* struct server.workspaces */
struct server *server;
char *name;
struct wlr_scene_tree *tree;
struct wlr_scene_tree *view_trees[3];
struct lab_cosmic_workspace *cosmic_workspace;
struct {
struct wl_listener activate;
struct wl_listener deactivate;
struct wl_listener remove;
} on_cosmic;
struct lab_ext_workspace *ext_workspace;
struct {
struct wl_listener activate;
struct wl_listener deactivate;
struct wl_listener assign;
struct wl_listener remove;
} on_ext;
struct wlr_ext_workspace_handle_v1 *ext_workspace;
};
void workspaces_init(struct server *server);
void workspaces_init(void);
void workspaces_switch_to(struct workspace *target, bool update_focus);
void workspaces_destroy(struct server *server);
void workspaces_destroy(void);
void workspaces_osd_hide(struct seat *seat);
struct workspace *workspaces_find(struct workspace *anchor, const char *name,
bool wrap);
void workspaces_reconfigure(struct server *server);
void workspaces_reconfigure(void);
#endif /* LABWC_WORKSPACES_H */

View file

@ -11,7 +11,6 @@ struct wlr_output;
struct wlr_output_layout;
struct xwayland_unmanaged {
struct server *server;
struct wlr_xwayland_surface *xwayland_surface;
struct wlr_scene_node *node;
struct wl_list link;
@ -40,6 +39,7 @@ struct xwayland_view {
struct view base;
struct wlr_xwayland_surface *xwayland_surface;
bool focused_before_map;
bool initial_geometry_set;
/* Events unique to XWayland views */
struct wl_listener associate;
@ -53,33 +53,34 @@ struct xwayland_view {
struct wl_listener set_override_redirect;
struct wl_listener set_strut_partial;
struct wl_listener set_window_type;
struct wl_listener set_icon;
struct wl_listener focus_in;
struct wl_listener map_request;
/* Not (yet) implemented */
/* struct wl_listener set_role; */
/* struct wl_listener set_hints; */
/* Events coming in from the view itself */
struct {
struct wl_listener always_on_top;
} on_view;
};
void xwayland_unmanaged_create(struct server *server,
struct wlr_xwayland_surface *xsurface, bool mapped);
void xwayland_unmanaged_create(struct wlr_xwayland_surface *xsurface, bool mapped);
void xwayland_view_create(struct server *server,
struct wlr_xwayland_surface *xsurface, bool mapped);
void xwayland_view_create(struct wlr_xwayland_surface *xsurface, bool mapped);
void xwayland_server_init(struct server *server,
struct wlr_compositor *compositor);
void xwayland_server_finish(struct server *server);
void xwayland_server_init(struct wlr_compositor *compositor);
void xwayland_server_finish(void);
void xwayland_adjust_usable_area(struct view *view,
struct wlr_output_layout *layout, struct wlr_output *output,
struct wlr_box *usable);
void xwayland_update_workarea(struct server *server);
void xwayland_update_workarea(void);
void xwayland_reset_cursor(struct server *server);
void xwayland_flush(struct server *server);
void xwayland_flush(void);
#endif /* HAVE_XWAYLAND */
#endif /* LABWC_XWAYLAND_H */

View file

@ -1,7 +1,7 @@
project(
'labwc',
'c',
version: '0.9.5',
version: '0.20.1',
license: 'GPL-2.0-only',
meson_version: '>=0.59.0',
default_options: [
@ -51,9 +51,9 @@ endif
add_project_arguments('-DLABWC_VERSION=@0@'.format(version), language: 'c')
wlroots = dependency(
'wlroots-0.19',
'wlroots-0.20',
default_options: ['default_library=static', 'examples=false'],
version: ['>=0.19.0', '<0.20.0'],
version: ['>=0.20.1', '<0.21.0'],
)
wlroots_has_xwayland = wlroots.get_variable('have_xwayland') == 'true'
@ -65,13 +65,13 @@ xkbcommon = dependency('xkbcommon')
xcb = dependency('xcb', required: get_option('xwayland'))
xcb_ewmh = dependency('xcb-ewmh', required: get_option('xwayland'))
xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland'))
drm_full = dependency('libdrm')
drm_full = dependency('libdrm', required: wlroots.get_variable('have_drm_backend') == 'true')
drm = drm_full.partial_dependency(compile_args: true, includes: true)
xml2 = dependency('libxml-2.0')
glib = dependency('glib-2.0')
cairo = dependency('cairo')
pangocairo = dependency('pangocairo')
input = dependency('libinput', version: '>=1.14')
input = dependency('libinput', version: '>=1.26', required: wlroots.get_variable('have_libinput_backend') == 'true')
pixman = dependency('pixman-1')
math = cc.find_library('m')
png = dependency('libpng')
@ -211,6 +211,17 @@ install_data('data/labwc.desktop', install_dir: get_option('datadir') / 'wayland
install_data('data/labwc-portals.conf', install_dir: get_option('datadir') / 'xdg-desktop-portal')
# Install labwc-session.target so that systemd user services with
# WantedBy=graphical-session.target can be started and stopped in sync
# with a labwc session (see labwc(1) SESSION MANAGEMENT for the opt-in
# autostart/shutdown snippet).
systemd_feat = get_option('systemd-session')
systemd = dependency('systemd', required: systemd_feat)
if systemd.found()
install_data('data/labwc-session.target',
install_dir: systemd.get_variable('systemduserunitdir'))
endif
icons = ['labwc-symbolic.svg', 'labwc.svg']
foreach icon : icons
icon_path = join_paths('data', icon)

View file

@ -2,7 +2,9 @@ option('man-pages', type: 'feature', value: 'auto', description: 'Generate and i
option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications')
option('svg', type: 'feature', value: 'enabled', description: 'Enable svg window buttons')
option('icon', type: 'feature', value: 'enabled', description: 'Enable window icons')
option('labnag', type: 'feature', value: 'auto', description: 'Build labnag notification daemon')
option('nls', type: 'feature', value: 'auto', description: 'Enable native language support')
option('systemd-session', type: 'feature', value: 'auto', description: 'Install labwc-session.target systemd user unit')
option('static_analyzer', type: 'feature', value: 'disabled', description: 'Run gcc static analyzer')
option('test', type: 'feature', value: 'disabled', description: 'Run tests')
option('sections', type: 'feature', value: 'disabled', description: 'Show unused functions')

View file

@ -1 +1 @@
ar ca cs da de el es et eu fa fi fr gl he hr hu id it ja ka kk ko lt ms nl pa pl pt pt_BR ru sk sv tr uk vi zh_CN zh_TW
ar ca cs da de el es et eu fa fi fr gl he hr hu id it ja ka kab kk ko lt ms nl pa pl pt pt_BR ru sk sr_Latn sv tr uk vi zh_CN zh_TW

View file

@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-09-19 21:09+1000\n"
"PO-Revision-Date: 2025-08-15 20:01+0000\n"
"Last-Translator: Ettore Atalan <atalanttore@googlemail.com>\n"
"PO-Revision-Date: 2026-05-18 18:28+0000\n"
"Last-Translator: Demian <Demian@gmx.co.uk>\n"
"Language-Team: German <https://translate.lxqt-project.org/projects/labwc/"
"labwc/de/>\n"
"Language: de\n"
@ -21,7 +21,7 @@ msgstr ""
#: src/menu/menu.c:1016
msgid "Go there..."
msgstr "Dorthin gehen..."
msgstr "Dorthin wechseln ..."
#: src/menu/menu.c:1034
msgid "Terminal"
@ -61,11 +61,11 @@ msgstr "Immer im Vordergrund"
#: src/menu/menu.c:1071
msgid "Move Left"
msgstr "nach links"
msgstr "Nach links"
#: src/menu/menu.c:1078
msgid "Move Right"
msgstr "nach rechts"
msgstr "Nach rechts"
#: src/menu/menu.c:1083
msgid "Always on Visible Workspace"

80
po/kab.po Normal file
View file

@ -0,0 +1,80 @@
# Labwc pot file
# Copyright (C) 2024
# This file is distributed under the same license as the labwc package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-09-19 21:09+1000\n"
"PO-Revision-Date: 2026-03-31 08:01+0000\n"
"Last-Translator: ButterflyOfFire <butterflyoffire@protonmail.com>\n"
"Language-Team: Kabyle <https://translate.lxqt-project.org/projects/labwc/"
"labwc/kab/>\n"
"Language: kab\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 4.2.1\n"
#: src/menu/menu.c:1016
msgid "Go there..."
msgstr ""
#: src/menu/menu.c:1034
msgid "Terminal"
msgstr "Ixf"
#: src/menu/menu.c:1040
msgid "Reconfigure"
msgstr ""
#: src/menu/menu.c:1042
msgid "Exit"
msgstr "Ffeɣ"
#: src/menu/menu.c:1056
msgid "Minimize"
msgstr ""
#: src/menu/menu.c:1058
msgid "Maximize"
msgstr ""
#: src/menu/menu.c:1060
msgid "Fullscreen"
msgstr "Agdil aččuran"
#: src/menu/menu.c:1062
msgid "Roll Up/Down"
msgstr ""
#: src/menu/menu.c:1064
msgid "Decorations"
msgstr ""
#: src/menu/menu.c:1066
msgid "Always on Top"
msgstr ""
#: src/menu/menu.c:1071
msgid "Move Left"
msgstr ""
#: src/menu/menu.c:1078
msgid "Move Right"
msgstr ""
#: src/menu/menu.c:1083
msgid "Always on Visible Workspace"
msgstr ""
#: src/menu/menu.c:1086
msgid "Workspace"
msgstr ""
#: src/menu/menu.c:1089
msgid "Close"
msgstr "Mdel"

81
po/sr_Latn.po Normal file
View file

@ -0,0 +1,81 @@
# Labwc pot file
# Copyright (C) 2024
# This file is distributed under the same license as the labwc package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-09-19 21:09+1000\n"
"PO-Revision-Date: 2026-05-05 08:28+0000\n"
"Last-Translator: Tobias Si <dragoslav123@protonmail.com>\n"
"Language-Team: Serbian (latin) <https://translate.lxqt-project.org/projects/"
"labwc/labwc/sr_Latn/>\n"
"Language: sr_Latn\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 4.2.1\n"
#: src/menu/menu.c:1016
msgid "Go there..."
msgstr "Idi tamo..."
#: src/menu/menu.c:1034
msgid "Terminal"
msgstr "Terminal"
#: src/menu/menu.c:1040
msgid "Reconfigure"
msgstr "Podesi"
#: src/menu/menu.c:1042
msgid "Exit"
msgstr "Izlaz"
#: src/menu/menu.c:1056
msgid "Minimize"
msgstr "Minimiziraj"
#: src/menu/menu.c:1058
msgid "Maximize"
msgstr "Maksimiziraj"
#: src/menu/menu.c:1060
msgid "Fullscreen"
msgstr "Prikaz preko celog ekrana"
#: src/menu/menu.c:1062
msgid "Roll Up/Down"
msgstr "Prevuci Gore/Dole"
#: src/menu/menu.c:1064
msgid "Decorations"
msgstr "Dekoracije"
#: src/menu/menu.c:1066
msgid "Always on Top"
msgstr "Uvek na vrhu"
#: src/menu/menu.c:1071
msgid "Move Left"
msgstr "Pomeri levo"
#: src/menu/menu.c:1078
msgid "Move Right"
msgstr "Pomeri desno"
#: src/menu/menu.c:1083
msgid "Always on Visible Workspace"
msgstr "Uvek na vidljivom radnom prostoru"
#: src/menu/menu.c:1086
msgid "Workspace"
msgstr "Radni prostor"
#: src/menu/menu.c:1089
msgid "Close"
msgstr "Zatvori"

View file

@ -1,364 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="cosmic_workspace_unstable_v1">
<copyright>
Copyright © 2019 Christopher Billington
Copyright © 2020 Ilia Bozhinov
Copyright © 2022 Victoria Brekenfeld
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<interface name="zcosmic_workspace_manager_v1" version="1">
<description summary="list and control workspaces">
Workspaces, also called virtual desktops, are groups of surfaces. A
compositor with a concept of workspaces may only show some such groups of
surfaces (those of 'active' workspaces) at a time. 'Activating' a
workspace is a request for the compositor to display that workspace's
surfaces as normal, whereas the compositor may hide or otherwise
de-emphasise surfaces that are associated only with 'inactive' workspaces.
Workspaces are grouped by which sets of outputs they correspond to, and
may contain surfaces only from those outputs. In this way, it is possible
for each output to have its own set of workspaces, or for all outputs (or
any other arbitrary grouping) to share workspaces. Compositors may
optionally conceptually arrange each group of workspaces in an
N-dimensional grid.
The purpose of this protocol is to enable the creation of taskbars and
docks by providing them with a list of workspaces and their properties,
and allowing them to activate and deactivate workspaces.
After a client binds the zcosmic_workspace_manager_v1, each workspace will be
sent via the workspace event.
</description>
<event name="workspace_group">
<description summary="a workspace group has been created">
This event is emitted whenever a new workspace group has been created.
All initial details of the workspace group (workspaces, outputs) will be
sent immediately after this event via the corresponding events in
zcosmic_workspace_group_handle_v1.
</description>
<arg name="workspace_group" type="new_id" interface="zcosmic_workspace_group_handle_v1"/>
</event>
<request name="commit">
<description summary="all requests about the workspaces have been sent">
The client must send this request after it has finished sending other
requests. The compositor must process a series of requests preceding a
commit request atomically.
This allows changes to the workspace properties to be seen as atomic,
even if they happen via multiple events, and even if they involve
multiple zcosmic_workspace_handle_v1 objects, for example, deactivating one
workspace and activating another.
</description>
</request>
<event name="done">
<description summary="all information about the workspace groups has been sent">
This event is sent after all changes in all workspace groups have been
sent.
This allows changes to one or more zcosmic_workspace_group_handle_v1
properties and zcosmic_workspace_handle_v1 properties to be seen as atomic,
even if they happen via multiple events.
In particular, an output moving from one workspace group to
another sends an output_enter event and an output_leave event to the two
zcosmic_workspace_group_handle_v1 objects in question. The compositor sends
the done event only after updating the output information in both
workspace groups.
</description>
</event>
<event name="finished">
<description summary="the compositor has finished with the workspace_manager">
This event indicates that the compositor is done sending events to the
zcosmic_workspace_manager_v1. The server will destroy the object
immediately after sending this request, so it will become invalid and
the client should free any resources associated with it.
</description>
</event>
<request name="stop">
<description summary="stop sending events">
Indicates the client no longer wishes to receive events for new
workspace groups. However the compositor may emit further workspace
events, until the finished event is emitted.
The client must not send any more requests after this one.
</description>
</request>
</interface>
<interface name="zcosmic_workspace_group_handle_v1" version="1">
<description summary="a workspace group assigned to a set of outputs">
A zcosmic_workspace_group_handle_v1 object represents a a workspace group
that is assigned a set of outputs and contains a number of workspaces.
The set of outputs assigned to the workspace group is conveyed to the client via
output_enter and output_leave events, and its workspaces are conveyed with
workspace events.
For example, a compositor which has a set of workspaces for each output may
advertise a workspace group (and its workspaces) per output, whereas a compositor
where a workspace spans all outputs may advertise a single workspace group for all
outputs.
</description>
<enum name="zcosmic_workspace_group_capabilities_v1">
<entry name="create_workspace" value="1" summary="create_workspace request is available"/>
</enum>
<event name="capabilities">
<description summary="compositor capabilities">
This event advertises the capabilities supported by the compositor. If
a capability isn't supported, clients should hide or disable the UI
elements that expose this functionality. For instance, if the
compositor doesn't advertise support for creating workspaces, a button
triggering the create_workspace request should not be displayed.
The compositor will ignore requests it doesn't support. For instance,
a compositor which doesn't advertise support for creating workspaces will ignore
create_workspace requests.
Compositors must send this event once after creation of an
zcosmic_workspace_group_handle_v1 . When the capabilities change, compositors
must send this event again.
The capabilities are sent as an array of 32-bit unsigned integers in
native endianness.
</description>
<arg name="capabilities" type="array" summary="array of 32-bit capabilities"/>
</event>
<event name="output_enter">
<description summary="output assigned to workspace group">
This event is emitted whenever an output is assigned to the workspace
group.
</description>
<arg name="output" type="object" interface="wl_output"/>
</event>
<event name="output_leave">
<description summary="output removed from workspace group">
This event is emitted whenever an output is removed from the workspace
group.
</description>
<arg name="output" type="object" interface="wl_output"/>
</event>
<event name="workspace">
<description summary="workspace added to workspace group">
This event is emitted whenever a new workspace has been created.
A workspace can only be a member of a single workspace group and cannot
be re-assigned.
All initial details of the workspace (name, coordinates, state) will
be sent immediately after this event via the corresponding events in
zcosmic_workspace_handle_v1.
</description>
<arg name="workspace" type="new_id" interface="zcosmic_workspace_handle_v1"/>
</event>
<event name="remove">
<description summary="this workspace group has been destroyed">
This event means the zcosmic_workspace_group_handle_v1 has been destroyed.
It is guaranteed there won't be any more events for this
zcosmic_workspace_group_handle_v1. The zext_workspace_group_handle_v1 becomes
inert so any requests will be ignored except the destroy request.
The compositor must remove all workspaces belonging to a workspace group
before removing the workspace group.
</description>
</event>
<request name="create_workspace">
<description summary="create a new workspace">
Request that the compositor create a new workspace with the given name.
There is no guarantee that the compositor will create a new workspace,
or that the created workspace will have the provided name.
</description>
<arg name="workspace" type="string"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the zcosmic_workspace_group_handle_v1 object">
Destroys the zcosmic_workspace_group_handle_v1 object.
This request should be called either when the client does not want to
use the workspace object any more or after the remove event to finalize
the destruction of the object.
</description>
</request>
</interface>
<interface name="zcosmic_workspace_handle_v1" version="1">
<description summary="a workspace handing a group of surfaces">
A zcosmic_workspace_handle_v1 object represents a a workspace that handles a
group of surfaces.
Each workspace has a name, conveyed to the client with the name event; a
list of states, conveyed to the client with the state event; and
optionally a set of coordinates, conveyed to the client with the
coordinates event. The client may request that the compositor activate or
deactivate the workspace.
Each workspace can belong to only a single workspace group.
Depepending on the compositor policy, there might be workspaces with
the same name in different workspace groups, but these workspaces are still
separate (e.g. one of them might be active while the other is not).
</description>
<event name="name">
<description summary="workspace name changed">
This event is emitted immediately after the zcosmic_workspace_handle_v1 is
created and whenever the name of the workspace changes.
</description>
<arg name="name" type="string"/>
</event>
<event name="coordinates">
<description summary="workspace coordinates changed">
This event is used to organize workspaces into an N-dimensional grid
within a workspace group, and if supported, is emitted immediately after
the zcosmic_workspace_handle_v1 is created and whenever the coordinates of
the workspace change. Compositors may not send this event if they do not
conceptually arrange workspaces in this way. If compositors simply
number workspaces, without any geometric interpretation, they may send
1D coordinates, which clients should not interpret as implying any
geometry. Sending an empty array means that the compositor no longer
orders the workspace geometrically.
Coordinates have an arbitrary number of dimensions N with an uint32
position along each dimension. By convention if N > 1, the first
dimension is X, the second Y, the third Z, and so on. The compositor may
chose to utilize these events for a more novel workspace layout
convention, however. No guarantee is made about the grid being filled or
bounded; there may be a workspace at coordinate 1 and another at
coordinate 1000 and none in between. Within a workspace group, however,
workspaces must have unique coordinates of equal dimensionality.
</description>
<arg name="coordinates" type="array"/>
</event>
<event name="state">
<description summary="the state of the workspace changed">
This event is emitted immediately after the zcosmic_workspace_handle_v1 is
created and each time the workspace state changes, either because of a
compositor action or because of a request in this protocol.
</description>
<arg name="state" type="array"/>
</event>
<enum name="state">
<description summary="types of states on the workspace">
The different states that a workspace can have.
</description>
<entry name="active" value="0" summary="the workspace is active"/>
<entry name="urgent" value="1" summary="the workspace requests attention"/>
<entry name="hidden" value="2">
<description summary="the workspace is not visible">
The workspace is not visible in its workspace group, and clients
attempting to visualize the compositor workspace state should not
display such workspaces.
</description>
</entry>
</enum>
<enum name="zcosmic_workspace_capabilities_v1">
<entry name="activate" value="1" summary="activate request is available"/>
<entry name="deactivate" value="2" summary="deactivate request is available"/>
<entry name="remove" value="3" summary="remove request is available"/>
</enum>
<event name="capabilities">
<description summary="compositor capabilities">
This event advertises the capabilities supported by the compositor. If
a capability isn't supported, clients should hide or disable the UI
elements that expose this functionality. For instance, if the
compositor doesn't advertise support for removing workspaces, a button
triggering the remove request should not be displayed.
The compositor will ignore requests it doesn't support. For instance,
a compositor which doesn't advertise support for remove will ignore
remove requests.
Compositors must send this event once after creation of an
zcosmic_workspace_handle_v1 . When the capabilities change, compositors
must send this event again.
The capabilities are sent as an array of 32-bit unsigned integers in
native endianness.
</description>
<arg name="capabilities" type="array" summary="array of 32-bit capabilities"/>
</event>
<event name="remove">
<description summary="this workspace has been destroyed">
This event means the zcosmic_workspace_handle_v1 has been destroyed. It is
guaranteed there won't be any more events for this
zcosmic_workspace_handle_v1. The zext_workspace_handle_v1 becomes inert so
any requests will be ignored except the destroy request.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="destroy the zcosmic_workspace_handle_v1 object">
Destroys the zcosmic_workspace_handle_v1 object.
This request should be called either when the client does not want to
use the workspace object any more or after the remove event to finalize
the destruction of the object.
</description>
</request>
<request name="activate">
<description summary="activate the workspace">
Request that this workspace be activated.
There is no guarantee the workspace will be actually activated, and
behaviour may be compositor-dependent. For example, activating a
workspace may or may not deactivate all other workspaces in the same
group.
</description>
</request>
<request name="deactivate">
<description summary="activate the workspace">
Request that this workspace be deactivated.
There is no guarantee the workspace will be actually deactivated.
</description>
</request>
<request name="remove">
<description summary="remove the workspace">
Request that this workspace be removed.
There is no guarantee the workspace will be actually removed.
</description>
</request>
</interface>
</protocol>

View file

@ -14,18 +14,6 @@ wayland_scanner_server = generator(
)
server_protocols = [
wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml',
wl_protocol_dir / 'stable/tablet/tablet-v2.xml',
wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml',
wl_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml',
wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml',
wl_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml',
wl_protocol_dir / 'staging/ext-workspace/ext-workspace-v1.xml',
wl_protocol_dir / 'staging/ext-image-capture-source/ext-image-capture-source-v1.xml',
wl_protocol_dir / 'staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml',
'cosmic-workspace-unstable-v1.xml',
'wlr-layer-shell-unstable-v1.xml',
'wlr-output-power-management-unstable-v1.xml',
]

View file

@ -21,12 +21,14 @@
#include "cycle.h"
#include "debug.h"
#include "input/keyboard.h"
#include "input/key-state.h"
#include "labwc.h"
#include "magnifier.h"
#include "menu/menu.h"
#include "output.h"
#include "output-virtual.h"
#include "regions.h"
#include "show-desktop.h"
#include "ssd.h"
#include "theme.h"
#include "translate.h"
@ -68,145 +70,114 @@ struct action_arg_list {
struct wl_list value;
};
#define ACTION_TYPE_LIST(X) \
X(NONE, "None") \
X(CLOSE, "Close") \
X(KILL, "Kill") \
X(DEBUG, "Debug") \
X(EXECUTE, "Execute") \
X(EXIT, "Exit") \
X(MOVE_TO_EDGE, "MoveToEdge") \
X(TOGGLE_SNAP_TO_EDGE, "ToggleSnapToEdge") \
X(SNAP_TO_EDGE, "SnapToEdge") \
X(GROW_TO_EDGE, "GrowToEdge") \
X(SHRINK_TO_EDGE, "ShrinkToEdge") \
X(NEXT_WINDOW, "NextWindow") \
X(PREVIOUS_WINDOW, "PreviousWindow") \
X(NEXT_WINDOW_IMMEDIATE, "NextWindowImmediate") \
X(PREVIOUS_WINDOW_IMMEDIATE, "PreviousWindowImmediate") \
X(RECONFIGURE, "Reconfigure") \
X(SHOW_MENU, "ShowMenu") \
X(TOGGLE_MAXIMIZE, "ToggleMaximize") \
X(MAXIMIZE, "Maximize") \
X(UNMAXIMIZE, "UnMaximize") \
X(TOGGLE_FULLSCREEN, "ToggleFullscreen") \
X(SET_DECORATIONS, "SetDecorations") \
X(TOGGLE_DECORATIONS, "ToggleDecorations") \
X(TOGGLE_ALWAYS_ON_TOP, "ToggleAlwaysOnTop") \
X(TOGGLE_ALWAYS_ON_BOTTOM, "ToggleAlwaysOnBottom") \
X(TOGGLE_OMNIPRESENT, "ToggleOmnipresent") \
X(FOCUS, "Focus") \
X(UNFOCUS, "Unfocus") \
X(ICONIFY, "Iconify") \
X(RAISE, "Raise") \
X(LOWER, "Lower") \
X(MOVE, "Move") \
X(RESIZE, "Resize") \
X(RESIZE_RELATIVE, "ResizeRelative") \
X(MOVETO, "MoveTo") \
X(RESIZETO, "ResizeTo") \
X(MOVETO_CURSOR, "MoveToCursor") \
X(MOVE_RELATIVE, "MoveRelative") \
X(SEND_TO_DESKTOP, "SendToDesktop") \
X(GO_TO_DESKTOP, "GoToDesktop") \
X(TOGGLE_SNAP_TO_REGION, "ToggleSnapToRegion") \
X(SNAP_TO_REGION, "SnapToRegion") \
X(UNSNAP, "UnSnap") \
X(TOGGLE_KEYBINDS, "ToggleKeybinds") \
X(FOCUS_OUTPUT, "FocusOutput") \
X(MOVE_TO_OUTPUT, "MoveToOutput") \
X(FIT_TO_OUTPUT, "FitToOutput") \
X(IF, "If") \
X(FOR_EACH, "ForEach") \
X(VIRTUAL_OUTPUT_ADD, "VirtualOutputAdd") \
X(VIRTUAL_OUTPUT_REMOVE, "VirtualOutputRemove") \
X(AUTO_PLACE, "AutoPlace") \
X(TOGGLE_TEARING, "ToggleTearing") \
X(SHADE, "Shade") \
X(UNSHADE, "Unshade") \
X(TOGGLE_SHADE, "ToggleShade") \
X(ENABLE_SCROLL_WHEEL_EMULATION, "EnableScrollWheelEmulation") \
X(DISABLE_SCROLL_WHEEL_EMULATION, "DisableScrollWheelEmulation") \
X(TOGGLE_SCROLL_WHEEL_EMULATION, "ToggleScrollWheelEmulation") \
X(ENABLE_TABLET_MOUSE_EMULATION, "EnableTabletMouseEmulation") \
X(DISABLE_TABLET_MOUSE_EMULATION, "DisableTabletMouseEmulation") \
X(TOGGLE_TABLET_MOUSE_EMULATION, "ToggleTabletMouseEmulation") \
X(TOGGLE_MAGNIFY, "ToggleMagnify") \
X(ZOOM_IN, "ZoomIn") \
X(ZOOM_OUT, "ZoomOut") \
X(TOGGLE_SHOW_DESKTOP, "ToggleShowDesktop") \
X(WARP_CURSOR, "WarpCursor") \
X(HIDE_CURSOR, "HideCursor") \
X(DEBUG_TOGGLE_KEY_STATE_INDICATOR, "DebugToggleKeyStateIndicator")
/*
* Will expand to:
*
* enum action_type {
* ACTION_TYPE_INVALID = 0,
* ACTION_TYPE_NONE,
* ACTION_TYPE_CLOSE,
* ACTION_TYPE_KILL,
* etc...
*/
enum action_type {
ACTION_TYPE_INVALID = 0,
ACTION_TYPE_NONE,
ACTION_TYPE_CLOSE,
ACTION_TYPE_KILL,
ACTION_TYPE_DEBUG,
ACTION_TYPE_EXECUTE,
ACTION_TYPE_EXIT,
ACTION_TYPE_MOVE_TO_EDGE,
ACTION_TYPE_TOGGLE_SNAP_TO_EDGE,
ACTION_TYPE_SNAP_TO_EDGE,
ACTION_TYPE_GROW_TO_EDGE,
ACTION_TYPE_SHRINK_TO_EDGE,
ACTION_TYPE_NEXT_WINDOW,
ACTION_TYPE_PREVIOUS_WINDOW,
ACTION_TYPE_RECONFIGURE,
ACTION_TYPE_SHOW_MENU,
ACTION_TYPE_TOGGLE_MAXIMIZE,
ACTION_TYPE_MAXIMIZE,
ACTION_TYPE_UNMAXIMIZE,
ACTION_TYPE_TOGGLE_FULLSCREEN,
ACTION_TYPE_SET_DECORATIONS,
ACTION_TYPE_TOGGLE_DECORATIONS,
ACTION_TYPE_TOGGLE_ALWAYS_ON_TOP,
ACTION_TYPE_TOGGLE_ALWAYS_ON_BOTTOM,
ACTION_TYPE_TOGGLE_OMNIPRESENT,
ACTION_TYPE_FOCUS,
ACTION_TYPE_UNFOCUS,
ACTION_TYPE_ICONIFY,
ACTION_TYPE_MOVE,
ACTION_TYPE_RAISE,
ACTION_TYPE_LOWER,
ACTION_TYPE_RESIZE,
ACTION_TYPE_RESIZE_RELATIVE,
ACTION_TYPE_MOVETO,
ACTION_TYPE_RESIZETO,
ACTION_TYPE_MOVETO_CURSOR,
ACTION_TYPE_MOVE_RELATIVE,
ACTION_TYPE_SEND_TO_DESKTOP,
ACTION_TYPE_GO_TO_DESKTOP,
ACTION_TYPE_TOGGLE_SNAP_TO_REGION,
ACTION_TYPE_SNAP_TO_REGION,
ACTION_TYPE_UNSNAP,
ACTION_TYPE_TOGGLE_KEYBINDS,
ACTION_TYPE_FOCUS_OUTPUT,
ACTION_TYPE_MOVE_TO_OUTPUT,
ACTION_TYPE_FIT_TO_OUTPUT,
ACTION_TYPE_IF,
ACTION_TYPE_FOR_EACH,
ACTION_TYPE_VIRTUAL_OUTPUT_ADD,
ACTION_TYPE_VIRTUAL_OUTPUT_REMOVE,
ACTION_TYPE_AUTO_PLACE,
ACTION_TYPE_TOGGLE_TEARING,
ACTION_TYPE_SHADE,
ACTION_TYPE_UNSHADE,
ACTION_TYPE_TOGGLE_SHADE,
ACTION_TYPE_ENABLE_SCROLL_WHEEL_EMULATION,
ACTION_TYPE_DISABLE_SCROLL_WHEEL_EMULATION,
ACTION_TYPE_TOGGLE_SCROLL_WHEEL_EMULATION,
ACTION_TYPE_ENABLE_TABLET_MOUSE_EMULATION,
ACTION_TYPE_DISABLE_TABLET_MOUSE_EMULATION,
ACTION_TYPE_TOGGLE_TABLET_MOUSE_EMULATION,
ACTION_TYPE_TOGGLE_MAGNIFY,
ACTION_TYPE_ZOOM_IN,
ACTION_TYPE_ZOOM_OUT,
ACTION_TYPE_WARP_CURSOR,
ACTION_TYPE_HIDE_CURSOR,
#define X(name, str) ACTION_TYPE_##name,
ACTION_TYPE_LIST(X)
#undef X
};
/*
* Will expand to:
*
* const char *action_names[] = {
* [ACTION_TYPE_INVALID] = "INVALID",
* [ACTION_TYPE_NONE] = "None",
* [ACTION_TYPE_CLOSE] = "Close",
* [ACTION_TYPE_KILL] = "Kill",
* etc...
*/
const char *action_names[] = {
"INVALID",
"None",
"Close",
"Kill",
"Debug",
"Execute",
"Exit",
"MoveToEdge",
"ToggleSnapToEdge",
"SnapToEdge",
"GrowToEdge",
"ShrinkToEdge",
"NextWindow",
"PreviousWindow",
"Reconfigure",
"ShowMenu",
"ToggleMaximize",
"Maximize",
"UnMaximize",
"ToggleFullscreen",
"SetDecorations",
"ToggleDecorations",
"ToggleAlwaysOnTop",
"ToggleAlwaysOnBottom",
"ToggleOmnipresent",
"Focus",
"Unfocus",
"Iconify",
"Move",
"Raise",
"Lower",
"Resize",
"ResizeRelative",
"MoveTo",
"ResizeTo",
"MoveToCursor",
"MoveRelative",
"SendToDesktop",
"GoToDesktop",
"ToggleSnapToRegion",
"SnapToRegion",
"UnSnap",
"ToggleKeybinds",
"FocusOutput",
"MoveToOutput",
"FitToOutput",
"If",
"ForEach",
"VirtualOutputAdd",
"VirtualOutputRemove",
"AutoPlace",
"ToggleTearing",
"Shade",
"Unshade",
"ToggleShade",
"EnableScrollWheelEmulation",
"DisableScrollWheelEmulation",
"ToggleScrollWheelEmulation",
"EnableTabletMouseEmulation",
"DisableTabletMouseEmulation",
"ToggleTabletMouseEmulation",
"ToggleMagnify",
"ZoomIn",
"ZoomOut",
"WarpCursor",
"HideCursor",
[ACTION_TYPE_INVALID] = "INVALID",
#define X(name, str)[ACTION_TYPE_##name] = str,
ACTION_TYPE_LIST(X)
#undef X
NULL
};
#undef ACTION_TYPE_LIST
void
action_arg_add_str(struct action *action, const char *key, const char *value)
{
@ -368,6 +339,8 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
break;
case ACTION_TYPE_NEXT_WINDOW:
case ACTION_TYPE_PREVIOUS_WINDOW:
case ACTION_TYPE_NEXT_WINDOW_IMMEDIATE:
case ACTION_TYPE_PREVIOUS_WINDOW_IMMEDIATE:
if (!strcasecmp(argument, "workspace")) {
if (!strcasecmp(content, "all")) {
action_arg_add_int(action, argument, CYCLE_WORKSPACE_ALL);
@ -739,23 +712,23 @@ action_list_free(struct wl_list *action_list)
}
static void
show_menu(struct server *server, struct view *view, struct cursor_context *ctx,
show_menu(struct view *view, struct cursor_context *ctx,
const char *menu_name, bool at_cursor,
const char *pos_x, const char *pos_y)
{
if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH
&& server->input_mode != LAB_INPUT_STATE_MENU) {
if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH
&& server.input_mode != LAB_INPUT_STATE_MENU) {
/* Prevent opening a menu while resizing / moving a view */
return;
}
struct menu *menu = menu_get_by_id(server, menu_name);
struct menu *menu = menu_get_by_id(menu_name);
if (!menu) {
return;
}
int x = server->seat.cursor->x;
int y = server->seat.cursor->y;
int x = server.seat.cursor->x;
int y = server.seat.cursor->y;
/* The client menu needs an active client */
bool is_client_menu = !strcasecmp(menu_name, "client-menu");
@ -774,7 +747,7 @@ show_menu(struct server *server, struct view *view, struct cursor_context *ctx,
int lx, ly;
wlr_scene_node_coords(ctx->node, &lx, &ly);
/* MAX() prevents negative x when the window is maximized */
x = MAX(x, lx - server->theme->menu_border_width);
x = MAX(x, lx - rc.theme->menu_border_width);
}
}
@ -783,8 +756,8 @@ show_menu(struct server *server, struct view *view, struct cursor_context *ctx,
* x/y can be number, "center" or a %percent of screen dimensions
*/
if (pos_x && pos_y) {
struct output *output = output_nearest_to(server,
server->seat.cursor->x, server->seat.cursor->y);
struct output *output = output_nearest_to(
server.seat.cursor->x, server.seat.cursor->y);
struct wlr_box usable = output_usable_area_in_layout_coords(output);
if (!strcasecmp(pos_x, "center")) {
@ -828,8 +801,7 @@ show_menu(struct server *server, struct view *view, struct cursor_context *ctx,
}
static struct view *
view_for_action(struct view *activator, struct server *server,
struct action *action, struct cursor_context *ctx)
view_for_action(struct view *activator, struct action *action, struct cursor_context *ctx)
{
/* View is explicitly specified for mousebinds */
if (activator) {
@ -841,17 +813,16 @@ view_for_action(struct view *activator, struct server *server,
case ACTION_TYPE_FOCUS:
case ACTION_TYPE_MOVE:
case ACTION_TYPE_RESIZE: {
*ctx = get_cursor_context(server);
*ctx = get_cursor_context();
return ctx->view;
}
default:
return server->active_view;
return server.active_view;
}
}
struct action_prompt {
/* Set when created */
struct server *server;
struct action *action;
struct view *view;
@ -928,7 +899,7 @@ print_prompt_command(struct buf *buf, const char *format,
}
static void
action_prompt_create(struct view *view, struct server *server, struct action *action)
action_prompt_create(struct view *view, struct action *action)
{
struct buf command = BUF_INIT;
print_prompt_command(&command, rc.prompt_command, action, rc.theme);
@ -945,7 +916,6 @@ action_prompt_create(struct view *view, struct server *server, struct action *ac
close(pipe_fd);
struct action_prompt *prompt = znew(*prompt);
prompt->server = server;
prompt->action = action;
prompt->view = view;
prompt->pid = prompt_pid;
@ -994,8 +964,7 @@ action_check_prompt_result(pid_t pid, int exit_code)
}
if (actions) {
wlr_log(WLR_INFO, "Running actions");
actions_run(prompt->view, prompt->server,
actions, /*cursor_ctx*/ NULL);
actions_run(prompt->view, actions, /*cursor_ctx*/ NULL);
} else {
wlr_log(WLR_INFO, "No actions for selected branch");
}
@ -1026,14 +995,13 @@ match_queries(struct view *view, struct action *action)
}
static struct output *
get_target_output(struct output *output, struct server *server,
struct action *action)
get_target_output(struct output *output, struct action *action)
{
const char *output_name = action_get_str(action, "output", NULL);
struct output *target = NULL;
if (output_name) {
target = output_from_name(server, output_name);
target = output_from_name(output_name);
} else {
enum lab_edge edge =
action_get_int(action, "direction", LAB_EDGE_NONE);
@ -1049,9 +1017,9 @@ get_target_output(struct output *output, struct server *server,
}
static void
warp_cursor(struct server *server, struct view *view, const char *to, const char *x, const char *y)
warp_cursor(struct view *view, const char *to, const char *x, const char *y)
{
struct output *output = output_nearest_to_cursor(server);
struct output *output = output_nearest_to_cursor();
struct wlr_box target_area = {0};
int goto_x;
int goto_y;
@ -1082,12 +1050,12 @@ warp_cursor(struct server *server, struct view *view, const char *to, const char
target_area.y + target_area.height + offset_y;
}
wlr_cursor_warp(server->seat.cursor, NULL, goto_x, goto_y);
cursor_update_focus(server);
wlr_cursor_warp(server.seat.cursor, NULL, goto_x, goto_y);
cursor_update_focus();
}
static void
run_action(struct view *view, struct server *server, struct action *action,
run_action(struct view *view, struct action *action,
struct cursor_context *ctx)
{
switch (action->type) {
@ -1109,7 +1077,7 @@ run_action(struct view *view, struct server *server, struct action *action,
}
break;
case ACTION_TYPE_DEBUG:
debug_dump_scene(server);
debug_dump_scene();
break;
case ACTION_TYPE_EXECUTE: {
struct buf cmd = BUF_INIT;
@ -1120,7 +1088,7 @@ run_action(struct view *view, struct server *server, struct action *action,
break;
}
case ACTION_TYPE_EXIT:
wl_display_terminate(server->wl_display);
wl_display_terminate(server.wl_display);
break;
case ACTION_TYPE_MOVE_TO_EDGE:
if (view) {
@ -1175,18 +1143,33 @@ run_action(struct view *view, struct server *server, struct action *action,
.app_id = action_get_int(action, "identifier",
CYCLE_APP_ID_ALL),
};
if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
cycle_step(server, dir);
if (server.input_mode == LAB_INPUT_STATE_CYCLE) {
cycle_step(dir);
} else {
cycle_begin(server, dir, filter);
cycle_begin(dir, filter);
}
break;
}
case ACTION_TYPE_NEXT_WINDOW_IMMEDIATE:
case ACTION_TYPE_PREVIOUS_WINDOW_IMMEDIATE: {
enum lab_cycle_dir dir = (action->type == ACTION_TYPE_NEXT_WINDOW_IMMEDIATE) ?
LAB_CYCLE_DIR_FORWARD : LAB_CYCLE_DIR_BACKWARD;
struct cycle_filter filter = {
.workspace = action_get_int(action, "workspace",
rc.window_switcher.workspace_filter),
.output = action_get_int(action, "output",
CYCLE_OUTPUT_ALL),
.app_id = action_get_int(action, "identifier",
CYCLE_APP_ID_ALL),
};
cycle_immediate(dir, filter);
break;
}
case ACTION_TYPE_RECONFIGURE:
kill(getpid(), SIGHUP);
break;
case ACTION_TYPE_SHOW_MENU:
show_menu(server, view, ctx,
show_menu(view, ctx,
action_get_str(action, "menu", NULL),
action_get_bool(action, "atCursor", true),
action_get_str(action, "x.position", NULL),
@ -1253,27 +1236,13 @@ run_action(struct view *view, struct server *server, struct action *action,
}
break;
case ACTION_TYPE_UNFOCUS:
seat_focus_surface(&server->seat, NULL);
seat_focus_surface(&server.seat, NULL);
break;
case ACTION_TYPE_ICONIFY:
if (view) {
view_minimize(view, true);
}
break;
case ACTION_TYPE_MOVE:
if (view) {
/*
* If triggered by mousebind, grab context was already
* set by button press handling. For keybind-triggered
* Move, set it now from current cursor position.
*/
if (view != server->seat.pressed.ctx.view) {
interactive_set_grab_context(ctx);
}
interactive_begin(view, LAB_INPUT_STATE_MOVE,
LAB_EDGE_NONE);
}
break;
case ACTION_TYPE_RAISE:
if (view) {
view_move_to_front(view);
@ -1284,6 +1253,20 @@ run_action(struct view *view, struct server *server, struct action *action,
view_move_to_back(view);
}
break;
case ACTION_TYPE_MOVE:
if (view) {
/*
* If triggered by mousebind, grab context was already
* set by button press handling. For keybind-triggered
* Move, set it now from current cursor position.
*/
if (view != server.seat.pressed.ctx.view) {
interactive_set_grab_context(ctx);
}
interactive_begin(view, LAB_INPUT_STATE_MOVE,
LAB_EDGE_NONE);
}
break;
case ACTION_TYPE_RESIZE:
if (view) {
/*
@ -1298,7 +1281,7 @@ run_action(struct view *view, struct server *server, struct action *action,
* set by button press handling. For keybind-triggered
* Resize, set it now from current cursor position.
*/
if (view != server->seat.pressed.ctx.view) {
if (view != server.seat.pressed.ctx.view) {
interactive_set_grab_context(ctx);
}
interactive_begin(view, LAB_INPUT_STATE_RESIZE,
@ -1329,7 +1312,7 @@ run_action(struct view *view, struct server *server, struct action *action,
/*
* To support only setting one of width/height
* in <action name="ResizeTo" width="" height=""/>
* in <action name="ResizeTo" width="" height="" />
* we fall back to current dimension when unset.
*/
struct wlr_box box = {
@ -1354,7 +1337,8 @@ run_action(struct view *view, struct server *server, struct action *action,
"Action MoveToCursor is deprecated. To ensure your config works in future labwc "
"releases, please use <action name=\"AutoPlace\" policy=\"cursor\">");
if (view) {
view_move_to_cursor(view);
view_place_by_policy(view, /* allow_cursor */ true,
LAB_PLACE_CURSOR);
}
break;
case ACTION_TYPE_SEND_TO_DESKTOP:
@ -1372,12 +1356,12 @@ run_action(struct view *view, struct server *server, struct action *action,
* a required argument for both SendToDesktop and GoToDesktop.
*/
struct workspace *target_workspace = workspaces_find(
server->workspaces.current, to, wrap);
server.workspaces.current, to, wrap);
if (action->type == ACTION_TYPE_GO_TO_DESKTOP) {
bool toggle = action_get_bool(action, "toggle", false);
if (target_workspace == server->workspaces.current
if (target_workspace == server.workspaces.current
&& toggle) {
target_workspace = server->workspaces.last;
target_workspace = server.workspaces.last;
}
}
if (!target_workspace) {
@ -1388,8 +1372,8 @@ run_action(struct view *view, struct server *server, struct action *action,
follow = action_get_bool(action, "follow", true);
/* Ensure that the focus is not on another desktop */
if (!follow && server->active_view == view) {
desktop_focus_topmost_view(server);
if (!follow && server.active_view == view) {
desktop_focus_topmost_view();
}
}
if (follow) {
@ -1403,7 +1387,7 @@ run_action(struct view *view, struct server *server, struct action *action,
break;
}
struct output *target_output =
get_target_output(view->output, server, action);
get_target_output(view->output, action);
if (target_output) {
view_move_to_output(view, target_output);
}
@ -1455,9 +1439,9 @@ run_action(struct view *view, struct server *server, struct action *action,
}
break;
case ACTION_TYPE_FOCUS_OUTPUT: {
struct output *output = output_nearest_to_cursor(server);
struct output *output = output_nearest_to_cursor();
struct output *target_output =
get_target_output(output, server, action);
get_target_output(output, action);
if (target_output) {
desktop_focus_output(target_output);
}
@ -1470,7 +1454,7 @@ run_action(struct view *view, struct server *server, struct action *action,
* We delay the selection and execution of the
* branch until we get a response from the user.
*/
action_prompt_create(view, server, action);
action_prompt_create(view, action);
} else if (view) {
struct wl_list *actions;
if (match_queries(view, action)) {
@ -1479,7 +1463,7 @@ run_action(struct view *view, struct server *server, struct action *action,
actions = action_get_actionlist(action, "else");
}
if (actions) {
actions_run(view, server, actions, ctx);
actions_run(view, actions, ctx);
}
}
break;
@ -1490,7 +1474,7 @@ run_action(struct view *view, struct server *server, struct action *action,
struct wl_array views;
wl_array_init(&views);
view_array_append(server, &views, LAB_VIEW_CRITERIA_NONE);
view_array_append(&views, LAB_VIEW_CRITERIA_NONE);
struct view **item;
wl_array_for_each(item, &views) {
@ -1501,14 +1485,14 @@ run_action(struct view *view, struct server *server, struct action *action,
actions = action_get_actionlist(action, "else");
}
if (actions) {
actions_run(*item, server, actions, ctx);
actions_run(*item, actions, ctx);
}
}
wl_array_release(&views);
if (!matches) {
actions = action_get_actionlist(action, "none");
if (actions) {
actions_run(view, server, actions, NULL);
actions_run(view, actions, NULL);
}
}
break;
@ -1517,7 +1501,7 @@ run_action(struct view *view, struct server *server, struct action *action,
/* TODO: rename this argument to "outputName" */
const char *output_name =
action_get_str(action, "output_name", NULL);
output_virtual_add(server, output_name,
output_virtual_add(output_name,
/*store_wlr_output*/ NULL);
break;
}
@ -1525,7 +1509,7 @@ run_action(struct view *view, struct server *server, struct action *action,
/* TODO: rename this argument to "outputName" */
const char *output_name =
action_get_str(action, "output_name", NULL);
output_virtual_remove(server, output_name);
output_virtual_remove(output_name);
break;
}
case ACTION_TYPE_AUTO_PLACE:
@ -1572,14 +1556,14 @@ run_action(struct view *view, struct server *server, struct action *action,
}
break;
case ACTION_TYPE_ENABLE_SCROLL_WHEEL_EMULATION:
server->seat.cursor_scroll_wheel_emulation = true;
server.seat.cursor_scroll_wheel_emulation = true;
break;
case ACTION_TYPE_DISABLE_SCROLL_WHEEL_EMULATION:
server->seat.cursor_scroll_wheel_emulation = false;
server.seat.cursor_scroll_wheel_emulation = false;
break;
case ACTION_TYPE_TOGGLE_SCROLL_WHEEL_EMULATION:
server->seat.cursor_scroll_wheel_emulation =
!server->seat.cursor_scroll_wheel_emulation;
server.seat.cursor_scroll_wheel_emulation =
!server.seat.cursor_scroll_wheel_emulation;
break;
case ACTION_TYPE_ENABLE_TABLET_MOUSE_EMULATION:
rc.tablet.force_mouse_emulation = true;
@ -1591,23 +1575,29 @@ run_action(struct view *view, struct server *server, struct action *action,
rc.tablet.force_mouse_emulation = !rc.tablet.force_mouse_emulation;
break;
case ACTION_TYPE_TOGGLE_MAGNIFY:
magnifier_toggle(server);
magnifier_toggle();
break;
case ACTION_TYPE_ZOOM_IN:
magnifier_set_scale(server, MAGNIFY_INCREASE);
magnifier_set_scale(MAGNIFY_INCREASE);
break;
case ACTION_TYPE_ZOOM_OUT:
magnifier_set_scale(server, MAGNIFY_DECREASE);
magnifier_set_scale(MAGNIFY_DECREASE);
break;
case ACTION_TYPE_TOGGLE_SHOW_DESKTOP:
show_desktop_toggle();
break;
case ACTION_TYPE_WARP_CURSOR: {
const char *to = action_get_str(action, "to", "output");
const char *x = action_get_str(action, "x", "center");
const char *y = action_get_str(action, "y", "center");
warp_cursor(server, view, to, x, y);
warp_cursor(view, to, x, y);
break;
}
case ACTION_TYPE_HIDE_CURSOR:
cursor_set_visible(&server->seat, false);
cursor_set_visible(&server.seat, false);
break;
case ACTION_TYPE_DEBUG_TOGGLE_KEY_STATE_INDICATOR:
key_state_indicator_toggle();
break;
case ACTION_TYPE_INVALID:
wlr_log(WLR_ERROR, "Not executing unknown action");
@ -1625,8 +1615,7 @@ run_action(struct view *view, struct server *server, struct action *action,
}
void
actions_run(struct view *activator, struct server *server,
struct wl_list *actions, struct cursor_context *cursor_ctx)
actions_run(struct view *activator, struct wl_list *actions, struct cursor_context *cursor_ctx)
{
if (!actions) {
wlr_log(WLR_ERROR, "empty actions");
@ -1643,7 +1632,7 @@ actions_run(struct view *activator, struct server *server,
struct action *action;
wl_list_for_each(action, actions, link) {
if (server->input_mode == LAB_INPUT_STATE_CYCLE
if (server.input_mode == LAB_INPUT_STATE_CYCLE
&& action->type != ACTION_TYPE_NEXT_WINDOW
&& action->type != ACTION_TYPE_PREVIOUS_WINDOW) {
wlr_log(WLR_INFO, "Only NextWindow or PreviousWindow "
@ -1658,8 +1647,8 @@ actions_run(struct view *activator, struct server *server,
* Refetch view because it may have been changed due to the
* previous action
*/
struct view *view = view_for_action(activator, server, action, &ctx);
struct view *view = view_for_action(activator, action, &ctx);
run_action(view, server, action, &ctx);
run_action(view, action, &ctx);
}
}

View file

@ -95,6 +95,7 @@ buffer_adopt_cairo_surface(cairo_surface_t *surface)
buffer->surface = surface;
buffer->data = cairo_image_surface_get_data(buffer->surface);
assert(buffer->data);
buffer->format = DRM_FORMAT_ARGB8888;
buffer->stride = cairo_image_surface_get_stride(buffer->surface);
buffer->logical_width = width;
@ -107,6 +108,12 @@ buffer_adopt_cairo_surface(cairo_surface_t *surface)
struct lab_data_buffer *
buffer_create_cairo(uint32_t logical_width, uint32_t logical_height, float scale)
{
if (!logical_width || !logical_height) {
wlr_log(WLR_ERROR, "Failed to create cairo buffer of %ux%u: dimensions must be > 0",
logical_width, logical_height);
return NULL;
}
/* Create an image surface with the scaled size */
cairo_surface_t *surface =
cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
@ -142,6 +149,7 @@ buffer_create_from_data(void *pixel_data, uint32_t width, uint32_t height,
buffer->logical_width = width;
buffer->logical_height = height;
buffer->data = pixel_data;
assert(buffer->data);
buffer->format = DRM_FORMAT_ARGB8888;
buffer->stride = stride;
buffer->surface = cairo_image_surface_create_for_data(
@ -188,6 +196,10 @@ buffer_resize(struct lab_data_buffer *src_buffer, int width, int height,
struct lab_data_buffer *buffer =
buffer_create_cairo(width, height, scale);
if (!buffer) {
wlr_log(WLR_INFO, "Failed to resize buffer to %dx%d", width, height);
return NULL;
}
cairo_t *cairo = cairo_create(buffer->surface);
struct wlr_box container = {

View file

@ -53,15 +53,14 @@ buf_expand_shell_variables(struct buf *s)
if (s->data[i] == '$' && isvalid(s->data[i+1])) {
/* expand environment variable */
buf_clear(&environment_variable);
buf_add(&environment_variable, s->data + i + 1);
char *p = environment_variable.data;
while (isvalid(*p)) {
++p;
int len = 0;
while (isvalid(s->data[i + 1 + len])) {
buf_add_char(&environment_variable, s->data[i + 1 + len]);
++len;
}
*p = '\0';
i += strlen(environment_variable.data);
i += len;
strip_curly_braces(environment_variable.data);
p = getenv(environment_variable.data);
char *p = getenv(environment_variable.data);
if (p) {
buf_add(&tmp, p);
}

View file

@ -79,9 +79,10 @@ font_get_buffer_size(int max_width, const char *text, struct font *font,
void
font_buffer_create(struct lab_data_buffer **buffer, int max_width,
int height, const char *text, struct font *font, const float *color,
cairo_pattern_t *bg_pattern, double scale)
cairo_pattern_t *bg_pattern, double scale, bool use_markup)
{
if (string_null_or_empty(text)) {
*buffer = NULL;
return;
}
@ -91,6 +92,13 @@ font_buffer_create(struct lab_data_buffer **buffer, int max_width,
height = computed_height;
}
if (height <= 0 || width <= 0) {
wlr_log(WLR_INFO, "Refusing to create invisible font buffer of %dx%d",
width, height);
*buffer = NULL;
return;
}
*buffer = buffer_create_cairo(width, height, scale);
if (!*buffer) {
wlr_log(WLR_ERROR, "Failed to create font buffer");
@ -123,7 +131,13 @@ font_buffer_create(struct lab_data_buffer **buffer, int max_width,
PangoLayout *layout = pango_cairo_create_layout(cairo);
pango_context_set_round_glyph_positions(pango_layout_get_context(layout), false);
pango_layout_set_width(layout, width * PANGO_SCALE);
pango_layout_set_text(layout, text, -1);
if (use_markup) {
pango_layout_set_markup(layout, text, -1);
} else {
pango_layout_set_text(layout, text, -1);
}
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
if (!opaque_bg) {

View file

@ -3,6 +3,7 @@
#include <assert.h>
#include <wlr/types/wlr_scene.h>
#include "common/mem.h"
#include "common/scene-helpers.h"
struct border_scene {
struct wlr_scene_tree *tree;
@ -26,20 +27,20 @@ lab_scene_rect_create(struct wlr_scene_tree *parent,
rect->border_width = opts->border_width;
rect->nr_borders = opts->nr_borders;
rect->borders = znew_n(rect->borders[0], opts->nr_borders);
rect->tree = wlr_scene_tree_create(parent);
rect->tree = lab_wlr_scene_tree_create(parent);
if (opts->bg_color) {
rect->fill = wlr_scene_rect_create(rect->tree, 0, 0, opts->bg_color);
rect->fill = lab_wlr_scene_rect_create(rect->tree, 0, 0, opts->bg_color);
}
for (int i = 0; i < rect->nr_borders; i++) {
struct border_scene *border = &rect->borders[i];
float *color = opts->border_colors[i];
border->tree = wlr_scene_tree_create(rect->tree);
border->top = wlr_scene_rect_create(border->tree, 0, 0, color);
border->right = wlr_scene_rect_create(border->tree, 0, 0, color);
border->bottom = wlr_scene_rect_create(border->tree, 0, 0, color);
border->left = wlr_scene_rect_create(border->tree, 0, 0, color);
border->tree = lab_wlr_scene_tree_create(rect->tree);
border->top = lab_wlr_scene_rect_create(border->tree, 0, 0, color);
border->right = lab_wlr_scene_rect_create(border->tree, 0, 0, color);
border->bottom = lab_wlr_scene_rect_create(border->tree, 0, 0, color);
border->left = lab_wlr_scene_rect_create(border->tree, 0, 0, color);
}
rect->node_destroy.notify = handle_node_destroy;

View file

@ -5,6 +5,7 @@
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>
#include "common/mem.h"
#include "magnifier.h"
#include "output.h"
@ -24,6 +25,34 @@ lab_wlr_surface_from_node(struct wlr_scene_node *node)
return NULL;
}
struct wlr_scene_tree *
lab_wlr_scene_tree_create(struct wlr_scene_tree *parent)
{
struct wlr_scene_tree *tree = wlr_scene_tree_create(parent);
die_if_null(tree);
return tree;
}
struct wlr_scene_rect *
lab_wlr_scene_rect_create(struct wlr_scene_tree *parent,
int width, int height, const float color[static 4])
{
struct wlr_scene_rect *rect =
wlr_scene_rect_create(parent, width, height, color);
die_if_null(rect);
return rect;
}
struct wlr_scene_buffer *
lab_wlr_scene_buffer_create(struct wlr_scene_tree *parent,
struct wlr_buffer *buffer)
{
struct wlr_scene_buffer *scene_buffer =
wlr_scene_buffer_create(parent, buffer);
die_if_null(scene_buffer);
return scene_buffer;
}
struct wlr_scene_node *
lab_wlr_scene_get_prev_node(struct wlr_scene_node *node)
{

View file

@ -89,6 +89,37 @@ out:
g_strfreev(argv);
}
void
spawn_sync_no_shell(char const *command)
{
GError *err = NULL;
gchar **argv = NULL;
assert(command);
g_shell_parse_argv((gchar *)command, NULL, &argv, &err);
if (err) {
g_message("%s", err->message);
g_error_free(err);
return;
}
pid_t child = fork();
switch (child) {
case -1:
wlr_log(WLR_ERROR, "unable to fork()");
break;
case 0:
reset_signals_and_limits();
execvp(argv[0], argv);
_exit(1);
default:
waitpid(child, NULL, 0);
break;
}
g_strfreev(argv);
}
pid_t
spawn_primary_client(const char *command)
{

View file

@ -121,9 +121,9 @@ update_keycodes_iter(struct xkb_keymap *keymap, xkb_keycode_t key, void *data)
}
void
keybind_update_keycodes(struct server *server)
keybind_update_keycodes(void)
{
struct xkb_state *state = server->seat.keyboard_group->keyboard.xkb_state;
struct xkb_state *state = server.seat.keyboard_group->keyboard.xkb_state;
struct xkb_keymap *keymap = xkb_state_get_keymap(state);
struct keybind *keybind;

View file

@ -25,6 +25,7 @@ libinput_category_init(struct libinput_category *l)
l->dwt = -1;
l->click_method = -1;
l->scroll_method = -1;
l->scroll_button = -1;
l->send_events_mode = -1;
l->have_calibration_matrix = false;
l->scroll_factor = 1.0;

View file

@ -94,6 +94,43 @@ parse_window_type(const char *type)
}
}
uint32_t
parse_privileged_interface(const char *name)
{
static const char * const ifaces[] = {
"wp_drm_lease_device_v1",
"zwlr_gamma_control_manager_v1",
"zwlr_output_manager_v1",
"zwlr_output_power_manager_v1",
"zwp_input_method_manager_v2",
"zwlr_virtual_pointer_manager_v1",
"zwp_virtual_keyboard_manager_v1",
"zwlr_export_dmabuf_manager_v1",
"zwlr_screencopy_manager_v1",
"ext_data_control_manager_v1",
"zwlr_data_control_manager_v1",
"wp_security_context_manager_v1",
"ext_idle_notifier_v1",
"zwlr_foreign_toplevel_manager_v1",
"ext_foreign_toplevel_list_v1",
"ext_session_lock_manager_v1",
"zwlr_layer_shell_v1",
"ext_workspace_manager_v1",
"ext_image_copy_capture_manager_v1",
"ext_output_image_capture_source_manager_v1",
};
static_assert(ARRAY_SIZE(ifaces) <= 32,
"return type too small for amount of privileged protocols");
for (size_t i = 0; i < ARRAY_SIZE(ifaces); i++) {
if (!strcmp(name, ifaces[i])) {
return 1 << i;
}
}
return 0;
}
/*
* Openbox/labwc comparison
*
@ -300,6 +337,8 @@ fill_window_rule(xmlNode *node)
set_property(content, &window_rule->ignore_configure_request);
} else if (!strcasecmp(key, "fixedPosition")) {
set_property(content, &window_rule->fixed_position);
} else if (!strcasecmp(key, "allowAlwaysOnTop")) {
set_property(content, &window_rule->allow_always_on_top);
}
}
@ -563,6 +602,7 @@ fill_keybind(xmlNode *node)
lab_xml_get_bool(node, "onRelease", &keybind->on_release);
lab_xml_get_bool(node, "layoutDependent", &keybind->use_syms_only);
lab_xml_get_bool(node, "allowWhenLocked", &keybind->allow_when_locked);
lab_xml_get_bool(node, "overrideInhibition", &keybind->override_inhibition);
append_parsed_actions(node, &keybind->actions);
}
@ -573,9 +613,9 @@ fill_mousebind(xmlNode *node, const char *context)
/*
* Example of what we are parsing:
* <mousebind button="Left" action="DoubleClick">
* <action name="Focus"/>
* <action name="Raise"/>
* <action name="ToggleMaximize"/>
* <action name="Focus" />
* <action name="Raise" />
* <action name="ToggleMaximize" />
* </mousebind>
*/
@ -851,9 +891,19 @@ fill_libinput_category(xmlNode *node)
} else if (!strcasecmp(content, "twofinger")) {
category->scroll_method =
LIBINPUT_CONFIG_SCROLL_2FG;
} else if (!strcasecmp(content, "onbutton")) {
category->scroll_method =
LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
} else {
wlr_log(WLR_ERROR, "invalid scrollMethod");
}
} else if (!strcasecmp(key, "scrollButton")) {
int button = atoi(content);
if (button != 0) {
category->scroll_button = button;
} else {
wlr_log(WLR_ERROR, "invalid scrollButton");
}
} else if (!strcasecmp(key, "sendEventsMode")) {
category->send_events_mode =
get_send_events_mode(content);
@ -1030,6 +1080,16 @@ set_tearing_mode(const char *str, enum tearing_mode *variable)
}
}
static void
set_hdr_mode(const char *str, enum render_bit_depth *variable)
{
if (parse_bool(str, -1) == 1) {
*variable = LAB_RENDER_BIT_DEPTH_10;
} else {
*variable = LAB_RENDER_BIT_DEPTH_8;
}
}
/* Returns true if the node's children should also be traversed */
static bool
entry(xmlNode *node, char *nodename, char *content)
@ -1094,6 +1154,8 @@ entry(xmlNode *node, char *nodename, char *content)
set_adaptive_sync_mode(content, &rc.adaptive_sync);
} else if (!strcasecmp(nodename, "allowTearing.core")) {
set_tearing_mode(content, &rc.allow_tearing);
} else if (!strcasecmp(nodename, "Hdr.core")) {
set_hdr_mode(content, &rc.target_render_depth);
} else if (!strcasecmp(nodename, "autoEnableOutputs.core")) {
set_bool(content, &rc.auto_enable_outputs);
} else if (!strcasecmp(nodename, "reuseOutputMode.core")) {
@ -1145,6 +1207,9 @@ entry(xmlNode *node, char *nodename, char *content)
set_bool(content, &rc.focus_follow_mouse_requires_movement);
} else if (!strcasecmp(nodename, "raiseOnFocus.focus")) {
set_bool(content, &rc.raise_on_focus);
} else if (!strcasecmp(nodename, "raiseOnFocusDelay.focus")) {
long val = strtol(content, NULL, 10);
rc.raise_on_focus_delay_ms = val > 0 ? (uint32_t)val : 0;
} else if (!strcasecmp(nodename, "doubleClickTime.mouse")) {
long doubleclick_time_parsed = strtol(content, NULL, 10);
if (doubleclick_time_parsed > 0) {
@ -1353,6 +1418,12 @@ entry(xmlNode *node, char *nodename, char *content)
} else if (!strcasecmp(nodename, "relativeMotionSensitivity.tabletTool")) {
rc.tablet_tool.relative_motion_sensitivity =
tablet_get_dbl_if_positive(content, "relativeMotionSensitivity");
} else if (!strcasecmp(nodename, "minPressure.tabletTool")) {
rc.tablet_tool.min_pressure =
tablet_get_dbl_if_positive(content, "minPressure");
} else if (!strcasecmp(nodename, "maxPressure.tabletTool")) {
rc.tablet_tool.max_pressure =
tablet_get_dbl_if_positive(content, "maxPressure");
} else if (!strcasecmp(nodename, "ignoreButtonReleasePeriod.menu")) {
rc.menu_ignore_button_release_period = atoi(content);
} else if (!strcasecmp(nodename, "showIcons.menu")) {
@ -1369,6 +1440,16 @@ entry(xmlNode *node, char *nodename, char *content)
rc.mag_increment = MAX(0, rc.mag_increment);
} else if (!strcasecmp(nodename, "useFilter.magnifier")) {
set_bool(content, &rc.mag_filter);
} else if (!strcasecmp(nodename, "privilegedInterfaces")) {
rc.allowed_interfaces = 0;
} else if (!strcasecmp(nodename, "allow.privilegedInterfaces")) {
uint32_t iface_id = parse_privileged_interface(content);
if (iface_id) {
rc.allowed_interfaces |= iface_id;
} else {
wlr_log(WLR_ERROR, "invalid value for "
"<privilegedInterfaces><allow>");
}
}
return false;
@ -1449,8 +1530,10 @@ rcxml_init(void)
rc.gap = 0;
rc.adaptive_sync = LAB_ADAPTIVE_SYNC_DISABLED;
rc.allow_tearing = LAB_TEARING_DISABLED;
rc.target_render_depth = LAB_RENDER_BIT_DEPTH_DEFAULT;
rc.auto_enable_outputs = true;
rc.reuse_output_mode = false;
rc.allowed_interfaces = UINT32_MAX;
rc.xwayland_persistence = false;
rc.primary_selection = true;
@ -1463,6 +1546,7 @@ rcxml_init(void)
rc.focus_follow_mouse = false;
rc.focus_follow_mouse_requires_movement = true;
rc.raise_on_focus = false;
rc.raise_on_focus_delay_ms = 0;
rc.doubleclick_time = 500;
@ -1473,6 +1557,8 @@ rcxml_init(void)
tablet_load_default_button_mappings();
rc.tablet_tool.motion = LAB_MOTION_ABSOLUTE;
rc.tablet_tool.relative_motion_sensitivity = 1.0;
rc.tablet_tool.min_pressure = 0.0;
rc.tablet_tool.max_pressure = 1.0;
rc.repeat_rate = 25;
rc.repeat_delay = 600;
@ -1648,6 +1734,8 @@ deduplicate_key_bindings(void)
wl_list_remove(&current->link);
keybind_destroy(current);
cleared++;
} else if (actions_contain_toggle_keybinds(&current->actions)) {
current->override_inhibition = true;
}
}
if (replaced) {

View file

@ -8,8 +8,8 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <wlr/backend/drm.h>
#include <wlr/backend/multi.h>
#include <wlr/config.h>
#include <wlr/util/log.h>
#include "common/buf.h"
#include "common/dir.h"
@ -20,8 +20,13 @@
#include "config/rcxml.h"
#include "labwc.h"
#if WLR_HAS_DRM_BACKEND
#include <wlr/backend/drm.h>
#else
#define wlr_backend_is_drm(backend) (false)
#endif
static const char *const env_vars[] = {
"DISPLAY",
"WAYLAND_DISPLAY",
"XDG_CURRENT_DESKTOP",
"XCURSOR_SIZE",
@ -185,10 +190,8 @@ backend_check_drm(struct wlr_backend *backend, void *is_drm)
}
static bool
should_update_activation(struct server *server)
should_update_activation(void)
{
assert(server);
static const char *act_env = "LABWC_UPDATE_ACTIVATION_ENV";
char *env = getenv(act_env);
if (env) {
@ -206,14 +209,29 @@ should_update_activation(struct server *server)
/* With no valid preference, update when a DRM backend is in use */
bool have_drm = false;
wlr_multi_for_each_backend(server->backend, backend_check_drm, &have_drm);
wlr_multi_for_each_backend(server.backend, backend_check_drm, &have_drm);
return have_drm;
}
static void
update_activation_env(struct server *server, bool initialize)
execute_update(const char *env_keys, const char *env_unset_keys, bool initialize)
{
if (!should_update_activation(server)) {
char *cmd =
strdup_printf("dbus-update-activation-environment %s",
initialize ? env_keys : env_unset_keys);
spawn_sync_no_shell(cmd);
free(cmd);
cmd = strdup_printf("systemctl --user %s %s",
initialize ? "import-environment" : "unset-environment", env_keys);
spawn_sync_no_shell(cmd);
free(cmd);
}
static void
update_activation_env(bool initialize)
{
if (!should_update_activation()) {
return;
}
@ -229,19 +247,18 @@ update_activation_env(struct server *server, bool initialize)
char *env_keys = str_join(env_vars, "%s", " ");
char *env_unset_keys = initialize ? NULL : str_join(env_vars, "%s=", " ");
char *cmd =
strdup_printf("dbus-update-activation-environment %s",
initialize ? env_keys : env_unset_keys);
spawn_async_no_shell(cmd);
free(cmd);
cmd = strdup_printf("systemctl --user %s %s",
initialize ? "import-environment" : "unset-environment", env_keys);
spawn_async_no_shell(cmd);
free(cmd);
execute_update(env_keys, env_unset_keys, initialize);
free(env_keys);
free(env_unset_keys);
#if HAVE_XWAYLAND
if (server.xwayland) {
// DISPLAY is only set if xwayland was initialized successfully,
// so we only update the env in that case
execute_update("DISPLAY", "DISPLAY=", initialize);
}
#endif
}
void
@ -314,18 +331,18 @@ session_run_script(const char *script)
}
void
session_autostart_init(struct server *server)
session_autostart_init(void)
{
/* Update dbus and systemd user environment, each may fail gracefully */
update_activation_env(server, /* initialize */ true);
update_activation_env(/* initialize */ true);
session_run_script("autostart");
}
void
session_shutdown(struct server *server)
session_shutdown(void)
{
session_run_script("shutdown");
/* Clear the dbus and systemd user environment, each may fail gracefully */
update_activation_env(server, /* initialize */ false);
update_activation_env(/* initialize */ false);
}

View file

@ -9,6 +9,7 @@
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "config/types.h"
#include "labwc.h"
#include "node.h"
#include "output.h"
@ -18,17 +19,16 @@
#include "theme.h"
#include "view.h"
static bool init_cycle(struct server *server, struct cycle_filter filter);
static void update_cycle(struct server *server);
static void destroy_cycle(struct server *server);
static bool init_cycle(struct cycle_filter filter);
static void update_cycle(void);
static void destroy_cycle(void);
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;
struct theme *theme = rc.theme;
struct lab_scene_rect *rect = server.cycle.preview_outline;
if (!rect) {
struct lab_scene_rect_options opts = {
.border_colors = (float *[3]) {
@ -39,10 +39,10 @@ update_preview_outlines(struct view *view)
.nr_borders = 3,
.border_width = theme->osd_window_switcher_preview_border_width,
};
rect = lab_scene_rect_create(&server->scene->tree, &opts);
rect = lab_scene_rect_create(&server.scene->tree, &opts);
wlr_scene_node_place_above(&rect->tree->node,
&server->cycle_preview_tree->node);
server->cycle.preview_outline = rect;
&server.cycle_preview_tree->node);
server.cycle.preview_outline = rect;
}
struct wlr_box geo = ssd_max_extents(view);
@ -52,21 +52,21 @@ update_preview_outlines(struct view *view)
/* 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)
get_next_selected_view(enum lab_cycle_dir dir)
{
struct cycle_state *cycle = &server->cycle;
struct cycle_state *cycle = &server.cycle;
assert(cycle->selected_view);
assert(!wl_list_empty(&server->cycle.views));
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) {
if (link == &server.cycle.views) {
link = link->next;
}
} else {
link = cycle->selected_view->cycle_link.prev;
if (link == &server->cycle.views) {
if (link == &server.cycle.views) {
link = link->prev;
}
}
@ -83,22 +83,22 @@ get_first_view(struct wl_list *views)
}
void
cycle_reinitialize(struct server *server)
cycle_reinitialize(void)
{
struct cycle_state *cycle = &server->cycle;
struct cycle_state *cycle = &server.cycle;
if (server->input_mode != LAB_INPUT_STATE_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);
get_next_selected_view(LAB_CYCLE_DIR_BACKWARD);
struct cycle_filter filter = cycle->filter;
destroy_cycle(server);
if (init_cycle(server, filter)) {
destroy_cycle();
if (init_cycle(filter)) {
/*
* Preserve the selected view (or its previous view) if it's
* still in the cycle list
@ -110,104 +110,104 @@ cycle_reinitialize(struct server *server)
} else {
/* should be unreachable */
wlr_log(WLR_ERROR, "could not find view to select");
cycle->selected_view = get_first_view(&server->cycle.views);
cycle->selected_view = get_first_view(&server.cycle.views);
}
update_cycle(server);
update_cycle();
} else {
/* Failed to re-init window switcher, exit */
cycle_finish(server, /*switch_focus*/ false);
cycle_finish(/*switch_focus*/ false);
}
}
void
cycle_on_cursor_release(struct server *server, struct wlr_scene_node *node)
cycle_on_cursor_release(struct wlr_scene_node *node)
{
assert(server->input_mode == LAB_INPUT_STATE_CYCLE);
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);
server.cycle.selected_view = item->view;
cycle_finish(/*switch_focus*/ true);
}
static void
restore_preview_node(struct server *server)
restore_preview_node(void)
{
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);
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_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);
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;
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,
cycle_begin(enum lab_cycle_dir direction,
struct cycle_filter filter)
{
if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
return;
}
if (!init_cycle(server, filter)) {
if (!init_cycle(filter)) {
return;
}
struct view *active_view = server->active_view;
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;
/* Select the active view if 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);
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);
server.cycle.selected_view = get_next_selected_view(direction);
seat_focus_override_begin(&server->seat,
seat_focus_override_begin(&server.seat,
LAB_INPUT_STATE_CYCLE, LAB_CURSOR_DEFAULT);
update_cycle(server);
update_cycle();
/* Update cursor, in case it is within the area covered by OSD */
cursor_update_focus(server);
cursor_update_focus();
}
void
cycle_step(struct server *server, enum lab_cycle_dir direction)
cycle_step(enum lab_cycle_dir direction)
{
assert(server->input_mode == LAB_INPUT_STATE_CYCLE);
assert(server.input_mode == LAB_INPUT_STATE_CYCLE);
server->cycle.selected_view = get_next_selected_view(server, direction);
update_cycle(server);
server.cycle.selected_view = get_next_selected_view(direction);
update_cycle();
}
void
cycle_finish(struct server *server, bool switch_focus)
cycle_finish(bool switch_focus)
{
if (server->input_mode != LAB_INPUT_STATE_CYCLE) {
if (server.input_mode != LAB_INPUT_STATE_CYCLE) {
return;
}
struct view *selected_view = server->cycle.selected_view;
destroy_cycle(server);
struct view *selected_view = server.cycle.selected_view;
destroy_cycle();
seat_focus_override_end(&server->seat, /*restore_focus*/ false);
seat_focus_override_end(&server.seat, /*restore_focus*/ false);
/* Hiding OSD may need a cursor change */
cursor_update_focus(server);
cursor_update_focus();
if (switch_focus && selected_view) {
if (rc.window_switcher.unshade) {
@ -222,16 +222,15 @@ preview_selected_view(struct view *view)
{
assert(view);
assert(view->scene_tree);
struct server *server = view->server;
struct cycle_state *cycle = &server->cycle;
struct cycle_state *cycle = &server.cycle;
/* Move previous selected node back to its original place */
restore_preview_node(server);
restore_preview_node();
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(
struct wlr_scene_rect *dummy_rect = lab_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);
@ -246,7 +245,7 @@ preview_selected_view(struct view *view)
}
wlr_scene_node_reparent(cycle->preview_node,
view->server->cycle_preview_tree);
server.cycle_preview_tree);
/* Finally raise selected node to the top */
wlr_scene_node_raise_to_top(cycle->preview_node);
@ -265,8 +264,7 @@ get_osd_impl(void)
}
static uint64_t
get_outputs_by_filter(struct server *server,
enum cycle_output_filter output_filter)
get_outputs_by_filter(enum cycle_output_filter output_filter)
{
struct output *output = NULL;
@ -274,15 +272,15 @@ get_outputs_by_filter(struct server *server,
case CYCLE_OUTPUT_ALL:
break;
case CYCLE_OUTPUT_CURSOR:
output = output_nearest_to_cursor(server);
output = output_nearest_to_cursor();
break;
case CYCLE_OUTPUT_FOCUSED: {
struct view *view = server->active_view;
struct view *view = server.active_view;
if (view && output_is_usable(view->output)) {
output = view->output;
} else {
/* Fallback to pointer */
output = output_nearest_to_cursor(server);
output = output_nearest_to_cursor();
}
break;
}
@ -325,52 +323,111 @@ handle_osd_tree_destroy(struct wl_listener *listener, void *data)
free(osd_output);
}
/* Return false on failure */
static bool
init_cycle(struct server *server, struct cycle_filter filter)
static enum lab_view_criteria
get_view_criteria(struct cycle_filter *filter)
{
enum lab_view_criteria criteria =
LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER
| LAB_VIEW_CRITERIA_NO_DIALOG;
if (filter.workspace == CYCLE_WORKSPACE_CURRENT) {
if (filter->workspace == CYCLE_WORKSPACE_CURRENT) {
criteria |= LAB_VIEW_CRITERIA_CURRENT_WORKSPACE;
}
return criteria;
}
uint64_t cycle_outputs =
get_outputs_by_filter(server, filter.output);
static const char *
get_cycle_app_id(struct cycle_filter *filter)
{
if (filter->app_id == CYCLE_APP_ID_CURRENT && server.active_view) {
return server.active_view->app_id;
}
return NULL;
}
const char *cycle_app_id = NULL;
if (filter.app_id == CYCLE_APP_ID_CURRENT && server->active_view) {
cycle_app_id = server->active_view->app_id;
static struct wl_list *prev(struct wl_list *elm) { return elm->prev; }
static struct wl_list *next(struct wl_list *elm) { return elm->next; }
void
cycle_immediate(enum lab_cycle_dir direction, struct cycle_filter filter)
{
if (wl_list_empty(&server.views)) {
return;
}
struct view *view;
for_each_view(view, &server->views, criteria) {
if (!(cycle_outputs & view->output->id_bit)) {
enum lab_view_criteria criteria = get_view_criteria(&filter);
uint64_t cycle_outputs = get_outputs_by_filter(filter.output);
const char *cycle_app_id = get_cycle_app_id(&filter);
struct wl_list *head = &server.views;
struct wl_list *(*iter)(struct wl_list *list);
iter = direction == LAB_CYCLE_DIR_FORWARD ? next : prev;
struct wl_list *from = (direction == LAB_CYCLE_DIR_FORWARD) && server.active_view
? &server.active_view->link : head;
for (struct wl_list *elm = iter(from); elm != head; elm = iter(elm)) {
struct view *view = wl_container_of(elm, view, link);
if (!view_matches_criteria(view, criteria)) {
continue;
}
if (filter.output != CYCLE_OUTPUT_ALL) {
if (!view->output || !(cycle_outputs & view->output->id_bit)) {
continue;
}
}
if (cycle_app_id && strcmp(view->app_id, cycle_app_id) != 0) {
continue;
}
if (server.active_view && direction == LAB_CYCLE_DIR_FORWARD) {
/*
* When cycling forward, the current active view needs to be
* sent to back to keep the same sequence and avoid getting
* stuck in the 2 topmost views.
*/
view_move_to_back(server.active_view);
}
desktop_focus_view(view, true);
break;
}
cursor_update_focus();
}
/* Return false on failure */
static bool
init_cycle(struct cycle_filter filter)
{
enum lab_view_criteria criteria = get_view_criteria(&filter);
uint64_t cycle_outputs = get_outputs_by_filter(filter.output);
const char *cycle_app_id = get_cycle_app_id(&filter);
struct view *view;
for_each_view(view, &server.views, criteria) {
if (filter.output != CYCLE_OUTPUT_ALL) {
if (!view->output || !(cycle_outputs & view->output->id_bit)) {
continue;
}
}
if (cycle_app_id && strcmp(view->app_id, cycle_app_id) != 0) {
continue;
}
if (rc.window_switcher.order == WINDOW_SWITCHER_ORDER_AGE) {
insert_view_ordered_by_age(&server->cycle.views, view);
insert_view_ordered_by_age(&server.cycle.views, view);
} else {
wl_list_append(&server->cycle.views, &view->cycle_link);
wl_list_append(&server.cycle.views, &view->cycle_link);
}
}
if (wl_list_empty(&server->cycle.views)) {
if (wl_list_empty(&server.cycle.views)) {
wlr_log(WLR_DEBUG, "no views to switch between");
return false;
}
server->cycle.filter = filter;
server.cycle.filter = filter;
if (rc.window_switcher.osd.show) {
/* Create OSD */
uint64_t osd_outputs = get_outputs_by_filter(server,
rc.window_switcher.osd.output_filter);
uint64_t osd_outputs = get_outputs_by_filter(rc.window_switcher.osd.output_filter);
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
wl_list_for_each(output, &server.outputs, link) {
if (!(osd_outputs & output->id_bit)) {
continue;
}
@ -379,7 +436,7 @@ init_cycle(struct server *server, struct cycle_filter filter)
}
struct cycle_osd_output *osd_output = znew(*osd_output);
wl_list_append(&server->cycle.osd_outputs, &osd_output->link);
wl_list_append(&server.cycle.osd_outputs, &osd_output->link);
osd_output->output = output;
wl_list_init(&osd_output->items);
@ -395,9 +452,9 @@ init_cycle(struct server *server, struct cycle_filter filter)
}
static void
update_cycle(struct server *server)
update_cycle(void)
{
struct cycle_state *cycle = &server->cycle;
struct cycle_state *cycle = &server.cycle;
if (rc.window_switcher.osd.show) {
struct cycle_osd_output *osd_output;
@ -418,29 +475,29 @@ update_cycle(struct server *server)
}
}
/* Resets all the states in server->cycle */
/* Resets all the states in server.cycle */
static void
destroy_cycle(struct server *server)
destroy_cycle(void)
{
struct cycle_osd_output *osd_output, *tmp;
wl_list_for_each_safe(osd_output, tmp, &server->cycle.osd_outputs, link) {
wl_list_for_each_safe(osd_output, tmp, &server.cycle.osd_outputs, link) {
/* calls handle_osd_tree_destroy() */
wlr_scene_node_destroy(&osd_output->tree->node);
}
restore_preview_node(server);
restore_preview_node();
if (server->cycle.preview_outline) {
wlr_scene_node_destroy(&server->cycle.preview_outline->tree->node);
if (server.cycle.preview_outline) {
wlr_scene_node_destroy(&server.cycle.preview_outline->tree->node);
}
struct view *view, *tmp2;
wl_list_for_each_safe(view, tmp2, &server->cycle.views, cycle_link) {
wl_list_for_each_safe(view, tmp2, &server.cycle.views, cycle_link) {
wl_list_remove(&view->cycle_link);
view->cycle_link = (struct wl_list){0};
}
server->cycle = (struct cycle_state){0};
wl_list_init(&server->cycle.views);
wl_list_init(&server->cycle.osd_outputs);
server.cycle = (struct cycle_state){0};
wl_list_init(&server.cycle.views);
wl_list_init(&server.cycle.osd_outputs);
}

View file

@ -9,6 +9,7 @@
#include "common/lab-scene-rect.h"
#include "common/list.h"
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "common/string-helpers.h"
#include "config/rcxml.h"
#include "cycle.h"
@ -27,11 +28,11 @@ struct cycle_osd_classic_item {
};
static void
create_fields_scene(struct server *server, struct view *view,
create_fields_scene(struct view *view,
struct wlr_scene_tree *parent, const float *text_color,
const float *bg_color, int field_widths_sum, int x, int y)
{
struct theme *theme = server->theme;
struct theme *theme = rc.theme;
struct window_switcher_classic_theme *switcher_theme =
&theme->osd_window_switcher_classic;
@ -45,8 +46,7 @@ create_fields_scene(struct server *server, struct view *view,
int icon_size = MIN(field_width,
switcher_theme->item_icon_size);
struct scaled_icon_buffer *icon_buffer =
scaled_icon_buffer_create(parent,
server, icon_size, icon_size);
scaled_icon_buffer_create(parent, icon_size, icon_size);
scaled_icon_buffer_set_view(icon_buffer, view);
node = &icon_buffer->scene_buffer->node;
height = icon_size;
@ -80,17 +80,16 @@ static void
cycle_osd_classic_init(struct cycle_osd_output *osd_output)
{
struct output *output = osd_output->output;
struct server *server = output->server;
struct theme *theme = server->theme;
struct theme *theme = rc.theme;
struct window_switcher_classic_theme *switcher_theme =
&theme->osd_window_switcher_classic;
int padding = theme->osd_border_width + switcher_theme->padding;
bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1;
const char *workspace_name = server->workspaces.current->name;
int nr_views = wl_list_length(&server->cycle.views);
const char *workspace_name = server.workspaces.current->name;
int nr_views = wl_list_length(&server.cycle.views);
struct wlr_box output_box;
wlr_output_layout_get_box(server->output_layout, output->wlr_output,
wlr_output_layout_get_box(server.output_layout, output->wlr_output,
&output_box);
int w = switcher_theme->width;
@ -108,7 +107,7 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output)
int h = workspace_name_h + nr_visible_views * switcher_theme->item_height
+ 2 * padding;
osd_output->tree = wlr_scene_tree_create(output->cycle_osd_tree);
osd_output->tree = lab_wlr_scene_tree_create(output->cycle_osd_tree);
float *text_color = theme->osd_label_text_color;
float *bg_color = theme->osd_bg_color;
@ -161,15 +160,15 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output)
float *active_bg_color = switcher_theme->item_active_bg_color;
float *active_border_color = switcher_theme->item_active_border_color;
osd_output->items_tree = wlr_scene_tree_create(osd_output->tree);
osd_output->items_tree = lab_wlr_scene_tree_create(osd_output->tree);
/* Draw text for each node */
struct view *view;
wl_list_for_each(view, &server->cycle.views, cycle_link) {
wl_list_for_each(view, &server.cycle.views, cycle_link) {
struct cycle_osd_classic_item *item = znew(*item);
wl_list_append(&osd_output->items, &item->base.link);
item->base.view = view;
item->base.tree = wlr_scene_tree_create(osd_output->items_tree);
item->base.tree = lab_wlr_scene_tree_create(osd_output->items_tree);
node_descriptor_create(&item->base.tree->node,
LAB_NODE_CYCLE_OSD_ITEM, NULL, item);
/*
@ -191,8 +190,8 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output)
int x = padding
+ switcher_theme->item_active_border_width
+ switcher_theme->item_padding_x;
item->normal_tree = wlr_scene_tree_create(item->base.tree);
item->active_tree = wlr_scene_tree_create(item->base.tree);
item->normal_tree = lab_wlr_scene_tree_create(item->base.tree);
item->active_tree = lab_wlr_scene_tree_create(item->base.tree);
wlr_scene_node_set_enabled(&item->active_tree->node, false);
/* Highlight around selected window's item */
@ -209,13 +208,13 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output)
wlr_scene_node_set_position(&highlight_rect->tree->node, padding, y);
/* hitbox for mouse clicks */
struct wlr_scene_rect *hitbox = wlr_scene_rect_create(item->base.tree,
struct wlr_scene_rect *hitbox = lab_wlr_scene_rect_create(item->base.tree,
w - 2 * padding, switcher_theme->item_height, (float[4]) {0});
wlr_scene_node_set_position(&hitbox->node, padding, y);
create_fields_scene(server, view, item->normal_tree,
create_fields_scene(view, item->normal_tree,
text_color, bg_color, field_widths_sum, x, y);
create_fields_scene(server, view, item->active_tree,
create_fields_scene(view, item->active_tree,
text_color, active_bg_color, field_widths_sum, x, y);
y += switcher_theme->item_height;
@ -242,12 +241,11 @@ error:;
static void
cycle_osd_classic_update(struct cycle_osd_output *osd_output)
{
struct server *server = osd_output->output->server;
cycle_osd_scroll_update(osd_output);
struct cycle_osd_classic_item *item;
wl_list_for_each(item, &osd_output->items, base.link) {
bool active = item->base.view == server->cycle.selected_view;
bool active = item->base.view == server.cycle.selected_view;
wlr_scene_node_set_enabled(&item->normal_tree->node, !active);
wlr_scene_node_set_enabled(&item->active_tree->node, active);
}

View file

@ -45,7 +45,7 @@ static const char *
get_desktop_name(struct view *view)
{
#if HAVE_LIBSFDO
const char *name = desktop_entry_name_lookup(view->server, view->app_id);
const char *name = desktop_entry_name_lookup(view->app_id);
if (name) {
return name;
}
@ -148,7 +148,7 @@ static void
field_set_output_short(struct buf *buf, struct view *view, const char *format)
{
/* custom type conversion-specifier: o */
if (wl_list_length(&view->server->outputs) > 1 &&
if (wl_list_length(&server.outputs) > 1 &&
output_is_usable(view->output)) {
buf_add(buf, view->output->wlr_output->name);
}

View file

@ -2,6 +2,7 @@
#include <assert.h>
#include <wlr/types/wlr_scene.h>
#include "common/lab-scene-rect.h"
#include "common/scene-helpers.h"
#include "labwc.h"
#include "cycle.h"
#include "output.h"
@ -23,7 +24,7 @@ cycle_osd_scroll_init(struct cycle_osd_output *osd_output, struct wlr_box bar_ar
scroll->top_row_idx = 0;
scroll->bar_area = bar_area;
scroll->delta_y = delta_y;
scroll->bar_tree = wlr_scene_tree_create(osd_output->tree);
scroll->bar_tree = lab_wlr_scene_tree_create(osd_output->tree);
wlr_scene_node_set_position(&scroll->bar_tree->node,
bar_area.x, bar_area.y);
@ -41,12 +42,10 @@ cycle_osd_scroll_init(struct cycle_osd_output *osd_output, struct wlr_box bar_ar
static int
get_cycle_idx(struct cycle_osd_output *osd_output)
{
struct server *server = osd_output->output->server;
int idx = 0;
struct cycle_osd_item *item;
wl_list_for_each(item, &osd_output->items, link) {
if (item->view == server->cycle.selected_view) {
if (item->view == server.cycle.selected_view) {
return idx;
}
idx++;

View file

@ -2,6 +2,7 @@
#include <assert.h>
#include <wlr/render/allocator.h>
#include <wlr/render/swapchain.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_scene.h>
#include "config/rcxml.h"
@ -10,6 +11,7 @@
#include "common/lab-scene-rect.h"
#include "common/list.h"
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "cycle.h"
#include "labwc.h"
#include "node.h"
@ -27,7 +29,7 @@ struct cycle_osd_thumbnail_item {
};
static void
render_node(struct server *server, struct wlr_render_pass *pass,
render_node(struct wlr_render_pass *pass,
struct wlr_scene_node *node, int x, int y)
{
switch (node->type) {
@ -35,7 +37,7 @@ render_node(struct server *server, struct wlr_render_pass *pass,
struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child;
wl_list_for_each(child, &tree->children, link) {
render_node(server, pass, child, x + node->x, y + node->y);
render_node(pass, child, x + node->x, y + node->y);
}
break;
}
@ -45,8 +47,12 @@ render_node(struct server *server, struct wlr_render_pass *pass,
if (!scene_buffer->buffer) {
break;
}
struct wlr_texture *texture = wlr_texture_from_buffer(
server->renderer, scene_buffer->buffer);
struct wlr_texture *texture = NULL;
struct wlr_client_buffer *client_buffer =
wlr_client_buffer_get(scene_buffer->buffer);
if (client_buffer) {
texture = client_buffer->texture;
}
if (!texture) {
break;
}
@ -61,7 +67,6 @@ render_node(struct server *server, struct wlr_render_pass *pass,
},
.transform = scene_buffer->transform,
});
wlr_texture_destroy(texture);
break;
}
case WLR_SCENE_NODE_RECT:
@ -81,13 +86,16 @@ render_thumb(struct output *output, struct view *view)
*/
return NULL;
}
struct server *server = output->server;
struct wlr_buffer *buffer = wlr_allocator_create_buffer(server->allocator,
struct wlr_buffer *buffer = wlr_allocator_create_buffer(server.allocator,
view->current.width, view->current.height,
&output->wlr_output->swapchain->format);
if (!buffer) {
wlr_log(WLR_ERROR, "failed to allocate buffer for thumbnail");
return NULL;
}
struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(
server->renderer, buffer, NULL);
render_node(server, pass, &view->content_tree->node, 0, 0);
server.renderer, buffer, NULL);
render_node(pass, &view->content_tree->node, 0, 0);
if (!wlr_render_pass_submit(pass)) {
wlr_log(WLR_ERROR, "failed to submit render pass");
wlr_buffer_drop(buffer);
@ -119,8 +127,7 @@ static struct cycle_osd_thumbnail_item *
create_item_scene(struct wlr_scene_tree *parent, struct view *view,
struct cycle_osd_output *osd_output)
{
struct server *server = osd_output->output->server;
struct theme *theme = server->theme;
struct theme *theme = rc.theme;
struct window_switcher_thumbnail_theme *switcher_theme =
&theme->osd_window_switcher_thumbnail;
int padding = theme->border_width + switcher_theme->item_padding;
@ -138,7 +145,7 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view,
struct cycle_osd_thumbnail_item *item = znew(*item);
wl_list_append(&osd_output->items, &item->base.link);
struct wlr_scene_tree *tree = wlr_scene_tree_create(parent);
struct wlr_scene_tree *tree = lab_wlr_scene_tree_create(parent);
node_descriptor_create(&tree->node, LAB_NODE_CYCLE_OSD_ITEM, NULL, item);
item->base.tree = tree;
item->base.view = view;
@ -155,14 +162,14 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view,
item->active_bg = lab_scene_rect_create(tree, &opts);
/* hitbox for mouse clicks */
wlr_scene_rect_create(tree, switcher_theme->item_width,
lab_wlr_scene_rect_create(tree, switcher_theme->item_width,
switcher_theme->item_height, (float[4]) {0});
/* thumbnail */
struct wlr_buffer *thumb_buffer = render_thumb(osd_output->output, view);
if (thumb_buffer) {
struct wlr_scene_buffer *thumb_scene_buffer =
wlr_scene_buffer_create(tree, thumb_buffer);
lab_wlr_scene_buffer_create(tree, thumb_buffer);
wlr_buffer_drop(thumb_buffer);
struct wlr_box thumb_box = box_fit_within(
thumb_buffer->width, thumb_buffer->height,
@ -184,7 +191,7 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view,
/* icon */
int icon_size = switcher_theme->item_icon_size;
struct scaled_icon_buffer *icon_buffer =
scaled_icon_buffer_create(tree, server, icon_size, icon_size);
scaled_icon_buffer_create(tree, icon_size, icon_size);
scaled_icon_buffer_set_view(icon_buffer, view);
int x = (switcher_theme->item_width - icon_size) / 2;
int y = title_y - padding - icon_size + 10; /* slide by 10px */
@ -197,7 +204,7 @@ static void
get_items_geometry(struct output *output, int nr_thumbs,
int *nr_cols, int *nr_rows, int *nr_visible_rows)
{
struct theme *theme = output->server->theme;
struct theme *theme = rc.theme;
struct window_switcher_thumbnail_theme *switcher_theme =
&theme->osd_window_switcher_thumbnail;
int output_width, output_height;
@ -233,16 +240,15 @@ static void
cycle_osd_thumbnail_init(struct cycle_osd_output *osd_output)
{
struct output *output = osd_output->output;
struct server *server = output->server;
struct theme *theme = server->theme;
struct theme *theme = rc.theme;
struct window_switcher_thumbnail_theme *switcher_theme =
&theme->osd_window_switcher_thumbnail;
int padding = theme->osd_border_width + switcher_theme->padding;
osd_output->tree = wlr_scene_tree_create(output->cycle_osd_tree);
osd_output->items_tree = wlr_scene_tree_create(osd_output->tree);
osd_output->tree = lab_wlr_scene_tree_create(output->cycle_osd_tree);
osd_output->items_tree = lab_wlr_scene_tree_create(osd_output->tree);
int nr_views = wl_list_length(&server->cycle.views);
int nr_views = wl_list_length(&server.cycle.views);
assert(nr_views > 0);
int nr_cols, nr_rows, nr_visible_rows;
get_items_geometry(output, nr_views, &nr_cols, &nr_rows, &nr_visible_rows);
@ -250,7 +256,7 @@ cycle_osd_thumbnail_init(struct cycle_osd_output *osd_output)
/* items */
struct view *view;
int index = 0;
wl_list_for_each(view, &server->cycle.views, cycle_link) {
wl_list_for_each(view, &server.cycle.views, cycle_link) {
struct cycle_osd_thumbnail_item *item = create_item_scene(
osd_output->items_tree, view, osd_output);
if (!item) {
@ -291,7 +297,7 @@ cycle_osd_thumbnail_init(struct cycle_osd_output *osd_output)
/* center */
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,
&output_box);
int lx = output_box.x + (output_box.width - bg_opts.width) / 2;
int ly = output_box.y + (output_box.height - bg_opts.height) / 2;
@ -301,12 +307,11 @@ cycle_osd_thumbnail_init(struct cycle_osd_output *osd_output)
static void
cycle_osd_thumbnail_update(struct cycle_osd_output *osd_output)
{
struct server *server = osd_output->output->server;
cycle_osd_scroll_update(osd_output);
struct cycle_osd_thumbnail_item *item;
wl_list_for_each(item, &osd_output->items, base.link) {
bool active = (item->base.view == server->cycle.selected_view);
bool active = (item->base.view == server.cycle.selected_view);
wlr_scene_node_set_enabled(&item->active_bg->tree->node, active);
wlr_scene_node_set_enabled(
&item->active_label->scene_buffer->node, active);

View file

@ -89,10 +89,10 @@ get_view_part(struct view *view, struct wlr_scene_node *node)
}
static struct workspace *
get_workspace_from_node(struct server *server, struct wlr_scene_node *node)
get_workspace_from_node(struct wlr_scene_node *node)
{
struct workspace *workspace;
wl_list_for_each(workspace, &server->workspaces.all, link) {
wl_list_for_each(workspace, &server.workspaces.all, link) {
if (&workspace->tree->node == node) {
return workspace;
}
@ -101,31 +101,31 @@ get_workspace_from_node(struct server *server, struct wlr_scene_node *node)
}
static const char *
get_special(struct server *server, struct wlr_scene_node *node)
get_special(struct wlr_scene_node *node)
{
struct wlr_scene_tree *grand_parent =
node->parent ? node->parent->node.parent : NULL;
struct wlr_scene_tree *grand_grand_parent =
grand_parent ? grand_parent->node.parent : NULL;
if (node == &server->scene->tree.node) {
return "server->scene";
if (node == &server.scene->tree.node) {
return "server.scene";
}
if (node == &server->menu_tree->node) {
return "server->menu_tree";
if (node == &server.menu_tree->node) {
return "server.menu_tree";
}
if (node == &server->workspace_tree->node) {
return "server->workspace_tree";
if (node == &server.workspace_tree->node) {
return "server.workspace_tree";
}
if (node->parent == server->workspace_tree) {
struct workspace *workspace = get_workspace_from_node(server, node);
if (node->parent == server.workspace_tree) {
struct workspace *workspace = get_workspace_from_node(node);
if (workspace) {
return workspace->name;
}
return "unknown workspace";
}
if (grand_parent == server->workspace_tree) {
if (grand_parent == server.workspace_tree) {
struct workspace *workspace =
get_workspace_from_node(server, &node->parent->node);
get_workspace_from_node(&node->parent->node);
if (workspace) {
struct wlr_scene_tree **trees = workspace->view_trees;
if (node == &trees[VIEW_LAYER_NORMAL]->node) {
@ -138,9 +138,9 @@ get_special(struct server *server, struct wlr_scene_node *node)
}
return "unknown tree";
}
if (node->parent == &server->scene->tree) {
if (node->parent == &server.scene->tree) {
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
wl_list_for_each(output, &server.outputs, link) {
if (node == &output->cycle_osd_tree->node) {
return "output->osd_tree";
}
@ -157,33 +157,33 @@ get_special(struct server *server, struct wlr_scene_node *node)
}
}
}
if (node == &server->xdg_popup_tree->node) {
return "server->xdg_popup_tree";
if (node == &server.xdg_popup_tree->node) {
return "server.xdg_popup_tree";
}
if (node == &server->seat.drag.icons->node) {
if (node == &server.seat.drag.icons->node) {
return "seat->drag.icons";
}
if (server->seat.overlay.rect
&& node == &server->seat.overlay.rect->tree->node) {
if (server.seat.overlay.rect
&& node == &server.seat.overlay.rect->tree->node) {
/* Created on-demand */
return "seat->overlay.rect";
}
if (server->seat.input_method_relay->popup_tree
&& node == &server->seat.input_method_relay->popup_tree->node) {
if (server.seat.input_method_relay->popup_tree
&& node == &server.seat.input_method_relay->popup_tree->node) {
/* Created on-demand */
return "seat->im_relay->popup_tree";
}
if (server->cycle.preview_outline
&& node == &server->cycle.preview_outline->tree->node) {
if (server.cycle.preview_outline
&& node == &server.cycle.preview_outline->tree->node) {
/* Created on-demand */
return "cycle_state->preview_outline";
}
#if HAVE_XWAYLAND
if (node == &server->unmanaged_tree->node) {
return "server->unmanaged_tree";
if (node == &server.unmanaged_tree->node) {
return "server.unmanaged_tree";
}
#endif
if (grand_grand_parent == server->workspace_tree && node->data) {
if (grand_grand_parent == server.workspace_tree && node->data) {
last_view = node_view_from_node(node);
}
const char *view_part = get_view_part(last_view, node);
@ -209,10 +209,10 @@ get_center_padding(const char *text, uint8_t max_width)
}
static void
dump_tree(struct server *server, struct wlr_scene_node *node,
dump_tree(struct wlr_scene_node *node,
int pos, int x, int y)
{
const char *type = get_special(server, node);
const char *type = get_special(node);
if (pos) {
printf("%*c+-- ", pos, ' ');
@ -234,13 +234,13 @@ 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);
if ((IGNORE_MENU && node == &server->menu_tree->node)
if ((IGNORE_MENU && node == &server.menu_tree->node)
|| (IGNORE_SSD && last_view
&& ssd_debug_is_root_node(last_view->ssd, node))
|| (IGNORE_CYCLE_PREVIEW_OUTLINE && server->cycle.preview_outline
&& node == &server->cycle.preview_outline->tree->node)
|| (IGNORE_SNAPPING_OVERLAY && server->seat.overlay.rect
&& node == &server->seat.overlay.rect->tree->node)) {
|| (IGNORE_CYCLE_PREVIEW_OUTLINE && server.cycle.preview_outline
&& node == &server.cycle.preview_outline->tree->node)
|| (IGNORE_SNAPPING_OVERLAY && server.seat.overlay.rect
&& node == &server.seat.overlay.rect->tree->node)) {
printf("%*c%s\n", pos + 4 + INDENT_SIZE, ' ', "<skipping children>");
return;
}
@ -249,17 +249,17 @@ dump_tree(struct server *server, struct wlr_scene_node *node,
struct wlr_scene_node *child;
struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node);
wl_list_for_each(child, &tree->children, link) {
dump_tree(server, child, pos + INDENT_SIZE,
dump_tree(child, pos + INDENT_SIZE,
x + child->x, y + child->y);
}
}
}
void
debug_dump_scene(struct server *server)
debug_dump_scene(void)
{
printf("\n");
dump_tree(server, &server->scene->tree.node, 0, 0, 0);
dump_tree(&server.scene->tree.node, 0, 0, 0);
printf("\n");
/*

View file

@ -119,10 +119,10 @@ kde_server_decoration_update_default(void)
}
void
kde_server_decoration_init(struct server *server)
kde_server_decoration_init(void)
{
assert(!kde_deco_mgr);
kde_deco_mgr = wlr_server_decoration_manager_create(server->wl_display);
kde_deco_mgr = wlr_server_decoration_manager_create(server.wl_display);
if (!kde_deco_mgr) {
wlr_log(WLR_ERROR, "unable to create the kde server deco manager");
exit(EXIT_FAILURE);
@ -131,12 +131,12 @@ kde_server_decoration_init(struct server *server)
wl_list_init(&decorations);
kde_server_decoration_update_default();
wl_signal_add(&kde_deco_mgr->events.new_decoration, &server->kde_server_decoration);
server->kde_server_decoration.notify = handle_new_server_decoration;
wl_signal_add(&kde_deco_mgr->events.new_decoration, &server.kde_server_decoration);
server.kde_server_decoration.notify = handle_new_server_decoration;
}
void
kde_server_decoration_finish(struct server *server)
kde_server_decoration_finish(void)
{
wl_list_remove(&server->kde_server_decoration.link);
wl_list_remove(&server.kde_server_decoration.link);
}

View file

@ -115,22 +115,22 @@ xdg_toplevel_decoration(struct wl_listener *listener, void *data)
}
void
xdg_server_decoration_init(struct server *server)
xdg_server_decoration_init(void)
{
struct wlr_xdg_decoration_manager_v1 *xdg_deco_mgr = NULL;
xdg_deco_mgr = wlr_xdg_decoration_manager_v1_create(server->wl_display);
xdg_deco_mgr = wlr_xdg_decoration_manager_v1_create(server.wl_display);
if (!xdg_deco_mgr) {
wlr_log(WLR_ERROR, "unable to create the XDG deco manager");
exit(EXIT_FAILURE);
}
wl_signal_add(&xdg_deco_mgr->events.new_toplevel_decoration,
&server->xdg_toplevel_decoration);
server->xdg_toplevel_decoration.notify = xdg_toplevel_decoration;
&server.xdg_toplevel_decoration);
server.xdg_toplevel_decoration.notify = xdg_toplevel_decoration;
}
void
xdg_server_decoration_finish(struct server *server)
xdg_server_decoration_finish(void)
{
wl_list_remove(&server->xdg_toplevel_decoration.link);
wl_list_remove(&server.xdg_toplevel_decoration.link);
}

View file

@ -55,7 +55,7 @@ log_handler(enum sfdo_log_level level, const char *fmt, va_list args, void *tag)
}
void
desktop_entry_init(struct server *server)
desktop_entry_init(void)
{
struct sfdo *sfdo = znew(*sfdo);
@ -134,7 +134,7 @@ desktop_entry_init(struct server *server)
/* basedir_ctx is not referenced by other objects */
sfdo_basedir_ctx_destroy(basedir_ctx);
server->sfdo = sfdo;
server.sfdo = sfdo;
return;
err_icon_theme:
@ -155,9 +155,9 @@ err_basedir_ctx:
}
void
desktop_entry_finish(struct server *server)
desktop_entry_finish(void)
{
struct sfdo *sfdo = server->sfdo;
struct sfdo *sfdo = server.sfdo;
if (!sfdo) {
return;
}
@ -167,7 +167,7 @@ desktop_entry_finish(struct server *server)
sfdo_icon_ctx_destroy(sfdo->icon_ctx);
sfdo_desktop_ctx_destroy(sfdo->desktop_ctx);
free(sfdo);
server->sfdo = NULL;
server.sfdo = NULL;
}
struct icon_ctx {
@ -348,14 +348,14 @@ convert_img_type(enum sfdo_icon_file_format fmt)
}
struct lab_img *
desktop_entry_load_icon(struct server *server, const char *icon_name, int size, float scale)
desktop_entry_load_icon(const char *icon_name, int size, float scale)
{
/* static analyzer isn't able to detect the NULL check in string_null_or_empty() */
if (string_null_or_empty(icon_name) || !icon_name) {
return NULL;
}
struct sfdo *sfdo = server->sfdo;
struct sfdo *sfdo = server.sfdo;
if (!sfdo) {
return NULL;
}
@ -387,14 +387,13 @@ desktop_entry_load_icon(struct server *server, const char *icon_name, int size,
}
struct lab_img *
desktop_entry_load_icon_from_app_id(struct server *server,
const char *app_id, int size, float scale)
desktop_entry_load_icon_from_app_id(const char *app_id, int size, float scale)
{
if (string_null_or_empty(app_id)) {
return NULL;
}
struct sfdo *sfdo = server->sfdo;
struct sfdo *sfdo = server.sfdo;
if (!sfdo) {
return NULL;
}
@ -405,22 +404,22 @@ desktop_entry_load_icon_from_app_id(struct server *server,
icon_name = sfdo_desktop_entry_get_icon(entry, NULL);
}
struct lab_img *img = desktop_entry_load_icon(server, icon_name, size, scale);
struct lab_img *img = desktop_entry_load_icon(icon_name, size, scale);
if (!img) {
/* Icon not defined in .desktop file or could not be loaded */
img = desktop_entry_load_icon(server, app_id, size, scale);
img = desktop_entry_load_icon(app_id, size, scale);
}
return img;
}
const char *
desktop_entry_name_lookup(struct server *server, const char *app_id)
desktop_entry_name_lookup(const char *app_id)
{
if (string_null_or_empty(app_id)) {
return NULL;
}
struct sfdo *sfdo = server->sfdo;
struct sfdo *sfdo = server.sfdo;
if (!sfdo) {
return NULL;
}

View file

@ -9,11 +9,13 @@
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_xdg_shell.h>
#include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "dnd.h"
#include "labwc.h"
#include "layers.h"
#include "node.h"
#include "output.h"
#include "show-desktop.h"
#include "ssd.h"
#include "view.h"
#include "workspaces.h"
@ -23,7 +25,7 @@
#endif
void
desktop_arrange_all_views(struct server *server)
desktop_arrange_all_views(void)
{
/*
* Adjust window positions/sizes. Skip views with no size since
@ -36,7 +38,7 @@ desktop_arrange_all_views(struct server *server)
* views.
*/
struct view *view;
wl_list_for_each(view, &server->views, link) {
wl_list_for_each(view, &server.views, link) {
if (!wlr_box_empty(&view->pending)) {
view_adjust_for_layout_change(view);
}
@ -46,16 +48,16 @@ desktop_arrange_all_views(struct server *server)
static void
set_or_offer_focus(struct view *view)
{
struct seat *seat = &view->server->seat;
struct seat *seat = &server.seat;
switch (view_wants_focus(view)) {
case VIEW_WANTS_FOCUS_ALWAYS:
if (view->surface != seat->seat->keyboard_state.focused_surface) {
if (view->surface != seat->wlr_seat->keyboard_state.focused_surface) {
seat_focus_surface(seat, view->surface);
}
break;
case VIEW_WANTS_FOCUS_LIKELY:
case VIEW_WANTS_FOCUS_UNLIKELY:
if (view->surface != seat->seat->keyboard_state.focused_surface) {
if (view->surface != seat->wlr_seat->keyboard_state.focused_surface) {
view_offer_focus(view);
}
break;
@ -64,8 +66,51 @@ set_or_offer_focus(struct view *view)
}
}
static int
handle_auto_raise_timer(void *data)
{
(void)data;
struct view *view = server.pending_auto_raise_view;
server.pending_auto_raise_view = NULL;
if (view && view->mapped) {
view_move_to_front(view);
}
return 0; /* ignored per wl_event_loop docs */
}
void
desktop_focus_view(struct view *view, bool raise)
desktop_cancel_pending_auto_raise(void)
{
server.pending_auto_raise_view = NULL;
if (server.pending_auto_raise_timer) {
/* Disarm by setting to 0 ms */
wl_event_source_timer_update(server.pending_auto_raise_timer, 0);
}
}
static void
schedule_delayed_auto_raise(struct view *view)
{
server.pending_auto_raise_view = view;
if (!server.pending_auto_raise_timer) {
server.pending_auto_raise_timer =
wl_event_loop_add_timer(server.wl_event_loop,
handle_auto_raise_timer, NULL);
}
wl_event_source_timer_update(server.pending_auto_raise_timer,
rc.raise_on_focus_delay_ms);
}
/*
* The raise_on_focus_delay is only meant to dampen z-order churn from
* focus-follows-mouse cursor passes. Explicit focus changes (alt-tab,
* Focus action, xdg/xwayland activation, etc.) should raise immediately.
* allow_delay is therefore only set when the caller is the sloppy-focus
* path in desktop_focus_view_or_surface().
*/
static void
desktop_focus_view_internal(struct view *view, bool raise, bool allow_delay)
{
assert(view);
/*
@ -76,7 +121,7 @@ desktop_focus_view(struct view *view, bool raise)
return;
}
if (view->server->input_mode == LAB_INPUT_STATE_CYCLE) {
if (server.input_mode == LAB_INPUT_STATE_CYCLE) {
wlr_log(WLR_DEBUG, "not focusing window while window switching");
return;
}
@ -102,8 +147,17 @@ desktop_focus_view(struct view *view, bool raise)
workspaces_switch_to(view->workspace, /*update_focus*/ false);
}
/*
* A new focus change supersedes any pending auto-raise from a
* previous focus event, regardless of whether we raise now.
*/
desktop_cancel_pending_auto_raise();
if (raise) {
view_move_to_front(view);
if (allow_delay && rc.raise_on_focus_delay_ms > 0) {
schedule_delayed_auto_raise(view);
} else {
view_move_to_front(view);
}
}
/*
@ -113,6 +167,14 @@ desktop_focus_view(struct view *view, bool raise)
*/
struct view *dialog = view_get_modal_dialog(view);
set_or_offer_focus(dialog ? dialog : view);
show_desktop_reset();
}
void
desktop_focus_view(struct view *view, bool raise)
{
desktop_focus_view_internal(view, raise, /*allow_delay*/ false);
}
/* TODO: focus layer-shell surfaces also? */
@ -122,7 +184,7 @@ desktop_focus_view_or_surface(struct seat *seat, struct view *view,
{
assert(view || surface);
if (view) {
desktop_focus_view(view, raise);
desktop_focus_view_internal(view, raise, /*allow_delay*/ true);
#if HAVE_XWAYLAND
} else {
struct wlr_xwayland_surface *xsurface =
@ -135,10 +197,10 @@ desktop_focus_view_or_surface(struct seat *seat, struct view *view,
}
static struct view *
desktop_topmost_focusable_view(struct server *server)
desktop_topmost_focusable_view(void)
{
struct view *view;
for_each_view(view, &server->views,
for_each_view(view, &server.views,
LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) {
if (!view->minimized) {
return view;
@ -148,9 +210,9 @@ desktop_topmost_focusable_view(struct server *server)
}
void
desktop_focus_topmost_view(struct server *server)
desktop_focus_topmost_view(void)
{
struct view *view = desktop_topmost_focusable_view(server);
struct view *view = desktop_topmost_focusable_view();
if (view) {
desktop_focus_view(view, /*raise*/ true);
} else {
@ -158,49 +220,47 @@ desktop_focus_topmost_view(struct server *server)
* Defocus previous focused surface/view if no longer
* focusable (e.g. unmapped or on a different workspace).
*/
seat_focus_surface(&server->seat, NULL);
seat_focus_surface(&server.seat, NULL);
}
}
void
desktop_focus_output(struct output *output)
{
struct server *server = output->server;
if (!output_is_usable(output) || server->input_mode
if (!output_is_usable(output) || server.input_mode
!= LAB_INPUT_STATE_PASSTHROUGH) {
return;
}
struct view *view;
for_each_view(view, &server->views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) {
for_each_view(view, &server.views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) {
if (view->outputs & output->id_bit) {
desktop_focus_view(view, /*raise*/ false);
wlr_cursor_warp(server->seat.cursor, NULL,
wlr_cursor_warp(server.seat.cursor, NULL,
view->current.x + view->current.width / 2,
view->current.y + view->current.height / 2);
cursor_update_focus(server);
cursor_update_focus();
return;
}
}
/* No view found on desired output */
struct wlr_box layout_box;
wlr_output_layout_get_box(server->output_layout,
wlr_output_layout_get_box(server.output_layout,
output->wlr_output, &layout_box);
wlr_cursor_warp(server->seat.cursor, NULL,
wlr_cursor_warp(server.seat.cursor, NULL,
layout_box.x + output->usable_area.x + output->usable_area.width / 2,
layout_box.y + output->usable_area.y + output->usable_area.height / 2);
cursor_update_focus(server);
cursor_update_focus();
}
void
desktop_update_top_layer_visibility(struct server *server)
desktop_update_top_layer_visibility(void)
{
struct view *view;
struct output *output;
uint32_t top = ZWLR_LAYER_SHELL_V1_LAYER_TOP;
/* Enable all top layers */
wl_list_for_each(output, &server->outputs, link) {
wl_list_for_each(output, &server.outputs, link) {
if (!output_is_usable(output)) {
continue;
}
@ -212,7 +272,7 @@ desktop_update_top_layer_visibility(struct server *server)
* any views above it
*/
uint64_t outputs_covered = 0;
for_each_view(view, &server->views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) {
for_each_view(view, &server.views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) {
if (view->minimized) {
continue;
}
@ -262,22 +322,22 @@ avoid_edge_rounding_issues(struct cursor_context *ctx)
/* TODO: make this less big and scary */
struct cursor_context
get_cursor_context(struct server *server)
get_cursor_context(void)
{
struct cursor_context ret = {.type = LAB_NODE_NONE};
struct wlr_cursor *cursor = server->seat.cursor;
struct wlr_cursor *cursor = server.seat.cursor;
/* Prevent drag icons to be on top of the hitbox detection */
if (server->seat.drag.active) {
dnd_icons_show(&server->seat, false);
if (server.seat.drag.active) {
dnd_icons_show(&server.seat, false);
}
struct wlr_scene_node *node =
wlr_scene_node_at(&server->scene->tree.node,
wlr_scene_node_at(&server.scene->tree.node,
cursor->x, cursor->y, &ret.sx, &ret.sy);
if (server->seat.drag.active) {
dnd_icons_show(&server->seat, true);
if (server.seat.drag.active) {
dnd_icons_show(&server.seat, true);
}
if (!node) {
@ -292,7 +352,7 @@ get_cursor_context(struct server *server)
#if HAVE_XWAYLAND
/* TODO: attach LAB_NODE_UNMANAGED node-descriptor to unmanaged surfaces */
if (node->type == WLR_SCENE_NODE_BUFFER) {
if (node->parent == server->unmanaged_tree) {
if (node->parent == server.unmanaged_tree) {
ret.type = LAB_NODE_UNMANAGED;
return ret;
}

View file

@ -4,6 +4,7 @@
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>
#include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "input/cursor.h"
#include "labwc.h" /* for struct seat */
@ -17,8 +18,8 @@ handle_drag_request(struct wl_listener *listener, void *data)
struct wlr_seat_request_start_drag_event *event = data;
if (wlr_seat_validate_pointer_grab_serial(
seat->seat, event->origin, event->serial)) {
wlr_seat_start_pointer_drag(seat->seat, event->drag,
seat->wlr_seat, event->origin, event->serial)) {
wlr_seat_start_pointer_drag(seat->wlr_seat, event->drag,
event->serial);
} else {
wlr_data_source_destroy(event->drag->source);
@ -67,7 +68,7 @@ handle_drag_destroy(struct wl_listener *listener, void *data)
return;
}
struct cursor_context ctx = get_cursor_context(seat->server);
struct cursor_context ctx = get_cursor_context();
if (!ctx.surface) {
return;
}
@ -83,16 +84,16 @@ handle_drag_destroy(struct wl_listener *listener, void *data)
void
dnd_init(struct seat *seat)
{
seat->drag.icons = wlr_scene_tree_create(&seat->server->scene->tree);
seat->drag.icons = lab_wlr_scene_tree_create(&server.scene->tree);
wlr_scene_node_set_enabled(&seat->drag.icons->node, false);
seat->drag.events.request.notify = handle_drag_request;
seat->drag.events.start.notify = handle_drag_start;
seat->drag.events.destroy.notify = handle_drag_destroy;
wl_signal_add(&seat->seat->events.request_start_drag,
wl_signal_add(&seat->wlr_seat->events.request_start_drag,
&seat->drag.events.request);
wl_signal_add(&seat->seat->events.start_drag, &seat->drag.events.start);
wl_signal_add(&seat->wlr_seat->events.start_drag, &seat->drag.events.start);
/*
* destroy.notify is listened to in handle_drag_start() and reset in
* handle_drag_destroy()

View file

@ -328,7 +328,7 @@ subtract_node_tree(struct wlr_scene_tree *tree, pixman_region32_t *available,
}
void
edges_calculate_visibility(struct server *server, struct view *ignored_view)
edges_calculate_visibility(struct view *ignored_view)
{
/*
* The region stores the available output layout space
@ -353,17 +353,17 @@ edges_calculate_visibility(struct server *server, struct view *ignored_view)
*/
struct output *output;
struct wlr_box layout_box;
wl_list_for_each(output, &server->outputs, link) {
wl_list_for_each(output, &server.outputs, link) {
if (!output_is_usable(output)) {
continue;
}
wlr_output_layout_get_box(server->output_layout,
wlr_output_layout_get_box(server.output_layout,
output->wlr_output, &layout_box);
pixman_region32_union_rect(&region, &region,
layout_box.x, layout_box.y, layout_box.width, layout_box.height);
}
subtract_node_tree(&server->scene->tree, &region, ignored_view);
subtract_node_tree(&server.scene->tree, &region, ignored_view);
pixman_region32_fini(&region);
}
@ -389,7 +389,7 @@ edges_find_neighbors(struct border *nearest_edges, struct view *view,
edges_for_target_geometry(&target_edges, view, target);
struct view *v;
for_each_view(v, &view->server->views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) {
for_each_view(v, &server.views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) {
if (v == view || v->minimized || !output_is_usable(v->output)) {
continue;
}
@ -447,7 +447,7 @@ edges_find_outputs(struct border *nearest_edges, struct view *view,
edges_for_target_geometry(&target_edges, view, target);
struct output *o;
wl_list_for_each(o, &view->server->outputs, link) {
wl_list_for_each(o, &server.outputs, link) {
if (!output_is_usable(o)) {
continue;
}

View file

@ -59,7 +59,7 @@ void
ext_foreign_toplevel_init(struct ext_foreign_toplevel *ext_toplevel,
struct view *view)
{
assert(view->server->foreign_toplevel_list);
assert(server.foreign_toplevel_list);
ext_toplevel->view = view;
struct wlr_ext_foreign_toplevel_handle_v1_state state = {
@ -67,7 +67,7 @@ ext_foreign_toplevel_init(struct ext_foreign_toplevel *ext_toplevel,
.app_id = view->app_id,
};
ext_toplevel->handle = wlr_ext_foreign_toplevel_handle_v1_create(
view->server->foreign_toplevel_list, &state);
server.foreign_toplevel_list, &state);
if (!ext_toplevel->handle) {
wlr_log(WLR_ERROR, "cannot create ext toplevel handle for (%s)",
@ -75,6 +75,9 @@ ext_foreign_toplevel_init(struct ext_foreign_toplevel *ext_toplevel,
return;
}
/* In support for ext-toplevel-capture */
ext_toplevel->handle->data = view;
/* Client side requests */
ext_toplevel->on.handle_destroy.notify = handle_handle_destroy;
wl_signal_add(&ext_toplevel->handle->events.destroy, &ext_toplevel->on.handle_destroy);

View file

@ -123,7 +123,7 @@ handle_new_outputs(struct wl_listener *listener, void *data)
* wlr_foreign_toplevel handle the rest.
*/
struct output *output;
wl_list_for_each(output, &wlr_toplevel->view->server->outputs, link) {
wl_list_for_each(output, &server.outputs, link) {
if (view_on_output(wlr_toplevel->view, output)) {
wlr_foreign_toplevel_handle_v1_output_enter(
wlr_toplevel->handle, output->wlr_output);
@ -184,11 +184,11 @@ void
wlr_foreign_toplevel_init(struct wlr_foreign_toplevel *wlr_toplevel,
struct view *view)
{
assert(view->server->foreign_toplevel_manager);
assert(server.foreign_toplevel_manager);
wlr_toplevel->view = view;
wlr_toplevel->handle = wlr_foreign_toplevel_handle_v1_create(
view->server->foreign_toplevel_manager);
server.foreign_toplevel_manager);
if (!wlr_toplevel->handle) {
wlr_log(WLR_ERROR, "cannot create wlr foreign toplevel handle for (%s)",
view->title);
@ -203,7 +203,7 @@ wlr_foreign_toplevel_init(struct wlr_foreign_toplevel *wlr_toplevel,
handle_minimized(&wlr_toplevel->on_view.minimized, NULL);
handle_fullscreened(&wlr_toplevel->on_view.fullscreened, NULL);
handle_activated(&wlr_toplevel->on_view.activated,
&(bool){view == view->server->active_view});
&(bool){view == server.active_view});
/* Client side requests */
CONNECT_SIGNAL(wlr_toplevel->handle, &wlr_toplevel->on, request_maximize);

View file

@ -84,7 +84,7 @@ idle_manager_create(struct wl_display *display)
}
void
idle_manager_notify_activity(struct wlr_seat *seat)
idle_manager_notify_activity(struct wlr_seat *wlr_seat)
{
/*
* The display destroy event might have been triggered
@ -96,5 +96,5 @@ idle_manager_notify_activity(struct wlr_seat *seat)
return;
}
wlr_idle_notifier_v1_notify_activity(manager->ext, seat);
wlr_idle_notifier_v1_notify_activity(manager->ext, wlr_seat);
}

View file

@ -226,7 +226,7 @@ out:
/*
* Openbox built-in icons are not bigger than 8x8, so have only written this
* function to cope wit that max size
* function to cope with that max size
*/
#define LABWC_BUILTIN_ICON_MAX_SIZE (8)

View file

@ -351,7 +351,7 @@ xpm_load_to_surface(struct file_handle *handle)
goto out;
}
for (int n = 0, xcnt = 0; n < wbytes; n += cpp, xcnt++) {
for (int n = 0; n < wbytes; n += cpp) {
g_strlcpy(pixel_str, &buffer[n], cpp + 1);
struct xpm_color *color =

View file

@ -3,7 +3,7 @@
#include "input/cursor.h"
#include <assert.h>
#include <time.h>
#include <wlr/backend/libinput.h>
#include <wlr/config.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_cursor_shape_v1.h>
#include <wlr/types/wlr_data_device.h>
@ -38,6 +38,10 @@
#include "view.h"
#include "xwayland.h"
#if WLR_HAS_LIBINPUT_BACKEND
#include <wlr/backend/libinput.h>
#endif
#define LAB_CURSOR_SHAPE_V1_VERSION 1
struct constraint {
@ -148,7 +152,7 @@ handle_request_set_cursor(struct wl_listener *listener, void *data)
{
struct seat *seat = wl_container_of(listener, seat, request_set_cursor);
if (seat->server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
/* Prevent setting a cursor image when moving or resizing */
return;
}
@ -180,7 +184,7 @@ handle_request_set_cursor(struct wl_listener *listener, void *data)
*/
struct wlr_seat_pointer_request_set_cursor_event *event = data;
struct wlr_seat_client *focused_client =
seat->seat->pointer_state.focused_client;
seat->wlr_seat->pointer_state.focused_client;
/*
* This can be sent by any client, so we check to make sure this one
@ -205,10 +209,10 @@ handle_request_set_shape(struct wl_listener *listener, void *data)
struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data;
const char *shape_name = wlr_cursor_shape_v1_name(event->shape);
struct seat *seat = wl_container_of(listener, seat, request_set_shape);
struct wlr_seat_client *focused_client = seat->seat->pointer_state.focused_client;
struct wlr_seat_client *focused_client = seat->wlr_seat->pointer_state.focused_client;
/* Prevent setting a cursor image when moving or resizing */
if (seat->server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
return;
}
@ -250,7 +254,7 @@ handle_request_set_selection(struct wl_listener *listener, void *data)
struct seat *seat = wl_container_of(
listener, seat, request_set_selection);
struct wlr_seat_request_set_selection_event *event = data;
wlr_seat_set_selection(seat->seat, event->source,
wlr_seat_set_selection(seat->wlr_seat, event->source,
event->serial);
}
@ -260,17 +264,17 @@ handle_request_set_primary_selection(struct wl_listener *listener, void *data)
struct seat *seat = wl_container_of(
listener, seat, request_set_primary_selection);
struct wlr_seat_request_set_primary_selection_event *event = data;
wlr_seat_set_primary_selection(seat->seat, event->source,
wlr_seat_set_primary_selection(seat->wlr_seat, event->source,
event->serial);
}
static void
process_cursor_move(struct server *server, uint32_t time)
process_cursor_move(uint32_t time)
{
struct view *view = server->grabbed_view;
struct view *view = server.grabbed_view;
int x = server->grab_box.x + (server->seat.cursor->x - server->grab_x);
int y = server->grab_box.y + (server->seat.cursor->y - server->grab_y);
int x = server.grab_box.x + (server.seat.cursor->x - server.grab_x);
int y = server.grab_box.y + (server.seat.cursor->y - server.grab_y);
/* Apply resistance for maximized/tiled view */
bool needs_untile = resistance_unsnap_apply(view, &x, &y);
@ -286,7 +290,7 @@ process_cursor_move(struct server *server, uint32_t time)
.width = view->natural_geometry.width,
.height = view->natural_geometry.height,
};
interactive_anchor_to_cursor(server, &new_geo);
interactive_anchor_to_cursor(&new_geo);
/* Shaded clients will not process resize events until unshaded */
view_set_shade(view, false);
view_set_maximized(view, VIEW_AXIS_NONE);
@ -300,18 +304,18 @@ process_cursor_move(struct server *server, uint32_t time)
resistance_move_apply(view, &x, &y);
view_move(view, x, y);
overlay_update(&server->seat);
overlay_update(&server.seat);
}
static void
process_cursor_resize(struct server *server, uint32_t time)
process_cursor_resize(uint32_t time)
{
/* Rate-limit resize events respecting monitor refresh rate */
static uint32_t last_resize_time = 0;
static struct view *last_resize_view = NULL;
assert(server->grabbed_view);
if (server->grabbed_view == last_resize_view) {
assert(server.grabbed_view);
if (server.grabbed_view == last_resize_view) {
int32_t refresh = 0;
if (output_is_usable(last_resize_view->output)) {
refresh = last_resize_view->output->wlr_output->refresh;
@ -327,43 +331,43 @@ process_cursor_resize(struct server *server, uint32_t time)
}
last_resize_time = time;
last_resize_view = server->grabbed_view;
last_resize_view = server.grabbed_view;
double dx = server->seat.cursor->x - server->grab_x;
double dy = server->seat.cursor->y - server->grab_y;
double dx = server.seat.cursor->x - server.grab_x;
double dy = server.seat.cursor->y - server.grab_y;
struct view *view = server->grabbed_view;
struct view *view = server.grabbed_view;
struct wlr_box new_view_geo = view->current;
if (server->resize_edges & LAB_EDGE_TOP) {
if (server.resize_edges & LAB_EDGE_TOP) {
/* Shift y to anchor bottom edge when resizing top */
new_view_geo.y = server->grab_box.y + dy;
new_view_geo.height = server->grab_box.height - dy;
} else if (server->resize_edges & LAB_EDGE_BOTTOM) {
new_view_geo.height = server->grab_box.height + dy;
new_view_geo.y = server.grab_box.y + dy;
new_view_geo.height = server.grab_box.height - dy;
} else if (server.resize_edges & LAB_EDGE_BOTTOM) {
new_view_geo.height = server.grab_box.height + dy;
}
if (server->resize_edges & LAB_EDGE_LEFT) {
if (server.resize_edges & LAB_EDGE_LEFT) {
/* Shift x to anchor right edge when resizing left */
new_view_geo.x = server->grab_box.x + dx;
new_view_geo.width = server->grab_box.width - dx;
} else if (server->resize_edges & LAB_EDGE_RIGHT) {
new_view_geo.width = server->grab_box.width + dx;
new_view_geo.x = server.grab_box.x + dx;
new_view_geo.width = server.grab_box.width - dx;
} else if (server.resize_edges & LAB_EDGE_RIGHT) {
new_view_geo.width = server.grab_box.width + dx;
}
resistance_resize_apply(view, &new_view_geo);
view_adjust_size(view, &new_view_geo.width, &new_view_geo.height);
if (server->resize_edges & LAB_EDGE_TOP) {
if (server.resize_edges & LAB_EDGE_TOP) {
/* After size adjustments, make sure to anchor bottom edge */
new_view_geo.y = server->grab_box.y +
server->grab_box.height - new_view_geo.height;
new_view_geo.y = server.grab_box.y +
server.grab_box.height - new_view_geo.height;
}
if (server->resize_edges & LAB_EDGE_LEFT) {
if (server.resize_edges & LAB_EDGE_LEFT) {
/* After size adjustments, make sure to anchor bottom right */
new_view_geo.x = server->grab_box.x +
server->grab_box.width - new_view_geo.width;
new_view_geo.x = server.grab_box.x +
server.grab_box.width - new_view_geo.width;
}
if (rc.resize_draw_contents) {
@ -418,11 +422,11 @@ cursor_update_image(struct seat *seat)
* a new output instance), we have to force a re-enter of
* the surface so the client sets its own cursor again.
*/
if (seat->seat->pointer_state.focused_surface) {
if (seat->wlr_seat->pointer_state.focused_surface) {
seat->server_cursor = LAB_CURSOR_DEFAULT;
wlr_cursor_set_xcursor(seat->cursor, seat->xcursor_manager, "");
wlr_seat_pointer_clear_focus(seat->seat);
cursor_update_focus(seat->server);
wlr_seat_pointer_clear_focus(seat->wlr_seat);
cursor_update_focus();
}
return;
}
@ -515,7 +519,7 @@ update_pressed_surface(struct seat *seat, const struct cursor_context *ctx)
* context menus (in contrast) do not use an XDG popup grab and
* do not work properly if we send leave/enter events.
*/
if (!wlr_seat_pointer_has_grab(seat->seat)) {
if (!wlr_seat_pointer_has_grab(seat->wlr_seat)) {
return false;
}
if (seat->pressed.ctx.surface && ctx->surface != seat->pressed.ctx.surface) {
@ -533,15 +537,15 @@ update_pressed_surface(struct seat *seat, const struct cursor_context *ctx)
* and process_cursor_axis()
*/
static void
cursor_update_common(struct server *server, const struct cursor_context *ctx,
cursor_update_common(const struct cursor_context *ctx,
struct cursor_context *notified_ctx)
{
struct seat *seat = &server->seat;
struct wlr_seat *wlr_seat = seat->seat;
struct seat *seat = &server.seat;
struct wlr_seat *wlr_seat = seat->wlr_seat;
ssd_update_hovered_button(server, ctx->node);
ssd_update_hovered_button(ctx->node);
if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
/*
* Prevent updating focus/cursor image during
* interactive move/resize, window switcher and
@ -565,8 +569,8 @@ cursor_update_common(struct server *server, const struct cursor_context *ctx,
int lx, ly;
wlr_scene_node_coords(seat->pressed.ctx.node, &lx, &ly);
*notified_ctx = seat->pressed.ctx;
notified_ctx->sx = server->seat.cursor->x - lx;
notified_ctx->sy = server->seat.cursor->y - ly;
notified_ctx->sx = server.seat.cursor->x - lx;
notified_ctx->sy = server.seat.cursor->y - ly;
}
return;
}
@ -604,7 +608,7 @@ cursor_update_common(struct server *server, const struct cursor_context *ctx,
}
enum lab_edge
cursor_get_resize_edges(struct wlr_cursor *cursor, struct cursor_context *ctx)
cursor_get_resize_edges(struct wlr_cursor *cursor, const struct cursor_context *ctx)
{
enum lab_edge resize_edges = node_type_to_edges(ctx->type);
if (ctx->view && !resize_edges) {
@ -620,24 +624,24 @@ cursor_get_resize_edges(struct wlr_cursor *cursor, struct cursor_context *ctx)
}
bool
cursor_process_motion(struct server *server, uint32_t time, double *sx, double *sy)
cursor_process_motion(uint32_t time, double *sx, double *sy)
{
/* If the mode is non-passthrough, delegate to those functions. */
if (server->input_mode == LAB_INPUT_STATE_MOVE) {
process_cursor_move(server, time);
if (server.input_mode == LAB_INPUT_STATE_MOVE) {
process_cursor_move(time);
return false;
} else if (server->input_mode == LAB_INPUT_STATE_RESIZE) {
process_cursor_resize(server, time);
} else if (server.input_mode == LAB_INPUT_STATE_RESIZE) {
process_cursor_resize(time);
return false;
}
/* Otherwise, find view under the pointer and send the event along */
struct cursor_context ctx = get_cursor_context(server);
struct seat *seat = &server->seat;
struct cursor_context ctx = get_cursor_context();
struct seat *seat = &server.seat;
if (ctx.type == LAB_NODE_MENUITEM) {
menu_process_cursor_motion(ctx.node);
cursor_set(&server->seat, LAB_CURSOR_DEFAULT);
cursor_set(&server.seat, LAB_CURSOR_DEFAULT);
return false;
}
@ -659,7 +663,7 @@ cursor_process_motion(struct server *server, uint32_t time, double *sx, double *
* moving/resizing the wrong view
*/
mousebind->pressed_in_context = false;
actions_run(seat->pressed.ctx.view, server,
actions_run(seat->pressed.ctx.view,
&mousebind->actions, &seat->pressed.ctx);
}
}
@ -670,7 +674,7 @@ cursor_process_motion(struct server *server, uint32_t time, double *sx, double *
* and the pressed view is set while out-of-surface dragging.
*/
struct cursor_context notified_ctx = {0};
cursor_update_common(server, &ctx, &notified_ctx);
cursor_update_common(&ctx, &notified_ctx);
if (rc.focus_follow_mouse) {
/*
@ -701,10 +705,10 @@ cursor_process_motion(struct server *server, uint32_t time, double *sx, double *
}
static void
_cursor_update_focus(struct server *server)
_cursor_update_focus(void)
{
/* Focus surface under cursor if it isn't already focused */
struct cursor_context ctx = get_cursor_context(server);
struct cursor_context ctx = get_cursor_context();
if ((ctx.view || ctx.surface) && rc.focus_follow_mouse
&& !rc.focus_follow_mouse_requires_movement) {
@ -712,21 +716,21 @@ _cursor_update_focus(struct server *server)
* Always focus the surface below the cursor when
* followMouse=yes and followMouseRequiresMovement=no.
*/
desktop_focus_view_or_surface(&server->seat, ctx.view,
desktop_focus_view_or_surface(&server.seat, ctx.view,
ctx.surface, rc.raise_on_focus);
}
cursor_update_common(server, &ctx, NULL);
cursor_update_common(&ctx, NULL);
}
void
cursor_update_focus(struct server *server)
cursor_update_focus(void)
{
/* Prevent recursion via view_move_to_front() */
static bool updating_focus = false;
if (!updating_focus) {
updating_focus = true;
_cursor_update_focus(server);
_cursor_update_focus();
updating_focus = false;
}
}
@ -735,7 +739,7 @@ static void
warp_cursor_to_constraint_hint(struct seat *seat,
struct wlr_pointer_constraint_v1 *constraint)
{
if (!seat->server->active_view) {
if (!server.active_view) {
return;
}
@ -744,11 +748,11 @@ warp_cursor_to_constraint_hint(struct seat *seat,
double sx = constraint->current.cursor_hint.x;
double sy = constraint->current.cursor_hint.y;
wlr_cursor_warp(seat->cursor, NULL,
seat->server->active_view->current.x + sx,
seat->server->active_view->current.y + sy);
server.active_view->current.x + sx,
server.active_view->current.y + sy);
/* Make sure we are not sending unnecessary surface movements */
wlr_seat_pointer_warp(seat->seat, sx, sy);
wlr_seat_pointer_warp(seat->wlr_seat, sx, sy);
}
}
@ -788,26 +792,24 @@ void
create_constraint(struct wl_listener *listener, void *data)
{
struct wlr_pointer_constraint_v1 *wlr_constraint = data;
struct server *server = wl_container_of(listener, server,
new_constraint);
struct constraint *constraint = znew(*constraint);
constraint->constraint = wlr_constraint;
constraint->seat = &server->seat;
constraint->seat = &server.seat;
constraint->destroy.notify = handle_constraint_destroy;
wl_signal_add(&wlr_constraint->events.destroy, &constraint->destroy);
struct view *view = server->active_view;
struct view *view = server.active_view;
if (view && view->surface == wlr_constraint->surface) {
constrain_cursor(server, wlr_constraint);
constrain_cursor(wlr_constraint);
}
}
void
constrain_cursor(struct server *server, struct wlr_pointer_constraint_v1
constrain_cursor(struct wlr_pointer_constraint_v1
*constraint)
{
struct seat *seat = &server->seat;
struct seat *seat = &server.seat;
if (seat->current_constraint == constraint) {
return;
}
@ -837,7 +839,7 @@ constrain_cursor(struct server *server, struct wlr_pointer_constraint_v1
static void
apply_constraint(struct seat *seat, struct wlr_pointer *pointer, double *x, double *y)
{
if (!seat->server->active_view) {
if (!server.active_view) {
return;
}
if (!seat->current_constraint
@ -850,8 +852,8 @@ apply_constraint(struct seat *seat, struct wlr_pointer *pointer, double *x, doub
double sx = seat->cursor->x;
double sy = seat->cursor->y;
sx -= seat->server->active_view->current.x;
sy -= seat->server->active_view->current.y;
sx -= server.active_view->current.x;
sy -= server.active_view->current.y;
double sx_confined, sy_confined;
if (!wlr_region_confine(&seat->current_constraint->region, sx, sy,
@ -870,7 +872,7 @@ cursor_locked(struct seat *seat, struct wlr_pointer *pointer)
&& pointer->base.type == WLR_INPUT_DEVICE_POINTER
&& seat->current_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED
&& seat->current_constraint->surface
== seat->seat->pointer_state.focused_surface;
== seat->wlr_seat->pointer_state.focused_surface;
}
static void
@ -892,14 +894,15 @@ preprocess_cursor_motion(struct seat *seat, struct wlr_pointer *pointer,
*/
wlr_cursor_move(seat->cursor, &pointer->base, dx, dy);
double sx, sy;
bool notify = cursor_process_motion(seat->server, time_msec, &sx, &sy);
bool notify = cursor_process_motion(time_msec, &sx, &sy);
if (notify) {
wlr_seat_pointer_notify_motion(seat->seat, time_msec, sx, sy);
wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy);
}
}
static double get_natural_scroll_factor(struct wlr_input_device *wlr_input_device)
{
#if WLR_HAS_LIBINPUT_BACKEND
if (wlr_input_device_is_libinput(wlr_input_device)) {
struct libinput_device *libinput_device =
wlr_libinput_get_device_handle(wlr_input_device);
@ -907,7 +910,7 @@ static double get_natural_scroll_factor(struct wlr_input_device *wlr_input_devic
return -1.0;
}
}
#endif
return 1.0;
}
@ -919,9 +922,8 @@ handle_motion(struct wl_listener *listener, void *data)
* _relative_ pointer motion event (i.e. a delta)
*/
struct seat *seat = wl_container_of(listener, seat, on_cursor.motion);
struct server *server = seat->server;
struct wlr_pointer_motion_event *event = data;
idle_manager_notify_activity(seat->seat);
idle_manager_notify_activity(seat->wlr_seat);
cursor_set_visible(seat, /* visible */ true);
if (seat->cursor_scroll_wheel_emulation) {
@ -952,8 +954,8 @@ handle_motion(struct wl_listener *listener, void *data)
WL_POINTER_AXIS_SOURCE_CONTINUOUS, event->time_msec);
} else {
wlr_relative_pointer_manager_v1_send_relative_motion(
server->relative_pointer_manager,
seat->seat, (uint64_t)event->time_msec * 1000,
server.relative_pointer_manager,
seat->wlr_seat, (uint64_t)event->time_msec * 1000,
event->delta_x, event->delta_y, event->unaccel_dx,
event->unaccel_dy);
@ -975,7 +977,7 @@ handle_motion_absolute(struct wl_listener *listener, void *data)
*/
struct seat *seat = wl_container_of(listener, seat, on_cursor.motion_absolute);
struct wlr_pointer_motion_absolute_event *event = data;
idle_manager_notify_activity(seat->seat);
idle_manager_notify_activity(seat->wlr_seat);
cursor_set_visible(seat, /* visible */ true);
double lx, ly;
@ -990,15 +992,14 @@ handle_motion_absolute(struct wl_listener *listener, void *data)
}
static void
process_release_mousebinding(struct server *server,
struct cursor_context *ctx, uint32_t button)
process_release_mousebinding(struct cursor_context *ctx, uint32_t button)
{
if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
if (server.input_mode == LAB_INPUT_STATE_CYCLE) {
return;
}
struct mousebind *mousebind;
uint32_t modifiers = keyboard_get_all_modifiers(&server->seat);
uint32_t modifiers = keyboard_get_all_modifiers(&server.seat);
wl_list_for_each(mousebind, &rc.mousebinds, link) {
if (ctx->type == LAB_NODE_CLIENT
@ -1019,7 +1020,7 @@ process_release_mousebinding(struct server *server,
default:
continue;
}
actions_run(ctx->view, server, &mousebind->actions, ctx);
actions_run(ctx->view, &mousebind->actions, ctx);
}
}
}
@ -1059,17 +1060,17 @@ is_double_click(long double_click_speed, uint32_t button,
}
static bool
process_press_mousebinding(struct server *server, struct cursor_context *ctx,
process_press_mousebinding(struct cursor_context *ctx,
uint32_t button)
{
if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
if (server.input_mode == LAB_INPUT_STATE_CYCLE) {
return false;
}
struct mousebind *mousebind;
bool double_click = is_double_click(rc.doubleclick_time, button, ctx);
bool consumed_by_frame_context = false;
uint32_t modifiers = keyboard_get_all_modifiers(&server->seat);
uint32_t modifiers = keyboard_get_all_modifiers(&server.seat);
wl_list_for_each(mousebind, &rc.mousebinds, link) {
if (ctx->type == LAB_NODE_CLIENT
@ -1108,7 +1109,7 @@ process_press_mousebinding(struct server *server, struct cursor_context *ctx,
}
consumed_by_frame_context |= mousebind->context == LAB_NODE_FRAME;
consumed_by_frame_context |= mousebind->context == LAB_NODE_ALL;
actions_run(ctx->view, server, &mousebind->actions, ctx);
actions_run(ctx->view, &mousebind->actions, ctx);
}
}
return consumed_by_frame_context;
@ -1138,8 +1139,7 @@ static uint32_t press_msec;
bool
cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_msec)
{
struct server *server = seat->server;
struct cursor_context ctx = get_cursor_context(server);
struct cursor_context ctx = get_cursor_context();
/* Used on next button release to check if it can close menu or select menu item */
press_msec = time_msec;
@ -1150,7 +1150,7 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms
interactive_set_grab_context(&ctx);
}
if (server->input_mode == LAB_INPUT_STATE_MENU) {
if (server.input_mode == LAB_INPUT_STATE_MENU) {
/*
* If menu was already opened on press, set a very small value
* so subsequent release always closes menu or selects menu item.
@ -1171,7 +1171,7 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms
if (layer && layer->current.keyboard_interactive) {
layer_try_set_focus(seat, layer);
}
#ifdef HAVE_XWAYLAND
#if HAVE_XWAYLAND
} else if (ctx.type == LAB_NODE_UNMANAGED) {
desktop_focus_view_or_surface(seat, NULL, ctx.surface,
/*raise*/ false);
@ -1179,7 +1179,7 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms
}
if (ctx.type != LAB_NODE_CLIENT && ctx.type != LAB_NODE_LAYER_SURFACE
&& wlr_seat_pointer_has_grab(seat->seat)) {
&& wlr_seat_pointer_has_grab(seat->wlr_seat)) {
/*
* If we have an active popup grab (an open popup) we want to
* cancel that grab whenever the user presses on anything that
@ -1188,14 +1188,14 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms
*
* Note: This does not work for XWayland clients
*/
wlr_seat_pointer_end_grab(seat->seat);
wlr_seat_pointer_end_grab(seat->wlr_seat);
lab_set_add(&seat->bound_buttons, button);
return false;
}
/* Bindings to the Frame context swallow mouse events if activated */
bool consumed_by_frame_context =
process_press_mousebinding(server, &ctx, button);
process_press_mousebinding(&ctx, button);
if (ctx.surface && !consumed_by_frame_context) {
/* Notify client with pointer focus of button press */
@ -1210,8 +1210,7 @@ bool
cursor_process_button_release(struct seat *seat, uint32_t button,
uint32_t time_msec)
{
struct server *server = seat->server;
struct cursor_context ctx = get_cursor_context(server);
struct cursor_context ctx = get_cursor_context();
struct wlr_surface *pressed_surface = seat->pressed.ctx.surface;
/* Always notify button release event when it's not bound */
@ -1219,26 +1218,26 @@ cursor_process_button_release(struct seat *seat, uint32_t button,
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 */
if (time_msec - press_msec > rc.menu_ignore_button_release_period) {
if (ctx.type == LAB_NODE_MENUITEM) {
menu_call_selected_actions(server);
menu_call_selected_actions();
} else {
menu_close_root(server);
cursor_update_focus(server);
menu_close_root();
cursor_update_focus();
}
}
return notify;
}
if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
if (server.input_mode == LAB_INPUT_STATE_CYCLE) {
if (ctx.type == LAB_NODE_CYCLE_OSD_ITEM) {
cycle_on_cursor_release(server, ctx.node);
cycle_on_cursor_release(ctx.node);
}
return notify;
}
if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
return notify;
}
@ -1250,7 +1249,7 @@ cursor_process_button_release(struct seat *seat, uint32_t button,
return notify;
}
process_release_mousebinding(server, &ctx, button);
process_release_mousebinding(&ctx, button);
return notify;
}
@ -1258,8 +1257,6 @@ cursor_process_button_release(struct seat *seat, uint32_t button,
bool
cursor_finish_button_release(struct seat *seat, uint32_t button)
{
struct server *server = seat->server;
/* Clear "pressed" status for all bindings of this mouse button */
struct mousebind *mousebind;
wl_list_for_each(mousebind, &rc.mousebinds, link) {
@ -1270,17 +1267,17 @@ cursor_finish_button_release(struct seat *seat, uint32_t button)
lab_set_remove(&seat->bound_buttons, button);
if (server->input_mode == LAB_INPUT_STATE_MOVE
|| server->input_mode == LAB_INPUT_STATE_RESIZE) {
if (resize_outlines_enabled(server->grabbed_view)) {
resize_outlines_finish(server->grabbed_view);
if (server.input_mode == LAB_INPUT_STATE_MOVE
|| server.input_mode == LAB_INPUT_STATE_RESIZE) {
if (resize_outlines_enabled(server.grabbed_view)) {
resize_outlines_finish(server.grabbed_view);
}
/* Exit interactive move/resize mode */
interactive_finish(server->grabbed_view);
interactive_finish(server.grabbed_view);
return true;
} else if (server->grabbed_view) {
} else if (server.grabbed_view) {
/* Button was released without starting move/resize */
interactive_cancel(server->grabbed_view);
interactive_cancel(server.grabbed_view);
}
return false;
@ -1295,7 +1292,7 @@ handle_button(struct wl_listener *listener, void *data)
*/
struct seat *seat = wl_container_of(listener, seat, on_cursor.button);
struct wlr_pointer_button_event *event = data;
idle_manager_notify_activity(seat->seat);
idle_manager_notify_activity(seat->wlr_seat);
cursor_set_visible(seat, /* visible */ true);
bool notify;
@ -1304,7 +1301,7 @@ handle_button(struct wl_listener *listener, void *data)
notify = cursor_process_button_press(seat, event->button,
event->time_msec);
if (notify) {
wlr_seat_pointer_notify_button(seat->seat, event->time_msec,
wlr_seat_pointer_notify_button(seat->wlr_seat, event->time_msec,
event->button, event->state);
}
break;
@ -1312,7 +1309,7 @@ handle_button(struct wl_listener *listener, void *data)
notify = cursor_process_button_release(seat, event->button,
event->time_msec);
if (notify) {
wlr_seat_pointer_notify_button(seat->seat, event->time_msec,
wlr_seat_pointer_notify_button(seat->wlr_seat, event->time_msec,
event->button, event->state);
}
cursor_finish_button_release(seat, event->button);
@ -1370,15 +1367,15 @@ compare_delta(double delta, double delta_discrete, struct accumulated_scroll *ac
}
static bool
process_cursor_axis(struct server *server, enum wl_pointer_axis orientation,
process_cursor_axis(enum wl_pointer_axis orientation,
double delta, double delta_discrete)
{
struct cursor_context ctx = get_cursor_context(server);
uint32_t modifiers = keyboard_get_all_modifiers(&server->seat);
struct cursor_context ctx = get_cursor_context();
uint32_t modifiers = keyboard_get_all_modifiers(&server.seat);
enum direction direction = LAB_DIRECTION_INVALID;
struct scroll_info info = compare_delta(delta, delta_discrete,
&server->seat.accumulated_scrolls[orientation]);
&server.seat.accumulated_scrolls[orientation]);
if (orientation == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
if (info.direction < 0) {
@ -1415,7 +1412,7 @@ process_cursor_axis(struct server *server, enum wl_pointer_axis orientation,
* on touchpads or hi-res mice doesn't exceed the threshold
*/
if (info.run_action) {
actions_run(ctx.view, server, &mousebind->actions, &ctx);
actions_run(ctx.view, &mousebind->actions, &ctx);
}
}
}
@ -1424,7 +1421,7 @@ process_cursor_axis(struct server *server, enum wl_pointer_axis orientation,
/* Bindings swallow mouse events if activated */
if (ctx.surface && !consumed) {
/* Make sure we are sending the events to the surface under the cursor */
cursor_update_common(server, &ctx, NULL);
cursor_update_common(&ctx, NULL);
return true;
}
@ -1440,9 +1437,8 @@ handle_axis(struct wl_listener *listener, void *data)
* event, for example when you move the scroll wheel.
*/
struct seat *seat = wl_container_of(listener, seat, on_cursor.axis);
struct server *server = seat->server;
struct wlr_pointer_axis_event *event = data;
idle_manager_notify_activity(seat->seat);
idle_manager_notify_activity(seat->wlr_seat);
cursor_set_visible(seat, /* visible */ true);
/* input->scroll_factor is set for pointer/touch devices */
@ -1451,12 +1447,12 @@ handle_axis(struct wl_listener *listener, void *data)
struct input *input = event->pointer->base.data;
double scroll_factor = input->scroll_factor;
bool notify = process_cursor_axis(server, event->orientation,
bool notify = process_cursor_axis(event->orientation,
event->delta, event->delta_discrete);
if (notify) {
/* Notify the client with pointer focus of the axis event. */
wlr_seat_pointer_notify_axis(seat->seat, event->time_msec,
wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec,
event->orientation, scroll_factor * event->delta,
round(scroll_factor * event->delta_discrete),
event->source, event->relative_direction);
@ -1475,7 +1471,7 @@ handle_frame(struct wl_listener *listener, void *data)
*/
struct seat *seat = wl_container_of(listener, seat, on_cursor.frame);
/* Notify the client with pointer focus of the frame event. */
wlr_seat_pointer_notify_frame(seat->seat);
wlr_seat_pointer_notify_frame(seat->wlr_seat);
}
void
@ -1483,7 +1479,6 @@ cursor_emulate_axis(struct seat *seat, struct wlr_input_device *device,
enum wl_pointer_axis orientation, double delta, double delta_discrete,
enum wl_pointer_axis_source source, uint32_t time_msec)
{
struct server *server = seat->server;
struct input *input = device->data;
double scroll_factor = 1.0;
@ -1493,15 +1488,15 @@ cursor_emulate_axis(struct seat *seat, struct wlr_input_device *device,
scroll_factor = input->scroll_factor;
}
bool notify = process_cursor_axis(server, orientation, delta, delta_discrete);
bool notify = process_cursor_axis(orientation, delta, delta_discrete);
if (notify) {
/* Notify the client with pointer focus of the axis event. */
wlr_seat_pointer_notify_axis(seat->seat, time_msec,
wlr_seat_pointer_notify_axis(seat->wlr_seat, time_msec,
orientation, scroll_factor * delta,
round(scroll_factor * delta_discrete),
source, WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL);
}
wlr_seat_pointer_notify_frame(seat->seat);
wlr_seat_pointer_notify_frame(seat->wlr_seat);
}
void
@ -1514,17 +1509,17 @@ cursor_emulate_move(struct seat *seat, struct wlr_input_device *device,
}
wlr_relative_pointer_manager_v1_send_relative_motion(
seat->server->relative_pointer_manager,
seat->seat, (uint64_t)time_msec * 1000,
server.relative_pointer_manager,
seat->wlr_seat, (uint64_t)time_msec * 1000,
dx, dy, dx, dy);
wlr_cursor_move(seat->cursor, device, dx, dy);
double sx, sy;
bool notify = cursor_process_motion(seat->server, time_msec, &sx, &sy);
bool notify = cursor_process_motion(time_msec, &sx, &sy);
if (notify) {
wlr_seat_pointer_notify_motion(seat->seat, time_msec, sx, sy);
wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy);
}
wlr_seat_pointer_notify_frame(seat->seat);
wlr_seat_pointer_notify_frame(seat->wlr_seat);
}
void
@ -1550,18 +1545,18 @@ cursor_emulate_button(struct seat *seat, uint32_t button,
case WL_POINTER_BUTTON_STATE_PRESSED:
notify = cursor_process_button_press(seat, button, time_msec);
if (notify) {
wlr_seat_pointer_notify_button(seat->seat, time_msec, button, state);
wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state);
}
break;
case WL_POINTER_BUTTON_STATE_RELEASED:
notify = cursor_process_button_release(seat, button, time_msec);
if (notify) {
wlr_seat_pointer_notify_button(seat->seat, time_msec, button, state);
wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state);
}
cursor_finish_button_release(seat, button);
break;
}
wlr_seat_pointer_notify_frame(seat->seat);
wlr_seat_pointer_notify_frame(seat->wlr_seat);
}
static void
@ -1614,9 +1609,6 @@ void
cursor_reload(struct seat *seat)
{
cursor_load(seat);
#if HAVE_XWAYLAND
xwayland_reset_cursor(seat->server);
#endif
cursor_update_image(seat);
}
@ -1640,10 +1632,10 @@ cursor_init(struct seat *seat)
touch_init(seat);
tablet_init(seat);
CONNECT_SIGNAL(seat->seat, seat, request_set_cursor);
CONNECT_SIGNAL(seat->wlr_seat, seat, request_set_cursor);
struct wlr_cursor_shape_manager_v1 *cursor_shape_manager =
wlr_cursor_shape_manager_v1_create(seat->server->wl_display,
wlr_cursor_shape_manager_v1_create(server.wl_display,
LAB_CURSOR_SHAPE_V1_VERSION);
if (!cursor_shape_manager) {
wlr_log(WLR_ERROR, "unable to create cursor_shape interface");
@ -1651,8 +1643,8 @@ cursor_init(struct seat *seat)
}
CONNECT_SIGNAL(cursor_shape_manager, seat, request_set_shape);
CONNECT_SIGNAL(seat->seat, seat, request_set_selection);
CONNECT_SIGNAL(seat->seat, seat, request_set_primary_selection);
CONNECT_SIGNAL(seat->wlr_seat, seat, request_set_selection);
CONNECT_SIGNAL(seat->wlr_seat, seat, request_set_primary_selection);
}
void cursor_finish(struct seat *seat)

View file

@ -12,11 +12,11 @@ handle_pinch_begin(struct wl_listener *listener, void *data)
struct seat *seat = wl_container_of(listener, seat, pinch_begin);
struct wlr_pointer_pinch_begin_event *event = data;
idle_manager_notify_activity(seat->seat);
idle_manager_notify_activity(seat->wlr_seat);
cursor_set_visible(seat, /* visible */ true);
wlr_pointer_gestures_v1_send_pinch_begin(seat->pointer_gestures,
seat->seat, event->time_msec, event->fingers);
seat->wlr_seat, event->time_msec, event->fingers);
}
static void
@ -25,11 +25,11 @@ handle_pinch_update(struct wl_listener *listener, void *data)
struct seat *seat = wl_container_of(listener, seat, pinch_update);
struct wlr_pointer_pinch_update_event *event = data;
idle_manager_notify_activity(seat->seat);
idle_manager_notify_activity(seat->wlr_seat);
cursor_set_visible(seat, /* visible */ true);
wlr_pointer_gestures_v1_send_pinch_update(seat->pointer_gestures,
seat->seat, event->time_msec, event->dx, event->dy,
seat->wlr_seat, event->time_msec, event->dx, event->dy,
event->scale, event->rotation);
}
@ -39,11 +39,11 @@ handle_pinch_end(struct wl_listener *listener, void *data)
struct seat *seat = wl_container_of(listener, seat, pinch_end);
struct wlr_pointer_pinch_end_event *event = data;
idle_manager_notify_activity(seat->seat);
idle_manager_notify_activity(seat->wlr_seat);
cursor_set_visible(seat, /* visible */ true);
wlr_pointer_gestures_v1_send_pinch_end(seat->pointer_gestures,
seat->seat, event->time_msec, event->cancelled);
seat->wlr_seat, event->time_msec, event->cancelled);
}
static void
@ -52,11 +52,11 @@ handle_swipe_begin(struct wl_listener *listener, void *data)
struct seat *seat = wl_container_of(listener, seat, swipe_begin);
struct wlr_pointer_swipe_begin_event *event = data;
idle_manager_notify_activity(seat->seat);
idle_manager_notify_activity(seat->wlr_seat);
cursor_set_visible(seat, /* visible */ true);
wlr_pointer_gestures_v1_send_swipe_begin(seat->pointer_gestures,
seat->seat, event->time_msec, event->fingers);
seat->wlr_seat, event->time_msec, event->fingers);
}
static void
@ -65,11 +65,11 @@ handle_swipe_update(struct wl_listener *listener, void *data)
struct seat *seat = wl_container_of(listener, seat, swipe_update);
struct wlr_pointer_swipe_update_event *event = data;
idle_manager_notify_activity(seat->seat);
idle_manager_notify_activity(seat->wlr_seat);
cursor_set_visible(seat, /* visible */ true);
wlr_pointer_gestures_v1_send_swipe_update(seat->pointer_gestures,
seat->seat, event->time_msec, event->dx, event->dy);
seat->wlr_seat, event->time_msec, event->dx, event->dy);
}
static void
@ -78,11 +78,11 @@ handle_swipe_end(struct wl_listener *listener, void *data)
struct seat *seat = wl_container_of(listener, seat, swipe_end);
struct wlr_pointer_swipe_end_event *event = data;
idle_manager_notify_activity(seat->seat);
idle_manager_notify_activity(seat->wlr_seat);
cursor_set_visible(seat, /* visible */ true);
wlr_pointer_gestures_v1_send_swipe_end(seat->pointer_gestures,
seat->seat, event->time_msec, event->cancelled);
seat->wlr_seat, event->time_msec, event->cancelled);
}
static void
@ -91,11 +91,11 @@ handle_hold_begin(struct wl_listener *listener, void *data)
struct seat *seat = wl_container_of(listener, seat, hold_begin);
struct wlr_pointer_hold_begin_event *event = data;
idle_manager_notify_activity(seat->seat);
idle_manager_notify_activity(seat->wlr_seat);
cursor_set_visible(seat, /* visible */ true);
wlr_pointer_gestures_v1_send_hold_begin(seat->pointer_gestures,
seat->seat, event->time_msec, event->fingers);
seat->wlr_seat, event->time_msec, event->fingers);
}
static void
@ -104,17 +104,17 @@ handle_hold_end(struct wl_listener *listener, void *data)
struct seat *seat = wl_container_of(listener, seat, hold_end);
struct wlr_pointer_hold_end_event *event = data;
idle_manager_notify_activity(seat->seat);
idle_manager_notify_activity(seat->wlr_seat);
cursor_set_visible(seat, /* visible */ true);
wlr_pointer_gestures_v1_send_hold_end(seat->pointer_gestures,
seat->seat, event->time_msec, event->cancelled);
seat->wlr_seat, event->time_msec, event->cancelled);
}
void
gestures_init(struct seat *seat)
{
seat->pointer_gestures = wlr_pointer_gestures_v1_create(seat->server->wl_display);
seat->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display);
CONNECT_SIGNAL(seat->cursor, seat, pinch_begin);
CONNECT_SIGNAL(seat->cursor, seat, pinch_update);

View file

@ -12,6 +12,7 @@
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_xdg_shell.h>
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "input/keyboard.h"
#include "labwc.h"
#include "node.h"
@ -178,7 +179,6 @@ static void
update_popup_position(struct input_method_popup *popup)
{
struct input_method_relay *relay = popup->relay;
struct server *server = relay->seat->server;
struct text_input *text_input = relay->active_text_input;
if (!text_input || !relay->focused_surface
@ -219,7 +219,7 @@ update_popup_position(struct input_method_popup *popup)
}
struct output *output =
output_nearest_to(server, cursor_rect.x, cursor_rect.y);
output_nearest_to(cursor_rect.x, cursor_rect.y);
if (!output_is_usable(output)) {
wlr_log(WLR_ERROR,
"Cannot position IME popup (unusable output)");
@ -227,7 +227,7 @@ update_popup_position(struct input_method_popup *popup)
}
struct wlr_box output_box;
wlr_output_layout_get_box(
server->output_layout, output->wlr_output, &output_box);
server.output_layout, output->wlr_output, &output_box);
/* Use xdg-positioner utilities to position popup */
struct wlr_xdg_positioner_rules rules = {
@ -309,7 +309,8 @@ handle_keyboard_grab_destroy(struct wl_listener *listener, void *data)
{
struct input_method_relay *relay =
wl_container_of(listener, relay, keyboard_grab_destroy);
struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data;
struct wlr_input_method_keyboard_grab_v2 *keyboard_grab =
relay->input_method->keyboard_grab;
assert(keyboard_grab);
wl_list_remove(&relay->keyboard_grab_destroy.link);
@ -330,7 +331,7 @@ handle_input_method_grab_keyboard(struct wl_listener *listener, void *data)
struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data;
struct wlr_keyboard *active_keyboard =
wlr_seat_get_keyboard(relay->seat->seat);
wlr_seat_get_keyboard(relay->seat->wlr_seat);
if (!is_keyboard_emulated_by_input_method(
active_keyboard, relay->input_method)) {
@ -398,6 +399,8 @@ handle_input_method_new_popup_surface(struct wl_listener *listener, void *data)
popup->tree = wlr_scene_subsurface_tree_create(
relay->popup_tree, popup->popup_surface->surface);
die_if_null(popup->tree);
node_descriptor_create(&popup->tree->node, LAB_NODE_IME_POPUP,
/*view*/ NULL, /*data*/ NULL);
@ -412,7 +415,7 @@ handle_new_input_method(struct wl_listener *listener, void *data)
struct input_method_relay *relay =
wl_container_of(listener, relay, new_input_method);
struct wlr_input_method_v2 *input_method = data;
if (relay->seat->seat != input_method->seat) {
if (relay->seat->wlr_seat != input_method->seat) {
return;
}
@ -534,7 +537,7 @@ handle_new_text_input(struct wl_listener *listener, void *data)
struct input_method_relay *relay =
wl_container_of(listener, relay, new_text_input);
struct wlr_text_input_v3 *wlr_text_input = data;
if (relay->seat->seat != wlr_text_input->seat) {
if (relay->seat->wlr_seat != wlr_text_input->seat) {
return;
}
@ -580,14 +583,14 @@ input_method_relay_create(struct seat *seat)
relay->seat = seat;
wl_list_init(&relay->text_inputs);
wl_list_init(&relay->popups);
relay->popup_tree = wlr_scene_tree_create(&seat->server->scene->tree);
relay->popup_tree = lab_wlr_scene_tree_create(&server.scene->tree);
relay->new_text_input.notify = handle_new_text_input;
wl_signal_add(&seat->server->text_input_manager->events.text_input,
wl_signal_add(&server.text_input_manager->events.new_text_input,
&relay->new_text_input);
relay->new_input_method.notify = handle_new_input_method;
wl_signal_add(&seat->server->input_method_manager->events.input_method,
wl_signal_add(&server.input_method_manager->events.new_input_method,
&relay->new_input_method);
relay->focused_surface_destroy.notify = handle_focused_surface_destroy;

View file

@ -4,10 +4,171 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_scene.h>
#include <xkbcommon/xkbcommon.h>
#include "config/rcxml.h"
#include "common/buf.h"
#include "common/set.h"
#include "input/keyboard.h"
#include "labwc.h"
#include "scaled-buffer/scaled-font-buffer.h"
static struct lab_set pressed, bound, pressed_sent;
static bool show_debug_indicator;
static struct indicator_state {
struct wlr_scene_tree *tree;
struct scaled_font_buffer *sfb_pressed;
struct scaled_font_buffer *sfb_bound;
struct scaled_font_buffer *sfb_pressed_sent;
struct scaled_font_buffer *sfb_modifiers;
struct xkb_keymap *keymap;
} indicator_state;
static const char *
keycode_to_keyname(struct xkb_keymap *keymap, uint32_t keycode)
{
const xkb_keysym_t *syms;
int syms_len = xkb_keymap_key_get_syms_by_level(keymap, keycode + 8, 0, 0, &syms);
if (!syms_len) {
return NULL;
}
static char buf[256];
if (!xkb_keysym_get_name(syms[0], buf, sizeof(buf))) {
return NULL;
}
return buf;
}
static const char *
modifier_to_name(uint32_t modifier)
{
switch (modifier) {
case WLR_MODIFIER_SHIFT:
return "S";
case WLR_MODIFIER_CAPS:
return "caps";
case WLR_MODIFIER_CTRL:
return "C";
case WLR_MODIFIER_ALT:
return "A";
case WLR_MODIFIER_MOD2:
return "numlock";
case WLR_MODIFIER_MOD3:
return "H";
case WLR_MODIFIER_LOGO:
return "W";
case WLR_MODIFIER_MOD5:
return "M";
default:
return "?";
}
}
static void
init_indicator(struct indicator_state *state)
{
state->tree = wlr_scene_tree_create(&server.scene->tree);
wlr_scene_node_set_enabled(&state->tree->node, false);
state->sfb_pressed = scaled_font_buffer_create(state->tree);
wlr_scene_node_set_position(&state->sfb_pressed->scene_buffer->node, 0, 0);
state->sfb_bound = scaled_font_buffer_create(state->tree);
wlr_scene_node_set_position(&state->sfb_bound->scene_buffer->node, 0, 20);
state->sfb_pressed_sent = scaled_font_buffer_create(state->tree);
wlr_scene_node_set_position(&state->sfb_pressed_sent->scene_buffer->node, 0, 40);
state->sfb_modifiers = scaled_font_buffer_create(state->tree);
wlr_scene_node_set_position(&state->sfb_modifiers->scene_buffer->node, 0, 60);
struct xkb_rule_names rules = { 0 };
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
state->keymap = xkb_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
}
static void
update_key_indicator_callback(void *data)
{
struct seat *seat = data;
uint32_t all_modifiers = keyboard_get_all_modifiers(seat);
float black[4] = {0, 0, 0, 1};
float white[4] = {1, 1, 1, 1};
if (!indicator_state.tree) {
init_indicator(&indicator_state);
}
if (show_debug_indicator) {
wlr_scene_node_set_enabled(&indicator_state.tree->node, true);
} else {
wlr_scene_node_set_enabled(&indicator_state.tree->node, false);
return;
}
struct buf buf = BUF_INIT;
buf_add(&buf, "pressed=");
for (int i = 0; i < pressed.size; i++) {
const char *keyname = keycode_to_keyname(indicator_state.keymap,
pressed.values[i]);
buf_add_fmt(&buf, "%s (%d), ", keyname, pressed.values[i]);
}
scaled_font_buffer_update(indicator_state.sfb_pressed, buf.data,
-1, &rc.font_osd, black, white);
buf_clear(&buf);
buf_add(&buf, "bound=");
for (int i = 0; i < bound.size; i++) {
const char *keyname = keycode_to_keyname(indicator_state.keymap,
bound.values[i]);
buf_add_fmt(&buf, "%s (%d), ", keyname, bound.values[i]);
}
scaled_font_buffer_update(indicator_state.sfb_bound, buf.data,
-1, &rc.font_osd, black, white);
buf_clear(&buf);
buf_add(&buf, "pressed_sent=");
for (int i = 0; i < pressed_sent.size; i++) {
const char *keyname = keycode_to_keyname(indicator_state.keymap,
pressed_sent.values[i]);
buf_add_fmt(&buf, "%s (%d), ", keyname, pressed_sent.values[i]);
}
scaled_font_buffer_update(indicator_state.sfb_pressed_sent, buf.data, -1,
&rc.font_osd, black, white);
buf_clear(&buf);
buf_add(&buf, "modifiers=");
for (int i = 0; i <= 7; i++) {
uint32_t mod = 1 << i;
if (all_modifiers & mod) {
buf_add_fmt(&buf, "%s, ", modifier_to_name(mod));
}
}
buf_add_fmt(&buf, "(%d)", all_modifiers);
scaled_font_buffer_update(indicator_state.sfb_modifiers, buf.data, -1,
&rc.font_osd, black, white);
buf_reset(&buf);
}
void
key_state_indicator_update(struct seat *seat)
{
if (!show_debug_indicator) {
return;
}
wl_event_loop_add_idle(server.wl_event_loop,
update_key_indicator_callback, seat);
}
void
key_state_indicator_toggle(void)
{
show_debug_indicator = !show_debug_indicator;
}
static void
report(struct lab_set *key_set, const char *msg)
{

View file

@ -3,7 +3,7 @@
#include "input/keyboard.h"
#include <assert.h>
#include <stdlib.h>
#include <wlr/backend/session.h>
#include <wlr/config.h>
#include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/types/wlr_keyboard_group.h>
#include <wlr/types/wlr_seat.h>
@ -21,6 +21,10 @@
#include "view.h"
#include "workspaces.h"
#if WLR_HAS_SESSION
#include <wlr/backend/session.h>
#endif
enum lab_key_handled {
LAB_KEY_HANDLED_FALSE = 0,
LAB_KEY_HANDLED_TRUE = 1,
@ -52,9 +56,11 @@ keyboard_reset_current_keybind(void)
}
static void
change_vt(struct server *server, unsigned int vt)
change_vt(unsigned int vt)
{
wlr_session_change_vt(server->session, vt);
#if WLR_HAS_SESSION
wlr_session_change_vt(server.session, vt);
#endif
}
uint32_t
@ -90,7 +96,7 @@ seat_client_from_keyboard_resource(struct wl_resource *resource)
}
static void
broadcast_modifiers_to_unfocused_clients(struct wlr_seat *seat,
broadcast_modifiers_to_unfocused_clients(struct wlr_seat *wlr_seat,
const struct keyboard *keyboard,
const struct wlr_keyboard_modifiers *modifiers)
{
@ -100,8 +106,8 @@ broadcast_modifiers_to_unfocused_clients(struct wlr_seat *seat,
}
struct wlr_seat_client *client;
wl_list_for_each(client, &seat->clients, link) {
if (client == seat->keyboard_state.focused_client) {
wl_list_for_each(client, &wlr_seat->clients, link) {
if (client == wlr_seat->keyboard_state.focused_client) {
/*
* We've already notified the focused client by calling
* wlr_seat_keyboard_notify_modifiers()
@ -131,17 +137,18 @@ handle_modifiers(struct wl_listener *listener, void *data)
{
struct keyboard *keyboard = wl_container_of(listener, keyboard, modifiers);
struct seat *seat = keyboard->base.seat;
struct server *server = seat->server;
struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard;
if (server->input_mode == LAB_INPUT_STATE_MOVE) {
key_state_indicator_update(seat);
if (server.input_mode == LAB_INPUT_STATE_MOVE) {
/* Any change to the modifier state re-enable region snap */
seat->region_prevent_snap = false;
/* Pressing/releasing modifier key may show/hide region overlay */
overlay_update(seat);
}
bool cycling = server->input_mode == LAB_INPUT_STATE_CYCLE;
bool cycling = server.input_mode == LAB_INPUT_STATE_CYCLE;
if ((cycling || seat->workspace_osd_shown_by_modifier)
&& !keyboard_get_all_modifiers(seat)) {
@ -150,7 +157,7 @@ handle_modifiers(struct wl_listener *listener, void *data)
should_cancel_cycling_on_next_key_release = true;
} else {
should_cancel_cycling_on_next_key_release = false;
cycle_finish(server, /*switch_focus*/ true);
cycle_finish(/*switch_focus*/ true);
}
}
if (seat->workspace_osd_shown_by_modifier) {
@ -160,7 +167,7 @@ handle_modifiers(struct wl_listener *listener, void *data)
if (!input_method_keyboard_grab_forward_modifiers(keyboard)) {
/* Send modifiers to focused client */
wlr_seat_keyboard_notify_modifiers(seat->seat,
wlr_seat_keyboard_notify_modifiers(seat->wlr_seat,
&wlr_keyboard->modifiers);
/*
@ -182,13 +189,13 @@ handle_modifiers(struct wl_listener *listener, void *data)
* consequences. If so, modifiers ought to still be passed to
* clients with pointer-focus (see issue #2271)
*/
broadcast_modifiers_to_unfocused_clients(seat->seat,
broadcast_modifiers_to_unfocused_clients(seat->wlr_seat,
keyboard, &wlr_keyboard->modifiers);
}
}
static struct keybind *
match_keybinding_for_sym(struct server *server, uint32_t modifiers,
match_keybinding_for_sym(uint32_t modifiers,
xkb_keysym_t sym, xkb_keycode_t xkb_keycode)
{
struct keybind *keybind;
@ -196,8 +203,10 @@ match_keybinding_for_sym(struct server *server, uint32_t modifiers,
if (modifiers ^ keybind->modifiers) {
continue;
}
if (view_inhibits_actions(server->active_view, &keybind->actions)) {
continue;
if (!(keybind->override_inhibition)) {
if (view_inhibits_actions(server.active_view, &keybind->actions)) {
continue;
}
}
if (sym == XKB_KEY_NoSymbol) {
/* Use keycodes */
@ -241,12 +250,12 @@ match_keybinding_for_sym(struct server *server, uint32_t modifiers,
* the raw keysym fallback.
*/
static struct keybind *
match_keybinding(struct server *server, struct keyinfo *keyinfo,
match_keybinding(struct keyinfo *keyinfo,
bool is_virtual)
{
if (!is_virtual) {
/* First try keycodes */
struct keybind *keybind = match_keybinding_for_sym(server,
struct keybind *keybind = match_keybinding_for_sym(
keyinfo->modifiers, XKB_KEY_NoSymbol, keyinfo->xkb_keycode);
if (keybind) {
wlr_log(WLR_DEBUG, "keycode matched");
@ -257,7 +266,7 @@ match_keybinding(struct server *server, struct keyinfo *keyinfo,
/* Then fall back to keysyms */
for (int i = 0; i < keyinfo->translated.nr_syms; i++) {
struct keybind *keybind =
match_keybinding_for_sym(server, keyinfo->modifiers,
match_keybinding_for_sym(keyinfo->modifiers,
keyinfo->translated.syms[i], keyinfo->xkb_keycode);
if (keybind) {
wlr_log(WLR_DEBUG, "translated keysym matched");
@ -268,7 +277,7 @@ match_keybinding(struct server *server, struct keyinfo *keyinfo,
/* And finally test for keysyms without modifier */
for (int i = 0; i < keyinfo->raw.nr_syms; i++) {
struct keybind *keybind =
match_keybinding_for_sym(server, keyinfo->modifiers,
match_keybinding_for_sym(keyinfo->modifiers,
keyinfo->raw.syms[i], keyinfo->xkb_keycode);
if (keybind) {
wlr_log(WLR_DEBUG, "raw keysym matched");
@ -366,7 +375,7 @@ get_keyinfo(struct wlr_keyboard *wlr_keyboard, uint32_t evdev_keycode)
}
static enum lab_key_handled
handle_key_release(struct server *server, uint32_t evdev_keycode)
handle_key_release(uint32_t evdev_keycode)
{
/*
* Release events for keys that were not bound should always be
@ -387,7 +396,7 @@ handle_key_release(struct server *server, uint32_t evdev_keycode)
*/
if (should_cancel_cycling_on_next_key_release) {
should_cancel_cycling_on_next_key_release = false;
cycle_finish(server, /*switch_focus*/ true);
cycle_finish(/*switch_focus*/ true);
}
/*
@ -399,7 +408,7 @@ handle_key_release(struct server *server, uint32_t evdev_keycode)
}
static bool
handle_change_vt_key(struct server *server, struct keyboard *keyboard,
handle_change_vt_key(struct keyboard *keyboard,
struct keysyms *translated)
{
for (int i = 0; i < translated->nr_syms; i++) {
@ -407,7 +416,7 @@ handle_change_vt_key(struct server *server, struct keyboard *keyboard,
translated->syms[i] - XKB_KEY_XF86Switch_VT_1 + 1;
if (vt >= 1 && vt <= 12) {
keyboard_cancel_keybind_repeat(keyboard);
change_vt(server, vt);
change_vt(vt);
return true;
}
}
@ -415,42 +424,51 @@ handle_change_vt_key(struct server *server, struct keyboard *keyboard,
}
static void
handle_menu_keys(struct server *server, struct keysyms *syms)
handle_menu_keys(struct keysyms *syms)
{
assert(server->input_mode == LAB_INPUT_STATE_MENU);
assert(server.input_mode == LAB_INPUT_STATE_MENU);
for (int i = 0; i < syms->nr_syms; i++) {
switch (syms->syms[i]) {
case XKB_KEY_Down:
menu_item_select_next(server);
menu_item_select_next();
break;
case XKB_KEY_Up:
menu_item_select_previous(server);
menu_item_select_previous();
break;
case XKB_KEY_Right:
menu_submenu_enter(server);
menu_submenu_enter();
break;
case XKB_KEY_Left:
menu_submenu_leave(server);
menu_submenu_leave();
break;
case XKB_KEY_Return:
case XKB_KEY_KP_Enter:
menu_call_selected_actions(server);
if (!menu_call_selected_actions()) {
menu_submenu_enter();
};
break;
case XKB_KEY_Escape:
menu_close_root(server);
cursor_update_focus(server);
menu_close_root();
cursor_update_focus();
break;
default:
continue;
}
default: {
uint32_t accelerator = xkb_keysym_to_utf32(syms->syms[i]);
if (accelerator == 0) {
continue;
}
if (menu_item_select_by_accelerator(accelerator)) {
menu_call_selected_actions();
}
break;
}}
break;
}
}
/* Returns true if the keystroke is consumed */
static bool
handle_cycle_view_key(struct server *server, struct keyinfo *keyinfo)
handle_cycle_view_key(struct keyinfo *keyinfo)
{
if (keyinfo->is_modifier) {
return false;
@ -460,19 +478,19 @@ handle_cycle_view_key(struct server *server, struct keyinfo *keyinfo)
for (int i = 0; i < keyinfo->translated.nr_syms; i++) {
if (keyinfo->translated.syms[i] == XKB_KEY_Escape) {
/* Esc deactivates window switcher */
cycle_finish(server, /*switch_focus*/ false);
cycle_finish(/*switch_focus*/ false);
return true;
}
if (keyinfo->translated.syms[i] == XKB_KEY_Up
|| keyinfo->translated.syms[i] == XKB_KEY_Left) {
/* Up/Left cycles the window backward */
cycle_step(server, LAB_CYCLE_DIR_BACKWARD);
cycle_step(LAB_CYCLE_DIR_BACKWARD);
return true;
}
if (keyinfo->translated.syms[i] == XKB_KEY_Down
|| keyinfo->translated.syms[i] == XKB_KEY_Right) {
/* Down/Right cycles the window forward */
cycle_step(server, LAB_CYCLE_DIR_FORWARD);
cycle_step(LAB_CYCLE_DIR_FORWARD);
return true;
}
}
@ -483,11 +501,9 @@ static enum lab_key_handled
handle_compositor_keybindings(struct keyboard *keyboard,
struct wlr_keyboard_key_event *event)
{
struct seat *seat = keyboard->base.seat;
struct server *server = seat->server;
struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard;
struct keyinfo keyinfo = get_keyinfo(wlr_keyboard, event->keycode);
bool locked = seat->server->session_lock_manager->locked;
bool locked = server.session_lock_manager->locked;
key_state_set_pressed(event->keycode,
event->state == WL_KEYBOARD_KEY_STATE_PRESSED);
@ -499,15 +515,15 @@ handle_compositor_keybindings(struct keyboard *keyboard,
cur_keybind = NULL;
return LAB_KEY_HANDLED_TRUE;
}
actions_run(NULL, server, &cur_keybind->actions, NULL);
actions_run(NULL, &cur_keybind->actions, NULL);
return LAB_KEY_HANDLED_TRUE;
} else {
return handle_key_release(server, event->keycode);
return handle_key_release(event->keycode);
}
}
/* Catch C-A-F1 to C-A-F12 to change tty */
if (handle_change_vt_key(server, keyboard, &keyinfo.translated)) {
if (handle_change_vt_key(keyboard, &keyinfo.translated)) {
key_state_store_pressed_key_as_bound(event->keycode);
return LAB_KEY_HANDLED_TRUE_AND_VT_CHANGED;
}
@ -518,12 +534,12 @@ handle_compositor_keybindings(struct keyboard *keyboard,
* _all_ key press/releases are registered
*/
if (!locked) {
if (server->input_mode == LAB_INPUT_STATE_MENU) {
if (server.input_mode == LAB_INPUT_STATE_MENU) {
key_state_store_pressed_key_as_bound(event->keycode);
handle_menu_keys(server, &keyinfo.translated);
handle_menu_keys(&keyinfo.translated);
return LAB_KEY_HANDLED_TRUE;
} else if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
if (handle_cycle_view_key(server, &keyinfo)) {
} else if (server.input_mode == LAB_INPUT_STATE_CYCLE) {
if (handle_cycle_view_key(&keyinfo)) {
key_state_store_pressed_key_as_bound(event->keycode);
return LAB_KEY_HANDLED_TRUE;
}
@ -533,7 +549,7 @@ handle_compositor_keybindings(struct keyboard *keyboard,
/*
* Handle compositor keybinds
*/
cur_keybind = match_keybinding(server, &keyinfo, keyboard->is_virtual);
cur_keybind = match_keybinding(&keyinfo, keyboard->is_virtual);
if (cur_keybind && (!locked || cur_keybind->allow_when_locked)) {
/*
* Update key-state before action_run() because the action
@ -542,7 +558,7 @@ handle_compositor_keybindings(struct keyboard *keyboard,
*/
key_state_store_pressed_key_as_bound(event->keycode);
if (!cur_keybind->on_release) {
actions_run(NULL, server, &cur_keybind->actions, NULL);
actions_run(NULL, &cur_keybind->actions, NULL);
}
return LAB_KEY_HANDLED_TRUE;
}
@ -572,7 +588,7 @@ handle_keybind_repeat(void *data)
}
static void
start_keybind_repeat(struct server *server, struct keyboard *keyboard,
start_keybind_repeat(struct keyboard *keyboard,
struct wlr_keyboard_key_event *event)
{
struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard;
@ -583,7 +599,7 @@ start_keybind_repeat(struct server *server, struct keyboard *keyboard,
keyboard->keybind_repeat_keycode = event->keycode;
keyboard->keybind_repeat_rate = wlr_keyboard->repeat_info.rate;
keyboard->keybind_repeat = wl_event_loop_add_timer(
server->wl_event_loop, handle_keybind_repeat, keyboard);
server.wl_event_loop, handle_keybind_repeat, keyboard);
wl_event_source_timer_update(keyboard->keybind_repeat,
wlr_keyboard->repeat_info.delay);
}
@ -618,8 +634,11 @@ handle_key(struct wl_listener *listener, void *data)
struct keyboard *keyboard = wl_container_of(listener, keyboard, key);
struct seat *seat = keyboard->base.seat;
struct wlr_keyboard_key_event *event = data;
struct wlr_seat *wlr_seat = seat->seat;
idle_manager_notify_activity(seat->seat);
struct wlr_seat *wlr_seat = seat->wlr_seat;
key_state_indicator_update(seat);
idle_manager_notify_activity(seat->wlr_seat);
/* any new press/release cancels current keybind repeat */
keyboard_cancel_keybind_repeat(keyboard);
@ -639,7 +658,7 @@ handle_key(struct wl_listener *listener, void *data)
*/
if (!is_modifier(keyboard->wlr_keyboard, event->keycode)
&& event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
start_keybind_repeat(seat->server, keyboard, event);
start_keybind_repeat(keyboard, event);
}
} else if (!input_method_keyboard_grab_forward_key(keyboard, event)) {
wlr_seat_set_keyboard(wlr_seat, keyboard->wlr_keyboard);
@ -715,7 +734,7 @@ keyboard_update_layout(struct seat *seat, xkb_layout_index_t layout)
}
static void
reset_window_keyboard_layout_groups(struct server *server)
reset_window_keyboard_layout_groups(void)
{
if (!rc.kb_layout_per_window) {
return;
@ -727,15 +746,15 @@ reset_window_keyboard_layout_groups(struct server *server)
* but let's keep it simple for now and just reset them all.
*/
struct view *view;
for_each_view(view, &server->views, LAB_VIEW_CRITERIA_NONE) {
for_each_view(view, &server.views, LAB_VIEW_CRITERIA_NONE) {
view->keyboard_layout = 0;
}
struct view *active_view = server->active_view;
struct view *active_view = server.active_view;
if (!active_view) {
return;
}
keyboard_update_layout(&server->seat, active_view->keyboard_layout);
keyboard_update_layout(&server.seat, active_view->keyboard_layout);
}
/*
@ -743,12 +762,23 @@ reset_window_keyboard_layout_groups(struct server *server)
* XKB_DEFAULT_OPTIONS, and friends.
*/
static void
set_layout(struct server *server, struct wlr_keyboard *kb)
set_layout(struct wlr_keyboard *kb)
{
static bool fallback_mode;
struct xkb_rule_names rules = { 0 };
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
enum xkb_context_flags ctx_flags = XKB_CONTEXT_NO_FLAGS;
#ifdef __ANDROID__
/*
* Android's bionic libc implements secure_getenv() as a function
* that always returns NULL (the app process has no AT_SECURE).
* This prevents xkbcommon from reading XKB_DEFAULT_LAYOUT and
* friends via secure_getenv(). Use the flag to fall back to
* regular getenv() which works fine on Android.
*/
ctx_flags |= XKB_CONTEXT_NO_SECURE_GETENV;
#endif
struct xkb_context *context = xkb_context_new(ctx_flags);
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
XKB_KEYMAP_COMPILE_NO_FLAGS);
@ -762,7 +792,7 @@ set_layout(struct server *server, struct wlr_keyboard *kb)
if (keymap && !layout_empty) {
if (!wlr_keyboard_keymaps_match(kb->keymap, keymap)) {
wlr_keyboard_set_keymap(kb, keymap);
reset_window_keyboard_layout_groups(server);
reset_window_keyboard_layout_groups();
}
xkb_keymap_unref(keymap);
} else {
@ -772,7 +802,7 @@ set_layout(struct server *server, struct wlr_keyboard *kb)
wlr_log(WLR_ERROR, "entering fallback mode with layout 'us'");
fallback_mode = true;
setenv("XKB_DEFAULT_LAYOUT", "us", 1);
set_layout(server, kb);
set_layout(kb);
}
}
xkb_context_unref(context);
@ -782,10 +812,10 @@ void
keyboard_configure(struct seat *seat, struct wlr_keyboard *kb, bool is_virtual)
{
if (!is_virtual) {
set_layout(seat->server, kb);
set_layout(kb);
}
wlr_keyboard_set_repeat_info(kb, rc.repeat_rate, rc.repeat_delay);
keybind_update_keycodes(seat->server);
keybind_update_keycodes();
}
void

Some files were not shown because too many files have changed in this diff Show more