File indexing completed on 2024-10-13 13:14:58
0001 /* 0002 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 #include "utils/xcbutils.h" 0007 0008 #include <QApplication> 0009 #include <QCheckBox> 0010 #include <QHBoxLayout> 0011 #include <QMenu> 0012 #include <QPlatformSurfaceEvent> 0013 #include <QPushButton> 0014 #include <QScreen> 0015 #include <QTimer> 0016 #include <QToolButton> 0017 #include <QWidget> 0018 #include <QWindow> 0019 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0020 #include <private/qtx11extras_p.h> 0021 #else 0022 #include <QX11Info> 0023 #endif 0024 0025 #include <KWindowSystem> 0026 0027 #include <KWayland/Client/connection_thread.h> 0028 #include <KWayland/Client/plasmashell.h> 0029 #include <KWayland/Client/registry.h> 0030 #include <KWayland/Client/surface.h> 0031 0032 class ScreenEdgeHelper : public QObject 0033 { 0034 Q_OBJECT 0035 protected: 0036 ScreenEdgeHelper(QWidget *widget, QObject *parent = nullptr); 0037 0038 QWindow *window() const 0039 { 0040 return m_widget->windowHandle(); 0041 } 0042 0043 virtual void restore() = 0; 0044 0045 public: 0046 ~ScreenEdgeHelper() override; 0047 0048 virtual void hide() = 0; 0049 virtual void raiseOrShow(bool raise) = 0; 0050 virtual void init(){}; 0051 0052 virtual void moveToTop(); 0053 virtual void moveToRight(); 0054 virtual void moveToBottom(); 0055 virtual void moveToLeft(); 0056 virtual void moveToFloating(); 0057 0058 void hideAndRestore() 0059 { 0060 hide(); 0061 m_timer->start(10000); 0062 } 0063 0064 private: 0065 QWidget *m_widget; 0066 QTimer *m_timer; 0067 }; 0068 0069 class ScreenEdgeHelperX11 : public ScreenEdgeHelper 0070 { 0071 Q_OBJECT 0072 public: 0073 ScreenEdgeHelperX11(QWidget *widget, QObject *parent = nullptr); 0074 ~ScreenEdgeHelperX11() override = default; 0075 0076 void hide() override; 0077 void raiseOrShow(bool raise) override; 0078 0079 void moveToTop() override; 0080 void moveToRight() override; 0081 void moveToBottom() override; 0082 void moveToLeft() override; 0083 void moveToFloating() override; 0084 0085 protected: 0086 void restore() override; 0087 0088 private: 0089 uint32_t m_locationValue = 2; 0090 uint32_t m_actionValue = 0; 0091 KWin::Xcb::Atom m_atom; 0092 }; 0093 0094 class ScreenEdgeHelperWayland : public ScreenEdgeHelper 0095 { 0096 Q_OBJECT 0097 public: 0098 ScreenEdgeHelperWayland(QWidget *widget, QObject *parent = nullptr); 0099 ~ScreenEdgeHelperWayland() override = default; 0100 0101 void hide() override; 0102 void raiseOrShow(bool raise) override; 0103 void init() override; 0104 0105 bool eventFilter(QObject *watched, QEvent *event) override; 0106 0107 protected: 0108 void restore() override; 0109 0110 private: 0111 void setupSurface(); 0112 KWayland::Client::PlasmaShell *m_shell = nullptr; 0113 KWayland::Client::PlasmaShellSurface *m_shellSurface = nullptr; 0114 bool m_autoHide = true; 0115 }; 0116 0117 ScreenEdgeHelper::ScreenEdgeHelper(QWidget *widget, QObject *parent) 0118 : QObject(parent) 0119 , m_widget(widget) 0120 , m_timer(new QTimer(this)) 0121 { 0122 m_timer->setSingleShot(true); 0123 connect(m_timer, &QTimer::timeout, this, &ScreenEdgeHelper::restore); 0124 } 0125 0126 ScreenEdgeHelper::~ScreenEdgeHelper() = default; 0127 0128 void ScreenEdgeHelper::moveToTop() 0129 { 0130 const QRect geo = QGuiApplication::primaryScreen()->geometry(); 0131 m_widget->setGeometry(geo.x(), geo.y(), geo.width(), 100); 0132 } 0133 0134 void ScreenEdgeHelper::moveToRight() 0135 { 0136 const QRect geo = QGuiApplication::primaryScreen()->geometry(); 0137 m_widget->setGeometry(geo.x(), geo.y(), geo.width(), 100); 0138 } 0139 0140 void ScreenEdgeHelper::moveToBottom() 0141 { 0142 const QRect geo = QGuiApplication::primaryScreen()->geometry(); 0143 m_widget->setGeometry(geo.x(), geo.y() + geo.height() - 100, geo.width(), 100); 0144 } 0145 0146 void ScreenEdgeHelper::moveToLeft() 0147 { 0148 const QRect geo = QGuiApplication::primaryScreen()->geometry(); 0149 m_widget->setGeometry(geo.x(), geo.y(), 100, geo.height()); 0150 } 0151 0152 void ScreenEdgeHelper::moveToFloating() 0153 { 0154 const QRect geo = QGuiApplication::primaryScreen()->geometry(); 0155 m_widget->setGeometry(QRect(geo.center(), QSize(100, 100))); 0156 } 0157 0158 ScreenEdgeHelperX11::ScreenEdgeHelperX11(QWidget *widget, QObject *parent) 0159 : ScreenEdgeHelper(widget, parent) 0160 , m_atom(QByteArrayLiteral("_KDE_NET_WM_SCREEN_EDGE_SHOW")) 0161 { 0162 } 0163 0164 void ScreenEdgeHelperX11::hide() 0165 { 0166 uint32_t value = m_locationValue | (m_actionValue << 8); 0167 xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, window()->winId(), m_atom, XCB_ATOM_CARDINAL, 32, 1, &value); 0168 } 0169 0170 void ScreenEdgeHelperX11::restore() 0171 { 0172 xcb_delete_property(QX11Info::connection(), window()->winId(), m_atom); 0173 } 0174 0175 void ScreenEdgeHelperX11::raiseOrShow(bool raise) 0176 { 0177 m_actionValue = raise ? 1 : 0; 0178 } 0179 0180 void ScreenEdgeHelperX11::moveToBottom() 0181 { 0182 ScreenEdgeHelper::moveToBottom(); 0183 m_locationValue = 2; 0184 } 0185 0186 void ScreenEdgeHelperX11::moveToFloating() 0187 { 0188 ScreenEdgeHelper::moveToFloating(); 0189 m_locationValue = 4; 0190 } 0191 0192 void ScreenEdgeHelperX11::moveToLeft() 0193 { 0194 ScreenEdgeHelper::moveToLeft(); 0195 m_locationValue = 3; 0196 } 0197 0198 void ScreenEdgeHelperX11::moveToRight() 0199 { 0200 ScreenEdgeHelper::moveToRight(); 0201 m_locationValue = 1; 0202 } 0203 0204 void ScreenEdgeHelperX11::moveToTop() 0205 { 0206 ScreenEdgeHelper::moveToTop(); 0207 m_locationValue = 0; 0208 } 0209 0210 using namespace KWayland::Client; 0211 0212 ScreenEdgeHelperWayland::ScreenEdgeHelperWayland(QWidget *widget, QObject *parent) 0213 : ScreenEdgeHelper(widget, parent) 0214 { 0215 ConnectionThread *connection = ConnectionThread::fromApplication(this); 0216 Registry *registry = new Registry(connection); 0217 registry->create(connection); 0218 0219 connect(registry, &Registry::interfacesAnnounced, this, 0220 [registry, this] { 0221 const auto interface = registry->interface(Registry::Interface::PlasmaShell); 0222 if (interface.name == 0) { 0223 return; 0224 } 0225 m_shell = registry->createPlasmaShell(interface.name, interface.version); 0226 }); 0227 0228 registry->setup(); 0229 connection->roundtrip(); 0230 } 0231 0232 void ScreenEdgeHelperWayland::init() 0233 { 0234 window()->installEventFilter(this); 0235 setupSurface(); 0236 } 0237 0238 void ScreenEdgeHelperWayland::setupSurface() 0239 { 0240 if (!m_shell) { 0241 return; 0242 } 0243 if (auto s = Surface::fromWindow(window())) { 0244 m_shellSurface = m_shell->createSurface(s, window()); 0245 m_shellSurface->setRole(PlasmaShellSurface::Role::Panel); 0246 m_shellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AutoHide); 0247 m_shellSurface->setPosition(window()->position()); 0248 } 0249 } 0250 0251 void ScreenEdgeHelperWayland::hide() 0252 { 0253 if (m_shellSurface && m_autoHide) { 0254 m_shellSurface->requestHideAutoHidingPanel(); 0255 } 0256 } 0257 0258 void ScreenEdgeHelperWayland::restore() 0259 { 0260 if (m_shellSurface && m_autoHide) { 0261 m_shellSurface->requestShowAutoHidingPanel(); 0262 } 0263 } 0264 0265 void ScreenEdgeHelperWayland::raiseOrShow(bool raise) 0266 { 0267 m_autoHide = !raise; 0268 if (m_shellSurface) { 0269 if (raise) { 0270 m_shellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::WindowsCanCover); 0271 } else { 0272 m_shellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AutoHide); 0273 } 0274 } 0275 } 0276 0277 bool ScreenEdgeHelperWayland::eventFilter(QObject *watched, QEvent *event) 0278 { 0279 if (watched != window() || !m_shell) { 0280 return false; 0281 } 0282 if (event->type() == QEvent::PlatformSurface) { 0283 QPlatformSurfaceEvent *pe = static_cast<QPlatformSurfaceEvent *>(event); 0284 if (pe->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) { 0285 setupSurface(); 0286 } else { 0287 delete m_shellSurface; 0288 m_shellSurface = nullptr; 0289 } 0290 } 0291 if (event->type() == QEvent::Move) { 0292 if (m_shellSurface) { 0293 m_shellSurface->setPosition(window()->position()); 0294 } 0295 } 0296 return false; 0297 } 0298 0299 int main(int argc, char **argv) 0300 { 0301 QApplication app(argc, argv); 0302 QApplication::setApplicationDisplayName(QStringLiteral("Screen Edge Show Test App")); 0303 ScreenEdgeHelper *helper = nullptr; 0304 std::unique_ptr<QWidget> widget(new QWidget(nullptr, Qt::FramelessWindowHint)); 0305 if (KWindowSystem::isPlatformX11()) { 0306 app.setProperty("x11Connection", QVariant::fromValue<void *>(QX11Info::connection())); 0307 helper = new ScreenEdgeHelperX11(widget.get(), &app); 0308 } else if (KWindowSystem::isPlatformWayland()) { 0309 helper = new ScreenEdgeHelperWayland(widget.get(), &app); 0310 } 0311 0312 if (!helper) { 0313 return 2; 0314 } 0315 0316 QPushButton *hideWindowButton = new QPushButton(QStringLiteral("Hide"), widget.get()); 0317 0318 QObject::connect(hideWindowButton, &QPushButton::clicked, helper, &ScreenEdgeHelper::hide); 0319 0320 QPushButton *hideAndRestoreButton = new QPushButton(QStringLiteral("Hide and Restore after 10 sec"), widget.get()); 0321 QObject::connect(hideAndRestoreButton, &QPushButton::clicked, helper, &ScreenEdgeHelper::hideAndRestore); 0322 0323 QToolButton *edgeButton = new QToolButton(widget.get()); 0324 0325 QCheckBox *raiseCheckBox = new QCheckBox("Raise:", widget.get()); 0326 QObject::connect(raiseCheckBox, &QCheckBox::toggled, helper, &ScreenEdgeHelper::raiseOrShow); 0327 0328 edgeButton->setText(QStringLiteral("Edge")); 0329 edgeButton->setPopupMode(QToolButton::MenuButtonPopup); 0330 QMenu *edgeButtonMenu = new QMenu(edgeButton); 0331 QObject::connect(edgeButtonMenu->addAction("Top"), &QAction::triggered, helper, &ScreenEdgeHelper::moveToTop); 0332 QObject::connect(edgeButtonMenu->addAction("Right"), &QAction::triggered, helper, &ScreenEdgeHelper::moveToRight); 0333 QObject::connect(edgeButtonMenu->addAction("Bottom"), &QAction::triggered, helper, &ScreenEdgeHelper::moveToBottom); 0334 QObject::connect(edgeButtonMenu->addAction("Left"), &QAction::triggered, helper, &ScreenEdgeHelper::moveToLeft); 0335 edgeButtonMenu->addSeparator(); 0336 QObject::connect(edgeButtonMenu->addAction("Floating"), &QAction::triggered, helper, &ScreenEdgeHelper::moveToFloating); 0337 edgeButton->setMenu(edgeButtonMenu); 0338 0339 QHBoxLayout *layout = new QHBoxLayout(widget.get()); 0340 layout->addWidget(hideWindowButton); 0341 layout->addWidget(hideAndRestoreButton); 0342 layout->addWidget(edgeButton); 0343 widget->setLayout(layout); 0344 0345 const QRect geo = QGuiApplication::primaryScreen()->geometry(); 0346 widget->setGeometry(geo.x(), geo.y() + geo.height() - 100, geo.width(), 100); 0347 widget->show(); 0348 helper->init(); 0349 0350 return app.exec(); 0351 } 0352 0353 #include "screenedgeshowtest.moc"