diff --git a/include/bar.hpp b/include/bar.hpp index 01a9d034..c16f68c5 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -21,6 +21,8 @@ struct waybar_output { Glib::RefPtr monitor; std::string name; std::string identifier; + int32_t width; + int32_t height; std::unique_ptr xdg_output = { nullptr, &zxdg_output_v1_destroy}; diff --git a/include/client.hpp b/include/client.hpp index 7fc3dce7..625926fa 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -43,6 +43,7 @@ class Client { static void handleGlobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version); static void handleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name); + static void handleOutputLogicalSize(void *, struct zxdg_output_v1 *, int32_t, int32_t); static void handleOutputDone(void *, struct zxdg_output_v1 *); static void handleOutputName(void *, struct zxdg_output_v1 *, const char *); static void handleOutputDescription(void *, struct zxdg_output_v1 *, const char *); diff --git a/include/config.hpp b/include/config.hpp index 82d55995..9df1ba94 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -25,7 +25,7 @@ class Config { Json::Value &getConfig() { return config_; } - std::vector getOutputConfigs(const std::string &name, const std::string &identifier); + std::vector getOutputConfigs(const std::string &name, const std::string &identifier, int32_t width, int32_t height); private: void setupConfig(Json::Value &dst, const std::string &config_file, int depth); diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index c42f6eb8..edea8102 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -31,6 +31,10 @@ Also a minimal example configuration can be found on the at the bottom of this m typeof: string|array ++ Specifies on which screen this bar will be displayed. Exclamation mark(*!*) can be used to exclude specific output. +*output-dimensions* ++ + typeof: string|array ++ + If *output* is not specified, allows selecting outputs by dimensions. Format "(width/height) () (value)", e.g. "width > 1080". Multiple conditions can be chained. + *position* ++ typeof: string ++ default: top ++ @@ -240,6 +244,24 @@ A module group is defined by specifying a module named "group/some-group-name". } ``` +## Select outputs by dimensions + +``` +{ + "layer": "top", + "output-dimensions": "width > 1080", + ... +} +``` + +``` +{ + "layer": "top", + "output-dimensions": ["width < 3840", "height < 2160"], + ... +} +``` + # SUPPORTED MODULES - *waybar-backlight(5)* diff --git a/src/client.cpp b/src/client.cpp index 8adbeac1..11d8a5fd 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -40,7 +40,7 @@ void waybar::Client::handleGlobalRemove(void * data, struct wl_registry * /*re void waybar::Client::handleOutput(struct waybar_output &output) { static const struct zxdg_output_v1_listener xdgOutputListener = { .logical_position = [](void *, struct zxdg_output_v1 *, int32_t, int32_t) {}, - .logical_size = [](void *, struct zxdg_output_v1 *, int32_t, int32_t) {}, + .logical_size = &handleOutputLogicalSize, .done = &handleOutputDone, .name = &handleOutputName, .description = &handleOutputDescription, @@ -61,7 +61,19 @@ struct waybar::waybar_output &waybar::Client::getOutput(void *addr) { } std::vector waybar::Client::getOutputConfigs(struct waybar_output &output) { - return config.getOutputConfigs(output.name, output.identifier); + return config.getOutputConfigs(output.name, output.identifier, output.width, output.height); +} + +void waybar::Client::handleOutputLogicalSize(void *data, struct zxdg_output_v1 *object, + int32_t width, int32_t height) { + auto client = waybar::Client::inst(); + try { + auto &output = client->getOutput(data); + output.width = width; + output.height = height; + } catch (const std::exception &e) { + std::cerr << e.what() << std::endl; + } } void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_output*/) { diff --git a/src/config.cpp b/src/config.cpp index 63149cbd..ee38d963 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -103,7 +103,7 @@ void Config::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) { } } bool isValidOutput(const Json::Value &config, const std::string &name, - const std::string &identifier) { + const std::string &identifier, int32_t width, int32_t height) { if (config["output"].isArray()) { for (auto const &output_conf : config["output"]) { if (output_conf.isString() && @@ -122,6 +122,38 @@ bool isValidOutput(const Json::Value &config, const std::string &name, } } + // if "output-dimensions" is a string, make it an array of size 1 + Json::Value config_output_dimensions = config["output-dimensions"]; + if (config_output_dimensions.isString()) { + Json::Value jsonArray(Json::arrayValue); + jsonArray.append(config_output_dimensions); + config_output_dimensions = jsonArray; + } + if (config_output_dimensions.isArray()) { + for (auto const &config_output_dimension : config_output_dimensions) { + if (!config_output_dimension.isString()) { + continue; + } + std::string str = config_output_dimension.asString(); + int i = str.find(" "); + std::string dimension = str.substr(0, i); + str = str.substr(i + 1); + i = str.find(" "); + std::string comparator = str.substr(0, i); + int value = std::stoi(str.substr(i)); + + if (dimension == "height" && comparator == "<" && height >= value) { + return false; + } else if (dimension == "height" && comparator == ">" && height <= value) { + return false; + }else if (dimension == "width" && comparator == "<" && width >= value) { + return false; + }else if (dimension == "width" && comparator == ">" && width <= value) { + return false; + } + return true; + } + } return true; } @@ -136,15 +168,16 @@ void Config::load(const std::string &config) { } std::vector Config::getOutputConfigs(const std::string &name, - const std::string &identifier) { + const std::string &identifier, + int32_t width, int32_t height) { std::vector configs; if (config_.isArray()) { for (auto const &config : config_) { - if (config.isObject() && isValidOutput(config, name, identifier)) { + if (config.isObject() && isValidOutput(config, name, identifier, width, height)) { configs.push_back(config); } } - } else if (isValidOutput(config_, name, identifier)) { + } else if (isValidOutput(config_, name, identifier, width, height)) { configs.push_back(config_); } return configs; diff --git a/test/config.cpp b/test/config.cpp index edd6d6b8..4eb6f902 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -13,11 +13,11 @@ TEST_CASE("Load simple config", "[config]") { REQUIRE(data["height"].asInt() == 30); } SECTION("select configs for configured output") { - auto configs = conf.getOutputConfigs("HDMI-0", "Fake HDMI output #0"); + auto configs = conf.getOutputConfigs("HDMI-0", "Fake HDMI output #0", 0, 0); REQUIRE(configs.size() == 1); } SECTION("select configs for missing output") { - auto configs = conf.getOutputConfigs("HDMI-1", "Fake HDMI output #1"); + auto configs = conf.getOutputConfigs("HDMI-1", "Fake HDMI output #1", 0, 0); REQUIRE(configs.empty()); } } @@ -27,7 +27,7 @@ TEST_CASE("Load config with multiple bars", "[config]") { conf.load("test/config/multi.json"); SECTION("select multiple configs #1") { - auto data = conf.getOutputConfigs("DP-0", "Fake DisplayPort output #0"); + auto data = conf.getOutputConfigs("DP-0", "Fake DisplayPort output #0", 0, 0); REQUIRE(data.size() == 3); REQUIRE(data[0]["layer"].asString() == "bottom"); REQUIRE(data[0]["height"].asInt() == 20); @@ -39,7 +39,7 @@ TEST_CASE("Load config with multiple bars", "[config]") { REQUIRE(data[2]["height"].asInt() == 23); } SECTION("select multiple configs #2") { - auto data = conf.getOutputConfigs("HDMI-0", "Fake HDMI output #0"); + auto data = conf.getOutputConfigs("HDMI-0", "Fake HDMI output #0", 0, 0); REQUIRE(data.size() == 2); REQUIRE(data[0]["layer"].asString() == "bottom"); REQUIRE(data[0]["height"].asInt() == 20); @@ -48,7 +48,7 @@ TEST_CASE("Load config with multiple bars", "[config]") { REQUIRE(data[1]["height"].asInt() == 23); } SECTION("select single config by output description") { - auto data = conf.getOutputConfigs("HDMI-1", "Fake HDMI output #1"); + auto data = conf.getOutputConfigs("HDMI-1", "Fake HDMI output #1", 0, 0); REQUIRE(data.size() == 1); REQUIRE(data[0]["layer"].asString() == "overlay"); REQUIRE(data[0]["position"].asString() == "left"); @@ -71,11 +71,11 @@ TEST_CASE("Load simple config with include", "[config]") { REQUIRE((data.isMember("nullOption") && data["nullOption"].isNull())); } SECTION("select configs for configured output") { - auto configs = conf.getOutputConfigs("HDMI-0", "Fake HDMI output #0"); + auto configs = conf.getOutputConfigs("HDMI-0", "Fake HDMI output #0", 0, 0); REQUIRE(configs.size() == 1); } SECTION("select configs for missing output") { - auto configs = conf.getOutputConfigs("HDMI-1", "Fake HDMI output #1"); + auto configs = conf.getOutputConfigs("HDMI-1", "Fake HDMI output #1", 0, 0); REQUIRE(configs.empty()); } } @@ -85,25 +85,25 @@ TEST_CASE("Load multiple bar config with include", "[config]") { conf.load("test/config/include-multi.json"); SECTION("bar config with sole include") { - auto data = conf.getOutputConfigs("OUT-0", "Fake output #0"); + auto data = conf.getOutputConfigs("OUT-0", "Fake output #0", 0, 0); REQUIRE(data.size() == 1); REQUIRE(data[0]["height"].asInt() == 20); } SECTION("bar config with output and include") { - auto data = conf.getOutputConfigs("OUT-1", "Fake output #1"); + auto data = conf.getOutputConfigs("OUT-1", "Fake output #1", 0, 0); REQUIRE(data.size() == 1); REQUIRE(data[0]["height"].asInt() == 21); } SECTION("bar config with output override") { - auto data = conf.getOutputConfigs("OUT-2", "Fake output #2"); + auto data = conf.getOutputConfigs("OUT-2", "Fake output #2", 0, 0); REQUIRE(data.size() == 1); REQUIRE(data[0]["height"].asInt() == 22); } SECTION("multiple levels of include") { - auto data = conf.getOutputConfigs("OUT-3", "Fake output #3"); + auto data = conf.getOutputConfigs("OUT-3", "Fake output #3", 0, 0); REQUIRE(data.size() == 1); REQUIRE(data[0]["height"].asInt() == 23); }