Skip to content

Commit a7f7400

Browse files
committed
wayland: support xdg-shell as a fallback
Support the xdg-shell wayland protocol as a fallback in case the wlr-layer-shell-unstable-v1 protocol is not present. This allows running dunst on wayland compositors not supporting the layer shell protocol. Note that the xdg-shell protocol doesn't allow dunst to specify where it should be displayed on the screen. Therefore it is only chosen, when the layer-shell protocol is not available.
1 parent d353fbd commit a7f7400

File tree

2 files changed

+157
-46
lines changed

2 files changed

+157
-46
lines changed

src/wayland/wl.c

Lines changed: 154 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,47 @@ static void layer_surface_handle_configure(void *data,
8383
send_frame();
8484
}
8585

86-
static void layer_surface_handle_closed(void *data,
87-
struct zwlr_layer_surface_v1 *surface) {
88-
LOG_I("Destroying layer");
89-
if (ctx.layer_surface)
90-
zwlr_layer_surface_v1_destroy(ctx.layer_surface);
91-
ctx.layer_surface = NULL;
86+
static void xdg_surface_handle_configure(void *data,
87+
struct xdg_surface *surface,
88+
uint32_t serial) {
89+
xdg_surface_ack_configure(ctx.xdg_surface, serial);
90+
91+
if (ctx.configured) {
92+
wl_surface_commit(ctx.surface);
93+
return;
94+
}
95+
96+
ctx.configured = true;
97+
98+
send_frame();
99+
}
92100

101+
static void xdg_toplevel_handle_configure(void *data,
102+
struct xdg_toplevel *xdg_toplevel,
103+
int32_t width,
104+
int32_t height,
105+
struct wl_array *states) {
106+
if (width == ctx.width && height == ctx.height) {
107+
return;
108+
}
109+
110+
ctx.configured = false;
111+
ctx.width = width;
112+
ctx.height = height;
113+
}
114+
115+
static void xdg_toplevel_wm_capabilities(void *data,
116+
struct xdg_toplevel *xdg_toplevel,
117+
struct wl_array *capabilities) {
118+
}
119+
120+
static void xdg_toplevel_handle_configure_bounds (void *data,
121+
struct xdg_toplevel *xdg_toplevel,
122+
int32_t width,
123+
int32_t height) {
124+
}
125+
126+
static void surface_handle_closed(void) {
93127
if (ctx.surface)
94128
wl_surface_destroy(ctx.surface);
95129
ctx.surface = NULL;
@@ -111,11 +145,46 @@ static void layer_surface_handle_closed(void *data,
111145
}
112146
}
113147

148+
static void layer_surface_handle_closed(void *data,
149+
struct zwlr_layer_surface_v1 *surface) {
150+
LOG_I("Destroying layer");
151+
if (ctx.layer_surface)
152+
zwlr_layer_surface_v1_destroy(ctx.layer_surface);
153+
ctx.layer_surface = NULL;
154+
155+
surface_handle_closed();
156+
}
157+
158+
static void xdg_toplevel_handle_close(void *data,
159+
struct xdg_toplevel *surface) {
160+
LOG_I("Destroying layer");
161+
if (ctx.xdg_toplevel)
162+
xdg_toplevel_destroy(ctx.xdg_toplevel);
163+
ctx.xdg_toplevel = NULL;
164+
if (ctx.xdg_surface)
165+
xdg_surface_destroy(ctx.xdg_surface);
166+
ctx.xdg_surface = NULL;
167+
168+
surface_handle_closed();
169+
}
170+
114171
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
115172
.configure = layer_surface_handle_configure,
116173
.closed = layer_surface_handle_closed,
117174
};
118175

