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 13 commits into
base: main
Choose a base branch
from
Open

Add per-window content scaling #9428

wants to merge 13 commits into from

Conversation

dawsers
Copy link
Contributor

@dawsers dawsers commented Feb 17, 2025

Add a new dispatcher scaleactive that scales the content of the active window

per-window_scaling

Each 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:

# Scaling submap
# will switch to a submap called scaling
bind = $mainMod, period, submap, scaling
# will start a submap called "scaling"
submap = scaling
# sets repeatable binds for scaling the content of the active window
bind = , 1, scaleactive, 0.4 
bind = , 1, submap, reset
bind = , 2, scaleactive, 0.6
bind = , 2, submap, reset
bind = , 3, scaleactive, 0.8
bind = , 3, submap, reset
bind = , 4, scaleactive, 1.25
bind = , 4, submap, reset
bind = , 5, scaleactive, 1.5
bind = , 5, submap, reset
bind = , 6, scaleactive, 2.0
bind = , 6, submap, reset
bind = , 0, scaleactive, -1.0
bind = , 0, submap, reset
# use reset to go back to the global submap
bind = , escape, submap, reset
# will reset the submap, meaning end the current one and return to the global one
submap = reset

#bind = $mainMode, period, scroller:scaleactive, 0.5
bind = $mainMode, comma, scaleactive, -1

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!

Copy link
Member

@vaxerski vaxerski left a 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

@dawsers
Copy link
Contributor Author

dawsers commented Feb 18, 2025

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 contentScale as a parameter to a few functions, I tried not to. I think I only added it to those that didn't have access to a window:

std::pair<SP<CWLSurfaceResource>, Vector2D> CWLSurfaceResource::at(const Vector2D& localCoords, bool allowsInput, double scale)

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.

void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface, PHLMONITOR pMonitor, bool main, const Vector2D& projSize, const Vector2D& projSizeUnscaled, bool fixMisalignedFSV1, float contentScale);

This function is called by CSurfacePassElement::draw(), and sometimes there is no window there either.

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!

@SNAKESLIGHT
Copy link

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.

Copy link
Member

@vaxerski vaxerski left a 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

@dawsers
Copy link
Contributor Author

dawsers commented Feb 21, 2025

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.

@vaxerski
Copy link
Member

they aren't, it's xdg_positioner. Good luck with getting that to work. Check CXDGPositionerRules::getPosition

@dawsers
Copy link
Contributor Author

dawsers commented Feb 25, 2025

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.

@dawsers
Copy link
Contributor Author

dawsers commented Feb 25, 2025

I see XWayland popups are "regular" floating windows, and don't keep any reference to a parent. I see there is a CWindow::m_pX11Parent field, but it is never used or updated. This means I cannot know if the window needs its coordinates tansformed, so I don't think there is anything else to do for now until this changes or anyone has an idea on how to make this work. Maybe some other structure keeps parenting information or are these popups handled specifically by the application?

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?

@vaxerski
Copy link
Member

for X11, I'd say:
who gaf? :P

I'll test this MR a bit tomorrow, if I don't forget.

@dawsers
Copy link
Contributor Author

dawsers commented Feb 28, 2025

Cool.

I think this may close #3861

@github-actions github-actions bot added the debug label Mar 2, 2025
Copy link
Member

@vaxerski vaxerski left a comment

Choose a reason for hiding this comment

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

res ok

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants