File indexing completed on 2024-05-19 05:35:17

0001 
0002 //////////////////////////////////////////////////////////////////////////////
0003 // oxygendetectwidget.cpp
0004 // Note: this class is a stripped down version of
0005 // /kdebase/workspace/kwin/kcmkwin/kwinrules/detectwidget.cpp
0006 // SPDX-FileCopyrightText: 2004 Lubos Lunak <l.lunak@kde.org>
0007 // -------------------
0008 //
0009 // SPDX-FileCopyrightText: 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0010 //
0011 // SPDX-License-Identifier: MIT
0012 //////////////////////////////////////////////////////////////////////////////
0013 
0014 #include "oxygendetectwidget.h"
0015 
0016 #include "oxygen.h"
0017 
0018 #include <KWindowInfo>
0019 
0020 #include <QMouseEvent>
0021 #include <QPushButton>
0022 #include <config-oxygen.h>
0023 #if OXYGEN_HAVE_X11
0024 #include <private/qtx11extras_p.h>
0025 #include <xcb/xcb.h>
0026 #endif
0027 
0028 namespace Oxygen
0029 {
0030 //_________________________________________________________
0031 DetectDialog::DetectDialog(QWidget *parent)
0032     : QDialog(parent)
0033 {
0034     // setup
0035     m_ui.setupUi(this);
0036 
0037     connect(m_ui.buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(close()));
0038     m_ui.windowClassCheckBox->setChecked(true);
0039 
0040 #if OXYGEN_HAVE_X11
0041     if (QX11Info::isPlatformX11()) {
0042         // create atom
0043         xcb_connection_t *connection(QX11Info::connection());
0044         const QString atomName(QStringLiteral("WM_STATE"));
0045         xcb_intern_atom_cookie_t cookie(xcb_intern_atom(connection, false, atomName.size(), qPrintable(atomName)));
0046         QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> reply(xcb_intern_atom_reply(connection, cookie, nullptr));
0047         m_wmStateAtom = reply ? reply->atom : 0;
0048     }
0049 #endif
0050 }
0051 
0052 //_________________________________________________________
0053 void DetectDialog::detect(WId window)
0054 {
0055     if (window == 0)
0056         selectWindow();
0057     else
0058         readWindow(window);
0059 }
0060 
0061 //_________________________________________________________
0062 void DetectDialog::readWindow(WId window)
0063 {
0064     if (window == 0) {
0065         emit detectionDone(false);
0066         return;
0067     }
0068 
0069     m_info.reset(new KWindowInfo(window, NET::WMAllProperties, NET::WM2AllProperties));
0070     if (!m_info->valid()) {
0071         emit detectionDone(false);
0072         return;
0073     }
0074 
0075     const QString wmClassClass(QString::fromUtf8(m_info->windowClassClass()));
0076     const QString wmClassName(QString::fromUtf8(m_info->windowClassName()));
0077 
0078     m_ui.windowClass->setText(QStringLiteral("%1 (%2 %3)").arg(wmClassClass).arg(wmClassName).arg(wmClassClass));
0079     m_ui.windowTitle->setText(m_info->name());
0080     emit detectionDone(exec() == QDialog::Accepted);
0081 
0082     return;
0083 }
0084 
0085 //_________________________________________________________
0086 void DetectDialog::selectWindow()
0087 {
0088     // use a dialog, so that all user input is blocked
0089     // use WX11BypassWM and moving away so that it's not actually visible
0090     // grab only mouse, so that keyboard can be used e.g. for switching windows
0091     m_grabber = new QDialog(nullptr, Qt::X11BypassWindowManagerHint);
0092     m_grabber->move(-1000, -1000);
0093     m_grabber->setModal(true);
0094     m_grabber->show();
0095 
0096     // need to explicitly override cursor for Qt5
0097     qApp->setOverrideCursor(Qt::CrossCursor);
0098     m_grabber->grabMouse(Qt::CrossCursor);
0099     m_grabber->installEventFilter(this);
0100 }
0101 
0102 //_________________________________________________________
0103 bool DetectDialog::eventFilter(QObject *o, QEvent *e)
0104 {
0105     // check object and event type
0106     if (o != m_grabber)
0107         return false;
0108     if (e->type() != QEvent::MouseButtonRelease)
0109         return false;
0110 
0111     // need to explicitely release cursor for Qt5
0112     qApp->restoreOverrideCursor();
0113 
0114     // delete old m_grabber
0115     delete m_grabber;
0116     m_grabber = nullptr;
0117 
0118     // check button
0119     if (static_cast<QMouseEvent *>(e)->button() != Qt::LeftButton)
0120         return true;
0121 
0122     // read window information
0123     readWindow(findWindow());
0124 
0125     return true;
0126 }
0127 
0128 //_________________________________________________________
0129 WId DetectDialog::findWindow()
0130 {
0131 #if OXYGEN_HAVE_X11
0132     if (!QX11Info::isPlatformX11()) {
0133         return 0;
0134     }
0135     // check atom
0136     if (!m_wmStateAtom)
0137         return 0;
0138 
0139     xcb_connection_t *connection(QX11Info::connection());
0140     xcb_window_t parent(QX11Info::appRootWindow());
0141 
0142     // why is there a loop of only 10 here
0143     for (int i = 0; i < 10; ++i) {
0144         // query pointer
0145         xcb_query_pointer_cookie_t pointerCookie(xcb_query_pointer(connection, parent));
0146         QScopedPointer<xcb_query_pointer_reply_t, QScopedPointerPodDeleter> pointerReply(xcb_query_pointer_reply(connection, pointerCookie, nullptr));
0147         if (!(pointerReply && pointerReply->child))
0148             return 0;
0149 
0150         const xcb_window_t child(pointerReply->child);
0151         xcb_get_property_cookie_t cookie(xcb_get_property(connection, 0, child, m_wmStateAtom, XCB_GET_PROPERTY_TYPE_ANY, 0, 0));
0152         QScopedPointer<xcb_get_property_reply_t, QScopedPointerPodDeleter> reply(xcb_get_property_reply(connection, cookie, nullptr));
0153         if (reply && reply->type)
0154             return child;
0155         else
0156             parent = child;
0157     }
0158 #endif
0159 
0160     return 0;
0161 }
0162 }