1 2 ////////////////////////////////////////////////////////////////////////////// 3 // oxygendetectwidget.cpp 4 // Note: this class is a stripped down version of 5 // /kdebase/workspace/kwin/kcmkwin/kwinrules/detectwidget.cpp 6 // SPDX-FileCopyrightText: 2004 Lubos Lunak <l.lunak@kde.org> 7 // ------------------- 8 // 9 // SPDX-FileCopyrightText: 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr> 10 // 11 // SPDX-License-Identifier: MIT 12 ////////////////////////////////////////////////////////////////////////////// 13 14 #include "oxygendetectwidget.h" 15 16 #include "oxygen.h" 17 18 #include <KWindowInfo> 19 20 #include <QPushButton> 21 #include <QMouseEvent> 22 #include <config-oxygen.h> 23 #if OXYGEN_HAVE_X11 24 #include <QX11Info> 25 #include <xcb/xcb.h> 26 #endif 27 28 namespace Oxygen 29 { 30 31 //_________________________________________________________ DetectDialog(QWidget * parent)32 DetectDialog::DetectDialog( QWidget* parent ): 33 QDialog( parent ) 34 { 35 36 // setup 37 m_ui.setupUi( this ); 38 39 connect( m_ui.buttonBox->button( QDialogButtonBox::Cancel ), SIGNAL(clicked()), this, SLOT(close()) ); 40 m_ui.windowClassCheckBox->setChecked( true ); 41 42 #if OXYGEN_HAVE_X11 43 if (QX11Info::isPlatformX11()) { 44 // create atom 45 xcb_connection_t* connection( QX11Info::connection() ); 46 const QString atomName( QStringLiteral( "WM_STATE" ) ); 47 xcb_intern_atom_cookie_t cookie( xcb_intern_atom( connection, false, atomName.size(), qPrintable( atomName ) ) ); 48 QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> reply( xcb_intern_atom_reply( connection, cookie, nullptr) ); 49 m_wmStateAtom = reply ? reply->atom : 0; 50 } 51 #endif 52 53 } 54 55 //_________________________________________________________ detect(WId window)56 void DetectDialog::detect( WId window ) 57 { 58 if( window == 0 ) selectWindow(); 59 else readWindow( window ); 60 } 61 62 //_________________________________________________________ readWindow(WId window)63 void DetectDialog::readWindow( WId window ) 64 { 65 66 if( window == 0 ) 67 { 68 emit detectionDone( false ); 69 return; 70 } 71 72 m_info.reset(new KWindowInfo( window, NET::WMAllProperties, NET::WM2AllProperties )); 73 if( !m_info->valid()) 74 { 75 emit detectionDone( false ); 76 return; 77 } 78 79 const QString wmClassClass( QString::fromUtf8( m_info->windowClassClass() ) ); 80 const QString wmClassName( QString::fromUtf8( m_info->windowClassName() ) ); 81 82 m_ui.windowClass->setText( QStringLiteral( "%1 (%2 %3)" ).arg( wmClassClass ).arg( wmClassName ).arg( wmClassClass ) ); 83 m_ui.windowTitle->setText( m_info->name() ); 84 emit detectionDone( exec() == QDialog::Accepted ); 85 86 return; 87 88 } 89 90 //_________________________________________________________ selectWindow()91 void DetectDialog::selectWindow() 92 { 93 94 // use a dialog, so that all user input is blocked 95 // use WX11BypassWM and moving away so that it's not actually visible 96 // grab only mouse, so that keyboard can be used e.g. for switching windows 97 m_grabber = new QDialog( 0, Qt::X11BypassWindowManagerHint ); 98 m_grabber->move( -1000, -1000 ); 99 m_grabber->setModal( true ); 100 m_grabber->show(); 101 102 // need to explicitly override cursor for Qt5 103 qApp->setOverrideCursor( Qt::CrossCursor ); 104 m_grabber->grabMouse( Qt::CrossCursor ); 105 m_grabber->installEventFilter( this ); 106 107 } 108 109 //_________________________________________________________ eventFilter(QObject * o,QEvent * e)110 bool DetectDialog::eventFilter( QObject* o, QEvent* e ) 111 { 112 // check object and event type 113 if( o != m_grabber ) return false; 114 if( e->type() != QEvent::MouseButtonRelease ) return false; 115 116 // need to explicitely release cursor for Qt5 117 qApp->restoreOverrideCursor(); 118 119 // delete old m_grabber 120 delete m_grabber; 121 m_grabber = 0; 122 123 // check button 124 if( static_cast< QMouseEvent* >( e )->button() != Qt::LeftButton ) return true; 125 126 // read window information 127 readWindow( findWindow() ); 128 129 return true; 130 } 131 132 //_________________________________________________________ findWindow()133 WId DetectDialog::findWindow() 134 { 135 136 #if OXYGEN_HAVE_X11 137 if (!QX11Info::isPlatformX11()) { 138 return 0; 139 } 140 // check atom 141 if( !m_wmStateAtom ) return 0; 142 143 xcb_connection_t* connection( QX11Info::connection() ); 144 xcb_window_t parent( QX11Info::appRootWindow() ); 145 146 // why is there a loop of only 10 here 147 for( int i = 0; i < 10; ++i ) 148 { 149 150 // query pointer 151 xcb_query_pointer_cookie_t pointerCookie( xcb_query_pointer( connection, parent ) ); 152 QScopedPointer<xcb_query_pointer_reply_t, QScopedPointerPodDeleter> pointerReply( xcb_query_pointer_reply( connection, pointerCookie, nullptr ) ); 153 if( !( pointerReply && pointerReply->child ) ) return 0; 154 155 const xcb_window_t child( pointerReply->child ); 156 xcb_get_property_cookie_t cookie( xcb_get_property( connection, 0, child, m_wmStateAtom, XCB_GET_PROPERTY_TYPE_ANY, 0, 0 ) ); 157 QScopedPointer<xcb_get_property_reply_t, QScopedPointerPodDeleter> reply( xcb_get_property_reply( connection, cookie, nullptr ) ); 158 if( reply && reply->type ) return child; 159 else parent = child; 160 161 } 162 #endif 163 164 return 0; 165 166 } 167 168 } 169