diff options
author | Leon Henrik Plickat <leonhenrik.plickat@stud.uni-goettingen.de> | 2020-09-24 18:01:28 +0200 |
---|---|---|
committer | Leon Henrik Plickat <leonhenrik.plickat@stud.uni-goettingen.de> | 2020-09-24 18:01:28 +0200 |
commit | 9ff4f4959b8d78104fa4defb3c4cea9606e3c484 (patch) | |
tree | ac80aef7e1caff0d0af491ea4816ca63706a8116 /src | |
download | wlclock-9ff4f4959b8d78104fa4defb3c4cea9606e3c484.tar.gz wlclock-9ff4f4959b8d78104fa4defb3c4cea9606e3c484.tar.bz2 |
Init
Diffstat (limited to 'src')
-rw-r--r-- | src/buffer.c | 184 | ||||
-rw-r--r-- | src/buffer.h | 25 | ||||
-rw-r--r-- | src/misc.c | 30 | ||||
-rw-r--r-- | src/misc.h | 10 | ||||
-rw-r--r-- | src/output.c | 169 | ||||
-rw-r--r-- | src/output.h | 33 | ||||
-rw-r--r-- | src/surface.c | 163 | ||||
-rw-r--r-- | src/surface.h | 31 | ||||
-rw-r--r-- | src/wlclock.c | 298 | ||||
-rw-r--r-- | src/wlclock.h | 40 |
10 files changed, 983 insertions, 0 deletions
diff --git a/src/buffer.c b/src/buffer.c new file mode 100644 index 0000000..83f48ad --- /dev/null +++ b/src/buffer.c @@ -0,0 +1,184 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <time.h> +#include <sys/mman.h> +#include <cairo/cairo.h> + +#include"buffer.h" +#include"misc.h" + +static void randomize_string (char *str, size_t len) +{ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + long r = ts.tv_nsec; + + for (size_t i = 0; i < len; i++, str++) + { + /* Use two byte from the current nano-second to pseudo-randomly + * increase the ASCII character 'A' into another character, + * which will then subsitute the character at *str. + */ + *str = (char)('A' + (r&15) + (r&16)); + r >>= 5; + } +} + +/* Tries to create a shared memory object and returns its file descriptor if + * successful. + */ +static bool get_shm_fd (int *fd, size_t size) +{ + char name[] = "/wlclock-RANDOM"; + char *rp = name + 9; /* Pointer to random part. */ + size_t rl = 6; /* Length of random part. */ + + /* Try a few times to get a unique name. */ + for (int tries = 100; tries > 0; tries--) + { + /* Make the name pseudo-random to not conflict with other + * running instances of Wlclock. + */ + randomize_string(rp, rl); + + /* Try to create a shared memory object. Returns -1 if the + * memory object already exists. + */ + errno = 0; + *fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + + /* If a shared memory object was created, set its size and + * return its file descriptor. + */ + if ( *fd >= 0 ) + { + shm_unlink(name); + if ( ftruncate(*fd, (off_t)size) < 0 ) + { + close(*fd); + return false; + } + return true; + } + + /* The EEXIST error means that the name is not unique and we + * must try again. + */ + if ( errno != EEXIST ) + { + clocklog(NULL, 0, "ERROR: shm_open: %s\n", strerror(errno)); + return false; + } + } + + return false; +} + +static void buffer_handle_release (void *data, struct wl_buffer *wl_buffer) +{ + struct Wlclock_buffer *buffer = (struct Wlclock_buffer *)data; + buffer->busy = false; +} + +static const struct wl_buffer_listener buffer_listener = { + .release = buffer_handle_release, +}; + +static bool create_buffer (struct wl_shm *shm, struct Wlclock_buffer *buffer, + uint32_t _w, uint32_t _h) +{ + int32_t w = (int32_t)_w, h = (int32_t)_h; + + const enum wl_shm_format wl_fmt = WL_SHM_FORMAT_ARGB8888; + const cairo_format_t cairo_fmt = CAIRO_FORMAT_ARGB32; + + int32_t stride = cairo_format_stride_for_width(cairo_fmt, w); + size_t size = (size_t)(stride * h); + + buffer->w = _w; + buffer->h = _h; + buffer->size = size; + + if ( size == 0 ) + { + buffer->memory_object = NULL; + buffer->surface = NULL; + buffer->cairo = NULL; + return true; + } + + int fd; + if (! get_shm_fd(&fd, size)) + return false; + + errno = 0; + if ( MAP_FAILED == (buffer->memory_object = mmap(NULL, size, + PROT_READ | PROT_WRITE, MAP_SHARED, + fd, 0)) ) + { + close(fd); + clocklog(NULL, 0, "ERROR: mmap: %s\n", strerror(errno)); + return false; + } + + struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, (int32_t)size); + buffer->buffer = wl_shm_pool_create_buffer(pool, 0, w, h, stride, + wl_fmt); + wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer); + wl_shm_pool_destroy(pool); + + close(fd); + + buffer->surface = cairo_image_surface_create_for_data( + buffer->memory_object, cairo_fmt, w, h, stride); + buffer->cairo = cairo_create(buffer->surface); + + return true; +} + +void finish_buffer (struct Wlclock_buffer *buffer) +{ + if (buffer->buffer) + wl_buffer_destroy(buffer->buffer); + if (buffer->cairo) + cairo_destroy(buffer->cairo); + if (buffer->surface) + cairo_surface_destroy(buffer->surface); + if (buffer->memory_object) + munmap(buffer->memory_object, buffer->size); + memset(buffer, 0, sizeof(struct Wlclock_buffer)); +} + +bool next_buffer (struct Wlclock_buffer **buffer, struct wl_shm *shm, + struct Wlclock_buffer buffers[static 2], uint32_t w, uint32_t h) +{ + /* Check if buffers are busy and use first non-busy one, if it exists. + * If all buffers are busy, exit. + */ + if (! buffers[0].busy) + *buffer = &buffers[0]; + else if (! buffers[1].busy) + *buffer = &buffers[1]; + else + { + clocklog(NULL, 0, "ERROR: All buffers are busy.\n"); + *buffer = NULL; + return false; + } + + /* If the buffers dimensions do not match, or if there is no wl_buffer + * or if the buffer does not exist, close it and create a new one. + */ + if ( (*buffer)->w != w || (*buffer)->h != h || ! (*buffer)->buffer ) + { + finish_buffer(*buffer); + if (! create_buffer(shm, *buffer, w, h)) + return false; + } + + return true; +} diff --git a/src/buffer.h b/src/buffer.h new file mode 100644 index 0000000..3e82541 --- /dev/null +++ b/src/buffer.h @@ -0,0 +1,25 @@ +#ifndef WLCLOCK_BUFFER_H +#define WLCLOCK_BUFFER_H + +#include<stdint.h> +#include<stdbool.h> +#include<cairo/cairo.h> +#include<wayland-client.h> + +struct Wlclock_buffer +{ + struct wl_buffer *buffer; + cairo_surface_t *surface; + cairo_t *cairo; + uint32_t w; + uint32_t h; + void *memory_object; + size_t size; + bool busy; +}; + +bool next_buffer (struct Wlclock_buffer **buffer, struct wl_shm *shm, + struct Wlclock_buffer buffers[static 2], uint32_t w, uint32_t h); +void finish_buffer (struct Wlclock_buffer *buffer); + +#endif diff --git a/src/misc.c b/src/misc.c new file mode 100644 index 0000000..dd4500d --- /dev/null +++ b/src/misc.c @@ -0,0 +1,30 @@ +#include<stdarg.h> +#include<stdio.h> +#include<stdlib.h> +#include<string.h> +#include<unistd.h> + +#include"wlclock.h" + +void free_if_set (void *ptr) +{ + if ( ptr != NULL ) + free(ptr); +} + +void set_string (char **ptr, char *arg) +{ + free_if_set(*ptr); + *ptr = strdup(arg); +} + +void clocklog (struct Wlclock *clock, int level, const char *fmt, ...) +{ + if ( clock != NULL && level > clock->verbosity ) + return; + + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} diff --git a/src/misc.h b/src/misc.h new file mode 100644 index 0000000..f262771 --- /dev/null +++ b/src/misc.h @@ -0,0 +1,10 @@ +#ifndef WLCLOCK_MISC_H +#define WLCLOCK_MISC_H + +struct Wlclock; + +void free_if_set (void *ptr); +void set_string (char **ptr, char *arg); +void clocklog (struct Wlclock *clock, int level, const char *fmt, ...); + +#endif diff --git a/src/output.c b/src/output.c new file mode 100644 index 0000000..102d510 --- /dev/null +++ b/src/output.c @@ -0,0 +1,169 @@ +#include<stdio.h> +#include<stdlib.h> +#include<stdbool.h> +#include<unistd.h> +#include<string.h> +#include<assert.h> + +#include<wayland-server.h> +#include<wayland-client.h> +#include<wayland-client-protocol.h> + +#include"xdg-output-unstable-v1-protocol.h" +#include"xdg-shell-protocol.h" + +#include"wlclock.h" +#include"misc.h" +#include"output.h" +#include"surface.h" + +/* No-Op function. */ +static void noop () {} + +static void output_handle_scale (void *data, struct wl_output *wl_output, + int32_t factor) +{ + struct Wlclock_output *output = (struct Wlclock_output *)data; + output->scale = (uint32_t)factor; + clocklog(output->clock, 1, "[output] Property update: global_name=%d scale=%d\n", + output->global_name, output->scale); +} + +static void output_update_surface (struct Wlclock_output *output) +{ + if ( ! output->configured || output->name == NULL ) + return; + + struct Wlclock *clock = output->clock; + if ( clock->output == NULL || ! strcmp(clock->output, output->name) ) + create_surface(output); +} + +static void output_handle_done (void *data, struct wl_output *wl_output) +{ + /* This event is sent after all output property changes (by wl_output + * and by xdg_output) have been advertised by preceding events. + */ + struct Wlclock_output *output = (struct Wlclock_output *)data; + clocklog(output->clock, 1, "[output] Atomic update complete: global_name=%d\n", + output->global_name); + if ( output->surface != NULL ) + update_surface(output->surface); + else + output_update_surface(output); +} + +static const struct wl_output_listener output_listener = { + .scale = output_handle_scale, + .geometry = noop, + .mode = noop, + .done = output_handle_done +}; + +static void xdg_output_handle_name (void *data, struct zxdg_output_v1 *xdg_output, + const char *name) +{ + struct Wlclock_output *output = (struct Wlclock_output *)data; + set_string(&output->name, (char *)name); + clocklog(output->clock, 1, "[output] Property update: global_name=%d name=%s\n", + output->global_name, name); +} + +static const struct zxdg_output_v1_listener xdg_output_listener = { + .name = xdg_output_handle_name, + .logical_size = noop, + .logical_position = noop, + .description = noop, + + /* Deprecated since version 3, xdg_output property changes now send wl_output.done */ + .done = noop +}; + +bool configure_output (struct Wlclock_output *output) +{ + struct Wlclock *clock = output->clock; + clocklog(clock, 1, "[output] Configuring: global_name=%d\n", output->global_name); + + /* Create xdg_output and attach listeners. */ + if ( NULL == (output->xdg_output = zxdg_output_manager_v1_get_xdg_output( + clock->xdg_output_manager, output->wl_output)) ) + { + clocklog(NULL, 0, "ERROR: Could not get XDG output.\n"); + return false; + } + + zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output); + output->configured = true; + return true; +} + +bool create_output (struct Wlclock *clock, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + clocklog(clock, 1, "[output] Creating: global_name=%d\n", name); + + struct wl_output *wl_output = wl_registry_bind(registry, name, + &wl_output_interface, 3); + assert(wl_output); + + struct Wlclock_output *output = calloc(1, sizeof(struct Wlclock_output)); + if ( output == NULL ) + { + clocklog(NULL, 0, "ERROR: Could not allocate.\n"); + return false; + } + + output->clock = clock; + output->global_name = name; + output->scale = 1; + output->wl_output = wl_output; + output->configured = false; + output->name = NULL; + + wl_list_insert(&clock->outputs, &output->link); + wl_output_set_user_data(wl_output, output); + wl_output_add_listener(wl_output, &output_listener, output); + + /* We can only use the output if we have both xdg_output_manager and + * the layer_shell. If either one is not available yet, we have to + * configure the output later (see init_wayland()). + */ + if ( clock->xdg_output_manager != NULL && clock->layer_shell != NULL ) + { + if (! configure_output(output)) + return false; + } + else + clocklog(clock, 2, "[output] Not yet configureable.\n"); + + return true; +} + +struct Wlclock_output *get_output_from_global_name (struct Wlclock *clock, uint32_t name) +{ + struct Wlclock_output *op; + wl_list_for_each(op, &clock->outputs, link) + if ( op->global_name == name ) + return op; + return NULL; +} + +void destroy_output (struct Wlclock_output *output) +{ + if ( output == NULL ) + return; + if ( output->surface != NULL ) + destroy_surface(output->surface); + wl_list_remove(&output->link); + wl_output_destroy(output->wl_output); + free(output); +} + +void destroy_all_outputs (struct Wlclock *clock) +{ + clocklog(clock, 1, "[output] Destroying all outputs.\n"); + struct Wlclock_output *op, *tmp; + wl_list_for_each_safe(op, tmp, &clock->outputs, link) + destroy_output(op); +} + diff --git a/src/output.h b/src/output.h new file mode 100644 index 0000000..0085bd7 --- /dev/null +++ b/src/output.h @@ -0,0 +1,33 @@ +#ifndef WLCLOCK_OUTPUT_H +#define WLCLOCK_OUTPUT_H + +#include<wayland-server.h> + +struct Wlclock; +struct Wlclock_surface; + +struct Wlclock_output +{ + struct wl_list link; + struct Wlclock *clock; + + struct wl_output *wl_output; + struct zxdg_output_v1 *xdg_output; + + char *name; + uint32_t global_name; + uint32_t scale; + + bool configured; + + struct Wlclock_surface *surface; +}; + +bool create_output (struct Wlclock *clock, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version); +bool configure_output (struct Wlclock_output *output); +struct Wlclock_output *get_output_from_global_name (struct Wlclock *clock, uint32_t name); +void destroy_output (struct Wlclock_output *output); +void destroy_all_outputs (struct Wlclock *clock); + +#endif diff --git a/src/surface.c b/src/surface.c new file mode 100644 index 0000000..9e6c8e2 --- /dev/null +++ b/src/surface.c @@ -0,0 +1,163 @@ +#include<stdio.h> +#include<stdlib.h> +#include<stdbool.h> +#include<unistd.h> +#include<string.h> + +#include<cairo/cairo.h> +#include<wayland-server.h> +#include<wayland-client.h> +#include<wayland-client-protocol.h> + +#include"wlr-layer-shell-unstable-v1-protocol.h" +#include"xdg-output-unstable-v1-protocol.h" +#include"xdg-shell-protocol.h" + +#include"wlclock.h" +#include"output.h" +#include"misc.h" +#include"surface.h" +#include"buffer.h" + +static uint32_t min (uint32_t a, uint32_t b) +{ + return a > b ? b : a; +} + +static void layer_surface_handle_configure (void *data, + struct zwlr_layer_surface_v1 *layer_surface, uint32_t serial, + uint32_t w, uint32_t h) +{ + struct Wlclock_surface *surface = (struct Wlclock_surface *)data; + clocklog(surface->output->clock, 1, + "[surface] Layer surface configure request: global_name=%d w=%d h=%d serial=%d\n", + surface->output->global_name, w, h, serial); + zwlr_layer_surface_v1_ack_configure(layer_surface, serial); + if ( w > 0 && h > 0 ) /* Try to fit as best as possible. */ + surface->size = min(w, h); + surface->configured = true; + update_surface(surface); +} + +static void layer_surface_handle_closed (void *data, struct zwlr_layer_surface_v1 *layer_surface) +{ + struct Wlclock_surface *surface = (struct Wlclock_surface *)data; + clocklog(surface->output->clock, 1, + "[surface] Layer surface has been closed: global_name=%d\n", + surface->output->global_name); + destroy_surface(surface); +} + +const struct zwlr_layer_surface_v1_listener layer_surface_listener = { + .configure = layer_surface_handle_configure, + .closed = layer_surface_handle_closed +}; + +static int32_t get_exclusive_zone (struct Wlclock_surface *surface) +{ + if ( surface->output->clock->exclusive_zone == 1 ) + return surface->size; + return surface->output->clock->exclusive_zone; +} + +static void configure_layer_surface (struct Wlclock_surface *surface) +{ + struct Wlclock *clock = surface->output->clock; + clocklog(clock, 1, "[surface] Configuring surface: global_name=%d\n", + surface->output->global_name); + zwlr_layer_surface_v1_set_size(surface->layer_surface, + surface->size, surface->size); + zwlr_layer_surface_v1_set_anchor(surface->layer_surface, clock->anchor); + zwlr_layer_surface_v1_set_margin(surface->layer_surface, + clock->margin_top, clock->margin_right, + clock->margin_bottom, clock->margin_left); + zwlr_layer_surface_v1_set_exclusive_zone(surface->layer_surface, + get_exclusive_zone(surface)); + if (! clock->input) + { + struct wl_region *region = wl_compositor_create_region(clock->compositor); + wl_surface_set_input_region(surface->surface, region); + wl_region_destroy(region); + } +} + +bool create_surface (struct Wlclock_output *output) +{ + struct Wlclock *clock = output->clock; + clocklog(clock, 1, "[surface] Creating surface: global_name=%d\n", output->global_name); + + struct Wlclock_surface *surface = calloc(1, sizeof(struct Wlclock_surface)); + if ( surface == NULL ) + { + clocklog(NULL, 0, "ERROR: Could not allocate.\n"); + return false; + } + + output->surface = surface; + surface->size = clock->size; + surface->output = output; + surface->surface = NULL; + surface->layer_surface = NULL; + surface->configured = false; + + if ( NULL == (surface->surface = wl_compositor_create_surface(clock->compositor)) ) + { + clocklog(NULL, 0, "ERROR: Compositor did not create wl_surface.\n"); + return false; + } + if ( NULL == (surface->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + clock->layer_shell, surface->surface, + output->wl_output, clock->layer, + clock->namespace)) ) + { + clocklog(NULL, 0, "ERROR: Compositor did not create layer_surface.\n"); + return false; + } + + configure_layer_surface(surface); + zwlr_layer_surface_v1_add_listener(surface->layer_surface, + &layer_surface_listener, surface); + wl_surface_commit(surface->surface); + + return true; +} + +void destroy_surface (struct Wlclock_surface *surface) +{ + if ( surface == NULL ) + return; + if ( surface->layer_surface != NULL ) + zwlr_layer_surface_v1_destroy(surface->layer_surface); + if ( surface->surface != NULL ) + wl_surface_destroy(surface->surface); + finish_buffer(&surface->buffers[0]); + finish_buffer(&surface->buffers[1]); + free(surface); +} + +void destroy_all_surfaces (struct Wlclock *clock) +{ + clocklog(clock, 1, "[surface] Destroying all surfaces.\n"); + struct Wlclock_output *op, *tmp; + wl_list_for_each_safe(op, tmp, &clock->outputs, link) + if ( op->surface != NULL ) + destroy_surface(op->surface); +} + +void update_surface (struct Wlclock_surface *surface) +{ + if ( surface == NULL || ! surface->configured ) + return; + configure_layer_surface(surface); + // render_surface_frame(surface); // TODO + wl_surface_commit(surface->surface); +} + +void update_all_surfaces (struct Wlclock *clock) +{ + clocklog(clock, 1, "[surface] Updating all surfaces.\n"); + struct Wlclock_output *op, *tmp; + wl_list_for_each_safe(op, tmp, &clock->outputs, link) + if ( op->surface != NULL ) + update_surface(op->surface); +} diff --git a/src/surface.h b/src/surface.h new file mode 100644 index 0000000..8fd99bc --- /dev/null +++ b/src/surface.h @@ -0,0 +1,31 @@ +#ifndef WLCLOCK_SURFACE_H +#define WLCLOCK_SURFACE_H + +#include<wayland-server.h> + +#include"buffer.h" + +#include<stdint.h> +#include<stdbool.h> + +struct Wlclock; +struct Wlclock_output; + +struct Wlclock_surface +{ + struct Wlclock_output *output; + struct wl_surface *surface; + struct zwlr_layer_surface_v1 *layer_surface; + + int32_t size; + struct Wlclock_buffer buffers[2]; + bool configured; +}; + +bool create_surface (struct Wlclock_output *output); +void destroy_surface (struct Wlclock_surface *surface); +void destroy_all_surfaces (struct Wlclock *clock); +void update_surface (struct Wlclock_surface *surface); +void update_all_surfaces (struct Wlclock *clock); + +#endif diff --git a/src/wlclock.c b/src/wlclock.c new file mode 100644 index 0000000..a676285 --- /dev/null +++ b/src/wlclock.c @@ -0,0 +1,298 @@ +#include<errno.h> +#include<getopt.h> +#include<poll.h> +#include<stdbool.h> +#include<stdio.h> +#include<stdlib.h> +#include<string.h> +#include<unistd.h> + +#if HANDLE_SIGNALS +#include<sys/signalfd.h> +#include<signal.h> +#endif + +#include<wayland-server.h> +#include<wayland-client.h> +#include<wayland-client-protocol.h> + +#include"wlr-layer-shell-unstable-v1-protocol.h" +#include"xdg-output-unstable-v1-protocol.h" +#include"xdg-shell-protocol.h" + +#include"wlclock.h" +#include"misc.h" +#include"output.h" +#include"surface.h" + +static void registry_handle_global (void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + struct Wlclock *clock = (struct Wlclock *)data; + + if (! strcmp(interface, wl_compositor_interface.name)) + { + clocklog(clock, 2, "[main] Get wl_compositor.\n"); + clock->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4); + } + else if (! strcmp(interface, wl_shm_interface.name)) + { + clocklog(clock, 2, "[main] Get wl_shm.\n"); + clock->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + } + else if (! strcmp(interface, zwlr_layer_shell_v1_interface.name)) + { + clocklog(clock, 2, "[main] Get zwlr_layer_shell_v1.\n"); + clock->layer_shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1); + } + else if (! strcmp(interface, zxdg_output_manager_v1_interface.name)) + { + clocklog(clock, 2, "[main] Get zxdg_output_manager_v1.\n"); + clock->xdg_output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, 3); + } + else if (! strcmp(interface, wl_output_interface.name)) + { + if (! create_output(data, registry, name, interface, version)) + goto error; + } + + return; +error: + clock->loop = false; + clock->ret = EXIT_FAILURE; +} + +static void registry_handle_global_remove (void *data, + struct wl_registry *registry, uint32_t name) +{ + struct Wlclock *clock = (struct Wlclock *)data; + clocklog(clock, 1, "[main] Global remove.\n"); + destroy_output(get_output_from_global_name(clock, name)); +} + +static const struct wl_registry_listener registry_listener = { + .global = registry_handle_global, + .global_remove = registry_handle_global_remove +}; + +/* Helper function for capability support error message. */ +static bool capability_test (void *ptr, const char *name) +{ + if ( ptr != NULL ) + return true; + clocklog(NULL, 0, "ERROR: Wayland compositor does not support %s.\n", name); + return false; +} + +static bool init_wayland (struct Wlclock *clock) +{ + clocklog(clock, 1, "[main] Init Wayland.\n"); + + /* Connect to Wayland server. */ + clocklog(clock, 2, "[main] Connecting to server.\n"); + if ( NULL == (clock->display = wl_display_connect(NULL)) ) + { + clocklog(NULL, 0, "ERROR: Can not connect to a Wayland server.\n"); + return false; + } + + /* Get registry and add listeners. */ + clocklog(clock, 2, "[main] Get wl_registry.\n"); + if ( NULL == (clock->registry = wl_display_get_registry(clock->display)) ) + { + clocklog(NULL, 0, "ERROR: Can not get registry.\n"); + return false; + } + wl_registry_add_listener(clock->registry, ®istry_listener, clock); + + /* Allow registry listeners to catch up. */ + if ( wl_display_roundtrip(clock->display) == -1 ) + { + clocklog(NULL, 0, "ERROR: Roundtrip failed.\n"); + return false; + } + + /* Testing compatibilities. */ + if (! capability_test(clock->compositor, "wl_compositor")) + return false; + if (! capability_test(clock->shm, "wl_shm")) + return false; + if (! capability_test(clock->layer_shell, "zwlr_layer_shell")) + return false; + if (! capability_test(clock->xdg_output_manager, "xdg_output_manager")) + return false; + + clocklog(clock, 2, "[main] Catching up on output configuration.\n"); + struct Wlclock_output *op; + wl_list_for_each(op, &clock->outputs, link) + if ( ! op->configured && ! configure_output(op) ) + return false; + + return true; +} + +/* Finish him! */ +static void finish_wayland (struct Wlclock *clock) +{ + clocklog(clock, 1, "[main] Finish Wayland.\n"); + + destroy_all_outputs(clock); + + clocklog(clock, 2, "[main] Destroying Wayland objects.\n"); + if ( clock->layer_shell != NULL ) + zwlr_layer_shell_v1_destroy(clock->layer_shell); + if ( clock->compositor != NULL ) + wl_compositor_destroy(clock->compositor); + if ( clock->shm != NULL ) + wl_shm_destroy(clock->shm); + if ( clock->registry != NULL ) + wl_registry_destroy(clock->registry); + + if ( clock->display != NULL ) + { + clocklog(clock, 2, "[main] Diconnecting from server.\n"); + wl_display_disconnect(clock->display); + } +} + +static bool handle_command_flags (struct Wlclock *clock, int argc, char *argv[]) +{ + static struct option opts[] = { + {"help", no_argument, NULL, 'h'}, + {"verbose", no_argument, NULL, 'v'}, + {"version", no_argument, NULL, 'V'}, + + {"output", required_argument, NULL, 100}, + {"no-input", no_argument, NULL, 101} + }; + + const char *usage = + "Usage: wlclock [options]\n" + "\n" + " -h, --help Show this helptext.\n" + " -v, --verbose Increase verbosity of output.\n" + " -V, --version Show the version.\n" + " --output Name of output the clock should be displayed on.\n" + " --no-input The clock surface will not catch input events.\n" + "\n"; + + int opt; + extern int optind; + extern char *optarg; + while ( (opt = getopt_long(argc, argv, "hvV", opts, &optind)) != -1 ) switch (opt) + { + case 'h': + fputs(usage, stderr); + clock->ret = EXIT_SUCCESS; + return false; + + case 'v': + clock->verbosity++; + break; + + case 'V': + fputs("wlclock version " VERSION "\n", stderr); + clock->ret = EXIT_SUCCESS; + return false; + + case 100: + set_string(&clock->output, optarg); + break; + + case 101: + clock->input = false; + break; + + default: + return false; + } + + return true; +} + +static time_t get_timeout (time_t now) +{ + return ((now / 60 * 60 ) + 60 - now) * 1000; +} + +static void clock_run (struct Wlclock *clock) +{ + clocklog(clock, 1, "[main] Starting loop.\n"); + + clock->ret = EXIT_SUCCESS; + + struct pollfd fds[] = { + { .fd = wl_display_get_fd(clock->display), .events = POLLIN } + }; + + while (clock->loop) + { + /* Flush Wayland events. */ + errno = 0; + do { + if ( wl_display_flush(clock->display) == 1 && errno != EAGAIN ) + { + clocklog(NULL, 0, "ERROR: wl_display_flush: %s\n", + strerror(errno)); + break; + } + } while ( errno == EAGAIN ); + + clock->now = time(NULL); + errno = 0; + int ret = poll(fds, 1, get_timeout(clock->now)); + + if ( ret == 0 ) + update_all_surfaces(clock); + else if ( ret > 0 ) + { + if ( fds[0].revents & POLLIN && wl_display_dispatch(clock->display) == -1 ) + { + clocklog(NULL, 0, "ERROR: wl_display_flush: %s\n", strerror(errno)); + goto error; + } + if ( fds[0].revents & POLLOUT && wl_display_flush(clock->display) == -1 ) + { + clocklog(NULL, 0, "ERROR: wl_display_flush: %s\n", strerror(errno)); + goto error; + } + } + else + clocklog(NULL, 0, "ERROR: poll: %s\n", strerror(errno)); + } + + return; +error: + clock->ret = EXIT_FAILURE; + return; +} + +int main (int argc, char *argv[]) +{ + struct Wlclock clock = { 0 }; + clock.ret = EXIT_FAILURE; + clock.loop = true; + clock.verbosity = 0; + clock.size = 100; + clock.exclusive_zone = 1; + clock.input = true; + clock.layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; + clock.anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + set_string(&clock.namespace, "wlclock"); + wl_list_init(&clock.outputs); + + if (! handle_command_flags(&clock, argc, argv)) + return clock.ret; + + clocklog(&clock, 1, "[main] wlclock: version=%s\n", VERSION); + + if (! init_wayland(&clock)) + goto exit; + + clock_run(&clock); + +exit: + finish_wayland(&clock); + return clock.ret; +} + diff --git a/src/wlclock.h b/src/wlclock.h new file mode 100644 index 0000000..684c4b3 --- /dev/null +++ b/src/wlclock.h @@ -0,0 +1,40 @@ +#ifndef WLCLOCK_WLCLOCK_H +#define WLCLOCK_WLCLOCK_H + +#include<stdbool.h> +#include<stdint.h> +#include<time.h> +#include<wayland-server.h> + +#include"wlr-layer-shell-unstable-v1-protocol.h" + +struct Wlclock +{ + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_shm *shm; + struct zwlr_layer_shell_v1 *layer_shell; + struct zxdg_output_manager_v1 *xdg_output_manager; + + struct wl_list outputs; + char *output; + + time_t now; + + bool loop; + int verbosity; + int ret; + + enum zwlr_layer_shell_v1_layer layer; + uint32_t size; + char *namespace; + int32_t exclusive_zone; + uint32_t border_top, border_right, border_bottom, border_left; + uint32_t margin_top, margin_right, margin_bottom, margin_left; + uint32_t radius_top_left, radius_top_right, radius_bottom_left, radius_bottom_right; + uint32_t anchor; + bool input; +}; + +#endif |