mirror of
https://github.com/hyprwm/Hyprland.git
synced 2026-08-24 10:04:19 -05:00
monitor: refactor inheritance, expand query, add unit tests for position and query (#15073)
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
#include <state/MonitorPositionController.hpp>
|
||||
|
||||
#include <config/shared/monitor/MonitorRule.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class CPositionTestMonitor : public Monitor::IMonitorArrangeable {
|
||||
public:
|
||||
CPositionTestMonitor(MONITORID id, const std::string& name, const Vector2D& size) : m_id(id), m_name(name), m_size(size), m_transformedSize(size) {
|
||||
;
|
||||
}
|
||||
|
||||
virtual MONITORID id() const override {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
virtual std::string_view name() const override {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
virtual std::string_view description() const override {
|
||||
return m_description;
|
||||
}
|
||||
|
||||
virtual std::string_view shortDescription() const override {
|
||||
return m_shortDescription;
|
||||
}
|
||||
|
||||
virtual bool matchesStaticSelector(std::string_view selector) const override {
|
||||
return selector == m_name;
|
||||
}
|
||||
|
||||
virtual Vector2D position() const override {
|
||||
return m_position;
|
||||
}
|
||||
|
||||
virtual Vector2D size() const override {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
virtual Vector2D pixelSize() const override {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
virtual Vector2D transformedSize() const override {
|
||||
return m_transformedSize;
|
||||
}
|
||||
|
||||
virtual float scale() const override {
|
||||
return m_scale;
|
||||
}
|
||||
|
||||
virtual Hyprutils::Math::eTransform transform() const override {
|
||||
return m_transform;
|
||||
}
|
||||
|
||||
virtual CBox logicalBox() const override {
|
||||
return {m_position, m_size};
|
||||
}
|
||||
|
||||
virtual CBox logicalBoxMinusReserved() const override {
|
||||
return logicalBox();
|
||||
}
|
||||
|
||||
virtual Vector2D middle() const override {
|
||||
return m_position + m_size / 2.F;
|
||||
}
|
||||
|
||||
virtual void moveTo(const Vector2D& pos) override {
|
||||
m_position = pos;
|
||||
}
|
||||
|
||||
virtual std::optional<Vector2D> explicitPosition() const override {
|
||||
return m_explicitPosition;
|
||||
}
|
||||
|
||||
virtual Config::eAutoDirs autoDirection() const override {
|
||||
return m_autoDirection;
|
||||
}
|
||||
|
||||
virtual Vector2D xwaylandPosition() const override {
|
||||
return m_xwaylandPosition;
|
||||
}
|
||||
|
||||
virtual float xwaylandScale() const override {
|
||||
return m_xwaylandScale;
|
||||
}
|
||||
|
||||
virtual void setXWaylandPosition(const Vector2D& pos) override {
|
||||
m_xwaylandPosition = pos;
|
||||
}
|
||||
|
||||
virtual void setXWaylandScale(float scale) override {
|
||||
m_xwaylandScale = scale;
|
||||
}
|
||||
|
||||
MONITORID m_id = 0;
|
||||
std::string m_name;
|
||||
std::string m_description;
|
||||
std::string m_shortDescription;
|
||||
Vector2D m_position;
|
||||
Vector2D m_size;
|
||||
Vector2D m_transformedSize;
|
||||
float m_scale = 1.F;
|
||||
Hyprutils::Math::eTransform m_transform = Hyprutils::Math::HYPRUTILS_TRANSFORM_NORMAL;
|
||||
std::optional<Vector2D> m_explicitPosition = {};
|
||||
Config::eAutoDirs m_autoDirection = Config::DIR_AUTO_RIGHT;
|
||||
Vector2D m_xwaylandPosition;
|
||||
float m_xwaylandScale = 1.F;
|
||||
};
|
||||
|
||||
static SP<CPositionTestMonitor> testMonitor(MONITORID id, const std::string& name, const Vector2D& size) {
|
||||
return makeShared<CPositionTestMonitor>(id, name, size);
|
||||
}
|
||||
|
||||
static SP<Monitor::IMonitorArrangeable> arrangeable(const SP<CPositionTestMonitor>& monitor) {
|
||||
return dynamicPointerCast<Monitor::IMonitorArrangeable>(monitor);
|
||||
}
|
||||
|
||||
static void arrange(const std::vector<SP<CPositionTestMonitor>>& monitors, bool xwaylandForceZeroScaling = false) {
|
||||
std::vector<SP<Monitor::IMonitorArrangeable>> arrangeableMonitors;
|
||||
arrangeableMonitors.reserve(monitors.size());
|
||||
|
||||
for (const auto& monitor : monitors)
|
||||
arrangeableMonitors.push_back(arrangeable(monitor));
|
||||
|
||||
State::CMonitorPositionController{}.arrange(arrangeableMonitors, xwaylandForceZeroScaling);
|
||||
}
|
||||
|
||||
TEST(MonitorPositionController, explicitPositionsAreAppliedFirst) {
|
||||
const auto left = testMonitor(0, "left", {100, 100});
|
||||
const auto right = testMonitor(1, "right", {100, 100});
|
||||
const auto autoM = testMonitor(2, "auto", {50, 50});
|
||||
|
||||
left->m_explicitPosition = Vector2D{-100, 0};
|
||||
right->m_explicitPosition = Vector2D{100, 0};
|
||||
autoM->m_autoDirection = Config::DIR_AUTO_RIGHT;
|
||||
|
||||
arrange({left, autoM, right});
|
||||
|
||||
EXPECT_EQ(left->m_position, Vector2D(-100, 0));
|
||||
EXPECT_EQ(right->m_position, Vector2D(100, 0));
|
||||
EXPECT_EQ(autoM->m_position, Vector2D(200, 0));
|
||||
}
|
||||
|
||||
TEST(MonitorPositionController, autoRightPlacesAfterRightmostEdge) {
|
||||
const auto explicitM = testMonitor(0, "explicit", {100, 100});
|
||||
const auto autoM = testMonitor(1, "auto", {50, 50});
|
||||
|
||||
explicitM->m_explicitPosition = Vector2D{0, 0};
|
||||
autoM->m_autoDirection = Config::DIR_AUTO_RIGHT;
|
||||
|
||||
arrange({explicitM, autoM});
|
||||
|
||||
EXPECT_EQ(autoM->m_position, Vector2D(100, 0));
|
||||
}
|
||||
|
||||
TEST(MonitorPositionController, autoLeftPlacesBeforeLeftmostEdge) {
|
||||
const auto explicitM = testMonitor(0, "explicit", {100, 100});
|
||||
const auto autoM = testMonitor(1, "auto", {50, 50});
|
||||
|
||||
explicitM->m_explicitPosition = Vector2D{0, 0};
|
||||
autoM->m_autoDirection = Config::DIR_AUTO_LEFT;
|
||||
|
||||
arrange({explicitM, autoM});
|
||||
|
||||
EXPECT_EQ(autoM->m_position, Vector2D(-50, 0));
|
||||
}
|
||||
|
||||
TEST(MonitorPositionController, autoDownPlacesBelowLowestEdge) {
|
||||
const auto explicitM = testMonitor(0, "explicit", {100, 100});
|
||||
const auto autoM = testMonitor(1, "auto", {50, 50});
|
||||
|
||||
explicitM->m_explicitPosition = Vector2D{0, 0};
|
||||
autoM->m_autoDirection = Config::DIR_AUTO_DOWN;
|
||||
|
||||
arrange({explicitM, autoM});
|
||||
|
||||
EXPECT_EQ(autoM->m_position, Vector2D(0, 100));
|
||||
}
|
||||
|
||||
TEST(MonitorPositionController, autoUpPlacesAboveHighestEdge) {
|
||||
const auto explicitM = testMonitor(0, "explicit", {100, 100});
|
||||
const auto autoM = testMonitor(1, "auto", {50, 50});
|
||||
|
||||
explicitM->m_explicitPosition = Vector2D{0, 0};
|
||||
autoM->m_autoDirection = Config::DIR_AUTO_UP;
|
||||
|
||||
arrange({explicitM, autoM});
|
||||
|
||||
EXPECT_EQ(autoM->m_position, Vector2D(0, -50));
|
||||
}
|
||||
|
||||
TEST(MonitorPositionController, autoCenterRightCentersVertically) {
|
||||
const auto explicitM = testMonitor(0, "explicit", {100, 100});
|
||||
const auto autoM = testMonitor(1, "auto", {50, 40});
|
||||
|
||||
explicitM->m_explicitPosition = Vector2D{0, 0};
|
||||
autoM->m_autoDirection = Config::DIR_AUTO_CENTER_RIGHT;
|
||||
|
||||
arrange({explicitM, autoM});
|
||||
|
||||
EXPECT_EQ(autoM->m_position, Vector2D(100, 30));
|
||||
}
|
||||
|
||||
TEST(MonitorPositionController, autoCenterDownCentersHorizontally) {
|
||||
const auto explicitM = testMonitor(0, "explicit", {100, 100});
|
||||
const auto autoM = testMonitor(1, "auto", {40, 50});
|
||||
|
||||
explicitM->m_explicitPosition = Vector2D{0, 0};
|
||||
autoM->m_autoDirection = Config::DIR_AUTO_CENTER_DOWN;
|
||||
|
||||
arrange({explicitM, autoM});
|
||||
|
||||
EXPECT_EQ(autoM->m_position, Vector2D(30, 100));
|
||||
}
|
||||
|
||||
TEST(MonitorPositionController, xwaylandPositionsUseLogicalSizeByDefault) {
|
||||
const auto first = testMonitor(0, "first", {100, 100});
|
||||
const auto second = testMonitor(1, "second", {50, 50});
|
||||
|
||||
first->m_explicitPosition = Vector2D{0, 0};
|
||||
second->m_explicitPosition = Vector2D{100, 0};
|
||||
first->m_transformedSize = Vector2D{200, 100};
|
||||
first->m_scale = 2.F;
|
||||
|
||||
arrange({first, second}, false);
|
||||
|
||||
EXPECT_EQ(first->m_xwaylandPosition, Vector2D(0, 0));
|
||||
EXPECT_EQ(second->m_xwaylandPosition, Vector2D(100, 0));
|
||||
EXPECT_FLOAT_EQ(first->m_xwaylandScale, 1.F);
|
||||
EXPECT_FLOAT_EQ(second->m_xwaylandScale, 1.F);
|
||||
}
|
||||
|
||||
TEST(MonitorPositionController, xwaylandPositionsUseTransformedSizeWhenForceZeroScaling) {
|
||||
const auto first = testMonitor(0, "first", {100, 100});
|
||||
const auto second = testMonitor(1, "second", {50, 50});
|
||||
|
||||
first->m_explicitPosition = Vector2D{0, 0};
|
||||
second->m_explicitPosition = Vector2D{100, 0};
|
||||
first->m_transformedSize = Vector2D{200, 100};
|
||||
first->m_scale = 2.F;
|
||||
|
||||
arrange({first, second}, true);
|
||||
|
||||
EXPECT_EQ(first->m_xwaylandPosition, Vector2D(0, 0));
|
||||
EXPECT_EQ(second->m_xwaylandPosition, Vector2D(200, 0));
|
||||
EXPECT_FLOAT_EQ(first->m_xwaylandScale, 2.F);
|
||||
EXPECT_FLOAT_EQ(second->m_xwaylandScale, 1.F);
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
#include <state/MonitorQueryCore.hpp>
|
||||
|
||||
#include <aquamarine/output/Output.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
#include <hyprutils/string/String.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using namespace Hyprutils::String;
|
||||
|
||||
class CQueryTestMonitor : public Monitor::IMonitorQueryable {
|
||||
public:
|
||||
CQueryTestMonitor(MONITORID id, const std::string& name, const Vector2D& position, const Vector2D& size) : m_id(id), m_name(name), m_position(position), m_size(size) {
|
||||
;
|
||||
}
|
||||
|
||||
virtual MONITORID id() const override {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
virtual std::string_view name() const override {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
virtual std::string_view description() const override {
|
||||
return m_description;
|
||||
}
|
||||
|
||||
virtual std::string_view shortDescription() const override {
|
||||
return m_shortDescription;
|
||||
}
|
||||
|
||||
virtual bool matchesStaticSelector(std::string_view selector) const override {
|
||||
if (m_selectorResult.has_value())
|
||||
return *m_selectorResult;
|
||||
|
||||
if (selector.starts_with("desc:")) {
|
||||
const auto DESCRIPTIONSELECTOR = trim(selector.substr(5));
|
||||
return m_description.starts_with(DESCRIPTIONSELECTOR) || m_shortDescription.starts_with(DESCRIPTIONSELECTOR);
|
||||
}
|
||||
|
||||
return selector == m_name;
|
||||
}
|
||||
|
||||
virtual Vector2D position() const override {
|
||||
return m_position;
|
||||
}
|
||||
|
||||
virtual Vector2D size() const override {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
virtual Vector2D pixelSize() const override {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
virtual Vector2D transformedSize() const override {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
virtual float scale() const override {
|
||||
return 1.F;
|
||||
}
|
||||
|
||||
virtual Hyprutils::Math::eTransform transform() const override {
|
||||
return Hyprutils::Math::HYPRUTILS_TRANSFORM_NORMAL;
|
||||
}
|
||||
|
||||
virtual CBox logicalBox() const override {
|
||||
return {m_position, m_size};
|
||||
}
|
||||
|
||||
virtual CBox logicalBoxMinusReserved() const override {
|
||||
return logicalBox();
|
||||
}
|
||||
|
||||
virtual Vector2D middle() const override {
|
||||
return m_position + m_size / 2.F;
|
||||
}
|
||||
|
||||
virtual bool enabled() const override {
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
virtual bool hasOutput() const override {
|
||||
return m_hasOutput;
|
||||
}
|
||||
|
||||
virtual SP<Aquamarine::IOutput> output() const override {
|
||||
return m_output;
|
||||
}
|
||||
|
||||
MONITORID m_id = 0;
|
||||
std::string m_name;
|
||||
std::string m_description;
|
||||
std::string m_shortDescription;
|
||||
Vector2D m_position;
|
||||
Vector2D m_size;
|
||||
bool m_enabled = true;
|
||||
bool m_hasOutput = true;
|
||||
SP<Aquamarine::IOutput> m_output;
|
||||
std::optional<bool> m_selectorResult;
|
||||
};
|
||||
|
||||
static SP<CQueryTestMonitor> testMonitor(MONITORID id, const std::string& name, const Vector2D& position, const Vector2D& size = {100, 100}) {
|
||||
return makeShared<CQueryTestMonitor>(id, name, position, size);
|
||||
}
|
||||
|
||||
static SP<Monitor::IMonitorQueryable> queryable(const SP<CQueryTestMonitor>& monitor) {
|
||||
return dynamicPointerCast<Monitor::IMonitorQueryable>(monitor);
|
||||
}
|
||||
|
||||
static std::vector<SP<Monitor::IMonitorQueryable>> queryables(const std::vector<SP<CQueryTestMonitor>>& monitors) {
|
||||
std::vector<SP<Monitor::IMonitorQueryable>> result;
|
||||
result.reserve(monitors.size());
|
||||
|
||||
for (const auto& monitor : monitors)
|
||||
result.push_back(queryable(monitor));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST(MonitorQueryCore, queryById) {
|
||||
const auto first = testMonitor(0, "DP-1", {0, 0});
|
||||
const auto second = testMonitor(1, "DP-2", {100, 0});
|
||||
auto monitors = queryables({first, second});
|
||||
|
||||
EXPECT_EQ(std::move(State::CMonitorQueryCore{monitors}).id(1).run(), queryable(second));
|
||||
}
|
||||
|
||||
TEST(MonitorQueryCore, queryByName) {
|
||||
const auto first = testMonitor(0, "DP-1", {0, 0});
|
||||
const auto second = testMonitor(1, "HDMI-A-1", {100, 0});
|
||||
auto monitors = queryables({first, second});
|
||||
|
||||
EXPECT_EQ(std::move(State::CMonitorQueryCore{monitors}).name("HDMI-A-1").run(), queryable(second));
|
||||
}
|
||||
|
||||
TEST(MonitorQueryCore, queryByDescriptionPrefix) {
|
||||
const auto first = testMonitor(0, "DP-1", {0, 0});
|
||||
auto monitors = queryables({first});
|
||||
|
||||
first->m_description = "Dell Inc. ABC";
|
||||
|
||||
EXPECT_EQ(std::move(State::CMonitorQueryCore{monitors}).description("Dell").run(), queryable(first));
|
||||
EXPECT_FALSE(std::move(State::CMonitorQueryCore{monitors}).description("Nope").run());
|
||||
}
|
||||
|
||||
TEST(MonitorQueryCore, queryByVectorInsideBox) {
|
||||
const auto first = testMonitor(0, "DP-1", {0, 0});
|
||||
const auto second = testMonitor(1, "DP-2", {100, 0});
|
||||
auto monitors = queryables({first, second});
|
||||
|
||||
EXPECT_EQ(std::move(State::CMonitorQueryCore{monitors}).vec({150, 50}).run(), queryable(second));
|
||||
}
|
||||
|
||||
TEST(MonitorQueryCore, closestMonitorForVectorOutsideAllBoxes) {
|
||||
const auto first = testMonitor(0, "DP-1", {0, 0});
|
||||
const auto second = testMonitor(1, "DP-2", {300, 0});
|
||||
auto monitors = queryables({first, second});
|
||||
|
||||
EXPECT_EQ(std::move(State::CMonitorQueryCore{monitors}).vec({250, 50}).run(), queryable(second));
|
||||
}
|
||||
|
||||
TEST(MonitorQueryCore, directionLookupRight) {
|
||||
const auto first = testMonitor(0, "DP-1", {0, 0});
|
||||
const auto second = testMonitor(1, "DP-2", {100, 0});
|
||||
auto monitors = queryables({first, second});
|
||||
|
||||
EXPECT_EQ(std::move(State::CMonitorQueryCore{monitors}).relativeTo(queryable(first)).inDirection(Math::DIRECTION_RIGHT).run(), queryable(second));
|
||||
}
|
||||
|
||||
TEST(MonitorQueryCore, directionLookupChoosesLongestIntersection) {
|
||||
const auto reference = testMonitor(0, "reference", {0, 0}, {100, 100});
|
||||
const auto small = testMonitor(1, "small", {100, 80}, {100, 20});
|
||||
const auto large = testMonitor(2, "large", {100, 0}, {100, 60});
|
||||
auto monitors = queryables({reference, small, large});
|
||||
|
||||
EXPECT_EQ(std::move(State::CMonitorQueryCore{monitors}).relativeTo(queryable(reference)).inDirection(Math::DIRECTION_RIGHT).run(), queryable(large));
|
||||
}
|
||||
|
||||
TEST(MonitorQueryCore, configCurrentReturnsRelativeMonitor) {
|
||||
const auto first = testMonitor(0, "DP-1", {0, 0});
|
||||
const auto second = testMonitor(1, "DP-2", {100, 0});
|
||||
auto monitors = queryables({first, second});
|
||||
|
||||
EXPECT_EQ(std::move(State::CMonitorQueryCore{monitors}).relativeTo(queryable(second)).configString("current").run(), queryable(second));
|
||||
}
|
||||
|
||||
TEST(MonitorQueryCore, configDirectionUsesRelativeMonitor) {
|
||||
const auto first = testMonitor(0, "DP-1", {0, 0});
|
||||
const auto second = testMonitor(1, "DP-2", {100, 0});
|
||||
auto monitors = queryables({first, second});
|
||||
|
||||
EXPECT_EQ(std::move(State::CMonitorQueryCore{monitors}).relativeTo(queryable(first)).configString("r").run(), queryable(second));
|
||||
}
|
||||
|
||||
TEST(MonitorQueryCore, configRelativePositiveWraps) {
|
||||
const auto first = testMonitor(0, "DP-1", {0, 0});
|
||||
const auto second = testMonitor(1, "DP-2", {100, 0});
|
||||
const auto third = testMonitor(2, "DP-3", {200, 0});
|
||||
auto monitors = queryables({first, second, third});
|
||||
|
||||
EXPECT_EQ(std::move(State::CMonitorQueryCore{monitors}).relativeTo(queryable(second)).configString("+1").run(), queryable(third));
|
||||
EXPECT_EQ(std::move(State::CMonitorQueryCore{monitors}).relativeTo(queryable(third)).configString("+1").run(), queryable(first));
|
||||
}
|
||||
|
||||
TEST(MonitorQueryCore, configRelativeNegativeWraps) {
|
||||
const auto first = testMonitor(0, "DP-1", {0, 0});
|
||||
const auto second = testMonitor(1, "DP-2", {100, 0});
|
||||
const auto third = testMonitor(2, "DP-3", {200, 0});
|
||||
auto monitors = queryables({first, second, third});
|
||||
|
||||
EXPECT_EQ(std::move(State::CMonitorQueryCore{monitors}).relativeTo(queryable(first)).configString("-1").run(), queryable(third));
|
||||
}
|
||||
|
||||
TEST(MonitorQueryCore, configNumericId) {
|
||||
const auto first = testMonitor(0, "DP-1", {0, 0});
|
||||
const auto second = testMonitor(1, "DP-2", {100, 0});
|
||||
auto monitors = queryables({first, second});
|
||||
|
||||
EXPECT_EQ(std::move(State::CMonitorQueryCore{monitors}).configString("1").run(), queryable(second));
|
||||
}
|
||||
|
||||
TEST(MonitorQueryCore, configSelectorSkipsNoOutput) {
|
||||
const auto withoutOutput = testMonitor(0, "DP-1", {0, 0});
|
||||
const auto withOutput = testMonitor(1, "DP-1", {100, 0});
|
||||
auto monitors = queryables({withoutOutput, withOutput});
|
||||
|
||||
withoutOutput->m_hasOutput = false;
|
||||
|
||||
EXPECT_EQ(std::move(State::CMonitorQueryCore{monitors}).configString("DP-1").run(), queryable(withOutput));
|
||||
}
|
||||
|
||||
TEST(MonitorQueryCore, selectorQueryUsesInterfaceMatcher) {
|
||||
const auto first = testMonitor(0, "DP-1", {0, 0});
|
||||
const auto second = testMonitor(1, "DP-2", {100, 0});
|
||||
auto monitors = queryables({first, second});
|
||||
|
||||
first->m_selectorResult = false;
|
||||
second->m_selectorResult = true;
|
||||
|
||||
EXPECT_EQ(std::move(State::CMonitorQueryCore{monitors}).selector("anything").run(), queryable(second));
|
||||
}
|
||||
Reference in New Issue
Block a user