Skip to content

Commit d0f8976

Browse files
authored
Merge pull request #1433 from pslldq/supportxdgshell
wayland: Support xdg-shell as a fallback
2 parents 1dcc520 + 83e95f9 commit d0f8976

File tree

2 files changed

+141
-24
lines changed

2 files changed

+141
-24
lines changed

src/wayland/wl.c

+138-24
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ struct window_wl {
4040
cairo_t * c_ctx;
4141
};
4242

43-
struct wl_ctx ctx;
43+
struct wl_ctx ctx = { 0 };
4444

4545
static void surface_handle_enter(void *data, struct wl_surface *surface,
4646
struct wl_output *wl_output) {
@@ -83,13 +83,32 @@ 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+
}
9295

96+
ctx.configured = true;
97+
98+
send_frame();
99+
}
100+
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+
// TODO We currently don't support the compositor
107+
// providing us with a size different to our requested size.
108+
// Therefore just ignore the suggested surface size.
109+
}
110+
111+
static void surface_handle_closed(void) {
93112
if (ctx.surface)
94113
wl_surface_destroy(ctx.surface);
95114
ctx.surface = NULL;
@@ -111,11 +130,53 @@ static void layer_surface_handle_closed(void *data,
111130
}
112131
}
113132

133+
static void layer_surface_handle_closed(void *data,
134+
struct zwlr_layer_surface_v1 *surface) {
135+
LOG_I("Destroying layer");
136+
if (ctx.layer_surface)
137+
zwlr_layer_surface_v1_destroy(ctx.layer_surface);
138+
ctx.layer_surface = NULL;
139+
140+
surface_handle_closed();
141+
}
142+
143+
static void xdg_toplevel_handle_close(void *data,
144+
struct xdg_toplevel *surface) {
145+
LOG_I("Destroying layer");
146+
if (ctx.xdg_toplevel)
147+
xdg_toplevel_destroy(ctx.xdg_toplevel);
148+
ctx.xdg_toplevel = NULL;
149+
if (ctx.xdg_surface)
150+
xdg_surface_destroy(ctx.xdg_surface);
151+
ctx.xdg_surface = NULL;
152+
153+
surface_handle_closed();
154+
}
155+
156+
static void xdg_wm_base_handle_ping(void *data,
157+
struct xdg_wm_base *xdg_wm_base,
158+
uint32_t serial) {
159+
xdg_wm_base_pong(ctx.xdg_shell, serial);
160+
}
161+
114162
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
115163
.configure = layer_surface_handle_configure,
116164
.closed = layer_surface_handle_closed,
117165
};
118166

