Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add per-window content scaling #9428

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
6 changes: 4 additions & 2 deletions src/Compositor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1015,11 +1015,13 @@ SP<CWLSurfaceResource> CCompositor::vectorWindowToSurface(const Vector2D& pos, P

if (PPOPUP) {
const auto OFF = PPOPUP->coordsRelativeToParent();
sl = pos - pWindow->m_vRealPosition->goal() - OFF;
// The offset of the popup is in scaled coords (as the window surface has its contents scaled),
// but the popup doesn't have the surface scaled.
sl = (pos - pWindow->m_vRealPosition->goal()) * PPOPUP->getContentScale() - OFF;
return PPOPUP->m_pWLSurface->resource();
}

auto [surf, local] = pWindow->m_pWLSurface->resource()->at(pos - pWindow->m_vRealPosition->goal(), true);
auto [surf, local] = pWindow->m_pWLSurface->resource()->at(pos - pWindow->m_vRealPosition->goal(), true, pWindow->getContentScale());
if (surf) {
sl = local;
return surf;
Expand Down
28 changes: 24 additions & 4 deletions src/desktop/Popup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ void CPopup::onMap() {
const auto PMONITOR = g_pCompositor->getMonitorFromVector(COORDS);

CBox box = m_pWLSurface->resource()->extends();
box.scale(1.0 / getContentScale());
box.translate(COORDS).expand(4);
g_pHyprRenderer->damageBox(box);

Expand Down Expand Up @@ -132,6 +133,7 @@ void CPopup::onUnmap() {
const auto COORDS = coordsGlobal();

CBox box = m_pWLSurface->resource()->extends();
box.scale(1.0 / getContentScale());
box.translate(COORDS).expand(4);
g_pHyprRenderer->damageBox(box);

Expand All @@ -147,6 +149,7 @@ void CPopup::onUnmap() {
return;

auto box = CBox{p->coordsGlobal(), p->size()};
box.scale(1.0 / p->getContentScale());
g_pHyprRenderer->damageBox(box);
},
nullptr);
Expand Down Expand Up @@ -186,7 +189,7 @@ void CPopup::onCommit(bool ignoreSiblings) {
const auto COORDSLOCAL = coordsRelativeToParent();

if (m_vLastSize != m_pResource->surface->surface->current.size || m_bRequestedReposition || m_vLastPos != COORDSLOCAL) {
CBox box = {localToGlobal(m_vLastPos), m_vLastSize};
CBox box = {localToGlobal(m_vLastPos), m_vLastSize / getContentScale()};
g_pHyprRenderer->damageBox(box);
m_vLastSize = m_pResource->surface->surface->current.size;
box = {COORDS, m_vLastSize};
Expand All @@ -198,7 +201,7 @@ void CPopup::onCommit(bool ignoreSiblings) {
if (!ignoreSiblings && m_pSubsurfaceHead)
m_pSubsurfaceHead->recheckDamageForSubsurfaces();

g_pHyprRenderer->damageSurface(m_pWLSurface->resource(), COORDS.x, COORDS.y);
g_pHyprRenderer->damageSurface(m_pWLSurface->resource(), COORDS.x, COORDS.y, 1.0 / getContentScale());

m_bRequestedReposition = false;

Expand All @@ -223,7 +226,11 @@ void CPopup::reposition() {
if (!PMONITOR)
return;

CBox box = {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
CBox box = {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
const auto scale = getContentScale();
box.w = COORDS.x + (box.w - COORDS.x) * scale;
box.h = COORDS.y + (box.h - COORDS.y) * scale;

m_pResource->applyPositioning(box, COORDS);
}

Expand Down Expand Up @@ -259,7 +266,11 @@ Vector2D CPopup::coordsGlobal() {
}

Vector2D CPopup::localToGlobal(const Vector2D& rel) {
return t1ParentCoords() + rel;
const auto coords = t1ParentCoords();
if (!m_pWindowOwner.expired())
return coords + rel / m_pWindowOwner->getContentScale();

return coords + rel;
}

Vector2D CPopup::t1ParentCoords() {
Expand All @@ -272,6 +283,13 @@ Vector2D CPopup::t1ParentCoords() {
return {};
}

float CPopup::getContentScale() const {
if (!m_pWindowOwner.expired())
return m_pWindowOwner->getContentScale();

return 1.0f;
}

void CPopup::recheckTree() {
WP<CPopup> curr = m_pSelf;
while (curr->m_pParent) {
Expand Down Expand Up @@ -358,6 +376,8 @@ WP<CPopup> CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
if (size == Vector2D{})
size = p->size();

size /= getContentScale();

const auto BOX = CBox{p->coordsGlobal() + offset, size};
if (BOX.containsPoint(globalCoords))
return p;
Expand Down
2 changes: 2 additions & 0 deletions src/desktop/Popup.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class CPopup {
bool visible();
bool inert() const;

float getContentScale() const;

// will also loop over this node
void breadthfirst(std::function<void(WP<CPopup>, void*)> fn, void* data);
WP<CPopup> at(const Vector2D& globalCoords, bool allowsInput = false);
Expand Down
14 changes: 13 additions & 1 deletion src/desktop/Window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,14 @@ void CWindow::applyDynamicRule(const SP<CWindowRule>& r) {
} catch (std::exception& e) { Debug::log(ERR, "minsize rule \"{}\" failed with: {}", r->szRule, e.what()); }
break;
}
case CWindowRule::RULE_CONTENTSCALE: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this shouldn't be needed

try {
const auto SCALE = std::stof(r->szRule.substr(13));
m_sWindowData.contentScale = CWindowOverridableVar(1.0f / SCALE, priority);
sendWindowSize();
} catch (std::exception& e) { Debug::log(ERR, "contentscale rule \"{}\" failed with: {}", r->szRule, e.what()); }
break;
}
case CWindowRule::RULE_RENDERUNFOCUSED: {
m_sWindowData.renderUnfocused = CWindowOverridableVar(true, priority);
g_pHyprRenderer->addWindowToRenderUnfocused(m_pSelf.lock());
Expand Down Expand Up @@ -1229,6 +1237,10 @@ float CWindow::getScrollTouchpad() {
return m_sWindowData.scrollTouchpad.valueOr(*PTOUCHPADSCROLLFACTOR);
}

float CWindow::getContentScale() {
return m_sWindowData.contentScale.valueOr(1.0f);
}

bool CWindow::canBeTorn() {
static auto PTEARING = CConfigValue<Hyprlang::INT>("general:allow_tearing");
return m_sWindowData.tearing.valueOr(m_bTearingHint) && *PTEARING;
Expand Down Expand Up @@ -1761,7 +1773,7 @@ void CWindow::sendWindowSize(bool force) {
// TODO: this should be decoupled from setWindowSize IMO
const auto REPORTPOS = realToReportPosition();

const auto REPORTSIZE = realToReportSize();
const auto REPORTSIZE = realToReportSize() * getContentScale();

if (!force && m_vPendingReportedSize == REPORTSIZE && (m_vReportedPosition == REPORTPOS || !m_bIsX11))
return;
Expand Down
4 changes: 4 additions & 0 deletions src/desktop/Window.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ struct SWindowData {
CWindowOverridableVar<CGradientValueData> activeBorderColor;
CWindowOverridableVar<CGradientValueData> inactiveBorderColor;

CWindowOverridableVar<float> contentScale;

CWindowOverridableVar<bool> persistentSize;
};

Expand Down Expand Up @@ -440,6 +442,7 @@ class CWindow {
int getRealBorderSize();
float getScrollMouse();
float getScrollTouchpad();
float getContentScale();
void updateWindowData();
void updateWindowData(const struct SWorkspaceRule&);
void onBorderAngleAnimEnd(WP<Hyprutils::Animation::CBaseAnimatedVariable> pav);
Expand Down Expand Up @@ -575,6 +578,7 @@ namespace NWindowProperties {
{"roundingpower", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.roundingPower; }},
{"scrollmouse", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.scrollMouse; }},
{"scrolltouchpad", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.scrollTouchpad; }},
{"contentscale", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.contentScale; }},
};
};

Expand Down
4 changes: 3 additions & 1 deletion src/desktop/WindowRule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ static const auto RULES = std::unordered_set<std::string>{
static const auto RULES_PREFIX = std::unordered_set<std::string>{
"animation", "bordercolor", "bordersize", "center", "content", "fullscreenstate", "group", "idleinhibit", "maxsize", "minsize",
"monitor", "move", "opacity", "plugin:", "prop", "pseudo", "rounding", "roundingpower", "scrollmouse", "scrolltouchpad",
"size", "suppressevent", "tag", "workspace", "xray",
"size", "suppressevent", "tag", "workspace", "xray", "contentscale",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this shouldn't be needed, props are already being searched
(and same for rounding roundingpower scrollmouse scrolltouchpad it seems)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this because like you saw..."everybody is doing it, why don't we". I thought maybe there is some need or plan to have every property listed there, even if they don't get accessed this way. Do I remove it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you're using a prop (NWindowProperties) it's already checked for you

};

CWindowRule::CWindowRule(const std::string& rule, const std::string& value, bool isV2, bool isExecRule) : szValue(value), szRule(rule), v2(isV2), execRule(isExecRule) {
Expand Down Expand Up @@ -57,6 +57,8 @@ CWindowRule::CWindowRule(const std::string& rule, const std::string& value, bool
ruleType = RULE_MAXSIZE;
else if (rule.starts_with("minsize"))
ruleType = RULE_MINSIZE;
else if (rule.starts_with("contentscale"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same thing here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the property needs to be here, because it needs special treatment. Like you mention below, it needs to invert the value and also call sendWindowSize() to update the client's resolution. Adding an if below is in my opinion, less clean than stating the property is "special" in a way. In the special code (in src/desktop/Window.cpp), aside from inversion and updating the client, we could also add checks for the range of the value if wanted (as there are in the dispatcher, suggested by @vaxerski

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah it does if you're using your custom rule type, but like I said bellow this should use the default treatment if you're using a prop (NWindowProperties)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, since you are using a prop it's handled for you

ruleType = RULE_CONTENTSCALE;
else if (rule.starts_with("monitor"))
ruleType = RULE_MONITOR;
else if (rule.starts_with("move"))
Expand Down
3 changes: 2 additions & 1 deletion src/desktop/WindowRule.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class CWindowRule {
RULE_IDLEINHIBIT,
RULE_MAXSIZE,
RULE_MINSIZE,
RULE_CONTENTSCALE,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nor this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what is your suggestion? It is the only way I found to be able to update the client's resolution, invert the scale etc. It seems to me a generic floating point property doesn't allow me to do those things.

RULE_MONITOR,
RULE_MOVE,
RULE_OPACITY,
Expand Down Expand Up @@ -68,4 +69,4 @@ class CWindowRule {
CRuleRegexContainer rInitialTitle;
CRuleRegexContainer rInitialClass;
CRuleRegexContainer rV1Regex;
};
};
39 changes: 39 additions & 0 deletions src/managers/KeybindManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ CKeybindManager::CKeybindManager() {
m_mDispatchers["togglespecialworkspace"] = toggleSpecialWorkspace;
m_mDispatchers["forcerendererreload"] = forceRendererReload;
m_mDispatchers["resizeactive"] = resizeActive;
m_mDispatchers["scaleactive"] = scaleActive;
m_mDispatchers["moveactive"] = moveActive;
m_mDispatchers["cyclenext"] = circleNext;
m_mDispatchers["focuswindowbyclass"] = focusWindow;
Expand Down Expand Up @@ -2148,6 +2149,41 @@ SDispatchResult CKeybindManager::resizeActive(std::string args) {
return {};
}

SDispatchResult CKeybindManager::scaleActive(std::string args) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't the setprop dispatcher be used instead of making a new one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dispatcher allows absolute and delta increases, as requested above.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#9566 would allow that for all props

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, so we will wait for that to happen.

const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();

if (!PLASTWINDOW)
return {.success = false, .error = "Window not found"};

std::optional<float> scaleResult;
bool exact = false;

if (args.starts_with("exact")) {
exact = true;
scaleResult = getPlusMinusKeywordResult(args.substr(5), 0);
} else
scaleResult = getPlusMinusKeywordResult(args, 0);

if (!scaleResult.has_value()) {
Debug::log(ERR, "Invalid arg \"{}\" in scaleactive!", args);
return {.success = false, .error = "Invalid scale argument in scaleactive!"};
}

float scale = scaleResult.value();
if (!exact)
scale += 1.0 / PLASTWINDOW->m_sWindowData.contentScale.valueOr(1.0f);

if (scale > 0.0f) {
scale = std::max(scale, 0.25f);
PLASTWINDOW->m_sWindowData.contentScale = CWindowOverridableVar(1.0f / scale, PRIORITY_SET_PROP);
} else
PLASTWINDOW->m_sWindowData.contentScale.unset(PRIORITY_SET_PROP);

PLASTWINDOW->sendWindowSize();

return {};
}

SDispatchResult CKeybindManager::moveActive(std::string args) {
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();

Expand Down Expand Up @@ -3135,6 +3171,9 @@ SDispatchResult CKeybindManager::setProp(std::string args) {
PWINDOW->m_sWindowData.minSize = CWindowOverridableVar(configStringToVector2D(VAL), PRIORITY_SET_PROP);
PWINDOW->clampWindowSize(PWINDOW->m_sWindowData.minSize.value(), std::nullopt);
PWINDOW->setHidden(false);
} else if (PROP == "contentscale") {
PWINDOW->m_sWindowData.contentScale = CWindowOverridableVar(1.0f / std::stof(VAL), PRIORITY_SET_PROP);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should also use the already existing window prop code to support unset and avoid duplication
you won't be able to store the inverted value doe
as for the PWINDOW->sendWindowSize(); maybe add a separate if check at the end just for it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we need to agree on whether this property is "special" like the others in the if statements or not.

When I implemented it at the beginning, I tried to add it to the list, like you suggest. But then I saw I couldn't do the things I needed if I added it there. I saw other properties had the same problem, and I followed their process.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a point in inverting the value here
just invert the value when reading/writing or just make the actual internal value what the user will input

as for adding an if, I don't know how heavy that function is
but the best is probably just adding it at the end where all the recalculation is being done, if not inside recalculateWindow();, which should be called at the end by g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The input value is chosen to be similar to fractional scale so the user has a better intuition on how to use the feature, but then internally it is easier to manage it inverted. That is the only reason, and inverting it just here is easier and more efficient than having to do it every time the value is read or written, or so I thought.

PWINDOW->sendWindowSize();
} else if (PROP == "alpha") {
PWINDOW->m_sWindowData.alpha = CWindowOverridableVar(SAlphaValue{std::stof(VAL), PWINDOW->m_sWindowData.alpha.valueOrDefault().m_bOverride}, PRIORITY_SET_PROP);
} else if (PROP == "alphainactive") {
Expand Down
1 change: 1 addition & 0 deletions src/managers/KeybindManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ class CKeybindManager {
static SDispatchResult toggleSpecialWorkspace(std::string);
static SDispatchResult forceRendererReload(std::string);
static SDispatchResult resizeActive(std::string);
static SDispatchResult scaleActive(std::string);
static SDispatchResult moveActive(std::string);
static SDispatchResult moveWindow(std::string);
static SDispatchResult resizeWindow(std::string);
Expand Down
5 changes: 3 additions & 2 deletions src/managers/input/InputManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,13 +456,14 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse) {

m_bEmptyFocusCursorSet = false;

Vector2D surfaceLocal = surfacePos == Vector2D(-1337, -1337) ? surfaceCoords : mouseCoords - surfacePos;
const Vector2D relMouseCoords = mouseCoords - surfacePos;
Vector2D surfaceLocal = surfacePos == Vector2D(-1337, -1337) ? surfaceCoords : (pFoundWindow ? relMouseCoords * pFoundWindow->getContentScale() : relMouseCoords);

if (pFoundWindow && !pFoundWindow->m_bIsX11 && surfacePos != Vector2D(-1337, -1337)) {
// calc for oversized windows... fucking bullshit.
CBox geom = pFoundWindow->m_pXDGSurface->current.geometry;

surfaceLocal = mouseCoords - surfacePos + geom.pos();
surfaceLocal = (pFoundWindow ? relMouseCoords * pFoundWindow->getContentScale() : relMouseCoords) + geom.pos();
}

if (pFoundWindow && pFoundWindow->m_bIsX11) // for x11 force scale zero
Expand Down
10 changes: 5 additions & 5 deletions src/protocols/core/Compositor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,19 +323,19 @@ SP<CWLSurfaceResource> CWLSurfaceResource::findFirstPreorder(std::function<bool(
return findFirstPreorderHelper(self.lock(), fn);
}

std::pair<SP<CWLSurfaceResource>, Vector2D> CWLSurfaceResource::at(const Vector2D& localCoords, bool allowsInput) {
std::pair<SP<CWLSurfaceResource>, Vector2D> CWLSurfaceResource::at(const Vector2D& localCoords, bool allowsInput, double scale) {
std::vector<std::pair<SP<CWLSurfaceResource>, Vector2D>> surfs;
breadthfirst([&surfs](SP<CWLSurfaceResource> surf, const Vector2D& offset, void* data) { surfs.emplace_back(std::make_pair<>(surf, offset)); }, &surfs);

for (auto const& [surf, pos] : surfs | std::views::reverse) {
if (!allowsInput) {
const auto BOX = CBox{pos, surf->current.size};
if (BOX.containsPoint(localCoords))
return {surf, localCoords - pos};
if (BOX.containsPoint(localCoords * scale))
return {surf, localCoords * scale - pos};
} else {
const auto REGION = surf->current.input.copy().intersect(CBox{{}, surf->current.size}).translate(pos);
if (REGION.containsPoint(localCoords))
return {surf, localCoords - pos};
if (REGION.containsPoint(localCoords * scale))
return {surf, localCoords * scale - pos};
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/protocols/core/Compositor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class CWLSurfaceResource {

// returns a pair: found surface (null if not found) and surface local coords.
// localCoords param is relative to 0,0 of this surface
std::pair<SP<CWLSurfaceResource>, Vector2D> at(const Vector2D& localCoords, bool allowsInput = false);
std::pair<SP<CWLSurfaceResource>, Vector2D> at(const Vector2D& localCoords, bool allowsInput = false, double scale = 1.0);

private:
SP<CWlSurface> resource;
Expand Down
Loading
Loading