Merge branch 'wayland-survive-output-disable-enable'

Closes #106
This commit is contained in:
Daniel Eklöf 2022-01-01 11:48:39 +01:00
commit a59d3cfbd6
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
2 changed files with 197 additions and 77 deletions

View file

@ -28,6 +28,8 @@
* Made `libmpdclient` an optional dependency * Made `libmpdclient` an optional dependency
* battery: unknown battery states are now mapped to unknown, instead * battery: unknown battery states are now mapped to unknown, instead
of discharging. of discharging.
* Wayland: the bar no longer exits when the monitor is
disabled/unplugged (https://codeberg.org/dnkl/yambar/issues/106).
### Deprecated ### Deprecated

View file

@ -46,6 +46,7 @@ struct monitor {
struct wl_output *output; struct wl_output *output;
struct zxdg_output_v1 *xdg; struct zxdg_output_v1 *xdg;
char *name; char *name;
uint32_t wl_name;
int x; int x;
int y; int y;
@ -96,6 +97,7 @@ struct wayland_backend {
tll(struct monitor) monitors; tll(struct monitor) monitors;
const struct monitor *monitor; const struct monitor *monitor;
char *last_mapped_monitor;
int scale; int scale;
@ -113,6 +115,7 @@ struct wayland_backend {
tll(struct buffer) buffers; /* List of SHM buffers */ tll(struct buffer) buffers; /* List of SHM buffers */
struct buffer *next_buffer; /* Bar is rendering to this one */ struct buffer *next_buffer; /* Bar is rendering to this one */
struct buffer *pending_buffer; /* Finished, but not yet rendered */ struct buffer *pending_buffer; /* Finished, but not yet rendered */
struct wl_callback *frame_callback;
double aggregated_scroll; double aggregated_scroll;
bool have_discrete; bool have_discrete;
@ -492,12 +495,35 @@ output_scale(void *data, struct wl_output *wl_output, int32_t factor)
} }
} }
#if defined(WL_OUTPUT_NAME_SINCE_VERSION)
static void
output_name(void *data, struct wl_output *wl_output, const char *name)
{
struct monitor *mon = data;
free(mon->name);
mon->name = name != NULL ? strdup(name) : NULL;
}
#endif
#if defined(WL_OUTPUT_DESCRIPTION_SINCE_VERSION)
static void
output_description(void *data, struct wl_output *wl_output,
const char *description)
{
}
#endif
static const struct wl_output_listener output_listener = { static const struct wl_output_listener output_listener = {
.geometry = &output_geometry, .geometry = &output_geometry,
.mode = &output_mode, .mode = &output_mode,
.done = &output_done, .done = &output_done,
.scale = &output_scale, .scale = &output_scale,
#if defined(WL_OUTPUT_NAME_SINCE_VERSION)
.name = &output_name,
#endif
#if defined(WL_OUTPUT_DESCRIPTION_SINCE_VERSION)
.description = &output_description,
#endif
}; };
static void static void
@ -519,6 +545,9 @@ xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output,
mon->height_px = height; mon->height_px = height;
} }
static bool create_surface(struct wayland_backend *backend);
static void destroy_surface(struct wayland_backend *backend);
static void static void
xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output)
{ {
@ -531,13 +560,40 @@ xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output)
struct wayland_backend *backend = mon->backend; struct wayland_backend *backend = mon->backend;
struct private *bar = backend->bar->private; struct private *bar = backend->bar->private;
if (bar->monitor != NULL && mon->name != NULL && const bool is_mapped = backend->monitor != NULL;
strcmp(bar->monitor, mon->name) == 0) if (is_mapped) {
{ assert(backend->surface != NULL);
/* User specified a monitor, and this is one */ assert(backend->last_mapped_monitor == NULL);
backend->monitor = mon; return;
} }
const bool output_is_our_configured_monitor = (
bar->monitor != NULL &&
mon->name != NULL &&
strcmp(bar->monitor, mon->name) == 0);
const bool output_is_last_mapped = (
backend->last_mapped_monitor != NULL &&
mon->name != NULL &&
strcmp(backend->last_mapped_monitor, mon->name) == 0);
if (output_is_our_configured_monitor)
LOG_DBG("%s: using this monitor (user configured)", mon->name);
else if (output_is_last_mapped)
LOG_DBG("%s: using this monitor (last mapped)", mon->name);
if (output_is_our_configured_monitor || output_is_last_mapped) {
/* User specified a monitor, and this is one */
backend->monitor = mon;
free(backend->last_mapped_monitor);
backend->last_mapped_monitor = NULL;
if (create_surface(backend) && update_size(backend)) {
if (backend->pipe_fds[1] >= 0)
refresh(backend->bar);
}
}
} }
static void static void
@ -610,6 +666,7 @@ handle_global(void *data, struct wl_registry *registry,
tll_push_back(backend->monitors, ((struct monitor){ tll_push_back(backend->monitors, ((struct monitor){
.backend = backend, .backend = backend,
.wl_name = name,
.output = output})); .output = output}));
struct monitor *mon = &tll_back(backend->monitors); struct monitor *mon = &tll_back(backend->monitors);
@ -679,9 +736,23 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
} }
} }
LOG_WARN("unknown global removed: 0x%08x", name); tll_foreach(backend->monitors, it) {
struct monitor *mon = &it->item;
if (mon->wl_name == name) {
LOG_INFO("%s disconnected/disabled", mon->name);
/* TODO: need to handle displays and seats */ if (mon == backend->monitor) {
assert(backend->last_mapped_monitor == NULL);
backend->last_mapped_monitor = strdup(mon->name);
backend->monitor = NULL;
}
tll_remove(backend->monitors, it);
return;
}
}
LOG_WARN("unknown global removed: 0x%08x", name);
} }
static const struct wl_registry_listener registry_listener = { static const struct wl_registry_listener registry_listener = {
@ -703,22 +774,10 @@ layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface,
static void static void
layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface) layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface)
{ {
LOG_DBG("layer surface closed by compositor");
struct wayland_backend *backend = data; struct wayland_backend *backend = data;
destroy_surface(backend);
/*
* Called e.g. when an output is disabled. We don't get a
* corresponding event if/when that same output re-appears. So,
* for now, we simply shut down. In the future, we _could_ maybe
* destroy the surface, listen for output events and re-create the
* surface if the same output re-appears.
*/
LOG_WARN("compositor requested surface be closed - shutting down");
if (write(backend->bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t))
!= sizeof(uint64_t))
{
LOG_ERRNO("failed to signal abort to modules");
}
} }
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
@ -726,6 +785,82 @@ static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.closed = &layer_surface_closed, .closed = &layer_surface_closed,
}; };
static const struct wl_surface_listener surface_listener;
static bool
create_surface(struct wayland_backend *backend)
{
assert(tll_length(backend->monitors) > 0);
assert(backend->surface == NULL);
assert(backend->layer_surface == NULL);
struct bar *_bar = backend->bar;
struct private *bar = _bar->private;
backend->surface = wl_compositor_create_surface(backend->compositor);
if (backend->surface == NULL) {
LOG_ERR("failed to create panel surface");
return false;
}
wl_surface_add_listener(backend->surface, &surface_listener, backend);
enum zwlr_layer_shell_v1_layer layer = bar->layer == BAR_LAYER_BOTTOM
? ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM
: ZWLR_LAYER_SHELL_V1_LAYER_TOP;
backend->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
backend->layer_shell, backend->surface,
backend->monitor != NULL ? backend->monitor->output : NULL,
layer, "panel");
if (backend->layer_surface == NULL) {
LOG_ERR("failed to create layer shell surface");
return false;
}
zwlr_layer_surface_v1_add_listener(
backend->layer_surface, &layer_surface_listener, backend);
/* Aligned to top, maximum width */
enum zwlr_layer_surface_v1_anchor top_or_bottom = bar->location == BAR_TOP
? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
: ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
zwlr_layer_surface_v1_set_anchor(
backend->layer_surface,
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
top_or_bottom);
return true;
}
static void
destroy_surface(struct wayland_backend *backend)
{
if (backend->layer_surface != NULL)
zwlr_layer_surface_v1_destroy(backend->layer_surface);
if (backend->surface != NULL)
wl_surface_destroy(backend->surface);
if (backend->frame_callback != NULL)
wl_callback_destroy(backend->frame_callback);
if (backend->pending_buffer != NULL)
backend->pending_buffer->busy = false;
if (backend->next_buffer != NULL)
backend->next_buffer->busy = false;
backend->layer_surface = NULL;
backend->surface = NULL;
backend->frame_callback = NULL;
backend->pending_buffer = NULL;
backend->next_buffer = NULL;
backend->scale = 0;
backend->render_scheduled = false;
}
static void static void
buffer_release(void *data, struct wl_buffer *wl_buffer) buffer_release(void *data, struct wl_buffer *wl_buffer)
{ {
@ -892,6 +1027,8 @@ update_size(struct wayland_backend *backend)
const struct monitor *mon = backend->monitor; const struct monitor *mon = backend->monitor;
const int scale = mon != NULL ? mon->scale : guess_scale(backend); const int scale = mon != NULL ? mon->scale : guess_scale(backend);
assert(backend->surface != NULL);
if (backend->scale == scale) if (backend->scale == scale)
return true; return true;
@ -942,8 +1079,6 @@ update_size(struct wayland_backend *backend)
return true; return true;
} }
static const struct wl_surface_listener surface_listener;
static bool static bool
setup(struct bar *_bar) setup(struct bar *_bar)
{ {
@ -989,44 +1124,13 @@ setup(struct bar *_bar)
/* Trigger listeners registered in previous roundtrip */ /* Trigger listeners registered in previous roundtrip */
wl_display_roundtrip(backend->display); wl_display_roundtrip(backend->display);
backend->surface = wl_compositor_create_surface(backend->compositor); if (backend->surface == NULL && backend->layer_surface == NULL) {
if (backend->surface == NULL) { if (!create_surface(backend))
LOG_ERR("failed to create panel surface");
return false; return false;
}
wl_surface_add_listener(backend->surface, &surface_listener, backend);
enum zwlr_layer_shell_v1_layer layer = bar->layer == BAR_LAYER_BOTTOM
? ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM
: ZWLR_LAYER_SHELL_V1_LAYER_TOP;
backend->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
backend->layer_shell, backend->surface,
backend->monitor != NULL ? backend->monitor->output : NULL,
layer, "panel");
if (backend->layer_surface == NULL) {
LOG_ERR("failed to create layer shell surface");
return false;
}
zwlr_layer_surface_v1_add_listener(
backend->layer_surface, &layer_surface_listener, backend);
/* Aligned to top, maximum width */
enum zwlr_layer_surface_v1_anchor top_or_bottom = bar->location == BAR_TOP
? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
: ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
zwlr_layer_surface_v1_set_anchor(
backend->layer_surface,
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
top_or_bottom);
if (!update_size(backend)) if (!update_size(backend))
return false; return false;
}
assert(backend->monitor == NULL || assert(backend->monitor == NULL ||
backend->width / backend->monitor->scale <= backend->monitor->width_px); backend->width / backend->monitor->scale <= backend->monitor->width_px);
@ -1051,16 +1155,6 @@ cleanup(struct bar *_bar)
if (backend->pipe_fds[1] >= 0) if (backend->pipe_fds[1] >= 0)
close(backend->pipe_fds[1]); close(backend->pipe_fds[1]);
tll_foreach(backend->buffers, it) {
if (it->item.wl_buf != NULL)
wl_buffer_destroy(it->item.wl_buf);
if (it->item.pix != NULL)
pixman_image_unref(it->item.pix);
munmap(it->item.mmapped, it->item.size);
tll_remove(backend->buffers, it);
}
tll_foreach(backend->monitors, it) { tll_foreach(backend->monitors, it) {
struct monitor *mon = &it->item; struct monitor *mon = &it->item;
free(mon->name); free(mon->name);
@ -1071,6 +1165,7 @@ cleanup(struct bar *_bar)
wl_output_release(mon->output); wl_output_release(mon->output);
tll_remove(backend->monitors, it); tll_remove(backend->monitors, it);
} }
free(backend->last_mapped_monitor);
if (backend->xdg_output_manager != NULL) if (backend->xdg_output_manager != NULL)
zxdg_output_manager_v1_destroy(backend->xdg_output_manager); zxdg_output_manager_v1_destroy(backend->xdg_output_manager);
@ -1079,12 +1174,20 @@ cleanup(struct bar *_bar)
seat_destroy(&it->item); seat_destroy(&it->item);
tll_free(backend->seats); tll_free(backend->seats);
if (backend->layer_surface != NULL) destroy_surface(backend);
zwlr_layer_surface_v1_destroy(backend->layer_surface);
tll_foreach(backend->buffers, it) {
if (it->item.wl_buf != NULL)
wl_buffer_destroy(it->item.wl_buf);
if (it->item.pix != NULL)
pixman_image_unref(it->item.pix);
munmap(it->item.mmapped, it->item.size);
tll_remove(backend->buffers, it);
}
if (backend->layer_shell != NULL) if (backend->layer_shell != NULL)
zwlr_layer_shell_v1_destroy(backend->layer_shell); zwlr_layer_shell_v1_destroy(backend->layer_shell);
if (backend->surface != NULL)
wl_surface_destroy(backend->surface);
if (backend->compositor != NULL) if (backend->compositor != NULL)
wl_compositor_destroy(backend->compositor); wl_compositor_destroy(backend->compositor);
if (backend->shm != NULL) if (backend->shm != NULL)
@ -1209,7 +1312,15 @@ surface_leave(void *data, struct wl_surface *wl_surface,
struct wl_output *wl_output) struct wl_output *wl_output)
{ {
struct wayland_backend *backend = data; struct wayland_backend *backend = data;
const struct monitor *mon = backend->monitor;
assert(mon != NULL);
assert(mon->output == wl_output);
backend->monitor = NULL; backend->monitor = NULL;
assert(backend->last_mapped_monitor == NULL);
backend->last_mapped_monitor = mon->name != NULL ? strdup(mon->name) : NULL;
} }
static const struct wl_surface_listener surface_listener = { static const struct wl_surface_listener surface_listener = {
@ -1233,7 +1344,9 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da
backend->render_scheduled = false; backend->render_scheduled = false;
assert(wl_callback == backend->frame_callback);
wl_callback_destroy(wl_callback); wl_callback_destroy(wl_callback);
backend->frame_callback = NULL;
if (backend->pending_buffer != NULL) { if (backend->pending_buffer != NULL) {
struct buffer *buffer = backend->pending_buffer; struct buffer *buffer = backend->pending_buffer;
@ -1248,6 +1361,7 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da
wl_surface_commit(backend->surface); wl_surface_commit(backend->surface);
wl_display_flush(backend->display); wl_display_flush(backend->display);
backend->frame_callback = cb;
backend->pending_buffer = NULL; backend->pending_buffer = NULL;
backend->render_scheduled = true; backend->render_scheduled = true;
} else } else
@ -1262,6 +1376,9 @@ commit(const struct bar *_bar)
//printf("commit: %dxl%d\n", backend->width, backend->height); //printf("commit: %dxl%d\n", backend->width, backend->height);
if (backend->next_buffer == NULL)
return;
assert(backend->next_buffer != NULL); assert(backend->next_buffer != NULL);
assert(backend->next_buffer->busy); assert(backend->next_buffer->busy);
@ -1289,6 +1406,7 @@ commit(const struct bar *_bar)
wl_display_flush(backend->display); wl_display_flush(backend->display);
backend->render_scheduled = true; backend->render_scheduled = true;
backend->frame_callback = cb;
} }
backend->next_buffer = get_buffer(backend); backend->next_buffer = get_buffer(backend);
@ -1336,7 +1454,7 @@ set_cursor(struct bar *_bar, const char *cursor)
} }
static const char * static const char *
output_name(const struct bar *_bar) bar_output_name(const struct bar *_bar)
{ {
const struct private *bar = _bar->private; const struct private *bar = _bar->private;
const struct wayland_backend *backend = bar->backend.data; const struct wayland_backend *backend = bar->backend.data;
@ -1351,5 +1469,5 @@ const struct backend wayland_backend_iface = {
.commit = &commit, .commit = &commit,
.refresh = &refresh, .refresh = &refresh,
.set_cursor = &set_cursor, .set_cursor = &set_cursor,
.output_name = &output_name, .output_name = &bar_output_name,
}; };