From ed8e17c6c915d52866ba0ced2cee5368fc36e871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 3 Feb 2019 11:08:53 +0100 Subject: [PATCH] wayland: mucho wip: initial sort-of-working wayland backend Bar is drawn, though only TOP is supported atm. No screen/display/output selection is possible yet. Mouse *click* works, but not setting the cursor. Lots of debug output, crappy code yada yada. --- CMakeLists.txt | 51 ++++- bar/bar.c | 21 +- bar/wayland.c | 587 +++++++++++++++++++++++++++++++++++++++++++++++++ bar/wayland.h | 7 + main.c | 2 +- 5 files changed, 658 insertions(+), 10 deletions(-) create mode 100644 bar/wayland.c create mode 100644 bar/wayland.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fc8ecec..f190cfb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,11 +26,14 @@ find_package(PkgConfig REQUIRED) pkg_check_modules(XCB REQUIRED xcb xcb-aux xcb-cursor xcb-event xcb-ewmh xcb-randr xcb-render) pkg_check_modules(XCB_ERRORS xcb-errors) +pkg_check_modules(WAYLAND REQUIRED wayland-client wlroots) pkg_check_modules(FONTCONFIG REQUIRED fontconfig) pkg_check_modules(CAIRO REQUIRED cairo cairo-xcb cairo-ft) pkg_check_modules(YAML REQUIRED yaml-0.1) -add_library(bar-xcb STATIC xcb.c xcb.h bar/xcb.c bar/xcb.h) +add_library(xcb-stuff STATIC EXCLUDE_FROM_ALL xcb.c xcb.h) + +add_library(bar-xcb STATIC EXCLUDE_FROM_ALL bar/xcb.c bar/xcb.h) target_compile_options(bar-xcb PRIVATE ${XCB_CFLAGS_OTHER} @@ -44,7 +47,49 @@ target_include_directories(bar-xcb PRIVATE ${CAIRO_INCLUDE_DIRS} ) -target_link_libraries(bar-xcb ${XCB_LIBRARIES} ${XCB_ERRORS_LIBRARIES}) +target_link_libraries(bar-xcb xcb-stuff ${XCB_LIBRARIES} ${XCB_ERRORS_LIBRARIES}) + +add_custom_command( + OUTPUT wlr-layer-shell-unstable-v1.c + COMMAND wayland-scanner private-code < /home/daniel/AUR/wlroots-git/src/wlroots-git/protocol/wlr-layer-shell-unstable-v1.xml > wlr-layer-shell-unstable-v1.c + VERBATIM + MAIN_DEPENDENCY /home/daniel/AUR/wlroots-git/src/wlroots-git/protocol/wlr-layer-shell-unstable-v1.xml + ) +add_custom_command( + OUTPUT wlr-layer-shell-unstable-v1-client.h + COMMAND wayland-scanner client-header < /home/daniel/AUR/wlroots-git/src/wlroots-git/protocol/wlr-layer-shell-unstable-v1.xml > wlr-layer-shell-unstable-v1-client.h + VERBATIM + MAIN_DEPENDENCY /home/daniel/AUR/wlroots-git/src/wlroots-git/protocol/wlr-layer-shell-unstable-v1.xml + ) +add_custom_command( + OUTPUT xdg-shell.c + COMMAND wayland-scanner private-code < /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml > xdg-shell.c + VERBATIM + MAIN_DEPENDENCY /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml + ) + +add_library(wayland-protocols STATIC EXCLUDE_FROM_ALL + wlr-layer-shell-unstable-v1-client.h + wlr-layer-shell-unstable-v1.c + xdg-shell.c + ) +target_include_directories(wayland-protocols PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + +add_library(bar-wayland STATIC EXCLUDE_FROM_ALL bar/wayland.c bar/wayland.h) +target_compile_definitions(bar-wayland PRIVATE _GNU_SOURCE) +target_compile_options(bar-wayland PRIVATE + ${WAYLAND_CFLAGS_OTHER} + ${CAIRO_CFLAGS_OTHER} + ) + +target_include_directories(bar-wayland PRIVATE + ${CURRENT_BINARY_DIR} + ${WAYLAND_INCLUDE_DIRS} + ${CAIRO_INCLUDE_DIRS} + ) + +target_link_libraries(bar-wayland wayland-protocols ${WAYLAND_LIBRARIES}) + add_executable(f00bar config.c config.h @@ -63,7 +108,7 @@ add_executable(f00bar bar/bar.c bar/private.h bar/backend.h ) -target_link_libraries(f00bar bar-xcb) +target_link_libraries(f00bar xcb-stuff bar-xcb bar-wayland) # Make global symbols in f00bar visible to dlopen:ed plugins set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -rdynamic") diff --git a/bar/bar.c b/bar/bar.c index 36d9ed4..6fffeed 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -15,6 +15,7 @@ #include "../log.h" #include "xcb.h" +#include "wayland.h" /* * Calculate total width of left/center/rigth groups. @@ -323,14 +324,17 @@ run(struct bar *_bar) m->destroy(m); } - cairo_destroy(bar->cairo); - cairo_device_finish(cairo_surface_get_device(bar->cairo_surface)); - cairo_surface_finish(bar->cairo_surface); - cairo_surface_destroy(bar->cairo_surface); - cairo_debug_reset_static_data(); - bar->backend.iface->cleanup(_bar); + if (bar->cairo) + cairo_destroy(bar->cairo); + if (bar->cairo_surface) { + cairo_device_finish(cairo_surface_get_device(bar->cairo_surface)); + cairo_surface_finish(bar->cairo_surface); + cairo_surface_destroy(bar->cairo_surface); + } + cairo_debug_reset_static_data(); + LOG_DBG("bar exiting"); return ret; } @@ -379,8 +383,13 @@ bar_new(const struct bar_config *config) //priv->cursor_ctx = NULL; //priv->cursor = 0; priv->cursor_name = NULL; +#if 0 priv->backend.data = bar_backend_xcb_new(); priv->backend.iface = &xcb_backend_iface; +#else + priv->backend.data = bar_backend_wayland_new(); + priv->backend.iface = &wayland_backend_iface; +#endif for (size_t i = 0; i < priv->left.count; i++) { priv->left.mods[i] = config->left.mods[i]; diff --git a/bar/wayland.c b/bar/wayland.c new file mode 100644 index 0000000..b952998 --- /dev/null +++ b/bar/wayland.c @@ -0,0 +1,587 @@ +#include "wayland.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#define LOG_MODULE "bar:wayland" +#include "../log.h" +#include "../tllist.h" + +#include "private.h" + +struct buffer { + bool busy; + size_t size; + void *mmapped; + + struct wl_buffer *wl_buf; + + cairo_surface_t *cairo_surface; + cairo_t *cairo; +}; + +struct wayland_backend { + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_output *output; + struct wl_surface *surface; + struct zwlr_layer_shell_v1 *layer_shell; + struct zwlr_layer_surface_v1 *layer_surface; + struct wl_shm *shm; + struct wl_seat *seat; + + struct { + struct wl_pointer *pointer; + int x; + int y; + } pointer; + + /* TODO: set directly in bar instead */ + int width, height; + + /* Used to signal e.g. refresh */ + int pipe_fds[2]; + + /* We're already waiting for a frame done callback */ + bool render_scheduled; + + tll(struct buffer) buffers; /* List of SHM buffers */ + struct buffer *next_buffer; /* Bar is rendering to this one */ + struct buffer *pending_buffer; /* Finished, but not yet rendered */ +}; + +void * +bar_backend_wayland_new(void) +{ + return calloc(1, sizeof(struct wayland_backend)); +} + +static void +shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) +{ + printf("SHM format: 0x%08x\n", format); +} + +static const struct wl_shm_listener shm_listener = { + .format = &shm_format, +}; + +static void +wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t surface_x, wl_fixed_t surface_y) +{ +} + +static void +wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface) +{ +} + +static void +wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, + uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) +{ + struct wayland_backend *backend = data; + printf("MOTION: %dx%d\n", wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y)); + backend->pointer.x = wl_fixed_to_int(surface_x); + backend->pointer.y = wl_fixed_to_int(surface_y); +} + +static void +wl_pointer_button(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, uint32_t state) +{ + if (state != WL_POINTER_BUTTON_STATE_PRESSED) + return; + + struct wayland_backend *backend = data; + printf("BUTTON: %dx%d\n", backend->pointer.x, backend->pointer.y); + + write(backend->pipe_fds[1], &(uint8_t){2}, sizeof(uint8_t)); + write(backend->pipe_fds[1], &backend->pointer.x, sizeof(backend->pointer.x)); + write(backend->pipe_fds[1], &backend->pointer.y, sizeof(backend->pointer.y)); +} + +static void +wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) +{ +} + +static void +wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) +{ +} + +static void +wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, + uint32_t axis_source) +{ +} + +static void +wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis) +{ +} + +static void +wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, + uint32_t axis, int32_t discrete) +{ +} + +static const struct wl_pointer_listener pointer_listener = { + .enter = wl_pointer_enter, + .leave = wl_pointer_leave, + .motion = wl_pointer_motion, + .button = wl_pointer_button, + .axis = wl_pointer_axis, + .frame = wl_pointer_frame, + .axis_source = wl_pointer_axis_source, + .axis_stop = wl_pointer_axis_stop, + .axis_discrete = wl_pointer_axis_discrete, +}; + +static void +seat_handle_capabilities(void *data, struct wl_seat *wl_seat, + enum wl_seat_capability caps) +{ + struct wayland_backend *backend = data; + + if (backend->pointer.pointer != NULL) { + wl_pointer_release(backend->pointer.pointer); + backend->pointer.pointer = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_POINTER)) { + backend->pointer.pointer = wl_seat_get_pointer(wl_seat); + wl_pointer_add_listener(backend->pointer.pointer, &pointer_listener, backend); + } +} + +static void +seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) +{ + printf("seat: name=%s\n", name); +} + +static const struct wl_seat_listener seat_listener = { + .capabilities = seat_handle_capabilities, + .name = seat_handle_name, +}; + +static void +handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + struct wayland_backend *backend = data; + if (strcmp(interface, wl_compositor_interface.name) == 0) { + backend->compositor = wl_registry_bind( + registry, name, &wl_compositor_interface, 4); + } + + else if (strcmp(interface, wl_shm_interface.name) == 0) { + backend->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + wl_shm_add_listener(backend->shm, &shm_listener, backend); + } + + else if (strcmp(interface, wl_output_interface.name) == 0) { + backend->output = wl_registry_bind(registry, name, &wl_output_interface, 3); + //output_add_listener(backend->output, &output_listener, backend); + } + + else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { + backend->layer_shell = wl_registry_bind( + registry, name, &zwlr_layer_shell_v1_interface, 1); + } + + else if (strcmp(interface, wl_seat_interface.name) == 0) { + backend->seat = wl_registry_bind(registry, name, &wl_seat_interface, 3); + wl_seat_add_listener(backend->seat, &seat_listener, backend); + } +} + +static void +handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) +{ + //struct wayland_backend *backend = data; +} + +static const struct wl_registry_listener registry_listener = { + .global = &handle_global, + .global_remove = &handle_global_remove, +}; + +static void +layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, + uint32_t serial, uint32_t w, uint32_t h) +{ + struct wayland_backend *backend = data; + backend->width = w; + backend->height = h; + + zwlr_layer_surface_v1_ack_configure(surface, serial); +} + +static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { + .configure = layer_surface_configure, +}; + +static void +buffer_release(void *data, struct wl_buffer *wl_buffer) +{ + printf("buffer release\n"); + struct buffer *buffer = data; + assert(buffer->busy); + buffer->busy = false; +} + +static const struct wl_buffer_listener buffer_listener = { + .release = &buffer_release, +}; + +static struct buffer * +get_buffer(struct wayland_backend *backend) +{ + tll_foreach(backend->buffers, it) { + if (!it->item.busy) { + printf("re-using non-busy buffer\n"); + it->item.busy = true; + return &it->item; + } + } + + printf("allocating a new buffer\n"); + + struct buffer buffer; + + /* Backing memory for SHM */ + int pool_fd = memfd_create("wayland-test-buffer-pool", MFD_CLOEXEC); + assert(pool_fd != -1); + + /* Allocate memory for our single buffer */ + uint32_t stride = backend->width * 4; + buffer.size = stride * backend->height; + ftruncate(pool_fd, buffer.size); + + buffer.mmapped = mmap( + NULL, buffer.size, PROT_READ | PROT_WRITE, MAP_SHARED, pool_fd, 0); + assert(buffer.mmapped != MAP_FAILED); + + struct wl_shm_pool *pool = wl_shm_create_pool(backend->shm, pool_fd, buffer.size); + assert(pool != NULL); + + buffer.wl_buf = wl_shm_pool_create_buffer( + pool, 0, backend->width, backend->height, stride, WL_SHM_FORMAT_ARGB8888); + assert(buffer.wl_buf != NULL); + buffer.busy = true; + + wl_shm_pool_destroy(pool); + close(pool_fd); + + buffer.cairo_surface = cairo_image_surface_create_for_data( + buffer.mmapped, CAIRO_FORMAT_ARGB32, backend->width, backend->height, stride); + buffer.cairo = cairo_create(buffer.cairo_surface); + assert(cairo_status(buffer.cairo) == CAIRO_STATUS_SUCCESS); + + tll_push_back(backend->buffers, buffer); + + struct buffer *ret = &tll_back(backend->buffers); + wl_buffer_add_listener(buffer.wl_buf, &buffer_listener, ret); + return ret; +} + +static bool +setup(struct bar *_bar) +{ + struct private *bar = _bar->private; + struct wayland_backend *backend = bar->backend.data; + + backend->display = wl_display_connect(NULL); + assert(backend->display != NULL); + + backend->registry = wl_display_get_registry(backend->display); + assert(backend->registry != NULL); + + wl_registry_add_listener(backend->registry, ®istry_listener, backend); + + wl_display_roundtrip(backend->display); + assert(backend->compositor != NULL && + backend->layer_shell != NULL && + backend->output != NULL); + + backend->surface = wl_compositor_create_surface(backend->compositor); + assert(backend->surface != NULL); + + backend->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + backend->layer_shell, backend->surface, backend->output, + ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "f00bar"); + assert(backend->layer_surface != NULL); + + /* Aligned to top, maximum width */ + zwlr_layer_surface_v1_set_anchor( + backend->layer_surface, + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP); + + zwlr_layer_surface_v1_set_size(backend->layer_surface, 0, bar->height_with_border); + zwlr_layer_surface_v1_set_exclusive_zone(backend->layer_surface, bar->height_with_border); + + //zwlr_layer_surface_v1_set_margin( + // layer_surface, margin_top, margin_right, margin_bottom, margin_left); + //zwlr_layer_surface_v1_set_keyboard_interactivity( + // layer_surface, keyboard_interactive); + + zwlr_layer_surface_v1_add_listener( + backend->layer_surface, &layer_surface_listener, backend); + + /* Assign width/height */ + wl_surface_commit(backend->surface); + wl_display_roundtrip(backend->display); + + assert(backend->width != -1 && backend->height == bar->height_with_border); + bar->width = backend->width; + + pipe(backend->pipe_fds); + backend->render_scheduled = false; + + wl_surface_commit(backend->surface); + wl_display_roundtrip(backend->display); + + backend->next_buffer = get_buffer(backend); + assert(backend->next_buffer != NULL && backend->next_buffer->busy); + + bar->cairo_surface = backend->next_buffer->cairo_surface; + bar->cairo = backend->next_buffer->cairo; + + return true; +} + +static void +cleanup(struct bar *_bar) +{ + struct private *bar = _bar->private; + struct wayland_backend *backend = bar->backend.data; + + tll_foreach(backend->buffers, it) { + wl_buffer_destroy(it->item.wl_buf); + cairo_destroy(it->item.cairo); + cairo_surface_destroy(it->item.cairo_surface); + munmap(it->item.mmapped, it->item.size); + + tll_remove(backend->buffers, it); + } + + /* TODO: move to bar */ + free(bar->cursor_name); + + zwlr_layer_surface_v1_destroy(backend->layer_surface); + zwlr_layer_shell_v1_destroy(backend->layer_shell); + wl_compositor_destroy(backend->compositor); + wl_surface_destroy(backend->surface); + wl_shm_destroy(backend->shm); + wl_output_destroy(backend->output); + wl_registry_destroy(backend->registry); + wl_display_disconnect(backend->display); + + /* Destroyed when freeing buffer list */ + bar->cairo_surface = NULL; + bar->cairo = NULL; + +} + +static void +loop(struct bar *_bar, + void (*expose)(const struct bar *bar), + void (*on_mouse)(struct bar *bar, enum mouse_event event, int x, int y)) +{ + struct private *bar = _bar->private; + struct wayland_backend *backend = bar->backend.data; + +#if 0 + while (wl_display_prepare_read(backend->display) != 0){ + printf("initial wayland event\n"); + wl_display_dispatch_pending(backend->display); + } + wl_display_flush(backend->display); +#endif + + wl_display_dispatch_pending(backend->display); + wl_display_flush(backend->display); + + while (true) { + struct pollfd fds[] = { + {.fd = _bar->abort_fd, .events = POLLIN}, + {.fd = wl_display_get_fd(backend->display), .events = POLLIN}, + {.fd = backend->pipe_fds[0], .events = POLLIN}, + }; + + wl_display_flush(backend->display); + + printf("polling\n"); + poll(fds, sizeof(fds) / sizeof(fds[0]), -1); + if (fds[0].revents & POLLIN) { + //wl_display_cancel_read(backend->display); + break; + } + + if (fds[1].revents & POLLHUP) { + LOG_WARN("disconnceted from wayland"); + write(_bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t)); + break; + } + + if (fds[2].revents & POLLIN) { + uint8_t command; + //wl_display_cancel_read(backend->display); + read(backend->pipe_fds[0], &command, sizeof(command)); + + if (command == 1) { + printf("refresh\n"); + assert(command == 1); + expose(_bar); +#if 0 + while (wl_display_prepare_read(backend->display) != 0) { + printf("queued wayland events\n"); + wl_display_dispatch_pending(backend->display); + } + wl_display_flush(backend->display); +#endif + } + + if (command == 2) { + printf("mouse\n"); + int x, y; + read(backend->pipe_fds[0], &x, sizeof(x)); + read(backend->pipe_fds[0], &y, sizeof(y)); + on_mouse(_bar, ON_MOUSE_CLICK, x, y); + } + continue; + } + +#if 0 + printf("wayland events\n"); + wl_display_read_events(backend->display); + wl_display_dispatch_pending(backend->display); +#endif + printf("wayland events\n"); + wl_display_dispatch(backend->display); + } +} + +static void frame_callback( + void *data, struct wl_callback *wl_callback, uint32_t callback_data); + +static const struct wl_callback_listener frame_listener = { + .done = &frame_callback, +}; + +static void +frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_data) +{ + printf("frame callback\n"); + struct private *bar = data; + struct wayland_backend *backend = bar->backend.data; + + backend->render_scheduled = false; + + wl_callback_destroy(wl_callback); + + if (backend->pending_buffer != NULL) { + struct buffer *buffer = backend->pending_buffer; + assert(buffer->busy); + + wl_surface_attach(backend->surface, buffer->wl_buf, 0, 0); + wl_surface_damage(backend->surface, 0, 0, backend->width, backend->height); + + struct wl_callback *cb = wl_surface_frame(backend->surface); + wl_callback_add_listener(cb, &frame_listener, bar); + wl_surface_commit(backend->surface); + + backend->pending_buffer = NULL; + backend->render_scheduled = true; + } else + printf("nothing more to do\n"); +} + +static void +commit_surface(const struct bar *_bar) +{ + struct private *bar = _bar->private; + struct wayland_backend *backend = bar->backend.data; + + printf("commit: %dxl%d\n", backend->width, backend->height); + + assert(backend->next_buffer != NULL); + assert(backend->next_buffer->busy); + + if (backend->render_scheduled) { + printf("already scheduled\n"); + + if (backend->pending_buffer != NULL) + backend->pending_buffer->busy = false; + + backend->pending_buffer = backend->next_buffer; + backend->next_buffer = NULL; + } else { + + printf("scheduling new frame callback\n"); + struct buffer *buffer = backend->next_buffer; + assert(buffer->busy); + + wl_surface_attach(backend->surface, buffer->wl_buf, 0, 0); + wl_surface_damage(backend->surface, 0, 0, backend->width, backend->height); + + struct wl_callback *cb = wl_surface_frame(backend->surface); + wl_callback_add_listener(cb, &frame_listener, bar); + wl_surface_commit(backend->surface); + + backend->render_scheduled = true; + } + + backend->next_buffer = get_buffer(backend); + assert(backend->next_buffer != NULL && backend->next_buffer->busy); + + bar->cairo_surface = backend->next_buffer->cairo_surface; + bar->cairo = backend->next_buffer->cairo; +} + +static void +refresh(const struct bar *_bar) +{ + const struct private *bar = _bar->private; + const struct wayland_backend *backend = bar->backend.data; + + write(backend->pipe_fds[1], &(uint8_t){1}, sizeof(uint8_t)); +} + +static void +set_cursor(struct bar *_bar, const char *cursor) +{ +} + +const struct backend wayland_backend_iface = { + .setup = &setup, + .cleanup = &cleanup, + .loop = &loop, + .commit_surface = &commit_surface, + .refresh = &refresh, + .set_cursor = &set_cursor, +}; diff --git a/bar/wayland.h b/bar/wayland.h new file mode 100644 index 0000000..465ef46 --- /dev/null +++ b/bar/wayland.h @@ -0,0 +1,7 @@ +#pragma once + +#include "backend.h" + +extern const struct backend wayland_backend_iface; + +void *bar_backend_wayland_new(void); diff --git a/main.c b/main.c index 5def04c..78ec0ba 100644 --- a/main.c +++ b/main.c @@ -46,7 +46,7 @@ get_config_path(void) path_max = 1024; char *path = malloc(path_max + 1); - snprintf(path, path_max + 1, "%s/.config/f00bar/config.yml", home_dir); + snprintf(path, path_max + 1, "%s/.config/f00bar/config-wayland.yml", home_dir); return path; }