24 Commits
Author SHA1 Message Date
julmajustus d16036e690 Fix input_method_relay double allocation
- input_method_relay is allocated inside input_method_relay_create(), which leads the extra allocation to leak memory.
2026-09-18 22:09:39 +03:00
save196 45054a34a4 always apply a minimum size of 1x1
Resizing with the mouse can make the size negative, which makes dwl
crash, and the minimum is 1 because 0 makes some X11 clients crash.
2026-09-18 10:37:27 +02:00
save196 669b26d0a9 let the focus leave an unmanaged surface
Unmanaged clients spawned by a parent window, like menus, would not
close if the user changed workspace or focused another client while the
menu was selected, because the parent was never told that it lost the
focus. This remembers the last managed client and deactivates it on a
focus change, so that it closes the menu itself.

This also fixes a regression introduced by f6e3a28, where menus would
close immediately after being opened.
2026-09-18 09:17:40 +02:00
save196 e451862544 follow the geometry of unmanaged clients and fix formatting 2026-09-17 17:35:49 +00:00
save196 b3223b15e9 fix capturing hidden toplevels 2026-09-17 16:46:41 +00:00
Guido Cella 1fe1b4e66e show popups in fullscreen Xwayland apps
The dialogs opened by clicking items in Xwyland applications' toolbars
are unmanaged clients, so placing them in LyrFloat drew them below their
source client in LyrFS when it is fullscreened.

Add a new dedicated layer for unamanged clients to fix it. Don't put
them in LyrFS to prevent the regular fullscreen client from hiding
unmanaged ones, e.g. by opening dmenu and then clicking the regular
client.

Fixes https://codeberg.org/dwl/dwl/issues/556
2026-09-16 12:47:52 +02:00
Guido Cella 1dae5ba3ed size hints can be -1 2026-09-16 11:50:36 +02:00
Guido Cella 433c325fb2 fix a use-after-free
Prevent a use-after-free when closing a nested dwl with Ctrl+c.

It happened because cleanup() → input_method_relay_finish() frees
input_method_relay, and then closemon() → focusclient() →
input_method_relay_set_focus() uses relay->surface.
2026-09-15 23:29:50 +02:00
Guido Cella 6013835337 keep the focused border color of the old client in certain cases
When focusing an unmanaged client (like dmenu) or a layer shell surface
(like wmenu) keep the focused border color of the regular client, but
still deactivate it. This is consistent with dwm and sway.

This was already done for unmanaged clients but not for layer shell
surfaces.

This is not perfect because you can press MODKEY+j/k or move the cursor
while a program like dmenu is focused and have 2 clients with focused
border color after closing dmenu. But this doesn't work well even in
sway where if you change focus while dmenu is open you can longer focus
it, and after closing a layer shell surface with on-demand keyboard
interactivity all toplevels have the focused border color, so this will
do for now.
2026-09-15 22:17:24 +02:00
Guido Cella f6e3a2826e deactivate the old client when the new one wants focus
I see no issue with winecfg after removing this condition, so I assume
it was fixed upstream.
2026-09-15 22:16:35 +02:00
Guido Cella aa0c1659d3 Reapply "use modifier-independent key symbols in keybindings"
This reverts commit 0f8630282d.
2026-09-15 21:23:47 +02:00
Guido Cella bab3fdadf3 don't run keybindings twice when the shift keysym is the same
For keys like F1-F12 the keysym with the shift is the same as without.
Avoid running keybindings bound to these keys twice.
2026-09-15 21:22:41 +02:00
Guido Cella 0f8630282d Revert "use modifier-independent key symbols in keybindings"
This reverts commit f9c644f0b0.
2026-09-15 20:57:28 +02:00
Guido Cella 4de32d2d5c Reapply "respect size hints"
This reverts commit 31bf1cbaf6.

Fixes https://codeberg.org/dwl/dwl/issues/105
Fixes https://codeberg.org/dwl/dwl/issues/634
Fixes https://codeberg.org/dwl/dwl/issues/1171
2026-09-15 20:03:28 +02:00
Guido Cella 36c80aff03 Revert "don't resize on commit"
This reverts commit 498a0e605c.

Without resizing on every commit the top left of firefox is cropped.
2026-09-15 15:24:45 +02:00
Guido Cella 68c6319e3f Revert "reduce nesting in commitnotify()"
This reverts commit c824886b8c.
2026-09-15 15:24:27 +02:00
Guido Cella c824886b8c reduce nesting in commitnotify() 2026-09-15 12:07:13 +02:00
Guido Cella 498a0e605c don't resize on commit
Fixes https://codeberg.org/dwl/dwl/issues/1229
Reverts 32e66f4

This could be called only on the initial_commit, but it was added to fix
https://github.com/djpohly/dwl/issues/515 which I can't reproduce after
removing it, so let's try to remove it completely.
2026-09-15 12:06:35 +02:00
Guido Cella fec0699985 make client_set_size() return void
The return value is unused since the previous commit.
2026-09-15 12:06:32 +02:00
Guido Cella e7f185ed3e remove c->resize
Unused since 70c33c0.
2026-09-15 09:49:25 +02:00
Andrea ChiavazzaandGuido Cella f9c644f0b0 use modifier-independent key symbols in keybindings
Co-authored-by: Guido Cella <guido@guidocella.xyz>
2026-09-14 17:53:35 +02:00
Leonardo Hernández Hernández dc9a860191 use the new output swapchain manager in outputmgrapplyortest() 2026-09-14 10:45:33 +02:00
Guido Cella d1ebf3c985 fix firefox opening with rounded corners
Fixes https://codeberg.org/dwl/dwl/issues/1169

The c->isfloating check is needed to have the correct floating window
size with e.g. alacritty -o window.dimensions.columns=15
window.dimensions.lines=10.
2026-09-14 10:42:59 +02:00
sewn 8f52e96be4 replicate dwm behavior for snap resize 2026-09-14 02:25:48 +02:00
5 changed files with 217 additions and 131 deletions
+37 -17
View File
@@ -15,6 +15,32 @@ client_is_x11(Client *c)
return 0;
}
static inline void
client_get_size_hints(Client *c, struct wlr_box *max, struct wlr_box *min)
{
struct wlr_xdg_toplevel_state *state;
#ifdef XWAYLAND
if (client_is_x11(c)) {
xcb_size_hints_t *size_hints = c->surface.xwayland->size_hints;
if (size_hints) {
max->width = size_hints->max_width;
max->height = size_hints->max_height;
min->width = size_hints->min_width;
min->height = size_hints->min_height;
}
return;
}
#endif
state = &c->surface.xdg->toplevel->current;
max->width = state->max_width;
max->height = state->max_height;
min->width = state->min_width;
min->height = state->min_height;
}
static inline struct wlr_surface *
client_surface(Client *c)
{
@@ -206,13 +232,12 @@ client_get_title(Client *c)
static inline int
client_is_float_type(Client *c)
{
struct wlr_xdg_toplevel *toplevel;
struct wlr_xdg_toplevel_state state;
struct wlr_box min = {0}, max = {0};
client_get_size_hints(c, &max, &min);
#ifdef XWAYLAND
if (client_is_x11(c)) {
struct wlr_xwayland_surface *surface = c->surface.xwayland;
xcb_size_hints_t *size_hints = surface->size_hints;
if (surface->modal)
return 1;
@@ -223,17 +248,13 @@ client_is_float_type(Client *c)
return 1;
}
return size_hints && size_hints->min_width > 0 && size_hints->min_height > 0
&& (size_hints->max_width == size_hints->min_width
|| size_hints->max_height == size_hints->min_height);
return min.width > 0 && min.height > 0 &&
(min.width == max.width || min.height == max.height);
}
#endif
toplevel = c->surface.xdg->toplevel;
state = toplevel->current;
return toplevel->parent || (state.min_width != 0 && state.min_height != 0
&& (state.min_width == state.max_width
|| state.min_height == state.max_height));
return c->surface.xdg->toplevel->parent || (min.width > 0 && min.height > 0 &&
(min.width == max.width || min.height == max.height));
}
static inline int
@@ -344,20 +365,19 @@ client_set_scale(struct wlr_surface *s, float scale)
wlr_surface_set_preferred_buffer_scale(s, (int32_t)ceilf(scale));
}
static inline uint32_t
static inline void
client_set_size(Client *c, uint32_t width, uint32_t height)
{
#ifdef XWAYLAND
if (client_is_x11(c)) {
wlr_xwayland_surface_configure(c->surface.xwayland,
c->geom.x + c->bw, c->geom.y + c->bw, width, height);
return 0;
return;
}
#endif
if ((int32_t)width == c->surface.xdg->toplevel->current.width
&& (int32_t)height == c->surface.xdg->toplevel->current.height)
return 0;
return wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, (int32_t)width, (int32_t)height);
if ((int32_t)width != c->surface.xdg->toplevel->current.width
|| (int32_t)height != c->surface.xdg->toplevel->current.height)
wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, (int32_t)width, (int32_t)height);
}
static inline void
+18 -18
View File
@@ -7,6 +7,7 @@
static const int sloppyfocus = 1; /* focus follows mouse */
static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */
static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int snap = 32; /* snap pixel */
static const float rootcolor[] = COLOR(0x222222ff);
static const float bordercolor[] = COLOR(0x444444ff);
static const float focuscolor[] = COLOR(0x005577ff);
@@ -105,11 +106,11 @@ static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TA
/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */
#define MODKEY WLR_MODIFIER_ALT
#define TAGKEYS(KEY,SKEY,TAG) \
#define TAGKEYS(KEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} }
{ MODKEY|WLR_MODIFIER_SHIFT, KEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,KEY,toggletag, {.ui = 1 << TAG} }
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
@@ -119,7 +120,6 @@ static const char *termcmd[] = { "foot", NULL };
static const char *menucmd[] = { "wmenu-run", NULL };
static const Key keys[] = {
/* Note that Shift changes certain key codes: 2 -> at, etc. */
/* modifier key function argument */
{ MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} },
@@ -139,28 +139,28 @@ static const Key keys[] = {
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} },
{ MODKEY, XKB_KEY_0, view, {.ui = ~0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_0, tag, {.ui = ~0} },
{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} },
TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3),
TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4),
TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5),
TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6),
TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7),
TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_comma, tagmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_period, tagmon, {.i = WLR_DIRECTION_RIGHT} },
TAGKEYS( XKB_KEY_1, 0),
TAGKEYS( XKB_KEY_2, 1),
TAGKEYS( XKB_KEY_3, 2),
TAGKEYS( XKB_KEY_4, 3),
TAGKEYS( XKB_KEY_5, 4),
TAGKEYS( XKB_KEY_6, 5),
TAGKEYS( XKB_KEY_7, 6),
TAGKEYS( XKB_KEY_8, 7),
TAGKEYS( XKB_KEY_9, 8),
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_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} },
{ WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_BackSpace, quit, {0} },
/* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is
* do not remove them.
*/
#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} }
#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_F##n, chvt, {.ui = (n)} }
CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6),
CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
};
+149 -95
View File
@@ -3,6 +3,7 @@
*/
#include <getopt.h>
#include <libinput.h>
#include <limits.h>
#include <linux/input-event-codes.h>
#include <math.h>
#include <signal.h>
@@ -15,6 +16,7 @@
#include <wlr/backend.h>
#include <wlr/backend/libinput.h>
#include <wlr/render/allocator.h>
#include <wlr/render/swapchain.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_alpha_modifier_v1.h>
#include <wlr/types/wlr_compositor.h>
@@ -42,6 +44,7 @@
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output_management_v1.h>
#include <wlr/types/wlr_output_power_management_v1.h>
#include <wlr/types/wlr_output_swapchain_manager.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_presentation_time.h>
@@ -85,7 +88,8 @@
/* enums */
enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */
enum { XDGShell, LayerShell, X11 }; /* client types */
enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrIMPopup, LyrBlock, NUM_LAYERS }; /* scene layers */
enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrUnmanaged, LyrOverlay,
LyrIMPopup, LyrBlock, NUM_LAYERS }; /* scene layers */
enum { AxisUp, AxisRight, AxisDown, AxisLeft };
typedef union {
@@ -149,12 +153,12 @@ typedef struct {
struct wl_listener associate;
struct wl_listener dissociate;
struct wl_listener configure;
struct wl_listener set_geometry;
struct wl_listener set_hints;
#endif
unsigned int bw;
uint32_t tags;
int isfloating, isurgent, isfullscreen;
uint32_t resize; /* configure serial of a pending resize */
} Client;
typedef struct {
@@ -168,9 +172,8 @@ typedef struct {
struct wlr_keyboard_group *wlr_group;
struct wlr_keyboard *virtual_keyboard;
int nsyms;
const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */
uint32_t mods; /* invalid if nsyms == 0 */
xkb_keysym_t keysyms[2];
uint32_t mods;
struct wl_event_source *key_repeat_source;
struct wl_listener modifiers;
@@ -374,6 +377,7 @@ static void zoom(const Arg *arg);
static pid_t child_pid = -1;
static int locked;
static void *exclusive_focus;
static Client *focused_client;
static struct wl_display *dpy;
static struct wl_event_loop *event_loop;
static struct wlr_backend *backend;
@@ -464,6 +468,7 @@ static void associatex11(struct wl_listener *listener, void *data);
static void configurex11(struct wl_listener *listener, void *data);
static void createnotifyx11(struct wl_listener *listener, void *data);
static void dissociatex11(struct wl_listener *listener, void *data);
static void setgeometryx11(struct wl_listener *listener, void *data);
static void sethints(struct wl_listener *listener, void *data);
static void xwaylandready(struct wl_listener *listener, void *data);
static struct wl_listener new_xwayland_surface = {.notify = createnotifyx11};
@@ -483,10 +488,23 @@ static struct wlr_xwayland *xwayland;
void
applybounds(Client *c, struct wlr_box *bbox)
{
/* set minimum possible */
/* set minimum client size to 1 */
c->geom.width = MAX(1 + 2 * (int)c->bw, c->geom.width);
c->geom.height = MAX(1 + 2 * (int)c->bw, c->geom.height);
if (!c->isfullscreen) {
struct wlr_box min = {0}, max = {0};
client_get_size_hints(c, &max, &min);
c->geom.width = MAX(min.width + 2 * (int)c->bw, c->geom.width);
c->geom.height = MAX(min.height + 2 * (int)c->bw, c->geom.height);
/* Some clients set their max size to INT_MAX, which does not violate the
* protocol but it's unnecesary, as they can set their max size to zero. */
if (max.width > 0 && 2 * (int)c->bw <= INT_MAX - max.width) /* Checks for overflow */
c->geom.width = MIN(max.width + 2 * (int)c->bw, c->geom.width);
if (max.height > 0 && 2 * (int)c->bw <= INT_MAX - max.height) /* Checks for overflow */
c->geom.height = MIN(max.height + 2 * (int)c->bw, c->geom.height);
}
if (c->geom.x >= bbox->x + bbox->width)
c->geom.x = bbox->x + bbox->width - c->geom.width;
if (c->geom.y >= bbox->y + bbox->height)
@@ -538,7 +556,9 @@ arrange(Monitor *m)
wl_list_for_each(c, &clients, link) {
if (c->mon == m) {
wlr_scene_node_set_enabled(&c->scene->node, VISIBLEON(c, m));
client_set_suspended(c, !VISIBLEON(c, m));
client_set_suspended(c, !VISIBLEON(c, m)
&& (!c->image_capture_source
|| wl_list_empty(&c->image_capture_source->resources)));
}
}
@@ -738,6 +758,8 @@ capturerequest(struct wl_listener *listener, void *data)
wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept(
request, view->image_capture_source);
client_set_suspended(view, 0);
}
void
@@ -782,6 +804,7 @@ cleanup(void)
destroykeyboardgroup(&kb_group->destroy, NULL);
input_method_relay_finish(input_method_relay);
input_method_relay = NULL;
/* 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 */
@@ -951,14 +974,13 @@ commitnotify(struct wl_listener *listener, void *data)
if (c->decoration)
requestdecorationmode(&c->set_decoration_mode, c->decoration);
wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, 0, 0);
if (!c->isfloating)
client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT |
WLR_EDGE_RIGHT);
return;
}
resize(c, c->geom, (c->isfloating && !c->isfullscreen));
/* mark a pending resize as completed */
if (c->resize && c->resize <= c->surface.xdg->current.configure_serial)
c->resize = 0;
}
void
@@ -1415,6 +1437,7 @@ destroynotify(struct wl_listener *listener, void *data)
wl_list_remove(&c->associate.link);
wl_list_remove(&c->configure.link);
wl_list_remove(&c->dissociate.link);
wl_list_remove(&c->set_geometry.link);
wl_list_remove(&c->set_hints.link);
} else
#endif
@@ -1505,6 +1528,9 @@ focusclient(Client *c, int lift)
wlr_xdg_popup_destroy(popup);
}
if (old_c && old_c == exclusive_focus && client_wants_focus(old_c))
exclusive_focus = NULL;
/* Put the new client atop the focus stack and select its monitor */
if (c && !client_is_unmanaged(c)) {
wl_list_remove(&c->flink);
@@ -1518,25 +1544,23 @@ focusclient(Client *c, int lift)
client_set_border_color(c, focuscolor);
}
/* Deactivate old client if focus is changing */
if (old && (!c || client_surface(c) != old)) {
/* If an overlay is focused, don't focus or activate the client,
* but only update its position in fstack to render its border with focuscolor
* 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.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) {
return;
} else if (old_c && old_c == exclusive_focus && client_wants_focus(old_c)) {
return;
/* Don't deactivate old client if the new one wants focus, as this causes issues with winecfg
* and probably other clients */
} else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) {
client_set_border_color(old_c, bordercolor);
/* If an overlay is focused, don't focus or activate the client,
* but only update its position in fstack to render its border with focuscolor
* and focus it after the overlay is closed. */
if (old && (!c || client_surface(c) != old)
&& 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.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE)
return;
client_activate_surface(old, 0);
}
if (focused_client && focused_client != c && !(c && client_is_unmanaged(c))) {
struct wlr_surface *s = client_surface(focused_client);
if (c)
client_set_border_color(focused_client, bordercolor);
if (s && s->mapped)
client_activate_surface(s, 0);
focused_client = NULL;
}
printstatus();
@@ -1557,6 +1581,9 @@ focusclient(Client *c, int lift)
/* Activate the new client */
client_activate_surface(client_surface(c), 1);
if (!client_is_unmanaged(c))
focused_client = c;
}
void
@@ -1701,11 +1728,8 @@ keybinding(uint32_t mods, xkb_keysym_t sym)
*/
const Key *k;
for (k = keys; k < END(keys); k++) {
if (CLEANMASK(mods) == CLEANMASK(k->mod)
&& xkb_keysym_to_lower(sym) == xkb_keysym_to_lower(k->keysym)
&& k->func) {
if (CLEANMASK(mods) == CLEANMASK(k->mod) && sym == k->keysym && k->func)
return k;
}
}
return NULL;
}
@@ -1713,46 +1737,44 @@ keybinding(uint32_t mods, xkb_keysym_t sym)
void
keypress(struct wl_listener *listener, void *data)
{
int i;
/* This event is raised when a key is pressed or released. */
KeyboardGroup *group = wl_container_of(listener, group, key);
struct wlr_keyboard_key_event *event = data;
/* Translate libinput keycode -> xkbcommon */
uint32_t keycode = event->keycode + 8;
/* Get a list of keysyms based on the keymap for this keyboard */
const xkb_keysym_t *syms;
int nsyms = xkb_state_key_get_syms(
group->wlr_group->keyboard.xkb_state, keycode, &syms);
struct wlr_keyboard *kb = &group->wlr_group->keyboard;
xkb_layout_index_t layout = xkb_state_key_get_layout(kb->xkb_state, keycode);
int handled = 0;
uint32_t mods = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard);
// Get the keysyms for level 0 (normal) and level 1 (shifted)
// Only one keysym for each level:
// multiple keysyms per keycode don't really exist in the real world
for (int i = 0; i < 2; i++)
group->keysyms[i] = keymap_get_one_sym_by_level(kb->keymap, keycode, layout, i);
group->mods = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard);
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
/* 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++) {
const Key *key = keybinding(mods, syms[i]);
for (int i = 0; i < 2; i++) {
const Key *key = keybinding(group->mods, group->keysyms[i]);
if (key) {
consumed[event->keycode] = 1;
key->func(&key->arg);
handled = 1;
break;
}
}
}
if (handled && group->wlr_group->keyboard.repeat_info.delay > 0) {
group->mods = mods;
group->keysyms = syms;
group->nsyms = nsyms;
if (handled && group->wlr_group->keyboard.repeat_info.delay > 0)
wl_event_source_timer_update(group->key_repeat_source,
group->wlr_group->keyboard.repeat_info.delay);
} else {
group->nsyms = 0;
else
wl_event_source_timer_update(group->key_repeat_source, 0);
}
if (handled || input_method_keyboard_grab_forward_key(group, event))
return;
@@ -1788,17 +1810,18 @@ int
keyrepeat(void *data)
{
KeyboardGroup *group = data;
int i;
if (!group->nsyms || group->wlr_group->keyboard.repeat_info.rate <= 0)
if (group->wlr_group->keyboard.repeat_info.rate <= 0)
return 0;
wl_event_source_timer_update(group->key_repeat_source,
1000 / group->wlr_group->keyboard.repeat_info.rate);
for (i = 0; i < group->nsyms; i++) {
for (int i = 0; i < 2; i++) {
const Key *key = keybinding(group->mods, group->keysyms[i]);
if (key)
if (key) {
key->func(&key->arg);
break;
}
}
return 0;
@@ -1864,9 +1887,8 @@ mapnotify(struct wl_listener *listener, void *data)
/* Handle unmanaged clients first so we can return prior create borders */
if (client_is_unmanaged(c)) {
/* Unmanaged clients always are floating */
wlr_scene_node_reparent(&c->scene->node, layers[LyrFloat]);
wlr_scene_node_reparent(&c->scene->node, layers[LyrUnmanaged]);
wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
client_set_size(c, c->geom.width, c->geom.height);
if (client_wants_focus(c)) {
focusclient(c, 1);
exclusive_focus = c;
@@ -1881,7 +1903,6 @@ mapnotify(struct wl_listener *listener, void *data)
}
/* Initialize client geometry with room for border */
client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT);
c->geom.width += 2 * c->bw;
c->geom.height += 2 * c->bw;
@@ -1981,6 +2002,7 @@ void
motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy,
double dx_unaccel, double dy_unaccel)
{
int nx, ny;
double sx = 0, sy = 0, sx_confined, sy_confined;
Client *c = NULL, *w = NULL;
LayerSurface *l = NULL, *focusedl = NULL;
@@ -2038,7 +2060,17 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
/* If we are currently grabbing the mouse, handle and return */
if (cursor_mode == CurMove) {
/* Move the grabbed client to the new position. */
resize(grabc, (struct wlr_box){.x = (int)round(cursor->x) - grabcx, .y = (int)round(cursor->y) - grabcy,
nx = (int)round(cursor->x) - grabcx;
ny = (int)round(cursor->y) - grabcy;
if (abs(selmon->w.x - nx) < (int)snap)
nx = selmon->w.x;
else if (abs((selmon->w.x + selmon->w.width) - (nx + grabc->geom.width)) < (int)snap)
nx = selmon->w.x + selmon->w.width - grabc->geom.width;
if (abs(selmon->w.y - ny) < (int)snap)
ny = selmon->w.y;
else if (abs((selmon->w.y + selmon->w.height) - (ny + grabc->geom.height)) < (int)snap)
ny = selmon->w.y + selmon->w.height - grabc->geom.height;
resize(grabc, (struct wlr_box){.x = nx, .y = ny,
.width = grabc->geom.width, .height = grabc->geom.height}, 1);
return;
} else if (cursor_mode == CurResize) {
@@ -2116,49 +2148,61 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test)
* output_layout.change event, not here.
*/
struct wlr_output_configuration_head_v1 *config_head;
int ok = 1;
size_t states_len, i;
struct wlr_output_swapchain_manager swapchain_manager;
struct wlr_backend_output_state *states =
wlr_output_configuration_v1_build_state(config, &states_len);
int ok = 0;
if (!states) {
wlr_output_configuration_v1_send_failed(config);
return;
}
wlr_output_swapchain_manager_init(&swapchain_manager, backend);
ok = wlr_output_swapchain_manager_prepare(&swapchain_manager, states, states_len);
if (!ok || test)
goto out;
for (i = 0; i < states_len; i++) {
struct wlr_swapchain *swapchain = wlr_output_swapchain_manager_get_swapchain(
&swapchain_manager, states[i].output);
if (swapchain && !states[i].output->enabled)
wlr_output_state_set_buffer(&states[i].base, wlr_swapchain_acquire(swapchain));
}
if (!(ok = wlr_backend_commit(backend, states, states_len)))
goto out;
wlr_output_swapchain_manager_apply(&swapchain_manager);
wl_list_for_each(config_head, &config->heads, link) {
struct wlr_output *wlr_output = config_head->state.output;
Monitor *m = wlr_output->data;
struct wlr_output_state state;
/* Ensure displays previously disabled by wlr-output-power-management-v1
* are properly handled*/
m->asleep = 0;
wlr_output_state_init(&state);
wlr_output_state_set_enabled(&state, config_head->state.enabled);
if (!config_head->state.enabled)
goto apply_or_test;
if (config_head->state.mode)
wlr_output_state_set_mode(&state, config_head->state.mode);
else
wlr_output_state_set_custom_mode(&state,
config_head->state.custom_mode.width,
config_head->state.custom_mode.height,
config_head->state.custom_mode.refresh);
wlr_output_state_set_transform(&state, config_head->state.transform);
wlr_output_state_set_scale(&state, config_head->state.scale);
wlr_output_state_set_adaptive_sync_enabled(&state,
config_head->state.adaptive_sync_enabled);
apply_or_test:
ok &= test ? wlr_output_test_state(wlr_output, &state)
: wlr_output_commit_state(wlr_output, &state);
/* Don't move monitors if position wouldn't change. This avoids
* wlroots marking the output as manually configured.
* wlr_output_layout_add does not like disabled outputs */
if (!test && wlr_output->enabled && (m->m.x != config_head->state.x || m->m.y != config_head->state.y))
if (config_head->state.enabled
&& (m->m.x != config_head->state.x || m->m.y != config_head->state.y
|| !wlr_output_layout_get(output_layout, wlr_output))) {
wlr_output_layout_add(output_layout, wlr_output,
config_head->state.x, config_head->state.y);
wlr_output_state_finish(&state);
}
}
out:
wlr_output_swapchain_manager_finish(&swapchain_manager);
for (i = 0; i < states_len; i++)
wlr_output_state_finish(&states[i].base);
free(states);
if (ok)
wlr_output_configuration_v1_send_succeeded(config);
else
@@ -2343,9 +2387,7 @@ resize(Client *c, struct wlr_box geo, int interact)
wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw);
wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw);
/* this is a no-op if size hasn't changed */
c->resize = client_set_size(c, c->geom.width - 2 * c->bw,
c->geom.height - 2 * c->bw);
client_set_size(c, c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw);
client_get_clip(c, &clip);
wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip);
}
@@ -2772,7 +2814,6 @@ setup(void)
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,
@@ -2948,6 +2989,8 @@ unmapnotify(struct wl_listener *listener, void *data)
cursor_mode = CurNormal;
grabc = NULL;
}
if (c == focused_client)
focused_client = NULL;
if (client_is_unmanaged(c)) {
if (c == exclusive_focus) {
@@ -2972,6 +3015,8 @@ unmapnotify(struct wl_listener *listener, void *data)
#endif
wlr_scene_node_destroy(&c->image_capture_scene->tree.node);
c->image_capture_scene = NULL;
c->image_capture_source = NULL;
wlr_scene_node_destroy(&c->scene->node);
client_surface(c)->data = NULL;
printstatus();
@@ -3248,13 +3293,8 @@ configurex11(struct wl_listener *listener, void *data)
{
Client *c = wl_container_of(listener, c, configure);
struct wlr_xwayland_surface_configure_event *event = data;
if (!client_surface(c) || !client_surface(c)->mapped) {
wlr_xwayland_surface_configure(c->surface.xwayland,
event->x, event->y, event->width, event->height);
return;
}
if (client_is_unmanaged(c)) {
wlr_scene_node_set_position(&c->scene->node, event->x, event->y);
if (!client_surface(c) || !client_surface(c)->mapped
|| client_is_unmanaged(c)) {
wlr_xwayland_surface_configure(c->surface.xwayland,
event->x, event->y, event->width, event->height);
return;
@@ -3287,6 +3327,7 @@ createnotifyx11(struct wl_listener *listener, void *data)
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_geometry, &c->set_geometry, setgeometryx11);
LISTEN(&xsurface->events.set_hints, &c->set_hints, sethints);
LISTEN(&xsurface->events.set_title, &c->set_title, updatetitle);
}
@@ -3299,6 +3340,19 @@ dissociatex11(struct wl_listener *listener, void *data)
wl_list_remove(&c->unmap.link);
}
void
setgeometryx11(struct wl_listener *listener, void *data)
{
Client *c = wl_container_of(listener, c, set_geometry);
if (!client_is_unmanaged(c) || !client_surface(c)
|| !client_surface(c)->mapped)
return;
client_get_geometry(c, &c->geom);
wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
}
void
sethints(struct wl_listener *listener, void *data)
{
+1 -1
View File
@@ -625,7 +625,7 @@ void
input_method_relay_set_focus(struct input_method_relay *relay,
struct wlr_surface *surface)
{
if (relay->focused_surface == surface)
if (!relay || relay->focused_surface == surface)
return;
if (relay->focused_surface)
+12
View File
@@ -4,6 +4,7 @@
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <xkbcommon/xkbcommon.h>
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#define MIN(A, B) ((A) < (B) ? (A) : (B))
@@ -13,6 +14,8 @@
static void die(const char *fmt, ...);
static void *ecalloc(size_t nmemb, size_t size);
static int fd_set_nonblock(int fd);
xkb_keysym_t keymap_get_one_sym_by_level(struct xkb_keymap *keymap,
xkb_keycode_t key, xkb_layout_index_t layout, xkb_level_index_t level);
void
die(const char *fmt, ...) {
@@ -56,3 +59,12 @@ fd_set_nonblock(int fd) {
return 0;
}
xkb_keysym_t
keymap_get_one_sym_by_level(struct xkb_keymap *keymap, xkb_keycode_t key,
xkb_layout_index_t layout, xkb_level_index_t level)
{
const xkb_keysym_t *syms;
int count = xkb_keymap_key_get_syms_by_level(keymap, key, layout, level, &syms);
return count ? syms[0] : XKB_KEY_NoSymbol;
}