167+
static const struct xdg_wm_base_listener xdg_wm_base_listener = {
168+
.ping = xdg_wm_base_handle_ping,
169+
};
170+
171+
static const struct xdg_surface_listener xdg_surface_listener = {
172+
.configure = xdg_surface_handle_configure,
173+
};
174+
175+
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
176+
.configure = xdg_toplevel_handle_configure,
177+
.close = xdg_toplevel_handle_close,
178+
};
179+
119180
// Warning, can return NULL
120181
static struct dunst_output *get_configured_output(void) {
121182
int n = 0;
@@ -164,6 +225,10 @@ static void handle_global(void *data, struct wl_registry *registry,
164225
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
165226
ctx.layer_shell = wl_registry_bind(registry, name,
166227
&zwlr_layer_shell_v1_interface, 1);
228+
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
229+
ctx.xdg_shell = wl_registry_bind(registry, name,
230+
&xdg_wm_base_interface, 1);
231+
xdg_wm_base_add_listener(ctx.xdg_shell, &xdg_wm_base_listener, NULL);
167232
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
168233
create_seat(registry, name, version);
169234
LOG_D("Binding to seat %i", name);
@@ -263,8 +328,12 @@ bool wl_init(void) {
263328
return false;
264329
}
265330
if (ctx.layer_shell == NULL) {
266-
LOG_W("compositor doesn't support zwlr_layer_shell_v1");
267-
return false;
331+
if (ctx.xdg_shell == NULL) {
332+
LOG_W("compositor doesn't support zwlr_layer_shell_v1 or xdg_shell");
333+
return false;
334+
} else {
335+
LOG_W("compositor doesn't support zwlr_layer_shell_v1, falling back to xdg_shell. Notification window position will be set by the compositor.");
336+
}
268337
}
269338
if (wl_list_empty(&ctx.seats)) {
270339
LOG_W("no seat was found, so dunst cannot see input");
@@ -335,6 +404,12 @@ void wl_deinit(void) {
335404
if (ctx.layer_surface != NULL) {
336405
g_clear_pointer(&ctx.layer_surface, zwlr_layer_surface_v1_destroy);
337406
}
407+
if (ctx.xdg_toplevel != NULL) {
408+
g_clear_pointer(&ctx.xdg_toplevel, xdg_toplevel_destroy);
409+
}
410+
if (ctx.xdg_surface != NULL) {
411+
g_clear_pointer(&ctx.xdg_surface, xdg_surface_destroy);
412+
}
338413
if (ctx.frame_callback) {
339414
g_clear_pointer(&ctx.frame_callback, wl_callback_destroy);
340415
}
@@ -376,6 +451,9 @@ void wl_deinit(void) {
376451
if (ctx.layer_shell)
377452
g_clear_pointer(&ctx.layer_shell, zwlr_layer_shell_v1_destroy);
378453

454+
if (ctx.xdg_shell)
455+
g_clear_pointer(&ctx.xdg_shell, xdg_wm_base_destroy);
456+
379457
if (ctx.compositor)
380458
g_clear_pointer(&ctx.compositor, wl_compositor_destroy);
381459

@@ -426,6 +504,14 @@ static void send_frame(void) {
426504
zwlr_layer_surface_v1_destroy(ctx.layer_surface);
427505
ctx.layer_surface = NULL;
428506
}
507+
if (ctx.xdg_toplevel != NULL) {
508+
xdg_toplevel_destroy(ctx.xdg_toplevel);
509+
ctx.xdg_toplevel = NULL;
510+
}
511+
if (ctx.xdg_surface != NULL) {
512+
xdg_surface_destroy(ctx.xdg_surface);
513+
ctx.xdg_surface = NULL;
514+
}
429515
if (ctx.surface != NULL) {
430516
wl_surface_destroy(ctx.surface);
431517
ctx.surface = NULL;
@@ -458,20 +544,32 @@ static void send_frame(void) {
458544
// If we've made it here, there is something to draw. If the surface
459545
// doesn't exist (this is the first notification, or we moved to a
460546
// different output), we need to create it.
461-
if (ctx.layer_surface == NULL) {
462-
struct wl_output *wl_output = NULL;
463-
if (output != NULL) {
464-
wl_output = output->wl_output;
465-
}
547+
if (ctx.layer_surface == NULL && ctx.xdg_surface == NULL) {
466548
ctx.layer_surface_output = output;
467549
ctx.surface = wl_compositor_create_surface(ctx.compositor);
468550
wl_surface_add_listener(ctx.surface, &surface_listener, NULL);
469551

470-
ctx.layer_surface = zwlr_layer_shell_v1_get_layer_surface(
471-
ctx.layer_shell, ctx.surface, wl_output,
472-
settings.layer, "notifications");
473-
zwlr_layer_surface_v1_add_listener(ctx.layer_surface,
474-
&layer_surface_listener, NULL);
552+
if (ctx.layer_shell) {
553+
struct wl_output *wl_output = NULL;
554+
if (output != NULL) {
555+
wl_output = output->wl_output;
556+
}
557+
558+
ctx.layer_surface = zwlr_layer_shell_v1_get_layer_surface(
559+
ctx.layer_shell, ctx.surface, wl_output,
560+
settings.layer, "notifications");
561+
zwlr_layer_surface_v1_add_listener(ctx.layer_surface,
562+
&layer_surface_listener, NULL);
563+
} else {
564+
ctx.xdg_surface = xdg_wm_base_get_xdg_surface(
565+
ctx.xdg_shell, ctx.surface);
566+
xdg_surface_add_listener(ctx.xdg_surface, &xdg_surface_listener, NULL);
567+
568+
ctx.xdg_toplevel = xdg_surface_get_toplevel(ctx.xdg_surface);
569+
xdg_toplevel_set_title(ctx.xdg_toplevel, "Dunst");
570+
xdg_toplevel_set_app_id(ctx.xdg_toplevel, "org.knopwob.dunst");
571+
xdg_toplevel_add_listener(ctx.xdg_toplevel, &xdg_toplevel_listener, NULL);
572+
}
475573

476574
// Because we're creating a new surface, we aren't going to draw
477575
// anything into it during this call. We don't know what size the
@@ -483,13 +581,22 @@ static void send_frame(void) {
483581
// block to let it set the size for us.
484582
}
485583

486-
assert(ctx.layer_surface);
584+
assert(ctx.layer_surface || ctx.xdg_surface);
487585

488586
// We now want to resize the surface if it isn't the right size. If the
489587
// surface is brand new, it doesn't even have a size yet. If it already
490588
// exists, we might need to resize if the list of notifications has changed
491589
// since the last time we drew.
492-
if (ctx.height != height || ctx.width != width) {
590+
// We only do this for layer_surface, as xdg_surface only needs configuration
591+
// using xdg_surface_set_window_geometry if it differs from the buffer dimension.
592+
// Furthermore mutter intersects the buffer dimension and the window geometry.
593+
// As we directly do a commit+roundtrip, mutter will complain, as the missing
594+
// buffer causes a 0,0 intersection and also causes a size of 0,0 in the
595+
// configure event.
596+
if (ctx.layer_surface && (ctx.height != height || ctx.width != width)) {
597+
// TODO If the surface is already configured we should rather
598+
// adjust to the size the compositor requested instead of just trying
599+
// to set our preferred size again.
493600
struct dimensions dim = ctx.cur_dim;
494601
// Set window size
495602
zwlr_layer_surface_v1_set_size(ctx.layer_surface,
@@ -507,6 +614,14 @@ static void send_frame(void) {
507614
settings.offset.y, // bottom
508615
settings.offset.x);// left
509616

617+
// TODO Check if this is really necessary, as it causes an infinite
618+
// loop if the compositor doesn't configure our desired surface size
619+
// Without it wlroots 0.18.2 creates a wrong configure serial error
620+
// which seems to be caused by an ack_configure without an commit.
621+
ctx.configured = false;
622+
}
623+
624+
if (!ctx.configured) {
510625
wl_surface_commit(ctx.surface);
511626

512627
// Now we're going to bail without drawing anything. This gives the
@@ -518,9 +633,6 @@ static void send_frame(void) {
518633
// layer surface will exist and the height will hopefully match what
519634
// we asked for. That means we won't return here, and will actually
520635
// draw into the surface down below.
521-
// TODO: If the compositor doesn't send a configure with the size we
522-
// requested, we'll enter an infinite loop. We need to keep track of
523-
// the fact that a request was sent separately from what height we are.
524636
wl_display_roundtrip(ctx.display);
525637
return;
526638
}
@@ -556,6 +668,8 @@ static const struct wl_callback_listener frame_listener = {
556668

557669
static void schedule_frame_and_commit(void) {
558670
if (ctx.frame_callback) {
671+
// don't commit, as it probably won't make it to the display and
672+
// therefore waste resources
559673
return;
560674
}
561675
if (ctx.surface == NULL) {

src/wayland/wl_ctx.h

+3
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)