@@ -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
4545static 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+
114162static 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
120181static 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
557669static 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 ) {
0 commit comments