@@ -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,36 @@ 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+ }
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+ if (width == ctx .width && height == ctx .height ) {
107+ return ;
108+ }
109+
110+ ctx .configured = false;
111+ ctx .width = width ;
112+ ctx .height = height ;
113+ }
92114
115+ static void surface_handle_closed (void ) {
93116 if (ctx .surface )
94117 wl_surface_destroy (ctx .surface );
95118 ctx .surface = NULL ;
@@ -111,11 +134,53 @@ static void layer_surface_handle_closed(void *data,
111134 }
112135}
113136
137+ static void layer_surface_handle_closed (void * data ,
138+ struct zwlr_layer_surface_v1 * surface ) {
139+ LOG_I ("Destroying layer" );
140+ if (ctx .layer_surface )
141+ zwlr_layer_surface_v1_destroy (ctx .layer_surface );
142+ ctx .layer_surface = NULL ;
143+
144+ surface_handle_closed ();
145+ }
146+
147+ static void xdg_toplevel_handle_close (void * data ,
148+ struct xdg_toplevel * surface ) {
149+ LOG_I ("Destroying layer" );
150+ if (ctx .xdg_toplevel )
151+ xdg_toplevel_destroy (ctx .xdg_toplevel );
152+ ctx .xdg_toplevel = NULL ;
153+ if (ctx .xdg_surface )
154+ xdg_surface_destroy (ctx .xdg_surface );
155+ ctx .xdg_surface = NULL ;
156+
157+ surface_handle_closed ();
158+ }
159+
160+ static void xdg_wm_base_handle_ping (void * data ,
161+ struct xdg_wm_base * xdg_wm_base ,
162+ uint32_t serial ) {
163+ xdg_wm_base_pong (ctx .xdg_shell , serial );
164+ }
165+
114166static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
115167 .configure = layer_surface_handle_configure ,
116168 .closed = layer_surface_handle_closed ,
117169};
118170
171+ static const struct xdg_wm_base_listener xdg_wm_base_listener = {
172+ .ping = xdg_wm_base_handle_ping ,
173+ };
174+
175+ static const struct xdg_surface_listener xdg_surface_listener = {
176+ .configure = xdg_surface_handle_configure ,
177+ };
178+
179+ static const struct xdg_toplevel_listener xdg_toplevel_listener = {
180+ .configure = xdg_toplevel_handle_configure ,
181+ .close = xdg_toplevel_handle_close ,
182+ };
183+
119184// Warning, can return NULL
120185static struct dunst_output * get_configured_output (void ) {
121186 int n = 0 ;
@@ -164,6 +229,10 @@ static void handle_global(void *data, struct wl_registry *registry,
164229 } else if (strcmp (interface , zwlr_layer_shell_v1_interface .name ) == 0 ) {
165230 ctx .layer_shell = wl_registry_bind (registry , name ,
166231 & zwlr_layer_shell_v1_interface , 1 );
232+ } else if (strcmp (interface , xdg_wm_base_interface .name ) == 0 ) {
233+ ctx .xdg_shell = wl_registry_bind (registry , name ,
234+ & xdg_wm_base_interface , 1 );
235+ xdg_wm_base_add_listener (ctx .xdg_shell , & xdg_wm_base_listener , NULL );
167236 } else if (strcmp (interface , wl_seat_interface .name ) == 0 ) {
168237 create_seat (registry , name , version );
169238 LOG_D ("Binding to seat %i" , name );
@@ -262,8 +331,12 @@ bool wl_init(void) {
262331 return false;
263332 }
264333 if (ctx .layer_shell == NULL ) {
265- LOG_W ("compositor doesn't support zwlr_layer_shell_v1" );
266- return false;
334+ if (ctx .xdg_shell == NULL ) {
335+ LOG_W ("compositor doesn't support zwlr_layer_shell_v1 or xdg_shell" );
336+ return false;
337+ } else {
338+ LOG_W ("compositor doesn't support zwlr_layer_shell_v1, falling back to xdg_shell. Notification window position will be set by the compositor." );
339+ }
267340 }
268341 if (wl_list_empty (& ctx .seats )) {
269342 LOG_W ("no seat was found, so dunst cannot see input" );
@@ -334,6 +407,12 @@ void wl_deinit(void) {
334407 if (ctx .layer_surface != NULL ) {
335408 g_clear_pointer (& ctx .layer_surface , zwlr_layer_surface_v1_destroy );
336409 }
410+ if (ctx .xdg_toplevel != NULL ) {
411+ g_clear_pointer (& ctx .xdg_toplevel , xdg_toplevel_destroy );
412+ }
413+ if (ctx .xdg_surface != NULL ) {
414+ g_clear_pointer (& ctx .xdg_surface , xdg_surface_destroy );
415+ }
337416 if (ctx .surface != NULL ) {
338417 g_clear_pointer (& ctx .surface , wl_surface_destroy );
339418 }
@@ -371,6 +450,9 @@ void wl_deinit(void) {
371450 if (ctx .layer_shell )
372451 g_clear_pointer (& ctx .layer_shell , zwlr_layer_shell_v1_destroy );
373452
453+ if (ctx .xdg_shell )
454+ g_clear_pointer (& ctx .xdg_shell , xdg_wm_base_destroy );
455+
374456 if (ctx .compositor )
375457 g_clear_pointer (& ctx .compositor , wl_compositor_destroy );
376458
@@ -393,8 +475,6 @@ static void schedule_frame_and_commit(void);
393475
394476// Draw and commit a new frame.
395477static void send_frame (void ) {
396- int scale = wl_get_scale ();
397-
398478 if (wl_list_empty (& ctx .outputs )) {
399479 ctx .dirty = false;
400480 return ;
@@ -411,6 +491,14 @@ static void send_frame(void) {
411491 zwlr_layer_surface_v1_destroy (ctx .layer_surface );
412492 ctx .layer_surface = NULL ;
413493 }
494+ if (ctx .xdg_toplevel != NULL ) {
495+ xdg_toplevel_destroy (ctx .xdg_toplevel );
496+ ctx .xdg_toplevel = NULL ;
497+ }
498+ if (ctx .xdg_surface != NULL ) {
499+ xdg_surface_destroy (ctx .xdg_surface );
500+ ctx .xdg_surface = NULL ;
501+ }
414502 if (ctx .surface != NULL ) {
415503 wl_surface_destroy (ctx .surface );
416504 ctx .surface = NULL ;
@@ -443,20 +531,32 @@ static void send_frame(void) {
443531 // If we've made it here, there is something to draw. If the surface
444532 // doesn't exist (this is the first notification, or we moved to a
445533 // 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- }
534+ if (ctx .layer_surface == NULL && ctx .xdg_surface == NULL ) {
451535 ctx .layer_surface_output = output ;
452536 ctx .surface = wl_compositor_create_surface (ctx .compositor );
453537 wl_surface_add_listener (ctx .surface , & surface_listener , NULL );
454538
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 );
539+ if (ctx .layer_shell ) {
540+ struct wl_output * wl_output = NULL ;
541+ if (output != NULL ) {
542+ wl_output = output -> wl_output ;
543+ }
544+
545+ ctx .layer_surface = zwlr_layer_shell_v1_get_layer_surface (
546+ ctx .layer_shell , ctx .surface , wl_output ,
547+ settings .layer , "notifications" );
548+ zwlr_layer_surface_v1_add_listener (ctx .layer_surface ,
549+ & layer_surface_listener , NULL );
550+ } else {
551+ ctx .xdg_surface = xdg_wm_base_get_xdg_surface (
552+ ctx .xdg_shell , ctx .surface );
553+ xdg_surface_add_listener (ctx .xdg_surface , & xdg_surface_listener , NULL );
554+
555+ ctx .xdg_toplevel = xdg_surface_get_toplevel (ctx .xdg_surface );
556+ xdg_toplevel_set_title (ctx .xdg_toplevel , "Dunst" );
557+ xdg_toplevel_set_app_id (ctx .xdg_toplevel , "org.knopwob.dunst" );
558+ xdg_toplevel_add_listener (ctx .xdg_toplevel , & xdg_toplevel_listener , NULL );
559+ }
460560
461561 // Because we're creating a new surface, we aren't going to draw
462562 // anything into it during this call. We don't know what size the
@@ -468,56 +568,55 @@ static void send_frame(void) {
468568 // block to let it set the size for us.
469569 }
470570
471- assert (ctx .layer_surface );
571+ assert (ctx .layer_surface || ctx . xdg_surface );
472572
473573 // We now want to resize the surface if it isn't the right size. If the
474574 // surface is brand new, it doesn't even have a size yet. If it already
475575 // exists, we might need to resize if the list of notifications has changed
476576 // since the last time we drew.
477577 if (ctx .height != height || ctx .width != width ) {
478578 struct dimensions dim = ctx .cur_dim ;
479- // Set window size
480- zwlr_layer_surface_v1_set_size (ctx .layer_surface ,
579+ if (ctx .layer_surface ) {
580+ // Set window size
581+ zwlr_layer_surface_v1_set_size (ctx .layer_surface ,
481582 dim .w , dim .h );
482583
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
494-
495- wl_surface_commit (ctx .surface );
584+ // Put the window at the right position
585+ zwlr_layer_surface_v1_set_anchor (ctx .layer_surface ,
586+ settings .origin );
587+ zwlr_layer_surface_v1_set_margin (ctx .layer_surface ,
588+ // Offsets where no anchors are specified are
589+ // ignored. We can safely assume the offset is
590+ // positive.
591+ settings .offset .y , // top
592+ settings .offset .x , // right
593+ settings .offset .y , // bottom
594+ settings .offset .x );// left
595+ } else {
596+ // Just set the window size, as positioning is not part of the xdg-shell protocol
597+ xdg_surface_set_window_geometry (ctx .xdg_surface , 0 , 0 , dim .w , dim .h );
598+ }
496599
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 ;
600+ if (!ctx .configured ) {
601+ wl_surface_commit (ctx .surface );
602+
603+ LOG_W ("Doing a roundtrip" );
604+ // Now we're going to bail without drawing anything. This gives the
605+ // compositor a chance to create the surface and tell us what size we
606+ // were actually granted, which may be smaller than what we asked for
607+ // depending on the screen size and layout of other layer surfaces.
608+ // This information is provided in layer_surface_handle_configure,
609+ // which will then call send_frame again. When that call happens, the
610+ // layer surface will exist and the height will hopefully match what
611+ // we asked for. That means we won't return here, and will actually
612+ // draw into the surface down below.
613+ wl_display_roundtrip (ctx .display );
614+ return ;
615+ }
511616 }
512617
513618 assert (ctx .configured );
514619
515- // Yay we can finally draw something!
516- wl_surface_set_buffer_scale (ctx .surface , scale );
517- wl_surface_damage_buffer (ctx .surface , 0 , 0 , INT32_MAX , INT32_MAX );
518- wl_surface_attach (ctx .surface , ctx .current_buffer -> buffer , 0 , 0 );
519- ctx .current_buffer -> busy = true;
520-
521620 // Schedule a frame in case the state becomes dirty again
522621 schedule_frame_and_commit ();
523622
@@ -540,14 +639,25 @@ static const struct wl_callback_listener frame_listener = {
540639};
541640
542641static void schedule_frame_and_commit (void ) {
642+ int scale = wl_get_scale ();
643+
543644 if (ctx .frame_callback ) {
645+ // don't commit, as it probably won't make it to the display and
646+ // therefore waste resources
544647 return ;
545648 }
546649 if (ctx .surface == NULL ) {
547650 // We don't yet have a surface, create it immediately
548651 send_frame ();
549652 return ;
550653 }
654+
655+ // Yay we can finally draw something!
656+ wl_surface_set_buffer_scale (ctx .surface , scale );
657+ wl_surface_damage_buffer (ctx .surface , 0 , 0 , INT32_MAX , INT32_MAX );
658+ wl_surface_attach (ctx .surface , ctx .current_buffer -> buffer , 0 , 0 );
659+ ctx .current_buffer -> busy = true;
660+
551661 ctx .frame_callback = wl_surface_frame (ctx .surface );
552662 wl_callback_add_listener (ctx .frame_callback , & frame_listener , NULL );
553663 wl_surface_commit (ctx .surface );
0 commit comments