monitor: refactor inheritance, expand query, add unit tests for position and query (#15073)

This commit is contained in:
Vaxry
2026-06-11 17:24:15 +01:00
committed by GitHub
parent fed8884d21
commit 4d7ae07549
17 changed files with 1193 additions and 325 deletions
+10 -103
View File
@@ -26,6 +26,7 @@
#include "managers/permissions/DynamicPermissionManager.hpp"
#include "managers/screenshare/ScreenshareManager.hpp"
#include "state/FallbackState.hpp"
#include "state/MonitorPositionController.hpp"
#include "state/MonitorState.hpp"
#include <algorithm>
#include <aquamarine/output/Output.hpp>
@@ -2541,112 +2542,18 @@ void CCompositor::checkMonitorOverlaps() {
}
void CCompositor::arrangeMonitors() {
static auto PXWLFORCESCALEZERO = CConfigValue<Config::INTEGER>("xwayland:force_zero_scaling");
static auto PXWLFORCESCALEZERO = CConfigValue<Config::INTEGER>("xwayland:force_zero_scaling");
std::vector<PHLMONITOR> toArrange(State::monitorState()->monitors().begin(), State::monitorState()->monitors().end());
std::vector<PHLMONITOR> arranged;
arranged.reserve(toArrange.size());
Log::logger->log(Log::DEBUG, "arrangeMonitors: {} to arrange", toArrange.size());
for (auto it = toArrange.begin(); it != toArrange.end();) {
auto m = *it;
if (m->m_activeMonitorRule.m_offset != Vector2D{-INT32_MAX, -INT32_MAX}) {
// explicit.
Log::logger->log(Log::DEBUG, "arrangeMonitors: {} explicit {:j}", m->m_name, m->m_activeMonitorRule.m_offset);
m->moveTo(m->m_activeMonitorRule.m_offset);
arranged.push_back(m);
it = toArrange.erase(it);
if (it == toArrange.end())
break;
continue;
}
++it;
std::vector<SP<Monitor::IMonitorArrangeable>> arrangeableMonitors;
arrangeableMonitors.reserve(State::monitorState()->monitors().size());
for (const auto& m : State::monitorState()->monitors()) {
auto arrangeable = dynamicPointerCast<Monitor::IMonitorArrangeable>(m);
RASSERT(arrangeable, "CMonitor does not implement IMonitorArrangeable");
arrangeableMonitors.push_back(arrangeable);
}
// Variables to store the max and min values of monitors on each axis.
int maxXOffsetRight = 0;
int maxXOffsetLeft = 0;
int maxYOffsetUp = 0;
int maxYOffsetDown = 0;
auto recalcMaxOffsets = [&]() {
maxXOffsetRight = 0;
maxXOffsetLeft = 0;
maxYOffsetUp = 0;
maxYOffsetDown = 0;
// Finds the max and min values of explicitly placed monitors.
for (auto const& m : arranged) {
maxXOffsetRight = std::max<double>(m->m_position.x + m->m_size.x, maxXOffsetRight);
maxXOffsetLeft = std::min<double>(m->m_position.x, maxXOffsetLeft);
maxYOffsetDown = std::max<double>(m->m_position.y + m->m_size.y, maxYOffsetDown);
maxYOffsetUp = std::min<double>(m->m_position.y, maxYOffsetUp);
}
};
// Iterates through all non-explicitly placed monitors.
for (auto const& m : toArrange) {
recalcMaxOffsets();
// Moves the monitor to their appropriate position on the x/y axis and
// increments/decrements the corresponding max offset.
Vector2D newPosition = {0, 0};
switch (m->m_activeMonitorRule.m_autoDir) {
case Config::eAutoDirs::DIR_AUTO_UP: newPosition.y = maxYOffsetUp - m->m_size.y; break;
case Config::eAutoDirs::DIR_AUTO_DOWN: newPosition.y = maxYOffsetDown; break;
case Config::eAutoDirs::DIR_AUTO_LEFT: newPosition.x = maxXOffsetLeft - m->m_size.x; break;
case Config::eAutoDirs::DIR_AUTO_RIGHT:
case Config::eAutoDirs::DIR_AUTO_NONE: newPosition.x = maxXOffsetRight; break;
case Config::eAutoDirs::DIR_AUTO_CENTER_UP: {
int width = maxXOffsetRight - maxXOffsetLeft;
newPosition.y = maxYOffsetUp - m->m_size.y;
newPosition.x = maxXOffsetLeft + (width - m->m_size.x) / 2;
break;
}
case Config::eAutoDirs::DIR_AUTO_CENTER_DOWN: {
int width = maxXOffsetRight - maxXOffsetLeft;
newPosition.y = maxYOffsetDown;
newPosition.x = maxXOffsetLeft + (width - m->m_size.x) / 2;
break;
}
case Config::eAutoDirs::DIR_AUTO_CENTER_LEFT: {
int height = maxYOffsetDown - maxYOffsetUp;
newPosition.x = maxXOffsetLeft - m->m_size.x;
newPosition.y = maxYOffsetUp + (height - m->m_size.y) / 2;
break;
}
case Config::eAutoDirs::DIR_AUTO_CENTER_RIGHT: {
int height = maxYOffsetDown - maxYOffsetUp;
newPosition.x = maxXOffsetRight;
newPosition.y = maxYOffsetUp + (height - m->m_size.y) / 2;
break;
}
default: UNREACHABLE();
}
Log::logger->log(Log::DEBUG, "arrangeMonitors: {} auto {:j}", m->m_name, m->m_position);
m->moveTo(newPosition);
arranged.emplace_back(m);
}
// reset maxXOffsetRight (reuse)
// and set xwayland positions aka auto for all
maxXOffsetRight = 0;
for (auto const& m : State::monitorState()->monitors()) {
Log::logger->log(Log::DEBUG, "arrangeMonitors: {} xwayland [{}, {}]", m->m_name, maxXOffsetRight, 0);
m->m_xwaylandPosition = {maxXOffsetRight, 0};
maxXOffsetRight += (*PXWLFORCESCALEZERO ? m->m_transformedSize.x : m->m_size.x);
if (*PXWLFORCESCALEZERO)
m->m_xwaylandScale = m->m_scale;
else
m->m_xwaylandScale = 1.f;
}
Log::logger->log(Log::DEBUG, "arrangeMonitors: {} to arrange", arrangeableMonitors.size());
State::monitorPositionController()->arrange(arrangeableMonitors, *PXWLFORCESCALEZERO);
PROTO::xdgOutput->updateAllOutputs();
Event::bus()->m_events.monitor.layoutChanged.emit();
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include "IMonitorIdentifiable.hpp"
#include "IMonitorMutableGeometry.hpp"
#include <cstdint>
#include <optional>
namespace Config {
enum eAutoDirs : uint8_t;
}
namespace Monitor {
class IMonitorArrangeable : public virtual IMonitorIdentifiable, public virtual IMonitorMutableGeometry {
public:
virtual std::optional<Vector2D> explicitPosition() const = 0;
virtual Config::eAutoDirs autoDirection() const = 0;
virtual Vector2D xwaylandPosition() const = 0;
virtual float xwaylandScale() const = 0;
virtual void setXWaylandPosition(const Vector2D& pos) = 0;
virtual void setXWaylandScale(float scale) = 0;
};
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include "../SharedDefs.hpp"
namespace Monitor {
class IMonitorGeometry {
public:
virtual ~IMonitorGeometry() = default;
virtual Vector2D position() const = 0;
virtual Vector2D size() const = 0;
virtual Vector2D pixelSize() const = 0;
virtual Vector2D transformedSize() const = 0;
virtual float scale() const = 0;
virtual Hyprutils::Math::eTransform transform() const = 0;
virtual CBox logicalBox() const = 0;
virtual CBox logicalBoxMinusReserved() const = 0;
virtual Vector2D middle() const = 0;
};
}
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include "../SharedDefs.hpp"
#include <string_view>
namespace Monitor {
class IMonitorIdentifiable {
public:
virtual ~IMonitorIdentifiable() = default;
virtual MONITORID id() const = 0;
virtual std::string_view name() const = 0;
virtual std::string_view description() const = 0;
virtual std::string_view shortDescription() const = 0;
virtual bool matchesStaticSelector(std::string_view selector) const = 0;
};
}
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include "IMonitorGeometry.hpp"
namespace Monitor {
class IMonitorMutableGeometry : public virtual IMonitorGeometry {
public:
virtual void moveTo(const Vector2D& pos) = 0;
};
}
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include "IMonitorGeometry.hpp"
#include "IMonitorIdentifiable.hpp"
#include "../helpers/memory/Memory.hpp"
namespace Aquamarine {
class IOutput;
}
namespace Monitor {
class IMonitorQueryable : public virtual IMonitorIdentifiable, public virtual IMonitorGeometry {
public:
virtual bool enabled() const = 0;
virtual bool hasOutput() const = 0;
virtual SP<Aquamarine::IOutput> output() const = 0;
};
}
+82 -3
View File
@@ -1162,6 +1162,22 @@ bool CMonitor::isMirror() {
return m_mirrorOf != nullptr;
}
MONITORID CMonitor::id() const {
return m_id;
}
std::string_view CMonitor::name() const {
return m_name;
}
std::string_view CMonitor::description() const {
return m_description;
}
std::string_view CMonitor::shortDescription() const {
return m_shortDescription;
}
bool CMonitor::matchesStaticSelector(std::string_view selector) const {
if (selector.starts_with("desc:")) {
// match by description
@@ -1174,6 +1190,69 @@ bool CMonitor::matchesStaticSelector(std::string_view selector) const {
}
}
Vector2D CMonitor::position() const {
return m_position;
}
Vector2D CMonitor::size() const {
return m_size;
}
Vector2D CMonitor::pixelSize() const {
return m_pixelSize;
}
Vector2D CMonitor::transformedSize() const {
return m_transformedSize;
}
float CMonitor::scale() const {
return m_scale;
}
Hyprutils::Math::eTransform CMonitor::transform() const {
return Math::wlTransformToHyprutils(m_transform);
}
bool CMonitor::enabled() const {
return m_enabled;
}
bool CMonitor::hasOutput() const {
return m_output;
}
SP<Aquamarine::IOutput> CMonitor::output() const {
return m_output;
}
std::optional<Vector2D> CMonitor::explicitPosition() const {
if (m_activeMonitorRule.m_offset == Vector2D{-INT32_MAX, -INT32_MAX})
return {};
return m_activeMonitorRule.m_offset;
}
Config::eAutoDirs CMonitor::autoDirection() const {
return m_activeMonitorRule.m_autoDir;
}
Vector2D CMonitor::xwaylandPosition() const {
return m_xwaylandPosition;
}
float CMonitor::xwaylandScale() const {
return m_xwaylandScale;
}
void CMonitor::setXWaylandPosition(const Vector2D& pos) {
m_xwaylandPosition = pos;
}
void CMonitor::setXWaylandScale(float scale_) {
m_xwaylandScale = scale_;
}
WORKSPACEID CMonitor::findAvailableDefaultWS() {
for (WORKSPACEID i = 1; i < LONG_MAX; ++i) {
if (g_pCompositor->getWorkspaceByID(i))
@@ -1609,7 +1688,7 @@ void CMonitor::moveTo(const Vector2D& pos) {
}
}
Vector2D CMonitor::middle() {
Vector2D CMonitor::middle() const {
return m_position + m_size / 2.f;
}
@@ -1637,11 +1716,11 @@ WORKSPACEID CMonitor::activeSpecialWorkspaceID() {
return m_activeSpecialWorkspace ? m_activeSpecialWorkspace->m_id : 0;
}
CBox CMonitor::logicalBox() {
CBox CMonitor::logicalBox() const {
return {m_position, m_size};
}
CBox CMonitor::logicalBoxMinusReserved() {
CBox CMonitor::logicalBoxMinusReserved() const {
return m_reservedArea.apply(logicalBox());
}
+30 -7
View File
@@ -9,6 +9,8 @@
#include <array>
#include "../helpers/AnimatedVariable.hpp"
#include "../helpers/CMType.hpp"
#include "IMonitorArrangeable.hpp"
#include "IMonitorQueryable.hpp"
#include "MonitorTypes.hpp"
#include <xf86drmMode.h>
@@ -56,10 +58,10 @@ namespace Monitor {
CMonitor* m_owner = nullptr;
};
class CMonitor {
class CMonitor : public IMonitorQueryable, public IMonitorArrangeable {
public:
CMonitor(SP<Aquamarine::IOutput> output);
~CMonitor();
virtual ~CMonitor() override;
Vector2D m_position = Vector2D(-1, -1); // means unset
Vector2D m_xwaylandPosition = Vector2D(-1, -1); // means unset
@@ -261,18 +263,13 @@ namespace Monitor {
bool shouldSkipScheduleFrameOnMouseEvent();
void setMirror(const std::string&);
bool isMirror();
bool matchesStaticSelector(std::string_view selector) const;
float getDefaultScale();
void changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal = false, bool noMouseMove = false, bool noFocus = false);
void changeWorkspace(const WORKSPACEID& id, bool internal = false, bool noMouseMove = false, bool noFocus = false);
void setSpecialWorkspace(const PHLWORKSPACE& pWorkspace);
void setSpecialWorkspace(const WORKSPACEID& id);
void moveTo(const Vector2D& pos);
Vector2D middle();
WORKSPACEID activeWorkspaceID();
WORKSPACEID activeSpecialWorkspaceID();
CBox logicalBox();
CBox logicalBoxMinusReserved();
void scheduleDone();
uint32_t isSolitaryBlocked(bool full = false);
void recheckSolitary();
@@ -289,6 +286,32 @@ namespace Monitor {
void setDPMS(bool on);
bool shouldUseSoftwareCursors();
// IMonitorQueryable / IMonitorArrangeable
virtual MONITORID id() const override;
virtual std::string_view name() const override;
virtual std::string_view description() const override;
virtual std::string_view shortDescription() const override;
virtual bool matchesStaticSelector(std::string_view selector) const override;
virtual Vector2D position() const override;
virtual Vector2D size() const override;
virtual Vector2D pixelSize() const override;
virtual Vector2D transformedSize() const override;
virtual float scale() const override;
virtual Hyprutils::Math::eTransform transform() const override;
virtual CBox logicalBox() const override;
virtual CBox logicalBoxMinusReserved() const override;
virtual Vector2D middle() const override;
virtual void moveTo(const Vector2D& pos) override;
virtual bool enabled() const override;
virtual bool hasOutput() const override;
virtual SP<Aquamarine::IOutput> output() const override;
virtual std::optional<Vector2D> explicitPosition() const override;
virtual Config::eAutoDirs autoDirection() const override;
virtual Vector2D xwaylandPosition() const override;
virtual float xwaylandScale() const override;
virtual void setXWaylandPosition(const Vector2D& pos) override;
virtual void setXWaylandScale(float scale) override;
//
bool applyMonitorRule(Config::CMonitorRule&& pMonitorRule);
bool applyMonitorRuleSoft(Config::CMonitorRule&& pMonitorRule);
+1 -1
View File
@@ -15,4 +15,4 @@ namespace Monitor {
DIR_AUTO_CENTER_LEFT,
DIR_AUTO_CENTER_RIGHT
};
}
}
+110
View File
@@ -0,0 +1,110 @@
#include "MonitorPositionController.hpp"
#include "../config/shared/monitor/MonitorRule.hpp"
#include "../debug/log/Logger.hpp"
#include "../macros.hpp"
#include <algorithm>
using namespace State;
UP<CMonitorPositionController>& State::monitorPositionController() {
static UP<CMonitorPositionController> p = makeUnique<CMonitorPositionController>();
return p;
}
void CMonitorPositionController::arrange(std::span<const SP<Monitor::IMonitorArrangeable>> monitors, bool xwaylandForceZeroScaling) const {
std::vector<SP<Monitor::IMonitorArrangeable>> toArrange(monitors.begin(), monitors.end());
std::vector<SP<Monitor::IMonitorArrangeable>> arranged;
arranged.reserve(toArrange.size());
for (auto it = toArrange.begin(); it != toArrange.end();) {
auto m = *it;
if (const auto POS = m->explicitPosition(); POS.has_value()) {
Log::logger->log(Log::DEBUG, "arrangeMonitors: {} explicit {:j}", m->name(), *POS);
m->moveTo(*POS);
arranged.push_back(m);
it = toArrange.erase(it);
if (it == toArrange.end())
break;
continue;
}
++it;
}
// Variables to store the max and min values of monitors on each axis.
int maxXOffsetRight = 0;
int maxXOffsetLeft = 0;
int maxYOffsetUp = 0;
int maxYOffsetDown = 0;
auto recalcMaxOffsets = [&]() {
maxXOffsetRight = 0;
maxXOffsetLeft = 0;
maxYOffsetUp = 0;
maxYOffsetDown = 0;
for (auto const& m : arranged) {
maxXOffsetRight = std::max<double>(m->position().x + m->size().x, maxXOffsetRight);
maxXOffsetLeft = std::min<double>(m->position().x, maxXOffsetLeft);
maxYOffsetDown = std::max<double>(m->position().y + m->size().y, maxYOffsetDown);
maxYOffsetUp = std::min<double>(m->position().y, maxYOffsetUp);
}
};
// Iterates through all non-explicitly placed monitors.
for (auto const& m : toArrange) {
recalcMaxOffsets();
Vector2D newPosition = {0, 0};
switch (m->autoDirection()) {
case Config::eAutoDirs::DIR_AUTO_UP: newPosition.y = maxYOffsetUp - m->size().y; break;
case Config::eAutoDirs::DIR_AUTO_DOWN: newPosition.y = maxYOffsetDown; break;
case Config::eAutoDirs::DIR_AUTO_LEFT: newPosition.x = maxXOffsetLeft - m->size().x; break;
case Config::eAutoDirs::DIR_AUTO_RIGHT:
case Config::eAutoDirs::DIR_AUTO_NONE: newPosition.x = maxXOffsetRight; break;
case Config::eAutoDirs::DIR_AUTO_CENTER_UP: {
int width = maxXOffsetRight - maxXOffsetLeft;
newPosition.y = maxYOffsetUp - m->size().y;
newPosition.x = maxXOffsetLeft + (width - m->size().x) / 2;
break;
}
case Config::eAutoDirs::DIR_AUTO_CENTER_DOWN: {
int width = maxXOffsetRight - maxXOffsetLeft;
newPosition.y = maxYOffsetDown;
newPosition.x = maxXOffsetLeft + (width - m->size().x) / 2;
break;
}
case Config::eAutoDirs::DIR_AUTO_CENTER_LEFT: {
int height = maxYOffsetDown - maxYOffsetUp;
newPosition.x = maxXOffsetLeft - m->size().x;
newPosition.y = maxYOffsetUp + (height - m->size().y) / 2;
break;
}
case Config::eAutoDirs::DIR_AUTO_CENTER_RIGHT: {
int height = maxYOffsetDown - maxYOffsetUp;
newPosition.x = maxXOffsetRight;
newPosition.y = maxYOffsetUp + (height - m->size().y) / 2;
break;
}
default: UNREACHABLE();
}
m->moveTo(newPosition);
Log::logger->log(Log::DEBUG, "arrangeMonitors: {} auto {:j}", m->name(), m->position());
arranged.emplace_back(m);
}
maxXOffsetRight = 0;
for (auto const& m : monitors) {
Log::logger->log(Log::DEBUG, "arrangeMonitors: {} xwayland [{}, {}]", m->name(), maxXOffsetRight, 0);
m->setXWaylandPosition({maxXOffsetRight, 0});
maxXOffsetRight += xwaylandForceZeroScaling ? m->transformedSize().x : m->size().x;
m->setXWaylandScale(xwaylandForceZeroScaling ? m->scale() : 1.F);
}
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include "../output/IMonitorArrangeable.hpp"
#include "../helpers/memory/Memory.hpp"
#include <span>
#include <vector>
namespace State {
class CMonitorPositionController {
public:
void arrange(std::span<const SP<Monitor::IMonitorArrangeable>> monitors, bool xwaylandForceZeroScaling) const;
};
UP<CMonitorPositionController>& monitorPositionController();
}
+41 -207
View File
@@ -1,12 +1,11 @@
#include "MonitorQuery.hpp"
#include "MonitorQueryCore.hpp"
#include "MonitorStateTracker.hpp"
#include <utility>
#include <hyprutils/string/String.hpp>
#include <hyprutils/string/Numeric.hpp>
#include <vector>
using namespace State;
using namespace Hyprutils::String;
CMonitorQuery::CMonitorQuery(const IMonitorStateTracker& t) : m_tracker(t) {
;
@@ -63,215 +62,50 @@ CMonitorQuery&& CMonitorQuery::includeDisabled(bool inc) && {
}
PHLMONITOR CMonitorQuery::run() && {
if (m_vec && !m_desc && !m_str && !m_id && !m_name && !m_output && !m_dir) {
// legacy path for closest-monitor
return closestTo(*m_vec);
}
if (m_dir) {
// path for getting a monitor in a direction
return directionLookup(m_relativeTo.value_or(nullptr), *m_dir);
}
if (m_configString) {
// path for getting from a config string
return fromConfigString(*m_configString);
}
std::vector<SP<Monitor::IMonitorQueryable>> queryableMonitors;
queryableMonitors.reserve(mons().size());
for (const auto& m : mons()) {
if (m_desc && !m->m_description.starts_with(*m_desc))
continue;
if (m_name && *m_name != m->m_name)
continue;
if (m_id && *m_id != m->m_id)
continue;
if (m_output && *m_output != m->m_output)
continue;
if (m_vec && !m->logicalBox().containsPoint(*m_vec))
continue;
if (m_str && !m->matchesStaticSelector(*m_str))
continue;
return m;
auto queryable = dynamicPointerCast<Monitor::IMonitorQueryable>(m);
RASSERT(queryable, "CMonitor does not implement IMonitorQueryable");
queryableMonitors.push_back(queryable);
}
return nullptr;
CMonitorQueryCore core{queryableMonitors};
if (m_id)
std::move(core).id(*m_id);
if (m_name)
std::move(core).name(*m_name);
if (m_desc)
std::move(core).description(*m_desc);
if (m_str)
std::move(core).selector(*m_str);
if (m_configString)
std::move(core).configString(*m_configString);
if (m_vec)
std::move(core).vec(*m_vec);
if (m_output)
std::move(core).output(*m_output);
if (m_relativeTo.has_value()) {
SP<Monitor::IMonitorQueryable> relativeTo;
if (*m_relativeTo) {
relativeTo = dynamicPointerCast<Monitor::IMonitorQueryable>(*m_relativeTo);
RASSERT(relativeTo, "CMonitor does not implement IMonitorQueryable");
}
std::move(core).relativeTo(relativeTo);
}
if (m_dir)
std::move(core).inDirection(*m_dir);
const auto RESULT = std::move(core).run();
if (!RESULT)
return nullptr;
auto monitor = dynamicPointerCast<Monitor::CMonitor>(RESULT);
RASSERT(monitor, "CMonitorQueryCore returned a non-CMonitor queryable in CMonitorQuery");
return monitor;
}
const std::vector<PHLMONITOR>& CMonitorQuery::mons() const {
return m_includeDisabled ? m_tracker.allMonitors() : m_tracker.monitors();
}
PHLMONITOR CMonitorQuery::closestTo(const Vector2D& vec) const {
PHLMONITOR mon;
for (auto const& m : mons()) {
if (CBox{m->m_position, m->m_size}.containsPoint(vec)) {
mon = m;
break;
}
}
if (!mon) {
float bestDistance = 0.f;
PHLMONITOR pBestMon;
for (auto const& m : mons()) {
float dist = vecToRectDistanceSquared(vec, m->m_position, m->m_position + m->m_size);
if (dist < bestDistance || !pBestMon) {
bestDistance = dist;
pBestMon = m;
}
}
if (!pBestMon) { // ?????
Log::logger->log(Log::WARN, "CMonitorQuery::closestTo: no close mon???");
return nullptr;
}
return pBestMon;
}
return mon;
}
PHLMONITOR CMonitorQuery::directionLookup(PHLMONITOR ref, Math::eDirection dir) const {
if (!ref)
return nullptr;
const auto POSA = ref->m_position;
const auto SIZEA = ref->m_size;
auto longestIntersect = -1;
PHLMONITOR longestIntersectMonitor = nullptr;
for (auto const& m : mons()) {
if (m == ref)
continue;
const auto POSB = m->m_position;
const auto SIZEB = m->m_size;
switch (dir) {
case Math::DIRECTION_LEFT:
if (STICKS(POSA.x, POSB.x + SIZEB.x)) {
const auto INTERSECTLEN = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
if (INTERSECTLEN > longestIntersect) {
longestIntersect = INTERSECTLEN;
longestIntersectMonitor = m;
}
}
break;
case Math::DIRECTION_RIGHT:
if (STICKS(POSA.x + SIZEA.x, POSB.x)) {
const auto INTERSECTLEN = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
if (INTERSECTLEN > longestIntersect) {
longestIntersect = INTERSECTLEN;
longestIntersectMonitor = m;
}
}
break;
case Math::DIRECTION_UP:
if (STICKS(POSA.y, POSB.y + SIZEB.y)) {
const auto INTERSECTLEN = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
if (INTERSECTLEN > longestIntersect) {
longestIntersect = INTERSECTLEN;
longestIntersectMonitor = m;
}
}
break;
case Math::DIRECTION_DOWN:
if (STICKS(POSA.y + SIZEA.y, POSB.y)) {
const auto INTERSECTLEN = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
if (INTERSECTLEN > longestIntersect) {
longestIntersect = INTERSECTLEN;
longestIntersectMonitor = m;
}
}
break;
default: break;
}
}
if (longestIntersect != -1)
return longestIntersectMonitor;
return nullptr;
}
PHLMONITOR CMonitorQuery::fromConfigString(std::string_view sv) const {
if (sv.empty())
return nullptr;
if (sv == "current")
return m_relativeTo.value_or(nullptr);
else if (!sv.empty() && isDirection(sv[0]))
return directionLookup(m_relativeTo.value_or(nullptr), Math::fromChar(sv[0]));
else if (sv[0] == '+' || sv[0] == '-') {
// relative
if (mons().size() == 1)
return *mons().begin();
const auto OFFSET = sv[0] == '-' ? sv : sv.substr(1);
if (!isNumber(std::string{OFFSET})) {
Log::logger->log(Log::ERR, "Error in CMonitorQuery::fromConfigString: Not a number in relative.");
return nullptr;
}
int offsetLeft = strToNumber<int>(OFFSET).value_or(0);
offsetLeft = offsetLeft < 0 ? -((-offsetLeft) % mons().size()) : offsetLeft % mons().size();
int currentPlace = 0;
for (int i = 0; i < sc<int>(mons().size()); i++) {
if (mons()[i] == m_relativeTo.value_or(nullptr)) {
currentPlace = i;
break;
}
}
currentPlace += offsetLeft;
if (currentPlace < 0)
currentPlace = mons().size() + currentPlace;
else
currentPlace = currentPlace % mons().size();
if (currentPlace != std::clamp(currentPlace, 0, sc<int>(mons().size()) - 1)) {
Log::logger->log(Log::WARN, "Error in CMonitorQuery::fromConfigString: Vaxry's code sucks.");
currentPlace = std::clamp(currentPlace, 0, sc<int>(mons().size()) - 1);
}
return mons()[currentPlace];
} else if (isNumber(std::string{sv})) {
// change by ID
auto monID = strToNumber<MONITORID>(sv);
if (!monID) {
// shouldn't happen but jic
Log::logger->log(Log::ERR, "Error in CMonitorQuery::fromConfigString: invalid num");
return nullptr;
}
if (*monID > -1 && *monID < sc<MONITORID>(mons().size())) {
return CMonitorQuery{m_tracker}.id(*monID).run();
} else {
Log::logger->log(Log::ERR, "Error in CMonitorQuery::fromConfigString: invalid arg 1");
return nullptr;
}
} else {
for (auto const& m : mons()) {
if (!m->m_output)
continue;
if (m->matchesStaticSelector(sv))
return m;
}
}
return nullptr;
}
-4
View File
@@ -36,10 +36,6 @@ namespace State {
PHLMONITOR run() &&;
private:
PHLMONITOR closestTo(const Vector2D& vec) const;
PHLMONITOR directionLookup(PHLMONITOR ref, Math::eDirection dir) const;
PHLMONITOR fromConfigString(std::string_view sv) const;
const std::vector<PHLMONITOR>& mons() const;
std::optional<MONITORID> m_id;
+262
View File
@@ -0,0 +1,262 @@
#include "MonitorQueryCore.hpp"
#include "../debug/log/Logger.hpp"
#include "../helpers/MiscFunctions.hpp"
#include "../macros.hpp"
#include <algorithm>
#include <hyprutils/string/Numeric.hpp>
#include <hyprutils/string/String.hpp>
#include <utility>
using namespace State;
using namespace Hyprutils::String;
CMonitorQueryCore::CMonitorQueryCore(std::span<const SP<Monitor::IMonitorQueryable>> monitors) : m_monitors(monitors) {
;
}
CMonitorQueryCore&& CMonitorQueryCore::id(MONITORID id) && {
m_id = id;
return std::move(*this);
}
CMonitorQueryCore&& CMonitorQueryCore::name(std::string_view name) && {
m_name = name;
return std::move(*this);
}
CMonitorQueryCore&& CMonitorQueryCore::description(std::string_view desc) && {
m_desc = desc;
return std::move(*this);
}
CMonitorQueryCore&& CMonitorQueryCore::selector(std::string_view str) && {
m_str = str;
return std::move(*this);
}
CMonitorQueryCore&& CMonitorQueryCore::configString(std::string_view str) && {
m_configString = str;
return std::move(*this);
}
CMonitorQueryCore&& CMonitorQueryCore::vec(Vector2D vec) && {
m_vec = vec;
return std::move(*this);
}
CMonitorQueryCore&& CMonitorQueryCore::output(SP<Aquamarine::IOutput> output) && {
m_output = output;
return std::move(*this);
}
CMonitorQueryCore&& CMonitorQueryCore::relativeTo(SP<Monitor::IMonitorQueryable> reference) && {
m_relativeTo = reference;
return std::move(*this);
}
CMonitorQueryCore&& CMonitorQueryCore::inDirection(Math::eDirection dir) && {
m_dir = dir;
return std::move(*this);
}
SP<Monitor::IMonitorQueryable> CMonitorQueryCore::run() && {
if (m_vec && !m_desc && !m_str && !m_id && !m_name && !m_output && !m_dir)
return closestTo(*m_vec);
if (m_dir)
return directionLookup(m_relativeTo, *m_dir);
if (m_configString)
return fromConfigString(*m_configString);
for (const auto& m : m_monitors) {
if (m_desc && !m->description().starts_with(*m_desc))
continue;
if (m_name && *m_name != m->name())
continue;
if (m_id && *m_id != m->id())
continue;
if (m_output && *m_output != m->output())
continue;
if (m_vec && !m->logicalBox().containsPoint(*m_vec))
continue;
if (m_str && !m->matchesStaticSelector(*m_str))
continue;
return m;
}
return nullptr;
}
SP<Monitor::IMonitorQueryable> CMonitorQueryCore::closestTo(const Vector2D& vec) const {
SP<Monitor::IMonitorQueryable> mon;
for (auto const& m : m_monitors) {
if (m->logicalBox().containsPoint(vec)) {
mon = m;
break;
}
}
if (!mon) {
float bestDistance = 0.F;
SP<Monitor::IMonitorQueryable> pBestMon;
for (auto const& m : m_monitors) {
const auto BOX = m->logicalBox();
float dist = vecToRectDistanceSquared(vec, BOX.pos(), BOX.pos() + BOX.size());
if (dist < bestDistance || !pBestMon) {
bestDistance = dist;
pBestMon = m;
}
}
if (!pBestMon) {
Log::logger->log(Log::WARN, "CMonitorQueryCore::closestTo: no close mon???");
return nullptr;
}
return pBestMon;
}
return mon;
}
SP<Monitor::IMonitorQueryable> CMonitorQueryCore::directionLookup(SP<Monitor::IMonitorQueryable> ref, Math::eDirection dir) const {
if (!ref)
return nullptr;
const auto POSA = ref->position();
const auto SIZEA = ref->size();
auto longestIntersect = -1;
SP<Monitor::IMonitorQueryable> longestIntersectMonitor = nullptr;
for (auto const& m : m_monitors) {
if (m == ref)
continue;
const auto POSB = m->position();
const auto SIZEB = m->size();
switch (dir) {
case Math::DIRECTION_LEFT:
if (STICKS(POSA.x, POSB.x + SIZEB.x)) {
const auto INTERSECTLEN = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
if (INTERSECTLEN > longestIntersect) {
longestIntersect = INTERSECTLEN;
longestIntersectMonitor = m;
}
}
break;
case Math::DIRECTION_RIGHT:
if (STICKS(POSA.x + SIZEA.x, POSB.x)) {
const auto INTERSECTLEN = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
if (INTERSECTLEN > longestIntersect) {
longestIntersect = INTERSECTLEN;
longestIntersectMonitor = m;
}
}
break;
case Math::DIRECTION_UP:
if (STICKS(POSA.y, POSB.y + SIZEB.y)) {
const auto INTERSECTLEN = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
if (INTERSECTLEN > longestIntersect) {
longestIntersect = INTERSECTLEN;
longestIntersectMonitor = m;
}
}
break;
case Math::DIRECTION_DOWN:
if (STICKS(POSA.y + SIZEA.y, POSB.y)) {
const auto INTERSECTLEN = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
if (INTERSECTLEN > longestIntersect) {
longestIntersect = INTERSECTLEN;
longestIntersectMonitor = m;
}
}
break;
default: break;
}
}
if (longestIntersect != -1)
return longestIntersectMonitor;
return nullptr;
}
SP<Monitor::IMonitorQueryable> CMonitorQueryCore::fromConfigString(std::string_view sv) const {
if (sv.empty())
return nullptr;
if (sv == "current")
return m_relativeTo;
else if (!sv.empty() && isDirection(sv[0]))
return directionLookup(m_relativeTo, Math::fromChar(sv[0]));
else if (sv[0] == '+' || sv[0] == '-') {
if (m_monitors.size() == 1)
return *m_monitors.begin();
const auto OFFSET = sv[0] == '-' ? sv : sv.substr(1);
if (!isNumber(std::string{OFFSET})) {
Log::logger->log(Log::ERR, "Error in CMonitorQueryCore::fromConfigString: Not a number in relative.");
return nullptr;
}
int offsetLeft = strToNumber<int>(OFFSET).value_or(0);
offsetLeft = offsetLeft < 0 ? -((-offsetLeft) % m_monitors.size()) : offsetLeft % m_monitors.size();
int currentPlace = 0;
for (int i = 0; i < sc<int>(m_monitors.size()); i++) {
if (m_monitors[i] == m_relativeTo) {
currentPlace = i;
break;
}
}
currentPlace += offsetLeft;
if (currentPlace < 0)
currentPlace = m_monitors.size() + currentPlace;
else
currentPlace = currentPlace % m_monitors.size();
if (currentPlace != std::clamp(currentPlace, 0, sc<int>(m_monitors.size()) - 1)) {
Log::logger->log(Log::WARN, "Error in CMonitorQueryCore::fromConfigString: Vaxry's code sucks.");
currentPlace = std::clamp(currentPlace, 0, sc<int>(m_monitors.size()) - 1);
}
return m_monitors[currentPlace];
} else if (isNumber(std::string{sv})) {
auto monID = strToNumber<MONITORID>(sv);
if (!monID) {
Log::logger->log(Log::ERR, "Error in CMonitorQueryCore::fromConfigString: invalid num");
return nullptr;
}
if (*monID > -1 && *monID < sc<MONITORID>(m_monitors.size()))
return CMonitorQueryCore{m_monitors}.id(*monID).run();
Log::logger->log(Log::ERR, "Error in CMonitorQueryCore::fromConfigString: invalid arg 1");
return nullptr;
} else {
for (auto const& m : m_monitors) {
if (!m->hasOutput())
continue;
if (m->matchesStaticSelector(sv))
return m;
}
}
return nullptr;
}
+51
View File
@@ -0,0 +1,51 @@
#pragma once
#include "../helpers/math/Direction.hpp"
#include "../helpers/memory/Memory.hpp"
#include "../output/IMonitorQueryable.hpp"
#include <optional>
#include <span>
#include <string_view>
namespace Aquamarine {
class IOutput;
}
namespace State {
class CMonitorQueryCore {
public:
CMonitorQueryCore(std::span<const SP<Monitor::IMonitorQueryable>> monitors);
~CMonitorQueryCore() = default;
CMonitorQueryCore(const CMonitorQueryCore&) = delete;
CMonitorQueryCore(CMonitorQueryCore&) = delete;
CMonitorQueryCore(CMonitorQueryCore&&) = delete;
CMonitorQueryCore&& id(MONITORID id) &&;
CMonitorQueryCore&& name(std::string_view name) &&;
CMonitorQueryCore&& description(std::string_view desc) &&;
CMonitorQueryCore&& selector(std::string_view str) &&;
CMonitorQueryCore&& configString(std::string_view str) &&;
CMonitorQueryCore&& vec(Vector2D vec) &&;
CMonitorQueryCore&& output(SP<Aquamarine::IOutput> output) &&;
CMonitorQueryCore&& inDirection(Math::eDirection dir) &&;
CMonitorQueryCore&& relativeTo(SP<Monitor::IMonitorQueryable> reference) &&;
SP<Monitor::IMonitorQueryable> run() &&;
private:
SP<Monitor::IMonitorQueryable> closestTo(const Vector2D& vec) const;
SP<Monitor::IMonitorQueryable> directionLookup(SP<Monitor::IMonitorQueryable> ref, Math::eDirection dir) const;
SP<Monitor::IMonitorQueryable> fromConfigString(std::string_view sv) const;
std::span<const SP<Monitor::IMonitorQueryable>> m_monitors;
std::optional<MONITORID> m_id;
std::optional<std::string_view> m_name, m_desc, m_str, m_configString;
std::optional<SP<Aquamarine::IOutput>> m_output;
SP<Monitor::IMonitorQueryable> m_relativeTo;
std::optional<Math::eDirection> m_dir;
std::optional<Vector2D> m_vec;
};
}
+254
View File
@@ -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);
}
+247
View File
@@ -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));
}