33 Commits
Author SHA1 Message Date
Andrea ChiavazzaandGuido Cella f9c644f0b0 use modifier-independent key symbols in keybindings
Co-authored-by: Guido Cella <guido@guidocella.xyz>
2026-09-14 17:53:35 +02:00
Leonardo Hernández Hernández dc9a860191 use the new output swapchain manager in outputmgrapplyortest() 2026-09-14 10:45:33 +02:00
Guido Cella d1ebf3c985 fix firefox opening with rounded corners
Fixes https://codeberg.org/dwl/dwl/issues/1169

The c->isfloating check is needed to have the correct floating window
size with e.g. alacritty -o window.dimensions.columns=15
window.dimensions.lines=10.
2026-09-14 10:42:59 +02:00
sewn 8f52e96be4 replicate dwm behavior for snap resize 2026-09-14 02:25:48 +02:00
Guido Cella 5fd19feaff support monitors above and below in focusmon and tagmon
Fixes https://codeberg.org/dwl/dwl/issues/1105
2026-09-13 21:14:10 +02:00
Guido Cella b1c101515a remove zero flickering lie
This isn't true since dwl switched to the scene API in 2021 (1b38801).

The issue is tracked at https://codeberg.org/dwl/dwl/issues/552.
2026-09-13 19:34:27 +02:00
Guido CellaandDreamMaoMao 8ad2c920db use the right cursor after move and resize
Fixes https://codeberg.org/dwl/dwl/issues/1162

Co-authored-by: DreamMaoMao <2523610504@qq.com>
2026-09-13 19:06:54 +02:00
Peter HofmannandGuido Cella e8bf70d639 implement Axis mouse bindings
Co-authored-by: Guido Cella <guido@guidocella.xyz>
2026-09-13 18:14:47 +02:00
Guido Cellaandbrinkpw 99cdc9a825 set seat capabilities for virtual keyboards and pointers
wlr_seat_set_capabilities was never called without a real input device.

Fixes https://codeberg.org/dwl/dwl/issues/1155.

Co-authored-by: brinkpw
2026-09-13 08:38:07 +02:00
Guido Cella 7ba335063f don't crash when clicking an empty area
If l is not initialized, accessing
l->layer_surface->current.keyboard_interactive causes a segmentation
fault. This was hard to see because c above was never initialized but
never caused a crash, and dwl also didn't crash building with ASan.

Also initialize c to be safe.

Fixes 4c54bd8.
Fixes https://codeberg.org/dwl/dwl/issues/1225.
2026-09-13 08:25:20 +02:00
Guido CellaandZuki Air fb4102b5ae support screenshare of single windows
Supersedes https://codeberg.org/dwl/dwl/pulls/1157.

Co-authored-by: Zuki Air <zukirust@gmail.com>
2026-09-12 10:28:05 +02:00
Guido CellaandZuki Air 721f70ea8b implement ext-foreign-toplevel-list
This is used by lswt which lists toplevel ids and titles.
keyd-application-mapper will also support it hopefully.

This will also be needed to support screenshare of single windows
(https://codeberg.org/dwl/dwl/pulls/1157).

Co-authored-by: Zuki Air <zukirust@gmail.com>
2026-09-12 08:02:27 +02:00
0a6dc6d548 add support for layer-shell on-demand keyboard interactivity
Supersedes https://codeberg.org/dwl/dwl/pulls/674

Co-authored-by: Leonardo Hernández Hernández <leohdz172@proton.me>
Co-authored-by: MayOrMayNotBeACat <maybeacat804@gmail.com>
2026-09-12 08:00:20 +02:00
Guido Cella 4c54bd8113 implement xytonode() with toplevel_from_wlr_surface()
xytonode() does not return the layer surface under the cursor because
pnode->data is always NULL. It was not noticed because it is not passed
the LayerSurface argument anywhere. But it is noticeable after
implementing layer shell on-demand keyboard interactivity because layer
shell surfaces are never focused by sloppyfocus.

Rather than figuring out how to fix it, just replace its implementation
with toplevel_from_wlr_surface() which works and also reduces the SLOC.
2026-09-11 23:55:17 +02:00
Guido CellaandDreamMaoMao 2f3fe0671d add IME support
Based on mangowm, which is based on labwc, which is based on sway, so
this should be covered by LICENSE.sway.

Fixes https://codeberg.org/dwl/dwl/issues/6
Fixes https://codeberg.org/dwl/dwl/issues/710
Fixes https://codeberg.org/dwl/dwl/issues/1176
Fixes https://codeberg.org/dwl/dwl/issues/1185

Co-authored-by: DreamMaoMao <2523610504@qq.com>
2026-09-10 22:34:08 +02:00
Andrea Chiavazza f6800e95ee fix some key binding events being passed to clients 2026-09-09 21:50:41 +01:00
Siva Mahadevan 7fb20244c6 move util.c to util.h
This allows single-source compilation and whole-program optimization using something like:

make CFLAGS='-flto -ffunction-sections -fdata-sections -Wl,--gc-sections'

Furthermore, move more compositor-agnostic utility functions/macros to util.h,
similar to the original dwm sources.
2026-09-09 09:37:07 -04:00
A Frederick Christensen 21be76ac80 Formally not lead developers in README
User @guidocella joins dwl as a lead developer
2026-09-08 11:33:59 -05:00
julmajustus 70c33c015a Remove rendermon pending resize frame skip
This is no longer needed as wlroots => 0.19 handles this internally via scene-level surface state tracking and
linux-drm-syncobj explicit sync.

The manual commit skip now causes the c->resize to be stuck on non-zero
state which leads to choppy resizing experience.
2026-09-08 17:28:19 +02:00
Guido Cella ee788f0149 update wlroots version in commented variables 2026-09-08 11:44:44 +02:00
A Frederick Christensen 132606e133 add support for ext-image-copy-capture-v1 and ext-image-capture-source-v1 (wlroots!4545) 2026-09-08 11:44:44 +02:00
Thanatos 49ca434514 Bumped to 0.9 for wlroots-0.20.0. Updated cursor image buffer handling for xwayland 2026-09-08 11:44:44 +02:00
save196 babccfe6dc Preserve asleep disabled outputs in output config 2026-09-08 11:39:37 +02:00
klim 4847f97678 Clear surface->data on unmap to fix idle-inhibitor use-after-free
mapnotify() stores the client's scene tree in client_surface(c)->data;
checkidleinhibitor() later reads it back as a wlr_scene_tree and calls
wlr_scene_node_coords() on it. unmapnotify() frees the scene tree with
wlr_scene_node_destroy() but leaves surface->data dangling, so the next
arrange() -> checkidleinhibitor() dereferences freed memory and crashes
(SIGSEGV) whenever an idle inhibitor outlives the unmap.

Reset the pointer to NULL on unmap so the existing !tree guard handles it.
2026-09-08 09:30:56 +02:00
Alex Denes 04279f28e0 Remove decoration event listeners on destroynotify
When destroynotify doesn't clean up these listeners it causes a crash once the cleanup of the decoration is triggered

Thanks to kennylevinsen - https://codeberg.org/dwl/dwl/issues/1205#issuecomment-22424401
2026-09-06 13:52:31 +00:00
Alex Denes f4dfdabd0b NULL out decoration on destroy 2026-09-06 13:52:31 +00:00
Diego Viola a2d03cf618 Remove trailing whitespace from README 2026-03-02 02:13:43 +01:00
Diego Viola cb4cc3ae8f Fix typos 2026-02-25 02:38:09 -03:00
thanatos 9b76ee8e90 Updated README to clarify branch roles
This change effectively reverts DWL to the prior development model, where development of DWL itself happens against the current wlroots release, and changes in anticipation of the next wlroots release are made on a separate branch.
2026-02-21 19:50:17 +01:00
thanatos 908a73da82 Corrected README 2026-02-21 16:53:19 +01:00
thanatos 227cdf0128 Added Matrix to README 2026-02-21 16:46:08 +01:00
Guido Cella ca4123072d add support for the data control protocol
Enables the protocol documented at
https://wayland.app/protocols/ext-data-control-v1

It is the upstreamed version of the old wlr_data_control.

It is used e.g. by mpv to read and write the clipboard.
2026-02-12 01:47:40 +01:00
fauxmight a8915224e8 Remove "unmaintained" notice from README
User @thanatos has offered to take on the lead developer role for dwl.
2026-02-09 08:06:25 +01:00
9 changed files with 1061 additions and 236 deletions
+8 -10
View File
@@ -5,7 +5,7 @@ include config.mk
# flags for compiling # flags for compiling
DWLCPPFLAGS = -I. -DWLR_USE_UNSTABLE -D_POSIX_C_SOURCE=200809L \ DWLCPPFLAGS = -I. -DWLR_USE_UNSTABLE -D_POSIX_C_SOURCE=200809L \
-DVERSION=\"$(VERSION)\" $(XWAYLAND) -DVERSION=\"$(VERSION)\" $(XWAYLAND) $(CPPFLAGS)
DWLDEVCFLAGS = -g -Wpedantic -Wall -Wextra -Wdeclaration-after-statement \ DWLDEVCFLAGS = -g -Wpedantic -Wall -Wextra -Wdeclaration-after-statement \
-Wno-unused-parameter -Wshadow -Wunused-macros -Werror=strict-prototypes \ -Wno-unused-parameter -Wshadow -Wunused-macros -Werror=strict-prototypes \
-Werror=implicit -Werror=return-type -Werror=incompatible-pointer-types \ -Werror=implicit -Werror=return-type -Werror=incompatible-pointer-types \
@@ -17,12 +17,11 @@ DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEV
LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS) LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS)
all: dwl all: dwl
dwl: dwl.o util.o dwl: dwl.c client.h config.h ime.h util.h config.mk cursor-shape-v1-protocol.h \
$(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ ext-image-copy-capture-v1-protocol.h \
dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \
pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \ pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \
wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h
util.o: util.c util.h $(CC) dwl.c $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@
# wayland-scanner is a tool which generates C headers and rigging for Wayland # wayland-scanner is a tool which generates C headers and rigging for Wayland
# protocols, which are specified in XML. wlroots requires you to rig these up # protocols, which are specified in XML. wlroots requires you to rig these up
@@ -33,6 +32,9 @@ WAYLAND_PROTOCOLS = `$(PKG_CONFIG) --variable=pkgdatadir wayland-protocols`
cursor-shape-v1-protocol.h: cursor-shape-v1-protocol.h:
$(WAYLAND_SCANNER) enum-header \ $(WAYLAND_SCANNER) enum-header \
$(WAYLAND_PROTOCOLS)/staging/cursor-shape/cursor-shape-v1.xml $@ $(WAYLAND_PROTOCOLS)/staging/cursor-shape/cursor-shape-v1.xml $@
ext-image-copy-capture-v1-protocol.h:
$(WAYLAND_SCANNER) enum-header \
$(WAYLAND_PROTOCOLS)/staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml $@
pointer-constraints-unstable-v1-protocol.h: pointer-constraints-unstable-v1-protocol.h:
$(WAYLAND_SCANNER) enum-header \ $(WAYLAND_SCANNER) enum-header \
$(WAYLAND_PROTOCOLS)/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml $@ $(WAYLAND_PROTOCOLS)/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml $@
@@ -54,7 +56,7 @@ clean:
dist: clean dist: clean
mkdir -p dwl-$(VERSION) mkdir -p dwl-$(VERSION)
cp -R LICENSE* Makefile CHANGELOG.md README.md client.h config.def.h \ cp -R LICENSE* Makefile CHANGELOG.md README.md client.h config.def.h \
config.mk protocols dwl.1 dwl.c util.c util.h dwl.desktop \ config.mk protocols dwl.1 dwl.c ime.h util.h dwl.desktop \
dwl-$(VERSION) dwl-$(VERSION)
tar -caf dwl-$(VERSION).tar.gz dwl-$(VERSION) tar -caf dwl-$(VERSION).tar.gz dwl-$(VERSION)
rm -rf dwl-$(VERSION) rm -rf dwl-$(VERSION)
@@ -73,7 +75,3 @@ install: dwl
uninstall: uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/dwl $(DESTDIR)$(MANDIR)/man1/dwl.1 \ rm -f $(DESTDIR)$(PREFIX)/bin/dwl $(DESTDIR)$(MANDIR)/man1/dwl.1 \
$(DESTDIR)$(DATADIR)/wayland-sessions/dwl.desktop $(DESTDIR)$(DATADIR)/wayland-sessions/dwl.desktop
.SUFFIXES: .c .o
.c.o:
$(CC) $(CPPFLAGS) $(DWLCFLAGS) -o $@ -c $<
+17 -20
View File
@@ -1,15 +1,8 @@
# dwl - dwm for Wayland # dwl - dwm for Wayland
2025-08-16: Join us on our [Discord server]
dwl IS CURRENTLY UN-MAINTAINED. Or Matrix: [#dwl-official:matrix.org]
AT THE PRESENT TIME, I (@fauxmight) DO NOT HAVE Or on our IRC channel: [#dwl on Libera Chat]
THE TIME OR CAPACITY TO KEEP UP WITH [wlroots] CHANGES.
IF YOU ARE INTERESTED IN TAKING ON LEAD DEVELOPER RESPONSIBILITIES,
SEE ISSUE [#1166](https://codeberg.org/dwl/dwl/issues/1166).
---
Join us on our IRC channel: [#dwl on Libera Chat]
Or on the community-maintained [Discord server].
dwl is a compact, hackable compositor for [Wayland] based on [wlroots]. It is dwl is a compact, hackable compositor for [Wayland] based on [wlroots]. It is
intended to fill the same space in the Wayland world that [dwm] does in X11, intended to fill the same space in the Wayland world that [dwm] does in X11,
@@ -17,7 +10,7 @@ primarily in terms of functionality, and secondarily in terms of
philosophy. Like [dwm], dwl is: philosophy. Like [dwm], dwl is:
- Easy to understand, hack on, and extend with patches - Easy to understand, hack on, and extend with patches
- One C source file (or a very small number) configurable via `config.h` - One C source file configurable via `config.h`
- Tied to as few external dependencies as possible - Tied to as few external dependencies as possible
## Getting Started: ## Getting Started:
@@ -31,11 +24,13 @@ with each release on the [release] page
### Development branch [main] ### Development branch [main]
Active development progresses on the `main` branch. The `main` branch is built Active development progresses on the `main` branch. The `main` branch is built
against a late (and often changing) git commit of wlroots. While the adventurous against the latest release of [wlroots]. PRs should target this branch unless they
are welcome to use `main`, it is a rocky road. Using `main` requires that the depend on functionality that is not in the current release of `wlroots`.
user be willing to chase git commits of wlroots. Testing development pull
requests may involve merging unmerged pull requests in [wlroots]' git repository ### Preview branch [wlroots-next]
and/or git commits of wayland. The `wlroots-next` branch is built against the git version of [wlroots], which
is unstable and changes frequently. PRs requiring functionality from the git
version of `wlroots` should target this branch.
### Building dwl ### Building dwl
dwl has the following dependencies: dwl has the following dependencies:
@@ -158,7 +153,6 @@ given the base on which it is built. Implemented default features are:
- Support screen lockers via ext-session-lock-v1 protocol - Support screen lockers via ext-session-lock-v1 protocol
- Various Wayland protocols - Various Wayland protocols
- XWayland support as provided by wlroots (can be enabled in `config.mk`) - XWayland support as provided by wlroots (can be enabled in `config.mk`)
- Zero flickering - Wayland users naturally expect that "every frame is perfect"
- Layer shell popups (used by Waybar) - Layer shell popups (used by Waybar)
- Damage tracking provided by scenegraph API - Damage tracking provided by scenegraph API
@@ -171,9 +165,6 @@ indiscriminately. We will try to keep the code as small as possible.
Features under consideration (possibly as patches) are: Features under consideration (possibly as patches) are:
- Protocols made trivial by wlroots - Protocols made trivial by wlroots
- Implement the text-input and input-method protocols to support IME once ibus
implements input-method v2 (see https://github.com/ibus/ibus/pull/2256 and
https://codeberg.org/dwl/dwl/pulls/235)
Feature *non-goals* for the main codebase include: Feature *non-goals* for the main codebase include:
@@ -198,6 +189,10 @@ inspiration, and to the various contributors to the project, including:
and for helping to keep the project running and for helping to keep the project running
- Stivvo for output management and fullscreen support, and patch maintenance - Stivvo for output management and fullscreen support, and patch maintenance
Also, thanks to our current lead developers:
- @thanatos
- @guidocella
[wlroots]: https://gitlab.freedesktop.org/wlroots [wlroots]: https://gitlab.freedesktop.org/wlroots
[dwm]: https://dwm.suckless.org/ [dwm]: https://dwm.suckless.org/
@@ -210,6 +205,7 @@ inspiration, and to the various contributors to the project, including:
[dwl-patches]: https://codeberg.org/dwl/dwl-patches [dwl-patches]: https://codeberg.org/dwl/dwl-patches
[list of useful resources on our wiki]: https://codeberg.org/dwl/dwl/wiki/Home#migrating-from-x [list of useful resources on our wiki]: https://codeberg.org/dwl/dwl/wiki/Home#migrating-from-x
[main]: https://codeberg.org/dwl/dwl/src/branch/main [main]: https://codeberg.org/dwl/dwl/src/branch/main
[wlroots-next]: https://codeberg.org/dwl/dwl/src/branch/wlroots-next
[release]: https://codeberg.org/dwl/dwl/releases [release]: https://codeberg.org/dwl/dwl/releases
[runit]: http://smarden.org/runit/faq.html#userservices [runit]: http://smarden.org/runit/faq.html#userservices
[s6]: https://skarnet.org/software/s6/ [s6]: https://skarnet.org/software/s6/
@@ -217,3 +213,4 @@ inspiration, and to the various contributors to the project, including:
[wiki]: https://codeberg.org/dwl/dwl/wiki/Home#compatible-status-bars [wiki]: https://codeberg.org/dwl/dwl/wiki/Home#compatible-status-bars
[Discord server]: https://discord.gg/jJxZnrGPWN [Discord server]: https://discord.gg/jJxZnrGPWN
[Wayland]: https://wayland.freedesktop.org/ [Wayland]: https://wayland.freedesktop.org/
[#dwl-official:matrix.org]: https://matrix.to/#/#dwl-official:matrix.org
+11 -4
View File
@@ -291,11 +291,18 @@ client_is_unmanaged(Client *c)
static inline void static inline void
client_notify_enter(struct wlr_surface *s, struct wlr_keyboard *kb) client_notify_enter(struct wlr_surface *s, struct wlr_keyboard *kb)
{ {
if (kb) uint32_t filtered[WLR_KEYBOARD_KEYS_CAP];
wlr_seat_keyboard_notify_enter(seat, s, kb->keycodes, size_t size = 0;
kb->num_keycodes, &kb->modifiers); if (!kb) {
else
wlr_seat_keyboard_notify_enter(seat, s, NULL, 0, NULL); wlr_seat_keyboard_notify_enter(seat, s, NULL, 0, NULL);
return;
}
for (size_t i = 0; i < kb->num_keycodes; i++) {
uint32_t key = kb->keycodes[i];
if (!consumed[key])
filtered[size++] = key;
}
wlr_seat_keyboard_notify_enter(seat, s, filtered, size, &kb->modifiers);
} }
static inline void static inline void
+23 -18
View File
@@ -7,6 +7,7 @@
static const int sloppyfocus = 1; /* focus follows mouse */ static const int sloppyfocus = 1; /* focus follows mouse */
static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */ static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */
static const unsigned int borderpx = 1; /* border pixel of windows */ static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int snap = 32; /* snap pixel */
static const float rootcolor[] = COLOR(0x222222ff); static const float rootcolor[] = COLOR(0x222222ff);
static const float bordercolor[] = COLOR(0x444444ff); static const float bordercolor[] = COLOR(0x444444ff);
static const float focuscolor[] = COLOR(0x005577ff); static const float focuscolor[] = COLOR(0x005577ff);
@@ -105,11 +106,11 @@ static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TA
/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */ /* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */
#define MODKEY WLR_MODIFIER_ALT #define MODKEY WLR_MODIFIER_ALT
#define TAGKEYS(KEY,SKEY,TAG) \ #define TAGKEYS(KEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \ { MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \ { MODKEY|WLR_MODIFIER_SHIFT, KEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} } { MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,KEY,toggletag, {.ui = 1 << TAG} }
/* helper for spawning shell commands in the pre dwm-5.0 fashion */ /* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
@@ -119,7 +120,6 @@ static const char *termcmd[] = { "foot", NULL };
static const char *menucmd[] = { "wmenu-run", NULL }; static const char *menucmd[] = { "wmenu-run", NULL };
static const Key keys[] = { static const Key keys[] = {
/* Note that Shift changes certain key codes: 2 -> at, etc. */
/* modifier key function argument */ /* modifier key function argument */
{ MODKEY, XKB_KEY_p, spawn, {.v = menucmd} }, { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} },
@@ -139,28 +139,28 @@ static const Key keys[] = {
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} }, { MODKEY, XKB_KEY_e, togglefullscreen, {0} },
{ MODKEY, XKB_KEY_0, view, {.ui = ~0} }, { MODKEY, XKB_KEY_0, view, {.ui = ~0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_0, tag, {.ui = ~0} },
{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} }, { MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_comma, tagmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_period, tagmon, {.i = WLR_DIRECTION_RIGHT} },
TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0), TAGKEYS( XKB_KEY_1, 0),
TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), TAGKEYS( XKB_KEY_2, 1),
TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), TAGKEYS( XKB_KEY_3, 2),
TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3), TAGKEYS( XKB_KEY_4, 3),
TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4), TAGKEYS( XKB_KEY_5, 4),
TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5), TAGKEYS( XKB_KEY_6, 5),
TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6), TAGKEYS( XKB_KEY_7, 6),
TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7), TAGKEYS( XKB_KEY_8, 7),
TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8), TAGKEYS( XKB_KEY_9, 8),
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_q, quit, {0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_q, quit, {0} },
/* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */ /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
{ WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} }, { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_BackSpace, quit, {0} },
/* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is /* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is
* do not remove them. * do not remove them.
*/ */
#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} } #define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_F##n, chvt, {.ui = (n)} }
CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6), CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6),
CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12), CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
}; };
@@ -170,3 +170,8 @@ static const Button buttons[] = {
{ MODKEY, BTN_MIDDLE, togglefloating, {0} }, { MODKEY, BTN_MIDDLE, togglefloating, {0} },
{ MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} }, { MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
}; };
static const Axis axes[] = {
{ MODKEY, AxisUp, spawn, SHCMD("volume-up_EXAMPLE") },
{ MODKEY, AxisDown, spawn, SHCMD("volume-down_EXAMPLE") },
};
+7 -7
View File
@@ -1,4 +1,4 @@
_VERSION = 0.8-dev _VERSION = 0.9-dev
VERSION = `git describe --tags --dirty 2>/dev/null || echo $(_VERSION)` VERSION = `git describe --tags --dirty 2>/dev/null || echo $(_VERSION)`
PKG_CONFIG = pkg-config PKG_CONFIG = pkg-config
@@ -8,8 +8,8 @@ PREFIX = /usr/local
MANDIR = $(PREFIX)/share/man MANDIR = $(PREFIX)/share/man
DATADIR = $(PREFIX)/share DATADIR = $(PREFIX)/share
WLR_INCS = `$(PKG_CONFIG) --cflags wlroots-0.19` WLR_INCS = `$(PKG_CONFIG) --cflags wlroots-0.20`
WLR_LIBS = `$(PKG_CONFIG) --libs wlroots-0.19` WLR_LIBS = `$(PKG_CONFIG) --libs wlroots-0.20`
# Allow using an alternative wlroots installation # Allow using an alternative wlroots installation
# This has to have all the includes required by wlroots, e.g: # This has to have all the includes required by wlroots, e.g:
@@ -17,12 +17,12 @@ WLR_LIBS = `$(PKG_CONFIG) --libs wlroots-0.19`
#WLR_INCS = -I/usr/include/pixman-1 -I/usr/include/elogind -I/usr/include/libdrm \ #WLR_INCS = -I/usr/include/pixman-1 -I/usr/include/elogind -I/usr/include/libdrm \
# -I$(PWD)/wlroots/include # -I$(PWD)/wlroots/include
# Set -rpath to avoid using the wrong library. # Set -rpath to avoid using the wrong library.
#WLR_LIBS = -Wl,-rpath,$(PWD)/wlroots/build -L$(PWD)/wlroots/build -lwlroots-0.19 #WLR_LIBS = -Wl,-rpath,$(PWD)/wlroots/build -L$(PWD)/wlroots/build -lwlroots-0.20
# Assuming you ran "meson setup --prefix ${PWD}/0.19 build && ninja -C build install" # Assuming you ran "meson setup --prefix ${PWD}/0.20 build && ninja -C build install"
#WLR_INCS = -I/usr/include/pixman-1 -I/usr/include/elogind -I/usr/include/libdrm \ #WLR_INCS = -I/usr/include/pixman-1 -I/usr/include/elogind -I/usr/include/libdrm \
# -I$(PWD)/wlroots/0.19/include/wlroots-0.19 # -I$(PWD)/wlroots/0.20/include/wlroots-0.20
#WLR_LIBS = -Wl,-rpath,$(PWD)/wlroots/0.19/lib64 -L$(PWD)/wlroots/0.19/lib64 -lwlroots-0.19 #WLR_LIBS = -Wl,-rpath,$(PWD)/wlroots/0.20/lib64 -L$(PWD)/wlroots/0.20/lib64 -lwlroots-0.20
XWAYLAND = XWAYLAND =
XLIBS = XLIBS =
+286 -121
View File
@@ -15,6 +15,7 @@
#include <wlr/backend.h> #include <wlr/backend.h>
#include <wlr/backend/libinput.h> #include <wlr/backend/libinput.h>
#include <wlr/render/allocator.h> #include <wlr/render/allocator.h>
#include <wlr/render/swapchain.h>
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_alpha_modifier_v1.h> #include <wlr/types/wlr_alpha_modifier_v1.h>
#include <wlr/types/wlr_compositor.h> #include <wlr/types/wlr_compositor.h>
@@ -24,6 +25,10 @@
#include <wlr/types/wlr_data_device.h> #include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_drm.h> #include <wlr/types/wlr_drm.h>
#include <wlr/types/wlr_export_dmabuf_v1.h> #include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_ext_data_control_v1.h>
#include <wlr/types/wlr_ext_image_capture_source_v1.h>
#include <wlr/types/wlr_ext_image_copy_capture_v1.h>
#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
#include <wlr/types/wlr_fractional_scale_v1.h> #include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h> #include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_idle_inhibit_v1.h> #include <wlr/types/wlr_idle_inhibit_v1.h>
@@ -38,6 +43,7 @@
#include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output_management_v1.h> #include <wlr/types/wlr_output_management_v1.h>
#include <wlr/types/wlr_output_power_management_v1.h> #include <wlr/types/wlr_output_power_management_v1.h>
#include <wlr/types/wlr_output_swapchain_manager.h>
#include <wlr/types/wlr_pointer.h> #include <wlr/types/wlr_pointer.h>
#include <wlr/types/wlr_pointer_constraints_v1.h> #include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_presentation_time.h> #include <wlr/types/wlr_presentation_time.h>
@@ -68,15 +74,12 @@
#include <xcb/xcb_icccm.h> #include <xcb/xcb_icccm.h>
#endif #endif
#include "xdg-shell-protocol.h"
#include "util.h" #include "util.h"
/* macros */ /* macros */
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) #define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS)
#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) #define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags]))
#define LENGTH(X) (sizeof X / sizeof X[0])
#define END(A) ((A) + LENGTH(A))
#define TAGMASK ((1u << TAGCOUNT) - 1) #define TAGMASK ((1u << TAGCOUNT) - 1)
#define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) #define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L)))
#define LISTEN_STATIC(E, H) do { struct wl_listener *_l = ecalloc(1, sizeof(*_l)); _l->notify = (H); wl_signal_add((E), _l); } while (0) #define LISTEN_STATIC(E, H) do { struct wl_listener *_l = ecalloc(1, sizeof(*_l)); _l->notify = (H); wl_signal_add((E), _l); } while (0)
@@ -84,7 +87,8 @@
/* enums */ /* enums */
enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */
enum { XDGShell, LayerShell, X11 }; /* client types */ enum { XDGShell, LayerShell, X11 }; /* client types */
enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock, NUM_LAYERS }; /* scene layers */ enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrIMPopup, LyrBlock, NUM_LAYERS }; /* scene layers */
enum { AxisUp, AxisRight, AxisDown, AxisLeft };
typedef union { typedef union {
int i; int i;
@@ -93,6 +97,13 @@ typedef union {
const void *v; const void *v;
} Arg; } Arg;
typedef struct {
unsigned int mod;
unsigned int dir;
void (*func)(const Arg *);
const Arg arg;
} Axis;
typedef struct { typedef struct {
unsigned int mod; unsigned int mod;
unsigned int button; unsigned int button;
@@ -119,6 +130,13 @@ typedef struct {
struct wlr_xwayland_surface *xwayland; struct wlr_xwayland_surface *xwayland;
} surface; } surface;
struct wlr_xdg_toplevel_decoration_v1 *decoration; struct wlr_xdg_toplevel_decoration_v1 *decoration;
struct wlr_ext_foreign_toplevel_handle_v1 *foreign_toplevel_handle;
struct wlr_scene *image_capture_scene;
struct wlr_ext_image_capture_source_v1 *image_capture_source;
union {
struct wlr_scene_tree *image_capture_tree;
struct wlr_scene_surface *image_capture_scene_surface;
} capture;
struct wl_listener commit; struct wl_listener commit;
struct wl_listener map; struct wl_listener map;
struct wl_listener maximize; struct wl_listener maximize;
@@ -150,10 +168,10 @@ typedef struct {
typedef struct { typedef struct {
struct wlr_keyboard_group *wlr_group; struct wlr_keyboard_group *wlr_group;
struct wlr_keyboard *virtual_keyboard;
int nsyms; xkb_keysym_t keysyms[2];
const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */ uint32_t mods;
uint32_t mods; /* invalid if nsyms == 0 */
struct wl_event_source *key_repeat_source; struct wl_event_source *key_repeat_source;
struct wl_listener modifiers; struct wl_listener modifiers;
@@ -248,6 +266,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list,
static void arrangelayers(Monitor *m); static void arrangelayers(Monitor *m);
static void axisnotify(struct wl_listener *listener, void *data); static void axisnotify(struct wl_listener *listener, void *data);
static void buttonpress(struct wl_listener *listener, void *data); static void buttonpress(struct wl_listener *listener, void *data);
static void capturerequest(struct wl_listener *listener, void *data);
static void chvt(const Arg *arg); static void chvt(const Arg *arg);
static void checkidleinhibitor(struct wlr_surface *exclude); static void checkidleinhibitor(struct wlr_surface *exclude);
static void cleanup(void); static void cleanup(void);
@@ -291,7 +310,7 @@ static void gpureset(struct wl_listener *listener, void *data);
static void handlesig(int signo); static void handlesig(int signo);
static void incnmaster(const Arg *arg); static void incnmaster(const Arg *arg);
static void inputdevice(struct wl_listener *listener, void *data); static void inputdevice(struct wl_listener *listener, void *data);
static int keybinding(uint32_t mods, xkb_keysym_t sym); static const Key *keybinding(uint32_t mods, xkb_keysym_t sym);
static void keypress(struct wl_listener *listener, void *data); static void keypress(struct wl_listener *listener, void *data);
static void keypressmod(struct wl_listener *listener, void *data); static void keypressmod(struct wl_listener *listener, void *data);
static int keyrepeat(void *data); static int keyrepeat(void *data);
@@ -308,7 +327,7 @@ static void moveresize(const Arg *arg);
static void outputmgrapply(struct wl_listener *listener, void *data); static void outputmgrapply(struct wl_listener *listener, void *data);
static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test); static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test);
static void outputmgrtest(struct wl_listener *listener, void *data); static void outputmgrtest(struct wl_listener *listener, void *data);
static void pointerfocus(Client *c, struct wlr_surface *surface, static void pointerfocus(Client *c, LayerSurface* l, struct wlr_surface *surface,
double sx, double sy, uint32_t time); double sx, double sy, uint32_t time);
static void printstatus(void); static void printstatus(void);
static void powermgrsetmode(struct wl_listener *listener, void *data); static void powermgrsetmode(struct wl_listener *listener, void *data);
@@ -382,6 +401,8 @@ static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr;
static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr; static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr;
static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr; static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr;
static struct wlr_output_power_manager_v1 *power_mgr; static struct wlr_output_power_manager_v1 *power_mgr;
static struct wlr_ext_foreign_toplevel_list_v1 *foreign_toplevel_list;
static struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *ext_foreign_toplevel_image_capture_source_manager_v1;
static struct wlr_pointer_constraints_v1 *pointer_constraints; static struct wlr_pointer_constraints_v1 *pointer_constraints;
static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr; static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr;
@@ -405,6 +426,7 @@ static struct wlr_output_layout *output_layout;
static struct wlr_box sgeom; static struct wlr_box sgeom;
static struct wl_list mons; static struct wl_list mons;
static Monitor *selmon; static Monitor *selmon;
static int consumed[KEY_MAX + 1];
/* global event handlers */ /* global event handlers */
static struct wl_listener cursor_axis = {.notify = axisnotify}; static struct wl_listener cursor_axis = {.notify = axisnotify};
@@ -435,6 +457,7 @@ static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape};
static struct wl_listener request_start_drag = {.notify = requeststartdrag}; static struct wl_listener request_start_drag = {.notify = requeststartdrag};
static struct wl_listener start_drag = {.notify = startdrag}; static struct wl_listener start_drag = {.notify = startdrag};
static struct wl_listener new_session_lock = {.notify = locksession}; static struct wl_listener new_session_lock = {.notify = locksession};
static struct wl_listener new_foreign_toplevel_capture_request = {.notify = capturerequest};
#ifdef XWAYLAND #ifdef XWAYLAND
static void activatex11(struct wl_listener *listener, void *data); static void activatex11(struct wl_listener *listener, void *data);
@@ -455,6 +478,8 @@ static struct wlr_xwayland *xwayland;
/* attempt to encapsulate suck into one file */ /* attempt to encapsulate suck into one file */
#include "client.h" #include "client.h"
#include "ime.h"
/* function implementations */ /* function implementations */
void void
applybounds(Client *c, struct wlr_box *bbox) applybounds(Client *c, struct wlr_box *bbox)
@@ -585,14 +610,17 @@ arrangelayers(Monitor *m)
arrange(m); arrange(m);
} }
/* Arrange non-exlusive surfaces from top->bottom */ /* Arrange non-exclusive surfaces from top->bottom */
for (i = 3; i >= 0; i--) for (i = 3; i >= 0; i--)
arrangelayer(m, &m->layers[i], &usable_area, 0); arrangelayer(m, &m->layers[i], &usable_area, 0);
/* Find topmost keyboard interactive layer, if such a layer exists */ /* Find topmost keyboard interactive layer that has indicated it wants
* exclusive access to the keyboard, if such a layer exists */
for (i = 0; i < (int)LENGTH(layers_above_shell); i++) { for (i = 0; i < (int)LENGTH(layers_above_shell); i++) {
wl_list_for_each_reverse(l, &m->layers[layers_above_shell[i]], link) { wl_list_for_each_reverse(l, &m->layers[layers_above_shell[i]], link) {
if (locked || !l->layer_surface->current.keyboard_interactive || !l->mapped) if (locked ||
l->layer_surface->current.keyboard_interactive != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE ||
!l->mapped)
continue; continue;
/* Deactivate the focused client. */ /* Deactivate the focused client. */
focusclient(NULL, 0); focusclient(NULL, 0);
@@ -609,9 +637,26 @@ axisnotify(struct wl_listener *listener, void *data)
/* This event is forwarded by the cursor when a pointer emits an axis event, /* This event is forwarded by the cursor when a pointer emits an axis event,
* for example when you move the scroll wheel. */ * for example when you move the scroll wheel. */
struct wlr_pointer_axis_event *event = data; struct wlr_pointer_axis_event *event = data;
const Axis *a;
unsigned int adir;
struct wlr_keyboard *keyboard;
uint32_t mods;
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
/* TODO: allow usage of scroll wheel for mousebindings, it can be implemented
* by checking the event's orientation and the delta of the event */ if (event->orientation == WL_POINTER_AXIS_VERTICAL_SCROLL)
adir = event->delta > 0 ? AxisDown : AxisUp;
else
adir = event->delta > 0 ? AxisRight : AxisLeft;
keyboard = wlr_seat_get_keyboard(seat);
mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
for (a = axes; a < END(axes); a++) {
if (CLEANMASK(mods) == CLEANMASK(a->mod) && adir == a->dir && a->func) {
a->func(&a->arg);
return;
}
}
/* Notify the client with pointer focus of the axis event. */ /* Notify the client with pointer focus of the axis event. */
wlr_seat_pointer_notify_axis(seat, wlr_seat_pointer_notify_axis(seat,
event->time_msec, event->orientation, event->delta, event->time_msec, event->orientation, event->delta,
@@ -624,7 +669,8 @@ buttonpress(struct wl_listener *listener, void *data)
struct wlr_pointer_button_event *event = data; struct wlr_pointer_button_event *event = data;
struct wlr_keyboard *keyboard; struct wlr_keyboard *keyboard;
uint32_t mods; uint32_t mods;
Client *c; Client *c = NULL;
LayerSurface *l = NULL;
const Button *b; const Button *b;
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
@@ -636,10 +682,15 @@ buttonpress(struct wl_listener *listener, void *data)
if (locked) if (locked)
break; break;
/* Change focus if the button was _pressed_ over a client */ /* Change focus if the button was _pressed_ over a client
xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL); or a layer surface with on-demand keyboard interactivity */
if (c && (!client_is_unmanaged(c) || client_wants_focus(c))) xytonode(cursor->x, cursor->y, NULL, &c, &l, NULL, NULL);
if (c && (!client_is_unmanaged(c) || client_wants_focus(c))) {
focusclient(c, 1); focusclient(c, 1);
} else if (l && l->layer_surface->current.keyboard_interactive) {
focusclient(NULL, 0);
client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat));
}
keyboard = wlr_seat_get_keyboard(seat); keyboard = wlr_seat_get_keyboard(seat);
mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
@@ -653,10 +704,11 @@ buttonpress(struct wl_listener *listener, void *data)
break; break;
case WL_POINTER_BUTTON_STATE_RELEASED: case WL_POINTER_BUTTON_STATE_RELEASED:
/* If you released any buttons, we exit interactive move/resize mode. */ /* If you released any buttons, we exit interactive move/resize mode. */
/* TODO: should reset to the pointer focus's current setcursor */
if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) { if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) {
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
cursor_mode = CurNormal; cursor_mode = CurNormal;
/* Update the cursor */
wlr_seat_pointer_clear_focus(seat);
motionnotify(0, NULL, 0, 0, 0, 0);
/* Drop the window off on its new monitor */ /* Drop the window off on its new monitor */
selmon = xytomon(cursor->x, cursor->y); selmon = xytomon(cursor->x, cursor->y);
setmon(grabc, selmon, 0); setmon(grabc, selmon, 0);
@@ -672,6 +724,23 @@ buttonpress(struct wl_listener *listener, void *data)
event->time_msec, event->button, event->state); event->time_msec, event->button, event->state);
} }
void
capturerequest(struct wl_listener *listener, void *data)
{
struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request = data;
Client *view = request->toplevel_handle->data;
if (!view->image_capture_source) {
view->image_capture_source = wlr_ext_image_capture_source_v1_create_with_scene_node(
&view->image_capture_scene->tree.node, event_loop, alloc, drw);
if (!view->image_capture_source)
return;
}
wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept(
request, view->image_capture_source);
}
void void
chvt(const Arg *arg) chvt(const Arg *arg)
{ {
@@ -713,6 +782,8 @@ cleanup(void)
destroykeyboardgroup(&kb_group->destroy, NULL); destroykeyboardgroup(&kb_group->destroy, NULL);
input_method_relay_finish(input_method_relay);
/* If it's not destroyed manually, it will cause a use-after-free of wlr_seat. /* If it's not destroyed manually, it will cause a use-after-free of wlr_seat.
* Destroy it until it's fixed on the wlroots side */ * Destroy it until it's fixed on the wlroots side */
wlr_backend_destroy(backend); wlr_backend_destroy(backend);
@@ -782,6 +853,7 @@ cleanuplisteners(void)
wl_list_remove(&request_start_drag.link); wl_list_remove(&request_start_drag.link);
wl_list_remove(&start_drag.link); wl_list_remove(&start_drag.link);
wl_list_remove(&new_session_lock.link); wl_list_remove(&new_session_lock.link);
wl_list_remove(&new_foreign_toplevel_capture_request.link);
#ifdef XWAYLAND #ifdef XWAYLAND
wl_list_remove(&new_xwayland_surface.link); wl_list_remove(&new_xwayland_surface.link);
wl_list_remove(&xwayland_ready.link); wl_list_remove(&xwayland_ready.link);
@@ -837,6 +909,11 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data)
return; return;
} }
if (layer_surface == exclusive_focus
&& layer_surface->current.keyboard_interactive !=
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE)
exclusive_focus = NULL;
if (layer_surface->current.committed == 0 && l->mapped == layer_surface->surface->mapped) if (layer_surface->current.committed == 0 && l->mapped == layer_surface->surface->mapped)
return; return;
l->mapped = layer_surface->surface->mapped; l->mapped = layer_surface->surface->mapped;
@@ -875,6 +952,9 @@ commitnotify(struct wl_listener *listener, void *data)
if (c->decoration) if (c->decoration)
requestdecorationmode(&c->set_decoration_mode, c->decoration); requestdecorationmode(&c->set_decoration_mode, c->decoration);
wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, 0, 0); wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, 0, 0);
if (!c->isfloating)
client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT |
WLR_EDGE_RIGHT);
return; return;
} }
@@ -1241,6 +1321,7 @@ void
destroydecoration(struct wl_listener *listener, void *data) destroydecoration(struct wl_listener *listener, void *data)
{ {
Client *c = wl_container_of(listener, c, destroy_decoration); Client *c = wl_container_of(listener, c, destroy_decoration);
c->decoration = NULL;
wl_list_remove(&c->destroy_decoration.link); wl_list_remove(&c->destroy_decoration.link);
wl_list_remove(&c->set_decoration_mode.link); wl_list_remove(&c->set_decoration_mode.link);
@@ -1346,6 +1427,10 @@ destroynotify(struct wl_listener *listener, void *data)
wl_list_remove(&c->map.link); wl_list_remove(&c->map.link);
wl_list_remove(&c->unmap.link); wl_list_remove(&c->unmap.link);
wl_list_remove(&c->maximize.link); wl_list_remove(&c->maximize.link);
if (c->decoration) {
wl_list_remove(&c->set_decoration_mode.link);
wl_list_remove(&c->destroy_decoration.link);
}
} }
free(c); free(c);
} }
@@ -1393,7 +1478,8 @@ dirtomon(enum wlr_direction dir)
dir, selmon->wlr_output, selmon->m.x, selmon->m.y))) dir, selmon->wlr_output, selmon->m.x, selmon->m.y)))
return next->data; return next->data;
if ((next = wlr_output_layout_farthest_output(output_layout, if ((next = wlr_output_layout_farthest_output(output_layout,
dir ^ (WLR_DIRECTION_LEFT|WLR_DIRECTION_RIGHT), dir ^ (WLR_DIRECTION_LEFT | WLR_DIRECTION_RIGHT |
WLR_DIRECTION_UP | WLR_DIRECTION_DOWN),
selmon->wlr_output, selmon->m.x, selmon->m.y))) selmon->wlr_output, selmon->m.x, selmon->m.y)))
return next->data; return next->data;
return selmon; return selmon;
@@ -1443,7 +1529,8 @@ focusclient(Client *c, int lift)
* and focus it after the overlay is closed. */ * and focus it after the overlay is closed. */
if (old_client_type == LayerShell && wlr_scene_node_coords( if (old_client_type == LayerShell && wlr_scene_node_coords(
&old_l->scene->node, &unused_lx, &unused_ly) &old_l->scene->node, &unused_lx, &unused_ly)
&& old_l->layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { && old_l->layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP
&& old_l->layer_surface->current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) {
return; return;
} else if (old_c && old_c == exclusive_focus && client_wants_focus(old_c)) { } else if (old_c && old_c == exclusive_focus && client_wants_focus(old_c)) {
return; return;
@@ -1459,6 +1546,7 @@ focusclient(Client *c, int lift)
if (!c) { if (!c) {
/* With no client, all we have left is to clear focus */ /* With no client, all we have left is to clear focus */
input_method_relay_set_focus(input_method_relay, NULL);
wlr_seat_keyboard_notify_clear_focus(seat); wlr_seat_keyboard_notify_clear_focus(seat);
return; return;
} }
@@ -1466,6 +1554,8 @@ focusclient(Client *c, int lift)
/* Change cursor surface */ /* Change cursor surface */
motionnotify(0, NULL, 0, 0, 0, 0); motionnotify(0, NULL, 0, 0, 0, 0);
input_method_relay_set_focus(input_method_relay, client_surface(c));
/* Have a client, so focus its top-level wlr_surface */ /* Have a client, so focus its top-level wlr_surface */
client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat)); client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat));
@@ -1605,7 +1695,7 @@ inputdevice(struct wl_listener *listener, void *data)
wlr_seat_set_capabilities(seat, caps); wlr_seat_set_capabilities(seat, caps);
} }
int const Key *
keybinding(uint32_t mods, xkb_keysym_t sym) keybinding(uint32_t mods, xkb_keysym_t sym)
{ {
/* /*
@@ -1615,57 +1705,61 @@ keybinding(uint32_t mods, xkb_keysym_t sym)
*/ */
const Key *k; const Key *k;
for (k = keys; k < END(keys); k++) { for (k = keys; k < END(keys); k++) {
if (CLEANMASK(mods) == CLEANMASK(k->mod) if (CLEANMASK(mods) == CLEANMASK(k->mod) && sym == k->keysym && k->func)
&& xkb_keysym_to_lower(sym) == xkb_keysym_to_lower(k->keysym) return k;
&& k->func) {
k->func(&k->arg);
return 1;
} }
} return NULL;
return 0;
} }
void void
keypress(struct wl_listener *listener, void *data) keypress(struct wl_listener *listener, void *data)
{ {
int i;
/* This event is raised when a key is pressed or released. */ /* This event is raised when a key is pressed or released. */
KeyboardGroup *group = wl_container_of(listener, group, key); KeyboardGroup *group = wl_container_of(listener, group, key);
struct wlr_keyboard_key_event *event = data; struct wlr_keyboard_key_event *event = data;
/* Translate libinput keycode -> xkbcommon */ /* Translate libinput keycode -> xkbcommon */
uint32_t keycode = event->keycode + 8; uint32_t keycode = event->keycode + 8;
/* Get a list of keysyms based on the keymap for this keyboard */ struct wlr_keyboard *kb = &group->wlr_group->keyboard;
const xkb_keysym_t *syms; xkb_layout_index_t layout = xkb_state_key_get_layout(kb->xkb_state, keycode);
int nsyms = xkb_state_key_get_syms(
group->wlr_group->keyboard.xkb_state, keycode, &syms);
int handled = 0; int handled = 0;
uint32_t mods = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard);
// Get the keysyms for level 0 (normal) and level 1 (shifted)
// Only one keysym for each level:
// multiple keysyms per keycode don't really exist in the real world
for (int i = 0; i < 2; i++)
group->keysyms[i] = keymap_get_one_sym_by_level(kb->keymap, keycode, layout, i);
group->mods = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard);
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
/* On _press_ if there is no active screen locker, /* On _press_ if there is no active screen locker,
* attempt to process a compositor keybinding. */ * attempt to process a compositor keybinding. */
if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
for (i = 0; i < nsyms; i++) for (int i = 0; i < 2; i++) {
handled = keybinding(mods, syms[i]) || handled; const Key *key = keybinding(group->mods, group->keysyms[i]);
if (key) {
consumed[event->keycode] = 1;
key->func(&key->arg);
handled = 1;
}
}
} }
if (handled && group->wlr_group->keyboard.repeat_info.delay > 0) { if (handled && group->wlr_group->keyboard.repeat_info.delay > 0)
group->mods = mods;
group->keysyms = syms;
group->nsyms = nsyms;
wl_event_source_timer_update(group->key_repeat_source, wl_event_source_timer_update(group->key_repeat_source,
group->wlr_group->keyboard.repeat_info.delay); group->wlr_group->keyboard.repeat_info.delay);
} else { else
group->nsyms = 0;
wl_event_source_timer_update(group->key_repeat_source, 0); wl_event_source_timer_update(group->key_repeat_source, 0);
}
if (handled) if (handled || input_method_keyboard_grab_forward_key(group, event))
return; return;
if (consumed[event->keycode]) {
if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED)
consumed[event->keycode] = 0;
return; /* Don't pass to the client the release event of a handled key-press */
}
wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
/* Pass unhandled keycodes along to the client. */ /* Pass unhandled keycodes along to the client. */
wlr_seat_keyboard_notify_key(seat, event->time_msec, wlr_seat_keyboard_notify_key(seat, event->time_msec,
@@ -1679,6 +1773,9 @@ keypressmod(struct wl_listener *listener, void *data)
* pressed. We simply communicate this to the client. */ * pressed. We simply communicate this to the client. */
KeyboardGroup *group = wl_container_of(listener, group, modifiers); KeyboardGroup *group = wl_container_of(listener, group, modifiers);
if (input_method_keyboard_grab_forward_modifiers(group))
return;
wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
/* Send modifiers to the client. */ /* Send modifiers to the client. */
wlr_seat_keyboard_notify_modifiers(seat, wlr_seat_keyboard_notify_modifiers(seat,
@@ -1689,15 +1786,17 @@ int
keyrepeat(void *data) keyrepeat(void *data)
{ {
KeyboardGroup *group = data; KeyboardGroup *group = data;
int i; if (group->wlr_group->keyboard.repeat_info.rate <= 0)
if (!group->nsyms || group->wlr_group->keyboard.repeat_info.rate <= 0)
return 0; return 0;
wl_event_source_timer_update(group->key_repeat_source, wl_event_source_timer_update(group->key_repeat_source,
1000 / group->wlr_group->keyboard.repeat_info.rate); 1000 / group->wlr_group->keyboard.repeat_info.rate);
for (i = 0; i < group->nsyms; i++) for (int i = 0; i < 2; i++) {
keybinding(group->mods, group->keysyms[i]); const Key *key = keybinding(group->mods, group->keysyms[i]);
if (key)
key->func(&key->arg);
}
return 0; return 0;
} }
@@ -1743,6 +1842,11 @@ mapnotify(struct wl_listener *listener, void *data)
Monitor *m; Monitor *m;
int i; int i;
struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = {
.app_id = client_get_appid(c),
.title = client_get_title(c),
};
/* Create scene tree for this client and its border */ /* Create scene tree for this client and its border */
c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]); c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]);
/* Enabled later by a call to arrange() */ /* Enabled later by a call to arrange() */
@@ -1774,7 +1878,6 @@ mapnotify(struct wl_listener *listener, void *data)
} }
/* Initialize client geometry with room for border */ /* Initialize client geometry with room for border */
client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT);
c->geom.width += 2 * c->bw; c->geom.width += 2 * c->bw;
c->geom.height += 2 * c->bw; c->geom.height += 2 * c->bw;
@@ -1782,6 +1885,18 @@ mapnotify(struct wl_listener *listener, void *data)
wl_list_insert(&clients, &c->link); wl_list_insert(&clients, &c->link);
wl_list_insert(&fstack, &c->flink); wl_list_insert(&fstack, &c->flink);
c->foreign_toplevel_handle = wlr_ext_foreign_toplevel_handle_v1_create(
foreign_toplevel_list, &foreign_toplevel_state);
c->foreign_toplevel_handle->data = c;
c->image_capture_scene = wlr_scene_create();
if (c->type == XDGShell)
c->capture.image_capture_tree = wlr_scene_xdg_surface_create(&c->image_capture_scene->tree, c->surface.xdg);
#ifdef XWAYLAND
else
c->capture.image_capture_scene_surface = wlr_scene_surface_create(&c->image_capture_scene->tree, c->surface.xwayland->surface);
#endif
/* Set initial monitor, tags, floating status, and focus: /* Set initial monitor, tags, floating status, and focus:
* we always consider floating, clients that have parent and thus * we always consider floating, clients that have parent and thus
* we set the same tags and monitor as its parent. * we set the same tags and monitor as its parent.
@@ -1862,22 +1977,23 @@ void
motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy,
double dx_unaccel, double dy_unaccel) double dx_unaccel, double dy_unaccel)
{ {
int nx, ny;
double sx = 0, sy = 0, sx_confined, sy_confined; double sx = 0, sy = 0, sx_confined, sy_confined;
Client *c = NULL, *w = NULL; Client *c = NULL, *w = NULL;
LayerSurface *l = NULL; LayerSurface *l = NULL, *focusedl = NULL;
struct wlr_surface *surface = NULL; struct wlr_surface *surface = NULL;
struct wlr_pointer_constraint_v1 *constraint; struct wlr_pointer_constraint_v1 *constraint;
/* Find the client under the pointer and send the event along. */ /* Find the client under the pointer and send the event along. */
xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy); xytonode(cursor->x, cursor->y, &surface, &c, &l, &sx, &sy);
if (cursor_mode == CurPressed && !seat->drag if (cursor_mode == CurPressed && !seat->drag
&& surface != seat->pointer_state.focused_surface && surface != seat->pointer_state.focused_surface
&& toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w, &l) >= 0) { && toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w, &focusedl) >= 0) {
c = w; c = w;
surface = seat->pointer_state.focused_surface; surface = seat->pointer_state.focused_surface;
sx = cursor->x - (l ? l->scene->node.x : w->geom.x); sx = cursor->x - (focusedl ? focusedl->scene->node.x : w->geom.x);
sy = cursor->y - (l ? l->scene->node.y : w->geom.y); sy = cursor->y - (focusedl ? focusedl->scene->node.y : w->geom.y);
} }
/* time is 0 in internal calls meant to restore pointer focus. */ /* time is 0 in internal calls meant to restore pointer focus. */
@@ -1919,7 +2035,17 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
/* If we are currently grabbing the mouse, handle and return */ /* If we are currently grabbing the mouse, handle and return */
if (cursor_mode == CurMove) { if (cursor_mode == CurMove) {
/* Move the grabbed client to the new position. */ /* Move the grabbed client to the new position. */
resize(grabc, (struct wlr_box){.x = (int)round(cursor->x) - grabcx, .y = (int)round(cursor->y) - grabcy, nx = (int)round(cursor->x) - grabcx;
ny = (int)round(cursor->y) - grabcy;
if (abs(selmon->w.x - nx) < (int)snap)
nx = selmon->w.x;
else if (abs((selmon->w.x + selmon->w.width) - (nx + grabc->geom.width)) < (int)snap)
nx = selmon->w.x + selmon->w.width - grabc->geom.width;
if (abs(selmon->w.y - ny) < (int)snap)
ny = selmon->w.y;
else if (abs((selmon->w.y + selmon->w.height) - (ny + grabc->geom.height)) < (int)snap)
ny = selmon->w.y + selmon->w.height - grabc->geom.height;
resize(grabc, (struct wlr_box){.x = nx, .y = ny,
.width = grabc->geom.width, .height = grabc->geom.height}, 1); .width = grabc->geom.width, .height = grabc->geom.height}, 1);
return; return;
} else if (cursor_mode == CurResize) { } else if (cursor_mode == CurResize) {
@@ -1934,7 +2060,7 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
if (!surface && !seat->drag) if (!surface && !seat->drag)
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
pointerfocus(c, surface, sx, sy, time); pointerfocus(c, l, surface, sx, sy, time);
} }
void void
@@ -1997,48 +2123,60 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test)
* output_layout.change event, not here. * output_layout.change event, not here.
*/ */
struct wlr_output_configuration_head_v1 *config_head; struct wlr_output_configuration_head_v1 *config_head;
int ok = 1; size_t states_len, i;
struct wlr_output_swapchain_manager swapchain_manager;
struct wlr_backend_output_state *states =
wlr_output_configuration_v1_build_state(config, &states_len);
int ok = 0;
if (!states) {
wlr_output_configuration_v1_send_failed(config);
return;
}
wlr_output_swapchain_manager_init(&swapchain_manager, backend);
ok = wlr_output_swapchain_manager_prepare(&swapchain_manager, states, states_len);
if (!ok || test)
goto out;
for (i = 0; i < states_len; i++) {
struct wlr_swapchain *swapchain = wlr_output_swapchain_manager_get_swapchain(
&swapchain_manager, states[i].output);
if (swapchain && !states[i].output->enabled)
wlr_output_state_set_buffer(&states[i].base, wlr_swapchain_acquire(swapchain));
}
if (!(ok = wlr_backend_commit(backend, states, states_len)))
goto out;
wlr_output_swapchain_manager_apply(&swapchain_manager);
wl_list_for_each(config_head, &config->heads, link) { wl_list_for_each(config_head, &config->heads, link) {
struct wlr_output *wlr_output = config_head->state.output; struct wlr_output *wlr_output = config_head->state.output;
Monitor *m = wlr_output->data; Monitor *m = wlr_output->data;
struct wlr_output_state state;
/* Ensure displays previously disabled by wlr-output-power-management-v1 /* Ensure displays previously disabled by wlr-output-power-management-v1
* are properly handled*/ * are properly handled*/
m->asleep = 0; m->asleep = 0;
wlr_output_state_init(&state);
wlr_output_state_set_enabled(&state, config_head->state.enabled);
if (!config_head->state.enabled)
goto apply_or_test;
if (config_head->state.mode)
wlr_output_state_set_mode(&state, config_head->state.mode);
else
wlr_output_state_set_custom_mode(&state,
config_head->state.custom_mode.width,
config_head->state.custom_mode.height,
config_head->state.custom_mode.refresh);
wlr_output_state_set_transform(&state, config_head->state.transform);
wlr_output_state_set_scale(&state, config_head->state.scale);
wlr_output_state_set_adaptive_sync_enabled(&state,
config_head->state.adaptive_sync_enabled);
apply_or_test:
ok &= test ? wlr_output_test_state(wlr_output, &state)
: wlr_output_commit_state(wlr_output, &state);
/* Don't move monitors if position wouldn't change. This avoids /* Don't move monitors if position wouldn't change. This avoids
* wlroots marking the output as manually configured. * wlroots marking the output as manually configured.
* wlr_output_layout_add does not like disabled outputs */ * wlr_output_layout_add does not like disabled outputs */
if (!test && wlr_output->enabled && (m->m.x != config_head->state.x || m->m.y != config_head->state.y)) if (config_head->state.enabled
&& (m->m.x != config_head->state.x || m->m.y != config_head->state.y
|| !wlr_output_layout_get(output_layout, wlr_output))) {
wlr_output_layout_add(output_layout, wlr_output, wlr_output_layout_add(output_layout, wlr_output,
config_head->state.x, config_head->state.y); config_head->state.x, config_head->state.y);
wlr_output_state_finish(&state);
} }
}
out:
wlr_output_swapchain_manager_finish(&swapchain_manager);
for (i = 0; i < states_len; i++)
wlr_output_state_finish(&states[i].base);
free(states);
if (ok) if (ok)
wlr_output_configuration_v1_send_succeeded(config); wlr_output_configuration_v1_send_succeeded(config);
@@ -2058,14 +2196,19 @@ outputmgrtest(struct wl_listener *listener, void *data)
} }
void void
pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, pointerfocus(Client *c, LayerSurface *l, struct wlr_surface *surface, double sx, double sy,
uint32_t time) uint32_t time)
{ {
struct timespec now; struct timespec now;
if (surface != seat->pointer_state.focused_surface && if (surface != seat->pointer_state.focused_surface && sloppyfocus && time) {
sloppyfocus && time && c && !client_is_unmanaged(c)) if (c && (!client_is_unmanaged(c) || client_wants_focus(c))) {
focusclient(c, 0); focusclient(c, 0);
} else if (l && l->layer_surface->current.keyboard_interactive) {
focusclient(NULL, 0);
client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat));
}
}
/* If surface is NULL, clear pointer focus */ /* If surface is NULL, clear pointer focus */
if (!surface) { if (!surface) {
@@ -2133,7 +2276,7 @@ powermgrsetmode(struct wl_listener *listener, void *data)
if (!m) if (!m)
return; return;
m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the ouput */ m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the output */
wlr_output_state_set_enabled(&state, event->mode); wlr_output_state_set_enabled(&state, event->mode);
wlr_output_commit_state(m->wlr_output, &state); wlr_output_commit_state(m->wlr_output, &state);
@@ -2153,24 +2296,15 @@ rendermon(struct wl_listener *listener, void *data)
/* This function is called every time an output is ready to display a frame, /* This function is called every time an output is ready to display a frame,
* generally at the output's refresh rate (e.g. 60Hz). */ * generally at the output's refresh rate (e.g. 60Hz). */
Monitor *m = wl_container_of(listener, m, frame); Monitor *m = wl_container_of(listener, m, frame);
Client *c;
struct wlr_output_state pending = {0};
struct timespec now; struct timespec now;
/* Render if no XDG clients have an outstanding resize and are visible on if (!wlr_scene_output_needs_frame(m->scene_output)) {
* this monitor. */ return;
wl_list_for_each(c, &clients, link) {
if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c))
goto skip;
} }
wlr_scene_output_commit(m->scene_output, NULL); wlr_scene_output_commit(m->scene_output, NULL);
skip:
/* Let clients know a frame has been rendered */
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
wlr_scene_output_send_frame_done(m->scene_output, &now); wlr_scene_output_send_frame_done(m->scene_output, &now);
wlr_output_state_finish(&pending);
} }
void void
@@ -2455,7 +2589,7 @@ setup(void)
wlr_log_init(log_level, NULL); wlr_log_init(log_level, NULL);
/* The Wayland display is managed by libwayland. It handles accepting /* The Wayland display is managed by libwayland. It handles accepting
* clients from the Unix socket, manging Wayland globals, and so on. */ * clients from the Unix socket, managing Wayland globals, and so on. */
dpy = wl_display_create(); dpy = wl_display_create();
event_loop = wl_display_get_event_loop(dpy); event_loop = wl_display_get_event_loop(dpy);
@@ -2517,7 +2651,10 @@ setup(void)
wlr_data_device_manager_create(dpy); wlr_data_device_manager_create(dpy);
wlr_export_dmabuf_manager_v1_create(dpy); wlr_export_dmabuf_manager_v1_create(dpy);
wlr_screencopy_manager_v1_create(dpy); wlr_screencopy_manager_v1_create(dpy);
wlr_ext_image_copy_capture_manager_v1_create(dpy, 1);
wlr_ext_output_image_capture_source_manager_v1_create(dpy, 1);
wlr_data_control_manager_v1_create(dpy); wlr_data_control_manager_v1_create(dpy);
wlr_ext_data_control_manager_v1_create(dpy, 1);
wlr_primary_selection_v1_device_manager_create(dpy); wlr_primary_selection_v1_device_manager_create(dpy);
wlr_viewporter_create(dpy); wlr_viewporter_create(dpy);
wlr_single_pixel_buffer_manager_v1_create(dpy); wlr_single_pixel_buffer_manager_v1_create(dpy);
@@ -2559,7 +2696,7 @@ setup(void)
wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel); wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel);
wl_signal_add(&xdg_shell->events.new_popup, &new_xdg_popup); wl_signal_add(&xdg_shell->events.new_popup, &new_xdg_popup);
layer_shell = wlr_layer_shell_v1_create(dpy, 3); layer_shell = wlr_layer_shell_v1_create(dpy, 4);
wl_signal_add(&layer_shell->events.new_surface, &new_layer_surface); wl_signal_add(&layer_shell->events.new_surface, &new_layer_surface);
idle_notifier = wlr_idle_notifier_v1_create(dpy); idle_notifier = wlr_idle_notifier_v1_create(dpy);
@@ -2573,6 +2710,12 @@ setup(void)
(float [4]){0.1f, 0.1f, 0.1f, 1.0f}); (float [4]){0.1f, 0.1f, 0.1f, 1.0f});
wlr_scene_node_set_enabled(&locked_bg->node, 0); wlr_scene_node_set_enabled(&locked_bg->node, 0);
foreign_toplevel_list = wlr_ext_foreign_toplevel_list_v1_create(dpy,1);
ext_foreign_toplevel_image_capture_source_manager_v1 =
wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(dpy, 1);
wl_signal_add(&ext_foreign_toplevel_image_capture_source_manager_v1->events.new_request,
&new_foreign_toplevel_capture_request);
/* Use decoration protocols to negotiate server-side decorations */ /* Use decoration protocols to negotiate server-side decorations */
wlr_server_decoration_manager_set_default_mode( wlr_server_decoration_manager_set_default_mode(
wlr_server_decoration_manager_create(dpy), wlr_server_decoration_manager_create(dpy),
@@ -2646,6 +2789,11 @@ setup(void)
wl_signal_add(&output_mgr->events.apply, &output_mgr_apply); wl_signal_add(&output_mgr->events.apply, &output_mgr_apply);
wl_signal_add(&output_mgr->events.test, &output_mgr_test); wl_signal_add(&output_mgr->events.test, &output_mgr_test);
input_method_manager = wlr_input_method_manager_v2_create(dpy);
text_input_manager = wlr_text_input_manager_v3_create(dpy);
input_method_relay = ecalloc(1, sizeof(*input_method_relay));
input_method_relay = input_method_relay_create();
/* Make sure XWayland clients don't connect to the parent X server, /* Make sure XWayland clients don't connect to the parent X server,
* e.g when running in the x11 backend or the wayland backend and the * e.g when running in the x11 backend or the wayland backend and the
* compositor has Xwayland support */ * compositor has Xwayland support */
@@ -2831,7 +2979,20 @@ unmapnotify(struct wl_listener *listener, void *data)
wl_list_remove(&c->flink); wl_list_remove(&c->flink);
} }
if (c->foreign_toplevel_handle) {
wlr_ext_foreign_toplevel_handle_v1_destroy(c->foreign_toplevel_handle);
c->foreign_toplevel_handle = NULL;
}
#ifdef XWAYLAND
if (c->type != XDGShell && c->capture.image_capture_scene_surface) {
wlr_scene_node_destroy(&c->capture.image_capture_scene_surface->buffer->node);
c->capture.image_capture_scene_surface = NULL;
}
#endif
wlr_scene_node_destroy(&c->image_capture_scene->tree.node);
wlr_scene_node_destroy(&c->scene->node); wlr_scene_node_destroy(&c->scene->node);
client_surface(c)->data = NULL;
printstatus(); printstatus();
motionnotify(0, NULL, 0, 0, 0, 0); motionnotify(0, NULL, 0, 0, 0, 0);
} }
@@ -2854,10 +3015,13 @@ updatemons(struct wl_listener *listener, void *data)
/* First remove from the layout the disabled monitors */ /* First remove from the layout the disabled monitors */
wl_list_for_each(m, &mons, link) { wl_list_for_each(m, &mons, link) {
if (m->wlr_output->enabled || m->asleep) if (m->wlr_output->enabled)
continue; continue;
config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output); config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output);
config_head->state.enabled = 0; config_head->state.enabled = 0;
if (m->asleep)
continue;
/* Remove this output from the layout to avoid cursor enter inside it */ /* Remove this output from the layout to avoid cursor enter inside it */
wlr_output_layout_remove(output_layout, m->wlr_output); wlr_output_layout_remove(output_layout, m->wlr_output);
closemon(m); closemon(m);
@@ -2948,6 +3112,15 @@ updatetitle(struct wl_listener *listener, void *data)
Client *c = wl_container_of(listener, c, set_title); Client *c = wl_container_of(listener, c, set_title);
if (c == focustop(c->mon)) if (c == focustop(c->mon))
printstatus(); printstatus();
if (c->foreign_toplevel_handle) {
struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = {
.app_id = client_get_appid(c),
.title = client_get_title(c),
};
wlr_ext_foreign_toplevel_handle_v1_update_state(c->foreign_toplevel_handle,
&foreign_toplevel_state);
}
} }
void void
@@ -2985,12 +3158,14 @@ virtualkeyboard(struct wl_listener *listener, void *data)
struct wlr_virtual_keyboard_v1 *kb = data; struct wlr_virtual_keyboard_v1 *kb = data;
/* virtual keyboards shouldn't share keyboard group */ /* virtual keyboards shouldn't share keyboard group */
KeyboardGroup *group = createkeyboardgroup(); KeyboardGroup *group = createkeyboardgroup();
group->virtual_keyboard = &kb->keyboard;
/* Set the keymap to match the group keymap */ /* Set the keymap to match the group keymap */
wlr_keyboard_set_keymap(&kb->keyboard, group->wlr_group->keyboard.keymap); wlr_keyboard_set_keymap(&kb->keyboard, group->wlr_group->keyboard.keymap);
LISTEN(&kb->keyboard.base.events.destroy, &group->destroy, destroykeyboardgroup); LISTEN(&kb->keyboard.base.events.destroy, &group->destroy, destroykeyboardgroup);
/* Add the new keyboard to the group */ /* Add the new keyboard to the group */
wlr_keyboard_group_add_keyboard(group->wlr_group, &kb->keyboard); wlr_keyboard_group_add_keyboard(group->wlr_group, &kb->keyboard);
wlr_seat_set_capabilities(seat, seat->capabilities | WL_SEAT_CAPABILITY_KEYBOARD);
} }
void void
@@ -3002,6 +3177,7 @@ virtualpointer(struct wl_listener *listener, void *data)
wlr_cursor_attach_input_device(cursor, device); wlr_cursor_attach_input_device(cursor, device);
if (event->suggested_output) if (event->suggested_output)
wlr_cursor_map_input_to_output(cursor, device, event->suggested_output); wlr_cursor_map_input_to_output(cursor, device, event->suggested_output);
wlr_seat_set_capabilities(seat, seat->capabilities | WL_SEAT_CAPABILITY_POINTER);
} }
Monitor * Monitor *
@@ -3015,31 +3191,22 @@ void
xytonode(double x, double y, struct wlr_surface **psurface, xytonode(double x, double y, struct wlr_surface **psurface,
Client **pc, LayerSurface **pl, double *nx, double *ny) Client **pc, LayerSurface **pl, double *nx, double *ny)
{ {
struct wlr_scene_node *node, *pnode; struct wlr_scene_node *node;
struct wlr_surface *surface = NULL; struct wlr_surface *surface = NULL;
Client *c = NULL;
LayerSurface *l = NULL;
int layer; int layer;
for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) { for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) {
if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny))) if (layer == LyrIMPopup ||
!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny)))
continue; continue;
if (node->type == WLR_SCENE_NODE_BUFFER) if (node->type == WLR_SCENE_NODE_BUFFER)
surface = wlr_scene_surface_try_from_buffer( surface = wlr_scene_surface_try_from_buffer(
wlr_scene_buffer_from_node(node))->surface; wlr_scene_buffer_from_node(node))->surface;
/* Walk the tree to find a node that knows the client */
for (pnode = node; pnode && !c; pnode = &pnode->parent->node)
c = pnode->data;
if (c && c->type == LayerShell) {
c = NULL;
l = pnode->data;
}
} }
if (psurface) *psurface = surface; if (psurface) *psurface = surface;
if (pc) *pc = c; toplevel_from_wlr_surface(surface, pc, pl);
if (pl) *pl = l;
} }
void void
@@ -3176,9 +3343,7 @@ xwaylandready(struct wl_listener *listener, void *data)
/* Set the default XWayland cursor to match the rest of dwl. */ /* Set the default XWayland cursor to match the rest of dwl. */
if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "default", 1))) if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "default", 1)))
wlr_xwayland_set_cursor(xwayland, wlr_xwayland_set_cursor(xwayland, wlr_xcursor_image_get_buffer(xcursor->images[0]),
xcursor->images[0]->buffer, xcursor->images[0]->width * 4,
xcursor->images[0]->width, xcursor->images[0]->height,
xcursor->images[0]->hotspot_x, xcursor->images[0]->hotspot_y); xcursor->images[0]->hotspot_x, xcursor->images[0]->hotspot_y);
} }
#endif #endif
+639
View File
@@ -0,0 +1,639 @@
#include <wlr/types/wlr_input_method_v2.h>
#include <wlr/types/wlr_text_input_v3.h>
struct input_method_relay {
struct wl_list text_inputs; /* struct text_input.link */
struct wlr_input_method_v2 *input_method;
struct wlr_surface *focused_surface;
struct text_input *active_text_input;
struct wl_list popups; /* input_method_popup.link */
struct wlr_scene_tree *popup_tree;
struct wl_listener new_text_input;
struct wl_listener new_input_method;
struct wl_listener input_method_commit;
struct wl_listener input_method_grab_keyboard;
struct wl_listener input_method_destroy;
struct wl_listener input_method_new_popup_surface;
struct wl_listener keyboard_grab_destroy;
struct wl_listener focused_surface_destroy;
};
struct input_method_popup {
struct wlr_input_popup_surface_v2 *popup_surface;
struct wlr_scene_tree *tree;
struct wlr_scene_tree *scene_surface;
struct input_method_relay *relay;
struct wl_list link; /* input_method_relay.popups */
struct wl_listener destroy;
struct wl_listener commit;
};
struct text_input {
struct input_method_relay *relay;
struct wlr_text_input_v3 *input;
struct wl_list link;
struct wl_listener enable;
struct wl_listener commit;
struct wl_listener disable;
struct wl_listener destroy;
};
struct wlr_input_method_manager_v2 *input_method_manager;
struct wlr_text_input_manager_v3 *text_input_manager;
struct input_method_relay *input_method_relay;
/* functions used in dwl.c */
static int input_method_keyboard_grab_forward_key(
KeyboardGroup *keyboard, struct wlr_keyboard_key_event *event);
static int input_method_keyboard_grab_forward_modifiers(KeyboardGroup *keyboard);
static struct input_method_relay *input_method_relay_create(void);
static void input_method_relay_finish(struct input_method_relay *relay);
static void input_method_relay_set_focus(struct input_method_relay *relay,
struct wlr_surface *surface);
/* internal functions */
static struct text_input *get_active_text_input(struct input_method_relay *relay);
static struct wlr_input_method_keyboard_grab_v2 *get_keyboard_grab(KeyboardGroup *keyboard);
static int keyboard_is_emulated_by_input_method(struct wlr_keyboard *keyboard,
struct wlr_input_method_v2 *input_method);
static void handle_focused_surface_destroy(struct wl_listener *listener, void *data);
static void handle_input_method_commit(struct wl_listener *listener, void *data);
static void handle_input_method_destroy(struct wl_listener *listener, void *data);
static void handle_input_method_grab_keyboard(struct wl_listener *listener, void *data);
static void handle_input_method_new_popup_surface(struct wl_listener *listener, void *data);
static void handle_keyboard_grab_destroy(struct wl_listener *listener, void *data);
static void handle_new_input_method(struct wl_listener *listener, void *data);
static void handle_new_text_input(struct wl_listener *listener, void *data);
static void handle_popup_surface_commit(struct wl_listener *listener, void *data);
static void handle_popup_surface_destroy(struct wl_listener *listener, void *data);
static void handle_text_input_commit(struct wl_listener *listener, void *data);
static void handle_text_input_destroy(struct wl_listener *listener, void *data);
static void handle_text_input_disable(struct wl_listener *listener, void *data);
static void handle_text_input_enable(struct wl_listener *listener, void *data);
static Monitor *output_from_wlr_output(struct wlr_output *wlr_output);
static Monitor *output_nearest_to(int lx, int ly);
static void send_state_to_input_method(struct input_method_relay *relay);
static void update_active_text_input(struct input_method_relay *relay);
static void update_popup_position(struct input_method_popup *popup);
static void update_popups_position(struct input_method_relay *relay);
static void update_text_inputs_focused_surface(struct input_method_relay *relay);
Monitor *
output_from_wlr_output(struct wlr_output *wlr_output)
{
Monitor *m = NULL;
wl_list_for_each(m, &mons, link) {
if (m->wlr_output == wlr_output)
return m;
}
return NULL;
}
Monitor *
output_nearest_to(int lx, int ly)
{
double closest_x, closest_y;
wlr_output_layout_closest_point(output_layout, NULL, lx, ly, &closest_x,
&closest_y);
return output_from_wlr_output(
wlr_output_layout_output_at(output_layout, closest_x, closest_y));
}
int
keyboard_is_emulated_by_input_method(struct wlr_keyboard *keyboard,
struct wlr_input_method_v2 *input_method)
{
struct wlr_virtual_keyboard_v1 *virtual_keyboard;
if (!keyboard || !input_method)
return 0;
virtual_keyboard = wlr_input_device_get_virtual_keyboard(&keyboard->base);
return virtual_keyboard &&
wl_resource_get_client(virtual_keyboard->resource) ==
wl_resource_get_client(input_method->resource);
}
struct wlr_input_method_keyboard_grab_v2 *
get_keyboard_grab(KeyboardGroup *keyboard)
{
struct wlr_input_method_v2 *input_method = input_method_relay->input_method;
if (!input_method || !input_method->keyboard_grab || keyboard != kb_group ||
keyboard_is_emulated_by_input_method(keyboard->virtual_keyboard,
input_method))
return NULL;
return input_method->keyboard_grab;
}
int
input_method_keyboard_grab_forward_modifiers(KeyboardGroup *keyboard)
{
struct wlr_input_method_keyboard_grab_v2 *keyboard_grab =
get_keyboard_grab(keyboard);
struct wlr_keyboard_modifiers *modifiers =
&keyboard->wlr_group->keyboard.modifiers;
if (keyboard_grab) {
wlr_input_method_keyboard_grab_v2_set_keyboard(
keyboard_grab, &keyboard->wlr_group->keyboard);
wlr_input_method_keyboard_grab_v2_send_modifiers(keyboard_grab, modifiers);
return 1;
} else {
return 0;
}
}
int
input_method_keyboard_grab_forward_key(
KeyboardGroup *keyboard, struct wlr_keyboard_key_event *event)
{
struct wlr_input_method_keyboard_grab_v2 *keyboard_grab =
get_keyboard_grab(keyboard);
if (keyboard_grab) {
wlr_input_method_keyboard_grab_v2_set_keyboard(
keyboard_grab, &keyboard->wlr_group->keyboard);
wlr_input_method_keyboard_grab_v2_send_key(keyboard_grab, event->time_msec,
event->keycode, event->state);
return 1;
} else {
return 0;
}
}
struct text_input *
get_active_text_input(struct input_method_relay *relay)
{
struct text_input *text_input;
if (!relay->input_method)
return NULL;
wl_list_for_each(text_input, &relay->text_inputs, link) {
if (text_input->input->focused_surface &&
text_input->input->current_enabled)
return text_input;
}
return NULL;
}
void
update_active_text_input(struct input_method_relay *relay)
{
struct text_input *active_text_input = get_active_text_input(relay);
if (relay->input_method && relay->active_text_input != active_text_input) {
if (active_text_input)
wlr_input_method_v2_send_activate(relay->input_method);
else
wlr_input_method_v2_send_deactivate(relay->input_method);
wlr_input_method_v2_send_done(relay->input_method);
}
relay->active_text_input = active_text_input;
}
void
update_text_inputs_focused_surface(struct input_method_relay *relay)
{
struct text_input *text_input;
wl_list_for_each(text_input, &relay->text_inputs, link) {
struct wlr_text_input_v3 *input = text_input->input;
struct wlr_surface *new_focused_surface;
if (relay->input_method && relay->focused_surface &&
wl_resource_get_client(input->resource) ==
wl_resource_get_client(relay->focused_surface->resource)) {
new_focused_surface = relay->focused_surface;
} else {
new_focused_surface = NULL;
}
if (input->focused_surface == new_focused_surface)
continue;
if (input->focused_surface)
wlr_text_input_v3_send_leave(input);
if (new_focused_surface)
wlr_text_input_v3_send_enter(input, new_focused_surface);
}
}
void
update_popup_position(struct input_method_popup *popup)
{
struct input_method_relay *relay = popup->relay;
struct text_input *text_input = relay->active_text_input;
struct wlr_box cursor_rect;
struct wlr_xdg_surface *xdg_surface;
struct wlr_layer_surface_v1 *layer_surface;
struct wlr_scene_tree *tree;
int lx, ly;
Monitor *output;
struct wlr_box output_box;
struct wlr_xdg_positioner_rules positioner_rules;
struct wlr_box popup_box;
if (!text_input || !relay->focused_surface ||
!popup->popup_surface->surface->mapped)
return;
xdg_surface = wlr_xdg_surface_try_from_wlr_surface(relay->focused_surface);
layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(relay->focused_surface);
if ((text_input->input->current.features &
WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE) &&
(xdg_surface || layer_surface)) {
cursor_rect = text_input->input->current.cursor_rectangle;
tree = relay->focused_surface->data;
wlr_scene_node_coords(&tree->node, &lx, &ly);
cursor_rect.x += lx;
cursor_rect.y += ly;
if (xdg_surface) {
cursor_rect.x -= xdg_surface->geometry.x;
cursor_rect.y -= xdg_surface->geometry.y;
}
} else {
cursor_rect = (struct wlr_box){0};
}
output = output_nearest_to(cursor_rect.x, cursor_rect.y);
if (!output || !output->wlr_output->enabled)
return;
wlr_output_layout_get_box(output_layout, output->wlr_output, &output_box);
positioner_rules = (struct wlr_xdg_positioner_rules) {
.anchor_rect = cursor_rect,
.anchor = XDG_POSITIONER_ANCHOR_BOTTOM_LEFT,
.gravity = XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT,
.size =
{
.width = popup->popup_surface->surface->current.width,
.height = popup->popup_surface->surface->current.height,
},
.constraint_adjustment = XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y |
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X,
};
wlr_xdg_positioner_rules_get_geometry(&positioner_rules, &popup_box);
wlr_xdg_positioner_rules_unconstrain_box(&positioner_rules, &output_box, &popup_box);
wlr_scene_node_set_position(&popup->tree->node, popup_box.x, popup_box.y);
wlr_scene_node_raise_to_top(&relay->popup_tree->node);
wlr_input_popup_surface_v2_send_text_input_rectangle(
popup->popup_surface, &(struct wlr_box){
.x = cursor_rect.x - popup_box.x,
.y = cursor_rect.y - popup_box.y,
.width = cursor_rect.width,
.height = cursor_rect.height,
});
}
void
update_popups_position(struct input_method_relay *relay)
{
struct input_method_popup *popup;
wl_list_for_each(popup, &relay->popups, link) {
update_popup_position(popup);
}
}
void
handle_input_method_commit(struct wl_listener *listener,
void *data)
{
struct input_method_relay *relay =
wl_container_of(listener, relay, input_method_commit);
struct wlr_input_method_v2 *input_method = relay->input_method;
struct text_input *text_input = relay->active_text_input;
if (!text_input)
return;
if (input_method->current.preedit.text) {
wlr_text_input_v3_send_preedit_string(text_input->input,
input_method->current.preedit.text,
input_method->current.preedit.cursor_begin,
input_method->current.preedit.cursor_end);
}
if (input_method->current.commit_text) {
wlr_text_input_v3_send_commit_string(text_input->input,
input_method->current.commit_text);
}
if (input_method->current.delete.before_length ||
input_method->current.delete.after_length) {
wlr_text_input_v3_send_delete_surrounding_text(text_input->input,
input_method->current.delete.before_length,
input_method->current.delete.after_length);
}
wlr_text_input_v3_send_done(text_input->input);
}
void
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 =
relay->input_method->keyboard_grab;
wl_list_remove(&relay->keyboard_grab_destroy.link);
if (keyboard_grab->keyboard) {
/* Send modifier state to original client */
wlr_seat_keyboard_notify_modifiers(keyboard_grab->input_method->seat,
&keyboard_grab->keyboard->modifiers);
}
}
void
handle_input_method_grab_keyboard(struct wl_listener *listener,
void *data)
{
struct input_method_relay *relay =
wl_container_of(listener, relay, input_method_grab_keyboard);
struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data;
struct wlr_keyboard *active_keyboard = &kb_group->wlr_group->keyboard;
if (!keyboard_is_emulated_by_input_method(active_keyboard,
relay->input_method)) {
/* Send modifier state to grab */
wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab,
active_keyboard);
}
relay->keyboard_grab_destroy.notify = handle_keyboard_grab_destroy;
wl_signal_add(&keyboard_grab->events.destroy, &relay->keyboard_grab_destroy);
}
void
handle_input_method_destroy(struct wl_listener *listener,
void *data)
{
struct input_method_relay *relay =
wl_container_of(listener, relay, input_method_destroy);
wl_list_remove(&relay->input_method_commit.link);
wl_list_remove(&relay->input_method_grab_keyboard.link);
wl_list_remove(&relay->input_method_new_popup_surface.link);
wl_list_remove(&relay->input_method_destroy.link);
relay->input_method = NULL;
update_text_inputs_focused_surface(relay);
update_active_text_input(relay);
}
void
handle_popup_surface_destroy(struct wl_listener *listener,
void *data)
{
struct input_method_popup *popup = wl_container_of(listener, popup, destroy);
wlr_scene_node_destroy(&popup->tree->node);
wl_list_remove(&popup->destroy.link);
wl_list_remove(&popup->commit.link);
wl_list_remove(&popup->link);
free(popup);
}
void
handle_popup_surface_commit(struct wl_listener *listener,
void *data)
{
struct input_method_popup *popup = wl_container_of(listener, popup, commit);
update_popup_position(popup);
}
void
handle_input_method_new_popup_surface(struct wl_listener *listener,
void *data)
{
struct input_method_relay *relay =
wl_container_of(listener, relay, input_method_new_popup_surface);
struct input_method_popup *popup = ecalloc(1, sizeof(*popup));
popup->popup_surface = data;
popup->relay = relay;
popup->destroy.notify = handle_popup_surface_destroy;
wl_signal_add(&popup->popup_surface->events.destroy, &popup->destroy);
popup->commit.notify = handle_popup_surface_commit;
wl_signal_add(&popup->popup_surface->surface->events.commit, &popup->commit);
popup->tree = wlr_scene_tree_create(layers[LyrIMPopup]);
popup->scene_surface = wlr_scene_subsurface_tree_create(
popup->tree, popup->popup_surface->surface);
popup->scene_surface->node.data = popup;
wl_list_insert(&relay->popups, &popup->link);
update_popup_position(popup);
}
void
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 (seat != input_method->seat)
return;
if (relay->input_method) {
wlr_input_method_v2_send_unavailable(input_method);
return;
}
relay->input_method = input_method;
relay->input_method_commit.notify = handle_input_method_commit;
wl_signal_add(&relay->input_method->events.commit,
&relay->input_method_commit);
relay->input_method_grab_keyboard.notify = handle_input_method_grab_keyboard;
wl_signal_add(&relay->input_method->events.grab_keyboard,
&relay->input_method_grab_keyboard);
relay->input_method_destroy.notify = handle_input_method_destroy;
wl_signal_add(&relay->input_method->events.destroy,
&relay->input_method_destroy);
relay->input_method_new_popup_surface.notify =
handle_input_method_new_popup_surface;
wl_signal_add(&relay->input_method->events.new_popup_surface,
&relay->input_method_new_popup_surface);
update_text_inputs_focused_surface(relay);
update_active_text_input(relay);
}
void
send_state_to_input_method(struct input_method_relay *relay)
{
struct wlr_input_method_v2 *input_method = relay->input_method;
struct wlr_text_input_v3 *input = relay->active_text_input->input;
if (input->active_features & WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT) {
wlr_input_method_v2_send_surrounding_text(input_method,
input->current.surrounding.text,
input->current.surrounding.cursor,
input->current.surrounding.anchor);
}
wlr_input_method_v2_send_text_change_cause(input_method,
input->current.text_change_cause);
if (input->active_features & WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE) {
wlr_input_method_v2_send_content_type(input_method,
input->current.content_type.hint,
input->current.content_type.purpose);
}
wlr_input_method_v2_send_done(input_method);
}
void
handle_text_input_enable(struct wl_listener *listener, void *data)
{
struct text_input *text_input = wl_container_of(listener, text_input, enable);
struct input_method_relay *relay = text_input->relay;
update_active_text_input(relay);
if (relay->active_text_input == text_input) {
update_popups_position(relay);
send_state_to_input_method(relay);
}
wlr_text_input_v3_send_done(text_input->input);
}
void
handle_text_input_disable(struct wl_listener *listener,
void *data)
{
struct text_input *text_input =
wl_container_of(listener, text_input, disable);
update_active_text_input(text_input->relay);
}
void
handle_text_input_commit(struct wl_listener *listener, void *data)
{
struct text_input *text_input = wl_container_of(listener, text_input, commit);
struct input_method_relay *relay = text_input->relay;
if (relay->active_text_input == text_input) {
update_popups_position(relay);
send_state_to_input_method(relay);
}
}
void
handle_text_input_destroy(struct wl_listener *listener,
void *data)
{
struct text_input *text_input =
wl_container_of(listener, text_input, destroy);
wl_list_remove(&text_input->enable.link);
wl_list_remove(&text_input->disable.link);
wl_list_remove(&text_input->commit.link);
wl_list_remove(&text_input->destroy.link);
wl_list_remove(&text_input->link);
update_active_text_input(text_input->relay);
free(text_input);
}
void
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;
struct text_input *text_input;
if (seat != wlr_text_input->seat)
return;
text_input = ecalloc(1, sizeof(*text_input));
text_input->input = wlr_text_input;
text_input->relay = relay;
wl_list_insert(&relay->text_inputs, &text_input->link);
text_input->enable.notify = handle_text_input_enable;
wl_signal_add(&text_input->input->events.enable, &text_input->enable);
text_input->disable.notify = handle_text_input_disable;
wl_signal_add(&text_input->input->events.disable, &text_input->disable);
text_input->commit.notify = handle_text_input_commit;
wl_signal_add(&text_input->input->events.commit, &text_input->commit);
text_input->destroy.notify = handle_text_input_destroy;
wl_signal_add(&text_input->input->events.destroy, &text_input->destroy);
update_text_inputs_focused_surface(relay);
}
void
handle_focused_surface_destroy(struct wl_listener *listener,
void *data)
{
struct input_method_relay *relay =
wl_container_of(listener, relay, focused_surface_destroy);
input_method_relay_set_focus(relay, NULL);
}
struct input_method_relay *
input_method_relay_create()
{
struct input_method_relay *relay = ecalloc(1, sizeof(*relay));
wl_list_init(&relay->text_inputs);
wl_list_init(&relay->popups);
relay->popup_tree = wlr_scene_tree_create(&scene->tree);
relay->new_text_input.notify = handle_new_text_input;
wl_signal_add(&text_input_manager->events.new_text_input, &relay->new_text_input);
relay->new_input_method.notify = handle_new_input_method;
wl_signal_add(&input_method_manager->events.new_input_method,
&relay->new_input_method);
relay->focused_surface_destroy.notify = handle_focused_surface_destroy;
return relay;
}
void
input_method_relay_finish(struct input_method_relay *relay)
{
wl_list_remove(&relay->new_text_input.link);
wl_list_remove(&relay->new_input_method.link);
free(relay);
}
void
input_method_relay_set_focus(struct input_method_relay *relay,
struct wlr_surface *surface)
{
if (relay->focused_surface == surface)
return;
if (relay->focused_surface)
wl_list_remove(&relay->focused_surface_destroy.link);
relay->focused_surface = surface;
if (surface)
wl_signal_add(&surface->events.destroy, &relay->focused_surface_destroy);
update_text_inputs_focused_surface(relay);
update_active_text_input(relay);
}
-51
View File
@@ -1,51 +0,0 @@
/* See LICENSE.dwm file for copyright and license details. */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include "util.h"
void
die(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
fputc(' ', stderr);
perror(NULL);
} else {
fputc('\n', stderr);
}
exit(1);
}
void *
ecalloc(size_t nmemb, size_t size)
{
void *p;
if (!(p = calloc(nmemb, size)))
die("calloc:");
return p;
}
int
fd_set_nonblock(int fd) {
int flags = fcntl(fd, F_GETFL);
if (flags < 0) {
perror("fcntl(F_GETFL):");
return -1;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
perror("fcntl(F_SETFL):");
return -1;
}
return 0;
}
+68 -3
View File
@@ -1,5 +1,70 @@
/* See LICENSE.dwm file for copyright and license details. */ /* See LICENSE.dwm file for copyright and license details. */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <xkbcommon/xkbcommon.h>
void die(const char *fmt, ...); #define MAX(A, B) ((A) > (B) ? (A) : (B))
void *ecalloc(size_t nmemb, size_t size); #define MIN(A, B) ((A) < (B) ? (A) : (B))
int fd_set_nonblock(int fd); #define LENGTH(X) (sizeof X / sizeof X[0])
#define END(A) ((A) + LENGTH(A))
static void die(const char *fmt, ...);
static void *ecalloc(size_t nmemb, size_t size);
static int fd_set_nonblock(int fd);
xkb_keysym_t keymap_get_one_sym_by_level(struct xkb_keymap *keymap,
xkb_keycode_t key, xkb_layout_index_t layout, xkb_level_index_t level);
void
die(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
fputc(' ', stderr);
perror(NULL);
} else {
fputc('\n', stderr);
}
exit(1);
}
void *
ecalloc(size_t nmemb, size_t size)
{
void *p;
if (!(p = calloc(nmemb, size)))
die("calloc:");
return p;
}
int
fd_set_nonblock(int fd) {
int flags = fcntl(fd, F_GETFL);
if (flags < 0) {
perror("fcntl(F_GETFL):");
return -1;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
perror("fcntl(F_SETFL):");
return -1;
}
return 0;
}
xkb_keysym_t
keymap_get_one_sym_by_level(struct xkb_keymap *keymap, xkb_keycode_t key,
xkb_layout_index_t layout, xkb_level_index_t level)
{
const xkb_keysym_t *syms;
int count = xkb_keymap_key_get_syms_by_level(keymap, key, layout, level, &syms);
return count ? syms[0] : XKB_KEY_NoSymbol;
}