From 04cff0a4b0b971c5c08c002efa78b4059480db02 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sat, 1 Apr 2023 11:16:36 +1100 Subject: [PATCH] 70 add VRR_OFF (#77) * 70 add VRR_OFF config * 70 add VRR_OFF config * 70 add VRR_OFF cli * 70 add VRR_OFF to layout * 70 test layout handle_success, extract global * 70 test layout handle_success, extract global * 70 test layout handle_failure * 70 test layout handle_failure --- GNUmakefile | 5 +- cfg.yaml | 6 + doc/CONFIGURATION.md | 11 ++ doc/YAML_SCHEMAS.md | 2 + inc/cfg.h | 4 + inc/global.h | 11 ++ inc/server.h | 4 - src/cfg.c | 75 ++++++------ src/cli.c | 7 ++ src/convert.c | 1 + src/displ.c | 2 +- src/fds.c | 4 +- src/global.c | 9 ++ src/head.c | 2 +- src/layout.c | 26 +++-- src/lid.c | 2 +- src/marshalling.cpp | 24 +++- src/server.c | 5 +- tst/GNUmakefile | 2 +- tst/asserts.h | 8 +- tst/marshalling/cfg-all.yaml | 3 + tst/marshalling/cfg-bad.yaml | 2 + tst/marshalling/ipc-request-cfg-set.yaml | 3 + tst/marshalling/ipc-response-ok.yaml | 3 + tst/tst-cfg.c | 72 +++++++++--- tst/tst-cli.c | 50 ++++++-- tst/tst-head.c | 2 +- tst/tst-layout.c | 138 ++++++++++++++++++++++- tst/tst-marshalling.c | 13 ++- tst/wrap-info.c | 6 + way-displays.1.pandoc | 6 + 31 files changed, 410 insertions(+), 98 deletions(-) create mode 100644 inc/global.h create mode 100644 src/global.c diff --git a/GNUmakefile b/GNUmakefile index 002a1a7..2b87007 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -20,7 +20,7 @@ TST_CXX = $(wildcard tst/*.cpp) TST_O = $(TST_C:.c=.o) $(TST_CXX:.cpp=.o) TST_E = $(patsubst tst/%.c,%,$(wildcard tst/tst-*.c)) -all: way-displays +all: way-displays /tmp/vg.supp $(SRC_O): $(INC_H) $(PRO_H) config.mk GNUmakefile $(PRO_O): $(PRO_H) config.mk GNUmakefile @@ -41,6 +41,9 @@ $(PRO_C): $(PRO_X) clean: rm -f way-displays example_client $(SRC_O) $(EXAMPLE_O) $(PRO_O) $(PRO_H) $(PRO_C) $(TST_O) $(TST_E) +/tmp/vg.supp: .vg.supp + cp .vg.supp /tmp/vg.supp + install: way-displays way-displays.1 cfg.yaml mkdir -p $(DESTDIR)$(PREFIX)/bin cp -f way-displays $(DESTDIR)$(PREFIX)/bin diff --git a/cfg.yaml b/cfg.yaml index 033f5b6..26dc327 100644 --- a/cfg.yaml +++ b/cfg.yaml @@ -49,6 +49,12 @@ MODE: #- NAME_DESC: DP-2 # MAX: TRUE +# VRR / adaptive sync is enabled by default. Disable it per display. +VRR_OFF: + # - DP-2 + # - '!.*my monitor.*' + + # Laptop displays usually start with eDP e.g. eDP-1. This may be overridden if # your laptop is different. #LAPTOP_DISPLAY_PREFIX: 'eDP' diff --git a/doc/CONFIGURATION.md b/doc/CONFIGURATION.md index 73d5554..050ee20 100644 --- a/doc/CONFIGURATION.md +++ b/doc/CONFIGURATION.md @@ -136,6 +136,17 @@ MODE: - NAME_DESC: HDMI-A-1 MAX: TRUE ``` + +### VRR_OFF + +Adaptive sync is enabled by default. Disable it per display. + +```yaml +VRR_OFF: + - DP-2 + - '!.*my monitor.*' +``` + ### LAPTOP_DISPLAY_PREFIX Laptop displays usually start with `eDP` e.g. `eDP-1`. This may be overridden if your laptop is different e.g.: diff --git a/doc/YAML_SCHEMAS.md b/doc/YAML_SCHEMAS.md index f42833d..d8f825a 100644 --- a/doc/YAML_SCHEMAS.md +++ b/doc/YAML_SCHEMAS.md @@ -63,6 +63,8 @@ MODE: !!seq - !!map NAME_DESC: !!str MAX: !!bool +VRR_OFF: !!seq + - !!str DISABLED: !!seq - !!str LOG_THRESHOLD: !!log_threshold diff --git a/inc/cfg.h b/inc/cfg.h index 1655ef3..e92127d 100644 --- a/inc/cfg.h +++ b/inc/cfg.h @@ -54,6 +54,7 @@ struct Cfg { enum AutoScale auto_scale; struct SList *user_scales; struct SList *user_modes; + struct SList *adaptive_sync_off_name_desc; struct SList *max_preferred_refresh_name_desc; struct SList *disabled_name_desc; enum LogThreshold log_threshold; @@ -66,6 +67,7 @@ enum CfgElement { AUTO_SCALE, SCALE, MODE, + VRR_OFF, LAPTOP_DISPLAY_PREFIX, MAX_PREFERRED_REFRESH, LOG_THRESHOLD, @@ -75,6 +77,8 @@ enum CfgElement { void cfg_init(const char *cfg_path); +bool cfg_equal(struct Cfg *a, struct Cfg *b); + struct Cfg *cfg_merge(struct Cfg *to, struct Cfg *from, bool del); void cfg_file_reload(void); diff --git a/inc/global.h b/inc/global.h new file mode 100644 index 0000000..e38ac3e --- /dev/null +++ b/inc/global.h @@ -0,0 +1,11 @@ +#ifndef GLOBAL_H +#define GLOBAL_H + +extern struct Displ *displ; +extern struct Cfg *cfg; +extern struct Lid *lid; + +extern struct Head *head_changing_mode; +extern struct Head *head_changing_adaptive_sync; + +#endif // GLOBAL_H diff --git a/inc/server.h b/inc/server.h index 68296f8..2f73408 100644 --- a/inc/server.h +++ b/inc/server.h @@ -1,10 +1,6 @@ #ifndef SERVER_H #define SERVER_H -extern struct Displ *displ; -extern struct Cfg *cfg; -extern struct Lid *lid; - int server(char *cfg_path); #endif // SERVER_H diff --git a/src/cfg.c b/src/cfg.c index 7ac4d06..3c97ada 100644 --- a/src/cfg.c +++ b/src/cfg.c @@ -10,11 +10,11 @@ #include "cfg.h" #include "convert.h" +#include "global.h" #include "info.h" #include "list.h" #include "log.h" #include "marshalling.h" -#include "server.h" bool cfg_equal_user_mode_name(const void *value, const void *data) { if (!value || !data) { @@ -203,6 +203,11 @@ struct Cfg *clone_cfg(struct Cfg *from) { slist_append(&to->user_modes, to_user_mode); } + // VRR_OFF + for (i = from->adaptive_sync_off_name_desc; i; i = i->nex) { + slist_append(&to->adaptive_sync_off_name_desc, strdup((char*)i->val)); + } + // LAPTOP_DISPLAY_PREFIX if (from->laptop_display_prefix) { to->laptop_display_prefix = strdup(from->laptop_display_prefix); @@ -226,7 +231,7 @@ struct Cfg *clone_cfg(struct Cfg *from) { return to; } -bool equal_cfg(struct Cfg *a, struct Cfg *b) { +bool cfg_equal(struct Cfg *a, struct Cfg *b) { if (!a || !b) { return false; } @@ -261,6 +266,11 @@ bool equal_cfg(struct Cfg *a, struct Cfg *b) { return false; } + // VRR_OFF + if (!slist_equal(a->adaptive_sync_off_name_desc, b->adaptive_sync_off_name_desc, slist_equal_strcmp)) { + return false; + } + // LAPTOP_DISPLAY_PREFIX char *al = a->laptop_display_prefix; char *bl = b->laptop_display_prefix; @@ -410,6 +420,11 @@ void validate_warn(struct Cfg *cfg) { continue; warn_short_name_desc((const char*)i->val, "ORDER"); } + for (i = cfg->adaptive_sync_off_name_desc; i; i = i->nex) { + if (!i->val) + continue; + warn_short_name_desc((const char*)i->val, "VRR_OFF"); + } for (i = cfg->max_preferred_refresh_name_desc; i; i = i->nex) { if (!i->val) continue; @@ -484,6 +499,13 @@ struct Cfg *merge_set(struct Cfg *to, struct Cfg *from) { merged_user_mode->warned_no_mode = set_user_mode->warned_no_mode; } + // VRR_OFF + for (i = from->adaptive_sync_off_name_desc; i; i = i->nex) { + if (!slist_find_equal(merged->adaptive_sync_off_name_desc, slist_equal_strcmp, i->val)) { + slist_append(&merged->adaptive_sync_off_name_desc, strdup((char*)i->val)); + } + } + // DISABLED for (i = from->disabled_name_desc; i; i = i->nex) { if (!slist_find_equal(merged->disabled_name_desc, slist_equal_strcmp, i->val)) { @@ -513,6 +535,11 @@ struct Cfg *merge_del(struct Cfg *to, struct Cfg *from) { slist_remove_all_free(&merged->user_modes, cfg_equal_user_mode_name, i->val, cfg_user_mode_free); } + // VRR_OFF + for (i = from->adaptive_sync_off_name_desc; i; i = i->nex) { + slist_remove_all_free(&merged->adaptive_sync_off_name_desc, slist_equal_strcmp, i->val, NULL); + } + // DISABLED for (i = from->disabled_name_desc; i; i = i->nex) { slist_remove_all_free(&merged->disabled_name_desc, slist_equal_strcmp, i->val, NULL); @@ -538,7 +565,7 @@ struct Cfg *cfg_merge(struct Cfg *to, struct Cfg *from, bool del) { validate_fix(merged); validate_warn(merged); - if (equal_cfg(merged, to)) { + if (cfg_equal(merged, to)) { cfg_free(merged); merged = NULL; } @@ -647,44 +674,22 @@ void cfg_free(struct Cfg *cfg) { if (!cfg) return; - if (cfg->dir_path) { - free(cfg->dir_path); - } - if (cfg->file_path) { - free(cfg->file_path); - } - if (cfg->file_name) { - free(cfg->file_name); - } + free(cfg->dir_path); + free(cfg->file_path); + free(cfg->file_name); + free(cfg->laptop_display_prefix); - for (struct SList *i = cfg->order_name_desc; i; i = i->nex) { - free(i->val); - } - slist_free(&cfg->order_name_desc); + slist_free_vals(&cfg->order_name_desc, NULL); - for (struct SList *i = cfg->user_scales; i; i = i->nex) { - cfg_user_scale_free((struct UserScale*)i->val); - } - slist_free(&cfg->user_scales); + slist_free_vals(&cfg->user_scales, cfg_user_scale_free); - for (struct SList *i = cfg->user_modes; i; i = i->nex) { - cfg_user_mode_free((struct UserMode*)i->val); - } - slist_free(&cfg->user_modes); + slist_free_vals(&cfg->user_modes, cfg_user_mode_free); - for (struct SList *i = cfg->max_preferred_refresh_name_desc; i; i = i->nex) { - free(i->val); - } - slist_free(&cfg->max_preferred_refresh_name_desc); + slist_free_vals(&cfg->adaptive_sync_off_name_desc, NULL); - for (struct SList *i = cfg->disabled_name_desc; i; i = i->nex) { - free(i->val); - } - slist_free(&cfg->disabled_name_desc); + slist_free_vals(&cfg->max_preferred_refresh_name_desc, NULL); - if (cfg->laptop_display_prefix) { - free(cfg->laptop_display_prefix); - } + slist_free_vals(&cfg->disabled_name_desc, NULL); free(cfg); } diff --git a/src/cli.c b/src/cli.c index e71aaed..809754b 100644 --- a/src/cli.c +++ b/src/cli.c @@ -112,6 +112,12 @@ struct Cfg *parse_element(enum IpcRequestOperation op, enum CfgElement element, break; } break; + case VRR_OFF: + for (int i = optind; i < argc; i++) { + slist_append(&cfg->adaptive_sync_off_name_desc, strdup(argv[i])); + } + parsed = true; + break; case DISABLED: for (int i = optind; i < argc; i++) { slist_append(&cfg->disabled_name_desc, strdup(argv[i])); @@ -191,6 +197,7 @@ struct IpcRequest *parse_set(int argc, char **argv) { break; case AUTO_SCALE: case DISABLED: + case VRR_OFF: if (optind + 1 != argc) { log_error("%s requires one argument", cfg_element_name(element)); wd_exit(EXIT_FAILURE); diff --git a/src/convert.c b/src/convert.c index 66ea924..86734b2 100644 --- a/src/convert.c +++ b/src/convert.c @@ -21,6 +21,7 @@ static struct NameVal cfg_elements[] = { { .val = AUTO_SCALE, .name = "AUTO_SCALE", }, { .val = SCALE, .name = "SCALE", }, { .val = MODE, .name = "MODE", }, + { .val = VRR_OFF, .name = "VRR_OFF", }, { .val = LAPTOP_DISPLAY_PREFIX, .name = "LAPTOP_DISPLAY_PREFIX", }, { .val = MAX_PREFERRED_REFRESH, .name = "MAX_PREFERRED_REFRESH", }, { .val = LOG_THRESHOLD, .name = "LOG_THRESHOLD", }, diff --git a/src/displ.c b/src/displ.c index dca17c1..2851b9c 100644 --- a/src/displ.c +++ b/src/displ.c @@ -4,10 +4,10 @@ #include "displ.h" +#include "global.h" #include "listeners.h" #include "log.h" #include "process.h" -#include "server.h" void displ_init(void) { diff --git a/src/fds.c b/src/fds.c index 6956100..38597a8 100644 --- a/src/fds.c +++ b/src/fds.c @@ -11,11 +11,11 @@ #include "fds.h" #include "cfg.h" +#include "displ.h" #include "lid.h" #include "log.h" -#include "displ.h" +#include "global.h" #include "process.h" -#include "server.h" #include "sockets.h" #define PFDS_SIZE 5 diff --git a/src/global.c b/src/global.c new file mode 100644 index 0000000..9faeeae --- /dev/null +++ b/src/global.c @@ -0,0 +1,9 @@ +#include + +struct Displ *displ = NULL; +struct Lid *lid = NULL; +struct Cfg *cfg = NULL; + +struct Head *head_changing_mode = NULL; +struct Head *head_changing_adaptive_sync = NULL; + diff --git a/src/head.c b/src/head.c index 6176433..55b41f6 100644 --- a/src/head.c +++ b/src/head.c @@ -8,11 +8,11 @@ #include "head.h" #include "cfg.h" +#include "global.h" #include "info.h" #include "list.h" #include "log.h" #include "mode.h" -#include "server.h" struct SList *heads = NULL; struct SList *heads_arrived = NULL; diff --git a/src/layout.c b/src/layout.c index 9e42ab8..686c92f 100644 --- a/src/layout.c +++ b/src/layout.c @@ -8,6 +8,7 @@ #include "cfg.h" #include "displ.h" +#include "global.h" #include "head.h" #include "info.h" #include "lid.h" @@ -16,12 +17,8 @@ #include "log.h" #include "mode.h" #include "process.h" -#include "server.h" #include "wlr-output-management-unstable-v1.h" -struct Head *head_changing_mode = NULL; -struct Head *head_changing_adaptive_sync = NULL; - void position_heads(struct SList *heads) { struct Head *head; int32_t tallest = 0, widest = 0, x = 0, y = 0; @@ -206,13 +203,17 @@ void desire_scale(struct Head *head) { } void desire_adaptive_sync(struct Head *head) { - head->desired.adaptive_sync = false; + head->desired.adaptive_sync = ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED; if (!head->desired.enabled) { return; } - if (!head->adaptive_sync_failed) { + if (head->adaptive_sync_failed) { + return; + } + + if (!slist_find_equal(cfg->adaptive_sync_off_name_desc, head_name_desc_matches_head, head)) { head->desired.adaptive_sync = ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED; } } @@ -241,6 +242,8 @@ void desire(void) { void apply(void) { struct SList *heads_changing = NULL; + head_changing_mode = NULL; + head_changing_adaptive_sync = NULL; // determine whether changes are needed before initiating output configuration struct SList *i = heads; @@ -306,10 +309,13 @@ void handle_success(void) { } else if (head_changing_adaptive_sync) { + struct Head *head = head_changing_adaptive_sync; + head_changing_adaptive_sync = NULL; + // sway reports adaptive sync failure as success - if (head_current_adaptive_sync_not_desired(head_changing_adaptive_sync)) { - log_info("\n%s: Cannot enable VRR, display or compositor may not support it.", head_changing_adaptive_sync->name); - head_changing_adaptive_sync->adaptive_sync_failed = true; + if (head_current_adaptive_sync_not_desired(head)) { + log_info("\n%s: Cannot enable VRR, display or compositor may not support it.", head->name); + head->adaptive_sync_failed = true; return; } } @@ -338,6 +344,8 @@ void handle_failure(void) { log_info("\n%s: Cannot enable VRR, display or compositor may not support it.", head_changing_adaptive_sync->name); head_changing_adaptive_sync->adaptive_sync_failed = true; + head_changing_adaptive_sync = NULL; + } else { log_error("\nChanges failed"); diff --git a/src/lid.c b/src/lid.c index 3dea876..4e81475 100644 --- a/src/lid.c +++ b/src/lid.c @@ -13,8 +13,8 @@ #include "lid.h" #include "cfg.h" +#include "global.h" #include "log.h" -#include "server.h" static const char *LAPTOP_DISPLAY_PREFIX_DEFAULT = "eDP"; diff --git a/src/marshalling.cpp b/src/marshalling.cpp index 6900351..f3ad342 100644 --- a/src/marshalling.cpp +++ b/src/marshalling.cpp @@ -22,13 +22,13 @@ extern "C" { #include "cfg.h" #include "convert.h" +#include "global.h" #include "head.h" #include "ipc.h" #include "lid.h" #include "list.h" #include "log.h" #include "mode.h" -#include "server.h" } // If this is a regex pattern, attempt to compile it before including it in configuration. @@ -168,6 +168,14 @@ YAML::Emitter& operator << (YAML::Emitter& e, struct Cfg& cfg) { e << YAML::EndSeq; // MODE } + if (cfg.adaptive_sync_off_name_desc) { + e << YAML::Key << "VRR_OFF" << YAML::BeginSeq; // VRR_OFF + for (struct SList *i = cfg.adaptive_sync_off_name_desc; i; i = i->nex) { + e << (char*)i->val; + } + e << YAML::EndSeq; // VRR_OFF + } + if (cfg.laptop_display_prefix) { e << YAML::Key << "LAPTOP_DISPLAY_PREFIX" << YAML::Value << cfg.laptop_display_prefix; } @@ -374,6 +382,20 @@ void cfg_parse_node(struct Cfg *cfg, const YAML::Node &node) { } } + if (node["VRR_OFF"]) { + const auto &offs = node["VRR_OFF"]; + for (const auto &off : offs) { + const std::string &off_str = off.as(); + const char *off_cstr = off_str.c_str(); + if (!slist_find_equal(cfg->adaptive_sync_off_name_desc, slist_equal_strcmp, off_cstr)) { + if (!validate_regex(off_cstr, VRR_OFF)) { + continue; + } + slist_append(&cfg->adaptive_sync_off_name_desc, strdup(off_cstr)); + } + } + } + if (node["MAX_PREFERRED_REFRESH"]) { const auto &maxes = node["MAX_PREFERRED_REFRESH"]; for (const auto &max : maxes) { diff --git a/src/server.c b/src/server.c index 191c15c..b47d620 100644 --- a/src/server.c +++ b/src/server.c @@ -13,6 +13,7 @@ #include "convert.h" #include "displ.h" #include "fds.h" +#include "global.h" #include "head.h" #include "info.h" #include "ipc.h" @@ -21,10 +22,6 @@ #include "log.h" #include "process.h" -struct Displ *displ = NULL; -struct Lid *lid = NULL; -struct Cfg *cfg = NULL; - struct IpcResponse *ipc_response = NULL; void handle_ipc_in_progress(int server_socket) { diff --git a/tst/GNUmakefile b/tst/GNUmakefile index e247d85..0438e5d 100644 --- a/tst/GNUmakefile +++ b/tst/GNUmakefile @@ -12,7 +12,7 @@ OBJS = $(patsubst %.c,%.o,$(wildcard tst/wrap-*.c)) \ WRAPS = -Wl,$\ --wrap=log_set_threshold,$\ --wrap=log_,--wrap=log_error,--wrap=log_warn,--wrap=log_info,--wrap=log_error_nocap,$\ - --wrap=print_head,$\ + --wrap=print_head,--wrap=print_mode,$\ --wrap=wd_exit,--wrap=wd_exit_message $(TST_O): $(TST_H) $(SRC_O) config.mk GNUmakefile tst/GNUmakefile diff --git a/tst/asserts.h b/tst/asserts.h index f841182..fe90a98 100644 --- a/tst/asserts.h +++ b/tst/asserts.h @@ -5,13 +5,11 @@ #include #include +#include "cfg.h" #include "head.h" #include "list.h" #include "marshalling.h" -// forward declarations -bool equal_cfg(struct Cfg *a, struct Cfg *b); - void _assert_wl_fixed_t_equal_double(wl_fixed_t a, double b, const char * const file, const int line) { if (a != wl_fixed_from_double(b)) { @@ -55,13 +53,13 @@ void _assert_head_position(struct Head *head, int32_t x, int32_t y, const char * #define assert_head_position(h, x, y) _assert_head_position(h, x, y, __FILE__, __LINE__) void _assert_equal_cfg(struct Cfg *a, struct Cfg *b, const char * const file, const int line) { - if (!equal_cfg(a, b)) { + if (!cfg_equal(a, b)) { cmocka_print_error("assert_cfg_equal\nactual:\n\n%s\nexpected:\n\n%s\n\n", marshal_cfg(a), marshal_cfg(b)); _fail(file, line); } } -#define assert_equal_cfg(a, b) _assert_equal_cfg(a, b, __FILE__, __LINE__) +#define assert_cfg_equal(a, b) _assert_equal_cfg(a, b, __FILE__, __LINE__) #endif // ASSERTS_H diff --git a/tst/marshalling/cfg-all.yaml b/tst/marshalling/cfg-all.yaml index ed3c8b4..93b97a0 100644 --- a/tst/marshalling/cfg-all.yaml +++ b/tst/marshalling/cfg-all.yaml @@ -20,6 +20,9 @@ MODE: HEIGHT: 1440 - NAME_DESC: seven MAX: TRUE +VRR_OFF: + - ten + - ELEVEN DISABLED: - eight - EIGHT diff --git a/tst/marshalling/cfg-bad.yaml b/tst/marshalling/cfg-bad.yaml index 4a3b03a..60d1e7e 100644 --- a/tst/marshalling/cfg-bad.yaml +++ b/tst/marshalling/cfg-bad.yaml @@ -21,6 +21,8 @@ MODE: - NAME_DESC: BAD_MODE_HZ HZ: BAD_HZ - NAME_DESC: '!(mode' +VRR_OFF: + - '!(vrroff' MAX_PREFERRED_REFRESH: - '!(max' DISABLED: diff --git a/tst/marshalling/ipc-request-cfg-set.yaml b/tst/marshalling/ipc-request-cfg-set.yaml index 0038e94..31796e7 100644 --- a/tst/marshalling/ipc-request-cfg-set.yaml +++ b/tst/marshalling/ipc-request-cfg-set.yaml @@ -22,6 +22,9 @@ CFG: HEIGHT: 1440 - NAME_DESC: seven MAX: TRUE + VRR_OFF: + - ten + - ELEVEN DISABLED: - eight - EIGHT diff --git a/tst/marshalling/ipc-response-ok.yaml b/tst/marshalling/ipc-response-ok.yaml index 7b1c0c8..6922ab7 100644 --- a/tst/marshalling/ipc-response-ok.yaml +++ b/tst/marshalling/ipc-response-ok.yaml @@ -22,6 +22,9 @@ CFG: HEIGHT: 1440 - NAME_DESC: seven MAX: TRUE + VRR_OFF: + - ten + - ELEVEN DISABLED: - eight - EIGHT diff --git a/tst/tst-cfg.c b/tst/tst-cfg.c index 8beefda..7cc85de 100644 --- a/tst/tst-cfg.c +++ b/tst/tst-cfg.c @@ -62,7 +62,7 @@ void merge_set__arrange(void **state) { struct Cfg *merged = merge_set(s->to, s->from); - assert_equal_cfg(merged, s->expected); + assert_cfg_equal(merged, s->expected); cfg_free(merged); } @@ -75,7 +75,7 @@ void merge_set__align(void **state) { struct Cfg *merged = merge_set(s->to, s->from); - assert_equal_cfg(merged, s->expected); + assert_cfg_equal(merged, s->expected); cfg_free(merged); } @@ -90,7 +90,7 @@ void merge_set__order(void **state) { struct Cfg *merged = merge_set(s->to, s->from); - assert_equal_cfg(merged, s->expected); + assert_cfg_equal(merged, s->expected); cfg_free(merged); } @@ -103,7 +103,7 @@ void merge_set__auto_scale(void **state) { struct Cfg *merged = merge_set(s->to, s->from); - assert_equal_cfg(merged, s->expected); + assert_cfg_equal(merged, s->expected); cfg_free(merged); } @@ -123,7 +123,7 @@ void merge_set__user_scale(void **state) { struct Cfg *merged = merge_set(s->to, s->from); - assert_equal_cfg(merged, s->expected); + assert_cfg_equal(merged, s->expected); cfg_free(merged); } @@ -143,7 +143,27 @@ void merge_set__mode(void **state) { struct Cfg *merged = merge_set(s->to, s->from); - assert_equal_cfg(merged, s->expected); + assert_cfg_equal(merged, s->expected); + + cfg_free(merged); +} + +void merge_set__adaptive_sync_off(void **state) { + struct State *s = *state; + + slist_append(&s->to->adaptive_sync_off_name_desc, strdup("to")); + slist_append(&s->to->adaptive_sync_off_name_desc, strdup("both")); + + slist_append(&s->from->adaptive_sync_off_name_desc, strdup("from")); + slist_append(&s->from->adaptive_sync_off_name_desc, strdup("both")); + + slist_append(&s->expected->adaptive_sync_off_name_desc, strdup("to")); + slist_append(&s->expected->adaptive_sync_off_name_desc, strdup("both")); + slist_append(&s->expected->adaptive_sync_off_name_desc, strdup("from")); + + struct Cfg *merged = merge_set(s->to, s->from); + + assert_cfg_equal(merged, s->expected); cfg_free(merged); } @@ -163,7 +183,7 @@ void merge_set__disabled(void **state) { struct Cfg *merged = merge_set(s->to, s->from); - assert_equal_cfg(merged, s->expected); + assert_cfg_equal(merged, s->expected); cfg_free(merged); } @@ -181,7 +201,7 @@ void merge_del__scale(void **state) { struct Cfg *merged = merge_del(s->to, s->from); - assert_equal_cfg(merged, s->expected); + assert_cfg_equal(merged, s->expected); cfg_free(merged); } @@ -199,7 +219,25 @@ void merge_del__mode(void **state) { struct Cfg *merged = merge_del(s->to, s->from); - assert_equal_cfg(merged, s->expected); + assert_cfg_equal(merged, s->expected); + + cfg_free(merged); +} + +void merge_del__adaptive_sync_off(void **state) { + struct State *s = *state; + + slist_append(&s->to->adaptive_sync_off_name_desc, strdup("1")); + slist_append(&s->to->adaptive_sync_off_name_desc, strdup("2")); + + slist_append(&s->from->adaptive_sync_off_name_desc, strdup("2")); + slist_append(&s->from->adaptive_sync_off_name_desc, strdup("3")); + + slist_append(&s->expected->adaptive_sync_off_name_desc, strdup("1")); + + struct Cfg *merged = merge_del(s->to, s->from); + + assert_cfg_equal(merged, s->expected); cfg_free(merged); } @@ -217,7 +255,7 @@ void merge_del__disabled(void **state) { struct Cfg *merged = merge_del(s->to, s->from); - assert_equal_cfg(merged, s->expected); + assert_cfg_equal(merged, s->expected); cfg_free(merged); } @@ -234,7 +272,7 @@ void validate_fix__col(void **state) { validate_fix(s->from); - assert_equal_cfg(s->from, s->expected); + assert_cfg_equal(s->from, s->expected); } void validate_fix__row(void **state) { @@ -249,7 +287,7 @@ void validate_fix__row(void **state) { validate_fix(s->from); - assert_equal_cfg(s->from, s->expected); + assert_cfg_equal(s->from, s->expected); } void validate_fix__scale(void **state) { @@ -269,7 +307,7 @@ void validate_fix__scale(void **state) { slist_append(&s->expected->user_scales, cfg_user_scale_init("ok", 1)); - assert_equal_cfg(s->from, s->expected); + assert_cfg_equal(s->from, s->expected); } void validate_fix__mode(void **state) { @@ -298,7 +336,7 @@ void validate_fix__mode(void **state) { slist_append(&s->expected->user_modes, cfg_user_mode_init("ok", false, 1, 2, 3, false)); slist_append(&s->expected->user_modes, cfg_user_mode_init("max", true, -1, -1, -1, false)); - assert_equal_cfg(s->from, s->expected); + assert_cfg_equal(s->from, s->expected); } void validate_warn__(void **state) { @@ -318,6 +356,10 @@ void validate_warn__(void **state) { slist_append(&s->expected->order_name_desc, strdup("oooooooooo")); expect_log_warn(fmt, "ORDER", "ooo", NULL, NULL); + slist_append(&s->expected->adaptive_sync_off_name_desc, strdup("vvv")); + slist_append(&s->expected->adaptive_sync_off_name_desc, strdup("vvvvvvvvvv")); + expect_log_warn(fmt, "VRR_OFF", "vvv", NULL, NULL); + slist_append(&s->expected->max_preferred_refresh_name_desc, strdup("ppp")); slist_append(&s->expected->max_preferred_refresh_name_desc, strdup("pppppppppp")); expect_log_warn(fmt, "MAX_PREFERRED_REFRESH", "ppp", NULL, NULL); @@ -337,10 +379,12 @@ int main(void) { TEST(merge_set__auto_scale), TEST(merge_set__user_scale), TEST(merge_set__mode), + TEST(merge_set__adaptive_sync_off), TEST(merge_set__disabled), TEST(merge_del__scale), TEST(merge_del__mode), + TEST(merge_del__adaptive_sync_off), TEST(merge_del__disabled), TEST(validate_fix__col), diff --git a/tst/tst-cli.c b/tst/tst-cli.c index e8b3202..03ec203 100644 --- a/tst/tst-cli.c +++ b/tst/tst-cli.c @@ -68,7 +68,7 @@ void parse_element__arrange_align_ok(void **state) { .align = LEFT, }; - assert_equal_cfg(actual, &expected); + assert_cfg_equal(actual, &expected); cfg_free(actual); } @@ -93,7 +93,7 @@ void parse_element__auto_scale_ok(void **state) { .auto_scale = ON, }; - assert_equal_cfg(actual, &expected); + assert_cfg_equal(actual, &expected); cfg_free(actual); } @@ -123,7 +123,7 @@ void parse_element__scale_set_ok(void **state) { }; slist_append(&expected.user_scales, &expectedUserScale); - assert_equal_cfg(actual, &expected); + assert_cfg_equal(actual, &expected); cfg_free(actual); @@ -143,7 +143,7 @@ void parse_element__scale_del_ok(void **state) { struct Cfg expected = { 0 }; slist_append(&expected.user_scales, &expectedUserScale); - assert_equal_cfg(actual, &expected); + assert_cfg_equal(actual, &expected); cfg_free(actual); @@ -193,7 +193,7 @@ void parse_element__mode_set_max(void **state) { struct Cfg expected = { 0 }; slist_append(&expected.user_modes, expectedUserMode); - assert_equal_cfg(actual, &expected); + assert_cfg_equal(actual, &expected); cfg_free(actual); @@ -216,7 +216,7 @@ void parse_element__mode_set_res(void **state) { struct Cfg expected = { 0 }; slist_append(&expected.user_modes, expectedUserMode); - assert_equal_cfg(actual, &expected); + assert_cfg_equal(actual, &expected); cfg_free(actual); @@ -240,7 +240,7 @@ void parse_element__mode_set_res_refresh(void **state) { struct Cfg expected = { 0 }; slist_append(&expected.user_modes, expectedUserMode); - assert_equal_cfg(actual, &expected); + assert_cfg_equal(actual, &expected); cfg_free(actual); @@ -261,7 +261,7 @@ void parse_element__mode_del_ok(void **state) { struct Cfg expected = { 0 }; slist_append(&expected.user_modes, expectedUserMode); - assert_equal_cfg(actual, &expected); + assert_cfg_equal(actual, &expected); cfg_free(actual); @@ -269,6 +269,23 @@ void parse_element__mode_del_ok(void **state) { cfg_user_mode_free(expectedUserMode); } +void parse_element__adaptive_sync_off_ok(void **state) { + optind = 0; + char *argv[] = { "ONE", "TWO", }; + + struct Cfg *actual = parse_element(CFG_SET, VRR_OFF, 2, argv); + + struct Cfg expected = { 0 }; + slist_append(&expected.adaptive_sync_off_name_desc, "ONE"); + slist_append(&expected.adaptive_sync_off_name_desc, "TWO"); + + assert_cfg_equal(actual, &expected); + + cfg_free(actual); + + slist_free(&expected.disabled_name_desc); +} + void parse_element__disabled_ok(void **state) { optind = 0; char *argv[] = { "ONE", "TWO", }; @@ -279,7 +296,7 @@ void parse_element__disabled_ok(void **state) { slist_append(&expected.disabled_name_desc, "ONE"); slist_append(&expected.disabled_name_desc, "TWO"); - assert_equal_cfg(actual, &expected); + assert_cfg_equal(actual, &expected); cfg_free(actual); @@ -296,7 +313,7 @@ void parse_element__order_ok(void **state) { slist_append(&expected.order_name_desc, "ONE"); slist_append(&expected.order_name_desc, "TWO"); - assert_equal_cfg(actual, &expected); + assert_cfg_equal(actual, &expected); cfg_free(actual); @@ -379,6 +396,16 @@ void parse_set__disabled_nargs(void **state) { assert_null(parse_set(0, NULL)); } +void parse_set__adaptive_sync_off_nargs(void **state) { + optind = 0; + optarg = "VRR_OFF"; + + expect_log_error("%s requires one argument", optarg, NULL, NULL, NULL); + expect_value(__wrap_wd_exit, __status, EXIT_FAILURE); + + assert_null(parse_set(0, NULL)); +} + void parse_set__order_nargs(void **state) { optind = 0; optarg = "ORDER"; @@ -501,6 +528,8 @@ int main(void) { TEST(parse_element__mode_set_res_refresh), TEST(parse_element__mode_del_ok), + TEST(parse_element__adaptive_sync_off_ok), + TEST(parse_element__disabled_ok), TEST(parse_element__order_ok), @@ -513,6 +542,7 @@ int main(void) { TEST(parse_set__scale_nargs), TEST(parse_set__auto_scale_nargs), TEST(parse_set__disabled_nargs), + TEST(parse_set__adaptive_sync_off_nargs), TEST(parse_set__order_nargs), TEST(parse_set__invalid), TEST(parse_set__ok), diff --git a/tst/tst-head.c b/tst/tst-head.c index a656d72..002e411 100644 --- a/tst/tst-head.c +++ b/tst/tst-head.c @@ -10,9 +10,9 @@ #include #include "cfg.h" +#include "global.h" #include "list.h" #include "mode.h" -#include "server.h" #include "head.h" diff --git a/tst/tst-layout.c b/tst/tst-layout.c index 75596e7..34a7d7b 100644 --- a/tst/tst-layout.c +++ b/tst/tst-layout.c @@ -9,12 +9,12 @@ #include #include "cfg.h" +#include "global.h" #include "head.h" #include "info.h" #include "list.h" #include "log.h" #include "mode.h" -#include "server.h" #include "wlr-output-management-unstable-v1.h" struct SList *order_heads(struct SList *order_name_desc, struct SList *heads); @@ -23,7 +23,8 @@ void desire_enabled(struct Head *head); void desire_mode(struct Head *head); void desire_scale(struct Head *head); void desire_adaptive_sync(struct Head *head); - +void handle_success(void); +void handle_failure(void); bool __wrap_lid_is_closed(char *name) { check_expected(name); @@ -75,6 +76,9 @@ int before_each(void **state) { int after_each(void **state) { slist_free(&heads); + head_changing_mode = NULL; + head_changing_adaptive_sync = NULL; + cfg_destroy(); struct State *s = *state; @@ -477,7 +481,7 @@ void desire_scale__user(void **state) { assert_wl_fixed_t_equal_double(head0.desired.scale, 3.5); } -void desire_adaptive_sync__disabled(void **state) { +void desire_adaptive_sync__head_disabled(void **state) { struct Head head0 = { .desired.enabled = false, .desired.adaptive_sync = true, @@ -500,6 +504,20 @@ void desire_adaptive_sync__failed(void **state) { assert_int_equal(head0.desired.adaptive_sync, ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED); } +void desire_adaptive_sync__adaptive_sync_off(void **state) { + struct Head head0 = { + .name = "some head", + .desired.enabled = true, + .desired.adaptive_sync = true, + }; + + slist_append(&cfg->adaptive_sync_off_name_desc, strdup("!.*hea")); + + desire_adaptive_sync(&head0); + + assert_int_equal(head0.desired.adaptive_sync, ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED); +} + void desire_adaptive_sync__ok(void **state) { struct Head head0 = { .desired.enabled = true, @@ -511,6 +529,108 @@ void desire_adaptive_sync__ok(void **state) { assert_int_equal(head0.desired.adaptive_sync, ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED); } +void handle_success__head_changing_adaptive_sync(void **state) { + struct Head head = { + .desired.adaptive_sync = ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED, + .current.adaptive_sync = ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED, + .adaptive_sync_failed = false, + }; + head_changing_adaptive_sync = &head; + + expect_log_info("\nChanges successful", NULL, NULL, NULL, NULL); + + handle_success(); + + assert_null(head_changing_adaptive_sync); + assert_false(head.adaptive_sync_failed); +} + +void handle_success__head_changing_adaptive_sync_fail(void **state) { + struct Head head = { + .name = "head", + .desired.adaptive_sync = ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED, + .current.adaptive_sync = ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED, + }; + head_changing_adaptive_sync = &head; + + expect_log_info("\n%s: Cannot enable VRR, display or compositor may not support it.", "head", NULL, NULL, NULL); + + handle_success(); + + assert_null(head_changing_adaptive_sync); + assert_true(head.adaptive_sync_failed); +} + +void handle_success__head_changing_mode(void **state) { + struct Mode mode = { 0 }; + struct Head head = { + .desired.mode = &mode, + }; + head_changing_mode = &head; + + expect_log_info("\nChanges successful", NULL, NULL, NULL, NULL); + + handle_success(); + + assert_ptr_equal(head.current.mode, &mode); + assert_null(head_changing_mode); +} + +void handle_success__ok(void **state) { + expect_log_info("\nChanges successful", NULL, NULL, NULL, NULL); + + handle_success(); +} + +void handle_failure__mode(void **state) { + struct Mode mode_cur = { 0 }; + struct Mode mode_des = { 0 }; + struct Head head = { + .name = "nam", + .current.mode = &mode_cur, + .desired.mode = &mode_des, + }; + head_changing_mode = &head; + + expect_log_error("\nChanges failed", NULL, NULL, NULL, NULL); + expect_log_error(" %s:", "nam", NULL, NULL, NULL); + expect_value(__wrap_print_mode, t, ERROR); + expect_value(__wrap_print_mode, mode, &mode_des); + + handle_failure(); + + assert_null(head_changing_mode); + + assert_null(head.current.mode); + assert_ptr_equal(head.desired.mode, &mode_des); + + assert_ptr_equal(slist_find_equal_val(head.modes_failed, NULL, &mode_des), &mode_des); +} + +void handle_failure__adaptive_sync(void **state) { + struct Head head = { + .name = "nam", + .current.adaptive_sync = ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED, + .desired.adaptive_sync = ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED, + }; + head_changing_adaptive_sync = &head; + + expect_log_info("\n%s: Cannot enable VRR, display or compositor may not support it.", "nam", NULL, NULL, NULL); + + handle_failure(); + + assert_null(head_changing_adaptive_sync); + + assert_true(head.adaptive_sync_failed); +} + +void handle_failure__unspecified(void **state) { + expect_log_error("\nChanges failed", NULL, NULL, NULL, NULL); + expect_value(__wrap_wd_exit_message, __status, EXIT_FAILURE); + + handle_failure(); +} + int main(void) { const struct CMUnitTest tests[] = { TEST(order_heads__exact_partial_regex), @@ -538,9 +658,19 @@ int main(void) { TEST(desire_scale__auto), TEST(desire_scale__user), - TEST(desire_adaptive_sync__disabled), + TEST(desire_adaptive_sync__head_disabled), TEST(desire_adaptive_sync__failed), + TEST(desire_adaptive_sync__adaptive_sync_off), TEST(desire_adaptive_sync__ok), + + TEST(handle_success__head_changing_adaptive_sync), + TEST(handle_success__head_changing_adaptive_sync_fail), + TEST(handle_success__head_changing_mode), + TEST(handle_success__ok), + + TEST(handle_failure__mode), + TEST(handle_failure__adaptive_sync), + TEST(handle_failure__unspecified), }; return RUN(tests); diff --git a/tst/tst-marshalling.c b/tst/tst-marshalling.c index f7f5262..13488e3 100644 --- a/tst/tst-marshalling.c +++ b/tst/tst-marshalling.c @@ -13,13 +13,13 @@ #include #include "cfg.h" +#include "global.h" #include "head.h" #include "ipc.h" #include "lid.h" #include "list.h" #include "log.h" #include "mode.h" -#include "server.h" #include "marshalling.h" @@ -88,6 +88,9 @@ struct Cfg *cfg_all(void) { slist_append(&cfg->user_modes, cfg_user_mode_init("six", false, 2560, 1440, -1, false)); slist_append(&cfg->user_modes, cfg_user_mode_init("seven", true, -1, -1, -1, false)); + slist_append(&cfg->adaptive_sync_off_name_desc, strdup("ten")); + slist_append(&cfg->adaptive_sync_off_name_desc, strdup("ELEVEN")); + slist_append(&cfg->disabled_name_desc, strdup("eight")); slist_append(&cfg->disabled_name_desc, strdup("EIGHT")); slist_append(&cfg->disabled_name_desc, strdup("nine")); @@ -112,7 +115,7 @@ void unmarshal_cfg_from_file__ok(void **state) { struct Cfg *expected = cfg_all(); - assert_equal_cfg(read, expected); + assert_cfg_equal(read, expected); cfg_free(read); cfg_free(expected); @@ -165,6 +168,8 @@ void unmarshal_cfg_from_file__bad(void **state) { expect_log_warn("Ignoring bad %s regex '%s': %s", "MODE", "(mode", NULL, NULL); + expect_log_warn("Ignoring bad %s regex '%s': %s", "VRR_OFF", "(vrroff", NULL, NULL); + expect_log_warn("Ignoring bad %s regex '%s': %s", "MAX_PREFERRED_REFRESH", "(max", NULL, NULL); expect_log_warn("Ignoring bad %s regex '%s': %s", "DISABLED", "(disabled", NULL, NULL); @@ -173,7 +178,7 @@ void unmarshal_cfg_from_file__bad(void **state) { struct Cfg *expected = cfg_default(); - assert_equal_cfg(read, expected); + assert_cfg_equal(read, expected); cfg_free(read); cfg_free(expected); @@ -364,7 +369,7 @@ void unmarshal_ipc_request__cfg_set(void **state) { struct Cfg *expected_cfg = cfg_all(); - assert_equal_cfg(actual->cfg, expected_cfg); + assert_cfg_equal(actual->cfg, expected_cfg); ipc_request_free(actual); cfg_free(expected_cfg); diff --git a/tst/wrap-info.c b/tst/wrap-info.c index 9c243f4..532c1ec 100644 --- a/tst/wrap-info.c +++ b/tst/wrap-info.c @@ -6,6 +6,7 @@ #include "head.h" #include "info.h" +#include "head.h" void __wrap_print_head(enum LogThreshold t, enum InfoEvent event, struct Head *head) { check_expected(t); @@ -13,3 +14,8 @@ void __wrap_print_head(enum LogThreshold t, enum InfoEvent event, struct Head *h check_expected(head); } +void __wrap_print_mode(enum LogThreshold t, struct Mode *mode) { + check_expected(t); + check_expected(mode); +} + diff --git a/way-displays.1.pandoc b/way-displays.1.pandoc index df01df1..a4fc13a 100644 --- a/way-displays.1.pandoc +++ b/way-displays.1.pandoc @@ -97,6 +97,9 @@ The user must be a member of the `input` group. `MODE` <*name*> <*width*> <*height*> [<*Hz*>] : Specified resolution at its highest refresh. Optional refresh will choose a mode +-0.5Hz. + `VRR_OFF` <*name*> + : Disable VRR for a display. + `DISABLED` <*name*> : Disable a display. @@ -109,6 +112,9 @@ The user must be a member of the `input` group. `MODE` <*name*> : Use preferred or highest availble if no preferred. + `VRR_OFF` <*name*> + : Enable VRR for a display. + `DISABLED` <*name*> : Enable a display.