File indexing completed on 2024-05-05 05:33:35

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