wob-0.14.2/0000755000175100001710000000000014336511602011725 5ustar runnerdockerwob-0.14.2/meson.build0000644000175100001710000000545314336511601014075 0ustar runnerdockerproject( 'wob', 'c', version: '0.14.2', license: 'ISC', default_options: ['c_std=c99'] ) cc = meson.get_compiler('c') wayland_protos = dependency('wayland-protocols', version: '>=1.13') wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') wayland_scanner = find_program('wayland-scanner') wayland_client = dependency('wayland-client') rt = cc.find_library('rt') seccomp = dependency('libseccomp', required: get_option('seccomp')) inih = dependency('inih') wob_version = '"@0@"'.format(meson.project_version()) add_project_arguments('-DWOB_VERSION=@0@'.format(wob_version), language: 'c') wayland_scanner_code = generator( wayland_scanner, output: '@BASENAME@-protocol.c', arguments: ['private-code', '@INPUT@', '@OUTPUT@'], ) wayland_scanner_client = generator( wayland_scanner, output: '@BASENAME@-client-protocol.h', arguments: ['client-header', '@INPUT@', '@OUTPUT@'], ) client_protocols = [ [wl_protocol_dir + '/stable/xdg-shell', 'xdg-shell.xml'], [wl_protocol_dir + '/unstable/xdg-output', 'xdg-output-unstable-v1.xml'], [meson.source_root() + '/protocols', 'wlr-layer-shell-unstable-v1.xml'], ] foreach p : client_protocols xml = join_paths(p) src = wayland_scanner_code.process(xml) header = wayland_scanner_client.process(xml) name = p[1].split('.')[0].underscorify() lib = static_library( name, [src, header], dependencies: [wayland_client], ) dep = declare_dependency( link_with: lib, sources: header, ) set_variable(name, dep) endforeach wob_inc = include_directories('include') wob_sources = ['main.c', 'parse.c', 'buffer.c', 'log.c', 'color.c', 'config.c'] wob_dependencies = [xdg_output_unstable_v1, wayland_client, wlr_layer_shell_unstable_v1, xdg_shell, rt, inih] if seccomp.found() wob_dependencies += seccomp wob_sources += 'pledge_seccomp.c' else wob_sources += 'pledge.c' endif executable( 'wob', wob_sources, include_directories: [wob_inc], dependencies: wob_dependencies, install: true ) test('parse-input', executable( 'test-parse-input', ['tests/wob_parse_input.c', 'parse.c', 'color.c'], include_directories: [wob_inc] )) scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) if scdoc.found() scdfiles = ['wob.1.scd', 'wob.ini.5.scd'] scdoc = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) sh = find_program('sh', native: true) mandir = get_option('mandir') foreach scdfile : scdfiles manfile = scdfile.split('.scd')[0] section = scdfile.split('.')[-2] custom_target( manfile, input: scdfile, output: manfile, command: [ sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc.path(), manfile) ], install: true, install_dir: '@0@/man@1@'.format(mandir, section) ) endforeach endif wob-0.14.2/include/0000755000175100001710000000000014336511601013347 5ustar runnerdockerwob-0.14.2/include/parse.h0000644000175100001710000000036714336511601014640 0ustar runnerdocker#ifndef _WOB_PARSE_H #define _WOB_PARSE_H #include #include "color.h" bool wob_parse_input(const char *input_buffer, unsigned long *percentage, struct wob_color *background, struct wob_color *border, struct wob_color *bar); #endif wob-0.14.2/include/pledge.h0000644000175100001710000000014214336511601014755 0ustar runnerdocker#ifndef _WOB_PLEDGE_H #define _WOB_PLEDGE_H #include bool wob_pledge(void); #endif wob-0.14.2/include/color.h0000644000175100001710000000066514336511601014645 0ustar runnerdocker#ifndef _WOB_COLOR_H #define _WOB_COLOR_H #include #include struct wob_color { float a; float r; float g; float b; }; uint32_t wob_color_to_argb(struct wob_color color); uint32_t wob_color_to_rgba(struct wob_color color); struct wob_color wob_color_premultiply_alpha(struct wob_color color); bool wob_color_from_string(const char *restrict str, char **restrict str_end, struct wob_color *color); #endif wob-0.14.2/include/buffer.h0000644000175100001710000000021614336511601014770 0ustar runnerdocker#ifndef _WOB_BUFFER_H #define _WOB_BUFFER_H #include int wob_shm_create(); void *wob_shm_alloc(int shmid, size_t size); #endif wob-0.14.2/include/config.h0000644000175100001710000000252614336511601014772 0ustar runnerdocker#ifndef _WOB_CONFIG_H #define _WOB_CONFIG_H #include #include #include "color.h" #include "log.h" enum wob_overflow_mode { WOB_OVERFLOW_MODE_WRAP, WOB_OVERFLOW_MODE_NOWRAP, }; enum wob_anchor { WOB_ANCHOR_CENTER = 0, WOB_ANCHOR_TOP = 1, WOB_ANCHOR_BOTTOM = 2, WOB_ANCHOR_LEFT = 4, WOB_ANCHOR_RIGHT = 8, }; enum wob_output_mode { WOB_OUTPUT_MODE_WHITELIST, WOB_OUTPUT_MODE_ALL, WOB_OUTPUT_MODE_FOCUSED, }; struct wob_output_config { char *name; struct wl_list link; }; struct wob_colors { struct wob_color background; struct wob_color border; struct wob_color value; }; struct wob_dimensions { unsigned long width; unsigned long height; unsigned long border_offset; unsigned long border_size; unsigned long bar_padding; }; struct wob_config { unsigned long max; unsigned long timeout_msec; unsigned long margin; unsigned long anchor; enum wob_output_mode output_mode; enum wob_overflow_mode overflow_mode; struct wob_dimensions dimensions; struct wob_colors colors; struct wob_colors overflow_colors; struct wl_list outputs; }; void wob_config_init(struct wob_config *config); bool wob_config_load(struct wob_config *config, const char *config_path); void wob_config_destroy(struct wob_config *config); void wob_config_debug(struct wob_config *config); char *wob_config_default_path(); #endif wob-0.14.2/include/log.h0000644000175100001710000000174414336511601014307 0ustar runnerdocker#ifndef _WOB_LOG_H #define _WOB_LOG_H #include typedef enum { WOB_LOG_DEBUG = 0, WOB_LOG_INFO = 1, WOB_LOG_WARN = 2, WOB_LOG_ERROR = 3, } wob_log_importance; void wob_log(wob_log_importance importance, const char *file, int line, const char *fmt, ...); void wob_log_set_level(wob_log_importance importance); void wob_log_inc_verbosity(void); void wob_log_use_colors(bool use_colors); #define wob_log_debug(...) wob_log(WOB_LOG_DEBUG, WOB_FILE, __LINE__, __VA_ARGS__) #define wob_log_info(...) wob_log(WOB_LOG_INFO, WOB_FILE, __LINE__, __VA_ARGS__) #define wob_log_warn(...) wob_log(WOB_LOG_WARN, WOB_FILE, __LINE__, __VA_ARGS__) #define wob_log_error(...) wob_log(WOB_LOG_ERROR, WOB_FILE, __LINE__, __VA_ARGS__) #define wob_log_level_debug() wob_log_set_level(WOB_LOG_DEBUG); #define wob_log_level_info() wob_log_set_level(WOB_LOG_INFO); #define wob_log_level_warn() wob_log_set_level(WOB_LOG_WARN); #define wob_log_level_error() wob_log_set_level(WOB_LOG_ERROR); #endif wob-0.14.2/LICENSE0000644000175100001710000000135014336511601012730 0ustar runnerdockerISC License Copyright (c) 2019, Martin Franc Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. wob-0.14.2/wob.ini.5.scd0000644000175100001710000000350614336511601014133 0ustar runnerdockerwob.ini(5) # NAME wob.ini - wob configuration file You can run `wob -vv` to find the default values. # SECTION: default *timeout* Timeout after which wob hides itself, in milliseconds. *max* This value will be represented as fully filled bar. *width* Width of wob, in pixels. *height* Height of wob, in pixels. *border_offset* Border offset, in pixels. *border_size* Border size, in pixels. *bar_padding* Bar padding, in pixels. *anchor* Anchor point, combination of *top*, *left*, *right*, *bottom*, *center*. Example: *bottom* *right* *margin* Anchor margin, in pixels. *border_color* Border color, in RRGGBB[AA] format. *background_color* Background color, in RRGGBB[AA] format. *bar_color* Bar color, in RRGGBB[AA] format. *overflow_mode* Overflow mode, one of *wrap*, and *nowrap*. *wrap*: wrap value so *max* + 20 will be displayed as 20 *nowrap* values > *max* will be displayed as just *max* *overflow_bar_color* Overflow bar color, in RRGGBB[AA] format. *overflow_background_color* Overflow background color, in RRGGBB[AA] format. *overflow_border_color* Overflow border color, in RRGGBB[AA] format. *output_mode* Output mode, one of *whitelist*, *all*, *focused*. *whitelist*: show wob only on outputs defined in *output.\** sections of config file *all*: show wob on all available outputs *focused*: show wob on compositor default (focused) output # SECTION: output.* This section will be used in future releases of wob to provide per output configuration. Currently this servers only as list of outputs to show wob on in *whitelist* mode. Replace *\** with user friendly name of your choosing. *name* Output name. Example: DP-1 # EXAMPLE ``` timeout = 1000 max = 100 output_mode = whitelist bar_color = FFFFFF [output.left] name = DP-1 [output.ips] name = HDMI-1 ``` # See also *wob*(1) wob-0.14.2/README.md0000644000175100001710000001055114336511601013205 0ustar runnerdocker# wob — Wayland Overlay Bar [![Build Status](https://github.com/francma/wob/workflows/test/badge.svg)](https://github.com/francma/wob/actions) ![preview](https://martinfranc.eu/wob-preview.svg) A lightweight overlay volume/backlight/progress/anything bar for wlroots based Wayland compositors (requrires support for `wlr_layer_shell_unstable_v1`). This project is inspired by [xob - X Overlay Bar](https://github.com/florentc/xob). ## Release signatures Releases are signed with [5C6DA024DDE27178073EA103F4B432D5D67990E3](https://keys.openpgp.org/vks/v1/by-fingerprint/5C6DA024DDE27178073EA103F4B432D5D67990E3) and published on [GitHub](https://github.com/francma/wob/releases). ## Installation ### Compiling from source Install dependencies: - wayland - [inih](https://github.com/benhoyt/inih) - wayland-protocols \* - meson \* - [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optional: man page) \* - [libseccomp](https://github.com/seccomp/libseccomp) (optional: Linux kernel syscall filtering) \* \* _compile-time dependecy_ Run these commands: ``` git clone git@github.com:francma/wob.git cd wob meson build ninja -C build sudo ninja -C build install ``` ### From packages [![Packaging status](https://repology.org/badge/tiny-repos/wob.svg)](https://repology.org/project/wob/versions) ## Usage Launch wob in a terminal, enter a value (positive integer), press return. ``` wob ``` ### General case You may manage a bar for audio volume, backlight intensity, or whatever, using a named pipe. Create a named pipe, e.g. `/tmp/wobpipe`, on your filesystem using. ``` mkfifo /tmp/wobpipe ``` Connect the named pipe to the standard input of a wob instance. ``` tail -f /tmp/wobpipe | wob ``` Set up your environment so that after updating audio volume, backlight intensity, or whatever, to a new value like 43, it writes that value into the pipe: ``` echo 43 > /tmp/wobpipe ``` Adapt this use-case to your workflow (scripts, callbacks, or keybindings handled by the window manager). See [wob.ini.5](https://github.com/francma/wob/blob/master/wob.ini.5.scd) for styling and positioning options. ### Sway WM example Add these lines to your Sway config file: ``` set $WOBSOCK $XDG_RUNTIME_DIR/wob.sock exec rm -f $WOBSOCK && mkfifo $WOBSOCK && tail -f $WOBSOCK | wob ``` Volume using alsa: ``` bindsym XF86AudioRaiseVolume exec amixer sset Master 5%+ | sed -En 's/.*\[([0-9]+)%\].*/\1/p' | head -1 > $WOBSOCK bindsym XF86AudioLowerVolume exec amixer sset Master 5%- | sed -En 's/.*\[([0-9]+)%\].*/\1/p' | head -1 > $WOBSOCK bindsym XF86AudioMute exec amixer sset Master toggle | sed -En '/\[on\]/ s/.*\[([0-9]+)%\].*/\1/ p; /\[off\]/ s/.*/0/p' | head -1 > $WOBSOCK ``` Volume using pulse audio: ``` bindsym XF86AudioRaiseVolume exec pamixer -ui 2 && pamixer --get-volume > $WOBSOCK bindsym XF86AudioLowerVolume exec pamixer -ud 2 && pamixer --get-volume > $WOBSOCK bindsym XF86AudioMute exec pamixer --toggle-mute && ( [ "$(pamixer --get-mute)" = "true" ] && echo 0 > $WOBSOCK ) || pamixer --get-volume > $WOBSOCK ``` Volume using pulse audio (altenative with pactl) : ``` bindsym XF86AudioRaiseVolume exec pactl set-sink-volume @DEFAULT_SINK@ +5% && pactl get-sink-volume @DEFAULT_SINK@ | head -n 1| awk '{print substr($5, 1, length($5)-1)}' > $WOBSOCK bindsym XF86AudioLowerVolume exec pactl set-sink-volume @DEFAULT_SINK@ -5% && pactl get-sink-volume @DEFAULT_SINK@ | head -n 1 | awk '{print substr($5, 1, length($5)-1)}' > $WOBSOCK ``` Brightness using [haikarainen/light](https://github.com/haikarainen/light): ``` bindsym XF86MonBrightnessUp exec light -A 5 && light -G | cut -d'.' -f1 > $WOBSOCK bindsym XF86MonBrightnessDown exec light -U 5 && light -G | cut -d'.' -f1 > $WOBSOCK ``` Brightness using [brightnessctl](https://github.com/Hummer12007/brightnessctl): ``` bindsym XF86MonBrightnessDown exec brightnessctl set 5%- | sed -En 's/.*\(([0-9]+)%\).*/\1/p' > $WOBSOCK bindsym XF86MonBrightnessUp exec brightnessctl set +5% | sed -En 's/.*\(([0-9]+)%\).*/\1/p' > $WOBSOCK ``` #### Systemd Add this line to your config file: ``` exec systemctl --user import-environment DISPLAY WAYLAND_DISPLAY SWAYSOCK ``` Copy systemd unit files (if not provided by your distribution package): ``` cp contrib/systemd/wob.{service,socket} ~/.local/share/systemd/user/ systemctl daemon-reload --user ``` Enable systemd wob socket: ``` systemctl enable --now --user wob.socket ``` ## License ISC, see [LICENSE](/LICENSE). wob-0.14.2/main.c0000644000175100001710000004750714336511601013031 0ustar runnerdocker#define WOB_FILE "main.c" #define MIN_PERCENTAGE_BAR_WIDTH 1 #define MIN_PERCENTAGE_BAR_HEIGHT 1 // sizeof already includes NULL byte #define INPUT_BUFFER_LENGTH (3 * sizeof(unsigned long) + sizeof(" 000000FF FFFFFFFF FFFFFFFF\n")) #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define STDIN_BUFFER_LENGTH INPUT_BUFFER_LENGTH #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include "buffer.h" #include "color.h" #include "config.h" #include "log.h" #include "parse.h" #include "pledge.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" struct wob_surface { struct zwlr_layer_surface_v1 *wlr_layer_surface; struct wl_surface *wl_surface; }; struct wob_output { char *name; struct wl_list link; struct wl_output *wl_output; struct wob *app; struct wob_surface *wob_surface; struct zxdg_output_v1 *xdg_output; uint32_t wl_name; }; struct wob { int shmid; struct wl_buffer *wl_buffer; struct wl_compositor *wl_compositor; struct wl_display *wl_display; struct wl_list wob_outputs; struct wl_registry *wl_registry; struct wl_shm *wl_shm; struct zwlr_layer_shell_v1 *wlr_layer_shell; struct zxdg_output_manager_v1 *xdg_output_manager; struct wob_config wob_config; }; unsigned long wob_anchor_to_wlr_layer_surface_anchor(enum wob_anchor wob_anchor) { unsigned long wlr_layer_surface_anchor = 0; if (WOB_ANCHOR_TOP & wob_anchor) { wlr_layer_surface_anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; } if (WOB_ANCHOR_RIGHT & wob_anchor) { wlr_layer_surface_anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; } if (WOB_ANCHOR_BOTTOM & wob_anchor) { wlr_layer_surface_anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; } if (WOB_ANCHOR_LEFT & wob_anchor) { wlr_layer_surface_anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT; } return wlr_layer_surface_anchor; } void noop() { /* intentionally left blank */ } void layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t w, uint32_t h) { zwlr_layer_surface_v1_ack_configure(surface, serial); } struct wob_surface * wob_surface_create(struct wob *app, struct wl_output *wl_output) { struct wob_config config = app->wob_config; const static struct zwlr_layer_surface_v1_listener zwlr_layer_surface_listener = { .configure = layer_surface_configure, .closed = noop, }; struct wob_surface *wob_surface = calloc(1, sizeof(struct wob_surface)); if (wob_surface == NULL) { wob_log_error("calloc failed"); exit(EXIT_FAILURE); } wob_surface->wl_surface = wl_compositor_create_surface(app->wl_compositor); if (wob_surface->wl_surface == NULL) { wob_log_error("wl_compositor_create_surface failed"); exit(EXIT_FAILURE); } wob_surface->wlr_layer_surface = zwlr_layer_shell_v1_get_layer_surface(app->wlr_layer_shell, wob_surface->wl_surface, wl_output, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "wob"); if (wob_surface->wlr_layer_surface == NULL) { wob_log_error("wlr_layer_shell_v1_get_layer_surface failed"); exit(EXIT_FAILURE); } struct wob_dimensions dimensions = config.dimensions; unsigned long margin = config.margin; enum wob_anchor anchor = config.anchor; zwlr_layer_surface_v1_set_size(wob_surface->wlr_layer_surface, dimensions.width, dimensions.height); zwlr_layer_surface_v1_set_anchor(wob_surface->wlr_layer_surface, wob_anchor_to_wlr_layer_surface_anchor(anchor)); zwlr_layer_surface_v1_set_margin(wob_surface->wlr_layer_surface, margin, margin, margin, margin); zwlr_layer_surface_v1_add_listener(wob_surface->wlr_layer_surface, &zwlr_layer_surface_listener, app); wl_surface_commit(wob_surface->wl_surface); return wob_surface; } void wob_surface_destroy(struct wob_surface *wob_surface) { zwlr_layer_surface_v1_destroy(wob_surface->wlr_layer_surface); wl_surface_destroy(wob_surface->wl_surface); wob_surface->wl_surface = NULL; wob_surface->wlr_layer_surface = NULL; } void wob_output_destroy(struct wob_output *output) { if (output->wob_surface != NULL) { wob_surface_destroy(output->wob_surface); } if (output->xdg_output != NULL) { zxdg_output_v1_destroy(output->xdg_output); } if (output->wl_output != NULL) { wl_output_destroy(output->wl_output); } free(output->name); free(output->wob_surface); output->wob_surface = NULL; output->wl_output = NULL; output->xdg_output = NULL; output->name = NULL; } void xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) { struct wob_output *output = (struct wob_output *) data; output->name = strdup(name); if (output->name == NULL) { wob_log_error("strdup failed\n"); exit(EXIT_FAILURE); } } void xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) { struct wob_output *output = (struct wob_output *) data; struct wob *app = output->app; struct wob_output_config *output_config; wob_log_info("Detected output name %s", output->name); if (app->wob_config.output_mode == WOB_OUTPUT_MODE_ALL) { wl_list_insert(&output->app->wob_outputs, &output->link); wob_log_info("Bar will be displayed on %s, because 'output_mode = all' is selected", output->name); return; } wl_list_for_each (output_config, &app->wob_config.outputs, link) { if (strcmp(output->name, output_config->name) == 0) { wl_list_insert(&output->app->wob_outputs, &output->link); wob_log_info("Bar will be displayed on output %s, because it matches whitelist rule", output->name); return; } } wob_log_info("Bar will NOT be displayed on output %s", output->name); wob_output_destroy(output); free(output); } void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { const static struct zxdg_output_v1_listener xdg_output_listener = { .logical_position = noop, .logical_size = noop, .name = xdg_output_handle_name, .description = noop, .done = xdg_output_handle_done, }; struct wob *app = (struct wob *) data; if (strcmp(interface, wl_shm_interface.name) == 0) { app->wl_shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); } else if (strcmp(interface, wl_compositor_interface.name) == 0) { app->wl_compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); } else if (strcmp(interface, "wl_output") == 0) { if (app->wob_config.output_mode != WOB_OUTPUT_MODE_FOCUSED) { struct wob_output *output = calloc(1, sizeof(struct wob_output)); output->wl_output = wl_registry_bind(registry, name, &wl_output_interface, 1); output->app = app; output->wl_name = name; output->xdg_output = zxdg_output_manager_v1_get_xdg_output(app->xdg_output_manager, output->wl_output); zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output); if (wl_display_roundtrip(app->wl_display) < 1) { wob_log_error("wl_display_roundtrip failed"); exit(EXIT_FAILURE); } } } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { app->wlr_layer_shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1); } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { app->xdg_output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, 2); } } void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { struct wob *app = (struct wob *) data; struct wob_output *output, *output_tmp; wl_list_for_each_safe (output, output_tmp, &(app->wob_outputs), link) { if (output->wl_name == name) { wob_log_info("Output %s disconnected", output->name); wob_output_destroy(output); wl_list_remove(&output->link); free(output); return; } } } void wob_hide(struct wob *app) { struct wob_output *output; wl_list_for_each (output, &app->wob_outputs, link) { if (output->wob_surface == NULL) continue; wob_log_info("Hiding bar on output %s", output->name); wob_surface_destroy(output->wob_surface); free(output->wob_surface); output->wob_surface = NULL; } if (wl_display_roundtrip(app->wl_display) < 1) { wob_log_error("wl_display_roundtrip failed"); exit(EXIT_FAILURE); } } void wob_show(struct wob *app) { struct wob_output *output; wl_list_for_each (output, &app->wob_outputs, link) { if (output->wob_surface != NULL) continue; wob_log_info("Showing bar on output %s", output->name); output->wob_surface = wob_surface_create(app, output->wl_output); } if (wl_display_roundtrip(app->wl_display) < 1) { wob_log_error("wl_display_roundtrip failed"); exit(EXIT_FAILURE); } wl_list_for_each (output, &(app->wob_outputs), link) { wl_surface_attach(output->wob_surface->wl_surface, app->wl_buffer, 0, 0); wl_surface_damage(output->wob_surface->wl_surface, 0, 0, INT32_MAX, INT32_MAX); wl_surface_commit(output->wob_surface->wl_surface); } if (wl_display_roundtrip(app->wl_display) < 1) { wob_log_error("wl_display_roundtrip failed"); exit(EXIT_FAILURE); } } void wob_destroy(struct wob *app) { struct wob_output *output, *output_tmp; wl_list_for_each_safe (output, output_tmp, &app->wob_outputs, link) { wob_output_destroy(output); free(output); } wob_config_destroy(&app->wob_config); zwlr_layer_shell_v1_destroy(app->wlr_layer_shell); wl_registry_destroy(app->wl_registry); wl_buffer_destroy(app->wl_buffer); wl_compositor_destroy(app->wl_compositor); wl_shm_destroy(app->wl_shm); zxdg_output_manager_v1_destroy(app->xdg_output_manager); wl_display_disconnect(app->wl_display); } void wob_connect(struct wob *app) { const static struct wl_registry_listener wl_registry_listener = { .global = handle_global, .global_remove = handle_global_remove, }; app->wl_display = wl_display_connect(NULL); if (app->wl_display == NULL) { wob_log_error("wl_display_connect failed"); exit(EXIT_FAILURE); } app->wl_registry = wl_display_get_registry(app->wl_display); if (app->wl_registry == NULL) { wob_log_error("wl_display_get_registry failed"); exit(EXIT_FAILURE); } wl_registry_add_listener(app->wl_registry, &wl_registry_listener, app); wl_list_init(&app->wob_outputs); if (wl_display_roundtrip(app->wl_display) < 1) { wob_log_error("wl_display_roundtrip failed"); exit(EXIT_FAILURE); } if (app->wob_config.output_mode == WOB_OUTPUT_MODE_FOCUSED) { struct wob_output *output = calloc(1, sizeof(struct wob_output)); output->wl_output = NULL; output->app = app; output->wl_name = 0; output->xdg_output = NULL; output->name = strdup("focused"); wl_list_insert(&app->wob_outputs, &output->link); } struct wob_dimensions dimensions = app->wob_config.dimensions; struct wl_shm_pool *pool = wl_shm_create_pool(app->wl_shm, app->shmid, dimensions.height * dimensions.width * 4); if (pool == NULL) { wob_log_error("wl_shm_create_pool failed"); exit(EXIT_FAILURE); } app->wl_buffer = wl_shm_pool_create_buffer(pool, 0, dimensions.width, dimensions.height, dimensions.width * 4, WL_SHM_FORMAT_ARGB8888); wl_shm_pool_destroy(pool); if (app->wl_buffer == NULL) { wob_log_error("wl_shm_pool_create_buffer failed"); exit(EXIT_FAILURE); } } void wob_draw(uint32_t *argb, struct wob_colors colors, struct wob_dimensions dimensions, unsigned long percentage, unsigned long maximum) { uint32_t argb_bar_color = wob_color_to_argb(wob_color_premultiply_alpha(colors.value)); uint32_t argb_background_color = wob_color_to_argb(wob_color_premultiply_alpha(colors.background)); uint32_t argb_border_color = wob_color_to_argb(wob_color_premultiply_alpha(colors.border)); size_t offset_border_padding = dimensions.border_offset + dimensions.border_size + dimensions.bar_padding; size_t bar_width = dimensions.width - 2 * offset_border_padding; size_t bar_height = dimensions.height - 2 * offset_border_padding; size_t bar_colored_width = (bar_width * percentage) / maximum; for (size_t i = 0; i < dimensions.width * dimensions.height; ++i) { argb[i] = argb_background_color; } // create top and bottom line size_t i = dimensions.width * dimensions.border_offset; size_t k = dimensions.width * (dimensions.height - dimensions.border_offset - dimensions.border_size); for (size_t line = 0; line < dimensions.border_size; ++line) { i += dimensions.border_offset; k += dimensions.border_offset; for (size_t pixel = 0; pixel < dimensions.width - 2 * dimensions.border_offset; ++pixel) { argb[i++] = argb_border_color; argb[k++] = argb_border_color; } i += dimensions.border_offset; k += dimensions.border_offset; } // create left and right horizontal line i = dimensions.width * (dimensions.border_offset + dimensions.border_size); k = dimensions.width * (dimensions.border_offset + dimensions.border_size); for (size_t line = 0; line < dimensions.height - 2 * (dimensions.border_size + dimensions.border_offset); ++line) { i += dimensions.border_offset; k += dimensions.width - dimensions.border_offset - dimensions.border_size; for (size_t pixel = 0; pixel < dimensions.border_size; ++pixel) { argb[i++] = argb_border_color; argb[k++] = argb_border_color; } i += dimensions.width - dimensions.border_offset - dimensions.border_size; k += dimensions.border_offset; } // draw 1px horizontal line uint32_t *start, *end, *pixel; start = &argb[offset_border_padding * (dimensions.width + 1)]; end = start + bar_colored_width; for (pixel = start; pixel < end; ++pixel) { *pixel = argb_bar_color; } for (end = start + bar_width; pixel < end; ++pixel) { *pixel = argb_background_color; } // copy it to make full percentage bar uint32_t *source = &argb[offset_border_padding * dimensions.width]; uint32_t *destination = source + dimensions.width; end = &argb[dimensions.width * (bar_height + offset_border_padding)]; while (destination != end) { memcpy(destination, source, MIN(destination - source, end - destination) * sizeof(uint32_t)); destination += MIN(destination - source, end - destination); } } bool wob_pledge_enabled() { char *disable_pledge_env = getenv("WOB_DISABLE_PLEDGE"); if (disable_pledge_env != NULL && strcmp(disable_pledge_env, "0") != 0) { return false; } return true; } int main(int argc, char **argv) { wob_log_use_colors(isatty(STDERR_FILENO)); wob_log_level_warn(); // libc is doing fstat syscall to determine the optimal buffer size and that can be problematic to wob_pledge() // to solve this problem we can just pass the optimal buffer ourselves static char stdin_buffer[STDIN_BUFFER_LENGTH]; if (setvbuf(stdin, stdin_buffer, _IOFBF, sizeof(stdin_buffer)) != 0) { wob_log_error("Failed to set stdin buffer size to %zu", sizeof(stdin_buffer)); return EXIT_FAILURE; } setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); static struct option long_options[] = { {"config", required_argument, NULL, 'c'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {"verbose", no_argument, NULL, 'v'}, {0, 0, 0, 0}, }; const char *usage = "Usage: wob [options]\n" " -c, --config Specify a config file.\n" " -v, --verbose Increase verbosity of messages, defaults to errors and warnings only.\n" " -h, --help Show help message and quit.\n" " -V, --version Show the version number and quit.\n" "\n"; int c; int option_index = 0; char *wob_config_path = NULL; while ((c = getopt_long(argc, argv, "hvVc:", long_options, &option_index)) != -1) { switch (c) { case 'V': printf("wob version " WOB_VERSION "\n"); free(wob_config_path); return EXIT_SUCCESS; case 'h': printf("%s", usage); free(wob_config_path); return EXIT_SUCCESS; case 'v': wob_log_inc_verbosity(); break; case 'c': // fail if -c option is given multiple times if (wob_config_path != NULL) { free(wob_config_path); fprintf(stderr, "%s", usage); return EXIT_FAILURE; } free(wob_config_path); wob_config_path = strdup(optarg); break; default: fprintf(stderr, "%s", usage); free(wob_config_path); return EXIT_FAILURE; } } wob_log_info("wob version %s started", WOB_VERSION); struct wob app = {0}; if (wob_config_path == NULL) { wob_config_path = wob_config_default_path(); } wob_config_init(&app.wob_config); if (wob_config_path != NULL) { wob_log_info("Using configuration file at %s", wob_config_path); if (!wob_config_load(&app.wob_config, wob_config_path)) { wob_config_destroy(&app.wob_config); free(wob_config_path); return EXIT_FAILURE; } } wob_config_debug(&app.wob_config); free(wob_config_path); int shmid = wob_shm_create(); if (shmid < 0) { return EXIT_FAILURE; } app.shmid = shmid; uint32_t *argb = wob_shm_alloc(shmid, app.wob_config.dimensions.width * app.wob_config.dimensions.height * 4); if (argb == NULL) { return EXIT_FAILURE; } wob_connect(&app); if (app.wl_shm == NULL || app.wl_compositor == NULL || app.wlr_layer_shell == NULL) { wob_log_error("Wayland compositor doesn't support all required protocols"); return EXIT_FAILURE; } if (wob_pledge_enabled()) { if (!wob_pledge()) { return EXIT_FAILURE; } } struct wob_colors effective_colors; struct pollfd fds[2] = { { .fd = wl_display_get_fd(app.wl_display), .events = POLLIN, }, { .fd = STDIN_FILENO, .events = POLLIN, }, }; bool hidden = true; for (;;) { unsigned long percentage = 0; char input_buffer[INPUT_BUFFER_LENGTH] = {0}; char *fgets_rv; switch (poll(fds, 2, hidden ? -1 : app.wob_config.timeout_msec)) { case -1: wob_log_error("poll() failed: %s", strerror(errno)); return EXIT_FAILURE; case 0: if (!hidden) wob_hide(&app); hidden = true; break; default: if (fds[0].revents) { if (!(fds[0].revents & POLLIN)) { wob_log_error("WL_DISPLAY_FD unexpectedly closed, revents = %hd", fds[0].revents); return EXIT_FAILURE; } if (wl_display_dispatch(app.wl_display) == -1) { return EXIT_FAILURE; } } if (fds[1].revents) { if (!(fds[1].revents & POLLIN)) { wob_log_error("STDIN unexpectedly closed, revents = %hd", fds[1].revents); if (!hidden) wob_hide(&app); wob_destroy(&app); return EXIT_FAILURE; } fgets_rv = fgets(input_buffer, INPUT_BUFFER_LENGTH, stdin); if (feof(stdin)) { wob_log_info("Received EOF"); if (!hidden) wob_hide(&app); wob_destroy(&app); return EXIT_SUCCESS; } if (fgets_rv == NULL) { wob_log_error("fgets() failed: %s", strerror(errno)); if (!hidden) wob_hide(&app); wob_destroy(&app); return EXIT_FAILURE; } effective_colors = app.wob_config.colors; if (!wob_parse_input(input_buffer, &percentage, &effective_colors.background, &effective_colors.border, &effective_colors.value)) { wob_log_error("Received invalid input"); if (!hidden) wob_hide(&app); wob_destroy(&app); return EXIT_FAILURE; } wob_log_info( "Received input { value = %lu, bg = #%08jx, border = #%08jx, bar = #%08jx }", percentage, wob_color_to_rgba(effective_colors.background), wob_color_to_rgba(effective_colors.border), wob_color_to_rgba(effective_colors.value) ); if (percentage > app.wob_config.max) { effective_colors = app.wob_config.overflow_colors; switch (app.wob_config.overflow_mode) { case WOB_OVERFLOW_MODE_WRAP: percentage %= app.wob_config.max; break; case WOB_OVERFLOW_MODE_NOWRAP: percentage = app.wob_config.max; break; } } if (wl_list_empty(&app.wob_outputs) == 1) { wob_log_info("No output found to render wob on"); break; } wob_log_info( "Rendering { value = %lu, bg = #%08jx, border = #%08jx, bar = #%08jx }", percentage, wob_color_to_rgba(effective_colors.background), wob_color_to_rgba(effective_colors.border), wob_color_to_rgba(effective_colors.value) ); wob_draw(argb, effective_colors, app.wob_config.dimensions, percentage, app.wob_config.max); wob_show(&app); hidden = false; } } } } wob-0.14.2/parse.c0000644000175100001710000000174414336511601013210 0ustar runnerdocker#define WOB_FILE "parse.c" #include #include #include "parse.h" bool wob_parse_input(const char *input_buffer, unsigned long *percentage, struct wob_color *background_color, struct wob_color *border_color, struct wob_color *bar_color) { char *input_ptr, *newline_position, *str_end; newline_position = strchr(input_buffer, '\n'); if (newline_position == NULL) { return false; } if (newline_position == input_buffer) { return false; } *percentage = strtoul(input_buffer, &input_ptr, 10); if (input_ptr == newline_position) { return true; } struct wob_color *colors_to_parse[3] = { background_color, border_color, bar_color, }; for (size_t i = 0; i < sizeof(colors_to_parse) / sizeof(struct wob_color *); ++i) { if (input_ptr[0] != ' ') { return false; } input_ptr += 1; if (!wob_color_from_string(input_ptr, &str_end, colors_to_parse[i])) { return false; } input_ptr = str_end; } return input_ptr == newline_position; } wob-0.14.2/pledge.c0000644000175100001710000000013214336511601013324 0ustar runnerdocker#define WOB_FILE "pledge.c" #include "pledge.h" bool wob_pledge(void) { return true; } wob-0.14.2/log.c0000644000175100001710000000456314336511601012661 0ustar runnerdocker#define WOB_FILE "log.c" #define _POSIX_C_SOURCE 199506L #define COLOR_RESET "\x1B[0m" #define COLOR_WHITE "\x1B[1;37m" #define COLOR_BLACK "\x1B[0;30m" #define COLOR_BLUE "\x1B[0;34m" #define COLOR_LIGHT_BLUE "\x1B[1;34m" #define COLOR_GREEN "\x1B[0;32m" #define COLOR_LIGHT_GREEN "\x1B[1;32m" #define COLOR_CYAN "\x1B[0;36m" #define COLOR_LIGHT_CYAN "\x1B[1;36m" #define COLOR_RED "\x1B[0;31m" #define COLOR_LIGHT_RED "\x1B[1;31m" #define COLOR_PURPLE "\x1B[0;35m" #define COLOR_LIGHT_PURPLE "\x1B[1;35m" #define COLOR_BROWN "\x1B[0;33m" #define COLOR_YELLOW "\x1B[1;33m" #define COLOR_GRAY "\x1B[0;30m" #define COLOR_LIGHT_GRAY "\x1B[0;37m" #include #include #include #include #include #include #include #include #include #include "log.h" static wob_log_importance min_importance_to_log = WOB_LOG_WARN; static bool use_colors = false; static const char *verbosity_names[] = { "DEBUG", "INFO", "WARN", "ERROR", }; static const char *verbosity_colors[] = { COLOR_LIGHT_CYAN, COLOR_GREEN, COLOR_YELLOW, COLOR_LIGHT_RED, }; void wob_log(const wob_log_importance importance, const char *file, const int line, const char *fmt, ...) { if (importance < min_importance_to_log) { return; } struct timespec ts; if (clock_gettime(CLOCK_REALTIME, &ts) != 0) { fprintf(stderr, "clock_gettime() failed: %s\n", strerror(errno)); ts.tv_sec = 0; ts.tv_nsec = 0; } // formatting time via localtime() requires open syscall (to read /etc/localtime) // and that is problematic with seccomp rules in place if (use_colors) { fprintf( stderr, "%jd.%06ld %s%-5s%s %s%s:%d:%s ", (intmax_t) ts.tv_sec, ts.tv_nsec / 1000, verbosity_colors[importance], verbosity_names[importance], COLOR_RESET, COLOR_LIGHT_GRAY, file, line, COLOR_RESET ); } else { fprintf(stderr, "%jd.%06ld %s %s:%d: ", (intmax_t) ts.tv_sec, ts.tv_nsec / 1000, verbosity_names[importance], file, line); } va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "\n"); } void wob_log_set_level(const wob_log_importance importance) { min_importance_to_log = importance; } void wob_log_use_colors(const bool colors) { use_colors = colors; } void wob_log_inc_verbosity(void) { if (min_importance_to_log != WOB_LOG_DEBUG) { min_importance_to_log -= 1; } } wob-0.14.2/config.c0000644000175100001710000002615114336511601013342 0ustar runnerdocker#define WOB_FILE "config.c" #define MIN_PERCENTAGE_BAR_WIDTH 1 #define MIN_PERCENTAGE_BAR_HEIGHT 1 #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include "config.h" bool parse_output_mode(const char *str, enum wob_output_mode *value) { if (strcmp(str, "whitelist") == 0) { *value = WOB_OUTPUT_MODE_WHITELIST; return true; } if (strcmp(str, "all") == 0) { *value = WOB_OUTPUT_MODE_ALL; return true; } if (strcmp(str, "focused") == 0) { *value = WOB_OUTPUT_MODE_FOCUSED; return true; } return false; } bool parse_overflow_mode(const char *str, enum wob_overflow_mode *value) { if (strcmp(str, "wrap") == 0) { *value = WOB_OVERFLOW_MODE_WRAP; return true; } if (strcmp(str, "nowrap") == 0) { *value = WOB_OVERFLOW_MODE_NOWRAP; return true; } return false; } bool parse_number(const char *str, unsigned long *value) { char *str_end; unsigned long ul = strtoul(str, &str_end, 10); if (*str_end != '\0') { return false; } *value = ul; return true; } bool parse_anchor(const char *str, unsigned long *anchor) { char str_dup[INI_MAX_LINE + 1] = {0}; strncpy(str_dup, str, INI_MAX_LINE); char *token = strtok(str_dup, " "); *anchor = WOB_ANCHOR_CENTER; while (token) { if (strcmp(token, "left") == 0) { *anchor |= WOB_ANCHOR_LEFT; } else if (strcmp(token, "right") == 0) { *anchor |= WOB_ANCHOR_RIGHT; } else if (strcmp(token, "top") == 0) { *anchor |= WOB_ANCHOR_TOP; } else if (strcmp(token, "bottom") == 0) { *anchor |= WOB_ANCHOR_BOTTOM; } else if (strcmp(token, "center") != 0) { return false; } token = strtok(NULL, " "); } return true; } bool parse_color(const char *str, struct wob_color *color) { char *str_end; if (wob_color_from_string(str, &str_end, color) == false || *str_end != '\0') { return false; } return true; } int handler(void *user, const char *section, const char *name, const char *value) { struct wob_config *config = (struct wob_config *) user; unsigned long ul; struct wob_color color; if (strcmp(section, "") == 0) { if (strcmp(name, "max") == 0) { if (parse_number(value, &ul) == false || ul < 1 || ul > 10000) { wob_log_error("Maximum must be a value between 1 and %lu.", 10000); return 0; } config->max = ul; return 1; } if (strcmp(name, "timeout") == 0) { if (parse_number(value, &ul) == false || ul < 1 || ul > 10000) { wob_log_error("Timeout must be a value between 1 and %lu.", 10000); return 0; } config->timeout_msec = ul; return 1; } if (strcmp(name, "width") == 0) { if (parse_number(value, &ul) == false) { wob_log_error("Width must be a positive value."); return 0; } config->dimensions.width = ul; return 1; } if (strcmp(name, "height") == 0) { if (parse_number(value, &ul) == false) { wob_log_error("Height must be a positive value."); return 0; } config->dimensions.height = ul; return 1; } if (strcmp(name, "border_offset") == 0) { if (parse_number(value, &ul) == false) { wob_log_error("Border offset must be a positive value."); return 0; } config->dimensions.border_offset = ul; return 1; } if (strcmp(name, "border_size") == 0) { if (parse_number(value, &ul) == false) { wob_log_error("Border size must be a positive value."); return 0; } config->dimensions.border_size = ul; return 1; } if (strcmp(name, "bar_padding") == 0) { if (parse_number(value, &ul) == false) { wob_log_error("Bar padding must be a positive value."); return 0; } config->dimensions.bar_padding = ul; return 1; } if (strcmp(name, "margin") == 0) { if (parse_number(value, &ul) == false) { wob_log_error("Anchor margin must be a positive value."); return 0; } config->margin = ul; return 1; } if (strcmp(name, "anchor") == 0) { if (parse_anchor(value, &ul) == false) { wob_log_error("Anchor must be one of 'top', 'bottom', 'left', 'right', 'center'."); return 0; } config->anchor = ul; return 1; } if (strcmp(name, "background_color") == 0) { if (!parse_color(value, &color)) { wob_log_error("Background color must be in RRGGBB[AA] format"); return 0; } config->colors.background = color; return 1; } if (strcmp(name, "border_color") == 0) { if (!parse_color(value, &color)) { wob_log_error("Border color must be in RRGGBB[AA] format."); return 0; } config->colors.border = color; return 1; } if (strcmp(name, "bar_color") == 0) { if (!parse_color(value, &color)) { wob_log_error("Bar color must be in RRGGBB[AA] format."); return 0; } config->colors.value = color; return 1; } if (strcmp(name, "overflow_background_color") == 0) { if (!parse_color(value, &color)) { wob_log_error("Overflow background color must be in RRGGBB[AA] format."); return 0; } config->overflow_colors.background = color; return 1; } if (strcmp(name, "overflow_border_color") == 0) { if (!parse_color(value, &color)) { wob_log_error("Overflow border color must be in RRGGBB[AA] format."); return 0; } config->overflow_colors.border = color; return 1; } if (strcmp(name, "overflow_bar_color") == 0) { if (!parse_color(value, &color)) { wob_log_error("Overflow bar color must be in RRGGBB[AA] format."); return 0; } config->overflow_colors.value = color; return 1; } if (strcmp(name, "overflow_mode") == 0) { if (parse_overflow_mode(value, &config->overflow_mode) == false) { wob_log_error("Invalid argument for overflow-mode. Valid options are wrap and nowrap."); return 0; } return 1; } if (strcmp(name, "output_mode") == 0) { if (parse_output_mode(value, &config->output_mode) == false) { wob_log_error("Invalid argument for output_mode. Valid options are focused, whitelist and all."); return 0; } return 1; } wob_log_error("Unknown config key %s", name); return 0; } if (strncmp(section, "output.", sizeof("output.") - 1) == 0) { if (strcmp(name, "name") == 0) { struct wob_output_config *output_config = malloc(sizeof(struct wob_output_config)); output_config->name = strdup(value); wl_list_insert(&config->outputs, &output_config->link); return 1; } wob_log_error("Unknown config key %s", name); return 0; } wob_log_error("Unknown config section %s", section); return 0; } char * wob_config_default_path() { static const char *config_paths[] = { "$XDG_CONFIG_HOME/wob/wob.ini", "$HOME/.config/wob/wob.ini", }; for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { wob_log_debug("Looking for config file at %s", config_paths[i]); wordexp_t p; if (wordexp(config_paths[i], &p, WRDE_UNDEF) == 0) { char *path = strdup(p.we_wordv[0]); wordfree(&p); if (access(path, F_OK) == 0) { wob_log_info("Found configuration file at %s", config_paths[i]); return path; } wob_log_debug("Configuration file at %s not found", config_paths[i]); free(path); } } return NULL; } void wob_config_init(struct wob_config *config) { wl_list_init(&config->outputs); config->max = 100; config->timeout_msec = 1000; config->dimensions.width = 400; config->dimensions.height = 50; config->dimensions.border_offset = 4; config->dimensions.border_size = 4; config->dimensions.bar_padding = 4; config->margin = 0; config->anchor = WOB_ANCHOR_CENTER; config->overflow_mode = WOB_OVERFLOW_MODE_WRAP; config->output_mode = WOB_OUTPUT_MODE_FOCUSED; config->colors.background = (struct wob_color){.a = 1.0f, .r = 0.0f, .g = 0.0f, .b = 0.0f}; config->colors.value = (struct wob_color){.a = 1.0f, .r = 1.0f, .g = 1.0f, .b = 1.0f}; config->colors.border = (struct wob_color){.a = 1.0f, .r = 1.0f, .g = 1.0f, .b = 1.0f}; config->overflow_colors.background = (struct wob_color){.a = 1.0f, .r = 0.0f, .g = 0.0f, .b = 0.0f}; config->overflow_colors.value = (struct wob_color){.a = 1.0f, .r = 1.0f, .g = 0.0f, .b = 0.0f}; config->overflow_colors.border = (struct wob_color){.a = 1.0f, .r = 1.0f, .g = 1.0f, .b = 1.0f}; } bool wob_config_load(struct wob_config *config, const char *config_path) { int res = ini_parse(config_path, handler, config); if (res == -1 || res == -2) { wob_log_error("Failed to open config file %s", config_path); return false; } if (res != 0) { wob_log_error("Failed to parse config file %s, error at line %d", config_path, res); return false; } struct wob_dimensions dimensions = config->dimensions; if (dimensions.width < MIN_PERCENTAGE_BAR_WIDTH + 2 * (dimensions.border_offset + dimensions.border_size + dimensions.bar_padding)) { wob_log_error("Invalid geometry: width is too small for given parameters"); return false; } if (dimensions.height < MIN_PERCENTAGE_BAR_HEIGHT + 2 * (dimensions.border_offset + dimensions.border_size + dimensions.bar_padding)) { wob_log_error("Invalid geometry: height is too small for given parameters"); return false; } return true; } void wob_config_debug(struct wob_config *config) { wob_log_debug("config.max = %lu", config->max); wob_log_debug("config.timeout_msec = %lu", config->timeout_msec); wob_log_debug("config.dimensions.width = %lu", config->dimensions.width); wob_log_debug("config.dimensions.height = %lu", config->dimensions.height); wob_log_debug("config.dimensions.border_offset = %lu", config->dimensions.border_offset); wob_log_debug("config.dimensions.border_size = %lu", config->dimensions.border_size); wob_log_debug("config.dimensions.bar_padding = %lu", config->dimensions.bar_padding); wob_log_debug("config.margin = %lu", config->margin); wob_log_debug("config.anchor = %lu (top = %d, bottom = %d, left = %d, right = %d)", config->anchor, WOB_ANCHOR_TOP, WOB_ANCHOR_BOTTOM, WOB_ANCHOR_LEFT, WOB_ANCHOR_RIGHT); wob_log_debug("config.overflow_mode = %lu (wrap = %d, nowrap = %d)", config->overflow_mode, WOB_OVERFLOW_MODE_WRAP, WOB_OVERFLOW_MODE_NOWRAP); wob_log_debug("config.output_mode = %lu (whitelist = %d, all = %d, focused = %d)", config->output_mode, WOB_OUTPUT_MODE_WHITELIST, WOB_OUTPUT_MODE_ALL, WOB_OUTPUT_MODE_FOCUSED); wob_log_debug("config.colors.background = #%08jx", (uintmax_t) wob_color_to_rgba(config->colors.background)); wob_log_debug("config.colors.value = #%08jx", (uintmax_t) wob_color_to_rgba(config->colors.value)); wob_log_debug("config.colors.border = #%08jx", (uintmax_t) wob_color_to_rgba(config->colors.border)); wob_log_debug("config.overflow_colors.background = #%08jx", (uintmax_t) wob_color_to_rgba(config->overflow_colors.background)); wob_log_debug("config.overflow_colors.value = #%08jx", (uintmax_t) wob_color_to_rgba(config->overflow_colors.value)); wob_log_debug("config.overflow_colors.border = #%08jx", (uintmax_t) wob_color_to_rgba(config->overflow_colors.border)); struct wob_output_config *output_config; wl_list_for_each (output_config, &config->outputs, link) { wob_log_debug("config.output.%s.name = %s", output_config->name, output_config->name); } } void wob_config_destroy(struct wob_config *config) { struct wob_output_config *output, *output_tmp; wl_list_for_each_safe (output, output_tmp, &config->outputs, link) { free(output->name); free(output); } } wob-0.14.2/color.c0000644000175100001710000000321314336511601013205 0ustar runnerdocker#define WOB_FILE "color.c" #include #include #include #include #include #include "color.h" uint32_t wob_color_to_argb(const struct wob_color color) { uint8_t alpha = (uint8_t) (color.a * UINT8_MAX); uint8_t red = (uint8_t) (color.r * UINT8_MAX); uint8_t green = (uint8_t) (color.g * UINT8_MAX); uint8_t blue = (uint8_t) (color.b * UINT8_MAX); return (alpha << 24) + (red << 16) + (green << 8) + blue; } uint32_t wob_color_to_rgba(const struct wob_color color) { uint8_t alpha = (uint8_t) (color.a * UINT8_MAX); uint8_t red = (uint8_t) (color.r * UINT8_MAX); uint8_t green = (uint8_t) (color.g * UINT8_MAX); uint8_t blue = (uint8_t) (color.b * UINT8_MAX); return (red << 24) + (green << 16) + (blue << 8) + alpha; } struct wob_color wob_color_premultiply_alpha(const struct wob_color color) { struct wob_color premultiplied_color = { .a = color.a, .r = color.r * color.a, .g = color.g * color.a, .b = color.b * color.a, }; return premultiplied_color; } bool wob_color_from_string(const char *restrict str, char **restrict str_end, struct wob_color *color) { char buffer[3] = {0}; uint8_t parts[4]; parts[3] = 0xFF; int i; for (i = 0; i < 4; ++i) { strncpy(buffer, &str[i * 2], 2); if (!isxdigit(buffer[0]) || !isxdigit(buffer[1])) { break; } parts[i] = strtoul(buffer, NULL, 16); } if (i < 3) { return false; } if (str_end) { *str_end = ((char *) str) + i * 2; } *color = (struct wob_color){ .r = (float) parts[0] / UINT8_MAX, .g = (float) parts[1] / UINT8_MAX, .b = (float) parts[2] / UINT8_MAX, .a = (float) parts[3] / UINT8_MAX, }; return true; } wob-0.14.2/meson_options.txt0000644000175100001710000000027214336511601015362 0ustar runnerdockeroption('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') option('seccomp', type: 'feature', value: 'auto', description: 'Use seccomp on Linux') wob-0.14.2/buffer.c0000644000175100001710000000216514336511601013345 0ustar runnerdocker#define WOB_FILE "buffer.c" #define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include "buffer.h" #include "log.h" int wob_shm_create() { int shmid = -1; char shm_name[NAME_MAX]; for (int i = 0; i < UCHAR_MAX; ++i) { if (snprintf(shm_name, NAME_MAX, "/wob-%d", i) >= NAME_MAX) { break; } shmid = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, 0600); if (shmid > 0 || errno != EEXIST) { break; } } if (shmid < 0) { wob_log_error("shm_open() failed: %s", strerror(errno)); return -1; } if (shm_unlink(shm_name) != 0) { wob_log_error("shm_unlink() failed: %s", strerror(errno)); return -1; } return shmid; } void * wob_shm_alloc(const int shmid, const size_t size) { if (ftruncate(shmid, size) != 0) { wob_log_error("ftruncate() failed: %s", strerror(errno)); return NULL; } void *buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, shmid, 0); if (buffer == MAP_FAILED) { wob_log_error("mmap() failed: %s", strerror(errno)); return NULL; } return buffer; } wob-0.14.2/protocols/0000755000175100001710000000000014336511601013750 5ustar runnerdockerwob-0.14.2/protocols/wlr-layer-shell-unstable-v1.xml0000644000175100001710000003325114336511601021660 0ustar runnerdocker Copyright © 2017 Drew DeVault Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holders not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The copyright holders make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Clients can use this interface to assign the surface_layer role to wl_surfaces. Such surfaces are assigned to a "layer" of the output and rendered with a defined z-depth respective to each other. They may also be anchored to the edges and corners of a screen and specify input handling semantics. This interface should be suitable for the implementation of many desktop shell components, and a broad number of other applications that interact with the desktop. Create a layer surface for an existing surface. This assigns the role of layer_surface, or raises a protocol error if another role is already assigned. Creating a layer surface from a wl_surface which has a buffer attached or committed is a client error, and any attempts by a client to attach or manipulate a buffer prior to the first layer_surface.configure call must also be treated as errors. You may pass NULL for output to allow the compositor to decide which output to use. Generally this will be the one that the user most recently interacted with. Clients can specify a namespace that defines the purpose of the layer surface. These values indicate which layers a surface can be rendered in. They are ordered by z depth, bottom-most first. Traditional shell surfaces will typically be rendered between the bottom and top layers. Fullscreen shell surfaces are typically rendered at the top layer. Multiple surfaces can share a single layer, and ordering within a single layer is undefined. An interface that may be implemented by a wl_surface, for surfaces that are designed to be rendered as a layer of a stacked desktop-like environment. Layer surface state (layer, size, anchor, exclusive zone, margin, interactivity) is double-buffered, and will be applied at the time wl_surface.commit of the corresponding wl_surface is called. Sets the size of the surface in surface-local coordinates. The compositor will display the surface centered with respect to its anchors. If you pass 0 for either value, the compositor will assign it and inform you of the assignment in the configure event. You must set your anchor to opposite edges in the dimensions you omit; not doing so is a protocol error. Both values are 0 by default. Size is double-buffered, see wl_surface.commit. Requests that the compositor anchor the surface to the specified edges and corners. If two orthogonal edges are specified (e.g. 'top' and 'left'), then the anchor point will be the intersection of the edges (e.g. the top left corner of the output); otherwise the anchor point will be centered on that edge, or in the center if none is specified. Anchor is double-buffered, see wl_surface.commit. Requests that the compositor avoids occluding an area with other surfaces. The compositor's use of this information is implementation-dependent - do not assume that this region will not actually be occluded. A positive value is only meaningful if the surface is anchored to one edge or an edge and both perpendicular edges. If the surface is not anchored, anchored to only two perpendicular edges (a corner), anchored to only two parallel edges or anchored to all edges, a positive value will be treated the same as zero. A positive zone is the distance from the edge in surface-local coordinates to consider exclusive. Surfaces that do not wish to have an exclusive zone may instead specify how they should interact with surfaces that do. If set to zero, the surface indicates that it would like to be moved to avoid occluding surfaces with a positive exclusive zone. If set to -1, the surface indicates that it would not like to be moved to accommodate for other surfaces, and the compositor should extend it all the way to the edges it is anchored to. For example, a panel might set its exclusive zone to 10, so that maximized shell surfaces are not shown on top of it. A notification might set its exclusive zone to 0, so that it is moved to avoid occluding the panel, but shell surfaces are shown underneath it. A wallpaper or lock screen might set their exclusive zone to -1, so that they stretch below or over the panel. The default value is 0. Exclusive zone is double-buffered, see wl_surface.commit. Requests that the surface be placed some distance away from the anchor point on the output, in surface-local coordinates. Setting this value for edges you are not anchored to has no effect. The exclusive zone includes the margin. Margin is double-buffered, see wl_surface.commit. Set to 1 to request that the seat send keyboard events to this layer surface. For layers below the shell surface layer, the seat will use normal focus semantics. For layers above the shell surface layers, the seat will always give exclusive keyboard focus to the top-most layer which has keyboard interactivity set to true. Layer surfaces receive pointer, touch, and tablet events normally. If you do not want to receive them, set the input region on your surface to an empty region. Events is double-buffered, see wl_surface.commit. This assigns an xdg_popup's parent to this layer_surface. This popup should have been created via xdg_surface::get_popup with the parent set to NULL, and this request must be invoked before committing the popup's initial state. See the documentation of xdg_popup for more details about what an xdg_popup is and how it is used. When a configure event is received, if a client commits the surface in response to the configure event, then the client must make an ack_configure request sometime before the commit request, passing along the serial of the configure event. If the client receives multiple configure events before it can respond to one, it only has to ack the last configure event. A client is not required to commit immediately after sending an ack_configure request - it may even ack_configure several times before its next surface commit. A client may send multiple ack_configure requests before committing, but only the last request sent before a commit indicates which configure event the client really is responding to. This request destroys the layer surface. The configure event asks the client to resize its surface. Clients should arrange their surface for the new states, and then send an ack_configure request with the serial sent in this configure event at some point before committing the new surface. The client is free to dismiss all but the last configure event it received. The width and height arguments specify the size of the window in surface-local coordinates. The size is a hint, in the sense that the client is free to ignore it if it doesn't resize, pick a smaller size (to satisfy aspect ratio or resize in steps of NxM pixels). If the client picks a smaller size and is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the surface will be centered on this axis. If the width or height arguments are zero, it means the client should decide its own window dimension. The closed event is sent by the compositor when the surface will no longer be shown. The output may have been destroyed or the user may have asked for it to be removed. Further changes to the surface will be ignored. The client should destroy the resource after receiving this event, and create a new surface if they so choose. Change the layer that the surface is rendered on. Layer is double-buffered, see wl_surface.commit. wob-0.14.2/pledge_seccomp.c0000644000175100001710000000276014336511601015046 0ustar runnerdocker#define WOB_FILE "pledge_seccomp.c" #include #include #include #include #include #include #include #include "log.h" #include "pledge.h" bool wob_pledge(void) { const int scmp_sc[] = { SCMP_SYS(clock_gettime), SCMP_SYS(close), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(fcntl), SCMP_SYS(gettimeofday), SCMP_SYS(_llseek), SCMP_SYS(lseek), SCMP_SYS(mprotect), SCMP_SYS(munmap), SCMP_SYS(poll), SCMP_SYS(ppoll), SCMP_SYS(read), SCMP_SYS(readv), SCMP_SYS(recvmsg), SCMP_SYS(restart_syscall), SCMP_SYS(sendmsg), SCMP_SYS(write), SCMP_SYS(writev), }; int ret; scmp_filter_ctx scmp_ctx = seccomp_init(SCMP_ACT_KILL); if (scmp_ctx == NULL) { wob_log_error("seccomp_init(SCMP_ACT_KILL) failed"); return false; } for (size_t i = 0; i < sizeof(scmp_sc) / sizeof(int); ++i) { wob_log_debug("Adding syscall %d to whitelist", scmp_sc[i]); if ((ret = seccomp_rule_add(scmp_ctx, SCMP_ACT_ALLOW, scmp_sc[i], 0)) < 0) { wob_log_error("seccomp_rule_add(scmp_ctxm, SCMP_ACT_ALLOW, %d) failed with return value %d", scmp_sc[i], ret); seccomp_release(scmp_ctx); return false; } } if ((ret = seccomp_load(scmp_ctx)) < 0) { wob_log_error("seccomp_load(scmp_ctx) failed with return value %d", ret); seccomp_release(scmp_ctx); return false; } wob_log_debug("Seccomp syscall whitelist successfully installed"); seccomp_release(scmp_ctx); return true; } wob-0.14.2/wob.1.scd0000644000175100001710000000210714336511601013345 0ustar runnerdockerwob(1) # NAME wob - Wayland Overlay Bar # DESCRIPTION wob is a lightweight overlay volume/backlight/progress/anything bar for Wayland. # SYNOPSIS *wob* [options...] # OPTIONS *-c, --config * Configuration file location. *-v, --verbose* Increase verbosity of messages, defaults to errors and warnings only. *-V, --version* Show the version number and quit. *-h, --help* Show help message and quit. # USAGE wob reads values to display from standard input in the following formats: or Where is number in interval from 0 to *max* and <\*color> is color in RRGGBB[AA] format. # CONFIGURATION wob searches for a config file in the following locations, in this order: 1. $XDG_CONFIG_HOME/wob/wob.ini 2. ~/.config/wob/wob.ini For information on the config file format, see *wob.ini*(5). # ENVIRONMENT The following environment variables have an effect on wob: *WOB_DISABLE_PLEDGE* Disable seccomp syscall filtering on Linux. Set this if you are having trouble running wob with tools like valgrind. wob-0.14.2/tests/0000755000175100001710000000000014336511601013066 5ustar runnerdockerwob-0.14.2/tests/wob_parse_input.c0000644000175100001710000000344214336511601016435 0ustar runnerdocker#include #include #include #include "parse.h" int main(int argc, char **argv) { unsigned long percentage; struct wob_color background = {0}; struct wob_color border = {0}; struct wob_color bar = {0}; char *input; bool result; printf("running 1\n"); input = "25 000000FF FFFFFFFF FFFFFFFF\n"; result = wob_parse_input(input, &percentage, &background, &border, &bar); if (!result || percentage != 25 || wob_color_to_argb(background) != 0xFF000000 || wob_color_to_argb(border) != 0xFFFFFFFF || wob_color_to_argb(bar) != 0xFFFFFFFF) { return EXIT_FAILURE; } printf("running 2\n"); input = "25 000000FF\n"; result = wob_parse_input(input, &percentage, &background, &border, &bar); if (result) { return EXIT_FAILURE; } printf("running 3\n"); input = "25\n"; result = wob_parse_input(input, &percentage, &background, &border, &bar); if (!result || percentage != 25) { return EXIT_FAILURE; } printf("running 4\n"); input = "25 000000FF FFFFFFFF FFFFFFFF \n"; result = wob_parse_input(input, &percentage, &background, &border, &bar); if (result) { return EXIT_FAILURE; } printf("running 5\n"); input = "25 000000FF 16a085FF FF0000FF\n"; result = wob_parse_input(input, &percentage, &background, &border, &bar); if (!result || percentage != 25 || wob_color_to_argb(background) != 0xFF000000 || wob_color_to_argb(border) != 0xFF16a085 || wob_color_to_argb(bar) != 0xFFFF0000) { return EXIT_FAILURE; } printf("running 1\n"); input = "25 000000 FFFFFFFF FFFFFF\n"; result = wob_parse_input(input, &percentage, &background, &border, &bar); if (!result || percentage != 25 || wob_color_to_argb(background) != 0xFF000000 || wob_color_to_argb(border) != 0xFFFFFFFF || wob_color_to_argb(bar) != 0xFFFFFFFF) { return EXIT_FAILURE; } return EXIT_SUCCESS; }