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