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 @@ -1009,11 +1009,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->m_fContentScale);
if (surf) {
sl = local;
return surf;
Expand Down
22 changes: 19 additions & 3 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 Down Expand Up @@ -259,7 +262,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->m_fContentScale;

return coords + rel;
}

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

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

return 1.0f;
}

void CPopup::recheckTree() {
WP<CPopup> curr = m_pSelf;
while (curr->m_pParent) {
Expand Down Expand Up @@ -355,6 +369,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 @@ -35,6 +35,8 @@ class CPopup {

bool visible();

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
2 changes: 1 addition & 1 deletion src/desktop/Window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1750,7 +1750,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() * m_fContentScale;

if (!force && m_vPendingReportedSize == REPORTSIZE && (m_vReportedPosition == REPORTPOS || !m_bIsX11))
return;
Expand Down
2 changes: 2 additions & 0 deletions src/desktop/Window.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ class CWindow {
PHLANIMVAR<Vector2D> m_vRealPosition;
PHLANIMVAR<Vector2D> m_vRealSize;

float m_fContentScale = 1.0f;

// for not spamming the protocols
Vector2D m_vReportedPosition;
Vector2D m_vReportedSize;
Expand Down
27 changes: 27 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,32 @@ 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;

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

float scale = scaleResult.value();
if (scale > 0.0f) {
PLASTWINDOW->m_fContentScale = 1.0 / scale;
} else {
PLASTWINDOW->m_fContentScale = 1.0f;
}
PLASTWINDOW->sendWindowSize();

return {};
}

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

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 @@ -440,13 +440,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->m_fContentScale : 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->m_fContentScale : 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 @@ -306,19 +306,19 @@ void CWLSurfaceResource::breadthfirst(std::function<void(SP<CWLSurfaceResource>,
bfHelper(surfs, fn, data);
}

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 @@ -134,7 +134,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
32 changes: 18 additions & 14 deletions src/render/Renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,7 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, timespe
renderdata.texture = s->current.texture;
renderdata.surface = s;
renderdata.mainSurface = s == pWindow->m_pWLSurface->resource();
renderdata.contentScale = pWindow->m_fContentScale;
m_sRenderPass.add(makeShared<CSurfacePassElement>(renderdata));
renderdata.surfaceCounter++;
},
Expand Down Expand Up @@ -611,7 +612,7 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, timespe
if (!pWindow->m_bIsX11) {
CBox geom = pWindow->m_pXDGSurface->current.geometry;

renderdata.pos -= geom.pos();
renderdata.pos -= geom.pos() / pWindow->m_fContentScale;
renderdata.dontRound = true; // don't round popups
renderdata.pMonitor = pMonitor;
renderdata.squishOversized = false; // don't squish popups
Expand All @@ -638,10 +639,13 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, timespe
return;
const auto pos = popup->coordsRelativeToParent();
const Vector2D oldPos = renderdata.pos;
renderdata.pos += pos;

const float scale = popup->getContentScale();
// pos in window coordinates
renderdata.pos += pos / scale;
renderdata.contentScale = scale;
popup->m_pWLSurface->resource()->breadthfirst(
[this, &renderdata](SP<CWLSurfaceResource> s, const Vector2D& offset, void* data) {
// offset in surface coordinates
renderdata.localPos = offset;
renderdata.texture = s->current.texture;
renderdata.surface = s;
Expand Down Expand Up @@ -1015,7 +1019,7 @@ void CHyprRenderer::renderSessionLockMissing(PHLMONITOR pMonitor) {
}

void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface, PHLMONITOR pMonitor, bool main, const Vector2D& projSize,
const Vector2D& projSizeUnscaled, bool fixMisalignedFSV1) {
const Vector2D& projSizeUnscaled, bool fixMisalignedFSV1, float contentScale) {
if (!pWindow || !pWindow->m_bIsX11) {
static auto PEXPANDEDGES = CConfigValue<Hyprlang::INT>("render:expand_undersized_textures");

Expand Down Expand Up @@ -1054,7 +1058,7 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP<CWLSurfaceResour
const auto MONITOR_WL_SCALE = std::ceil(pMonitor->scale);
const bool SCALE_UNAWARE = MONITOR_WL_SCALE != pSurface->current.scale && !pSurface->current.viewport.hasDestination;
const auto EXPECTED_SIZE =
((pSurface->current.viewport.hasDestination ? pSurface->current.viewport.destination : pSurface->current.bufferSize / pSurface->current.scale) * pMonitor->scale)
((pSurface->current.viewport.hasDestination ? pSurface->current.viewport.destination : pSurface->current.bufferSize / pSurface->current.scale) * pMonitor->scale / contentScale)
.round();
if (!SCALE_UNAWARE && (EXPECTED_SIZE.x < projSize.x || EXPECTED_SIZE.y < projSize.y)) {
// this will not work with shm AFAIK, idk why.
Expand All @@ -1079,11 +1083,11 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP<CWLSurfaceResour
CBox geom = pWindow->m_pXDGSurface->current.geometry;

// ignore X and Y, adjust uv
if (geom.x != 0 || geom.y != 0 || geom.width > projSizeUnscaled.x || geom.height > projSizeUnscaled.y) {
const auto XPERC = (double)geom.x / (double)pSurface->current.size.x;
const auto YPERC = (double)geom.y / (double)pSurface->current.size.y;
const auto WPERC = (double)(geom.x + geom.width) / (double)pSurface->current.size.x;
const auto HPERC = (double)(geom.y + geom.height) / (double)pSurface->current.size.y;
if (geom.x != 0 || geom.y != 0 || geom.width > projSizeUnscaled.x * contentScale || geom.height > projSizeUnscaled.y * contentScale) {
const auto XPERC = (double)geom.x / (double)pSurface->current.size.x / contentScale;
const auto YPERC = (double)geom.y / (double)pSurface->current.size.y / contentScale;
const auto WPERC = (double)(geom.x + geom.width) / (double)pSurface->current.size.x / contentScale;
const auto HPERC = (double)(geom.y + geom.height) / (double)pSurface->current.size.y / contentScale;

const auto TOADDTL = Vector2D(XPERC * (uvBR.x - uvTL.x), YPERC * (uvBR.y - uvTL.y));
uvBR = uvBR - Vector2D((1.0 - WPERC) * (uvBR.x - uvTL.x), (1.0 - HPERC) * (uvBR.y - uvTL.y));
Expand All @@ -1094,10 +1098,10 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP<CWLSurfaceResour
if (pWindow->m_pWLSurface->small() && !pWindow->m_pWLSurface->m_bFillIgnoreSmall)
maxSize = pWindow->m_pWLSurface->getViewporterCorrectedSize();

if (geom.width > maxSize.x)
uvBR.x = uvBR.x * (maxSize.x / geom.width);
if (geom.height > maxSize.y)
uvBR.y = uvBR.y * (maxSize.y / geom.height);
if (geom.width > maxSize.x * contentScale)
uvBR.x = uvBR.x * (maxSize.x * contentScale / geom.width);
if (geom.height > maxSize.y * contentScale)
uvBR.y = uvBR.y * (maxSize.y * contentScale / geom.height);
}

g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = uvTL;
Expand Down
2 changes: 1 addition & 1 deletion src/render/Renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class CHyprRenderer {
bool shouldRenderCursor();
void setCursorHidden(bool hide);
void calculateUVForSurface(PHLWINDOW, SP<CWLSurfaceResource>, PHLMONITOR pMonitor, bool main = false, const Vector2D& projSize = {}, const Vector2D& projSizeUnscaled = {},
bool fixMisalignedFSV1 = false);
bool fixMisalignedFSV1 = false, float contentScale = 1.0f);
std::tuple<float, float, float> getRenderTimes(PHLMONITOR pMonitor); // avg max min
void renderLockscreen(PHLMONITOR pMonitor, timespec* now, const CBox& geometry);
void recheckSolitaryForMonitor(PHLMONITOR pMonitor);
Expand Down
8 changes: 4 additions & 4 deletions src/render/pass/SurfacePassElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ void CSurfacePassElement::draw(const CRegion& damage) {

if (data.surface->colorManagement.valid())
Debug::log(TRACE, "FIXME: rendering surface with color management enabled, should apply necessary transformations");
g_pHyprRenderer->calculateUVForSurface(data.pWindow, data.surface, data.pMonitor->self.lock(), data.mainSurface, windowBox.size(), PROJSIZEUNSCALED, MISALIGNEDFSV1);
g_pHyprRenderer->calculateUVForSurface(data.pWindow, data.surface, data.pMonitor->self.lock(), data.mainSurface, windowBox.size(), PROJSIZEUNSCALED, MISALIGNEDFSV1, data.contentScale);

auto cancelRender = false;
g_pHyprOpenGL->m_RenderData.clipRegion = visibleRegion(cancelRender);
Expand Down Expand Up @@ -146,7 +146,7 @@ CBox CSurfacePassElement::getTexBox() {

CBox windowBox;
if (data.surface && data.mainSurface) {
windowBox = {(int)outputX + data.pos.x + data.localPos.x, (int)outputY + data.pos.y + data.localPos.y, data.w, data.h};
windowBox = {(int)outputX + data.pos.x + (data.localPos.x / data.contentScale), (int)outputY + data.pos.y + (data.localPos.y / data.contentScale), data.w, data.h};

// however, if surface buffer w / h < box, we need to adjust them
const auto PWINDOW = PSURFACE ? PSURFACE->getWindow() : nullptr;
Expand All @@ -168,8 +168,8 @@ CBox CSurfacePassElement::getTexBox() {
}

} else { // here we clamp to 2, these might be some tiny specks
windowBox = {(int)outputX + data.pos.x + data.localPos.x, (int)outputY + data.pos.y + data.localPos.y, std::max((float)data.surface->current.size.x, 2.F),
std::max((float)data.surface->current.size.y, 2.F)};
windowBox = {(int)outputX + data.pos.x + (data.localPos.x / data.contentScale), (int)outputY + data.pos.y + (data.localPos.y / data.contentScale), std::max((float)data.surface->current.size.x / data.contentScale, 2.F),
std::max((float)data.surface->current.size.y / data.contentScale, 2.F)};
if (data.pWindow && data.pWindow->m_vRealSize->isBeingAnimated() && data.surface && !data.mainSurface && data.squishOversized /* subsurface */) {
// adjust subsurfaces to the window
windowBox.width = (windowBox.width / data.pWindow->m_vReportedSize.x) * data.pWindow->m_vRealSize->value().x;
Expand Down
1 change: 1 addition & 0 deletions src/render/pass/SurfacePassElement.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class CSurfacePassElement : public IPassElement {
float alpha = 1.F, fadeAlpha = 1.F;
bool blur = false;
bool blockBlurOptimization = false;
float contentScale = 1.F;

// only for windows, not popups
bool squishOversized = true;
Expand Down
Loading