mirror of
https://codeberg.org/dwl/dwl.git
synced 2026-09-19 13:52:48 +00:00
Compare commits
29
Commits
0.8
...
5fd19feaff
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fd19feaff | ||
|
|
b1c101515a | ||
|
|
8ad2c920db | ||
|
|
e8bf70d639 | ||
|
|
99cdc9a825 | ||
|
|
7ba335063f | ||
|
|
fb4102b5ae | ||
|
|
721f70ea8b | ||
|
|
0a6dc6d548 | ||
|
|
4c54bd8113 | ||
|
|
2f3fe0671d | ||
|
|
f6800e95ee | ||
|
|
7fb20244c6 | ||
|
|
21be76ac80 | ||
|
|
70c33c015a | ||
|
|
ee788f0149 | ||
|
|
132606e133 | ||
|
|
49ca434514 | ||
|
|
babccfe6dc | ||
|
|
4847f97678 | ||
|
|
04279f28e0 | ||
|
|
f4dfdabd0b | ||
|
|
a2d03cf618 | ||
|
|
cb4cc3ae8f | ||
|
|
9b76ee8e90 | ||
|
|
908a73da82 | ||
|
|
227cdf0128 | ||
|
|
ca4123072d | ||
|
|
a8915224e8 |
@@ -5,7 +5,7 @@ include config.mk
|
||||
|
||||
# flags for compiling
|
||||
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 \
|
||||
-Wno-unused-parameter -Wshadow -Wunused-macros -Werror=strict-prototypes \
|
||||
-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)
|
||||
|
||||
all: dwl
|
||||
dwl: dwl.o util.o
|
||||
$(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@
|
||||
dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \
|
||||
dwl: dwl.c client.h config.h ime.h util.h config.mk cursor-shape-v1-protocol.h \
|
||||
ext-image-copy-capture-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
|
||||
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
|
||||
# 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:
|
||||
$(WAYLAND_SCANNER) enum-header \
|
||||
$(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:
|
||||
$(WAYLAND_SCANNER) enum-header \
|
||||
$(WAYLAND_PROTOCOLS)/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml $@
|
||||
@@ -54,7 +56,7 @@ clean:
|
||||
dist: clean
|
||||
mkdir -p dwl-$(VERSION)
|
||||
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)
|
||||
tar -caf dwl-$(VERSION).tar.gz dwl-$(VERSION)
|
||||
rm -rf dwl-$(VERSION)
|
||||
@@ -73,7 +75,3 @@ install: dwl
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(PREFIX)/bin/dwl $(DESTDIR)$(MANDIR)/man1/dwl.1 \
|
||||
$(DESTDIR)$(DATADIR)/wayland-sessions/dwl.desktop
|
||||
|
||||
.SUFFIXES: .c .o
|
||||
.c.o:
|
||||
$(CC) $(CPPFLAGS) $(DWLCFLAGS) -o $@ -c $<
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
# dwl - dwm for Wayland
|
||||
|
||||
2025-08-16:
|
||||
dwl IS CURRENTLY UN-MAINTAINED.
|
||||
AT THE PRESENT TIME, I (@fauxmight) DO NOT HAVE
|
||||
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].
|
||||
Join us on our [Discord server]
|
||||
Or Matrix: [#dwl-official:matrix.org]
|
||||
Or on our IRC channel: [#dwl on Libera Chat]
|
||||
|
||||
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,
|
||||
@@ -17,7 +10,7 @@ primarily in terms of functionality, and secondarily in terms of
|
||||
philosophy. Like [dwm], dwl is:
|
||||
|
||||
- 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
|
||||
|
||||
## Getting Started:
|
||||
@@ -31,11 +24,13 @@ with each release on the [release] page
|
||||
|
||||
### Development branch [main]
|
||||
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
|
||||
are welcome to use `main`, it is a rocky road. Using `main` requires that the
|
||||
user be willing to chase git commits of wlroots. Testing development pull
|
||||
requests may involve merging unmerged pull requests in [wlroots]' git repository
|
||||
and/or git commits of wayland.
|
||||
against the latest release of [wlroots]. PRs should target this branch unless they
|
||||
depend on functionality that is not in the current release of `wlroots`.
|
||||
|
||||
### Preview branch [wlroots-next]
|
||||
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
|
||||
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
|
||||
- Various Wayland protocols
|
||||
- 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)
|
||||
- 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:
|
||||
|
||||
- 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:
|
||||
|
||||
@@ -198,6 +189,10 @@ inspiration, and to the various contributors to the project, including:
|
||||
and for helping to keep the project running
|
||||
- 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
|
||||
[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
|
||||
[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
|
||||
[wlroots-next]: https://codeberg.org/dwl/dwl/src/branch/wlroots-next
|
||||
[release]: https://codeberg.org/dwl/dwl/releases
|
||||
[runit]: http://smarden.org/runit/faq.html#userservices
|
||||
[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
|
||||
[Discord server]: https://discord.gg/jJxZnrGPWN
|
||||
[Wayland]: https://wayland.freedesktop.org/
|
||||
[#dwl-official:matrix.org]: https://matrix.to/#/#dwl-official:matrix.org
|
||||
|
||||
@@ -291,11 +291,18 @@ client_is_unmanaged(Client *c)
|
||||
static inline void
|
||||
client_notify_enter(struct wlr_surface *s, struct wlr_keyboard *kb)
|
||||
{
|
||||
if (kb)
|
||||
wlr_seat_keyboard_notify_enter(seat, s, kb->keycodes,
|
||||
kb->num_keycodes, &kb->modifiers);
|
||||
else
|
||||
uint32_t filtered[WLR_KEYBOARD_KEYS_CAP];
|
||||
size_t size = 0;
|
||||
if (!kb) {
|
||||
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
|
||||
|
||||
@@ -170,3 +170,8 @@ static const Button buttons[] = {
|
||||
{ MODKEY, BTN_MIDDLE, togglefloating, {0} },
|
||||
{ MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
|
||||
};
|
||||
|
||||
static const Axis axes[] = {
|
||||
{ MODKEY, AxisUp, spawn, SHCMD("volume-up_EXAMPLE") },
|
||||
{ MODKEY, AxisDown, spawn, SHCMD("volume-down_EXAMPLE") },
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_VERSION = 0.8-dev
|
||||
_VERSION = 0.9-dev
|
||||
VERSION = `git describe --tags --dirty 2>/dev/null || echo $(_VERSION)`
|
||||
|
||||
PKG_CONFIG = pkg-config
|
||||
@@ -8,8 +8,8 @@ PREFIX = /usr/local
|
||||
MANDIR = $(PREFIX)/share/man
|
||||
DATADIR = $(PREFIX)/share
|
||||
|
||||
WLR_INCS = `$(PKG_CONFIG) --cflags wlroots-0.19`
|
||||
WLR_LIBS = `$(PKG_CONFIG) --libs wlroots-0.19`
|
||||
WLR_INCS = `$(PKG_CONFIG) --cflags wlroots-0.20`
|
||||
WLR_LIBS = `$(PKG_CONFIG) --libs wlroots-0.20`
|
||||
|
||||
# Allow using an alternative wlroots installation
|
||||
# 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 \
|
||||
# -I$(PWD)/wlroots/include
|
||||
# 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 \
|
||||
# -I$(PWD)/wlroots/0.19/include/wlroots-0.19
|
||||
#WLR_LIBS = -Wl,-rpath,$(PWD)/wlroots/0.19/lib64 -L$(PWD)/wlroots/0.19/lib64 -lwlroots-0.19
|
||||
# -I$(PWD)/wlroots/0.20/include/wlroots-0.20
|
||||
#WLR_LIBS = -Wl,-rpath,$(PWD)/wlroots/0.20/lib64 -L$(PWD)/wlroots/0.20/lib64 -lwlroots-0.20
|
||||
|
||||
XWAYLAND =
|
||||
XLIBS =
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
#include <wlr/types/wlr_data_device.h>
|
||||
#include <wlr/types/wlr_drm.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_gamma_control_v1.h>
|
||||
#include <wlr/types/wlr_idle_inhibit_v1.h>
|
||||
@@ -68,15 +72,12 @@
|
||||
#include <xcb/xcb_icccm.h>
|
||||
#endif
|
||||
|
||||
#include "xdg-shell-protocol.h"
|
||||
#include "util.h"
|
||||
|
||||
/* 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 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 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)
|
||||
@@ -84,7 +85,8 @@
|
||||
/* enums */
|
||||
enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */
|
||||
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 {
|
||||
int i;
|
||||
@@ -93,6 +95,13 @@ typedef union {
|
||||
const void *v;
|
||||
} Arg;
|
||||
|
||||
typedef struct {
|
||||
unsigned int mod;
|
||||
unsigned int dir;
|
||||
void (*func)(const Arg *);
|
||||
const Arg arg;
|
||||
} Axis;
|
||||
|
||||
typedef struct {
|
||||
unsigned int mod;
|
||||
unsigned int button;
|
||||
@@ -119,6 +128,13 @@ typedef struct {
|
||||
struct wlr_xwayland_surface *xwayland;
|
||||
} surface;
|
||||
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 map;
|
||||
struct wl_listener maximize;
|
||||
@@ -150,6 +166,7 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
struct wlr_keyboard_group *wlr_group;
|
||||
struct wlr_keyboard *virtual_keyboard;
|
||||
|
||||
int nsyms;
|
||||
const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */
|
||||
@@ -248,6 +265,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list,
|
||||
static void arrangelayers(Monitor *m);
|
||||
static void axisnotify(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 checkidleinhibitor(struct wlr_surface *exclude);
|
||||
static void cleanup(void);
|
||||
@@ -291,7 +309,7 @@ static void gpureset(struct wl_listener *listener, void *data);
|
||||
static void handlesig(int signo);
|
||||
static void incnmaster(const Arg *arg);
|
||||
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 keypressmod(struct wl_listener *listener, void *data);
|
||||
static int keyrepeat(void *data);
|
||||
@@ -308,7 +326,7 @@ static void moveresize(const Arg *arg);
|
||||
static void outputmgrapply(struct wl_listener *listener, void *data);
|
||||
static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test);
|
||||
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);
|
||||
static void printstatus(void);
|
||||
static void powermgrsetmode(struct wl_listener *listener, void *data);
|
||||
@@ -382,6 +400,8 @@ static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr;
|
||||
static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr;
|
||||
static struct wlr_cursor_shape_manager_v1 *cursor_shape_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_relative_pointer_manager_v1 *relative_pointer_mgr;
|
||||
@@ -405,6 +425,7 @@ static struct wlr_output_layout *output_layout;
|
||||
static struct wlr_box sgeom;
|
||||
static struct wl_list mons;
|
||||
static Monitor *selmon;
|
||||
static int consumed[KEY_MAX + 1];
|
||||
|
||||
/* global event handlers */
|
||||
static struct wl_listener cursor_axis = {.notify = axisnotify};
|
||||
@@ -435,6 +456,7 @@ static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape};
|
||||
static struct wl_listener request_start_drag = {.notify = requeststartdrag};
|
||||
static struct wl_listener start_drag = {.notify = startdrag};
|
||||
static struct wl_listener new_session_lock = {.notify = locksession};
|
||||
static struct wl_listener new_foreign_toplevel_capture_request = {.notify = capturerequest};
|
||||
|
||||
#ifdef XWAYLAND
|
||||
static void activatex11(struct wl_listener *listener, void *data);
|
||||
@@ -455,6 +477,8 @@ static struct wlr_xwayland *xwayland;
|
||||
/* attempt to encapsulate suck into one file */
|
||||
#include "client.h"
|
||||
|
||||
#include "ime.h"
|
||||
|
||||
/* function implementations */
|
||||
void
|
||||
applybounds(Client *c, struct wlr_box *bbox)
|
||||
@@ -585,14 +609,17 @@ arrangelayers(Monitor *m)
|
||||
arrange(m);
|
||||
}
|
||||
|
||||
/* Arrange non-exlusive surfaces from top->bottom */
|
||||
/* Arrange non-exclusive surfaces from top->bottom */
|
||||
for (i = 3; i >= 0; i--)
|
||||
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++) {
|
||||
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;
|
||||
/* Deactivate the focused client. */
|
||||
focusclient(NULL, 0);
|
||||
@@ -609,9 +636,26 @@ axisnotify(struct wl_listener *listener, void *data)
|
||||
/* This event is forwarded by the cursor when a pointer emits an axis event,
|
||||
* for example when you move the scroll wheel. */
|
||||
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);
|
||||
/* 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. */
|
||||
wlr_seat_pointer_notify_axis(seat,
|
||||
event->time_msec, event->orientation, event->delta,
|
||||
@@ -624,7 +668,8 @@ buttonpress(struct wl_listener *listener, void *data)
|
||||
struct wlr_pointer_button_event *event = data;
|
||||
struct wlr_keyboard *keyboard;
|
||||
uint32_t mods;
|
||||
Client *c;
|
||||
Client *c = NULL;
|
||||
LayerSurface *l = NULL;
|
||||
const Button *b;
|
||||
|
||||
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
|
||||
@@ -636,10 +681,15 @@ buttonpress(struct wl_listener *listener, void *data)
|
||||
if (locked)
|
||||
break;
|
||||
|
||||
/* Change focus if the button was _pressed_ over a client */
|
||||
xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL);
|
||||
if (c && (!client_is_unmanaged(c) || client_wants_focus(c)))
|
||||
/* Change focus if the button was _pressed_ over a client
|
||||
or a layer surface with on-demand keyboard interactivity */
|
||||
xytonode(cursor->x, cursor->y, NULL, &c, &l, NULL, NULL);
|
||||
if (c && (!client_is_unmanaged(c) || client_wants_focus(c))) {
|
||||
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);
|
||||
mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
|
||||
@@ -653,10 +703,11 @@ buttonpress(struct wl_listener *listener, void *data)
|
||||
break;
|
||||
case WL_POINTER_BUTTON_STATE_RELEASED:
|
||||
/* 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) {
|
||||
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
|
||||
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 */
|
||||
selmon = xytomon(cursor->x, cursor->y);
|
||||
setmon(grabc, selmon, 0);
|
||||
@@ -672,6 +723,23 @@ buttonpress(struct wl_listener *listener, void *data)
|
||||
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
|
||||
chvt(const Arg *arg)
|
||||
{
|
||||
@@ -713,6 +781,8 @@ cleanup(void)
|
||||
|
||||
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.
|
||||
* Destroy it until it's fixed on the wlroots side */
|
||||
wlr_backend_destroy(backend);
|
||||
@@ -782,6 +852,7 @@ cleanuplisteners(void)
|
||||
wl_list_remove(&request_start_drag.link);
|
||||
wl_list_remove(&start_drag.link);
|
||||
wl_list_remove(&new_session_lock.link);
|
||||
wl_list_remove(&new_foreign_toplevel_capture_request.link);
|
||||
#ifdef XWAYLAND
|
||||
wl_list_remove(&new_xwayland_surface.link);
|
||||
wl_list_remove(&xwayland_ready.link);
|
||||
@@ -837,6 +908,11 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data)
|
||||
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)
|
||||
return;
|
||||
l->mapped = layer_surface->surface->mapped;
|
||||
@@ -1241,6 +1317,7 @@ void
|
||||
destroydecoration(struct wl_listener *listener, void *data)
|
||||
{
|
||||
Client *c = wl_container_of(listener, c, destroy_decoration);
|
||||
c->decoration = NULL;
|
||||
|
||||
wl_list_remove(&c->destroy_decoration.link);
|
||||
wl_list_remove(&c->set_decoration_mode.link);
|
||||
@@ -1346,6 +1423,10 @@ destroynotify(struct wl_listener *listener, void *data)
|
||||
wl_list_remove(&c->map.link);
|
||||
wl_list_remove(&c->unmap.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);
|
||||
}
|
||||
@@ -1393,7 +1474,8 @@ dirtomon(enum wlr_direction dir)
|
||||
dir, selmon->wlr_output, selmon->m.x, selmon->m.y)))
|
||||
return next->data;
|
||||
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)))
|
||||
return next->data;
|
||||
return selmon;
|
||||
@@ -1443,7 +1525,8 @@ focusclient(Client *c, int lift)
|
||||
* and focus it after the overlay is closed. */
|
||||
if (old_client_type == LayerShell && wlr_scene_node_coords(
|
||||
&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;
|
||||
} else if (old_c && old_c == exclusive_focus && client_wants_focus(old_c)) {
|
||||
return;
|
||||
@@ -1459,6 +1542,7 @@ focusclient(Client *c, int lift)
|
||||
|
||||
if (!c) {
|
||||
/* 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);
|
||||
return;
|
||||
}
|
||||
@@ -1466,6 +1550,8 @@ focusclient(Client *c, int lift)
|
||||
/* Change cursor surface */
|
||||
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 */
|
||||
client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat));
|
||||
|
||||
@@ -1605,7 +1691,7 @@ inputdevice(struct wl_listener *listener, void *data)
|
||||
wlr_seat_set_capabilities(seat, caps);
|
||||
}
|
||||
|
||||
int
|
||||
const Key *
|
||||
keybinding(uint32_t mods, xkb_keysym_t sym)
|
||||
{
|
||||
/*
|
||||
@@ -1618,11 +1704,10 @@ keybinding(uint32_t mods, xkb_keysym_t sym)
|
||||
if (CLEANMASK(mods) == CLEANMASK(k->mod)
|
||||
&& xkb_keysym_to_lower(sym) == xkb_keysym_to_lower(k->keysym)
|
||||
&& k->func) {
|
||||
k->func(&k->arg);
|
||||
return 1;
|
||||
return k;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1648,8 +1733,14 @@ keypress(struct wl_listener *listener, void *data)
|
||||
/* On _press_ if there is no active screen locker,
|
||||
* attempt to process a compositor keybinding. */
|
||||
if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
||||
for (i = 0; i < nsyms; i++)
|
||||
handled = keybinding(mods, syms[i]) || handled;
|
||||
for (i = 0; i < nsyms; i++) {
|
||||
const Key *key = keybinding(mods, syms[i]);
|
||||
if (key) {
|
||||
consumed[event->keycode] = 1;
|
||||
key->func(&key->arg);
|
||||
handled = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (handled && group->wlr_group->keyboard.repeat_info.delay > 0) {
|
||||
@@ -1663,9 +1754,14 @@ keypress(struct wl_listener *listener, void *data)
|
||||
wl_event_source_timer_update(group->key_repeat_source, 0);
|
||||
}
|
||||
|
||||
if (handled)
|
||||
if (handled || input_method_keyboard_grab_forward_key(group, event))
|
||||
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);
|
||||
/* Pass unhandled keycodes along to the client. */
|
||||
wlr_seat_keyboard_notify_key(seat, event->time_msec,
|
||||
@@ -1679,6 +1775,9 @@ keypressmod(struct wl_listener *listener, void *data)
|
||||
* pressed. We simply communicate this to the client. */
|
||||
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);
|
||||
/* Send modifiers to the client. */
|
||||
wlr_seat_keyboard_notify_modifiers(seat,
|
||||
@@ -1696,8 +1795,11 @@ keyrepeat(void *data)
|
||||
wl_event_source_timer_update(group->key_repeat_source,
|
||||
1000 / group->wlr_group->keyboard.repeat_info.rate);
|
||||
|
||||
for (i = 0; i < group->nsyms; i++)
|
||||
keybinding(group->mods, group->keysyms[i]);
|
||||
for (i = 0; i < group->nsyms; i++) {
|
||||
const Key *key = keybinding(group->mods, group->keysyms[i]);
|
||||
if (key)
|
||||
key->func(&key->arg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1743,6 +1845,11 @@ mapnotify(struct wl_listener *listener, void *data)
|
||||
Monitor *m;
|
||||
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 */
|
||||
c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]);
|
||||
/* Enabled later by a call to arrange() */
|
||||
@@ -1782,6 +1889,18 @@ mapnotify(struct wl_listener *listener, void *data)
|
||||
wl_list_insert(&clients, &c->link);
|
||||
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:
|
||||
* we always consider floating, clients that have parent and thus
|
||||
* we set the same tags and monitor as its parent.
|
||||
@@ -1864,20 +1983,20 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
|
||||
{
|
||||
double sx = 0, sy = 0, sx_confined, sy_confined;
|
||||
Client *c = NULL, *w = NULL;
|
||||
LayerSurface *l = NULL;
|
||||
LayerSurface *l = NULL, *focusedl = NULL;
|
||||
struct wlr_surface *surface = NULL;
|
||||
struct wlr_pointer_constraint_v1 *constraint;
|
||||
|
||||
/* 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
|
||||
&& 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;
|
||||
surface = seat->pointer_state.focused_surface;
|
||||
sx = cursor->x - (l ? l->scene->node.x : w->geom.x);
|
||||
sy = cursor->y - (l ? l->scene->node.y : w->geom.y);
|
||||
sx = cursor->x - (focusedl ? focusedl->scene->node.x : w->geom.x);
|
||||
sy = cursor->y - (focusedl ? focusedl->scene->node.y : w->geom.y);
|
||||
}
|
||||
|
||||
/* time is 0 in internal calls meant to restore pointer focus. */
|
||||
@@ -1934,7 +2053,7 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
|
||||
if (!surface && !seat->drag)
|
||||
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
|
||||
|
||||
pointerfocus(c, surface, sx, sy, time);
|
||||
pointerfocus(c, l, surface, sx, sy, time);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2058,14 +2177,19 @@ outputmgrtest(struct wl_listener *listener, void *data)
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct timespec now;
|
||||
|
||||
if (surface != seat->pointer_state.focused_surface &&
|
||||
sloppyfocus && time && c && !client_is_unmanaged(c))
|
||||
focusclient(c, 0);
|
||||
if (surface != seat->pointer_state.focused_surface && sloppyfocus && time) {
|
||||
if (c && (!client_is_unmanaged(c) || client_wants_focus(c))) {
|
||||
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) {
|
||||
@@ -2133,7 +2257,7 @@ powermgrsetmode(struct wl_listener *listener, void *data)
|
||||
if (!m)
|
||||
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_commit_state(m->wlr_output, &state);
|
||||
|
||||
@@ -2153,24 +2277,15 @@ rendermon(struct wl_listener *listener, void *data)
|
||||
/* This function is called every time an output is ready to display a frame,
|
||||
* generally at the output's refresh rate (e.g. 60Hz). */
|
||||
Monitor *m = wl_container_of(listener, m, frame);
|
||||
Client *c;
|
||||
struct wlr_output_state pending = {0};
|
||||
struct timespec now;
|
||||
|
||||
/* Render if no XDG clients have an outstanding resize and are visible on
|
||||
* this monitor. */
|
||||
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;
|
||||
if (!wlr_scene_output_needs_frame(m->scene_output)) {
|
||||
return;
|
||||
}
|
||||
|
||||
wlr_scene_output_commit(m->scene_output, NULL);
|
||||
|
||||
skip:
|
||||
/* Let clients know a frame has been rendered */
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
wlr_scene_output_send_frame_done(m->scene_output, &now);
|
||||
wlr_output_state_finish(&pending);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2455,7 +2570,7 @@ setup(void)
|
||||
wlr_log_init(log_level, NULL);
|
||||
|
||||
/* 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();
|
||||
event_loop = wl_display_get_event_loop(dpy);
|
||||
|
||||
@@ -2517,7 +2632,10 @@ setup(void)
|
||||
wlr_data_device_manager_create(dpy);
|
||||
wlr_export_dmabuf_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_ext_data_control_manager_v1_create(dpy, 1);
|
||||
wlr_primary_selection_v1_device_manager_create(dpy);
|
||||
wlr_viewporter_create(dpy);
|
||||
wlr_single_pixel_buffer_manager_v1_create(dpy);
|
||||
@@ -2559,7 +2677,7 @@ setup(void)
|
||||
wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel);
|
||||
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);
|
||||
|
||||
idle_notifier = wlr_idle_notifier_v1_create(dpy);
|
||||
@@ -2573,6 +2691,12 @@ setup(void)
|
||||
(float [4]){0.1f, 0.1f, 0.1f, 1.0f});
|
||||
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 */
|
||||
wlr_server_decoration_manager_set_default_mode(
|
||||
wlr_server_decoration_manager_create(dpy),
|
||||
@@ -2646,6 +2770,11 @@ setup(void)
|
||||
wl_signal_add(&output_mgr->events.apply, &output_mgr_apply);
|
||||
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,
|
||||
* e.g when running in the x11 backend or the wayland backend and the
|
||||
* compositor has Xwayland support */
|
||||
@@ -2831,7 +2960,20 @@ unmapnotify(struct wl_listener *listener, void *data)
|
||||
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);
|
||||
client_surface(c)->data = NULL;
|
||||
printstatus();
|
||||
motionnotify(0, NULL, 0, 0, 0, 0);
|
||||
}
|
||||
@@ -2854,10 +2996,13 @@ updatemons(struct wl_listener *listener, void *data)
|
||||
|
||||
/* First remove from the layout the disabled monitors */
|
||||
wl_list_for_each(m, &mons, link) {
|
||||
if (m->wlr_output->enabled || m->asleep)
|
||||
if (m->wlr_output->enabled)
|
||||
continue;
|
||||
config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output);
|
||||
config_head->state.enabled = 0;
|
||||
if (m->asleep)
|
||||
continue;
|
||||
|
||||
/* Remove this output from the layout to avoid cursor enter inside it */
|
||||
wlr_output_layout_remove(output_layout, m->wlr_output);
|
||||
closemon(m);
|
||||
@@ -2948,6 +3093,15 @@ updatetitle(struct wl_listener *listener, void *data)
|
||||
Client *c = wl_container_of(listener, c, set_title);
|
||||
if (c == focustop(c->mon))
|
||||
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
|
||||
@@ -2985,12 +3139,14 @@ virtualkeyboard(struct wl_listener *listener, void *data)
|
||||
struct wlr_virtual_keyboard_v1 *kb = data;
|
||||
/* virtual keyboards shouldn't share keyboard group */
|
||||
KeyboardGroup *group = createkeyboardgroup();
|
||||
group->virtual_keyboard = &kb->keyboard;
|
||||
/* Set the keymap to match the group keymap */
|
||||
wlr_keyboard_set_keymap(&kb->keyboard, group->wlr_group->keyboard.keymap);
|
||||
LISTEN(&kb->keyboard.base.events.destroy, &group->destroy, destroykeyboardgroup);
|
||||
|
||||
/* Add the new keyboard to the group */
|
||||
wlr_keyboard_group_add_keyboard(group->wlr_group, &kb->keyboard);
|
||||
wlr_seat_set_capabilities(seat, seat->capabilities | WL_SEAT_CAPABILITY_KEYBOARD);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -3002,6 +3158,7 @@ virtualpointer(struct wl_listener *listener, void *data)
|
||||
wlr_cursor_attach_input_device(cursor, device);
|
||||
if (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 *
|
||||
@@ -3015,31 +3172,22 @@ void
|
||||
xytonode(double x, double y, struct wlr_surface **psurface,
|
||||
Client **pc, LayerSurface **pl, double *nx, double *ny)
|
||||
{
|
||||
struct wlr_scene_node *node, *pnode;
|
||||
struct wlr_scene_node *node;
|
||||
struct wlr_surface *surface = NULL;
|
||||
Client *c = NULL;
|
||||
LayerSurface *l = NULL;
|
||||
int 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;
|
||||
|
||||
if (node->type == WLR_SCENE_NODE_BUFFER)
|
||||
surface = wlr_scene_surface_try_from_buffer(
|
||||
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 (pc) *pc = c;
|
||||
if (pl) *pl = l;
|
||||
toplevel_from_wlr_surface(surface, pc, pl);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -3176,9 +3324,7 @@ xwaylandready(struct wl_listener *listener, void *data)
|
||||
|
||||
/* Set the default XWayland cursor to match the rest of dwl. */
|
||||
if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "default", 1)))
|
||||
wlr_xwayland_set_cursor(xwayland,
|
||||
xcursor->images[0]->buffer, xcursor->images[0]->width * 4,
|
||||
xcursor->images[0]->width, xcursor->images[0]->height,
|
||||
wlr_xwayland_set_cursor(xwayland, wlr_xcursor_image_get_buffer(xcursor->images[0]),
|
||||
xcursor->images[0]->hotspot_x, xcursor->images[0]->hotspot_y);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,5 +1,58 @@
|
||||
/* See LICENSE.dwm file for copyright and license details. */
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
void die(const char *fmt, ...);
|
||||
void *ecalloc(size_t nmemb, size_t size);
|
||||
int fd_set_nonblock(int fd);
|
||||
#define MAX(A, B) ((A) > (B) ? (A) : (B))
|
||||
#define MIN(A, B) ((A) < (B) ? (A) : (B))
|
||||
#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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user