From cb86dd01f1f46fbbbfa2f93b436f75c31e080ca4 Mon Sep 17 00:00:00 2001 From: korei999 Date: Sun, 31 Dec 2023 17:48:27 +0200 Subject: [PATCH] updates --- Makefile | 27 +- client.h | 7 +- config.def.h | 57 ++-- dwl.c | 866 ++++++++++++++++++++++----------------------------- 4 files changed, 413 insertions(+), 544 deletions(-) diff --git a/Makefile b/Makefile index 8b3bf61..5b531f4 100644 --- a/Makefile +++ b/Makefile @@ -5,21 +5,22 @@ include config.mk # flags for compiling DWLCPPFLAGS = -I. -DWLR_USE_UNSTABLE -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XWAYLAND) -DWLDEVCFLAGS = -g3 -pedantic -Wall -Wextra -Wdeclaration-after-statement -Wno-unused-parameter -Wno-sign-compare -Wshadow -Wunused-macros\ +DWLDEVCFLAGS = -g -pedantic -Wall -Wextra -Wdeclaration-after-statement -Wno-unused-parameter -Wno-sign-compare -Wshadow -Wunused-macros\ -Werror=strict-prototypes -Werror=implicit -Werror=return-type -Werror=incompatible-pointer-types # CFLAGS / LDFLAGS CC = gcc -flto=auto CFLAGS = -O2 -march=native -PKGS = wlroots wayland-server xkbcommon libinput pixman-1 $(XLIBS) +PKGS = wlroots wayland-server xkbcommon libinput $(XLIBS) DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS) -LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` -lm $(LIBS) +LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(LIBS) all: dwl dwl: dwl.o util.o dwl-ipc-unstable-v2-protocol.o $(CC) dwl.o util.o dwl-ipc-unstable-v2-protocol.o $(LDLIBS) $(LDFLAGS) $(DWLCFLAGS) -o $@ -dwl.o: dwl.c config.mk config.def.h client.h cursor-shape-v1-protocol.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h wlr-output-power-management-unstable-v1-protocol.h dwl-ipc-unstable-v2-protocol.h pointer-constraints-unstable-v1-protocol.h +dwl.o: dwl.c config.mk config.def.h client.h cursor-shape-v1-protocol.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h dwl-ipc-unstable-v2-protocol.h wlr-output-power-management-unstable-v1-protocol.h pointer-constraints-unstable-v1-protocol.h util.o: util.c util.h + dwl-ipc-unstable-v2-protocol.o: dwl-ipc-unstable-v2-protocol.h # wayland-scanner is a tool which generates C headers and rigging for Wayland @@ -28,27 +29,27 @@ dwl-ipc-unstable-v2-protocol.o: dwl-ipc-unstable-v2-protocol.h WAYLAND_SCANNER = `$(PKG_CONFIG) --variable=wayland_scanner wayland-scanner` WAYLAND_PROTOCOLS = `$(PKG_CONFIG) --variable=pkgdatadir wayland-protocols` -xdg-shell-protocol.h: - $(WAYLAND_SCANNER) server-header \ - $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ -wlr-layer-shell-unstable-v1-protocol.h: - $(WAYLAND_SCANNER) server-header \ - protocols/wlr-layer-shell-unstable-v1.xml $@ cursor-shape-v1-protocol.h: $(WAYLAND_SCANNER) server-header \ $(WAYLAND_PROTOCOLS)/staging/cursor-shape/cursor-shape-v1.xml $@ -wlr-output-power-management-unstable-v1-protocol.h: - $(WAYLAND_SCANNER) server-header \ - protocols/wlr-output-power-management-unstable-v1.xml $@ dwl-ipc-unstable-v2-protocol.h: $(WAYLAND_SCANNER) server-header \ protocols/dwl-ipc-unstable-v2.xml $@ dwl-ipc-unstable-v2-protocol.c: $(WAYLAND_SCANNER) private-code \ protocols/dwl-ipc-unstable-v2.xml $@ +wlr-output-power-management-unstable-v1-protocol.h: + $(WAYLAND_SCANNER) server-header \ + protocols/wlr-output-power-management-unstable-v1.xml $@ pointer-constraints-unstable-v1-protocol.h: $(WAYLAND_SCANNER) server-header \ $(WAYLAND_PROTOCOLS)/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml $@ +xdg-shell-protocol.h: + $(WAYLAND_SCANNER) server-header \ + $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ +wlr-layer-shell-unstable-v1-protocol.h: + $(WAYLAND_SCANNER) server-header \ + protocols/wlr-layer-shell-unstable-v1.xml $@ clean: rm -f dwl *.o *-protocol.h diff --git a/client.h b/client.h index 71c7d76..1dae434 100644 --- a/client.h +++ b/client.h @@ -10,7 +10,7 @@ static inline int client_is_x11(Client *c) { #ifdef XWAYLAND - return c->type == X11Managed || c->type == X11Unmanaged; + return c->type == X11; #endif return 0; } @@ -270,7 +270,8 @@ static inline int client_is_unmanaged(Client *c) { #ifdef XWAYLAND - return c->type == X11Unmanaged; + if (client_is_x11(c)) + return c->surface.xwayland->override_redirect; #endif return 0; } @@ -351,7 +352,7 @@ client_set_tiled(Client *c, uint32_t edges) if (client_is_x11(c)) return; #endif - if (wl_resource_get_version(c->surface.xdg->resource) + if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >= XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION) { wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges); } else { diff --git a/config.def.h b/config.def.h index be72c10..fdd7624 100644 --- a/config.def.h +++ b/config.def.h @@ -5,29 +5,19 @@ (hex & 0xFF) / 255.0f } /* appearance */ static const int sloppyfocus = 1; /* focus follows mouse */ -static const int bypass_surface_visibility = 1; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */ -static const int numlock = 1; -static const int capslock = 0; +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 smartborders = 1; static const unsigned int borderpx = 3; /* border pixel of windows */ +static const float rootcolor[] = COLOR(0x222222ff); static const float bordercolor[] = COLOR(0x00000000); static const float focuscolor[] = COLOR(0x7aa6daff); static const float urgentcolor[] = COLOR(0xd54e53ff); static const float stickycolor[] = COLOR(0x8abeb7ff); static const float mixedcolor[] = COLOR(0xb5bd68ff); /* To conform the xdg-protocol, set the alpha to zero to restore the old behavior */ -static const float fullscreen_bg[] = {0.1, 0.1, 0.1, 0.0}; +static const float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0}; /* You can also use glsl colors */ static const int center_relative_to_monitor = 1; /* 0 means center floating relative to the window area */ -static const char *const autostart[] = { - "/home/korei/.config/dwl/dwlinit.sh", NULL, - NULL /* terminate */ -}; -/* tagging - TAGCOUNT must be no greater than 31 */ -#define TAGCOUNT (10) - -static int log_level = WLR_ERROR; - typedef enum { VIEW_L = -1, VIEW_R = 1, @@ -35,24 +25,32 @@ typedef enum { SHIFT_R = 2, } RotateTags; -/* keyboard layout change notification for status bar */ -static const char kblayout_file[] = "/tmp/dwl-keymap"; -static const char *kblayout_cmd[] = {"pkill", "-RTMIN+1", "someblocks", NULL}; -static const int kblayout_perclient = 0; +/* Autostart */ +static const char *const autostart[] = { + "/home/korei/.config/dwl/dwlinit.sh", NULL, + NULL /* terminate */ +}; /* keyboard layout change notification for status bar */ -/* example using someblocks: static const char kblayout_file[] = "/tmp/dwl-keymap"; static const char *kblayout_cmd[] = {"pkill", "-RTMIN+1", "someblocks", NULL}; -*/ + +/* tagging - TAGCOUNT must be no greater than 31 */ +#define TAGCOUNT (10) + +/* logging */ +static int log_level = WLR_ERROR; static const Rule rules[] = { /* app_id title tags mask isfloating monitor */ /* examples: - { "Gimp", NULL, 0, 1, -1 }, - */ - { "corectrl", NULL, 1 << 5, 0, -1 }, - { NULL, "Save File", 0, 1, -1 }, + { "Gimp", NULL, 0, 1, -1 }, + */ + {"corectrl", NULL, 1 << 5, 0, -1}, + { NULL, "Save File", 0, 1, -1}, + { NULL, "Confirm to replace files", 0, 1, -1}, + { NULL, "File Operation Progress", 0, 1, -1}, + { NULL, "Seer Console", 0, 1, -1}, }; /* layout(s) */ @@ -64,9 +62,8 @@ static const Layout layouts[] = { { "><>", NULL } /* no layout function means floating behavior */ }; -/* monitors - * The order in which monitors are defined determines their position. - * Non-configured monitors are always added to the left. */ +/* monitors */ +/* NOTE: ALWAYS add a fallback rule, even if you are completely sure it won't be used */ static const MonitorRule monrules[] = { /* name mfact nmaster scale layout rotate/reflect x y */ /* example of a HiDPI laptop monitor: @@ -76,7 +73,6 @@ static const MonitorRule monrules[] = { { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, }; - /* keyboard */ static const struct xkb_rule_names xkb_rules = { /* can specify fields: rules, model, layout, variant, options */ @@ -127,11 +123,8 @@ LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT; static const double accel_speed = -0.5; -static const int cursor_timeout = 2; -/* separate variables for trackpad */ static const enum libinput_config_accel_profile accel_profile_trackpad = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; static const double accel_speed_trackpad = -0.0; - /* You can choose between: LIBINPUT_CONFIG_TAP_MAP_LRM -- 1/2/3 finger tap maps to left/right/middle LIBINPUT_CONFIG_TAP_MAP_LMR -- 1/2/3 finger tap maps to left/middle/right @@ -155,7 +148,7 @@ static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TA /* commands */ static const char* Term[] = { "footclient", NULL }; -static const char* TermAlt[] = { "foot", NULL }; +static const char* TermAlt[] = { "foot", "--maximized", NULL }; static const char* Menu[] = { "fuzzel", NULL }; static const char* PowerMenu[] = { "powermenu.sh", NULL }; static const char* AudioUpdate[] = { "pkill", "-RTMIN+12", "someblocks", NULL }; @@ -260,7 +253,7 @@ static const Key keys[] = { TAGKEYS(Key_8, 7), TAGKEYS(Key_9, 8), TAGKEYS(Key_0, 9), - { SUPER | ALT, Key_q, quit, { 0 } }, + { SUPER | CTRL, Key_q, quit, { 0 } }, /* 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 } }, #define CHVT(KEY, n) \ diff --git a/dwl.c b/dwl.c index 45a7c8a..94a6c10 100644 --- a/dwl.c +++ b/dwl.c @@ -1,11 +1,9 @@ /* * See LICENSE file for copyright and license details. */ - #include #include #include -#include #include #include #include @@ -30,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -82,7 +81,7 @@ /* enums */ enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ -enum { XDGShell, LayerShell, X11Managed, X11Unmanaged }; /* client types */ +enum { XDGShell, LayerShell, X11 }; /* client types */ enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrFS, LyrTop, LyrOverlay, LyrBlock, NUM_LAYERS }; /* scene layers */ #ifdef XWAYLAND enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, @@ -118,6 +117,7 @@ typedef struct { struct wlr_xdg_surface *xdg; struct wlr_xwayland_surface *xwayland; } surface; + struct wlr_xdg_toplevel_decoration_v1 *decoration; struct wl_listener commit; struct wl_listener map; struct wl_listener maximize; @@ -125,6 +125,8 @@ typedef struct { struct wl_listener destroy; struct wl_listener set_title; struct wl_listener fullscreen; + struct wl_listener set_decoration_mode; + struct wl_listener destroy_decoration; struct wlr_box prev; /* layout-relative, includes border */ struct wlr_box bounds; #ifdef XWAYLAND @@ -156,7 +158,7 @@ typedef struct { typedef struct { struct wl_list link; - struct wlr_keyboard *wlr_keyboard; + struct wlr_keyboard_group *wlr_group; xkb_keycode_t keycode; uint32_t mods; /* invalid if keycode == 0 */ @@ -164,10 +166,9 @@ typedef struct { struct wl_listener modifiers; struct wl_listener key; - struct wl_listener destroy; xkb_layout_index_t layout_idx; -} Keyboard; +} KeyboardGroup; typedef struct { /* Must keep these three elements in this order */ @@ -194,7 +195,7 @@ typedef struct { struct Monitor { struct wl_list link; - struct wl_list dwl_ipc_outputs; + struct wl_list dwl_ipc_outputs; struct wlr_output *wlr_output; struct wlr_scene_output *scene_output; struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ @@ -205,7 +206,7 @@ struct Monitor { struct wlr_session_lock_surface_v1 *lock_surface; struct wlr_box m; /* monitor area, layout-relative */ struct wlr_box w; /* window area, layout-relative */ - struct wl_list layers[4]; /* LayerSurface::link */ + struct wl_list layers[4]; /* LayerSurface.link */ const Layout *lt[2]; unsigned int seltags; unsigned int sellt; @@ -228,9 +229,6 @@ typedef struct { typedef struct { struct wlr_pointer_constraint_v1 *constraint; - Client *focused; - - struct wl_listener set_region; struct wl_listener destroy; } PointerConstraint; @@ -253,25 +251,22 @@ typedef struct { /* function declarations */ static void applybounds(Client *c, struct wlr_box *bbox); +static void autostartexec(void); static void applyrules(Client *c); static void arrange(Monitor *m); static void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive); static void arrangelayers(Monitor *m); -static void autostartexec(void); static void axisnotify(struct wl_listener *listener, void *data); static void buttonpress(struct wl_listener *listener, void *data); -static void checkconstraintregion(void); static void chvt(const Arg *arg); static void checkidleinhibitor(struct wlr_surface *exclude); static void cleanup(void); -static void cleanupkeyboard(struct wl_listener *listener, void *data); static void cleanupmon(struct wl_listener *listener, void *data); static void closemon(Monitor *m); static void commitlayersurfacenotify(struct wl_listener *listener, void *data); static void commitnotify(struct wl_listener *listener, void *data); static void createdecoration(struct wl_listener *listener, void *data); -static void commitpointerconstraint(struct wl_listener *listener, void *data); static void createidleinhibitor(struct wl_listener *listener, void *data); static void createkeyboard(struct wlr_keyboard *keyboard); static void createlayersurface(struct wl_listener *listener, void *data); @@ -280,9 +275,9 @@ static void createmon(struct wl_listener *listener, void *data); static void createnotify(struct wl_listener *listener, void *data); static void createpointer(struct wlr_pointer *pointer); static void createpointerconstraint(struct wl_listener *listener, void *data); -static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint); static void cursorframe(struct wl_listener *listener, void *data); -static void cursorwarptoconstrainthint(void); +static void cursorwarptohint(void); +static void destroydecoration(struct wl_listener *listener, void *data); static void destroydragicon(struct wl_listener *listener, void *data); static void destroyidleinhibitor(struct wl_listener *listener, void *data); static void destroylayersurfacenotify(struct wl_listener *listener, void *data); @@ -309,13 +304,11 @@ static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); static Client *focustop(Monitor *m, int onlytiled); static void fullscreennotify(struct wl_listener *listener, void *data); -static void restorecursor(void); -static int hidecursor(void *data); static void handlesig(int signo); static void hidebehindmonocle(Monitor *m); static void incnmaster(const Arg *arg); static void inputdevice(struct wl_listener *listener, void *data); -static void kblayoutnotify(Keyboard *kb, int update); +static void kblayoutnotify(KeyboardGroup *kb, int update); static int keybinding(uint32_t mods, xkb_keycode_t keycode); static void keypress(struct wl_listener *listener, void *data); static void keypressmod(struct wl_listener *listener, void *data); @@ -334,13 +327,13 @@ 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 pointerconstraintsetregion(struct wl_listener *listener, void *data); static void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, uint32_t time); static void printstatus(void); static void powermgrsetmodenotify(struct wl_listener *listener, void *data); static void quit(const Arg *arg); static void rendermon(struct wl_listener *listener, void *data); +static void requestdecorationmode(struct wl_listener *listener, void *data); static void requeststartdrag(struct wl_listener *listener, void *data); static void requestmonstate(struct wl_listener *listener, void *data); static void resize(Client *c, struct wlr_box geo, int interact, int draw_borders); @@ -360,9 +353,9 @@ static void spawn(const Arg *arg); static void startdrag(struct wl_listener *listener, void *data); static void tag(const Arg *arg); static void tagmon(const Arg *arg); -static void tile(Monitor *m); -static void vertile(Monitor *m); static void togglebar(const Arg *arg); +static void vertile(Monitor *m); +static void tile(Monitor *m); static void togglefloating(const Arg *arg); static void togglesticky(const Arg *arg); static void togglefullscreen(const Arg *arg); @@ -411,31 +404,28 @@ static struct wlr_gamma_control_manager_v1 *gamma_control_mgr; static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr; -static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr; static struct wlr_pointer_constraints_v1 *pointer_constraints; -static struct wl_listener pointer_constraint_commit; -static PointerConstraint *active_constraint; -static pixman_region32_t active_confine; -static int active_confine_requires_warp; +static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr; +static struct wlr_pointer_constraint_v1 *active_constraint; static struct wlr_cursor *cursor; static struct wlr_xcursor_manager *cursor_mgr; -static struct wl_event_source *hide_source; -static bool cursor_hidden = false; +static struct wlr_scene_rect *root_bg; static struct wlr_session_lock_manager_v1 *session_lock_mgr; static struct wlr_scene_rect *locked_bg; static struct wlr_session_lock_v1 *cur_lock; static struct wl_listener lock_listener = {.notify = locksession}; +static struct zdwl_ipc_manager_v2_interface dwl_manager_implementation = {.release = dwl_ipc_manager_release, .get_output = dwl_ipc_manager_get_output}; +static struct zdwl_ipc_output_v2_interface dwl_output_implementation = {.release = dwl_ipc_output_release, .set_tags = dwl_ipc_output_set_tags, .set_layout = dwl_ipc_output_set_layout, .set_client_tags = dwl_ipc_output_set_client_tags}; static struct wlr_output_power_manager_v1 *power_mgr; static struct wl_listener power_mgr_set_mode = {.notify = powermgrsetmodenotify}; -static struct zdwl_ipc_manager_v2_interface dwl_manager_implementation = {.release = dwl_ipc_manager_release, .get_output = dwl_ipc_manager_get_output}; -static struct zdwl_ipc_output_v2_interface dwl_output_implementation = {.release = dwl_ipc_output_release, .set_tags = dwl_ipc_output_set_tags, .set_layout = dwl_ipc_output_set_layout, .set_client_tags = dwl_ipc_output_set_client_tags}; - static struct wlr_seat *seat; -static struct wl_list keyboards; +static KeyboardGroup kb_group = {0}; +static KeyboardGroup vkb_group = {0}; +static struct wlr_surface *held_grab; static unsigned int cursor_mode; static Client *grabc; static int grabcx, grabcy; /* client-relative */ @@ -487,6 +477,27 @@ applybounds(Client *c, struct wlr_box *bbox) c->geom.y = bbox->y; } +void +autostartexec(void) { + const char *const *p; + size_t i = 0; + + /* count entries */ + for (p = autostart; *p; autostart_len++, p++) + while (*++p); + + autostart_pids = calloc(autostart_len, sizeof(pid_t)); + for (p = autostart; *p; i++, p++) { + if ((autostart_pids[i] = fork()) == 0) { + setsid(); + execvp(*p, (char *const *)p); + die("dwl: execvp %s:", *p); + } + /* skip arguments */ + while (*++p); + } +} + void applyrules(Client *c) { @@ -508,9 +519,10 @@ applyrules(Client *c) c->isfloating = r->isfloating; newtags |= r->tags; i = 0; - wl_list_for_each(m, &mons, link) + wl_list_for_each(m, &mons, link) { if (r->monitor == i++) mon = m; + } } } wlr_scene_node_reparent(&c->scene->node, layers[c->isfloating ? LyrFloat : LyrTile]); @@ -547,9 +559,8 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int wl_list_for_each(l, list, link) { struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; - struct wlr_layer_surface_v1_state *state = &layer_surface->current; - if (exclusive != (state->exclusive_zone > 0)) + if (exclusive != (layer_surface->current.exclusive_zone > 0)) continue; wlr_scene_layer_surface_v1_configure(l->scene_layer, &full_area, usable_area); @@ -588,38 +599,17 @@ arrangelayers(Monitor *m) /* Find topmost keyboard interactive layer, if such a layer exists */ for (i = 0; i < 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) { - /* Deactivate the focused client. */ - focusclient(NULL, 0); - exclusive_focus = l; - client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat)); - return; - } + if (locked || !l->layer_surface->current.keyboard_interactive || !l->mapped) + continue; + /* Deactivate the focused client. */ + focusclient(NULL, 0); + exclusive_focus = l; + client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat)); + return; } } } -void -autostartexec(void) { - const char *const *p; - size_t i = 0; - - /* count entries */ - for (p = autostart; *p; autostart_len++, p++) - while (*++p); - - autostart_pids = calloc(autostart_len, sizeof(pid_t)); - for (p = autostart; *p; i++, p++) { - if ((autostart_pids[i] = fork()) == 0) { - setsid(); - execvp(*p, (char *const *)p); - die("dwl: execvp %s:", *p); - } - /* skip arguments */ - while (*++p); - } -} - void axisnotify(struct wl_listener *listener, void *data) { @@ -627,9 +617,6 @@ axisnotify(struct wl_listener *listener, void *data) * for example when you move the scroll wheel. */ struct wlr_pointer_axis_event *event = data; wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); - if (cursor_hidden) - wlr_cursor_unset_image(cursor); - /* TODO: allow usage of scroll whell for mousebindings, it can be implemented * checking the event's orientation and the delta of the event */ /* Notify the client with pointer focus of the axis event. */ @@ -648,11 +635,11 @@ buttonpress(struct wl_listener *listener, void *data) const Button *b; wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); - // handlecursoractivity(false); switch (event->state) { case WLR_BUTTON_PRESSED: cursor_mode = CurPressed; + held_grab = seat->pointer_state.focused_surface; if (locked) break; @@ -672,12 +659,12 @@ buttonpress(struct wl_listener *listener, void *data) } break; case WLR_BUTTON_RELEASED: + held_grab = NULL; /* 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; - 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); @@ -693,42 +680,6 @@ buttonpress(struct wl_listener *listener, void *data) event->time_msec, event->button, event->state); } -void -checkconstraintregion(void) -{ - struct wlr_pointer_constraint_v1 *constraint = active_constraint->constraint; - pixman_region32_t *region = &constraint->region; - Client *c = NULL; - double sx, sy; - toplevel_from_wlr_surface(constraint->surface, &c, NULL); - if (active_confine_requires_warp && c) { - active_confine_requires_warp = 0; - - sx = cursor->x + c->geom.x; - sy = cursor->y + c->geom.y; - - if (!pixman_region32_contains_point(region, - floor(sx), floor(sy), NULL)) { - int nboxes; - pixman_box32_t *boxes = pixman_region32_rectangles(region, &nboxes); - if (nboxes > 0) { - sx = (boxes[0].x1 + boxes[0].x2) / 2.; - sy = (boxes[0].y1 + boxes[0].y2) / 2.; - - wlr_cursor_warp_closest(cursor, NULL, - sx - c->geom.x, sy - c->geom.y); - } - } - } - - /* A locked pointer will result in an empty region, thus disallowing all movement. */ - if (constraint->type == WLR_POINTER_CONSTRAINT_V1_CONFINED) { - pixman_region32_copy(&active_confine, region); - } else { - pixman_region32_clear(&active_confine); - } -} - void chvt(const Arg *arg) { @@ -739,8 +690,6 @@ void checkidleinhibitor(struct wlr_surface *exclude) { int inhibited = 0, unused_lx, unused_ly; - Client *c = focustop(selmon, 0); - struct wlr_idle_inhibitor_v1 *inhibitor; wl_list_for_each(inhibitor, &idle_inhibit_mgr->inhibitors, link) { struct wlr_surface *surface = wlr_surface_get_root_surface(inhibitor->surface); @@ -752,55 +701,44 @@ checkidleinhibitor(struct wlr_surface *exclude) } } - if (c && c->isfullscreen) - inhibited = 1; - wlr_idle_notifier_v1_set_inhibited(idle_notifier, inhibited); } void cleanup(void) { - size_t i; + size_t i; #ifdef XWAYLAND wlr_xwayland_destroy(xwayland); xwayland = NULL; #endif + wl_display_destroy_clients(dpy); + /* kill child processes */ for (i = 0; i < autostart_len; i++) { if (0 < autostart_pids[i]) { kill(autostart_pids[i], SIGTERM); waitpid(autostart_pids[i], NULL, 0); } + } - wl_display_destroy_clients(dpy); if (child_pid > 0) { kill(child_pid, SIGTERM); waitpid(child_pid, NULL, 0); } wlr_xcursor_manager_destroy(cursor_mgr); wlr_output_layout_destroy(output_layout); - wl_display_destroy(dpy); -} + /* Remove event source that use the dpy event loop before destroying dpy */ + wl_event_source_remove(kb_group.key_repeat_source); + wl_event_source_remove(vkb_group.key_repeat_source); + + wl_display_destroy(dpy); /* Destroy after the wayland display (when the monitors are already destroyed) to avoid destroying them with an invalid scene output. */ wlr_scene_node_destroy(&scene->tree.node); } -void -cleanupkeyboard(struct wl_listener *listener, void *data) -{ - Keyboard *kb = wl_container_of(listener, kb, destroy); - - wl_event_source_remove(kb->key_repeat_source); - wl_list_remove(&kb->link); - wl_list_remove(&kb->modifiers.link); - wl_list_remove(&kb->key.link); - wl_list_remove(&kb->destroy.link); - free(kb); -} - void cleanupmon(struct wl_listener *listener, void *data) { @@ -811,14 +749,11 @@ cleanupmon(struct wl_listener *listener, void *data) DwlIpcOutput *ipc_output, *ipc_output_tmp; wl_list_for_each_safe(ipc_output, ipc_output_tmp, &m->dwl_ipc_outputs, link) wl_resource_destroy(ipc_output->resource); - for (i = 0; i <= ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY; i++) - wl_list_for_each_safe(l, tmp, &m->layers[i], link) - wlr_layer_surface_v1_destroy(l->layer_surface); - /* m->layers[i] are intentionally not unlinked */ - for (i = 0; i < LENGTH(m->layers); i++) + for (i = 0; i < LENGTH(m->layers); i++) { wl_list_for_each_safe(l, tmp, &m->layers[i], link) wlr_layer_surface_v1_destroy(l->layer_surface); + } wl_list_remove(&m->destroy.link); wl_list_remove(&m->frame.link); @@ -833,16 +768,17 @@ cleanupmon(struct wl_listener *listener, void *data) free(m); } + void closemon(Monitor *m) { /* update selmon if needed and * move closed monitor's clients to the focused one */ Client *c; - if (wl_list_empty(&mons)) { + int i = 0, nmons = wl_list_length(&mons); + if (!nmons) { selmon = NULL; } else if (m == selmon) { - int nmons = wl_list_length(&mons), i = 0; do /* don't switch to disabled mons */ selmon = wl_container_of(mons.next, selmon, link); while (!selmon->wlr_output->enabled && i++ < nmons); @@ -851,7 +787,7 @@ closemon(Monitor *m) wl_list_for_each(c, &clients, link) { if (c->isfloating && c->geom.x > m->m.width) resize(c, (struct wlr_box){.x = c->geom.x - m->w.width, .y = c->geom.y, - .width = c->geom.width, .height = c->geom.height}, 0, 1); + .width = c->geom.width, .height = c->geom.height}, 0, 1); if (c->mon == m) setmon(c, selmon, c->tags); } @@ -866,7 +802,6 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data) struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; struct wlr_scene_tree *scene_layer = layers[layermap[layer_surface->current.layer]]; - if (layer_surface->current.committed == 0 && l->mapped == layer_surface->surface->mapped) return; l->mapped = layer_surface->surface->mapped; @@ -887,12 +822,8 @@ commitnotify(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, commit); - if (client_surface(c)->mapped) { - if (c->mon && c->mon->lt[c->mon->sellt]->arrange && !c->isfullscreen && !c->isfloating) - c->mon->lt[c->mon->sellt]->arrange(c->mon); - else - resize(c, c->geom, (c->isfloating && !c->isfullscreen), (c->isfloating && !c->isfullscreen)); - } + if (client_surface(c)->mapped) + resize(c, c->geom, (c->isfloating && !c->isfullscreen), c->bw > 0); /* mark a pending resize as completed */ if (c->resize && c->resize <= c->surface.xdg->current.configure_serial) @@ -902,14 +833,14 @@ commitnotify(struct wl_listener *listener, void *data) void createdecoration(struct wl_listener *listener, void *data) { - struct wlr_xdg_toplevel_decoration_v1 *dec = data; - wlr_xdg_toplevel_decoration_v1_set_mode(dec, WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); -} + struct wlr_xdg_toplevel_decoration_v1 *deco = data; + Client *c = deco->toplevel->base->data; + c->decoration = deco; -void -commitpointerconstraint(struct wl_listener *listener, void *data) -{ - checkconstraintregion(); + LISTEN(&deco->events.request_mode, &c->set_decoration_mode, requestdecorationmode); + LISTEN(&deco->events.destroy, &c->destroy_decoration, destroydecoration); + + requestdecorationmode(&c->set_decoration_mode, deco); } void @@ -924,37 +855,14 @@ createidleinhibitor(struct wl_listener *listener, void *data) void createkeyboard(struct wlr_keyboard *keyboard) { - struct xkb_context *context; - struct xkb_keymap *keymap; - Keyboard *kb = keyboard->data = ecalloc(1, sizeof(*kb)); - kb->wlr_keyboard = keyboard; - - /* Prepare an XKB keymap and assign it to the keyboard. */ - context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - keymap = xkb_keymap_new_from_names(context, &xkb_rules, - XKB_KEYMAP_COMPILE_NO_FLAGS); - if (!keymap) - die("createkeyboard: failed to compile keymap"); - - wlr_keyboard_set_keymap(keyboard, keymap); - xkb_keymap_unref(keymap); - xkb_context_unref(context); + /* Set the keymap to match the group keymap */ + wlr_keyboard_set_keymap(keyboard, kb_group.wlr_group->keyboard.keymap); wlr_keyboard_set_repeat_info(keyboard, repeat_rate, repeat_delay); - /* Here we set up listeners for keyboard events. */ - LISTEN(&keyboard->events.modifiers, &kb->modifiers, keypressmod); - LISTEN(&keyboard->events.key, &kb->key, keypress); - LISTEN(&keyboard->base.events.destroy, &kb->destroy, cleanupkeyboard); + /* Add the new keyboard to the group */ + wlr_keyboard_group_add_keyboard(kb_group.wlr_group, keyboard); - wlr_seat_set_keyboard(seat, keyboard); - - kb->key_repeat_source = wl_event_loop_add_timer( - wl_display_get_event_loop(dpy), keyrepeat, kb); - - /* And add the keyboard to our list of keyboards */ - wl_list_insert(&keyboards, &kb->link); - - kblayoutnotify(kb, 1); + kblayoutnotify(&kb_group, 1); } void @@ -987,7 +895,6 @@ createlayersurface(struct wl_listener *listener, void *data) < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LyrTop] : scene_layer); l->scene->node.data = l->popups->node.data = l; - wl_list_insert(&l->mon->layers[layer_surface->pending.layer],&l->link); wlr_surface_send_enter(surface, layer_surface->output); @@ -1007,8 +914,8 @@ createlocksurface(struct wl_listener *listener, void *data) SessionLock *lock = wl_container_of(listener, lock, new_surface); struct wlr_session_lock_surface_v1 *lock_surface = data; Monitor *m = lock_surface->output->data; - struct wlr_scene_tree *scene_tree = lock_surface->surface->data = - wlr_scene_subsurface_tree_create(lock->scene, lock_surface->surface); + struct wlr_scene_tree *scene_tree = lock_surface->surface->data + = wlr_scene_subsurface_tree_create(lock->scene, lock_surface->surface); m->lock_surface = lock_surface; wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y); @@ -1038,9 +945,7 @@ createmon(struct wl_listener *listener, void *data) m->wlr_output = wlr_output; wl_list_init(&m->dwl_ipc_outputs); - wlr_output_init_render(wlr_output, alloc, drw); - /* Initialize monitor state using configured rules */ for (i = 0; i < LENGTH(m->layers); i++) wl_list_init(&m->layers[i]); @@ -1049,13 +954,14 @@ createmon(struct wl_listener *listener, void *data) m->tagset[0] = m->tagset[1] = 1; for (r = monrules; r < END(monrules); r++) { if (!r->name || strstr(wlr_output->name, r->name)) { - m->mfact = r->mfact; - m->nmaster = r->nmaster; - wlr_output_state_set_scale(&state, r->scale); - m->lt[0] = m->lt[1] = r->lt; - wlr_output_state_set_transform(&state, r->rr); m->m.x = r->x; m->m.y = r->y; + m->mfact = r->mfact; + m->nmaster = r->nmaster; + m->lt[0] = m->lt[1] = r->lt; + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol)); + wlr_output_state_set_scale(&state, r->scale); + wlr_output_state_set_transform(&state, r->rr); break; } } @@ -1101,7 +1007,28 @@ createmon(struct wl_listener *listener, void *data) wlr_output_layout_add_auto(output_layout, wlr_output); else wlr_output_layout_add(output_layout, wlr_output, m->m.x, m->m.y); - strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol)); +} + +void +createpointerconstraint(struct wl_listener *listener, void *data) +{ + struct wlr_surface *surface = seat->keyboard_state.focused_surface; + PointerConstraint *pointer_constraint = ecalloc(1, sizeof(*pointer_constraint)); + pointer_constraint->constraint = data; + + LISTEN(&pointer_constraint->constraint->events.destroy, + &pointer_constraint->destroy, destroypointerconstraint); + + if (surface && surface == pointer_constraint->constraint->surface) { + if (active_constraint == pointer_constraint->constraint) + return; + + if (active_constraint) + wlr_pointer_constraint_v1_send_deactivated(active_constraint); + + active_constraint = pointer_constraint->constraint; + wlr_pointer_constraint_v1_send_activated(pointer_constraint->constraint); + } } void @@ -1115,20 +1042,21 @@ createnotify(struct wl_listener *listener, void *data) struct wlr_xdg_surface *xdg_surface = data; Client *c = NULL; LayerSurface *l = NULL; + struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + struct wlr_xdg_popup *popup = xdg_surface->popup; struct wlr_box box; - int type = toplevel_from_wlr_surface(xdg_surface->surface, &c, &l); - if (!xdg_surface->popup->parent || type < 0) + if (toplevel_from_wlr_surface(popup->base->surface, &c, &l) < 0) return; - xdg_surface->surface->data = wlr_scene_xdg_surface_create( - xdg_surface->popup->parent->data, xdg_surface); + popup->base->surface->data = wlr_scene_xdg_surface_create( + popup->parent->data, popup->base); if ((l && !l->mon) || (c && !c->mon)) return; - box = type == LayerShell ? l->mon->m : c->mon->w; - box.x -= (type == LayerShell ? l->geom.x : c->geom.x); - box.y -= (type == LayerShell ? l->geom.y : c->geom.y); - wlr_xdg_popup_unconstrain_from_box(xdg_surface->popup, &box); + box = l ? l->mon->m : c->mon->w; + box.x -= (l ? l->geom.x : c->geom.x); + box.y -= (l ? l->geom.y : c->geom.y); + wlr_xdg_popup_unconstrain_from_box(popup, &box); return; } else if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE) return; @@ -1137,22 +1065,22 @@ createnotify(struct wl_listener *listener, void *data) c = xdg_surface->data = ecalloc(1, sizeof(*c)); c->surface.xdg = xdg_surface; c->bw = borderpx; - if (kblayout_perclient) - c->layout_idx = xkb_state_serialize_layout( - wlr_seat_get_keyboard(seat)->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE); + c->layout_idx = kb + ? xkb_state_serialize_layout(kb->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE) + : 0; wlr_xdg_toplevel_set_wm_capabilities(xdg_surface->toplevel, WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); + LISTEN(&xdg_surface->events.destroy, &c->destroy, destroynotify); LISTEN(&xdg_surface->surface->events.commit, &c->commit, commitnotify); LISTEN(&xdg_surface->surface->events.map, &c->map, mapnotify); LISTEN(&xdg_surface->surface->events.unmap, &c->unmap, unmapnotify); - LISTEN(&xdg_surface->events.destroy, &c->destroy, destroynotify); - LISTEN(&xdg_surface->toplevel->events.set_title, &c->set_title, updatetitle); LISTEN(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen, fullscreennotify); LISTEN(&xdg_surface->toplevel->events.request_maximize, &c->maximize, maximizenotify); + LISTEN(&xdg_surface->toplevel->events.set_title, &c->set_title, updatetitle); } void @@ -1211,74 +1139,6 @@ createpointer(struct wlr_pointer *pointer) wlr_cursor_attach_input_device(cursor, &pointer->base); } -void -createpointerconstraint(struct wl_listener *listener, void *data) -{ - struct wlr_pointer_constraint_v1 *wlr_constraint = data; - PointerConstraint *constraint = ecalloc(1, sizeof(*constraint)); - Client *c = NULL, *sel = focustop(selmon, 0); - toplevel_from_wlr_surface(wlr_constraint->surface, &c, NULL); - constraint->constraint = wlr_constraint; - wlr_constraint->data = constraint; - - LISTEN(&wlr_constraint->events.set_region, &constraint->set_region, - pointerconstraintsetregion); - LISTEN(&wlr_constraint->events.destroy, &constraint->destroy, - destroypointerconstraint); - - if (c == sel) - cursorconstrain(wlr_constraint); -} - -void -cursorconstrain(struct wlr_pointer_constraint_v1 *wlr_constraint) -{ - PointerConstraint *constraint = wlr_constraint->data; - - if (active_constraint == constraint) - return; - - wl_list_remove(&pointer_constraint_commit.link); - if (active_constraint) { - if (!wlr_constraint) - cursorwarptoconstrainthint(); - - wlr_pointer_constraint_v1_send_deactivated(active_constraint->constraint); - } - - active_constraint = constraint; - - if (!wlr_constraint) { - wl_list_init(&pointer_constraint_commit.link); - return; - } - - active_confine_requires_warp = 1; - - /* Stolen from sway/input/cursor.c:1435 - * - * FIXME: Big hack, stolen from wlr_pointer_constraints_v1.c:121. - * This is necessary because the focus may be set before the surface - * has finished committing, which means that warping won't work properly, - * since this code will be run *after* the focus has been set. - * That is why we duplicate the code here. - */ - if (pixman_region32_not_empty(&wlr_constraint->current.region)) { - pixman_region32_intersect(&wlr_constraint->region, - &wlr_constraint->surface->input_region, &wlr_constraint->current.region); - } else { - pixman_region32_copy(&wlr_constraint->region, - &wlr_constraint->surface->input_region); - } - - checkconstraintregion(); - - wlr_pointer_constraint_v1_send_activated(wlr_constraint); - - LISTEN(&wlr_constraint->surface->events.commit, &pointer_constraint_commit, - commitpointerconstraint); -} - void cursorframe(struct wl_listener *listener, void *data) { @@ -1291,31 +1151,44 @@ cursorframe(struct wl_listener *listener, void *data) } void -cursorwarptoconstrainthint(void) +cursorwarptohint(void) { - struct wlr_pointer_constraint_v1 *constraint = active_constraint->constraint; + Client *c = NULL; + double sx, sy; - if (constraint->current.committed & - WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) { - double lx, ly; - double sx = lx = constraint->current.cursor_hint.x; - double sy = ly = constraint->current.cursor_hint.y; - - Client *c = NULL; - toplevel_from_wlr_surface(constraint->surface, &c, NULL); - if (c) { - lx -= c->geom.x; - ly -= c->geom.y; - } - - wlr_cursor_warp(cursor, NULL, lx, ly); - - /* Warp the pointer as well, so that on the next pointer rebase we don't - * send an unexpected synthetic motion event to clients. */ + toplevel_from_wlr_surface(active_constraint->surface, &c, NULL); + /* TODO: wlroots 0.18: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4478 */ + if (c && (active_constraint->current.committed & WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT)) { + sx = active_constraint->current.cursor_hint.x + c->mon->m.x + c->bw; + sy = active_constraint->current.cursor_hint.y + c->mon->m.y + c->bw; + wlr_cursor_warp(cursor, NULL, sx - c->geom.x, sy - c->geom.y); wlr_seat_pointer_warp(seat, sx, sy); } } +void +destroypointerconstraint(struct wl_listener *listener, void *data) +{ + PointerConstraint *pointer_constraint = wl_container_of(listener, pointer_constraint, destroy); + + if (active_constraint == pointer_constraint->constraint) { + cursorwarptohint(); + active_constraint = NULL; + } + + wl_list_remove(&pointer_constraint->destroy.link); + free(pointer_constraint); +} + +void +destroydecoration(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, destroy_decoration); + + wl_list_remove(&c->destroy_decoration.link); + wl_list_remove(&c->set_decoration_mode.link); +} + void destroydragicon(struct wl_listener *listener, void *data) { @@ -1385,7 +1258,7 @@ destroylocksurface(struct wl_listener *listener, void *data) surface = wl_container_of(cur_lock->surfaces.next, surface, link); client_notify_enter(surface->surface, wlr_seat_get_keyboard(seat)); } else if (!locked) { - focusclient(focustop(selmon, 0), 1); + focusclient(focustop(selmon, 0), 1); } else { wlr_seat_keyboard_clear_focus(seat); } @@ -1416,26 +1289,6 @@ destroynotify(struct wl_listener *listener, void *data) free(c); } -void -destroypointerconstraint(struct wl_listener *listener, void *data) -{ - PointerConstraint *constraint = wl_container_of(listener, constraint, destroy); - wl_list_remove(&constraint->set_region.link); - wl_list_remove(&constraint->destroy.link); - - if (active_constraint == constraint) { - cursorwarptoconstrainthint(); - - if (pointer_constraint_commit.link.next) - wl_list_remove(&pointer_constraint_commit.link); - - wl_list_init(&pointer_constraint_commit.link); - active_constraint = NULL; - } - - free(constraint); -} - void destroysessionlock(struct wl_listener *listener, void *data) { @@ -1499,7 +1352,7 @@ dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resourc ipc_output = ecalloc(1, sizeof(*ipc_output)); ipc_output->resource = output_resource; - ipc_output->mon = monitor; + ipc_output->mon = monitor; wl_resource_set_implementation(output_resource, &dwl_output_implementation, ipc_output, dwl_ipc_output_destroy); wl_list_insert(&monitor->dwl_ipc_outputs, &ipc_output->link); dwl_ipc_output_printstatus_to(ipc_output); @@ -1533,11 +1386,11 @@ dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) Monitor *monitor = ipc_output->mon; Client *c, *focused; int tagmask, state, numclients, focused_client, tag; - const char *title, *appid; + const char *title, *appid; focused = focustop(monitor, 0); zdwl_ipc_output_v2_send_active(ipc_output->resource, monitor == selmon); - for (tag = 0 ; tag < TAGCOUNT; tag++) { + for (tag = 0 ; tag < TAGCOUNT; tag++) { numclients = state = focused_client = 0; tagmask = 1 << tag; if ((tagmask & monitor->tagset[monitor->seltags]) != 0) @@ -1650,6 +1503,7 @@ dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource) wl_resource_destroy(resource); } + void focusclient(Client *c, int lift) { @@ -1689,9 +1543,8 @@ focusclient(Client *c, int lift) /* Don't change border color if there is an exclusive focus or we are * handling a drag operation */ if (!exclusive_focus && !seat->drag) { - if (c->issticky) { + if (c->issticky) client_set_border_color(c, mixedcolor); - } else client_set_border_color(c, focuscolor); } @@ -1721,6 +1574,17 @@ focusclient(Client *c, int lift) } printstatus(); + /* Update keyboard layout */ + if (kb) { + mdepr = xkb_state_serialize_mods(kb->xkb_state, XKB_STATE_MODS_DEPRESSED); + mlatc = xkb_state_serialize_mods(kb->xkb_state, XKB_STATE_MODS_LATCHED); + mlock = xkb_state_serialize_mods(kb->xkb_state, XKB_STATE_MODS_LOCKED); + ldepr = xkb_state_serialize_layout(kb->xkb_state, XKB_STATE_LAYOUT_DEPRESSED); + llatc = xkb_state_serialize_layout(kb->xkb_state, XKB_STATE_LAYOUT_LATCHED); + llock = c ? c->layout_idx : 0; + xkb_state_update_mask(kb->xkb_state, mdepr, mlatc, mlock, ldepr, llatc, llock); + } + if (!c) { /* With no client, all we have left is to clear focus */ wlr_seat_keyboard_notify_clear_focus(seat); @@ -1735,27 +1599,17 @@ focusclient(Client *c, int lift) /* Activate the new client */ client_activate_surface(client_surface(c), 1); - - /* Update keyboard layout */ - if (kblayout_perclient) { - mdepr = xkb_state_serialize_mods(kb->xkb_state, XKB_STATE_MODS_DEPRESSED); - mlatc = xkb_state_serialize_mods(kb->xkb_state, XKB_STATE_MODS_LATCHED); - mlock = xkb_state_serialize_mods(kb->xkb_state, XKB_STATE_MODS_LOCKED); - ldepr = xkb_state_serialize_layout(kb->xkb_state, XKB_STATE_LAYOUT_DEPRESSED); - llatc = xkb_state_serialize_layout(kb->xkb_state, XKB_STATE_LAYOUT_LATCHED); - llock = c->layout_idx; - xkb_state_update_mask(kb->xkb_state, mdepr, mlatc, mlock, ldepr, llatc, llock); - } } void focusmon(const Arg *arg) { int i = 0, nmons = wl_list_length(&mons); - if (nmons) + if (nmons) { do /* don't switch to disabled mons */ selmon = dirtomon(arg->i); while (!selmon->wlr_output->enabled && i++ < nmons); + } focusclient(focustop(selmon, 0), 1); } @@ -1819,10 +1673,10 @@ handlesig(int signo) * XWayland. */ while (!waitid(P_ALL, 0, &in, WEXITED|WNOHANG|WNOWAIT) && in.si_pid -#ifdef XWAYLAND + #ifdef XWAYLAND && (!xwayland || in.si_pid != xwayland->server->pid) #endif - ) { + ) { pid_t *p, *lim; waitpid(in.si_pid, NULL, 0); if (in.si_pid == child_pid) @@ -1859,25 +1713,6 @@ hidebehindmonocle(Monitor *m) } } -void -restorecursor(void) -{ - wl_event_source_timer_update(hide_source, cursor_timeout * 1000); - - if (cursor_hidden) { - wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); - cursor_hidden = false; - } -} - -int -hidecursor(void *data) -{ - wlr_cursor_unset_image(cursor); - cursor_hidden = true; - return 1; -} - void incnmaster(const Arg *arg) { @@ -1912,24 +1747,24 @@ inputdevice(struct wl_listener *listener, void *data) * there are no pointer devices, so we always include that capability. */ /* TODO do we actually require a cursor? */ caps = WL_SEAT_CAPABILITY_POINTER; - if (!wl_list_empty(&keyboards)) + if (!wl_list_empty(&kb_group.wlr_group->devices)) caps |= WL_SEAT_CAPABILITY_KEYBOARD; wlr_seat_set_capabilities(seat, caps); } void -kblayoutnotify(Keyboard *kb, int update) +kblayoutnotify(KeyboardGroup *kb, int update) { FILE *f; Client *c; xkb_layout_index_t old = kb->layout_idx; if (update) { - kb->layout_idx = xkb_state_serialize_layout(kb->wlr_keyboard->xkb_state, + kb->layout_idx = xkb_state_serialize_layout(kb->wlr_group->keyboard.xkb_state, XKB_STATE_LAYOUT_EFFECTIVE); // Update client layout - if (kblayout_perclient && kb->layout_idx != old && (c = focustop(selmon, 0))) + if (kb->layout_idx != old && (c = focustop(selmon, 0))) c->layout_idx = kb->layout_idx; } @@ -1940,7 +1775,7 @@ kblayoutnotify(Keyboard *kb, int update) // Save current layout to kblayout_file if (*kblayout_file && (f = fopen(kblayout_file, "w"))) { - fputs(xkb_keymap_layout_get_name(kb->wlr_keyboard->keymap, + fputs(xkb_keymap_layout_get_name(kb->wlr_group->keyboard.keymap, kb->layout_idx), f); fclose(f); } @@ -1960,59 +1795,56 @@ keybinding(uint32_t mods, xkb_keycode_t keycode) * processing keys, rather than passing them on to the client for its own * processing. */ - int handled = 0; const Key *k; for (k = keys; k < END(keys); k++) { if (CLEANMASK(mods) == CLEANMASK(k->mod) && keycode == k->keycode && k->func) { k->func(&k->arg); - handled = 1; + return 1; } } - return handled; + return 0; } void keypress(struct wl_listener *listener, void *data) { /* This event is raised when a key is pressed or released. */ - Keyboard *kb = wl_container_of(listener, kb, key); + KeyboardGroup *group = wl_container_of(listener, group, key); struct wlr_keyboard_key_event *event = data; + int handled = 0; /* Translate libinput keycode -> xkbcommon */ uint32_t keycode = event->keycode + 8; - - int handled = 0; - uint32_t mods = wlr_keyboard_get_modifiers(kb->wlr_keyboard); + uint32_t mods = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard); wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); - if (cursor_hidden) - wlr_cursor_unset_image(cursor); - kblayoutnotify(kb, 0); + kblayoutnotify(group, 0); /* On _press_ if there is no active screen locker, * attempt to process a compositor keybinding. */ - if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) + if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { handled = keybinding(mods, keycode); + } - if (handled && kb->wlr_keyboard->repeat_info.delay > 0) { - kb->mods = mods; - kb->keycode = keycode; - wl_event_source_timer_update(kb->key_repeat_source, - kb->wlr_keyboard->repeat_info.delay); + if (handled && group->wlr_group->keyboard.repeat_info.delay > 0) { + group->mods = mods; + group->keycode = keycode; + wl_event_source_timer_update(group->key_repeat_source, + group->wlr_group->keyboard.repeat_info.delay); } else { - kb->keycode = 0; - wl_event_source_timer_update(kb->key_repeat_source, 0); + group->keycode = 0; + wl_event_source_timer_update(group->key_repeat_source, 0); } if (handled) return; + wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); /* Pass unhandled keycodes along to the client. */ - wlr_seat_set_keyboard(seat, kb->wlr_keyboard); wlr_seat_keyboard_notify_key(seat, event->time_msec, - event->keycode, event->state); + event->keycode, event->state); } void @@ -2020,33 +1852,27 @@ keypressmod(struct wl_listener *listener, void *data) { /* This event is raised when a modifier key, such as shift or alt, is * pressed. We simply communicate this to the client. */ - Keyboard *kb = wl_container_of(listener, kb, modifiers); + KeyboardGroup *group = wl_container_of(listener, group, modifiers); - kblayoutnotify(kb, 1); + kblayoutnotify(group, 1); - /* - * A seat can only have one keyboard, but this is a limitation of the - * Wayland protocol - not wlroots. We assign all connected keyboards to the - * same seat. You can swap out the underlying wlr_keyboard like this and - * wlr_seat handles this transparently. - */ - wlr_seat_set_keyboard(seat, kb->wlr_keyboard); + wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); /* Send modifiers to the client. */ wlr_seat_keyboard_notify_modifiers(seat, - &kb->wlr_keyboard->modifiers); + &group->wlr_group->keyboard.modifiers); } int keyrepeat(void *data) { - Keyboard *kb = data; - if (!kb->keycode || kb->wlr_keyboard->repeat_info.rate <= 0) + KeyboardGroup *group = data; + if (!group->keycode || group->wlr_group->keyboard.repeat_info.rate <= 0) return 0; - wl_event_source_timer_update(kb->key_repeat_source, - 1000 / kb->wlr_keyboard->repeat_info.rate); + wl_event_source_timer_update(group->key_repeat_source, + 1000 / group->wlr_group->keyboard.repeat_info.rate); - keybinding(kb->mods, kb->keycode); + keybinding(group->mods, group->keycode); return 0; } @@ -2107,13 +1933,14 @@ mapnotify(struct wl_listener *listener, void *data) : wlr_scene_subsurface_tree_create(c->scene, client_surface(c)); c->scene->node.data = c->scene_surface->node.data = c; + client_get_geometry(c, &c->geom); + /* Handle unmanaged clients first so we can return prior create borders */ if (client_is_unmanaged(c)) { - client_get_geometry(c, &c->geom); /* Unmanaged clients always are floating */ wlr_scene_node_reparent(&c->scene->node, layers[LyrFloat]); wlr_scene_node_set_position(&c->scene->node, c->geom.x + borderpx, - c->geom.y + borderpx); + c->geom.y + borderpx); if (client_wants_focus(c)) { focusclient(c, 1); exclusive_focus = c; @@ -2122,13 +1949,13 @@ mapnotify(struct wl_listener *listener, void *data) } for (i = 0; i < 4; i++) { - c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, bordercolor); + c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, + c->isurgent ? urgentcolor : bordercolor); c->border[i]->node.data = c; } /* Initialize client geometry with room for border */ client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); - client_get_geometry(c, &c->geom); c->geom.width += 2 * c->bw; c->geom.height += 2 * c->bw; @@ -2143,7 +1970,7 @@ mapnotify(struct wl_listener *listener, void *data) * we always consider floating, clients that have parent and thus * we set the same tags and monitor than its parent, if not * try to apply rules for them */ - /* TODO: https://github.com/djpohly/dwl/pull/334#issuecomment-1330166324 */ + /* TODO: https://github.com/djpohly/dwl/pull/334#issuecomment-1330166324 */ if (c->type == XDGShell && (p = client_get_parent(c))) { c->isfloating = 1; wlr_scene_node_reparent(&c->scene->node, layers[LyrFloat]); @@ -2155,9 +1982,10 @@ mapnotify(struct wl_listener *listener, void *data) unset_fullscreen: m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y); - wl_list_for_each(w, &clients, link) + wl_list_for_each(w, &clients, link) { if (w != c && w->isfullscreen && m == w->mon && (w->tags & c->tags)) setfullscreen(w, 0); + } } void @@ -2172,7 +2000,7 @@ maximizenotify(struct wl_listener *listener, void *data) * protocol version * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ Client *c = wl_container_of(listener, c, maximize); - if (wl_resource_get_version(c->surface.xdg->resource) + if (wl_resource_get_version(c->surface.xdg->toplevel->resource) < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) wlr_xdg_surface_schedule_configure(c->surface.xdg); } @@ -2205,10 +2033,10 @@ motionabsolute(struct wl_listener *listener, void *data) * emits these events. */ struct wlr_pointer_motion_absolute_event *event = data; double lx, ly, dx, dy; + wlr_cursor_absolute_to_layout_coords(cursor, &event->pointer->base, event->x, event->y, &lx, &ly); dx = lx - cursor->x; dy = ly - cursor->y; - motionnotify(event->time_msec, &event->pointer->base, dx, dy, dx, dy); } @@ -2216,26 +2044,10 @@ void motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, double dx_unaccel, double dy_unaccel) { - double sx = 0, sy = 0; - double sx_confined, sy_confined; + double sx = 0, sy = 0, sx_confined, sy_confined; Client *c = NULL, *w = NULL; LayerSurface *l = NULL; - int type; struct wlr_surface *surface = NULL; - struct wlr_pointer_constraint_v1 *constraint = NULL; - - /* Find the client under the pointer and send the event along. */ - xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy); - - if (cursor_mode == CurPressed && !seat->drag) { - if ((type = toplevel_from_wlr_surface( - seat->pointer_state.focused_surface, &w, &l)) >= 0) { - c = w; - surface = seat->pointer_state.focused_surface; - sx = cursor->x - (type == LayerShell ? l->geom.x : w->geom.x); - sy = cursor->y - (type == LayerShell ? l->geom.y : w->geom.y); - } - } /* time is 0 in internal calls meant to restore pointer focus. */ if (time) { @@ -2243,24 +2055,24 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d relative_pointer_mgr, seat, (uint64_t)time * 1000, dx, dy, dx_unaccel, dy_unaccel); - wl_list_for_each(constraint, &pointer_constraints->constraints, link) - cursorconstrain(constraint); + if (active_constraint && cursor_mode != CurResize && cursor_mode != CurMove) { + toplevel_from_wlr_surface(active_constraint->surface, &c, NULL); + if (c && active_constraint->surface == seat->keyboard_state.focused_surface) { + sx = cursor->x - c->geom.x - c->bw + c->mon->m.x; + sy = cursor->y - c->geom.y - c->bw + c->mon->m.y; + if (wlr_region_confine(&active_constraint->region, sx, sy, + sx + dx, sy + dy, &sx_confined, &sy_confined)) { + dx = sx_confined - sx; + dy = sy_confined - sy; + } - if (active_constraint) { - constraint = active_constraint->constraint; - if (constraint->surface == surface - && wlr_region_confine(&active_confine, sx, sy, sx + dx, - sy + dy, &sx_confined, &sy_confined)) { - dx = sx_confined - sx; - dy = sy_confined - sy; - } else { - return; + if (active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) + return; } } - wlr_cursor_move(cursor, device, dx, dy); + wlr_cursor_move(cursor, device, dx, dy); wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); - restorecursor(); /* Update selmon (even while dragging a window) */ if (sloppyfocus) @@ -2282,6 +2094,17 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d return; } + /* Find the client under the pointer and send the event along. */ + xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy); + + if (cursor_mode == CurPressed && !seat->drag && surface != held_grab + && toplevel_from_wlr_surface(held_grab, &w, &l) >= 0) { + c = w; + surface = held_grab; + sx = cursor->x - (l ? l->geom.x : w->geom.x); + sy = cursor->y - (l ? l->geom.y : w->geom.y); + } + /* If there's no client surface under the cursor, set the cursor image to a * default. This is what makes the cursor image appear when you move it * off of a client or over its border. */ @@ -2405,22 +2228,14 @@ outputmgrtest(struct wl_listener *listener, void *data) outputmgrapplyortest(config, 1); } -void -pointerconstraintsetregion(struct wl_listener *listener, void *data) -{ - PointerConstraint *constraint = wl_container_of(listener, constraint, set_region); - active_confine_requires_warp = 1; - constraint->constraint->surface->data = NULL; -} - void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, uint32_t time) { struct timespec now; - int internal_call = !time; - if (sloppyfocus && !internal_call && c && !client_is_unmanaged(c)) + if (surface != seat->pointer_state.focused_surface && + sloppyfocus && time && c && !client_is_unmanaged(c)) focusclient(c, 0); /* If surface is NULL, clear pointer focus */ @@ -2429,7 +2244,7 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, return; } - if (internal_call) { + if (!time) { clock_gettime(CLOCK_MONOTONIC, &now); time = now.tv_sec * 1000 + now.tv_nsec / 1000000; } @@ -2437,24 +2252,18 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, /* Let the client know that the mouse cursor has entered one * of its surfaces, and make keyboard focus follow if desired. * wlroots makes this a no-op if surface is already focused */ - /* Don't show the cursor when calling motionnotify(0) to restore pointer - * focus. */ - if (!cursor_hidden) - wlr_seat_pointer_notify_enter(seat, surface, sx, sy); + wlr_seat_pointer_notify_enter(seat, surface, sx, sy); wlr_seat_pointer_notify_motion(seat, time, sx, sy); - } void printstatus(void) { Monitor *m = NULL; - wl_list_for_each(m, &mons, link) dwl_ipc_output_printstatus(m); } - void powermgrsetmodenotify(struct wl_listener *listener, void *data) { @@ -2482,9 +2291,10 @@ rendermon(struct wl_listener *listener, void *data) /* Render if no XDG clients have an outstanding resize and are visible on * this monitor. */ - wl_list_for_each(c, &clients, link) + 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; + } /* * HACK: The "correct" way to set the gamma is to commit it together with @@ -2495,7 +2305,8 @@ rendermon(struct wl_listener *listener, void *data) * the gamma can not be committed). */ if (m->gamma_lut_changed) { - gamma_control = wlr_gamma_control_manager_v1_get_control(gamma_control_mgr, m->wlr_output); + gamma_control + = wlr_gamma_control_manager_v1_get_control(gamma_control_mgr, m->wlr_output); m->gamma_lut_changed = 0; if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) @@ -2519,6 +2330,14 @@ skip: wlr_output_state_finish(&pending); } +void +requestdecorationmode(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, set_decoration_mode); + wlr_xdg_toplevel_decoration_v1_set_mode(c->decoration, + WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); +} + void requeststartdrag(struct wl_listener *listener, void *data) { @@ -2546,7 +2365,7 @@ resize(Client *c, struct wlr_box geo, int interact, int draw_borders) struct wlr_box clip; client_set_bounds(c, geo.width, geo.height); c->geom = geo; - c->bw = draw_borders ? borderpx : 0; + c->bw = draw_borders ? borderpx : 0; applybounds(c, bbox); /* Update scene-graph, including borders */ @@ -2613,8 +2432,6 @@ run(char *startup_cmd) wlr_cursor_warp_closest(cursor, NULL, cursor->x, cursor->y); wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); - wl_event_source_timer_update(hide_source, cursor_timeout * 1000); - /* Run the Wayland event loop. This does not return until you exit the * compositor. Starting the backend rigged up all of the necessary event * loop configuration to listen to libinput events, DRM events, generate @@ -2653,7 +2470,7 @@ setcursorshape(struct wl_listener *listener, void *data) * use the provided cursor shape. */ if (event->seat_client == seat->pointer_state.focused_client) wlr_cursor_set_xcursor(cursor, cursor_mgr, - wlr_cursor_shape_v1_name(event->shape)); + wlr_cursor_shape_v1_name(event->shape)); } void @@ -2662,13 +2479,10 @@ setfloating(Client *c, int floating) c->isfloating = floating; if (!c->mon) return; - wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen ? LyrFS : c->isfloating ? LyrFloat : LyrTile]); - - if (c->isfloating && !c->bw) - resize(c, c->mon->m, 0, 1); - + if (c->isfloating && !c->bw) + resize(c, c->mon->m, 0, 1); arrange(c->mon); printstatus(); } @@ -2788,6 +2602,9 @@ setsel(struct wl_listener *listener, void *data) void setup(void) { + struct xkb_context *context; + struct xkb_keymap *keymap; + int i, sig[] = {SIGCHLD, SIGINT, SIGTERM, SIGPIPE}; struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = handlesig}; sigemptyset(&sa.sa_mask); @@ -2810,6 +2627,7 @@ setup(void) /* Initialize the scene graph used to lay out windows */ scene = wlr_scene_create(); + root_bg = wlr_scene_rect_create(&scene->tree, 0, 0, rootcolor); for (i = 0; i < NUM_LAYERS; i++) layers[i] = wlr_scene_tree_create(&scene->tree); drag_icon = wlr_scene_tree_create(&scene->tree); @@ -2914,8 +2732,10 @@ setup(void) xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(dpy); LISTEN_STATIC(&xdg_decoration_mgr->events.new_toplevel_decoration, createdecoration); - hide_source = wl_event_loop_add_timer(wl_display_get_event_loop(dpy), - hidecursor, cursor); + pointer_constraints = wlr_pointer_constraints_v1_create(dpy); + LISTEN_STATIC(&pointer_constraints->events.new_constraint, createpointerconstraint); + + relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy); /* * Creates a cursor, which is a wlroots utility for tracking the cursor @@ -2956,7 +2776,6 @@ setup(void) * pointer, touch, and drawing tablet device. We also rig up a listener to * let us know when new input devices are available on the backend. */ - wl_list_init(&keyboards); LISTEN_STATIC(&backend->events.new_input, inputdevice); virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy); LISTEN_STATIC(&virtual_keyboard_mgr->events.new_virtual_keyboard, virtualkeyboard); @@ -2967,25 +2786,70 @@ setup(void) LISTEN_STATIC(&seat->events.request_start_drag, requeststartdrag); LISTEN_STATIC(&seat->events.start_drag, startdrag); + /* + * Configures a keyboard group, which will keep track of all connected + * keyboards, keep their modifier and LED states in sync, and handle + * keypresses + */ + kb_group.wlr_group = wlr_keyboard_group_create(); + kb_group.wlr_group->data = &kb_group; + + /* + * Virtual keyboards need to be in a different group + * https://codeberg.org/dwl/dwl/issues/554 + */ + vkb_group.wlr_group = wlr_keyboard_group_create(); + vkb_group.wlr_group->data = &vkb_group; + + /* Prepare an XKB keymap and assign it to the keyboard group. */ + context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules, + XKB_KEYMAP_COMPILE_NO_FLAGS))) + die("failed to compile keymap"); + + wlr_keyboard_set_keymap(&kb_group.wlr_group->keyboard, keymap); + wlr_keyboard_set_keymap(&vkb_group.wlr_group->keyboard, keymap); + xkb_keymap_unref(keymap); + xkb_context_unref(context); + + wlr_keyboard_set_repeat_info(&kb_group.wlr_group->keyboard, repeat_rate, repeat_delay); + wlr_keyboard_set_repeat_info(&vkb_group.wlr_group->keyboard, repeat_rate, repeat_delay); + + /* Set up listeners for keyboard events */ + LISTEN(&kb_group.wlr_group->keyboard.events.key, &kb_group.key, keypress); + LISTEN(&kb_group.wlr_group->keyboard.events.modifiers, &kb_group.modifiers, keypressmod); + LISTEN(&vkb_group.wlr_group->keyboard.events.key, &vkb_group.key, keypress); + LISTEN(&vkb_group.wlr_group->keyboard.events.modifiers, &vkb_group.modifiers, keypressmod); + + kb_group.key_repeat_source = wl_event_loop_add_timer( + wl_display_get_event_loop(dpy), keyrepeat, &kb_group); + vkb_group.key_repeat_source = wl_event_loop_add_timer( + wl_display_get_event_loop(dpy), keyrepeat, &vkb_group); + + /* A seat can only have one keyboard, but this is a limitation of the + * Wayland protocol - not wlroots. We assign all connected keyboards to the + * same wlr_keyboard_group, which provides a single wlr_keyboard interface for + * all of them. Set this combined wlr_keyboard as the seat keyboard. + */ + wlr_seat_set_keyboard(seat, &kb_group.wlr_group->keyboard); + output_mgr = wlr_output_manager_v1_create(dpy); LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply); LISTEN_STATIC(&output_mgr->events.test, outputmgrtest); - relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy); - pointer_constraints = wlr_pointer_constraints_v1_create(dpy); - LISTEN_STATIC(&pointer_constraints->events.new_constraint, createpointerconstraint); - wl_list_init(&pointer_constraint_commit.link); - wlr_scene_set_presentation(scene, wlr_presentation_create(dpy, backend)); wl_global_create(dpy, &zdwl_ipc_manager_v2_interface, 2, NULL, dwl_ipc_manager_bind); + /* 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 */ + unsetenv("DISPLAY"); #ifdef XWAYLAND /* * Initialise the XWayland X server. * It will be started when the first X client is started. */ - xwayland = wlr_xwayland_create(dpy, compositor, 1); - if (xwayland) { + if ((xwayland = wlr_xwayland_create(dpy, compositor, 1))) { LISTEN_STATIC(&xwayland->events.ready, xwaylandready); LISTEN_STATIC(&xwayland->events.new_surface, createnotifyx11); @@ -3015,7 +2879,6 @@ startdrag(struct wl_listener *listener, void *data) return; drag->icon->data = &wlr_scene_drag_icon_create(drag_icon, drag->icon)->node; - motionnotify(0, NULL, 0, 0, 0, 0); LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon); } @@ -3119,6 +2982,7 @@ togglebar(const Arg *arg) { zdwl_ipc_output_v2_send_toggle_visibility(ipc_output->resource); } + void togglefloating(const Arg *arg) { @@ -3157,9 +3021,8 @@ togglefullscreen(const Arg *arg) void toggleview(const Arg *arg) { - uint32_t newtagset = selmon ? selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK) : 0; - - if (!newtagset) + uint32_t newtagset; + if (!(newtagset = selmon ? selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK) : 0)) return; selmon->tagset[selmon->seltags] = newtagset; @@ -3202,10 +3065,10 @@ unmapnotify(struct wl_listener *listener, void *data) } if (client_is_unmanaged(c)) { - if (c == exclusive_focus) + if (c == exclusive_focus) { exclusive_focus = NULL; - if (client_surface(c) == seat->keyboard_state.focused_surface) focusclient(focustop(selmon, 0), 1); + } } else { wl_list_remove(&c->link); setmon(c, NULL, 0); @@ -3227,8 +3090,8 @@ updatemons(struct wl_listener *listener, void *data) * positions, focus, and the stored configuration in wlroots' * output-manager implementation. */ - struct wlr_output_configuration_v1 *config = - wlr_output_configuration_v1_create(); + struct wlr_output_configuration_v1 *config + = wlr_output_configuration_v1_create(); Client *c; struct wlr_output_configuration_head_v1 *config_head; Monitor *m; @@ -3242,17 +3105,21 @@ updatemons(struct wl_listener *listener, void *data) /* Remove this output from the layout to avoid cursor enter inside it */ wlr_output_layout_remove(output_layout, m->wlr_output); closemon(m); - memset(&m->m, 0, sizeof(m->m)); - memset(&m->w, 0, sizeof(m->w)); + m->m = m->w = (struct wlr_box){0}; } /* Insert outputs that need to */ - wl_list_for_each(m, &mons, link) + wl_list_for_each(m, &mons, link) { if (m->wlr_output->enabled && !wlr_output_layout_get(output_layout, m->wlr_output)) wlr_output_layout_add_auto(output_layout, m->wlr_output); + } + /* Now that we update the output layout we can get its box */ wlr_output_layout_get_box(output_layout, NULL, &sgeom); + wlr_scene_node_set_position(&root_bg->node, sgeom.x, sgeom.y); + wlr_scene_rect_set_size(root_bg, sgeom.width, sgeom.height); + /* Make sure the clients are hidden when dwl is locked */ wlr_scene_node_set_position(&locked_bg->node, sgeom.x, sgeom.y); wlr_scene_rect_set_size(locked_bg, sgeom.width, sgeom.height); @@ -3273,8 +3140,7 @@ updatemons(struct wl_listener *listener, void *data) if (m->lock_surface) { struct wlr_scene_tree *scene_tree = m->lock_surface->surface->data; wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y); - wlr_session_lock_surface_v1_configure(m->lock_surface, m->m.width, - m->m.height); + wlr_session_lock_surface_v1_configure(m->lock_surface, m->m.width, m->m.height); } /* Calculate the effective monitor geometry to use for clients */ @@ -3283,19 +3149,21 @@ updatemons(struct wl_listener *listener, void *data) arrange(m); /* make sure fullscreen clients have the right size */ if ((c = focustop(m, 0)) && c->isfullscreen) - resize(c, m->m, 0, 0); + resize(c, m->m, 0, 1); + /* Try to re-set the gamma LUT when updating monitors, + * it's only really needed when enabling a disabled output, but meh. */ m->gamma_lut_changed = 1; - config_head->state.enabled = 1; - config_head->state.mode = m->wlr_output->current_mode; + config_head->state.x = m->m.x; config_head->state.y = m->m.y; } if (selmon && selmon->wlr_output->enabled) { - wl_list_for_each(c, &clients, link) + wl_list_for_each(c, &clients, link) { if (!c->mon && client_surface(c)->mapped) setmon(c, selmon, c->tags); + } focusclient(focustop(selmon, 0), 1); if (selmon->lock_surface) { client_notify_enter(selmon->lock_surface->surface, @@ -3331,10 +3199,11 @@ urgent(struct wl_listener *listener, void *data) if (!c || c == focustop(selmon, 0)) return; - if (client_surface(c)->mapped) - client_set_border_color(c, urgentcolor); c->isurgent = 1; printstatus(); + + if (client_surface(c)->mapped) + client_set_border_color(c, urgentcolor); } void @@ -3354,7 +3223,12 @@ void virtualkeyboard(struct wl_listener *listener, void *data) { struct wlr_virtual_keyboard_v1 *keyboard = data; - createkeyboard(&keyboard->keyboard); + /* Set the keymap to match the group keymap */ + wlr_keyboard_set_keymap(&keyboard->keyboard, vkb_group.wlr_group->keyboard.keymap); + wlr_keyboard_set_repeat_info(&keyboard->keyboard, repeat_rate, repeat_delay); + + /* Add the new keyboard to the group */ + wlr_keyboard_group_add_keyboard(vkb_group.wlr_group, &keyboard->keyboard); } Monitor * @@ -3405,12 +3279,13 @@ zoom(const Arg *arg) /* Search for the first tiled window that is not sel, marking sel as * NULL if we pass it along the way */ - wl_list_for_each(c, &clients, link) + wl_list_for_each(c, &clients, link) { if (VISIBLEON(c, selmon) && !c->isfloating) { if (c != sel) break; sel = NULL; } + } /* Return if no other tiled window was found */ if (&c->link == &clients) @@ -3423,6 +3298,7 @@ zoom(const Arg *arg) wl_list_remove(&sel->link); wl_list_insert(&clients, &sel->link); + focusclient(sel, 1); arrange(selmon); } @@ -3435,8 +3311,7 @@ rotatetags(const Arg *arg) bool shift = false; switch(abs(i)) { - default: - break; + default: break; case SHIFT_R: shift = true; break; @@ -3462,7 +3337,7 @@ activatex11(struct wl_listener *listener, void *data) Client *c = wl_container_of(listener, c, activate); /* Only "managed" windows can be activated */ - if (c->type == X11Managed) + if (!client_is_unmanaged(c)) wlr_xwayland_surface_activate(c->surface.xwayland, 1); } @@ -3482,9 +3357,9 @@ configurex11(struct wl_listener *listener, void *data) struct wlr_xwayland_surface_configure_event *event = data; if (!c->mon) return; - if (c->isfloating || c->type == X11Unmanaged) + if (c->isfloating || client_is_unmanaged(c)) resize(c, (struct wlr_box){.x = event->x, .y = event->y, - .width = event->width, .height = event->height}, 0, 1); + .width = event->width, .height = event->height}, 0, 1); else arrange(c->mon); } @@ -3498,18 +3373,18 @@ createnotifyx11(struct wl_listener *listener, void *data) /* Allocate a Client for this surface */ c = xsurface->data = ecalloc(1, sizeof(*c)); c->surface.xwayland = xsurface; - c->type = xsurface->override_redirect ? X11Unmanaged : X11Managed; + c->type = X11; c->bw = borderpx; /* Listen to the various events it can emit */ LISTEN(&xsurface->events.associate, &c->associate, associatex11); + LISTEN(&xsurface->events.destroy, &c->destroy, destroynotify); LISTEN(&xsurface->events.dissociate, &c->dissociate, dissociatex11); LISTEN(&xsurface->events.request_activate, &c->activate, activatex11); LISTEN(&xsurface->events.request_configure, &c->configure, configurex11); + LISTEN(&xsurface->events.request_fullscreen, &c->fullscreen, fullscreennotify); LISTEN(&xsurface->events.set_hints, &c->set_hints, sethints); LISTEN(&xsurface->events.set_title, &c->set_title, updatetitle); - LISTEN(&xsurface->events.destroy, &c->destroy, destroynotify); - LISTEN(&xsurface->events.request_fullscreen, &c->fullscreen, fullscreennotify); } void @@ -3542,11 +3417,10 @@ sethints(struct wl_listener *listener, void *data) return; c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints); + printstatus(); if (c->isurgent && surface && surface->mapped) client_set_border_color(c, urgentcolor); - - printstatus(); } void