-
-
Notifications
You must be signed in to change notification settings - Fork 1k
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
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some general design remarks:
- consider adding a window rule for this (see prop window rules) then the dispatcher would be to override it.
- we're adding the conentScale param everywhere - no better method? in most cases, it could be read from the window param.
No style nits for now, general design needs some work
Thank you for your prompt review. I have added the window rule and modified the dispatcher to only override it. I think I am not missing anything, but please let me know if you find something strange. The only difference with other WindowData properties is this one needs to trigger a sendWindowSize() call every time the property changes, so I had to create a specific entry in the window rule check instead of relying on the default for properties. About having to add
This function can be called for any type of surface, either attached to a window or to a layer, I don't think I can remove the parameter.
This function is called by I don't think I have added any more parameters to functions, but let me know if I am mistaken or any of those can be removed somehow I haven't figured out. Thanks! |
Would love to see the ability to use a relative float such as +0.2, similar to what the splitratio dispatcher accepts. That way you could bind + and - to scale a window. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as someone else pointed out, relative scaling for the dispatcher would be a nice addition
OK, done. I have one question. I want to try to fix the bug I mentioned about the application thinking the window doesn't fit in the viewport and locating some tooltips in the "wrong" position. Where is the code that sends monitor information to the applications? I found how to send window sizes (sendWindowSize() sends an Ack request through the protocol), but I haven't found how monitor sizes are communicated. |
they aren't, it's xdg_positioner. Good luck with getting that to work. Check |
Everything seems to be working now for Wayland windows. I am going to see if XWayland can be adapted too. Scaling is OK, popups are not, but I haven't tried anything yet. |
I see XWayland popups are "regular" floating windows, and don't keep any reference to a parent. I see there is a So, current status: It seems everything is working for Wayland windows: popups and not. For X windows, scaling works, but popups render at the wrong location. Anything else? |
for X11, I'd say: I'll test this MR a bit tomorrow, if I don't forget. |
Cool. I think this may close #3861 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
res ok
Do we want this or should I close it? |
sorry, just busy and I get tons of notifs. We do, just fix the conflict pls |
@@ -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", |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
@@ -55,6 +55,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")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same thing here
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
)
There was a problem hiding this comment.
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
@@ -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); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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);
There was a problem hiding this comment.
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.
@@ -2148,6 +2149,41 @@ SDispatchResult CKeybindManager::resizeActive(std::string args) { | |||
return {}; | |||
} | |||
|
|||
SDispatchResult CKeybindManager::scaleActive(std::string args) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
@@ -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: { |
There was a problem hiding this comment.
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
@@ -26,6 +26,7 @@ class CWindowRule { | |||
RULE_IDLEINHIBIT, | |||
RULE_MAXSIZE, | |||
RULE_MINSIZE, | |||
RULE_CONTENTSCALE, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nor this
There was a problem hiding this comment.
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.
here's an example diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp
index e5924b81..487302c7 100644
--- a/src/desktop/Window.cpp
+++ b/src/desktop/Window.cpp
@@ -769,14 +769,6 @@ 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: {
- 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());
@@ -835,6 +827,7 @@ void CWindow::updateDynamicRules() {
EMIT_HOOK_EVENT("windowUpdateRules", m_pSelf.lock());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID());
+ sendWindowSize();
}
// check if the point is "hidden" under a rounded corner of the window
@@ -1238,7 +1231,7 @@ float CWindow::getScrollTouchpad() {
}
float CWindow::getContentScale() {
- return m_sWindowData.contentScale.valueOr(1.0f);
+ return 1.f / m_sWindowData.contentScale.valueOr(1.0f);
}
bool CWindow::canBeTorn() {
diff --git a/src/desktop/Window.hpp b/src/desktop/Window.hpp
index 9e2a55d3..318b16f0 100644
--- a/src/desktop/Window.hpp
+++ b/src/desktop/Window.hpp
@@ -191,6 +191,7 @@ struct SWindowData {
CWindowOverridableVar<float> scrollMouse;
CWindowOverridableVar<float> scrollTouchpad;
+ CWindowOverridableVar<float> contentScale;
CWindowOverridableVar<std::string> animationStyle;
CWindowOverridableVar<Vector2D> maxSize;
@@ -199,8 +200,6 @@ struct SWindowData {
CWindowOverridableVar<CGradientValueData> activeBorderColor;
CWindowOverridableVar<CGradientValueData> inactiveBorderColor;
- CWindowOverridableVar<float> contentScale;
-
CWindowOverridableVar<bool> persistentSize;
};
@@ -578,7 +577,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; }},
+ {"scalecontent", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.contentScale; }},
};
};
diff --git a/src/desktop/WindowRule.cpp b/src/desktop/WindowRule.cpp
index 598c29e7..23269085 100644
--- a/src/desktop/WindowRule.cpp
+++ b/src/desktop/WindowRule.cpp
@@ -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", "contentscale",
+ "size", "suppressevent", "tag", "workspace", "xray",
};
CWindowRule::CWindowRule(const std::string& rule, const std::string& value, bool isV2, bool isExecRule) : szValue(value), szRule(rule), v2(isV2), execRule(isExecRule) {
@@ -57,8 +57,6 @@ 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"))
- ruleType = RULE_CONTENTSCALE;
else if (rule.starts_with("monitor"))
ruleType = RULE_MONITOR;
else if (rule.starts_with("move"))
diff --git a/src/desktop/WindowRule.hpp b/src/desktop/WindowRule.hpp
index 98a680ef..f0722362 100644
--- a/src/desktop/WindowRule.hpp
+++ b/src/desktop/WindowRule.hpp
@@ -26,7 +26,6 @@ class CWindowRule {
RULE_IDLEINHIBIT,
RULE_MAXSIZE,
RULE_MINSIZE,
- RULE_CONTENTSCALE,
RULE_MONITOR,
RULE_MOVE,
RULE_OPACITY,
diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp
index 6cb19e6a..f2ccef2b 100644
--- a/src/managers/KeybindManager.cpp
+++ b/src/managers/KeybindManager.cpp
@@ -3171,9 +3171,6 @@ 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);
- 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") {
@@ -3240,6 +3237,7 @@ SDispatchResult CKeybindManager::setProp(std::string args) {
return {.success = false, .error = "Prop not found"};
} catch (std::exception& e) { return {.success = false, .error = std::format("Error parsing prop value: {}", std::string(e.what()))}; }
+ PWINDOW->sendWindowSize();
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
if (!(PWINDOW->m_sWindowData.noFocus.valueOrDefault() == noFocus)) { |
on another note, the scaling doesn't seem to be working during animations that resize the window |
So, to be able to remove the special rule, you'd' rather call And that is also why during animations, this "feature" doesn't work until the animation is finished, because you shouldn't tell the client once very tick to update the resolution at which it has to render the application window. |
again, you can add a separate |
OK, we will wait until those PRs are in and I will try to make those changes then, because right now the system doesn't support what you are asking while covering the needs of this feature. I have been waiting for three weeks, and merged upstream changes quite a few times already, so this is starting to take much more time that I was expecting. We can also forget about this PR, and you take the pieces of this you want and use them. I don't care either way. I am not asking or care for any attribution, I only added this PR because I wanted to use the feature in my plugin. Now I don't even know if I will be able to after these changes anyway. |
Add a new dispatcher
scaleactive
that scales the content of the active windowEach window can have its contents with a different scale. This can be useful for background apps/jobs you want to control and you don't want them to take much space, pinned windows etc. You can also use it for applications that have their UI too big or too small and don't support native re-scaling.
I don't think I have covered every case yet, but I wanted to propose this initial PR so you guys tell me if there is any real interest for this before I spend more time in it. I was going to implement this in hyprscroller, but it made more sense to integrate the feature in Hyprland.
There is currently a bug that I haven't figured out: Sometimes the client renders popups to be "inside the monitor", so despite the window being visible and interactive, some popups render in what would be the non-scaled monitor extents. I can attach an image if there is any interest to help.
The change should be transparent. By default the scale is 1.0, so it shouldn't interfere with Hyprland unless changed.
To try it out, add something like this to your config:
The dispatcher accepts any positive floating point number as scale. Using a negative number (-1 by convention) will reset the scale of the content of that window. The scale has the same meaning as the fractional value for the monitor: 2 would make the content 2x2 times bigger. 0.5 would make it smaller.
Thanks!