File indexing completed on 2024-05-05 09:56:35
0001 /* 0002 SPDX-FileCopyrightText: 2023 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include <config-plasma.h> 0008 0009 #include "autohidescreenedge.h" 0010 #include "debug.h" 0011 #include "panelview.h" 0012 0013 #include "qwayland-kde-screen-edge-v1.h" 0014 0015 #include <KWindowSystem> 0016 0017 #include <QDebug> 0018 #include <QWindow> 0019 #include <QtWaylandClient/QWaylandClientExtension> 0020 #include <QtWaylandClient/QtWaylandClientVersion> 0021 #include <qpa/qplatformwindow_p.h> 0022 0023 #if HAVE_X11 0024 #include <private/qtx11extras_p.h> 0025 #include <xcb/xcb.h> 0026 #endif 0027 0028 class WaylandScreenEdgeManagerV1 : public QWaylandClientExtensionTemplate<WaylandScreenEdgeManagerV1>, public QtWayland::kde_screen_edge_manager_v1 0029 { 0030 Q_OBJECT 0031 0032 public: 0033 WaylandScreenEdgeManagerV1() 0034 : QWaylandClientExtensionTemplate(1) 0035 { 0036 initialize(); 0037 } 0038 0039 ~WaylandScreenEdgeManagerV1() override 0040 { 0041 if (isInitialized()) { 0042 destroy(); 0043 } 0044 } 0045 }; 0046 0047 class WaylandAutoHideScreenEdgeV1 : public QtWayland::kde_auto_hide_screen_edge_v1 0048 { 0049 public: 0050 WaylandAutoHideScreenEdgeV1(Plasma::Types::Location location, ::kde_auto_hide_screen_edge_v1 *edge) 0051 : QtWayland::kde_auto_hide_screen_edge_v1(edge) 0052 , location(location) 0053 { 0054 } 0055 0056 ~WaylandAutoHideScreenEdgeV1() override 0057 { 0058 destroy(); 0059 } 0060 0061 const Plasma::Types::Location location; 0062 }; 0063 0064 class WaylandAutoHideScreenEdge : public AutoHideScreenEdge 0065 { 0066 Q_OBJECT 0067 0068 public: 0069 WaylandAutoHideScreenEdge(PanelView *view); 0070 0071 void deactivate() override; 0072 void activate() override; 0073 0074 protected: 0075 bool eventFilter(QObject *watched, QEvent *event) override; 0076 0077 private: 0078 bool create(); 0079 void destroy(); 0080 0081 std::unique_ptr<WaylandScreenEdgeManagerV1> m_manager; 0082 std::unique_ptr<WaylandAutoHideScreenEdgeV1> m_edge; 0083 bool m_active = false; 0084 }; 0085 0086 WaylandAutoHideScreenEdge::WaylandAutoHideScreenEdge(PanelView *view) 0087 : AutoHideScreenEdge(view) 0088 { 0089 m_manager = std::make_unique<WaylandScreenEdgeManagerV1>(); 0090 if (!m_manager->isActive()) { 0091 qCWarning(PLASMASHELL) << m_manager->extensionInterface()->name << "is unsupported by the compositor. Panel autohide won't work correctly"; 0092 return; 0093 } 0094 0095 // Create a screen edge only if there's a surface role. For now, there's no better alternative 0096 // than check if the window is exposed. 0097 view->installEventFilter(this); 0098 } 0099 0100 bool WaylandAutoHideScreenEdge::eventFilter(QObject *watched, QEvent *event) 0101 { 0102 Q_UNUSED(watched) 0103 if (event->type() == QEvent::Expose) { 0104 if (!m_edge && m_active) { 0105 if (create()) { 0106 m_edge->activate(); 0107 } 0108 } 0109 } else if (event->type() == QEvent::Hide) { 0110 destroy(); 0111 } 0112 return false; 0113 } 0114 0115 void WaylandAutoHideScreenEdge::deactivate() 0116 { 0117 m_active = false; 0118 if (m_edge) { 0119 m_edge->deactivate(); 0120 } 0121 } 0122 0123 void WaylandAutoHideScreenEdge::activate() 0124 { 0125 if (!m_manager->isActive()) { 0126 return; 0127 } 0128 0129 if (m_edge && m_edge->location != m_view->location()) { 0130 m_edge.reset(); 0131 } 0132 0133 if (!m_edge) { 0134 if (m_view->isExposed()) { 0135 create(); 0136 } 0137 } 0138 0139 m_active = true; 0140 if (m_edge) { 0141 m_edge->activate(); 0142 } 0143 } 0144 0145 bool WaylandAutoHideScreenEdge::create() 0146 { 0147 auto waylandWindow = m_view->nativeInterface<QNativeInterface::Private::QWaylandWindow>(); 0148 if (!waylandWindow || !waylandWindow->surface()) { 0149 return false; 0150 } 0151 0152 uint32_t border; 0153 switch (m_view->location()) { 0154 case Plasma::Types::Location::LeftEdge: 0155 border = QtWayland::kde_screen_edge_manager_v1::border_left; 0156 break; 0157 case Plasma::Types::Location::RightEdge: 0158 border = QtWayland::kde_screen_edge_manager_v1::border_right; 0159 break; 0160 case Plasma::Types::Location::TopEdge: 0161 border = QtWayland::kde_screen_edge_manager_v1::border_top; 0162 break; 0163 case Plasma::Types::Location::BottomEdge: 0164 default: 0165 border = QtWayland::kde_screen_edge_manager_v1::border_bottom; 0166 break; 0167 } 0168 0169 m_edge = std::make_unique<WaylandAutoHideScreenEdgeV1>(m_view->location(), m_manager->get_auto_hide_screen_edge(border, waylandWindow->surface())); 0170 return true; 0171 } 0172 0173 void WaylandAutoHideScreenEdge::destroy() 0174 { 0175 m_edge.reset(); 0176 } 0177 0178 #if HAVE_X11 0179 0180 class X11AutoHideScreenEdge : public AutoHideScreenEdge 0181 { 0182 Q_OBJECT 0183 0184 public: 0185 X11AutoHideScreenEdge(PanelView *view); 0186 ~X11AutoHideScreenEdge() override; 0187 0188 void deactivate() override; 0189 void activate() override; 0190 0191 private: 0192 xcb_atom_t m_atom = XCB_ATOM_NONE; 0193 }; 0194 0195 X11AutoHideScreenEdge::X11AutoHideScreenEdge(PanelView *view) 0196 : AutoHideScreenEdge(view) 0197 { 0198 xcb_connection_t *connection = QX11Info::connection(); 0199 0200 const QByteArray atomName = QByteArrayLiteral("_KDE_NET_WM_SCREEN_EDGE_SHOW"); 0201 xcb_intern_atom_cookie_t cookie = xcb_intern_atom_unchecked(connection, false, atomName.length(), atomName.constData()); 0202 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, cookie, nullptr); 0203 if (reply) { 0204 m_atom = reply->atom; 0205 free(reply); 0206 } 0207 } 0208 0209 X11AutoHideScreenEdge::~X11AutoHideScreenEdge() 0210 { 0211 if (!m_view) { 0212 return; 0213 } 0214 deactivate(); 0215 } 0216 0217 void X11AutoHideScreenEdge::deactivate() 0218 { 0219 if (m_atom != XCB_ATOM_NONE) { 0220 xcb_delete_property(QX11Info::connection(), m_view->winId(), m_atom); 0221 } 0222 } 0223 0224 void X11AutoHideScreenEdge::activate() 0225 { 0226 if (m_atom == XCB_ATOM_NONE) { 0227 return; 0228 } 0229 0230 uint32_t value = 0; 0231 0232 switch (m_view->location()) { 0233 case Plasma::Types::TopEdge: 0234 value = 0; 0235 break; 0236 case Plasma::Types::RightEdge: 0237 value = 1; 0238 break; 0239 case Plasma::Types::BottomEdge: 0240 value = 2; 0241 break; 0242 case Plasma::Types::LeftEdge: 0243 value = 3; 0244 break; 0245 case Plasma::Types::Floating: 0246 default: 0247 value = 4; 0248 break; 0249 } 0250 0251 xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, m_view->winId(), m_atom, XCB_ATOM_CARDINAL, 32, 1, &value); 0252 } 0253 0254 #endif 0255 0256 AutoHideScreenEdge::AutoHideScreenEdge(PanelView *view) 0257 : QObject(view) 0258 , m_view(view) 0259 { 0260 } 0261 0262 AutoHideScreenEdge *AutoHideScreenEdge::create(PanelView *view) 0263 { 0264 if (KWindowSystem::isPlatformWayland()) { 0265 return new WaylandAutoHideScreenEdge(view); 0266 } else { 0267 #if HAVE_X11 0268 return new X11AutoHideScreenEdge(view); 0269 #else 0270 Q_UNREACHABLE(); 0271 #endif 0272 } 0273 } 0274 0275 #include "autohidescreenedge.moc"