mowc

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.nihaljere.xyz/mowc
Log | Files | Refs

commit 0424e9a0ec06e49b9c67d4835711b64aee3390b9
parent c5e6c238f6b1280d1cb545f14efe8b615deedfae
Author: Nihal Jere <nihal@nihaljere.xyz>
Date:   Sun, 27 Jun 2021 16:21:03 -0500

status_bar: stolen from velox, changed slightly

Diffstat:
Aclients/status_bar.c | 452+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmowc.c | 6++++++
2 files changed, 458 insertions(+), 0 deletions(-)

diff --git a/clients/status_bar.c b/clients/status_bar.c @@ -0,0 +1,452 @@ +/* mowc: clients/status_bar.c + * + * Copyright (c) 2021 Nihal Jere <nihal@nihaljere.xyz> + * Copyright (c) 2010, 2013, 2014 Michael Forney <mforney@mforney.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <poll.h> +#include <sys/signalfd.h> +#include <time.h> +#include <wayland-client.h> +#include <wld/wayland.h> +#include <wld/wld.h> + +#include "swc-client-protocol.h" + +#define BATTERY_CAP_PATH "/sys/class/power_supply/axp20x-battery/capacity" + +enum align { + ALIGN_LEFT, + ALIGN_CENTER, + ALIGN_RIGHT, +}; + +struct item { + const struct item_interface *interface; + const struct item_data *data; + struct wl_list link; +}; + +struct item_data { + uint32_t width; +}; + +struct text_item_data { + struct item_data base; + const char *text; +}; + +struct status_bar { + struct wl_surface *surface; + struct swc_panel *panel; + + struct wld_surface *wld_surface; + uint32_t width, height; + + struct wl_list items[3]; +}; + +struct item_interface { + void (*draw)(struct status_bar *status_bar, struct item *item, uint32_t x, uint32_t y); +}; + +struct style { + uint32_t fg, bg; +}; + +struct screen { + struct swc_screen *swc; + struct status_bar status_bar; + struct wl_list link; + + struct text_item_data focus_data; +}; + +/* Wayland listeners */ +static void registry_global(void *data, struct wl_registry *registry, + uint32_t name, const char *implementation, uint32_t version); +static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name); + +static void panel_docked(void *data, struct swc_panel *panel, uint32_t length); + +/* Item interfaces */ +static void text_draw(struct status_bar *status_bar, struct item *item, uint32_t x, uint32_t y); +static void divider_draw(struct status_bar *status_bar, struct item *item, uint32_t x, uint32_t y); + +static struct wl_display *display; +static struct wl_registry *registry; +static struct wl_compositor *compositor; +static struct swc_panel_manager *panel_manager; + +static struct wl_list screens; + +static struct { + struct wld_context *context; + struct wld_renderer *renderer; + struct wld_font_context *font_context; + struct wld_font *font; +} wld; + +static const struct wl_registry_listener registry_listener = { + .global = &registry_global, + .global_remove = &registry_global_remove +}; + +static const struct swc_panel_listener panel_listener = { + .docked = &panel_docked +}; + +static const struct item_interface text_interface = { + .draw = &text_draw +}; + +static const struct item_interface divider_interface = { + .draw = &divider_draw +}; + +/* Configuration parameters */ +static const int spacing = 12; +static const char *const font_name = "Terminus:pixelsize=30"; +static const struct style normal = { .bg = 0xff1a1a1a, .fg = 0xff999999 }; +static const struct style selected = { .bg = 0xff338833, .fg = 0xffffffff }; + +static timer_t timer; +static bool running, need_draw; +static char clock_text[32]; +static char bat_text[32]; +static struct item_data divider_data = {.width = 14 }; +static struct text_item_data clock_data = {.text = clock_text }; +static struct text_item_data bat_data = {.text = bat_text }; + +static void __attribute__((noreturn)) die(const char *const format, ...) +{ + va_list args; + + va_start(args, format); + fputs("FATAL: ", stderr); + vfprintf(stderr, format, args); + fputc('\n', stderr); + va_end(args); + exit(EXIT_FAILURE); +} + +static void * +xmalloc(size_t size) +{ + void *data; + + if (!(data = malloc(size))) + die("Allocation failed"); + return data; +} + +static struct item * +item_new(const struct item_interface *interface, const struct item_data *data) +{ + struct item *item; + + if (!(item = malloc(sizeof(*item)))) + die("Failed to allocate item"); + + item->interface = interface; + item->data = data; + + return item; +} + +static void +update_text_item_data(struct text_item_data *data) +{ + struct wld_extents extents; + + wld_font_text_extents(wld.font, data->text, &extents); + data->base.width = extents.advance + spacing; + need_draw = true; +} + +/* Wayland event handlers */ +static void +registry_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + if (strcmp(interface, "wl_compositor") == 0) { + compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 3); + } else if (strcmp(interface, "swc_panel_manager") == 0) { + panel_manager = wl_registry_bind(registry, name, &swc_panel_manager_interface, 1); + } else if (strcmp(interface, "swc_screen") == 0) { + struct screen *screen; + + screen = xmalloc(sizeof(*screen)); + screen->swc = wl_registry_bind(registry, name, &swc_screen_interface, 1); + if (!screen->swc) + die("Failed to bind swc_screen"); + wl_list_insert(screens.prev, &screen->link); + } +} + +static void +registry_global_remove(void *data, struct wl_registry *registry, uint32_t name) +{ +} + +static void +panel_docked(void *data, struct swc_panel *panel, uint32_t length) +{ + struct status_bar *bar = data; + + bar->width = length; + bar->height = wld.font->height + 2; + bar->wld_surface = wld_wayland_create_surface(wld.context, bar->width, bar->height, + WLD_FORMAT_XRGB8888, 0, bar->surface); +} + +/* Item implementations */ +void +text_draw(struct status_bar *bar, struct item *item, uint32_t x, uint32_t y) +{ + struct text_item_data *data = (void *)item->data; + + wld_draw_text(wld.renderer, wld.font, normal.fg, x, y + wld.font->ascent + 1, data->text, -1, NULL); +} + +void +divider_draw(struct status_bar *bar, struct item *item, uint32_t x, uint32_t y) +{ + wld_fill_rectangle(wld.renderer, normal.fg, x + spacing / 2, y, 2, bar->height); +} + +static void +draw(struct status_bar *bar) +{ + struct item *item; + uint32_t start_x[3] = { + 0, bar->width / 2, bar->width + }; + uint32_t x; + + wld_set_target_surface(wld.renderer, bar->wld_surface); + wld_fill_rectangle(wld.renderer, normal.bg, 0, 0, bar->width, bar->height); + + wl_list_for_each (item, &bar->items[ALIGN_CENTER], link) + start_x[ALIGN_CENTER] -= item->data->width / 2; + + wl_list_for_each (item, &bar->items[ALIGN_RIGHT], link) + start_x[ALIGN_RIGHT] -= item->data->width; + + x = start_x[ALIGN_LEFT]; + wl_list_for_each (item, &bar->items[ALIGN_LEFT], link) { + item->interface->draw(bar, item, x, 0); + x += item->data->width; + } + + x = start_x[ALIGN_CENTER]; + wl_list_for_each (item, &bar->items[ALIGN_CENTER], link) { + item->interface->draw(bar, item, x, 0); + x += item->data->width; + } + + x = start_x[ALIGN_RIGHT]; + wl_list_for_each (item, &bar->items[ALIGN_RIGHT], link) { + item->interface->draw(bar, item, x, 0); + x += item->data->width; + } + + wl_surface_damage(bar->surface, 0, 0, bar->width, bar->height); + wld_flush(wld.renderer); + wld_swap(bar->wld_surface); +} + +static void +setup(void) +{ + struct status_bar *status_bar; + struct screen *screen; + struct wl_list *items; + struct item *item; + + fprintf(stderr, "status bar: Initializing..."); + + wl_list_init(&screens); + + if (timer_create(CLOCK_MONOTONIC, NULL, &timer) != 0) + die("Failed to create timer: %s", strerror(errno)); + + if (!(display = wl_display_connect(NULL))) + die("Failed to connect to display"); + + if (!(registry = wl_display_get_registry(display))) + die("Failed to get registry"); + + wl_registry_add_listener(registry, &registry_listener, NULL); + + /* Wait for globals. */ + wl_display_roundtrip(display); + + if (!compositor || !panel_manager) { + die("Missing required globals: wl_compositor, swc_panel_manager"); + } + + wld.context = wld_wayland_create_context(display, WLD_ANY); + if (!wld.context) + die("Failed to create WLD context"); + + wld.renderer = wld_create_renderer(wld.context); + if (!wld.renderer) + die("Failed to create WLD renderer"); + + /* Font */ + wld.font_context = wld_font_create_context(); + if (!wld.font_context) + die("Failed to create WLD font context"); + wld.font = wld_font_open_name(wld.font_context, font_name); + if (!wld.font) + die("Failed to open font"); + + /* Create the panels */ + wl_list_for_each (screen, &screens, link) { + status_bar = &screen->status_bar; + status_bar->surface = wl_compositor_create_surface(compositor); + status_bar->panel = swc_panel_manager_create_panel(panel_manager, status_bar->surface); + swc_panel_add_listener(status_bar->panel, &panel_listener, status_bar); + swc_panel_dock(status_bar->panel, SWC_PANEL_EDGE_TOP, screen->swc, false); + + /* Add items */ + items = &screen->status_bar.items[ALIGN_LEFT]; + wl_list_init(items); + + /* Divider */ + //item = item_new(&divider_interface, &divider_data); + //wl_list_insert(items->prev, &item->link); + + items = &screen->status_bar.items[ALIGN_CENTER]; + wl_list_init(items); + + /* Clock */ + item = item_new(&text_interface, &clock_data.base); + wl_list_insert(items, &item->link); + + items = &screen->status_bar.items[ALIGN_RIGHT]; + wl_list_init(items); + + item = item_new(&text_interface, &bat_data.base); + wl_list_insert(items, &item->link); + + } + + /* Wait for dock notifications. */ + wl_display_roundtrip(display); + + wl_list_for_each (screen, &screens, link) { + if (!screen->status_bar.wld_surface) + die(""); + swc_panel_set_strut(screen->status_bar.panel, screen->status_bar.height, 0, screen->status_bar.width); + } + + wl_display_flush(display); +} + +static void +update_bat() +{ + FILE *f = fopen(BATTERY_CAP_PATH, "r"); + if (f == NULL) + return; + + size_t count = fread(bat_text, sizeof(char), 3, f); + if (count == -1 || count >= sizeof(bat_text)) + return; + + bat_text[count-1] = '%'; + + fclose(f); +} + +static void +run(void) +{ + sigset_t signals; + struct itimerspec timer_value = { + .it_interval = { 1, 0 }, + .it_value = { 0, 1 } + }; + struct pollfd fds[2]; + struct screen *screen; + + sigemptyset(&signals); + sigaddset(&signals, SIGALRM); + sigprocmask(SIG_BLOCK, &signals, NULL); + + fds[0].fd = wl_display_get_fd(display); + fds[0].events = POLLIN; + fds[1].fd = signalfd(-1, &signals, SFD_CLOEXEC); + fds[1].events = POLLIN; + + timer_settime(timer, 0, &timer_value, NULL); + running = true; + + while (true) { + if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) == -1) + break; + + if (fds[0].revents & POLLIN) { + if (wl_display_dispatch(display) == -1) { + fprintf(stderr, "Wayland dispatch error: %s\n", + strerror(wl_display_get_error(display))); + break; + } + } + if (fds[1].revents & POLLIN) { + time_t raw_time = time(NULL); + struct tm *local_time = localtime(&raw_time); + + sigwaitinfo(&signals, NULL); + + strftime(clock_text, sizeof(clock_text), "%I:%M %p", local_time); + update_text_item_data(&clock_data); + + /* just update battery whenever clock is updated */ + update_bat(); + update_text_item_data(&bat_data); + } + + if (need_draw) { + wl_list_for_each (screen, &screens, link) + draw(&screen->status_bar); + need_draw = false; + } + + wl_display_flush(display); + } +} + +int +main(int argc, char *argv[]) +{ + setup(); + run(); + + return EXIT_SUCCESS; +} diff --git a/mowc.c b/mowc.c @@ -7,6 +7,7 @@ #include <xkbcommon/xkbcommon.h> #define OVERLAY_PROGRAM MOWC_LIBEXEC "/overlay" +#define STATUS_BAR_PROGRAM MOWC_LIBEXEC "/status_bar" static struct wl_display *display; static struct swc_screen *curscreen; @@ -155,6 +156,11 @@ main(int argc, char *argv[]) return 1; } + if (launchchild(STATUS_BAR_PROGRAM) < 0) { + fprintf(stderr, "failed to launch status bar\n"); + return 1; + } + event_loop = wl_display_get_event_loop(display); wl_display_run(display); wl_display_destroy(display);