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:
+10
-103
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "IMonitorGeometry.hpp"
|
||||
|
||||
namespace Monitor {
|
||||
class IMonitorMutableGeometry : public virtual IMonitorGeometry {
|
||||
public:
|
||||
virtual void moveTo(const Vector2D& pos) = 0;
|
||||
};
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -15,4 +15,4 @@ namespace Monitor {
|
||||
DIR_AUTO_CENTER_LEFT,
|
||||
DIR_AUTO_CENTER_RIGHT
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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