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.
This commit is contained in:
Daniel Eklöf 2019-02-03 11:08:53 +01:00
parent ff88d87ca8
commit ed8e17c6c9
5 changed files with 658 additions and 10 deletions

View file

@ -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")

View file

@ -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];

587
bar/wayland.c Normal file
View file

@ -0,0 +1,587 @@
#include "wayland.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <poll.h>
#include <sys/mman.h>
#include <linux/memfd.h>
#include <cairo.h>
#include <wayland-client.h>
#include <wlr-layer-shell-unstable-v1-client.h>
#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, &registry_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,
};

7
bar/wayland.h Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#include "backend.h"
extern const struct backend wayland_backend_iface;
void *bar_backend_wayland_new(void);

2
main.c
View file

@ -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;
}