From 6de05c92e876e74186911e88508282a9a81d238e Mon Sep 17 00:00:00 2001 From: Michail Vourlakos Date: Sun, 19 Jul 2020 15:04:46 +0300 Subject: [PATCH] support XCP::SHAPE for Views --views can now specify their input area under X11 and give valuable space to underlying windows --- CMakeLists.txt | 2 +- README.md | 2 + app/main.cpp | 5 ++ app/view/effects.cpp | 22 ++++++ app/view/effects.h | 8 ++ app/wm/abstractwindowinterface.h | 1 + app/wm/waylandinterface.cpp | 7 +- app/wm/waylandinterface.h | 1 + app/wm/xwindowinterface.cpp | 51 ++++++++++++ app/wm/xwindowinterface.h | 7 ++ .../package/contents/ui/VisibilityManager.qml | 78 ++++++++++++++----- containment/package/contents/ui/main.qml | 18 +++++ declarativeimports/core/quickwindowsystem.cpp | 5 ++ declarativeimports/core/quickwindowsystem.h | 3 + 14 files changed, 189 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index febb1a338..b4c424d58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries" PURPOSE "Required for building the X11 based workspace") if(X11_FOUND) - find_package(XCB MODULE REQUIRED COMPONENTS XCB RANDR EVENT) + find_package(XCB MODULE REQUIRED COMPONENTS XCB RANDR SHAPE EVENT) set_package_properties(XCB PROPERTIES TYPE REQUIRED) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS X11Extras) diff --git a/README.md b/README.md index d3d5a32cf..c7cadfc32 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,8 @@ Minimum requirements: KF5WindowSystem >= 5.38.0 Qt5X11Extras >= 5.7.0 libxcb + libxcb-randr + libxcb-shape libSM ``` diff --git a/app/main.cpp b/app/main.cpp index 80fbdfc39..71920e27c 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -163,6 +163,11 @@ int main(int argc, char **argv) filterDebugTextOption.setFlags(QCommandLineOption::HiddenFromHelp); filterDebugTextOption.setValueName(i18nc("command line: debug-text", "filter_debug_text")); parser.addOption(filterDebugTextOption); + + QCommandLineOption filterDebugInputMask(QStringList() << QStringLiteral("input")); + filterDebugInputMask.setDescription(QStringLiteral("Show visual window indicators for calculated input mask.")); + filterDebugInputMask.setFlags(QCommandLineOption::HiddenFromHelp); + parser.addOption(filterDebugInputMask); //! END: Hidden options parser.process(app); diff --git a/app/view/effects.cpp b/app/view/effects.cpp index 9f8df9be2..dca940f79 100644 --- a/app/view/effects.cpp +++ b/app/view/effects.cpp @@ -24,6 +24,8 @@ #include #include "panelshadows_p.h" #include "view.h" +#include "../lattecorona.h" +#include "../wm/abstractwindowinterface.h" // Qt #include @@ -40,6 +42,8 @@ Effects::Effects(Latte::View *parent) : QObject(parent), m_view(parent) { + m_corona = qobject_cast(m_view->corona()); + init(); } @@ -235,6 +239,24 @@ void Effects::setMask(QRect area) emit maskChanged(); } +QRect Effects::inputMask() const +{ + return m_inputMask; +} + +void Effects::setInputMask(QRect area) +{ + if (m_inputMask == area) { + return; + } + + m_inputMask = area; + + m_corona->wm()->setInputMask(m_view, area); + + emit inputMaskChanged(); +} + void Effects::forceMaskRedraw() { if (m_background) { diff --git a/app/view/effects.h b/app/view/effects.h index 699e10fc2..ad7bfc7e3 100644 --- a/app/view/effects.h +++ b/app/view/effects.h @@ -31,6 +31,7 @@ #include namespace Latte { +class Corona; class View; } @@ -52,6 +53,7 @@ class Effects: public QObject Q_PROPERTY(QRect mask READ mask WRITE setMask NOTIFY maskChanged) Q_PROPERTY(QRect rect READ rect WRITE setRect NOTIFY rectChanged) + Q_PROPERTY(QRect inputMask READ inputMask WRITE setInputMask NOTIFY inputMaskChanged) Q_PROPERTY(Plasma::FrameSvg::EnabledBorders enabledBorders READ enabledBorders NOTIFY enabledBordersChanged) @@ -83,6 +85,9 @@ public: QRect mask() const; void setMask(QRect area); + QRect inputMask() const; + void setInputMask(QRect area); + QRect rect() const; void setRect(QRect area); @@ -110,6 +115,7 @@ signals: void enabledBordersChanged(); void maskChanged(); void innerShadowChanged(); + void inputMaskChanged(); void rectChanged(); void subtractedMaskRegionsChanged(); @@ -142,8 +148,10 @@ private: QRect m_rect; QRect m_mask; + QRect m_inputMask; QPointer m_view; + QPointer m_corona; Plasma::Theme m_theme; //only for the mask on disabled compositing, not to actually paint diff --git a/app/wm/abstractwindowinterface.h b/app/wm/abstractwindowinterface.h index dbf5a30d7..ab4c6d802 100644 --- a/app/wm/abstractwindowinterface.h +++ b/app/wm/abstractwindowinterface.h @@ -138,6 +138,7 @@ public: virtual void switchToPreviousVirtualDesktop() = 0; virtual void setFrameExtents(QWindow *view, const QMargins &margins) = 0; + virtual void setInputMask(QWindow *window, const QRect &rect) = 0; Latte::Corona *corona(); Tracker::Schemes *schemesTracker(); diff --git a/app/wm/waylandinterface.cpp b/app/wm/waylandinterface.cpp index bb24f9751..32103ba94 100644 --- a/app/wm/waylandinterface.cpp +++ b/app/wm/waylandinterface.cpp @@ -459,7 +459,12 @@ void WaylandInterface::setActiveEdge(QWindow *view, bool active) void WaylandInterface::setFrameExtents(QWindow *view, const QMargins &extents) { - //! do nothing yet until there is a wayland way to provide this + //! do nothing until there is a wayland way to provide this +} + +void WaylandInterface::setInputMask(QWindow *window, const QRect &rect) +{ + //! do nothins, QWindow::mask() is sufficient enough in order to define Window input mask } WindowInfoWrap WaylandInterface::requestInfoActive() diff --git a/app/wm/waylandinterface.h b/app/wm/waylandinterface.h index bfb65aa16..4342bcbe9 100644 --- a/app/wm/waylandinterface.h +++ b/app/wm/waylandinterface.h @@ -99,6 +99,7 @@ public: void switchToPreviousVirtualDesktop() override; void setFrameExtents(QWindow *view, const QMargins &margins) override; + void setInputMask(QWindow *window, const QRect &rect) override; void registerIgnoredWindow(WindowId wid) override; void unregisterIgnoredWindow(WindowId wid) override; diff --git a/app/wm/xwindowinterface.cpp b/app/wm/xwindowinterface.cpp index 7ede294fb..651439502 100644 --- a/app/wm/xwindowinterface.cpp +++ b/app/wm/xwindowinterface.cpp @@ -40,6 +40,7 @@ // X11 #include #include +#include namespace Latte { namespace WindowSystem { @@ -384,6 +385,56 @@ void XWindowInterface::setFrameExtents(QWindow *view, const QMargins &margins) #endif } +void XWindowInterface::checkShapeExtension() +{ + if (!m_shapeExtensionChecked) { + xcb_connection_t *c = QX11Info::connection(); + xcb_prefetch_extension_data(c, &xcb_shape_id); + const xcb_query_extension_reply_t *extension = xcb_get_extension_data(c, &xcb_shape_id); + if (extension->present) { + // query version + auto cookie = xcb_shape_query_version(c); + QScopedPointer version(xcb_shape_query_version_reply(c, cookie, nullptr)); + if (!version.isNull()) { + m_shapeAvailable = (version->major_version * 0x10 + version->minor_version) >= 0x11; + } + } + m_shapeExtensionChecked = true; + } +} + +void XWindowInterface::setInputMask(QWindow *window, const QRect &rect) +{ + if (!window || !window->isVisible()) { + return; + } + + xcb_connection_t *c = QX11Info::connection(); + + if (!m_shapeExtensionChecked) { + checkShapeExtension(); + } + + if (!m_shapeAvailable) { + return; + } + + if (!rect.isEmpty()) { + xcb_rectangle_t xcbrect; + xcbrect.x = qMax(SHRT_MIN, rect.x()); + xcbrect.y = qMax(SHRT_MIN, rect.y()); + xcbrect.width = qMin((int)USHRT_MAX, rect.width()); + xcbrect.height = qMin((int)USHRT_MAX, rect.height()); + + // set input shape, so that it doesn't accept any input events + xcb_shape_rectangles(c, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_INPUT, + XCB_CLIP_ORDERING_UNSORTED, window->winId(), 0, 0, 1, &xcbrect); + } else { + // delete the shape + xcb_shape_mask(c, XCB_SHAPE_SO_INTERSECT, XCB_SHAPE_SK_INPUT, + window->winId(), 0, 0, XCB_PIXMAP_NONE); + } +} WindowInfoWrap XWindowInterface::requestInfoActive() { diff --git a/app/wm/xwindowinterface.h b/app/wm/xwindowinterface.h index fba869b4d..68e9efabe 100644 --- a/app/wm/xwindowinterface.h +++ b/app/wm/xwindowinterface.h @@ -83,6 +83,7 @@ public: void switchToPreviousVirtualDesktop() override; void setFrameExtents(QWindow *view, const QMargins &margins) override; + void setInputMask(QWindow *window, const QRect &rect) override; private: bool isAcceptableWindow(WindowId wid); @@ -97,6 +98,12 @@ private: QUrl windowUrl(WindowId wid); + void checkShapeExtension(); + +private: + //xcb_shape + bool m_shapeExtensionChecked{false}; + bool m_shapeAvailable{false}; }; } diff --git a/containment/package/contents/ui/VisibilityManager.qml b/containment/package/contents/ui/VisibilityManager.qml index c26c57178..e052d8fe9 100644 --- a/containment/package/contents/ui/VisibilityManager.qml +++ b/containment/package/contents/ui/VisibilityManager.qml @@ -511,21 +511,11 @@ Item{ var tempLength = root.isHorizontal ? width : height; var tempThickness = root.isHorizontal ? height : width; - var noCompositingEdit = false; //!LatteCore.WindowSystem.compositingActive && root.editMode; - - if (LatteCore.WindowSystem.compositingActive || noCompositingEdit) { + if (LatteCore.WindowSystem.compositingActive) { if (normalState) { //console.log("entered normal state..."); //count panel length - - - //used when !compositing and in editMode - if (noCompositingEdit) { - tempLength = root.isHorizontal ? root.width : root.height; - } else { - tempLength = background.totals.visualLength; - } - + tempLength = background.totals.visualLength; tempThickness = thicknessNormal; if (animations.needThickness.count > 0) { @@ -560,9 +550,7 @@ Item{ } } - if (noCompositingEdit) { - localX = 0; - } else if (plasmoid.configuration.alignment === LatteCore.Types.Justify) { + if (plasmoid.configuration.alignment === LatteCore.Types.Justify) { localX = (latteView.width/2) - tempLength/2 + background.offset; } else if (root.panelAlignment === LatteCore.Types.Left) { localX = background.offset; @@ -590,9 +578,7 @@ Item{ } } - if (noCompositingEdit) { - localY = 0; - } else if (plasmoid.configuration.alignment === LatteCore.Types.Justify) { + if (plasmoid.configuration.alignment === LatteCore.Types.Justify) { localY = (latteView.height/2) - tempLength/2 + background.offset; } else if (root.panelAlignment === LatteCore.Types.Top) { localY = background.offset; @@ -675,7 +661,7 @@ Item{ if (root.isVertical) { maskThickness = maskArea.width; } - } else if (!noCompositingEdit){ + } else { //! no compositing case var overridesHidden = latteView.visibility.isHidden && !latteView.visibility.supportsKWinEdges; @@ -791,6 +777,60 @@ Item{ //console.log("update geometry ::: "+localGeometry); latteView.localGeometry = localGeometry; } + + + //! Input Mask + if (LatteCore.WindowSystem.isPlatformX11) { + //! This is not needed under wayland environment, mask() can be used instead + + var animated = ( animations.needBothAxis.count>0 + || animations.needLength.count>0 + || animations.needThickness.count>0 + || latteView.visibility.isHidden); + + if (!LatteCore.WindowSystem.compositingActive || animated || latteView.behaveAsPlasmaPanel) { + latteView.effects.inputMask = Qt.rect(0, 0, -1, -1); + } else { + var inputThickness = finalScreenEdgeMargin + metrics.totals.thickness; + var inputGeometry = Qt.rect(0, 0, root.width, root.height); + + if (plasmoid.location === PlasmaCore.Types.TopEdge) { + inputGeometry.x = latteView.effects.rect.x; // from effects area + inputGeometry.y = 0; + + inputGeometry.width = latteView.effects.rect.width; // from effects area + inputGeometry.height = inputThickness ; + } else if (plasmoid.location === PlasmaCore.Types.BottomEdge) { + inputGeometry.x = latteView.effects.rect.x; // from effects area + inputGeometry.y = root.height - inputThickness; + + inputGeometry.width = latteView.effects.rect.width; // from effects area + inputGeometry.height = inputThickness; + } else if (plasmoid.location === PlasmaCore.Types.LeftEdge) { + inputGeometry.x = 0; + inputGeometry.y = latteView.effects.rect.y; // from effects area + + inputGeometry.width = inputThickness; + inputGeometry.height = latteView.effects.rect.height; // from effects area + } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { + inputGeometry.x = root.width - inputThickness; + inputGeometry.y = latteView.effects.rect.y; // from effects area + + inputGeometry.width = inputThickness; + inputGeometry.height = latteView.effects.rect.height; // from effects area + } + + //set the boundaries for latteView local geometry + //qBound = qMax(min, qMin(value, max)). + + inputGeometry.x = Math.max(0, Math.min(inputGeometry.x, latteView.width)); + inputGeometry.y = Math.max(0, Math.min(inputGeometry.y, latteView.height)); + inputGeometry.width = Math.min(inputGeometry.width, latteView.width); + inputGeometry.height = Math.min(inputGeometry.height, latteView.height); + + latteView.effects.inputMask = inputGeometry; + } + } } Loader{ diff --git a/containment/package/contents/ui/main.qml b/containment/package/contents/ui/main.qml index f89c4d6ef..bb72afd87 100644 --- a/containment/package/contents/ui/main.qml +++ b/containment/package/contents/ui/main.qml @@ -62,6 +62,7 @@ Item { ////BEGIN properties property bool debugMode: Qt.application.arguments.indexOf("--graphics")>=0 + property bool debugModeInputMask: Qt.application.arguments.indexOf("--input")>=0 property bool debugModeLayouter: Qt.application.arguments.indexOf("--layouter")>=0 property bool debugModeLocalGeometry: Qt.application.arguments.indexOf("--localgeometry")>=0 property bool debugModeSpacers: Qt.application.arguments.indexOf("--spacers")>=0 @@ -1578,4 +1579,21 @@ Item { opacity: 0.35 } } + + Loader{ + anchors.fill: parent + active: latteView && latteView.effects && root.debugModeInputMask + sourceComponent: Rectangle{ + x: latteView.effects.inputMask.x + y: latteView.effects.inputMask.y + width: latteView.effects.inputMask.width + height: latteView.effects.inputMask.height + + color: "purple" + border.width: 2 + border.color: "purple" + + opacity: 0.35 + } + } } diff --git a/declarativeimports/core/quickwindowsystem.cpp b/declarativeimports/core/quickwindowsystem.cpp index 635f816cd..804db7ba9 100644 --- a/declarativeimports/core/quickwindowsystem.cpp +++ b/declarativeimports/core/quickwindowsystem.cpp @@ -63,4 +63,9 @@ bool QuickWindowSystem::isPlatformWayland() const return KWindowSystem::isPlatformWayland(); } +bool QuickWindowSystem::isPlatformX11() const +{ + return KWindowSystem::isPlatformX11(); +} + } //end of namespace diff --git a/declarativeimports/core/quickwindowsystem.h b/declarativeimports/core/quickwindowsystem.h index 5226fddad..1d2e1fc1b 100644 --- a/declarativeimports/core/quickwindowsystem.h +++ b/declarativeimports/core/quickwindowsystem.h @@ -38,6 +38,7 @@ class QuickWindowSystem final : public QObject Q_PROPERTY(bool compositingActive READ compositingActive NOTIFY compositingChanged FINAL) Q_PROPERTY(bool isPlatformWayland READ isPlatformWayland NOTIFY isPlatformWaylandChanged FINAL) + Q_PROPERTY(bool isPlatformX11 READ isPlatformX11 NOTIFY isPlatformX11Changed FINAL) public: explicit QuickWindowSystem(QObject *parent = nullptr); @@ -45,10 +46,12 @@ public: bool compositingActive() const; bool isPlatformWayland() const; + bool isPlatformX11() const; signals: void compositingChanged(); void isPlatformWaylandChanged(); + void isPlatformX11Changed(); private: bool m_compositing{true};