From ea35602f18cce8977ed8e9c1216b4f1d903d6631 Mon Sep 17 00:00:00 2001 From: ed Date: Tue, 24 Aug 2021 15:57:54 +0100 Subject: [PATCH] X11: Use XSettings to respond to window scale factor changes --- .../native/juce_linux_Windowing.cpp | 25 ++- .../native/x11/juce_linux_XWindowSystem.cpp | 192 ++++-------------- .../native/x11/juce_linux_XWindowSystem.h | 10 +- 3 files changed, 63 insertions(+), 164 deletions(-) diff --git a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp index 468fcf1aba..82cb46541e 100644 --- a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp @@ -31,7 +31,8 @@ static int numAlwaysOnTopPeers = 0; bool juce_areThereAnyAlwaysOnTopWindows() { return numAlwaysOnTopPeers > 0; } //============================================================================== -class LinuxComponentPeer : public ComponentPeer +class LinuxComponentPeer : public ComponentPeer, + private XWindowSystemUtilities::XSettings::Listener { public: LinuxComponentPeer (Component& comp, int windowStyleFlags, ::Window parentToAddTo) @@ -41,7 +42,9 @@ public: // it's dangerous to create a window on a thread other than the message thread. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED - if (! XWindowSystem::getInstance()->isX11Available()) + const auto* instance = XWindowSystem::getInstance(); + + if (! instance->isX11Available()) return; if (isAlwaysOnTop) @@ -49,11 +52,14 @@ public: repainter = std::make_unique (*this); - windowH = XWindowSystem::getInstance()->createWindow (parentToAddTo, this); + windowH = instance->createWindow (parentToAddTo, this); parentWindow = parentToAddTo; setTitle (component.getName()); + if (auto* xSettings = instance->getXSettings()) + xSettings->addListener (this); + getNativeRealtimeModifiers = []() -> ModifierKeys { return XWindowSystem::getInstance()->getNativeRealtimeModifiers(); }; } @@ -62,8 +68,13 @@ public: // it's dangerous to delete a window on a thread other than the message thread. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED + auto* instance = XWindowSystem::getInstance(); + repainter = nullptr; - XWindowSystem::getInstance()->destroyWindow (windowH); + instance->destroyWindow (windowH); + + if (auto* xSettings = instance->getXSettings()) + xSettings->removeListener (this); if (isAlwaysOnTop) --numAlwaysOnTopPeers; @@ -448,6 +459,12 @@ private: }; //============================================================================== + void settingChanged (const XWindowSystemUtilities::XSetting& settingThatHasChanged) override + { + if (settingThatHasChanged.name == XWindowSystem::getWindowScalingFactorSettingName()) + forceDisplayUpdate(); + } + void updateScaleFactorFromNewBounds (const Rectangle& newBounds, bool isPhysical) { Point translation = (parentWindow != 0 ? getScreenPosition (isPhysical) : Point()); diff --git a/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp b/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp index 100708aad3..df47b8a361 100644 --- a/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp +++ b/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp @@ -1087,159 +1087,6 @@ private: //=============================== X11 - Displays =============================== namespace DisplayHelpers { - template - void parseXSettings (const unsigned char* dataPtr, const size_t bytes, Callback&& callback) - { - struct Header - { - CARD8 byteOrder; - CARD8 padding[3]; - CARD32 serial; - CARD32 nSettings; - }; - - auto* data = dataPtr; - size_t byteNum = 0; - - const auto increment = [&] (size_t amount) - { - data += amount; - byteNum += amount; - }; - - const auto* header = reinterpret_cast (data); - increment (sizeof (Header)); - - const auto readCARD16 = [&]() -> CARD16 - { - if (byteNum + sizeof (CARD16) > bytes) - return {}; - - const auto value = header->byteOrder == MSBFirst ? ByteOrder::bigEndianShort (data) - : ByteOrder::littleEndianShort (data); - increment (sizeof (CARD16)); - return value; - }; - - const auto readCARD32 = [&]() -> CARD32 - { - if (byteNum + sizeof (CARD32) > bytes) - return {}; - - const auto value = header->byteOrder == MSBFirst ? ByteOrder::bigEndianInt (data) - : ByteOrder::littleEndianInt (data); - increment (sizeof (CARD32)); - return value; - }; - - const auto readString = [&] (size_t nameLen) -> std::string - { - const auto padded = (nameLen + 3) & (~(size_t) 3); - - if (byteNum + padded > bytes) - return {}; - - auto* ptr = reinterpret_cast (data); - const std::string result (ptr, ptr + nameLen); - increment (padded); - return result; - }; - - CARD16 setting = 0; - - while (byteNum < bytes && setting < header->nSettings) - { - const auto type = *reinterpret_cast (data); - increment (2); - - const auto name = readString (readCARD16()); - const auto serial = readCARD32(); - ignoreUnused (serial); - - enum { XSettingsTypeInteger, XSettingsTypeString, XSettingsTypeColor }; - - switch (type) - { - case XSettingsTypeInteger: - { - callback (name, (INT32) readCARD32()); - break; - } - - case XSettingsTypeString: - { - callback (name, readString (readCARD32())); - break; - } - - case XSettingsTypeColor: - { - // Order is important, these should be kept as separate statements! - const auto r = readCARD16(); - const auto g = readCARD16(); - const auto b = readCARD16(); - const auto a = readCARD16(); - callback (name, r, g, b, a); - break; - } - } - - setting += 1; - } - } - - double getScalingFactorFromXSettings() - { - if (auto* display = XWindowSystem::getInstance()->getDisplay()) - { - using namespace XWindowSystemUtilities; - - ScopedXLock xLock; - - const auto selectionWindow = X11Symbols::getInstance()->xGetSelectionOwner (display, - Atoms::getCreating (display, "_XSETTINGS_S0")); - - if (selectionWindow != None) - { - const auto xsettingsSettingsAtom = Atoms::getCreating (display, "_XSETTINGS_SETTINGS"); - - const GetXProperty prop { display, - selectionWindow, - xsettingsSettingsAtom, - 0L, - std::numeric_limits::max(), - false, - xsettingsSettingsAtom }; - - if (prop.success - && prop.actualType == xsettingsSettingsAtom - && prop.actualFormat == 8) - { - struct ExtractRelevantSettings - { - void operator() (const std::string& name, INT32 value) - { - if (name == "Gdk/WindowScalingFactor") - scaleFactor = value; - } - - void operator() (const std::string&, const std::string&) {} - void operator() (const std::string&, CARD16, CARD16, CARD16, CARD16) {} - - INT32 scaleFactor = 0; - }; - - ExtractRelevantSettings callback; - parseXSettings (prop.data, prop.numItems, callback); - - return (double) callback.scaleFactor; - } - } - } - - return 0.0; - } - static double getDisplayDPI (::Display* display, int index) { auto widthMM = X11Symbols::getInstance()->xDisplayWidthMM (display, index); @@ -1254,10 +1101,16 @@ namespace DisplayHelpers static double getDisplayScale (const String& name, double dpi) { - const auto scaleFactorFromXSettings = getScalingFactorFromXSettings(); + if (auto* xSettings = XWindowSystem::getInstance()->getXSettings()) + { + auto windowScalingFactorSetting = xSettings->getSetting (XWindowSystem::getWindowScalingFactorSettingName()); - if (scaleFactorFromXSettings > 0.0) - return scaleFactorFromXSettings; + if (windowScalingFactorSetting.isValid() + && windowScalingFactorSetting.integerValue > 0) + { + return (double) windowScalingFactorSetting.integerValue; + } + } if (name.isNotEmpty()) { @@ -3113,6 +2966,15 @@ long XWindowSystem::getUserTime (::Window windowH) const return result; } +void XWindowSystem::initialiseXSettings() +{ + xSettings = std::make_unique (display); + + X11Symbols::getInstance()->xSelectInput (display, + xSettings->getSettingsWindow(), + StructureNotifyMask | PropertyChangeMask); +} + XWindowSystem::DisplayVisuals::DisplayVisuals (::Display* xDisplay) { auto findVisualWithDepthOrNull = [&] (int desiredDepth) -> Visual* @@ -3197,6 +3059,7 @@ bool XWindowSystem::initialiseXDisplay() initialisePointerMap(); updateModifierMappings(); + initialiseXSettings(); #if JUCE_USE_XSHM if (XSHMHelpers::isShmAvailable (display)) @@ -3869,6 +3732,21 @@ void XWindowSystem::windowMessageReceive (XEvent& event) if (! juce_handleXEmbedEvent (nullptr, &event)) #endif { + auto* instance = XWindowSystem::getInstance(); + + if (auto* xSettings = instance->getXSettings()) + { + if (event.xany.window == xSettings->getSettingsWindow()) + { + if (event.xany.type == PropertyNotify) + xSettings->update(); + else if (event.xany.type == DestroyNotify) + instance->initialiseXSettings(); + + return; + } + } + if (auto* peer = dynamic_cast (getPeerFor (event.xany.window))) { XWindowSystem::getInstance()->handleWindowMessage (peer, event); @@ -3878,8 +3756,6 @@ void XWindowSystem::windowMessageReceive (XEvent& event) if (event.type != ConfigureNotify) return; - const auto* instance = XWindowSystem::getInstance(); - for (auto i = ComponentPeer::getNumPeers(); --i >= 0;) instance->dismissBlockingModals (dynamic_cast (ComponentPeer::getPeer (i)), event.xconfigure); diff --git a/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h b/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h index 9a45925ef0..c8ee0a83c0 100644 --- a/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h +++ b/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h @@ -231,11 +231,14 @@ public: String getTextFromClipboard() const; String getLocalClipboardContent() const noexcept { return localClipboardContent; } - ::Display* getDisplay() const noexcept { return display; } - const XWindowSystemUtilities::Atoms& getAtoms() const noexcept { return atoms; } + ::Display* getDisplay() const noexcept { return display; } + const XWindowSystemUtilities::Atoms& getAtoms() const noexcept { return atoms; } + XWindowSystemUtilities::XSettings* getXSettings() const noexcept { return xSettings.get(); } bool isX11Available() const noexcept { return xIsAvailable; } + static String getWindowScalingFactorSettingName() { return "Gdk/WindowScalingFactor"; } + //============================================================================== void handleWindowMessage (LinuxComponentPeer*, XEvent&) const; bool isParentWindowOf (::Window, ::Window possibleChild) const; @@ -287,6 +290,8 @@ private: long getUserTime (::Window) const; + void initialiseXSettings(); + //============================================================================== void handleKeyPressEvent (LinuxComponentPeer*, XKeyEvent&) const; void handleKeyReleaseEvent (LinuxComponentPeer*, const XKeyEvent&) const; @@ -320,6 +325,7 @@ private: XWindowSystemUtilities::Atoms atoms; ::Display* display = nullptr; std::unique_ptr displayVisuals; + std::unique_ptr xSettings; #if JUCE_USE_XSHM std::map<::Window, int> shmPaintsPendingMap;