176+
static const struct xdg_surface_listener xdg_surface_listener = {
177+
.configure = xdg_surface_handle_configure,
178+
};
179+
180+
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
181+
.configure = xdg_toplevel_handle_configure,
182+
.close = xdg_toplevel_handle_close,
183+
.configure_bounds = xdg_toplevel_handle_configure_bounds,
184+
.wm_capabilities = xdg_toplevel_wm_capabilities,
185+
};
186+
187+
119188
// Warning, can return NULL
120189
static struct dunst_output *get_configured_output(void) {
121190
int n = 0;
@@ -164,6 +233,9 @@ static void handle_global(void *data, struct wl_registry *registry,
164233
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
165234
ctx.layer_shell = wl_registry_bind(registry, name,
166235
&zwlr_layer_shell_v1_interface, 1);
236+
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
237+
ctx.xdg_shell = wl_registry_bind(registry, name,
238+
&xdg_wm_base_interface, 5);
167239
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
168240
create_seat(registry, name, version);
169241
LOG_D("Binding to seat %i", name);
@@ -262,8 +334,12 @@ bool wl_init(void) {
262334
return false;
263335
}
264336
if (ctx.layer_shell == NULL) {
265-
LOG_W("compositor doesn't support zwlr_layer_shell_v1");
266-
return false;
337+
if (ctx.xdg_shell == NULL) {
338+
LOG_W("compositor doesn't support zwlr_layer_shell_v1 or xdg_shell");
339+
return false;
340+
} else {
341+
LOG_W("compositor doesn't support zwlr_layer_shell_v1, falling back to xdg_shell. Notification window position will be set by the compositor.");
342+
}
267343
}
268344
if (wl_list_empty(&ctx.seats)) {
269345
LOG_W("no seat was found, so dunst cannot see input");
@@ -334,6 +410,12 @@ void wl_deinit(void) {
334410
if (ctx.layer_surface != NULL) {
335411
g_clear_pointer(&ctx.layer_surface, zwlr_layer_surface_v1_destroy);
336412
}
413+
if (ctx.xdg_toplevel != NULL) {
414+
g_clear_pointer(&ctx.xdg_toplevel, xdg_toplevel_destroy);
415+
}
416+
if (ctx.xdg_surface != NULL) {
417+
g_clear_pointer(&ctx.xdg_surface, xdg_surface_destroy);
418+
}
337419
if (ctx.surface != NULL) {
338420
g_clear_pointer(&ctx.surface, wl_surface_destroy);
339421
}
@@ -371,6 +453,9 @@ void wl_deinit(void) {
371453
if (ctx.layer_shell)
372454
g_clear_pointer(&ctx.layer_shell, zwlr_layer_shell_v1_destroy);
373455

456+
if (ctx.xdg_shell)
457+
g_clear_pointer(&ctx.xdg_shell, xdg_wm_base_destroy);
458+
374459
if (ctx.compositor)
375460
g_clear_pointer(&ctx.compositor, wl_compositor_destroy);
376461

@@ -411,6 +496,14 @@ static void send_frame(void) {
411496
zwlr_layer_surface_v1_destroy(ctx.layer_surface);
412497
ctx.layer_surface = NULL;
413498
}
499+
if (ctx.xdg_toplevel != NULL) {
500+
xdg_toplevel_destroy(ctx.xdg_toplevel);
501+
ctx.xdg_toplevel = NULL;
502+
}
503+
if (ctx.xdg_surface != NULL) {
504+
xdg_surface_destroy(ctx.xdg_surface);
505+
ctx.xdg_surface = NULL;
506+
}
414507
if (ctx.surface != NULL) {
415508
wl_surface_destroy(ctx.surface);
416509
ctx.surface = NULL;
@@ -443,20 +536,31 @@ static void send_frame(void) {
443536
// If we've made it here, there is something to draw. If the surface
444537
// doesn't exist (this is the first notification, or we moved to a
445538
// different output), we need to create it.
446-
if (ctx.layer_surface == NULL) {
447-
struct wl_output *wl_output = NULL;
448-
if (output != NULL) {
449-
wl_output = output->wl_output;
450-
}
539+
if (ctx.layer_surface == NULL && ctx.xdg_surface == NULL) {
451540
ctx.layer_surface_output = output;
452541
ctx.surface = wl_compositor_create_surface(ctx.compositor);
453542
wl_surface_add_listener(ctx.surface, &surface_listener, NULL);
454543

455-
ctx.layer_surface = zwlr_layer_shell_v1_get_layer_surface(
456-
ctx.layer_shell, ctx.surface, wl_output,
457-
settings.layer, "notifications");
458-
zwlr_layer_surface_v1_add_listener(ctx.layer_surface,
459-
&layer_surface_listener, NULL);
544+
if (ctx.layer_shell) {
545+
struct wl_output *wl_output = NULL;
546+
if (output != NULL) {
547+
wl_output = output->wl_output;
548+
}
549+
550+
ctx.layer_surface = zwlr_layer_shell_v1_get_layer_surface(
551+
ctx.layer_shell, ctx.surface, wl_output,
552+
settings.layer, "notifications");
553+
zwlr_layer_surface_v1_add_listener(ctx.layer_surface,
554+
&layer_surface_listener, NULL);
555+
} else {
556+
ctx.xdg_surface = xdg_wm_base_get_xdg_surface(
557+
ctx.xdg_shell, ctx.surface);
558+
xdg_surface_add_listener(ctx.xdg_surface, &xdg_surface_listener, NULL);
559+
560+
ctx.xdg_toplevel = xdg_surface_get_toplevel(ctx.xdg_surface);
561+
xdg_toplevel_set_app_id(ctx.xdg_toplevel, "org.knopwob.dunst");
562+
xdg_toplevel_add_listener(ctx.xdg_toplevel, &xdg_toplevel_listener, NULL);
563+
}
460564

461565
// Because we're creating a new surface, we aren't going to draw
462566
// anything into it during this call. We don't know what size the
@@ -468,46 +572,50 @@ static void send_frame(void) {
468572
// block to let it set the size for us.
469573
}
470574

471-
assert(ctx.layer_surface);
575+
assert(ctx.layer_surface || ctx.xdg_surface);
472576

473577
// We now want to resize the surface if it isn't the right size. If the
474578
// surface is brand new, it doesn't even have a size yet. If it already
475579
// exists, we might need to resize if the list of notifications has changed
476580
// since the last time we drew.
477581
if (ctx.height != height || ctx.width != width) {
478582
struct dimensions dim = ctx.cur_dim;
479-
// Set window size
480-
zwlr_layer_surface_v1_set_size(ctx.layer_surface,
583+
if (ctx.layer_surface) {
584+
// Set window size
585+
zwlr_layer_surface_v1_set_size(ctx.layer_surface,
481586
dim.w, dim.h);
482587

483-
// Put the window at the right position
484-
zwlr_layer_surface_v1_set_anchor(ctx.layer_surface,
485-
settings.origin);
486-
zwlr_layer_surface_v1_set_margin(ctx.layer_surface,
487-
// Offsets where no anchors are specified are
488-
// ignored. We can safely assume the offset is
489-
// positive.
490-
settings.offset.y, // top
491-
settings.offset.x, // right
492-
settings.offset.y, // bottom
493-
settings.offset.x);// left
588+
// Put the window at the right position
589+
zwlr_layer_surface_v1_set_anchor(ctx.layer_surface,
590+
settings.origin);
591+
zwlr_layer_surface_v1_set_margin(ctx.layer_surface,
592+
// Offsets where no anchors are specified are
593+
// ignored. We can safely assume the offset is
594+
// positive.
595+
settings.offset.y, // top
596+
settings.offset.x, // right
597+
settings.offset.y, // bottom
598+
settings.offset.x);// left
599+
} else {
600+
// Just set the window size, as positioning is not part of the xdg-shell protocol
601+
xdg_surface_set_window_geometry(ctx.xdg_surface, 0, 0, dim.w, dim.h);
602+
}
494603

495604
wl_surface_commit(ctx.surface);
496605

497-
// Now we're going to bail without drawing anything. This gives the
498-
// compositor a chance to create the surface and tell us what size we
499-
// were actually granted, which may be smaller than what we asked for
500-
// depending on the screen size and layout of other layer surfaces.
501-
// This information is provided in layer_surface_handle_configure,
502-
// which will then call send_frame again. When that call happens, the
503-
// layer surface will exist and the height will hopefully match what
504-
// we asked for. That means we won't return here, and will actually
505-
// draw into the surface down below.
506-
// TODO: If the compositor doesn't send a configure with the size we
507-
// requested, we'll enter an infinite loop. We need to keep track of
508-
// the fact that a request was sent separately from what height we are.
509-
wl_display_roundtrip(ctx.display);
510-
return;
606+
if (!ctx.configured) {
607+
// Now we're going to bail without drawing anything. This gives the
608+
// compositor a chance to create the surface and tell us what size we
609+
// were actually granted, which may be smaller than what we asked for
610+
// depending on the screen size and layout of other layer surfaces.
611+
// This information is provided in layer_surface_handle_configure,
612+
// which will then call send_frame again. When that call happens, the
613+
// layer surface will exist and the height will hopefully match what
614+
// we asked for. That means we won't return here, and will actually
615+
// draw into the surface down below.
616+
wl_display_roundtrip(ctx.display);
617+
return;
618+
}
511619
}
512620

513621
assert(ctx.configured);

src/wayland/wl_ctx.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@ struct wl_ctx {
2020
struct wl_compositor *compositor;
2121
struct wl_shm *shm;
2222
struct zwlr_layer_shell_v1 *layer_shell;
23+
struct xdg_wm_base *xdg_shell;
2324

2425
struct wl_list outputs; /* list of struct dunst_output */
2526
struct wl_list seats; /* list of struct dunst_seat */
2627

2728
struct wl_surface *surface;
2829
struct dunst_output *surface_output;
2930
struct zwlr_layer_surface_v1 *layer_surface;
31+
struct xdg_surface *xdg_surface;
32+
struct xdg_toplevel *xdg_toplevel;
3033
struct dunst_output *layer_surface_output;
3134
struct wl_callback *frame_callback;
3235
struct org_kde_kwin_idle *idle_handler;

0 commit comments

Comments
 (0)