diff options
Diffstat (limited to 'src/wlclock.c')
-rw-r--r-- | src/wlclock.c | 298 |
1 files changed, 298 insertions, 0 deletions
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; +} + |