Dunst
Lightweight notification daemon
Loading...
Searching...
No Matches
wl.c
Go to the documentation of this file.
1/* SPDX-License-Identifier: BSD-3-Clause */
7
8#define _POSIX_C_SOURCE 200112L
9#include <assert.h>
10#include <stdlib.h>
11#include <stdio.h>
12#include <stdbool.h>
13#include <wayland-client.h>
14#include <wayland-client-protocol.h>
15#include <wayland-util.h>
16#include <wayland-cursor.h>
17#include <errno.h>
18#include <fcntl.h>
19#include <sys/mman.h>
20#include <time.h>
21#include <unistd.h>
22#include <linux/input-event-codes.h>
23#include <string.h>
24#include <glib.h>
25
26#include "protocols/xdg-shell-client-header.h"
27#include "protocols/xdg-shell.h"
28#include "protocols/wlr-layer-shell-unstable-v1-client-header.h"
29#include "protocols/wlr-layer-shell-unstable-v1.h"
30#include "protocols/wlr-foreign-toplevel-management-unstable-v1-client-header.h"
31#include "protocols/wlr-foreign-toplevel-management-unstable-v1.h"
32
33#include "wl.h"
34#include "pool-buffer.h"
35#include "../dunst.h"
36#include "../log.h"
37#include "../settings.h"
38#include "libgwater-wayland.h"
39#include "foreign_toplevel.h"
40#include "wl_ctx.h"
41#include "wl_output.h"
42#include "wl_seat.h"
43
44struct window_wl {
45 cairo_surface_t *c_surface;
46 cairo_t * c_ctx;
47};
48
49struct wl_ctx ctx = { 0 };
50
51static void surface_handle_enter(void *data, struct wl_surface *surface,
52 struct wl_output *wl_output) {
53 // Don't bother keeping a list of outputs, a layer surface can only be on
54 // one output a a time
55 ctx.surface_output = wl_output_get_user_data(wl_output);
56 set_dirty();
57}
58
59static void surface_handle_leave(void *data, struct wl_surface *surface,
60 struct wl_output *wl_output) {
61 ctx.surface_output = NULL;
62}
63
64static const struct wl_surface_listener surface_listener = {
65 .enter = surface_handle_enter,
66 .leave = surface_handle_leave,
67};
68
69
70static void schedule_frame_and_commit(void);
71static void send_frame(void);
72
73static void layer_surface_handle_configure(void *data,
74 struct zwlr_layer_surface_v1 *surface,
75 uint32_t serial, uint32_t width, uint32_t height) {
76 zwlr_layer_surface_v1_ack_configure(surface, serial);
77
78 if (ctx.configured &&
79 ctx.width == (int32_t) width &&
80 ctx.height == (int32_t) height) {
81 wl_surface_commit(ctx.surface);
82 return;
83 }
84
85 ctx.configured = true;
86 ctx.width = width;
87 ctx.height = height;
88
89 send_frame();
90}
91
92static void xdg_surface_handle_configure(void *data,
93 struct xdg_surface *surface,
94 uint32_t serial) {
95 xdg_surface_ack_configure(ctx.xdg_surface, serial);
96
97 if (ctx.configured) {
98 wl_surface_commit(ctx.surface);
99 return;
100 }
101
102 ctx.configured = true;
103
104 send_frame();
105}
106
107static void xdg_toplevel_handle_configure(void *data,
108 struct xdg_toplevel *xdg_toplevel,
109 int32_t width,
110 int32_t height,
111 struct wl_array *states) {
112 // TODO We currently don't support the compositor
113 // providing us with a size different to our requested size.
114 // Therefore just ignore the suggested surface size.
115}
116
117static void surface_handle_closed(void) {
118 if (ctx.surface)
119 wl_surface_destroy(ctx.surface);
120 ctx.surface = NULL;
121
122 if (ctx.frame_callback) {
123 wl_callback_destroy(ctx.frame_callback);
124 ctx.frame_callback = NULL;
125 ctx.dirty = true;
126 }
127
128 if (ctx.configured) {
129 ctx.configured = false;
130 ctx.width = ctx.height = 0;
131 ctx.dirty = true;
132 }
133
134 if (ctx.dirty) {
135 schedule_frame_and_commit();
136 }
137}
138
139static void layer_surface_handle_closed(void *data,
140 struct zwlr_layer_surface_v1 *surface) {
141 LOG_I("Destroying layer");
142 if (ctx.layer_surface)
143 zwlr_layer_surface_v1_destroy(ctx.layer_surface);
144 ctx.layer_surface = NULL;
145
146 surface_handle_closed();
147}
148
149static void xdg_toplevel_handle_close(void *data,
150 struct xdg_toplevel *surface) {
151 LOG_I("Destroying layer");
152 if (ctx.xdg_toplevel)
153 xdg_toplevel_destroy(ctx.xdg_toplevel);
154 ctx.xdg_toplevel = NULL;
155 if (ctx.xdg_surface)
156 xdg_surface_destroy(ctx.xdg_surface);
157 ctx.xdg_surface = NULL;
158
159 surface_handle_closed();
160}
161
162static void xdg_wm_base_handle_ping(void *data,
163 struct xdg_wm_base *xdg_wm_base,
164 uint32_t serial) {
165 xdg_wm_base_pong(ctx.xdg_shell, serial);
166}
167
168static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
169 .configure = layer_surface_handle_configure,
170 .closed = layer_surface_handle_closed,
171};
172
173static const struct xdg_wm_base_listener xdg_wm_base_listener = {
174 .ping = xdg_wm_base_handle_ping,
175};
176
177static const struct xdg_surface_listener xdg_surface_listener = {
178 .configure = xdg_surface_handle_configure,
179};
180
181static const struct xdg_toplevel_listener xdg_toplevel_listener = {
182 .configure = xdg_toplevel_handle_configure,
183 .close = xdg_toplevel_handle_close,
184};
185
186// Warning, can return NULL
187static struct dunst_output *get_configured_output(void) {
188 int n = 0;
189 int target_monitor = settings.monitor_num;
190 const char *name = settings.monitor;
191
192 struct dunst_output *first_output = NULL, *configured_output = NULL,
193 *tmp_output = NULL;
194 wl_list_for_each(tmp_output, &ctx.outputs, link) {
195 if (n == 0)
196 first_output = tmp_output;
197 if (n == target_monitor)
198 configured_output = tmp_output;
199 if (g_strcmp0(name, tmp_output->name) == 0)
200 configured_output = tmp_output;
201 n++;
202 }
203
204 // There's only 1 output, so return that
205 if (n == 1)
206 return first_output;
207
208 switch (settings.f_mode){
209 case FOLLOW_NONE:
210 if (!configured_output) {
211 LOG_W("Screen '%s' not found, using focused monitor", settings.monitor);
212 }
213 return configured_output;
214 case FOLLOW_MOUSE:
215 // fallthrough
216 case FOLLOW_KEYBOARD:
217 // fallthrough
218 default:
219 return NULL;
220 }
221}
222
223static void handle_global(void *data, struct wl_registry *registry,
224 uint32_t name, const char *interface, uint32_t version) {
225 if (strcmp(interface, wl_compositor_interface.name) == 0) {
226 ctx.compositor = wl_registry_bind(registry, name,
227 &wl_compositor_interface, 4);
228 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
229 ctx.shm = wl_registry_bind(registry, name,
230 &wl_shm_interface, 1);
231 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
232 ctx.layer_shell = wl_registry_bind(registry, name,
233 &zwlr_layer_shell_v1_interface, 1);
234 } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
235 ctx.xdg_shell = wl_registry_bind(registry, name,
236 &xdg_wm_base_interface, 1);
237 xdg_wm_base_add_listener(ctx.xdg_shell, &xdg_wm_base_listener, NULL);
238 } else if (strcmp(interface, wl_seat_interface.name) == 0) {
239 create_seat(registry, name, version);
240 LOG_D("Binding to seat %i", name);
241 } else if (strcmp(interface, wl_output_interface.name) == 0) {
242 create_output(registry, name, version);
243 LOG_D("Binding to output %i", name);
244 } else if (strcmp(interface, org_kde_kwin_idle_interface.name) == 0 &&
245 version >= ORG_KDE_KWIN_IDLE_TIMEOUT_IDLE_SINCE_VERSION) {
246#ifdef HAVE_WL_EXT_IDLE_NOTIFY
247 if (ctx.ext_idle_notifier)
248 return;
249#endif
250 ctx.idle_handler = wl_registry_bind(registry, name, &org_kde_kwin_idle_interface, 1);
251 LOG_D("Found org_kde_kwin_idle");
252#ifdef HAVE_WL_EXT_IDLE_NOTIFY
253 } else if (strcmp(interface, ext_idle_notifier_v1_interface.name) == 0) {
254 ctx.ext_idle_notifier = wl_registry_bind(registry, name, &ext_idle_notifier_v1_interface, 1);
255 LOG_D("Found ext-idle-notify-v1");
256#endif
257#ifdef HAVE_WL_CURSOR_SHAPE
258 } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
259 ctx.cursor_shape_manager = wl_registry_bind(registry, name,
260 &wp_cursor_shape_manager_v1_interface, 1);
261#endif
262 } else if (strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name) == 0 &&
263 version >= ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN_SINCE_VERSION) {
264 LOG_D("Found toplevel manager %i", name);
265 ctx.toplevel_manager_name = name;
266 }
267}
268
269static void handle_global_remove(void *data, struct wl_registry *registry,
270 uint32_t name) {
271 struct dunst_output *output, *tmp;
272 wl_list_for_each_safe(output, tmp, &ctx.outputs, link) {
273 if (output->global_name == name) {
274 destroy_output(output);
275 return;
276 }
277 }
278
279 struct dunst_seat *seat, *tmp_seat;
280 wl_list_for_each_safe(seat, tmp_seat, &ctx.seats, link) {
281 if (seat->global_name == name) {
282 destroy_seat(seat);
283 return;
284 }
285 }
286}
287
288static const struct wl_registry_listener registry_listener = {
289 .global = handle_global,
290 .global_remove = handle_global_remove,
291};
292
293bool wl_init(void) {
294 ctx.dirty = false;
295 wl_list_init(&ctx.outputs);
296 wl_list_init(&ctx.seats);
297 wl_list_init(&toplevel_list);
298
299 ctx.esrc = g_water_wayland_source_new(NULL, NULL);
300 ctx.display = g_water_wayland_source_get_display(ctx.esrc);
301#if GLIB_CHECK_VERSION(2, 58, 0)
302 g_water_wayland_source_set_error_callback(ctx.esrc, G_SOURCE_FUNC(quit_signal), NULL, NULL);
303#else
304 g_water_wayland_source_set_error_callback(ctx.esrc, (GSourceFunc)quit_signal, NULL, NULL);
305#endif
306
307 if (ctx.display == NULL) {
308 LOG_W("failed to create display");
309 return false;
310 }
311
312 ctx.toplevel_manager_name = UINT32_MAX;
313
314 ctx.registry = wl_display_get_registry(ctx.display);
315 wl_registry_add_listener(ctx.registry, &registry_listener, NULL);
316 wl_display_roundtrip(ctx.display);
317 if (ctx.toplevel_manager_name != UINT32_MAX) {
318 ctx.toplevel_manager = wl_registry_bind(ctx.registry, ctx.toplevel_manager_name,
319 &zwlr_foreign_toplevel_manager_v1_interface,
320 2);
321 zwlr_foreign_toplevel_manager_v1_add_listener(ctx.toplevel_manager,
322 &toplevel_manager_impl, NULL);
323 }
324 wl_display_roundtrip(ctx.display); // load list of toplevels
325 wl_display_roundtrip(ctx.display); // load toplevel details
326 wl_display_flush(ctx.display);
327
328 if (ctx.compositor == NULL) {
329 LOG_W("compositor doesn't support wl_compositor");
330 return false;
331 }
332 if (ctx.shm == NULL) {
333 LOG_W("compositor doesn't support wl_shm");
334 return false;
335 }
336 if (ctx.layer_shell == NULL) {
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 }
343 }
344 if (wl_list_empty(&ctx.seats)) {
345 LOG_W("no seat was found, so dunst cannot see input");
346 } else if (ctx.idle_handler == NULL
347#ifdef HAVE_WL_EXT_IDLE_NOTIFY
348 && ctx.ext_idle_notifier == NULL
349#endif
350 ) {
351 LOG_I("compositor doesn't support org_kde_kwin_idle or ext-idle-notify-v1");
352 } else {
353 // Set up idle monitoring for any seats that we received before the idle protocols
354 struct dunst_seat *seat = NULL;
355 wl_list_for_each(seat, &ctx.seats, link) {
356 add_seat_to_idle_handler(seat);
357 }
358 }
359
360 if (ctx.toplevel_manager == NULL) {
361 LOG_W("compositor doesn't support zwlr_foreign_toplevel_v1. Fullscreen detection won't work");
362 }
363
364 // Set up the cursor. It needs a wl_surface with the cursor loaded into it.
365 // If one of these fail, dunst will work fine without the cursor being able to change.
366 const char *cursor_size_env = getenv("XCURSOR_SIZE");
367 int cursor_size = 24;
368 if (cursor_size_env != NULL) {
369 errno = 0;
370 char *end;
371 int temp_size = (int)strtol(cursor_size_env, &end, 10);
372 if (errno == 0 && cursor_size_env[0] != 0 && end[0] == 0 && temp_size > 0) {
373 cursor_size = temp_size;
374 } else {
375 LOG_W("Error: XCURSOR_SIZE is invalid");
376 }
377 }
378 const char *xcursor_theme = getenv("XCURSOR_THEME");
379 ctx.cursor_theme = wl_cursor_theme_load(xcursor_theme, cursor_size, ctx.shm);
380 if (ctx.cursor_theme == NULL) {
381 LOG_W("couldn't find a cursor theme");
382 return true;
383 }
384 // Try cursor spec (CSS) name first
385 struct wl_cursor *cursor = wl_cursor_theme_get_cursor(ctx.cursor_theme, "default");
386 if (cursor == NULL) {
387 // Try legacy Xcursor name
388 cursor = wl_cursor_theme_get_cursor(ctx.cursor_theme, "left_ptr");
389 }
390 if (cursor == NULL) {
391 LOG_W("couldn't find cursor icons \"default\" or \"left_ptr\"");
392 wl_cursor_theme_destroy(ctx.cursor_theme);
393 // Set to NULL so it doesn't get free'd again
394 ctx.cursor_theme = NULL;
395 return true;
396 }
397 ctx.cursor_image = cursor->images[0];
398 struct wl_buffer *cursor_buffer = wl_cursor_image_get_buffer(cursor->images[0]);
399 ctx.cursor_surface = wl_compositor_create_surface(ctx.compositor);
400 wl_surface_attach(ctx.cursor_surface, cursor_buffer, 0, 0);
401 wl_surface_commit(ctx.cursor_surface);
402
403 return true;
404}
405
406void wl_deinit(void) {
407 // We need to check if any of these are NULL, since the initialization
408 // could have been aborted half way through, or the compositor doesn't
409 // support some of these features.
410 if (ctx.layer_surface != NULL) {
411 g_clear_pointer(&ctx.layer_surface, zwlr_layer_surface_v1_destroy);
412 }
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 }
419 if (ctx.frame_callback) {
420 g_clear_pointer(&ctx.frame_callback, wl_callback_destroy);
421 }
422 if (ctx.surface != NULL) {
423 g_clear_pointer(&ctx.surface, wl_surface_destroy);
424 }
425 finish_buffer(&ctx.buffers[0]);
426 finish_buffer(&ctx.buffers[1]);
427 ctx.current_buffer = NULL;
428
429 // The output list is initialized at the start of init, so no need to
430 // check for NULL
431 struct dunst_output *output, *output_tmp;
432 wl_list_for_each_safe(output, output_tmp, &ctx.outputs, link) {
433 destroy_output(output);
434 }
435
436 struct dunst_seat *seat, *seat_tmp;
437 wl_list_for_each_safe(seat, seat_tmp, &ctx.seats, link) {
438 destroy_seat(seat);
439 }
440
441 ctx.outputs = (struct wl_list) {0};
442 ctx.seats = (struct wl_list) {0};
443
444#ifdef HAVE_WL_CURSOR_SHAPE
445 if (ctx.cursor_shape_manager)
446 g_clear_pointer(&ctx.cursor_shape_manager, wp_cursor_shape_manager_v1_destroy);
447#endif
448
449#ifdef HAVE_WL_EXT_IDLE_NOTIFY
450 if (ctx.ext_idle_notifier)
451 g_clear_pointer(&ctx.ext_idle_notifier, ext_idle_notifier_v1_destroy);
452#endif
453
454 if (ctx.idle_handler)
455 g_clear_pointer(&ctx.idle_handler, org_kde_kwin_idle_destroy);
456
457 if (ctx.layer_shell)
458 g_clear_pointer(&ctx.layer_shell, zwlr_layer_shell_v1_destroy);
459
460 if (ctx.xdg_shell)
461 g_clear_pointer(&ctx.xdg_shell, xdg_wm_base_destroy);
462
463 if (ctx.compositor)
464 g_clear_pointer(&ctx.compositor, wl_compositor_destroy);
465
466 if (ctx.shm)
467 g_clear_pointer(&ctx.shm, wl_shm_destroy);
468
469 if (ctx.registry)
470 g_clear_pointer(&ctx.registry, wl_registry_destroy);
471
472 if (ctx.cursor_theme != NULL) {
473 g_clear_pointer(&ctx.cursor_theme, wl_cursor_theme_destroy);
474 g_clear_pointer(&ctx.cursor_surface, wl_surface_destroy);
475 ctx.cursor_image = NULL;
476 }
477
478 if (ctx.toplevel_manager) {
479 zwlr_foreign_toplevel_manager_v1_stop(ctx.toplevel_manager);
480 g_clear_pointer(&ctx.toplevel_manager, zwlr_foreign_toplevel_manager_v1_destroy);
481 // Set it to the default initialization value instead to UINT32_MAX
482 // (the latter is used on initialization to mark a bad value)
483 ctx.toplevel_manager_name = 0;
484 }
485
486 // this also disconnects the wl_display
487 g_clear_pointer(&ctx.esrc, g_water_wayland_source_free);
488 ctx.display = NULL;
489}
490
491static void schedule_frame_and_commit(void);
492
493// Draw and commit a new frame.
494static void send_frame(void) {
495 int scale = wl_get_scale();
496
497 if (wl_list_empty(&ctx.outputs)) {
498 ctx.dirty = false;
499 return;
500 }
501
502 struct dunst_output *output = get_configured_output();
503 int height = ctx.cur_dim.h;
504 int width = ctx.cur_dim.w;
505
506 // There are two cases where we want to tear down the surface: zero
507 // notifications (height = 0) or moving between outputs.
508 if (height == 0 || ctx.layer_surface_output != output) {
509 if (ctx.layer_surface != NULL) {
510 zwlr_layer_surface_v1_destroy(ctx.layer_surface);
511 ctx.layer_surface = NULL;
512 }
513 if (ctx.xdg_toplevel != NULL) {
514 xdg_toplevel_destroy(ctx.xdg_toplevel);
515 ctx.xdg_toplevel = NULL;
516 }
517 if (ctx.xdg_surface != NULL) {
518 xdg_surface_destroy(ctx.xdg_surface);
519 ctx.xdg_surface = NULL;
520 }
521 if (ctx.surface != NULL) {
522 wl_surface_destroy(ctx.surface);
523 ctx.surface = NULL;
524 }
525 ctx.width = ctx.height = 0;
526 ctx.surface_output = NULL;
527 ctx.configured = false;
528 }
529
530 {
531 struct dunst_output *o;
532 int i = 0;
533 wl_list_for_each(o, &ctx.outputs, link) {
534 i++;
535 }
536 if (i == 0) {
537 // There are no outputs, so don't create a surface
538 ctx.dirty = false;
539 return;
540 }
541 }
542
543 // If there are no notifications, there's no point in recreating the
544 // surface right now.
545 if (height == 0) {
546 ctx.dirty = false;
547 return;
548 }
549
550 // If we've made it here, there is something to draw. If the surface
551 // doesn't exist (this is the first notification, or we moved to a
552 // different output), we need to create it.
553 if (ctx.layer_surface == NULL && ctx.xdg_surface == NULL) {
554 ctx.layer_surface_output = output;
555 ctx.surface = wl_compositor_create_surface(ctx.compositor);
556 wl_surface_add_listener(ctx.surface, &surface_listener, NULL);
557
558 if (ctx.layer_shell) {
559 struct wl_output *wl_output = NULL;
560 if (output != NULL) {
561 wl_output = output->wl_output;
562 }
563
564 ctx.layer_surface = zwlr_layer_shell_v1_get_layer_surface(
565 ctx.layer_shell, ctx.surface, wl_output,
566 settings.layer, "notifications");
567 zwlr_layer_surface_v1_add_listener(ctx.layer_surface,
568 &layer_surface_listener, NULL);
569 } else {
570 ctx.xdg_surface = xdg_wm_base_get_xdg_surface(
571 ctx.xdg_shell, ctx.surface);
572 xdg_surface_add_listener(ctx.xdg_surface, &xdg_surface_listener, NULL);
573
574 ctx.xdg_toplevel = xdg_surface_get_toplevel(ctx.xdg_surface);
575 xdg_toplevel_set_title(ctx.xdg_toplevel, "Dunst");
576 xdg_toplevel_set_app_id(ctx.xdg_toplevel, "org.knopwob.dunst");
577 xdg_toplevel_add_listener(ctx.xdg_toplevel, &xdg_toplevel_listener, NULL);
578 }
579
580 // Because we're creating a new surface, we aren't going to draw
581 // anything into it during this call. We don't know what size the
582 // surface will be until we've asked the compositor for what we want
583 // and it has responded with what it actually gave us. We also know
584 // that the height we would _like_ to draw (greater than zero, or we
585 // would have bailed already) is different from our ctx.height
586 // (which has to be zero here), so we can fall through to the next
587 // block to let it set the size for us.
588 }
589
590 assert(ctx.layer_surface || ctx.xdg_surface);
591
592 // We now want to resize the surface if it isn't the right size. If the
593 // surface is brand new, it doesn't even have a size yet. If it already
594 // exists, we might need to resize if the list of notifications has changed
595 // since the last time we drew.
596 // We only do this for layer_surface, as xdg_surface only needs configuration
597 // using xdg_surface_set_window_geometry if it differs from the buffer dimension.
598 // Furthermore mutter intersects the buffer dimension and the window geometry.
599 // As we directly do a commit+roundtrip, mutter will complain, as the missing
600 // buffer causes a 0,0 intersection and also causes a size of 0,0 in the
601 // configure event.
602 if (ctx.layer_surface && (ctx.height != height || ctx.width != width)) {
603 // TODO If the surface is already configured we should rather
604 // adjust to the size the compositor requested instead of just trying
605 // to set our preferred size again.
606 struct dimensions dim = ctx.cur_dim;
607 // Set window size
608 zwlr_layer_surface_v1_set_size(ctx.layer_surface,
609 dim.w, dim.h);
610
611 // Put the window at the right position
612 zwlr_layer_surface_v1_set_anchor(ctx.layer_surface,
613 settings.origin);
614 zwlr_layer_surface_v1_set_margin(ctx.layer_surface,
615 // Offsets where no anchors are specified are
616 // ignored. We can safely assume the offset is
617 // positive.
618 settings.offset.y, // top
619 settings.offset.x, // right
620 settings.offset.y, // bottom
621 settings.offset.x);// left
622
623 // TODO Check if this is really necessary, as it causes an infinite
624 // loop if the compositor doesn't configure our desired surface size
625 // Without it wlroots 0.18.2 creates a wrong configure serial error
626 // which seems to be caused by an ack_configure without an commit.
627 ctx.configured = false;
628 }
629
630 if (!ctx.configured) {
631 wl_surface_commit(ctx.surface);
632
633 // Now we're going to bail without drawing anything. This gives the
634 // compositor a chance to create the surface and tell us what size we
635 // were actually granted, which may be smaller than what we asked for
636 // depending on the screen size and layout of other layer surfaces.
637 // This information is provided in layer_surface_handle_configure,
638 // which will then call send_frame again. When that call happens, the
639 // layer surface will exist and the height will hopefully match what
640 // we asked for. That means we won't return here, and will actually
641 // draw into the surface down below.
642 wl_display_roundtrip(ctx.display);
643 return;
644 }
645
646 assert(ctx.configured);
647
648 // Yay we can finally draw something!
649 wl_surface_set_buffer_scale(ctx.surface, scale);
650 wl_surface_damage_buffer(ctx.surface, 0, 0, INT32_MAX, INT32_MAX);
651 wl_surface_attach(ctx.surface, ctx.current_buffer->buffer, 0, 0);
652 ctx.current_buffer->busy = true;
653
654 // Schedule a frame in case the state becomes dirty again
655 schedule_frame_and_commit();
656
657 ctx.dirty = false;
658}
659
660static void frame_handle_done(void *data, struct wl_callback *callback,
661 uint32_t time) {
662 wl_callback_destroy(ctx.frame_callback);
663 ctx.frame_callback = NULL;
664
665 // Only draw again if we need to
666 if (ctx.dirty) {
667 send_frame();
668 }
669}
670
671static const struct wl_callback_listener frame_listener = {
672 .done = frame_handle_done,
673};
674
675static void schedule_frame_and_commit(void) {
676 if (ctx.frame_callback) {
677 // don't commit, as it probably won't make it to the display and
678 // therefore waste resources
679 return;
680 }
681 if (ctx.surface == NULL) {
682 // We don't yet have a surface, create it immediately
683 send_frame();
684 return;
685 }
686 ctx.frame_callback = wl_surface_frame(ctx.surface);
687 wl_callback_add_listener(ctx.frame_callback, &frame_listener, NULL);
688 wl_surface_commit(ctx.surface);
689}
690
691void set_dirty(void) {
692 if (ctx.dirty) {
693 return;
694 }
695 ctx.dirty = true;
696 schedule_frame_and_commit();
697}
698
699window wl_win_create(void) {
700 struct window_wl *win = g_malloc0(sizeof(struct window_wl));
701 return win;
702}
703
704void wl_win_destroy(window winptr) {
705 struct window_wl *win = (struct window_wl*)winptr;
706 // FIXME: Dealloc everything
707 g_free(win);
708}
709
710void wl_win_show(window win) {
711 // This is here for compatibilty with the X11 output. The window is
712 // already shown in wl_display_surface.
713}
714
715void wl_win_hide(window win) {
716 LOG_I("Wayland: Hiding window");
717 ctx.cur_dim.h = 0;
718 set_dirty();
719 wl_display_roundtrip(ctx.display);
720}
721
722void wl_display_surface(cairo_surface_t *srf, window winptr, const struct dimensions* dim) {
723 /* struct window_wl *win = (struct window_wl*)winptr; */
724 int scale = wl_get_scale();
725 LOG_D("Buffer size (scaled) %ix%i", dim->w * scale, dim->h * scale);
726 ctx.current_buffer = get_next_buffer(ctx.shm, ctx.buffers,
727 dim->w * scale, dim->h * scale);
728
729 if(ctx.current_buffer == NULL) {
730 return;
731 }
732
733 cairo_t *c = ctx.current_buffer->cairo;
734 cairo_save(c);
735 cairo_set_source_surface(c, srf, 0, 0);
736 cairo_rectangle(c, 0, 0, dim->w * scale, dim->h * scale);
737 cairo_fill(c);
738 cairo_restore(c);
739
740 ctx.cur_dim = *dim;
741
742 set_dirty();
743 wl_display_roundtrip(ctx.display);
744}
745
746cairo_t* wl_win_get_context(window winptr) {
747 struct window_wl *win = (struct window_wl*)winptr;
748 ctx.current_buffer = get_next_buffer(ctx.shm, ctx.buffers, 500, 500);
749
750 if(ctx.current_buffer == NULL) {
751 return NULL;
752 }
753
754 win->c_surface = ctx.current_buffer->surface;
755 win->c_ctx = ctx.current_buffer->cairo;
756 return win->c_ctx;
757}
758
759const struct screen_info* wl_get_active_screen(void) {
760 static struct screen_info scr = {
761 .w = 3840,
762 .h = 2160,
763 .x = 0,
764 .y = 0,
765 .id = 0,
766 .mmh = 500
767 };
768 scr.dpi = wl_get_scale() * 96;
769
770 struct dunst_output *current_output = get_configured_output();
771 if (current_output != NULL) {
772 scr.w = current_output->width;
773 scr.h = current_output->height;
774 return &scr;
775 } else {
776 // Using auto output. We don't know on which output we are
777 // (although we might find it out by looking at the list of
778 // toplevels).
779 return &scr;
780 }
781}
782
783bool wl_is_idle(void) {
784 struct dunst_seat *seat = NULL;
785 bool is_idle = true;
786 // When the user doesn't have a seat, or their compositor doesn't support the idle
787 // protocol, we'll assume that they are not idle.
788 if (settings.idle_threshold == 0 || wl_list_empty(&ctx.seats)) {
789 is_idle = false;
790 } else {
791 wl_list_for_each(seat, &ctx.seats, link) {
792 // seat.is_idle cannot be set without an idle protocol
793 is_idle &= seat->is_idle;
794 }
795 }
796 LOG_I("Idle status queried: %i", is_idle);
797 return is_idle;
798}
799
800bool wl_have_fullscreen_window(void) {
801 struct dunst_output *current_output = get_configured_output();
802 uint32_t output_name = UINT32_MAX;
803 if (current_output)
804 output_name = current_output->global_name;
805
806 struct toplevel_v1 *toplevel;
807 wl_list_for_each(toplevel, &toplevel_list, link) {
808 if (!(toplevel->current.state & TOPLEVEL_STATE_FULLSCREEN &&
809 toplevel->current.state &
810 TOPLEVEL_STATE_ACTIVATED))
811 continue;
812
813 struct toplevel_output *pos;
814 wl_list_for_each(pos, &toplevel->output_list, link) {
815 if (output_name == UINT32_MAX ||
816 pos->dunst_output->global_name ==
817 output_name) {
818 return true;
819 }
820 }
821 }
822 return false;
823}
824
825double wl_get_scale(void) {
826 int scale = 0;
827 struct dunst_output *output = get_configured_output();
828 if (output) {
829 scale = output->scale;
830 } else {
831 // return the largest scale
832 struct dunst_output *output;
833 wl_list_for_each(output, &ctx.outputs, link) {
834 scale = MAX((int)output->scale, scale);
835 }
836 }
837 if (scale <= 0)
838 scale = 1;
839 return scale;
840}
Main event loop logic.
Wayland foreign toplevel support.
Logging subsystem and helpers.
Wayland rendering buffer pool.
Type definitions for settings.
struct position offset
Definition settings.h:189
Definition wl.c:44
Wayland window managment.
Wayland context tracking.
Wayland output wrapper.
Wayland seat managment.