/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. By using JUCE, you agree to the terms of both the JUCE 7 End-User License Agreement and JUCE Privacy Policy. End User License Agreement: www.juce.com/juce-7-licence Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ namespace juce { //============================================================================== namespace XWindowSystemUtilities { //============================================================================== /** A handy struct that uses XLockDisplay and XUnlockDisplay to lock the X server using RAII. @tags{GUI} */ struct ScopedXLock { ScopedXLock(); ~ScopedXLock(); }; //============================================================================== /** Gets a specified window property and stores its associated data, freeing it on deletion. @tags{GUI} */ struct GetXProperty { GetXProperty (::Display* display, ::Window windowH, Atom property, long offset, long length, bool shouldDelete, Atom requestedType); ~GetXProperty(); bool success = false; unsigned char* data = nullptr; unsigned long numItems = 0, bytesLeft = 0; Atom actualType; int actualFormat = -1; }; //============================================================================== /** Initialises and stores some atoms for the display. @tags{GUI} */ struct Atoms { enum ProtocolItems { TAKE_FOCUS = 0, DELETE_WINDOW = 1, PING = 2 }; Atoms() = default; explicit Atoms (::Display*); static Atom getIfExists (::Display*, const char* name); static Atom getCreating (::Display*, const char* name); static String getName (::Display*, Atom); static bool isMimeTypeFile (::Display*, Atom); static constexpr unsigned long DndVersion = 3; Atom protocols, protocolList[3], changeState, state, userTime, activeWin, pid, windowType, windowState, windowStateHidden, XdndAware, XdndEnter, XdndLeave, XdndPosition, XdndStatus, XdndDrop, XdndFinished, XdndSelection, XdndTypeList, XdndActionList, XdndActionDescription, XdndActionCopy, XdndActionPrivate, XembedMsgType, XembedInfo, allowedActions[5], allowedMimeTypes[4], utf8String, clipboard, targets; }; //============================================================================== /** Represents a setting according to the XSETTINGS specification. @tags{GUI} */ struct XSetting { enum class Type { integer, string, colour, invalid }; XSetting() = default; XSetting (const String& n, int v) : name (n), type (Type::integer), integerValue (v) {} XSetting (const String& n, const String& v) : name (n), type (Type::string), stringValue (v) {} XSetting (const String& n, const Colour& v) : name (n), type (Type::colour), colourValue (v) {} bool isValid() const noexcept { return type != Type::invalid; } String name; Type type = Type::invalid; int integerValue = -1; String stringValue; Colour colourValue; }; /** Parses and stores the X11 settings for a display according to the XSETTINGS specification. @tags{GUI} */ class XSettings { public: static std::unique_ptr createXSettings (::Display*); //============================================================================== void update(); ::Window getSettingsWindow() const noexcept { return settingsWindow; } XSetting getSetting (const String& settingName) const; //============================================================================== struct Listener { virtual ~Listener() = default; virtual void settingChanged (const XSetting& settingThatHasChanged) = 0; }; void addListener (Listener* listenerToAdd) { listeners.add (listenerToAdd); } void removeListener (Listener* listenerToRemove) { listeners.remove (listenerToRemove); } private: ::Display* display = nullptr; ::Window settingsWindow = None; Atom settingsAtom; int lastUpdateSerial = -1; std::unordered_map settings; ListenerList listeners; XSettings (::Display*, Atom, ::Window); //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XSettings) }; } //============================================================================== class LinuxComponentPeer; class XWindowSystem : public DeletedAtShutdown { public: //============================================================================== ::Window createWindow (::Window parentWindow, LinuxComponentPeer*) const; void destroyWindow (::Window); void setTitle (::Window, const String&) const; void setIcon (::Window , const Image&) const; void setVisible (::Window, bool shouldBeVisible) const; void setBounds (::Window, Rectangle, bool fullScreen) const; void updateConstraints (::Window) const; ComponentPeer::OptionalBorderSize getBorderSize (::Window) const; Rectangle getWindowBounds (::Window, ::Window parentWindow); Point getPhysicalParentScreenPosition() const; bool contains (::Window, Point localPos) const; void setMinimised (::Window, bool shouldBeMinimised) const; bool isMinimised (::Window) const; void setMaximised (::Window, bool shouldBeMinimised) const; void toFront (::Window, bool makeActive) const; void toBehind (::Window, ::Window otherWindow) const; bool isFocused (::Window) const; bool grabFocus (::Window) const; bool canUseSemiTransparentWindows() const; bool canUseARGBImages() const; bool isDarkModeActive() const; int getNumPaintsPendingForWindow (::Window); void processPendingPaintsForWindow (::Window); void addPendingPaintForWindow (::Window); void removePendingPaintForWindow (::Window); Image createImage (bool isSemiTransparentWindow, int width, int height, bool argb) const; void blitToWindow (::Window, Image, Rectangle destinationRect, Rectangle totalRect) const; void setScreenSaverEnabled (bool enabled) const; Point getCurrentMousePosition() const; void setMousePosition (Point pos) const; Cursor createCustomMouseCursorInfo (const Image&, Point hotspot) const; void deleteMouseCursor (Cursor cursorHandle) const; Cursor createStandardMouseCursor (MouseCursor::StandardCursorType) const; void showCursor (::Window, Cursor cursorHandle) const; bool isKeyCurrentlyDown (int keyCode) const; ModifierKeys getNativeRealtimeModifiers() const; Array findDisplays (float masterScale) const; ::Window createKeyProxy (::Window); void deleteKeyProxy (::Window) const; bool externalDragFileInit (LinuxComponentPeer*, const StringArray& files, bool canMove, std::function&& callback) const; bool externalDragTextInit (LinuxComponentPeer*, const String& text, std::function&& callback) const; void copyTextToClipboard (const String&); String getTextFromClipboard() const; String getLocalClipboardContent() const noexcept { return localClipboardContent; } ::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; } void startHostManagedResize (::Window window, Point mouseDown, ResizableBorderComponent::Zone zone); static String getWindowScalingFactorSettingName() { return "Gdk/WindowScalingFactor"; } static String getThemeNameSettingName() { return "Net/ThemeName"; } //============================================================================== void handleWindowMessage (LinuxComponentPeer*, XEvent&) const; bool isParentWindowOf (::Window, ::Window possibleChild) const; //============================================================================== JUCE_DECLARE_SINGLETON (XWindowSystem, false) private: XWindowSystem(); ~XWindowSystem(); //============================================================================== struct VisualAndDepth { Visual* visual; int depth; }; struct DisplayVisuals { explicit DisplayVisuals (::Display*); VisualAndDepth getBestVisualForWindow (bool) const; bool isValid() const noexcept; Visual* visual16Bit = nullptr; Visual* visual24Bit = nullptr; Visual* visual32Bit = nullptr; }; bool initialiseXDisplay(); void destroyXDisplay(); //============================================================================== ::Window getFocusWindow (::Window) const; bool isFrontWindow (::Window) const; //============================================================================== void xchangeProperty (::Window, Atom, Atom, int, const void*, int) const; void removeWindowDecorations (::Window) const; void addWindowButtons (::Window, int) const; void setWindowType (::Window, int) const; void initialisePointerMap(); void deleteIconPixmaps (::Window) const; void updateModifierMappings() const; long getUserTime (::Window) const; bool isHidden (Window) const; bool isIconic (Window) const; void initialiseXSettings(); //============================================================================== void handleKeyPressEvent (LinuxComponentPeer*, XKeyEvent&) const; void handleKeyReleaseEvent (LinuxComponentPeer*, const XKeyEvent&) const; void handleWheelEvent (LinuxComponentPeer*, const XButtonPressedEvent&, float) const; void handleButtonPressEvent (LinuxComponentPeer*, const XButtonPressedEvent&, int) const; void handleButtonPressEvent (LinuxComponentPeer*, const XButtonPressedEvent&) const; void handleButtonReleaseEvent (LinuxComponentPeer*, const XButtonReleasedEvent&) const; void handleMotionNotifyEvent (LinuxComponentPeer*, const XPointerMovedEvent&) const; void handleEnterNotifyEvent (LinuxComponentPeer*, const XEnterWindowEvent&) const; void handleLeaveNotifyEvent (LinuxComponentPeer*, const XLeaveWindowEvent&) const; void handleFocusInEvent (LinuxComponentPeer*) const; void handleFocusOutEvent (LinuxComponentPeer*) const; void handleExposeEvent (LinuxComponentPeer*, XExposeEvent&) const; void handleConfigureNotifyEvent (LinuxComponentPeer*, XConfigureEvent&) const; void handleGravityNotify (LinuxComponentPeer*) const; void propertyNotifyEvent (LinuxComponentPeer*, const XPropertyEvent&) const; void handleMappingNotify (XMappingEvent&) const; void handleClientMessageEvent (LinuxComponentPeer*, XClientMessageEvent&, XEvent&) const; void handleXEmbedMessage (LinuxComponentPeer*, XClientMessageEvent&) const; void dismissBlockingModals (LinuxComponentPeer*) const; void dismissBlockingModals (LinuxComponentPeer*, const XConfigureEvent&) const; void updateConstraints (::Window, ComponentPeer&) const; ::Window findTopLevelWindowOf (::Window) const; static void windowMessageReceive (XEvent&); //============================================================================== bool xIsAvailable = false; XWindowSystemUtilities::Atoms atoms; ::Display* display = nullptr; std::unique_ptr displayVisuals; std::unique_ptr xSettings; #if JUCE_USE_XSHM std::map<::Window, int> shmPaintsPendingMap; #endif int shmCompletionEvent = 0; int pointerMap[5] = {}; String localClipboardContent; Point parentScreenPosition; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XWindowSystem) }; } // namespace juce