diff --git a/.editorconfig b/.editorconfig index de29994..79d8340 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,7 +8,6 @@ trim_trailing_whitespace = true [*.{c,h,txt}] indent_style = tab -indent_size = 4 [*.{xml,yml}] indent_style = space diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1441429..9cfee0c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,21 +9,21 @@ on: jobs: test: runs-on: ubuntu-latest - container: alpine:3.17 + container: alpine:3.18 steps: - uses: actions/checkout@v2 - name: Install dependencies - run: apk update && apk add meson wayland-dev musl-dev wayland-protocols gcc inih-dev pixman-dev cmocka-dev + run: apk update && apk add meson wayland-dev musl-dev wayland-protocols gcc inih-dev cmocka-dev - name: Run tests run: meson setup test && ninja -C test test analyze: runs-on: ubuntu-latest - container: alpine:3.17 + container: alpine:3.18 steps: - uses: actions/checkout@v2 - name: Install dependencies - run: apk update && apk add meson wayland-dev musl-dev wayland-protocols gcc inih-dev pixman-dev cmocka-dev clang15 clang15-extra-tools clang15-analyzer + run: apk update && apk add meson wayland-dev musl-dev wayland-protocols gcc inih-dev cmocka-dev clang16 clang16-extra-tools clang16-analyzer - name: Run clang-analyzer if: success() || failure() run: scan-build meson setup clang-analyzer && scan-build --status-bugs ninja -C clang-analyzer @@ -33,10 +33,10 @@ jobs: format: runs-on: ubuntu-latest - container: alpine:3.17 + container: alpine:3.18 steps: - uses: actions/checkout@v2 - name: Install dependencies - run: apk update && apk add clang15-extra-tools + run: apk update && apk add clang16-extra-tools - name: Run clang-format run: clang-format -Werror --dry-run src/* test/* diff --git a/meson.build b/meson.build index 0a8007b..89eb3e2 100644 --- a/meson.build +++ b/meson.build @@ -9,7 +9,8 @@ project( cc = meson.get_compiler('c') -wayland_protos = dependency('wayland-protocols', version: '>=1.13') +# minimal version with fractional scaling protocol +wayland_protos = dependency('wayland-protocols', version: '>=1.31') wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') wayland_scanner = find_program('wayland-scanner') wayland_client = dependency('wayland-client') @@ -32,34 +33,31 @@ wayland_scanner_client = generator( arguments: ['client-header', '@INPUT@', '@OUTPUT@'], ) -client_protocols = [ - [wl_protocol_dir + '/stable/xdg-shell', 'xdg-shell.xml'], - [meson.project_source_root() + '/protocols', 'wlr-layer-shell-unstable-v1.xml'], +wl_proto_xml = [ + wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', + wl_protocol_dir / 'stable/viewporter/viewporter.xml', + 'protocols/wlr-layer-shell-unstable-v1.xml', + wl_protocol_dir / 'staging/fractional-scale/fractional-scale-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) +wl_proto_headers = [] +wl_proto_src = [] +foreach proto : wl_proto_xml + wl_proto_headers += custom_target( + proto.underscorify() + '_client_header', + output: '@BASENAME@.h', + input: proto, + command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@']) + + wl_proto_src += custom_target( + proto.underscorify() + '_private_code', + output: '@BASENAME@.c', + input: proto, + command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@']) endforeach -wob_sources = ['src/main.c', 'src/image.c', 'src/log.c', 'src/color.c', 'src/config.c', 'src/wob.c'] -wob_dependencies = [wayland_client, wlr_layer_shell_unstable_v1, xdg_shell, rt, inih] +wob_sources = ['src/main.c', 'src/image.c', 'src/log.c', 'src/color.c', 'src/config.c', 'src/wob.c', 'src/shm.c', wl_proto_src, wl_proto_headers] +wob_dependencies = [wayland_client, rt, inih] if seccomp.found() wob_dependencies += seccomp wob_sources += 'src/pledge_seccomp.c' diff --git a/protocols/wlr-layer-shell-unstable-v1.xml b/protocols/wlr-layer-shell-unstable-v1.xml index adc6a17..d06ca6f 100644 --- a/protocols/wlr-layer-shell-unstable-v1.xml +++ b/protocols/wlr-layer-shell-unstable-v1.xml @@ -29,7 +29,7 @@ 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 + surface 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 @@ -42,9 +42,9 @@ 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 + Creating a layer surface from a wl_surface which has a shm_data 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 + or manipulate a shm_data 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 @@ -64,15 +64,15 @@ - + - These values indicate which layers a surface can be rendered in. They + These values indicate which layers a surface can be surface 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. + will typically be surface between the bottom and top layers. + Fullscreen shell surfaces are typically surface at the top layer. Multiple surfaces can share a single layer, and ordering within a single layer is undefined. @@ -87,7 +87,7 @@ 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 + are designed to be surface as a layer of a stacked desktop-like environment. Layer surface state (layer, size, anchor, exclusive zone, @@ -291,7 +291,7 @@ - Change the layer that the surface is rendered on. + Change the layer that the surface is surface on. Layer is double-buffered, see wl_surface.commit. diff --git a/src/color.c b/src/color.c index de14790..d823906 100644 --- a/src/color.c +++ b/src/color.c @@ -74,6 +74,7 @@ wob_color_from_rgba_string(const char *str, struct wob_color *color) switch (length) { case 8: parts[3] = hex_to_int(str[6]) * 16 + hex_to_int(str[7]); + // fallthrough case 6: parts[0] = hex_to_int(str[0]) * 16 + hex_to_int(str[1]); parts[1] = hex_to_int(str[2]) * 16 + hex_to_int(str[3]); diff --git a/src/config.c b/src/config.c index e56a625..32d0373 100644 --- a/src/config.c +++ b/src/config.c @@ -318,14 +318,52 @@ handler(void *user, const char *section, const char *name, const char *value) struct wob_output_config *output_config = wob_config_find_output(config, output_id); if (output_config == NULL) { - output_config = malloc(sizeof(struct wob_output_config)); + output_config = calloc(1, sizeof(struct wob_output_config)); + if (output_config == NULL) { + wob_log_panic("calloc() failed"); + } + output_config->id = strdup(output_id); + output_config->match = NULL; + output_config->dimensions = config->dimensions; + output_config->margin = config->margin; + output_config->anchor = config->anchor; wl_list_insert(&config->outputs, &output_config->link); } - if (strcmp(name, "name") == 0) { - output_config->name = strdup(value); - + if (strcmp(name, "match") == 0) { + output_config->match = strdup(value); + return 1; + } + if (strcmp(name, "width") == 0) { + if (parse_number(value, &ul) == false) { + wob_log_error("Width must be a positive value."); + return 0; + } + output_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; + } + output_config->dimensions.height = ul; + return 1; + } + if (strcmp(name, "margin") == 0) { + if (parse_margin(value, &output_config->margin) == false) { + wob_log_error("Margin must be in format or ."); + return 0; + } + 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; + } + output_config->anchor = ul; return 1; } @@ -339,7 +377,11 @@ handler(void *user, const char *section, const char *name, const char *value) struct wob_style *style = wob_config_find_style(config, style_name); if (style == NULL) { - style = malloc(sizeof(struct wob_style)); + style = calloc(1, sizeof(struct wob_style)); + if (style == NULL) { + wob_log_panic("calloc() failed"); + } + style->name = strdup(style_name); style->colors = config->default_style.colors; style->overflow_colors = config->default_style.overflow_colors; @@ -486,6 +528,14 @@ wob_config_load(struct wob_config *config, const char *config_path) return false; } + struct wob_output_config *output; + wl_list_for_each (output, &config->outputs, link) { + if (output->match == NULL) { + wob_log_error("Output %s is missing \"match\" property", output->id); + return false; + } + } + return true; } @@ -527,7 +577,23 @@ wob_config_debug(struct wob_config *config) 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->id, output_config->name); + wob_log_debug("config.output.%s.match = %s", output_config->id, output_config->match); + wob_log_debug("config.output.%s.margin.top = %lu", output_config->id, output_config->margin.top); + wob_log_debug("config.output.%s.margin.right = %lu", output_config->id, output_config->margin.right); + wob_log_debug("config.output.%s.margin.bottom = %lu", output_config->id, output_config->margin.bottom); + wob_log_debug("config.output.%s.margin.left = %lu", output_config->id, output_config->margin.left); + wob_log_debug("config.output.%s.dimensions.width = %lu", output_config->id, output_config->dimensions.width); + wob_log_debug("config.output.%s.dimensions.height = %lu", output_config->id, output_config->dimensions.height); + wob_log_debug("config.output.%s.dimensions.border_offset = %lu", output_config->id, output_config->dimensions.border_offset); + wob_log_debug("config.output.%s.dimensions.border_size = %lu", output_config->id, output_config->dimensions.border_size); + wob_log_debug("config.output.%s.dimensions.bar_padding = %lu", output_config->id, output_config->dimensions.bar_padding); + wob_log_debug( + "config.output.%s.dimensions.orientation = %lu (horizontal = %d, vertical = %d)", + output_config->id, + output_config->dimensions.orientation, + WOB_ORIENTATION_HORIZONTAL, + WOB_ORIENTATION_VERTICAL + ); } } @@ -536,7 +602,7 @@ 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->match); free(output->id); free(output); } @@ -591,12 +657,12 @@ wob_config_find_output(struct wob_config *config, const char *output_id) } struct wob_output_config * -wob_config_find_output_by_name(struct wob_config *config, const char *output_name) +wob_config_match_output(struct wob_config *config, const char *match) { struct wob_output_config *output_config = NULL; bool output_found = false; wl_list_for_each (output_config, &config->outputs, link) { - if (strcmp(output_config->name, output_name) == 0) { + if (strstr(output_config->match, match) == 0) { output_found = true; break; } @@ -609,3 +675,48 @@ wob_config_find_output_by_name(struct wob_config *config, const char *output_nam return NULL; } } + +uint32_t +scale_apply(uint32_t base, uint32_t scale) +{ + return base * (scale / 120.); +} + +struct wob_dimensions +wob_dimensions_apply_scale(struct wob_dimensions dimensions, uint32_t scale) +{ + struct wob_dimensions scaled_dimensions = { + .width = scale_apply(dimensions.width, scale), + .height = scale_apply(dimensions.height, scale), + .bar_padding = scale_apply(dimensions.bar_padding, scale), + .border_offset = scale_apply(dimensions.border_offset, scale), + .border_size = scale_apply(dimensions.border_size, scale), + .orientation = dimensions.orientation, + }; + + return scaled_dimensions; +} + +bool +wob_dimensions_eq(struct wob_dimensions a, struct wob_dimensions b) +{ + if (a.height != b.height) return false; + if (a.width != b.width) return false; + if (a.orientation != b.orientation) return false; + if (a.border_offset != b.border_offset) return false; + if (a.border_size != b.border_size) return false; + if (a.bar_padding != b.bar_padding) return false; + + return true; +} + +bool +wob_margin_eq(struct wob_margin a, struct wob_margin b) +{ + if (a.top != b.top) return false; + if (a.right != b.right) return false; + if (a.bottom != b.bottom) return false; + if (a.left != b.left) return false; + + return true; +} diff --git a/src/config.h b/src/config.h index 02af20c..418e777 100644 --- a/src/config.h +++ b/src/config.h @@ -37,10 +37,22 @@ struct wob_margin { unsigned long left; }; +struct wob_dimensions { + unsigned long width; + unsigned long height; + unsigned long border_offset; + unsigned long border_size; + unsigned long bar_padding; + enum wob_orientation orientation; +}; + struct wob_output_config { char *id; - char *name; + char *match; struct wl_list link; + struct wob_dimensions dimensions; + struct wob_margin margin; + unsigned long anchor; }; struct wob_colors { @@ -56,15 +68,6 @@ struct wob_style { struct wl_list link; }; -struct wob_dimensions { - unsigned long width; - unsigned long height; - unsigned long border_offset; - unsigned long border_size; - unsigned long bar_padding; - enum wob_orientation orientation; -}; - struct wob_config { unsigned long max; unsigned long timeout_msec; @@ -93,6 +96,12 @@ struct wob_style *wob_config_find_style(struct wob_config *config, const char *s struct wob_output_config *wob_config_find_output(struct wob_config *config, const char *output_id); -struct wob_output_config *wob_config_find_output_by_name(struct wob_config *config, const char *output_name); +struct wob_output_config *wob_config_match_output(struct wob_config *config, const char *match); + +struct wob_dimensions wob_dimensions_apply_scale(struct wob_dimensions dimensions, uint32_t scale); + +bool wob_dimensions_eq(struct wob_dimensions a, struct wob_dimensions b); + +bool wob_margin_eq(struct wob_margin a, struct wob_margin b); #endif diff --git a/src/image.c b/src/image.c index 4b5dd29..ef0c1e8 100644 --- a/src/image.c +++ b/src/image.c @@ -1,72 +1,6 @@ #define WOB_FILE "buffer.c" -#define _POSIX_C_SOURCE 200112L - -#include -#include -#include -#include -#include -#include -#include -#include - #include "image.h" -#include "log.h" - -struct wob_image * -wob_image_create_argb8888(size_t width, size_t height) -{ - 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 NULL; - } - - if (shm_unlink(shm_name) != 0) { - wob_log_error("shm_unlink() failed: %s", strerror(errno)); - return NULL; - } - - size_t size = width * height * 8; - 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; - } - - struct wob_image *image = malloc(sizeof(struct wob_image)); - - image->shmid = shmid; - image->width = width; - image->height = height; - image->size_in_bytes = width * height * 4; - image->data = buffer; - - return image; -} - -void -wob_image_destroy(struct wob_image *image) -{ - free(image); -} void fill_rectangle(uint32_t *pixels, size_t width, size_t height, size_t stride, uint32_t color) @@ -80,7 +14,7 @@ fill_rectangle(uint32_t *pixels, size_t width, size_t height, size_t stride, uin } void -wob_image_draw(struct wob_image *image, struct wob_colors colors, struct wob_dimensions dimensions, unsigned long percentage, unsigned long maximum) +wob_image_draw(uint32_t *image_data, struct wob_dimensions dimensions, struct wob_colors colors, double percentage) { uint32_t bar_color = wob_color_to_argb(wob_color_premultiply_alpha(colors.value)); uint32_t background_color = wob_color_to_argb(wob_color_premultiply_alpha(colors.background)); @@ -90,23 +24,23 @@ wob_image_draw(struct wob_image *image, struct wob_colors colors, struct wob_dim uint32_t height; uint32_t width; uint32_t offset; - uint32_t stride = image->width; + uint32_t stride = dimensions.width; height = dimensions.height; width = dimensions.width; - data = image->data; + data = image_data; fill_rectangle(data, width, height, stride, background_color); offset = dimensions.border_offset; - height = image->height - (2 * offset); - width = image->width - (2 * offset); - data = image->data + (offset * (dimensions.width + 1)); + height = dimensions.height - (2 * offset); + width = dimensions.width - (2 * offset); + data = image_data + (offset * (dimensions.width + 1)); fill_rectangle(data, width, height, stride, border_color); offset = dimensions.border_offset + dimensions.border_size; - height = image->height - (2 * offset); - width = image->width - (2 * offset); - data = image->data + (offset * (dimensions.width + 1)); + height = dimensions.height - (2 * offset); + width = dimensions.width - (2 * offset); + data = image_data + (offset * (dimensions.width + 1)); fill_rectangle(data, width, height, stride, background_color); offset = dimensions.border_offset + dimensions.border_size + dimensions.bar_padding; @@ -115,14 +49,14 @@ wob_image_draw(struct wob_image *image, struct wob_colors colors, struct wob_dim switch (dimensions.orientation) { case WOB_ORIENTATION_HORIZONTAL: height = bar_height; - width = bar_width * percentage / maximum; - data = image->data + (offset * (dimensions.width + 1)); + width = bar_width * percentage; + data = image_data + (offset * (dimensions.width + 1)); fill_rectangle(data, width, height, stride, bar_color); break; case WOB_ORIENTATION_VERTICAL: - height = bar_height * percentage / maximum; + height = bar_height * percentage; width = bar_width; - data = image->data + (offset * (dimensions.width + 1)) + (bar_height - height) * dimensions.width; + data = image_data + (offset * (dimensions.width + 1)) + (bar_height - height) * dimensions.width; fill_rectangle(data, width, height, stride, bar_color); break; } diff --git a/src/image.h b/src/image.h index 481ccf5..7212c6c 100644 --- a/src/image.h +++ b/src/image.h @@ -6,18 +6,6 @@ #include "config.h" -struct wob_image { - int shmid; - size_t width; - size_t height; - size_t size_in_bytes; - uint32_t *data; -}; - -struct wob_image *wob_image_create_argb8888(size_t width, size_t height); - -void wob_image_destroy(struct wob_image *image); - -void wob_image_draw(struct wob_image *image, struct wob_colors colors, struct wob_dimensions dimensions, unsigned long percentage, unsigned long maximum); +void wob_image_draw(uint32_t *data, struct wob_dimensions dimensions, struct wob_colors colors, double percentage); #endif diff --git a/src/main.c b/src/main.c index 85bb947..3be49a2 100644 --- a/src/main.c +++ b/src/main.c @@ -28,6 +28,8 @@ main(int argc, char **argv) setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); + wob_log_info("wob version %s started with pid %d", WOB_VERSION, getpid()); + static struct option long_options[] = { {"config", required_argument, NULL, 'c'}, {"help", no_argument, NULL, 'h'}, @@ -100,6 +102,5 @@ main(int argc, char **argv) wob_config_debug(config); free(wob_config_path); - wob_log_info("wob version %s started", WOB_VERSION); return wob_run(config); } diff --git a/src/pledge_seccomp.c b/src/pledge_seccomp.c index 995e7f4..f1eeb80 100644 --- a/src/pledge_seccomp.c +++ b/src/pledge_seccomp.c @@ -22,6 +22,7 @@ wob_pledge(void) SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(fcntl), + SCMP_SYS(ftruncate), SCMP_SYS(gettimeofday), SCMP_SYS(_llseek), SCMP_SYS(lseek), diff --git a/src/shm.c b/src/shm.c new file mode 100644 index 0000000..46ee35f --- /dev/null +++ b/src/shm.c @@ -0,0 +1,59 @@ +#define WOB_FILE "shm.c" + +#define _POSIX_C_SOURCE 200112L + +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "shm.h" + +int +wob_shm_open() +{ + 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_allocate(int shmid, size_t size) +{ + if (ftruncate(shmid, size) != 0) { + wob_log_error("ftruncate(%d) failed: %s", shmid, 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; +} diff --git a/src/shm.h b/src/shm.h new file mode 100644 index 0000000..654c2fe --- /dev/null +++ b/src/shm.h @@ -0,0 +1,11 @@ +#ifndef _WOB_SHM_H +#define _WOB_SHM_H + +#include +#include + +int wob_shm_open(); + +void *wob_shm_allocate(int shmid, size_t size); + +#endif diff --git a/src/wob.c b/src/wob.c index 6d98043..d51b3ac 100644 --- a/src/wob.c +++ b/src/wob.c @@ -8,39 +8,65 @@ #include #include #include +#include #include +#include +#include "fractional-scale-v1.h" #include "image.h" #include "log.h" #include "pledge.h" -#include "wlr-layer-shell-unstable-v1-client-protocol.h" +#include "shm.h" +#include "viewporter.h" +#include "wlr-layer-shell-unstable-v1.h" #include "wob.h" +struct wob_buffer { + struct wl_buffer *wl_buffer; + struct wob_dimensions dimensions; + uint32_t *shm_data; +}; + struct wob_surface { struct zwlr_layer_surface_v1 *wlr_layer_surface; struct wl_surface *wl_surface; + struct wp_fractional_scale_v1 *fractional; + struct wp_viewport *wp_viewport; + + struct wob_dimensions dimensions; + struct wob_margin margin; + enum wob_anchor anchor; + struct wob_buffer *wob_buffer; + uint32_t scale; + + // TODO move somewhere? + double desired_percentage; + struct wob_colors desired_colors; }; struct wob_output { char *name; + char *description; struct wl_list link; struct wl_output *wl_output; - struct wob_surface *wob_surface; uint32_t wl_name; - bool focused; }; struct wob { - struct wob_image *image; - 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 wob_config *config; + struct wob_surface *surface; + int shmid; +}; + +struct managers { + struct wl_compositor *wl_compositor; + struct wp_fractional_scale_manager_v1 *wp_fractional_scale; + struct zwlr_layer_shell_v1 *wlr_layer_shell; + struct wp_viewporter *wp_viewporter; + struct wl_shm *wl_shm; }; +static struct managers managers; void noop() @@ -48,21 +74,6 @@ noop() /* intentionally left blank */ } -bool -wob_should_render_on_output(struct wob *app, struct wob_output *output) -{ - switch (app->config->output_mode) { - case WOB_OUTPUT_MODE_FOCUSED: - return output->focused; - case WOB_OUTPUT_MODE_ALL: - return true; - case WOB_OUTPUT_MODE_WHITELIST: - return wob_config_find_output_by_name(app->config, output->name) != NULL; - } - - return false; -} - unsigned long wob_anchor_to_wlr_layer_surface_anchor(enum wob_anchor wob_anchor) { @@ -83,45 +94,243 @@ wob_anchor_to_wlr_layer_surface_anchor(enum wob_anchor wob_anchor) return wlr_layer_surface_anchor; } +static struct wl_callback_listener wl_surface_frame_listener; + +struct wob_buffer * +wob_buffer_create_argb8888(int shmid, struct wob_dimensions dimensions) +{ + size_t width = dimensions.width; + size_t height = dimensions.height; + size_t shm_size = width * height * 4; + + void *shm_data = wob_shm_allocate(shmid, shm_size); + if (shm_data == NULL) { + wob_log_panic("wob_shm_allocate() failed"); + } + + struct wl_shm_pool *pool = wl_shm_create_pool(managers.wl_shm, shmid, shm_size); + if (pool == NULL) { + wob_log_panic("wl_shm_create_pool failed"); + } + + struct wl_buffer *wl_buffer = wl_shm_pool_create_buffer(pool, 0, width, height, width * 4, WL_SHM_FORMAT_ARGB8888); + if (wl_buffer == NULL) { + wob_log_panic("wl_shm_pool_create_buffer failed"); + } + wl_shm_pool_destroy(pool); + + struct wob_buffer *wob_buffer = calloc(1, sizeof(struct wob_buffer)); + if (wob_buffer == NULL) { + wob_log_panic("calloc failed"); + } + + *wob_buffer = (struct wob_buffer){ + .wl_buffer = wl_buffer, + .dimensions = dimensions, + .shm_data = shm_data, + }; + + return wob_buffer; +} + +void +wob_buffer_destroy(struct wob_buffer *buffer) +{ + wl_buffer_destroy(buffer->wl_buffer); + if (buffer->shm_data != NULL) { + munmap(buffer->shm_data, buffer->dimensions.width * buffer->dimensions.height * 4); + } + free(buffer); +} + +void +layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *zwlr_surface, uint32_t serial, uint32_t w, uint32_t h) +{ + struct wob *state = (struct wob *) data; + + wob_log_debug("layer_surface_configure(%p, %u, %u, %u)", (void *) zwlr_surface, (uintmax_t) serial, w, h); + + zwlr_layer_surface_v1_ack_configure(zwlr_surface, serial); + + int shmid = state->shmid; + struct wob_surface *surface = state->surface; + if (surface == NULL) { + wob_log_panic("surface is NULL"); + } + + struct wob_dimensions scaled_dimensions = wob_dimensions_apply_scale(surface->dimensions, surface->scale); + if (surface->wob_buffer == NULL || !wob_dimensions_eq(surface->wob_buffer->dimensions, scaled_dimensions)) { + if (surface->wob_buffer != NULL) { + wob_buffer_destroy(surface->wob_buffer); + } + surface->wob_buffer = wob_buffer_create_argb8888(shmid, scaled_dimensions); + + // redraw only if we have dimensions set, otherwise keep the transparent pixel + if (surface->dimensions.height != 1 || surface->dimensions.width != 1) { + wob_image_draw(surface->wob_buffer->shm_data, surface->wob_buffer->dimensions, surface->desired_colors, surface->desired_percentage); + } + + if (surface->wp_viewport != NULL) { + wp_viewport_set_destination(surface->wp_viewport, surface->dimensions.width, surface->dimensions.height); + } + + wl_surface_attach(surface->wl_surface, surface->wob_buffer->wl_buffer, 0, 0); + wl_surface_damage_buffer(surface->wl_surface, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_commit(surface->wl_surface); + } +} + void -layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t w, uint32_t h) +layer_surface_enter(void *data, struct wl_surface *wl_surface, struct wl_output *entered_output) { - zwlr_layer_surface_v1_ack_configure(surface, serial); + wob_log_debug("layer_surface_enter(%x)", wl_surface); + struct wob *app = (struct wob *) data; + + struct wob_output *selected_output; + wl_list_for_each (selected_output, &app->wob_outputs, link) { + if (entered_output == selected_output->wl_output) { + break; + } + } + + // defaults + struct wob_margin margin = app->config->margin; + struct wob_dimensions dimensions = app->config->dimensions; + enum wob_anchor anchor = app->config->anchor; + + // output config + struct wob_output_config *output_config = wob_config_match_output(app->config, selected_output->name); + if (output_config == NULL) { + output_config = wob_config_match_output(app->config, selected_output->description); + } + + if (output_config != NULL) { + margin = output_config->margin; + dimensions = output_config->dimensions; + anchor = output_config->anchor; + } + + struct wob_surface *surface = app->surface; + if (!wob_dimensions_eq(surface->dimensions, dimensions) || !wob_margin_eq(margin, surface->margin) || anchor != surface->anchor) { + zwlr_layer_surface_v1_set_anchor(surface->wlr_layer_surface, wob_anchor_to_wlr_layer_surface_anchor(anchor)); + zwlr_layer_surface_v1_set_margin(surface->wlr_layer_surface, margin.top, margin.right, margin.bottom, margin.left); + zwlr_layer_surface_v1_set_size(surface->wlr_layer_surface, dimensions.width, dimensions.height); + + surface->dimensions = dimensions; + wl_surface_commit(surface->wl_surface); + } + + // no need to redraw, wait for configure event +} + +void +wp_fractional_scale_preferred_scale(void *data, struct wp_fractional_scale_v1 *wp_fractional_scale, uint32_t scale) +{ + (void) wp_fractional_scale; + + struct wob *app = (struct wob *) data; + + struct wob_surface *surface = app->surface; + + wob_log_debug("setting fractional scale to %u", scale); + surface->scale = scale; } struct wob_surface * -wob_surface_create(struct wob *app, struct wl_output *wl_output) +wob_create_surface(struct wob *app) { - struct wob_config config = *app->config; - const static struct zwlr_layer_surface_v1_listener zwlr_layer_surface_listener = { + static const 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_panic("calloc failed"); - } + static const struct wl_surface_listener wl_surface_listener = { + .enter = layer_surface_enter, + }; + + struct wob_dimensions dimensions = { + .width = 1, + .height = 1, + .bar_padding = 0, + .border_size = 0, + .border_offset = 0, + .orientation = WOB_ORIENTATION_HORIZONTAL, + }; - wob_surface->wl_surface = wl_compositor_create_surface(app->wl_compositor); - if (wob_surface->wl_surface == NULL) { + struct wob_margin margin = {.top = 0, .right = 0, .bottom = 0, .left = 0}; + + struct wl_surface *wl_surface = wl_compositor_create_surface(managers.wl_compositor); + if (wl_surface == NULL) { wob_log_panic("wl_compositor_create_surface failed"); } - 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) { + wl_surface_add_listener(wl_surface, &wl_surface_listener, app); + + struct zwlr_layer_surface_v1 *wlr_layer_surface = zwlr_layer_shell_v1_get_layer_surface(managers.wlr_layer_shell, wl_surface, NULL, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "wob"); + if (wlr_layer_surface == NULL) { wob_log_panic("wlr_layer_shell_v1_get_layer_surface failed"); } + zwlr_layer_surface_v1_set_size(wlr_layer_surface, dimensions.width, dimensions.height); + zwlr_layer_surface_v1_add_listener(wlr_layer_surface, &zwlr_layer_surface_listener, app); + + struct wp_viewport *wp_viewport = NULL; + if (managers.wp_viewporter != NULL) { + wp_viewport = wp_viewporter_get_viewport(managers.wp_viewporter, wl_surface); + if (wp_viewport == NULL) { + wob_log_panic("wp_viewporter_get_viewport failed"); + } + } + + static struct wp_fractional_scale_v1_listener wp_fractional_scale_listener = { + .preferred_scale = wp_fractional_scale_preferred_scale, + }; + + struct wp_fractional_scale_v1 *wp_fractional = NULL; + if (managers.wp_fractional_scale != NULL && wp_viewport != NULL) { + wp_fractional = wp_fractional_scale_manager_v1_get_fractional_scale(managers.wp_fractional_scale, wl_surface); + if (wp_fractional == NULL) { + wob_log_panic("wp_fractional_scale_manager_v1_get_fractional_scale failed"); + } + + wp_fractional_scale_v1_add_listener(wp_fractional, &wp_fractional_scale_listener, app); + } + + struct wob_surface *rendered = calloc(1, sizeof(struct wob_surface)); + if (rendered == NULL) { + wob_log_panic("calloc failed"); + } - struct wob_dimensions dimensions = config.dimensions; - struct wob_margin margin = config.margin; - enum wob_anchor anchor = config.anchor; + *rendered = (struct wob_surface){ + .wlr_layer_surface = wlr_layer_surface, + .wl_surface = wl_surface, + .dimensions = dimensions, + .scale = 120, + .wob_buffer = NULL, + .margin = margin, + .anchor = 0, + .wp_viewport = wp_viewport, + .fractional = wp_fractional, + }; - 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.top, margin.right, margin.bottom, margin.left); - zwlr_layer_surface_v1_add_listener(wob_surface->wlr_layer_surface, &zwlr_layer_surface_listener, app); + wl_surface_commit(wl_surface); - return wob_surface; + return rendered; +} + +void +wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) +{ + (void) time; + + wl_callback_destroy(cb); + + struct wob_surface *surface = (struct wob_surface *) data; + wob_log_debug("rendering frame"); + + wob_image_draw(surface->wob_buffer->shm_data, surface->wob_buffer->dimensions, surface->desired_colors, surface->desired_percentage); + + wl_surface_attach(surface->wl_surface, surface->wob_buffer->wl_buffer, 0, 0); + wl_surface_damage_buffer(surface->wl_surface, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_commit(surface->wl_surface); } void @@ -130,20 +339,27 @@ 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); + if (wob_surface->wp_viewport != NULL) { + wp_viewport_destroy(wob_surface->wp_viewport); + } + if (wob_surface->fractional != NULL) { + wp_fractional_scale_v1_destroy(wob_surface->fractional); + } + if (wob_surface->wob_buffer != NULL) { + wob_buffer_destroy(wob_surface->wob_buffer); + } + free(wob_surface); } void wob_output_destroy(struct wob_output *output) { - if (output->wob_surface != NULL) { - wob_surface_destroy(output->wob_surface); - } - if (output->wl_output != NULL) { wl_output_destroy(output->wl_output); } + free(output->description); free(output->name); free(output); } @@ -151,50 +367,98 @@ wob_output_destroy(struct wob_output *output) void xdg_output_handle_name(void *data, struct wl_output *wl_output, const char *name) { + (void) wl_output; + struct wob_output *output = (struct wob_output *) data; free(output->name); output->name = strdup(name); if (output->name == NULL) { - wob_log_panic("strdup failed\n"); + wob_log_panic("strdup failed"); + } +} + +void +xdg_output_handle_description(void *data, struct wl_output *wl_output, const char *description) +{ + (void) wl_output; + + struct wob_output *output = (struct wob_output *) data; + + free(output->description); + + output->description = strdup(description); + if (output->description == NULL) { + wob_log_panic("strdup failed"); + } +} + +void +xdg_output_handle_geometry( + void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const char *make, const char *model, int32_t transform +) +{ + (void) wl_output; + (void) x; + (void) y; + (void) physical_height; + (void) physical_width; + (void) subpixel; + (void) transform; + + struct wob_output *output = (struct wob_output *) data; + + if (strcmp(output->description, "UNKNOWN") == 0) { + free(output->description); + output->description = malloc(strlen(make) + strlen(model) + 1 + 1); // NULL BYTE + ' ' + if (output->description == NULL) { + wob_log_panic("malloc failed"); + } + + strcpy(output->description, make); + output->description[strlen(make)] = ' '; + strcpy(output->description + strlen(make) + 1, model); } } void xdg_output_handle_done(void *data, struct wl_output *wl_output) { + (void) wl_output; + struct wob_output *output = (struct wob_output *) data; - wob_log_debug("Detected new output %s", output->name); + wob_log_debug("Detected new output name = %s, description = %s", output->name, output->description); } void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { - const static struct wl_output_listener wl_output_listener = { - .geometry = noop, + static const struct wl_output_listener wl_output_listener = { + .geometry = xdg_output_handle_geometry, .mode = noop, .scale = noop, .name = xdg_output_handle_name, - .description = noop, + .description = xdg_output_handle_description, .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); + managers.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); + managers.wl_compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4); } else if (strcmp(interface, wl_output_interface.name) == 0) { struct wob_output *output = calloc(1, sizeof(struct wob_output)); output->wl_name = name; output->name = strdup("UNKNOWN"); + output->description = strdup("UNKNOWN"); if (version < 4) { - wob_log_warn("Need %s version > 4 to match outputs based on name, got version %zu. Some features might not work.", wl_output_interface.name, version); + wob_log_warn("Need %s version > 4 to match outputs based on name & description, got version %zu. Output matching will not work.", wl_output_interface.name, version); output->wl_output = wl_registry_bind(registry, name, &wl_output_interface, version); } else { @@ -205,13 +469,21 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, const cha wl_output_add_listener(output->wl_output, &wl_output_listener, output); } 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); + managers.wlr_layer_shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1); + } + else if (strcmp(interface, wp_viewporter_interface.name) == 0) { + managers.wp_viewporter = wl_registry_bind(registry, name, &wp_viewporter_interface, 1); + } + else if (strcmp(interface, wp_fractional_scale_manager_v1_interface.name) == 0) { + managers.wp_fractional_scale = wl_registry_bind(registry, name, &wp_fractional_scale_manager_v1_interface, 1); } } void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { + (void) registry; + struct wob *app = (struct wob *) data; struct wob_output *output, *output_tmp; @@ -228,30 +500,24 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) int wob_run(struct wob_config *config) { - struct wob *app = calloc(1, sizeof(struct wob)); - struct wob_image *image = wob_image_create_argb8888(config->dimensions.width, config->dimensions.height); + int _exit_code; - app->image = image; - app->config = config; + wl_surface_frame_listener.done = &wl_surface_frame_done; - wl_list_init(&app->wob_outputs); - if (app->config->output_mode == WOB_OUTPUT_MODE_FOCUSED) { - struct wob_output *output = calloc(1, sizeof(struct wob_output)); - output->wl_output = NULL; - output->wl_name = 0; - output->focused = true; - output->name = strdup("focused"); + struct wob *state = calloc(1, sizeof(struct wob)); - wl_list_insert(&app->wob_outputs, &output->link); - } + state->shmid = wob_shm_open(); - const static struct wl_registry_listener wl_registry_listener = { + state->config = config; + wl_list_init(&state->wob_outputs); + + static const 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) { + struct wl_display *wl_display = wl_display_connect(NULL); + if (wl_display == NULL) { wob_log_panic("wl_display_connect failed"); } @@ -259,41 +525,30 @@ wob_run(struct wob_config *config) wob_pledge(); } - app->wl_registry = wl_display_get_registry(app->wl_display); - if (app->wl_registry == NULL) { + struct wl_registry *wl_registry = wl_display_get_registry(wl_display); + if (wl_registry == NULL) { wob_log_panic("wl_display_get_registry failed"); } - wl_registry_add_listener(app->wl_registry, &wl_registry_listener, app); + wl_registry_add_listener(wl_registry, &wl_registry_listener, state); // first roundtrip to get global interfaces - if (wl_display_roundtrip(app->wl_display) < 0) { + if (wl_display_roundtrip(wl_display) < 0) { wob_log_panic("global listeners wl_display_roundtrip failed"); } // second roundtrip for output configuration - if (wl_display_roundtrip(app->wl_display) < 0) { + if (wl_display_roundtrip(wl_display) < 0) { wob_log_panic("global listeners wl_display_roundtrip failed"); } - if (app->wl_shm == NULL || app->wl_compositor == NULL || app->wlr_layer_shell == NULL) { + if (managers.wl_shm == NULL || managers.wl_compositor == NULL || managers.wlr_layer_shell == NULL) { wob_log_panic("Wayland compositor doesn't support all required protocols"); } - struct wl_shm_pool *pool = wl_shm_create_pool(app->wl_shm, app->image->shmid, app->image->size_in_bytes); - if (pool == NULL) { - wob_log_panic("wl_shm_create_pool failed"); - } - - app->wl_buffer = wl_shm_pool_create_buffer(pool, 0, app->image->width, app->image->height, app->image->width * 4, WL_SHM_FORMAT_ARGB8888); - wl_shm_pool_destroy(pool); - if (app->wl_buffer == NULL) { - wob_log_panic("wl_shm_pool_create_buffer failed"); - } - struct wob_colors effective_colors; struct pollfd fds[2] = { { - .fd = wl_display_get_fd(app->wl_display), + .fd = wl_display_get_fd(wl_display), .events = POLLIN, }, { @@ -302,29 +557,26 @@ wob_run(struct wob_config *config) }, }; - bool hidden = true; for (;;) { char input_buffer[INPUT_BUFFER_LENGTH] = {0}; - switch (poll(fds, 2, hidden ? -1 : app->config->timeout_msec)) { + int timeout = -1; + if (state->surface != NULL) { + timeout = state->config->timeout_msec; + } + + switch (poll(fds, 2, timeout)) { case -1: wob_log_panic("poll() failed: %s", strerror(errno)); case 0: - if (!hidden) { - 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); - output->wob_surface = NULL; - } + if (state->surface != NULL) { + wob_log_info("Hiding bar"); + wob_surface_destroy(state->surface); + state->surface = NULL; - if (wl_display_roundtrip(app->wl_display) < 1) { - wob_log_panic("wl_display_roundtrip failed"); - } + wl_display_flush(wl_display); } - hidden = true; break; default: if (fds[0].revents) { @@ -332,30 +584,32 @@ wob_run(struct wob_config *config) wob_log_panic("WL_DISPLAY_FD unexpectedly closed, revents = %hd", fds[0].revents); } - if (wl_display_dispatch(app->wl_display) == -1) { + wob_log_debug("read"); + if (wl_display_dispatch(wl_display) == -1) { wob_log_panic("wl_display_dispatch failed"); } - if (wl_display_roundtrip(app->wl_display) < 0) { - wob_log_panic("wl_display_roundtrip failed"); - } + wl_display_flush(wl_display); } if (fds[1].revents) { if (!(fds[1].revents & POLLIN)) { wob_log_error("STDIN unexpectedly closed, revents = %hd", fds[1].revents); - goto exit_failure; + _exit_code = EXIT_FAILURE; + goto _exit_cleanup; } char *fgets_rv = fgets(input_buffer, INPUT_BUFFER_LENGTH, stdin); if (fgets_rv == NULL) { if (feof(stdin)) { wob_log_info("Received EOF"); - goto exit_success; + _exit_code = EXIT_SUCCESS; + goto _exit_cleanup; } else { wob_log_error("fgets() failed: %s", strerror(errno)); - goto exit_failure; + _exit_code = EXIT_FAILURE; + goto _exit_cleanup; } } @@ -367,32 +621,34 @@ wob_run(struct wob_config *config) unsigned long percentage = strtoul(token, &str_end, 10); if (*str_end != '\0') { wob_log_error("Invalid value received '%s'", token); - goto exit_failure; + _exit_code = EXIT_FAILURE; + goto _exit_cleanup; } struct wob_style *selected_style = NULL; token = strtok(NULL, ""); if (token != NULL) { - selected_style = wob_config_find_style(app->config, token); + selected_style = wob_config_find_style(state->config, token); if (selected_style == NULL) { wob_log_error("Style named '%s' not found", token); - goto exit_failure; + _exit_code = EXIT_FAILURE; + goto _exit_cleanup; } wob_log_info("Received input { value = %lu, style = %s }", percentage, token); } else { - selected_style = &app->config->default_style; + selected_style = &state->config->default_style; wob_log_info("Received input { value = %lu, style = }", percentage); } - if (percentage > app->config->max) { + if (percentage > state->config->max) { effective_colors = selected_style->overflow_colors; - switch (app->config->overflow_mode) { + switch (state->config->overflow_mode) { case WOB_OVERFLOW_MODE_WRAP: - percentage %= app->config->max; + percentage %= state->config->max; break; case WOB_OVERFLOW_MODE_NOWRAP: - percentage = app->config->max; + percentage = state->config->max; break; } } @@ -400,7 +656,7 @@ wob_run(struct wob_config *config) effective_colors = selected_style->colors; } - if (wl_list_empty(&app->wob_outputs) == 1) { + if (wl_list_empty(&state->wob_outputs)) { wob_log_info("No output found to render wob on"); break; } @@ -413,72 +669,50 @@ wob_run(struct wob_config *config) WOB_COLOR_PRINTF_RGBA(effective_colors.value) ); - wob_image_draw(app->image, effective_colors, app->config->dimensions, percentage, app->config->max); - - struct wob_output *output; - wl_list_for_each (output, &app->wob_outputs, link) { - if (output->wob_surface != NULL) { - continue; - } - - if (!wob_should_render_on_output(app, output)) { - wob_log_info("NOT Showing bar on output %s", output->name); - continue; - } - - output->wob_surface = wob_surface_create(app, output->wl_output); - wl_surface_commit(output->wob_surface->wl_surface); - wob_log_info("Showing bar on output %s", output->name); + if (state->surface == NULL) { + state->surface = wob_create_surface(state); } - - if (wl_display_roundtrip(app->wl_display) < 0) { - wob_log_panic("wl_display_roundtrip failed"); - } - - wl_list_for_each (output, &(app->wob_outputs), link) { - if (output->wob_surface == NULL) { - continue; - } - 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); + else { + struct wl_callback *cb = wl_surface_frame(state->surface->wl_surface); + wl_callback_add_listener(cb, &wl_surface_frame_listener, state->surface); + wl_surface_commit(state->surface->wl_surface); } - if (wl_display_roundtrip(app->wl_display) < 1) { - wob_log_panic("wl_display_roundtrip failed"); - } + state->surface->desired_colors = effective_colors; + state->surface->desired_percentage = (double) percentage / (double) state->config->max; - hidden = false; + wl_display_flush(wl_display); } } } - int _exit_code; -_exit_cleanup : { +_exit_cleanup: + // cleanup state + if (state->surface != NULL) { + wob_surface_destroy(state->surface); + } struct wob_output *output, *output_tmp; - wl_list_for_each_safe (output, output_tmp, &app->wob_outputs, link) { + wl_list_for_each_safe (output, output_tmp, &state->wob_outputs, link) { wob_output_destroy(output); } + wob_config_destroy(state->config); + free(state); + + // cleanup global managers & registry + zwlr_layer_shell_v1_destroy(managers.wlr_layer_shell); + wl_compositor_destroy(managers.wl_compositor); + wl_shm_destroy(managers.wl_shm); + if (managers.wp_viewporter != NULL) { + wp_viewporter_destroy(managers.wp_viewporter); + } + if (managers.wp_fractional_scale != NULL) { + wp_fractional_scale_manager_v1_destroy(managers.wp_fractional_scale); + } + wl_registry_destroy(wl_registry); - zwlr_layer_shell_v1_destroy(app->wlr_layer_shell); - wl_buffer_destroy(app->wl_buffer); - wl_compositor_destroy(app->wl_compositor); - wl_shm_destroy(app->wl_shm); - wl_registry_destroy(app->wl_registry); - - wl_display_roundtrip(app->wl_display); - - wl_display_disconnect(app->wl_display); + // roundtrip and disconnect + wl_display_roundtrip(wl_display); + wl_display_disconnect(wl_display); - wob_config_destroy(app->config); - wob_image_destroy(app->image); - free(app); -} return _exit_code; -exit_success: - _exit_code = EXIT_SUCCESS; - goto _exit_cleanup; -exit_failure: - _exit_code = EXIT_FAILURE; - goto _exit_cleanup; } diff --git a/test/color_test.c b/test/color_test.c index 81b9a55..a22d2c8 100644 --- a/test/color_test.c +++ b/test/color_test.c @@ -12,6 +12,7 @@ void test_colors_are_case_insensitive(void **state) { + (void) state; struct wob_color color_a, color_b; assert_true(wob_color_from_rgba_string("ABCDEF", &color_a)); assert_true(wob_color_from_rgba_string("abcdef", &color_b)); @@ -25,6 +26,7 @@ test_colors_are_case_insensitive(void **state) void test_alpha_channel_is_optional(void **state) { + (void) state; struct wob_color color_a, color_b; assert_true(wob_color_from_rgba_string("AAAAAAFF", &color_a)); assert_true(wob_color_from_rgba_string("AAAAAA", &color_b)); @@ -38,6 +40,7 @@ test_alpha_channel_is_optional(void **state) void test_string_with_invalid_length_fails(void **state) { + (void) state; struct wob_color color; // too short assert_true(!wob_color_from_rgba_string("12345", &color)); @@ -50,6 +53,7 @@ test_string_with_invalid_length_fails(void **state) void test_string_with_invalid_characters_fails(void **state) { + (void) state; struct wob_color color; // whitespaces assert_true(!wob_color_from_rgba_string(" 123456 ", &color)); @@ -58,6 +62,7 @@ test_string_with_invalid_characters_fails(void **state) void test_valid_colors_from_string(void **state) { + (void) state; struct wob_color color; // without alpha assert_true(wob_color_from_rgba_string("123456", &color)); diff --git a/wob.ini.5.scd b/wob.ini.5.scd index d7d555d..9fd90d4 100644 --- a/wob.ini.5.scd +++ b/wob.ini.5.scd @@ -62,15 +62,6 @@ You can run `wob -vv` to find the default values. *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 - *orientation* Orientation of the bar, one of *horizontal* and *vertical*. @@ -78,15 +69,35 @@ You can run `wob -vv` to find the default values. # 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. +*match* + Substring to match the output. + + Example: DP-1, Dell U2722DE + +*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: DP-1 + Example: *bottom* *right* + +*margin* + Anchor margin, in pixels. Either as a single value or 4 values (top right bottom left). # SECTION: style.* @@ -120,7 +131,9 @@ bar_color = FFFFFF name = DP-1 [output.ips] -name = HDMI-1 +match = DELL U2722DE +width = 200 +height = 30 [style.muted] background_color = 032cfc