File indexing completed on 2024-11-10 04:58:09
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"