summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLeon Henrik Plickat <leonhenrik.plickat@stud.uni-goettingen.de>2020-09-24 18:01:28 +0200
committerLeon Henrik Plickat <leonhenrik.plickat@stud.uni-goettingen.de>2020-09-24 18:01:28 +0200
commit9ff4f4959b8d78104fa4defb3c4cea9606e3c484 (patch)
treeac80aef7e1caff0d0af491ea4816ca63706a8116 /src
downloadwlclock-9ff4f4959b8d78104fa4defb3c4cea9606e3c484.tar.gz
wlclock-9ff4f4959b8d78104fa4defb3c4cea9606e3c484.tar.bz2
Init
Diffstat (limited to 'src')
-rw-r--r--src/buffer.c184
-rw-r--r--src/buffer.h25
-rw-r--r--src/misc.c30
-rw-r--r--src/misc.h10
-rw-r--r--src/output.c169
-rw-r--r--src/output.h33
-rw-r--r--src/surface.c163
-rw-r--r--src/surface.h31
-rw-r--r--src/wlclock.c298
-rw-r--r--src/wlclock.h40
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, &registry_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