From 01f85a09a981eb6c8784d459f729d418c0dd19e3 Mon Sep 17 00:00:00 2001
From: vaxerski <vaxry@vaxry.net>
Date: Fri, 23 Jun 2023 21:14:04 +0200
Subject: [PATCH] xwayland: send zero scaling to xwayland if enabled

---
 CMakeLists.txt                   |  1 +
 protocols/meson.build            |  1 +
 src/Compositor.cpp               |  4 +-
 src/Compositor.hpp               |  1 +
 src/config/ConfigManager.cpp     |  3 ++
 src/events/Events.hpp            |  1 +
 src/events/Misc.cpp              |  2 +
 src/events/Monitors.cpp          |  9 +++-
 src/helpers/Monitor.cpp          |  2 +
 src/helpers/Monitor.hpp          |  1 +
 src/helpers/Vector2D.cpp         |  6 +--
 src/helpers/Vector2D.hpp         |  8 ++--
 src/managers/XWaylandManager.cpp | 74 +++++++++++++++++++++++++++++++-
 src/managers/XWaylandManager.hpp |  5 +++
 src/render/Renderer.cpp          |  2 +
 15 files changed, 109 insertions(+), 11 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index b5943e745..de7455273 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -197,5 +197,6 @@ protocol("subprojects/hyprland-protocols/protocols/hyprland-global-shortcuts-v1.
 protocol("subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.xml" "hyprland-toplevel-export-v1" true)
 protocol("stable/xdg-shell/xdg-shell.xml" "xdg-shell" false)
 protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml" "linux-dmabuf-unstable-v1" false)
+protocol("unstable/xdg-output/xdg-output-unstable-v1.xml" "xdg-output-unstable-v1" false)
 protocol("staging/fractional-scale/fractional-scale-v1.xml" "fractional-scale-v1" false)
 protocol("unstable/text-input/text-input-unstable-v1.xml" "text-input-unstable-v1" false)
diff --git a/protocols/meson.build b/protocols/meson.build
index e9934d0d1..296345429 100644
--- a/protocols/meson.build
+++ b/protocols/meson.build
@@ -22,6 +22,7 @@ protocols = [
   [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
   [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'],
   [wl_protocol_dir, 'unstable/text-input/text-input-unstable-v1.xml'],
+  [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
   [wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'],
   ['wlr-foreign-toplevel-management-unstable-v1.xml'],
   ['wlr-layer-shell-unstable-v1.xml'],
diff --git a/src/Compositor.cpp b/src/Compositor.cpp
index 76fefca11..a9d95f39f 100644
--- a/src/Compositor.cpp
+++ b/src/Compositor.cpp
@@ -194,8 +194,8 @@ void CCompositor::initServer() {
     m_sWLRXDGDecoMgr    = wlr_xdg_decoration_manager_v1_create(m_sWLDisplay);
     wlr_server_decoration_manager_set_default_mode(m_sWLRServerDecoMgr, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
 
-    wlr_xdg_output_manager_v1_create(m_sWLDisplay, m_sWLROutputLayout);
-    m_sWLROutputMgr = wlr_output_manager_v1_create(m_sWLDisplay);
+    m_sWLRXDGOutputMgr = wlr_xdg_output_manager_v1_create(m_sWLDisplay, m_sWLROutputLayout);
+    m_sWLROutputMgr    = wlr_output_manager_v1_create(m_sWLDisplay);
 
     m_sWLRInhibitMgr     = wlr_input_inhibit_manager_create(m_sWLDisplay);
     m_sWLRKbShInhibitMgr = wlr_keyboard_shortcuts_inhibit_v1_create(m_sWLDisplay);
diff --git a/src/Compositor.hpp b/src/Compositor.hpp
index 3cb0af62a..203ae85bb 100644
--- a/src/Compositor.hpp
+++ b/src/Compositor.hpp
@@ -60,6 +60,7 @@ class CCompositor {
     wlr_xcursor_manager*                       m_sWLRXCursorMgr;
     wlr_virtual_keyboard_manager_v1*           m_sWLRVKeyboardMgr;
     wlr_output_manager_v1*                     m_sWLROutputMgr;
+    wlr_xdg_output_manager_v1*                 m_sWLRXDGOutputMgr;
     wlr_presentation*                          m_sWLRPresentation;
     wlr_scene*                                 m_sWLRScene;
     wlr_input_inhibit_manager*                 m_sWLRInhibitMgr;
diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp
index 6fdce7c41..86c4a4c82 100644
--- a/src/config/ConfigManager.cpp
+++ b/src/config/ConfigManager.cpp
@@ -1567,6 +1567,9 @@ void CConfigManager::loadConfigLoadVars() {
     // update layout
     g_pLayoutManager->switchToLayout(configValues["general:layout"].strValue);
 
+    // update xwl scale
+    g_pXWaylandManager->updateXWaylandScale();
+
     // manual crash
     if (configValues["debug:manual_crash"].intValue && !m_bManualCrashInitiated) {
         m_bManualCrashInitiated = true;
diff --git a/src/events/Events.hpp b/src/events/Events.hpp
index b032b11ec..35f747cd9 100644
--- a/src/events/Events.hpp
+++ b/src/events/Events.hpp
@@ -103,6 +103,7 @@ namespace Events {
     DYNLISTENFUNC(monitorDamage);
     DYNLISTENFUNC(monitorNeedsFrame);
     DYNLISTENFUNC(monitorCommit);
+    DYNLISTENFUNC(monitorBind);
 
     // XWayland
     LISTENER(readyXWayland);
diff --git a/src/events/Misc.cpp b/src/events/Misc.cpp
index da680a00b..a422a21cf 100644
--- a/src/events/Misc.cpp
+++ b/src/events/Misc.cpp
@@ -74,6 +74,8 @@ void Events::listener_readyXWayland(wl_listener* listener, void* data) {
     }
 
     xcb_disconnect(XCBCONNECTION);
+
+    g_pXWaylandManager->updateXWaylandScale();
 #endif
 }
 
diff --git a/src/events/Monitors.cpp b/src/events/Monitors.cpp
index 23b41667a..6455d4df6 100644
--- a/src/events/Monitors.cpp
+++ b/src/events/Monitors.cpp
@@ -28,7 +28,7 @@ void Events::listener_change(wl_listener* listener, void* data) {
 
         const auto CONFIGHEAD = wlr_output_configuration_head_v1_create(CONFIG, m->output);
 
-        wlr_box BOX;
+        wlr_box    BOX;
         wlr_output_layout_get_box(g_pCompositor->m_sWLROutputLayout, m->output, &BOX);
 
         //m->vecSize.x = BOX.width;
@@ -218,4 +218,11 @@ void Events::listener_monitorCommit(void* owner, void* data) {
     const auto E = (wlr_output_event_commit*)data;
 
     g_pProtocolManager->m_pScreencopyProtocolManager->onOutputCommit(PMONITOR, E);
+
+    if (E->committed & (WLR_OUTPUT_STATE_SCALE | WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_MODE))
+        g_pXWaylandManager->updateXWaylandScale();
+}
+
+void Events::listener_monitorBind(void* owner, void* data) {
+    g_pXWaylandManager->updateXWaylandScale();
 }
diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp
index 0f8dddd49..0691cd37e 100644
--- a/src/helpers/Monitor.cpp
+++ b/src/helpers/Monitor.cpp
@@ -23,12 +23,14 @@ void CMonitor::onConnect(bool noRule) {
     hyprListener_monitorDamage.removeCallback();
     hyprListener_monitorNeedsFrame.removeCallback();
     hyprListener_monitorCommit.removeCallback();
+    hyprListener_monitorBind.removeCallback();
     hyprListener_monitorFrame.initCallback(&output->events.frame, &Events::listener_monitorFrame, this);
     hyprListener_monitorDestroy.initCallback(&output->events.destroy, &Events::listener_monitorDestroy, this);
     hyprListener_monitorStateRequest.initCallback(&output->events.request_state, &Events::listener_monitorStateRequest, this);
     hyprListener_monitorDamage.initCallback(&output->events.damage, &Events::listener_monitorDamage, this);
     hyprListener_monitorNeedsFrame.initCallback(&output->events.needs_frame, &Events::listener_monitorNeedsFrame, this);
     hyprListener_monitorCommit.initCallback(&output->events.commit, &Events::listener_monitorCommit, this);
+    hyprListener_monitorBind.initCallback(&output->events.bind, &Events::listener_monitorBind, this);
 
     if (m_bEnabled) {
         wlr_output_enable(output, 1);
diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp
index 9226e100e..610fa656d 100644
--- a/src/helpers/Monitor.hpp
+++ b/src/helpers/Monitor.hpp
@@ -72,6 +72,7 @@ class CMonitor {
     DYNLISTENER(monitorDamage);
     DYNLISTENER(monitorNeedsFrame);
     DYNLISTENER(monitorCommit);
+    DYNLISTENER(monitorBind);
 
     // hack: a group = workspaces on a monitor.
     // I don't really care lol :P
diff --git a/src/helpers/Vector2D.cpp b/src/helpers/Vector2D.cpp
index 2d52eb9e0..21701c97b 100644
--- a/src/helpers/Vector2D.cpp
+++ b/src/helpers/Vector2D.cpp
@@ -24,15 +24,15 @@ double Vector2D::normalize() {
     return max;
 }
 
-Vector2D Vector2D::floor() {
+Vector2D Vector2D::floor() const {
     return Vector2D(std::floor(x), std::floor(y));
 }
 
-Vector2D Vector2D::clamp(const Vector2D& min, const Vector2D& max) {
+Vector2D Vector2D::clamp(const Vector2D& min, const Vector2D& max) const {
     return Vector2D(std::clamp(this->x, min.x, max.x < min.x ? INFINITY : max.x), std::clamp(this->y, min.y, max.y < min.y ? INFINITY : max.y));
 }
 
-double Vector2D::distance(const Vector2D& other) {
+double Vector2D::distance(const Vector2D& other) const {
     double dx = x - other.x;
     double dy = y - other.y;
     return std::sqrt(dx * dx + dy * dy);
diff --git a/src/helpers/Vector2D.hpp b/src/helpers/Vector2D.hpp
index f2943b282..1178f734b 100644
--- a/src/helpers/Vector2D.hpp
+++ b/src/helpers/Vector2D.hpp
@@ -42,10 +42,10 @@ class Vector2D {
     Vector2D operator/(const Vector2D& a) const {
         return Vector2D(this->x / a.x, this->y / a.y);
     }
-    
-    double   distance(const Vector2D& other);
 
-    Vector2D clamp(const Vector2D& min, const Vector2D& max = Vector2D());
+    double   distance(const Vector2D& other) const;
 
-    Vector2D floor();
+    Vector2D clamp(const Vector2D& min, const Vector2D& max = Vector2D()) const;
+
+    Vector2D floor() const;
 };
diff --git a/src/managers/XWaylandManager.cpp b/src/managers/XWaylandManager.cpp
index c50d2b0d8..bee412795 100644
--- a/src/managers/XWaylandManager.cpp
+++ b/src/managers/XWaylandManager.cpp
@@ -1,6 +1,11 @@
 #include "XWaylandManager.hpp"
 #include "../Compositor.hpp"
 #include "../events/Events.hpp"
+#include "xdg-output-unstable-v1-protocol.h"
+
+#define OUTPUT_MANAGER_VERSION                   3
+#define OUTPUT_DONE_DEPRECATED_SINCE_VERSION     3
+#define OUTPUT_DESCRIPTION_MUTABLE_SINCE_VERSION 3
 
 CHyprXWaylandManager::CHyprXWaylandManager() {
 #ifndef NO_XWAYLAND
@@ -155,8 +160,10 @@ void CHyprXWaylandManager::setWindowSize(CWindow* pWindow, Vector2D size, bool f
         }
     }
 
+    const Vector2D POS = *PXWLFORCESCALEZERO && pWindow->m_bIsX11 ? pWindow->m_vRealPosition.vec() * pWindow->m_fX11SurfaceScaledBy : pWindow->m_vRealPosition.vec();
+
     if (pWindow->m_bIsX11)
-        wlr_xwayland_surface_configure(pWindow->m_uSurface.xwayland, pWindow->m_vRealPosition.vec().x, pWindow->m_vRealPosition.vec().y, size.x, size.y);
+        wlr_xwayland_surface_configure(pWindow->m_uSurface.xwayland, POS.x, POS.y, size.x, size.y);
     else
         wlr_xdg_toplevel_set_size(pWindow->m_uSurface.xdg->toplevel, size.x, size.y);
 }
@@ -289,3 +296,68 @@ Vector2D CHyprXWaylandManager::getMaxSizeForWindow(CWindow* pWindow) {
 
     return MAXSIZE;
 }
+
+void CHyprXWaylandManager::updateXWaylandScale() {
+    static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue;
+
+    setXWaylandScale(*PXWLFORCESCALEZERO ? std::optional<double>{1.0} : std::optional<double>{});
+}
+
+void CHyprXWaylandManager::setXWaylandScale(std::optional<double> scale) {
+    Debug::log(LOG, "Overriding XWayland scale with %.2f", (float)scale.value_or(0.0));
+
+#ifndef NO_XWAYLAND
+    wl_resource* res = nullptr;
+    for (auto& m : g_pCompositor->m_vMonitors) {
+        const Vector2D LOGICALSIZE = m->vecTransformedSize / scale.value_or(m->scale);
+
+        wl_resource*   outputResource = nullptr;
+        bool           needsDone      = false;
+
+        wl_list_for_each(res, &m->output->resources, link) {
+            const auto PCLIENT = wl_resource_get_client(res);
+
+            if (PCLIENT == m_sWLRXWayland->server->client) {
+                const auto VERSION = wl_resource_get_version(res);
+
+                wl_output_send_mode(res, WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED, (int32_t)LOGICALSIZE.x, (int32_t)LOGICALSIZE.y, m->output->refresh);
+
+                if (VERSION >= WL_OUTPUT_SCALE_SINCE_VERSION)
+                    wl_output_send_scale(res, (uint32_t)ceil(scale.value_or(m->scale)));
+
+                wl_output_send_name(res, getFormat("HL X11 %d", m->ID).c_str());
+
+                outputResource = res;
+                needsDone      = true;
+
+                break;
+            }
+        }
+
+        wlr_xdg_output_v1* output;
+        wl_list_for_each(output, &g_pCompositor->m_sWLRXDGOutputMgr->outputs, link) {
+            if (output->layout_output->output == m->output) {
+                wl_list_for_each(res, &output->resources, link) {
+                    const auto PCLIENT = wl_resource_get_client(res);
+
+                    if (PCLIENT == m_sWLRXWayland->server->client) {
+                        zxdg_output_v1_send_logical_size(res, LOGICALSIZE.x, LOGICALSIZE.y);
+
+                        if (wl_resource_get_version(res) < OUTPUT_DONE_DEPRECATED_SINCE_VERSION)
+                            zxdg_output_v1_send_done(res);
+
+                        needsDone = true;
+
+                        break;
+                    }
+                }
+
+                break;
+            }
+        }
+
+        if (needsDone && outputResource)
+            wl_output_send_done(outputResource);
+    }
+#endif
+}
diff --git a/src/managers/XWaylandManager.hpp b/src/managers/XWaylandManager.hpp
index bb4fd6408..e05551e4f 100644
--- a/src/managers/XWaylandManager.hpp
+++ b/src/managers/XWaylandManager.hpp
@@ -2,6 +2,7 @@
 
 #include "../defines.hpp"
 #include "../Window.hpp"
+#include <optional>
 
 class CHyprXWaylandManager {
   public:
@@ -25,6 +26,10 @@ class CHyprXWaylandManager {
     void          moveXWaylandWindow(CWindow*, const Vector2D&);
     void          checkBorders(CWindow*);
     Vector2D      getMaxSizeForWindow(CWindow*);
+    void          updateXWaylandScale();
+
+  private:
+    void setXWaylandScale(std::optional<double> scale);
 };
 
 inline std::unique_ptr<CHyprXWaylandManager> g_pXWaylandManager;
\ No newline at end of file
diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp
index 5b61e70f1..c9096ba05 100644
--- a/src/render/Renderer.cpp
+++ b/src/render/Renderer.cpp
@@ -1880,6 +1880,8 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
                (int)pMonitor->vecPixelSize.y, pMonitor->refreshRate, pMonitor->scale, (int)pMonitor->transform, (int)pMonitor->vecPosition.x, (int)pMonitor->vecPosition.y,
                (int)pMonitor->enabled10bit);
 
+    g_pXWaylandManager->updateXWaylandScale();
+
     return true;
 }