34 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
Guido Cella 5fd19feaff support monitors above and below in focusmon and tagmon
Fixes https://codeberg.org/dwl/dwl/issues/1105
2026-09-13 21:14:10 +02:00
Guido Cella b1c101515a remove zero flickering lie
This isn't true since dwl switched to the scene API in 2021 (1b38801).

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

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

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

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

Also initialize c to be safe.

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

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

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

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

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

Rather than figuring out how to fix it, just replace its implementation
with toplevel_from_wlr_surface() which works and also reduces the SLOC.
2026-09-11 23:55:17 +02:00
6 changed files with 369 additions and 166 deletions
-1
View File
@@ -153,7 +153,6 @@ given the base on which it is built. Implemented default features are:
- Support screen lockers via ext-session-lock-v1 protocol
- Various Wayland protocols
- XWayland support as provided by wlroots (can be enabled in `config.mk`)
- Zero flickering - Wayland users naturally expect that "every frame is perfect"
- Layer shell popups (used by Waybar)
- Damage tracking provided by scenegraph API
+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
+23 -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),
};
@@ -170,3 +170,8 @@ static const Button buttons[] = {
{ MODKEY, BTN_MIDDLE, togglefloating, {0} },
{ MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
};
static const Axis axes[] = {
{ MODKEY, AxisUp, spawn, SHCMD("volume-up_EXAMPLE") },
{ MODKEY, AxisDown, spawn, SHCMD("volume-down_EXAMPLE") },
};
+290 -123
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>
@@ -27,6 +29,7 @@
#include <wlr/types/wlr_ext_data_control_v1.h>
#include <wlr/types/wlr_ext_image_capture_source_v1.h>
#include <wlr/types/wlr_ext_image_copy_capture_v1.h>
#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
#include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
@@ -41,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>
@@ -84,7 +88,9 @@
/* 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 {
int i;
@@ -93,6 +99,13 @@ typedef union {
const void *v;
} Arg;
typedef struct {
unsigned int mod;
unsigned int dir;
void (*func)(const Arg *);
const Arg arg;
} Axis;
typedef struct {
unsigned int mod;
unsigned int button;
@@ -119,6 +132,13 @@ typedef struct {
struct wlr_xwayland_surface *xwayland;
} surface;
struct wlr_xdg_toplevel_decoration_v1 *decoration;
struct wlr_ext_foreign_toplevel_handle_v1 *foreign_toplevel_handle;
struct wlr_scene *image_capture_scene;
struct wlr_ext_image_capture_source_v1 *image_capture_source;
union {
struct wlr_scene_tree *image_capture_tree;
struct wlr_scene_surface *image_capture_scene_surface;
} capture;
struct wl_listener commit;
struct wl_listener map;
struct wl_listener maximize;
@@ -133,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 {
@@ -152,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;
@@ -249,6 +268,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list,
static void arrangelayers(Monitor *m);
static void axisnotify(struct wl_listener *listener, void *data);
static void buttonpress(struct wl_listener *listener, void *data);
static void capturerequest(struct wl_listener *listener, void *data);
static void chvt(const Arg *arg);
static void checkidleinhibitor(struct wlr_surface *exclude);
static void cleanup(void);
@@ -309,7 +329,7 @@ static void moveresize(const Arg *arg);
static void outputmgrapply(struct wl_listener *listener, void *data);
static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test);
static void outputmgrtest(struct wl_listener *listener, void *data);
static void pointerfocus(Client *c, struct wlr_surface *surface,
static void pointerfocus(Client *c, LayerSurface* l, struct wlr_surface *surface,
double sx, double sy, uint32_t time);
static void printstatus(void);
static void powermgrsetmode(struct wl_listener *listener, void *data);
@@ -357,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;
@@ -383,6 +404,8 @@ static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr;
static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr;
static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr;
static struct wlr_output_power_manager_v1 *power_mgr;
static struct wlr_ext_foreign_toplevel_list_v1 *foreign_toplevel_list;
static struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *ext_foreign_toplevel_image_capture_source_manager_v1;
static struct wlr_pointer_constraints_v1 *pointer_constraints;
static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr;
@@ -437,6 +460,7 @@ static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape};
static struct wl_listener request_start_drag = {.notify = requeststartdrag};
static struct wl_listener start_drag = {.notify = startdrag};
static struct wl_listener new_session_lock = {.notify = locksession};
static struct wl_listener new_foreign_toplevel_capture_request = {.notify = capturerequest};
#ifdef XWAYLAND
static void activatex11(struct wl_listener *listener, void *data);
@@ -444,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};
@@ -463,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)
@@ -518,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)));
}
}
@@ -593,10 +633,13 @@ arrangelayers(Monitor *m)
for (i = 3; i >= 0; i--)
arrangelayer(m, &m->layers[i], &usable_area, 0);
/* Find topmost keyboard interactive layer, if such a layer exists */
/* Find topmost keyboard interactive layer that has indicated it wants
* exclusive access to the keyboard, if such a layer exists */
for (i = 0; i < (int)LENGTH(layers_above_shell); i++) {
wl_list_for_each_reverse(l, &m->layers[layers_above_shell[i]], link) {
if (locked || !l->layer_surface->current.keyboard_interactive || !l->mapped)
if (locked ||
l->layer_surface->current.keyboard_interactive != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE ||
!l->mapped)
continue;
/* Deactivate the focused client. */
focusclient(NULL, 0);
@@ -613,9 +656,26 @@ axisnotify(struct wl_listener *listener, void *data)
/* This event is forwarded by the cursor when a pointer emits an axis event,
* for example when you move the scroll wheel. */
struct wlr_pointer_axis_event *event = data;
const Axis *a;
unsigned int adir;
struct wlr_keyboard *keyboard;
uint32_t mods;
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
/* TODO: allow usage of scroll wheel for mousebindings, it can be implemented
* by checking the event's orientation and the delta of the event */
if (event->orientation == WL_POINTER_AXIS_VERTICAL_SCROLL)
adir = event->delta > 0 ? AxisDown : AxisUp;
else
adir = event->delta > 0 ? AxisRight : AxisLeft;
keyboard = wlr_seat_get_keyboard(seat);
mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
for (a = axes; a < END(axes); a++) {
if (CLEANMASK(mods) == CLEANMASK(a->mod) && adir == a->dir && a->func) {
a->func(&a->arg);
return;
}
}
/* Notify the client with pointer focus of the axis event. */
wlr_seat_pointer_notify_axis(seat,
event->time_msec, event->orientation, event->delta,
@@ -628,7 +688,8 @@ buttonpress(struct wl_listener *listener, void *data)
struct wlr_pointer_button_event *event = data;
struct wlr_keyboard *keyboard;
uint32_t mods;
Client *c;
Client *c = NULL;
LayerSurface *l = NULL;
const Button *b;
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
@@ -640,10 +701,15 @@ buttonpress(struct wl_listener *listener, void *data)
if (locked)
break;
/* Change focus if the button was _pressed_ over a client */
xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL);
if (c && (!client_is_unmanaged(c) || client_wants_focus(c)))
/* Change focus if the button was _pressed_ over a client
or a layer surface with on-demand keyboard interactivity */
xytonode(cursor->x, cursor->y, NULL, &c, &l, NULL, NULL);
if (c && (!client_is_unmanaged(c) || client_wants_focus(c))) {
focusclient(c, 1);
} else if (l && l->layer_surface->current.keyboard_interactive) {
focusclient(NULL, 0);
client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat));
}
keyboard = wlr_seat_get_keyboard(seat);
mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
@@ -657,10 +723,11 @@ buttonpress(struct wl_listener *listener, void *data)
break;
case WL_POINTER_BUTTON_STATE_RELEASED:
/* If you released any buttons, we exit interactive move/resize mode. */
/* TODO: should reset to the pointer focus's current setcursor */
if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) {
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
cursor_mode = CurNormal;
/* Update the cursor */
wlr_seat_pointer_clear_focus(seat);
motionnotify(0, NULL, 0, 0, 0, 0);
/* Drop the window off on its new monitor */
selmon = xytomon(cursor->x, cursor->y);
setmon(grabc, selmon, 0);
@@ -676,6 +743,25 @@ buttonpress(struct wl_listener *listener, void *data)
event->time_msec, event->button, event->state);
}
void
capturerequest(struct wl_listener *listener, void *data)
{
struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request = data;
Client *view = request->toplevel_handle->data;
if (!view->image_capture_source) {
view->image_capture_source = wlr_ext_image_capture_source_v1_create_with_scene_node(
&view->image_capture_scene->tree.node, event_loop, alloc, drw);
if (!view->image_capture_source)
return;
}
wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept(
request, view->image_capture_source);
client_set_suspended(view, 0);
}
void
chvt(const Arg *arg)
{
@@ -718,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 */
@@ -788,6 +875,7 @@ cleanuplisteners(void)
wl_list_remove(&request_start_drag.link);
wl_list_remove(&start_drag.link);
wl_list_remove(&new_session_lock.link);
wl_list_remove(&new_foreign_toplevel_capture_request.link);
#ifdef XWAYLAND
wl_list_remove(&new_xwayland_surface.link);
wl_list_remove(&xwayland_ready.link);
@@ -843,6 +931,11 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data)
return;
}
if (layer_surface == exclusive_focus
&& layer_surface->current.keyboard_interactive !=
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE)
exclusive_focus = NULL;
if (layer_surface->current.committed == 0 && l->mapped == layer_surface->surface->mapped)
return;
l->mapped = layer_surface->surface->mapped;
@@ -881,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
@@ -1345,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
@@ -1404,7 +1497,8 @@ dirtomon(enum wlr_direction dir)
dir, selmon->wlr_output, selmon->m.x, selmon->m.y)))
return next->data;
if ((next = wlr_output_layout_farthest_output(output_layout,
dir ^ (WLR_DIRECTION_LEFT|WLR_DIRECTION_RIGHT),
dir ^ (WLR_DIRECTION_LEFT | WLR_DIRECTION_RIGHT |
WLR_DIRECTION_UP | WLR_DIRECTION_DOWN),
selmon->wlr_output, selmon->m.x, selmon->m.y)))
return next->data;
return selmon;
@@ -1434,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);
@@ -1447,24 +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(
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.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);
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();
@@ -1485,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
@@ -1629,58 +1728,53 @@ 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;
}
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;
@@ -1716,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;
@@ -1773,6 +1868,11 @@ mapnotify(struct wl_listener *listener, void *data)
Monitor *m;
int i;
struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = {
.app_id = client_get_appid(c),
.title = client_get_title(c),
};
/* Create scene tree for this client and its border */
c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]);
/* Enabled later by a call to arrange() */
@@ -1787,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;
@@ -1804,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;
@@ -1812,6 +1910,18 @@ mapnotify(struct wl_listener *listener, void *data)
wl_list_insert(&clients, &c->link);
wl_list_insert(&fstack, &c->flink);
c->foreign_toplevel_handle = wlr_ext_foreign_toplevel_handle_v1_create(
foreign_toplevel_list, &foreign_toplevel_state);
c->foreign_toplevel_handle->data = c;
c->image_capture_scene = wlr_scene_create();
if (c->type == XDGShell)
c->capture.image_capture_tree = wlr_scene_xdg_surface_create(&c->image_capture_scene->tree, c->surface.xdg);
#ifdef XWAYLAND
else
c->capture.image_capture_scene_surface = wlr_scene_surface_create(&c->image_capture_scene->tree, c->surface.xwayland->surface);
#endif
/* Set initial monitor, tags, floating status, and focus:
* we always consider floating, clients that have parent and thus
* we set the same tags and monitor as its parent.
@@ -1892,22 +2002,23 @@ 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;
LayerSurface *l = NULL, *focusedl = NULL;
struct wlr_surface *surface = NULL;
struct wlr_pointer_constraint_v1 *constraint;
/* Find the client under the pointer and send the event along. */
xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy);
xytonode(cursor->x, cursor->y, &surface, &c, &l, &sx, &sy);
if (cursor_mode == CurPressed && !seat->drag
&& surface != seat->pointer_state.focused_surface
&& toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w, &l) >= 0) {
&& toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w, &focusedl) >= 0) {
c = w;
surface = seat->pointer_state.focused_surface;
sx = cursor->x - (l ? l->scene->node.x : w->geom.x);
sy = cursor->y - (l ? l->scene->node.y : w->geom.y);
sx = cursor->x - (focusedl ? focusedl->scene->node.x : w->geom.x);
sy = cursor->y - (focusedl ? focusedl->scene->node.y : w->geom.y);
}
/* time is 0 in internal calls meant to restore pointer focus. */
@@ -1949,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) {
@@ -1964,7 +2085,7 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
if (!surface && !seat->drag)
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
pointerfocus(c, surface, sx, sy, time);
pointerfocus(c, l, surface, sx, sy, time);
}
void
@@ -2027,48 +2148,60 @@ 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);
@@ -2088,14 +2221,19 @@ outputmgrtest(struct wl_listener *listener, void *data)
}
void
pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy,
pointerfocus(Client *c, LayerSurface *l, struct wlr_surface *surface, double sx, double sy,
uint32_t time)
{
struct timespec now;
if (surface != seat->pointer_state.focused_surface &&
sloppyfocus && time && c && !client_is_unmanaged(c))
if (surface != seat->pointer_state.focused_surface && sloppyfocus && time) {
if (c && (!client_is_unmanaged(c) || client_wants_focus(c))) {
focusclient(c, 0);
} else if (l && l->layer_surface->current.keyboard_interactive) {
focusclient(NULL, 0);
client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat));
}
}
/* If surface is NULL, clear pointer focus */
if (!surface) {
@@ -2249,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);
}
@@ -2583,7 +2719,7 @@ setup(void)
wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel);
wl_signal_add(&xdg_shell->events.new_popup, &new_xdg_popup);
layer_shell = wlr_layer_shell_v1_create(dpy, 3);
layer_shell = wlr_layer_shell_v1_create(dpy, 4);
wl_signal_add(&layer_shell->events.new_surface, &new_layer_surface);
idle_notifier = wlr_idle_notifier_v1_create(dpy);
@@ -2597,6 +2733,12 @@ setup(void)
(float [4]){0.1f, 0.1f, 0.1f, 1.0f});
wlr_scene_node_set_enabled(&locked_bg->node, 0);
foreign_toplevel_list = wlr_ext_foreign_toplevel_list_v1_create(dpy,1);
ext_foreign_toplevel_image_capture_source_manager_v1 =
wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(dpy, 1);
wl_signal_add(&ext_foreign_toplevel_image_capture_source_manager_v1->events.new_request,
&new_foreign_toplevel_capture_request);
/* Use decoration protocols to negotiate server-side decorations */
wlr_server_decoration_manager_set_default_mode(
wlr_server_decoration_manager_create(dpy),
@@ -2672,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,
@@ -2848,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) {
@@ -2860,6 +3003,20 @@ unmapnotify(struct wl_listener *listener, void *data)
wl_list_remove(&c->flink);
}
if (c->foreign_toplevel_handle) {
wlr_ext_foreign_toplevel_handle_v1_destroy(c->foreign_toplevel_handle);
c->foreign_toplevel_handle = NULL;
}
#ifdef XWAYLAND
if (c->type != XDGShell && c->capture.image_capture_scene_surface) {
wlr_scene_node_destroy(&c->capture.image_capture_scene_surface->buffer->node);
c->capture.image_capture_scene_surface = NULL;
}
#endif
wlr_scene_node_destroy(&c->image_capture_scene->tree.node);
c->image_capture_scene = NULL;
c->image_capture_source = NULL;
wlr_scene_node_destroy(&c->scene->node);
client_surface(c)->data = NULL;
printstatus();
@@ -2981,6 +3138,15 @@ updatetitle(struct wl_listener *listener, void *data)
Client *c = wl_container_of(listener, c, set_title);
if (c == focustop(c->mon))
printstatus();
if (c->foreign_toplevel_handle) {
struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = {
.app_id = client_get_appid(c),
.title = client_get_title(c),
};
wlr_ext_foreign_toplevel_handle_v1_update_state(c->foreign_toplevel_handle,
&foreign_toplevel_state);
}
}
void
@@ -3025,6 +3191,7 @@ virtualkeyboard(struct wl_listener *listener, void *data)
/* Add the new keyboard to the group */
wlr_keyboard_group_add_keyboard(group->wlr_group, &kb->keyboard);
wlr_seat_set_capabilities(seat, seat->capabilities | WL_SEAT_CAPABILITY_KEYBOARD);
}
void
@@ -3036,6 +3203,7 @@ virtualpointer(struct wl_listener *listener, void *data)
wlr_cursor_attach_input_device(cursor, device);
if (event->suggested_output)
wlr_cursor_map_input_to_output(cursor, device, event->suggested_output);
wlr_seat_set_capabilities(seat, seat->capabilities | WL_SEAT_CAPABILITY_POINTER);
}
Monitor *
@@ -3049,10 +3217,8 @@ void
xytonode(double x, double y, struct wlr_surface **psurface,
Client **pc, LayerSurface **pl, double *nx, double *ny)
{
struct wlr_scene_node *node, *pnode;
struct wlr_scene_node *node;
struct wlr_surface *surface = NULL;
Client *c = NULL;
LayerSurface *l = NULL;
int layer;
for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) {
@@ -3063,18 +3229,10 @@ xytonode(double x, double y, struct wlr_surface **psurface,
if (node->type == WLR_SCENE_NODE_BUFFER)
surface = wlr_scene_surface_try_from_buffer(
wlr_scene_buffer_from_node(node))->surface;
/* Walk the tree to find a node that knows the client */
for (pnode = node; pnode && !c; pnode = &pnode->parent->node)
c = pnode->data;
if (c && c->type == LayerShell) {
c = NULL;
l = pnode->data;
}
}
if (psurface) *psurface = surface;
if (pc) *pc = c;
if (pl) *pl = l;
toplevel_from_wlr_surface(surface, pc, pl);
}
void
@@ -3135,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;
@@ -3174,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);
}
@@ -3186,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;
}