aboutsummaryrefslogtreecommitdiff
path: root/src-qt5/src-cpp/NativeEventFilter.cpp
blob: c13c1fc8b3a615467f06385495f090115d0adef3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
//===========================================
//  Lumina-desktop source code
//  Copyright (c) 2015-2017, Ken Moore
//  Available under the 3-clause BSD license
//  See the LICENSE file for full details
//===========================================
#include "NativeEventFilter.h"
#include <QCoreApplication>
#include <QDebug>

//#include <xcb/xcb_aux.h>
//#include <xcb/damage.h>

//==================================================
// NOTE: All the XCB interactions and atoms are accessed via:
//    obj->XCB->EWMH.(atom name)
//    obj->XCB->(do something)
//==================================================

/*
List of XCB response types (since almost impossible to find good docs on XCB)
switch (xcb_generic_event_t*->response_type  & ~0x80)
case values:
XCB_KEY_[PRESS | RELEASE]
XCB_BUTTON_[PRESS | RELEASE]
XCB_MOTION_NOTIFY
XCB_ENTER_NOTIFY
XCB_LEAVE_NOTIFY
XCB_FOCUS_[IN | OUT]
XCB_KEYMAP_NOTIFY
XCB_EXPOSE
XCB_GRAPHICS_EXPOSURE
XCB_VISIBILITY_NOTIFY
XCB_CREATE_NOTIFY
XCB_DESTROY_NOTIFY
XCB_UNMAP_NOTIFY
XCB_MAP_[NOTIFY | REQUEST]
XCB_REPARENT_NOTIFY
XCB_CONFIGURE_[NOTIFY | REQUEST]
XCB_GRAVITY_NOTIFY
XCB_RESIZE_REQUEST
XCB_CIRCULATE_[NOTIFY | REQUEST]
XCB_PROPERTY_NOTIFY
XCB_SELECTION_[CLEAR | REQUEST | NOTIFY]
XCB_COLORMAP_NOTIFY
XCB_CLIENT_MESSAGE
*/

//SYSTEM TRAY STANDARD DEFINITIONS
#define SYSTEM_TRAY_REQUEST_DOCK 0
#define SYSTEM_TRAY_BEGIN_MESSAGE 1
#define SYSTEM_TRAY_CANCEL_MESSAGE 2

//#include <LuminaX11.h>
#include <QX11Info>
#include <xcb/xcb_ewmh.h>
#include <xcb/xcb_keysyms.h>
#include <xcb/damage.h>

#define DEBUG 0

//Special objects/variables for XCB parsing
static xcb_ewmh_connection_t EWMH;
//static LXCB *XCB = 0;
static xcb_atom_t _NET_SYSTEM_TRAY_OPCODE = 0;

inline void ParsePropertyEvent(xcb_property_notify_event_t *ev, NativeEventFilter *obj){
  //qDebug() << "Got Property Event:" << ev->window << ev->atom;
  NativeWindow::Property prop = NativeWindow::None;
  //Now determine which properties are getting changed, and update the native window as appropriate
  if(ev->atom == EWMH._NET_WM_NAME){ prop = NativeWindow::Title; }
  else if(ev->atom == EWMH._NET_WM_ICON){ prop = NativeWindow::Icon; }
  else if(ev->atom == EWMH._NET_WM_ICON_NAME){ prop = NativeWindow::ShortTitle; }
  else if(ev->atom == EWMH._NET_WM_DESKTOP){ prop = NativeWindow::Workspace; }
  else if(ev->atom == EWMH._NET_WM_WINDOW_TYPE ){ prop = NativeWindow::WinTypes; }
  else if( ev->atom == EWMH._NET_WM_STATE){ prop = NativeWindow::States; }
  //Send out the signal if necessary
  if(prop!=NativeWindow::None){
    //if(DEBUG){
      //qDebug() << "Detected Property Change:" << ev->window << prop;
    //}
    obj->emit WindowPropertyChanged(ev->window, prop);
  }else{
    //Quick re-check of the simple properties (nothing like the icon or other graphics)
    obj->emit WindowPropertiesChanged(ev->window, QList<NativeWindow::Property>() << NativeWindow::Title
		<< NativeWindow::ShortTitle << NativeWindow::Workspace );
    //qDebug() << "Unknown Property Change:" << ev->window << ev->atom;
  }
}

inline void ParseClientMessageEvent(xcb_client_message_event_t *ev, NativeEventFilter *obj){
  NativeWindow::Property prop = NativeWindow::None;
  QVariant val;
  if(ev->type==EWMH._NET_WM_NAME){ prop = NativeWindow::Title; }
  else if(ev->type==EWMH._NET_WM_ICON){ prop = NativeWindow::Icon; }
  else if(ev->type==EWMH._NET_WM_ICON_NAME){ prop = NativeWindow::ShortTitle; }
  else if(ev->type==EWMH._NET_WM_DESKTOP){
		prop = NativeWindow::Workspace;
		val = QVariant( (int) ev->data.data32[0] );
  }else if(ev->type==EWMH._NET_WM_WINDOW_TYPE){ prop = NativeWindow::WinTypes; }
  else if(ev->type==EWMH._NET_WM_STATE){ prop = NativeWindow::States; }

  if(prop!=NativeWindow::None){
    //if(DEBUG){
      qDebug() << "Detected Property Change Request:" << ev->window << prop; //}
    if(val.isNull()){ obj->emit WindowPropertyChanged(ev->window, prop); }
    else{ obj->emit RequestWindowPropertyChange(ev->window, prop, val); }
  }

}


//Constructor for the Event Filter wrapper
NativeEventFilter::NativeEventFilter() : QObject(){
  EF = new EventFilter(this);
  if(EWMH.nb_screens <=0){
   xcb_intern_atom_cookie_t *cookie = xcb_ewmh_init_atoms(QX11Info::connection(), &EWMH);
   if(!xcb_ewmh_init_atoms_replies(&EWMH, cookie, NULL) ){
     qDebug() << "Error with XCB atom initializations";
   }
  }
  if(_NET_SYSTEM_TRAY_OPCODE==0){
    //_NET_SYSTEM_TRAY_OPCODE
    xcb_intern_atom_cookie_t cookie = xcb_intern_atom(QX11Info::connection(), 0, 23,"_NET_SYSTEM_TRAY_OPCODE");
    xcb_intern_atom_reply_t *r = xcb_intern_atom_reply(QX11Info::connection(), cookie, NULL);
    if(r){
      _NET_SYSTEM_TRAY_OPCODE = r->atom;
      free(r);
    }
  }
}

void NativeEventFilter::start(){
  if(DEBUG){ qDebug() << " - Install event filter..."; }
  QCoreApplication::instance()->installNativeEventFilter(EF);
   if(DEBUG){ qDebug() << " - Run request check..."; }

}

void NativeEventFilter::stop(){
  QCoreApplication::instance()->installNativeEventFilter(0);
}

//=============================
//  EventFilter Class
//=============================

//Constructor for the XCB event filter
EventFilter::EventFilter(NativeEventFilter *parent) : QAbstractNativeEventFilter(){
  obj = parent;
}

//This function format taken directly from the Qt5.3 documentation
bool EventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *){
	//qDebug() << "New Event";
	if(eventType=="xcb_generic_event_t"){
	  //Convert to known event type (for X11 systems)
	   xcb_generic_event_t *ev = static_cast<xcb_generic_event_t *>(message);
	  //Now parse the event and emit signals as necessary
	  switch( ev->response_type & ~0x80){
//==============================
//  INTERACTIVITY EVENTS
//==============================
	    case XCB_KEY_PRESS:
		//This is a keyboard key press
	 	//qDebug() << "Key Press Event"
	        obj->emit KeyPressed( ((xcb_key_press_event_t *) ev)->detail, ((xcb_key_press_event_t *) ev)->root );
		break;
	    case XCB_KEY_RELEASE:
		//This is a keyboard key release
		//qDebug() << "Key Release Event";
	        obj->emit KeyReleased( ((xcb_key_release_event_t *) ev)->detail, ((xcb_key_release_event_t *) ev)->root );
		break;
	    case XCB_BUTTON_PRESS:
		//This is a mouse button press
		//qDebug() << "Button Press Event";
                  obj->emit MousePressed( ((xcb_button_press_event_t *) ev)->detail, ((xcb_button_press_event_t *) ev)->root );
		break;
	    case XCB_BUTTON_RELEASE:
		//This is a mouse button release
		//qDebug() << "Button Release Event";
                  obj->emit MouseReleased( ((xcb_button_release_event_t *) ev)->detail, ((xcb_button_release_event_t *) ev)->root );
		break;
	    case XCB_MOTION_NOTIFY:
		//This is a mouse movement event
		if(DEBUG){ qDebug() << "Motion Notify Event"; }
                 obj->emit MouseMovement();
	        break;
	    case XCB_ENTER_NOTIFY:
		//This is a mouse movement event when mouse goes over a new window
		//qDebug() << "Enter Notify Event";
                 obj->emit MouseEnterWindow( ((xcb_enter_notify_event_t *) ev)->root );
	        break;
	    case XCB_LEAVE_NOTIFY:
		//This is a mouse movement event when mouse goes leaves a window
		//qDebug() << "Leave Notify Event";
                 obj->emit MouseLeaveWindow( ((xcb_leave_notify_event_t *) ev)->root );
	        break;
//==============================
	    case XCB_EXPOSE:
		//qDebug() << "Expose Notify Event:";
		//qDebug() << " - Given Window:" << ((xcb_property_notify_event_t*)ev)->window;
		break;
//==============================
	    case XCB_MAP_NOTIFY:
		//qDebug() << "Window Map Event:" << ((xcb_map_notify_event_t *)ev)->window;
                   obj->emit WindowPropertyChanged( ((xcb_map_notify_event_t *)ev)->window, NativeWindow::Visible, true);
		break; //This is just a notification that a window was mapped - nothing needs to change here
	    case XCB_MAP_REQUEST:
		//qDebug() << "Window Map Request Event";
                   obj->emit WindowCreated( ((xcb_map_request_event_t *) ev)->window );
		break;
//==============================
	    case XCB_CREATE_NOTIFY:
		//qDebug() << "Window Create Event";
	        break;
//==============================
	    case XCB_UNMAP_NOTIFY:
		//qDebug() << "Window Unmap Event:" << ((xcb_unmap_notify_event_t *)ev)->window;
                  obj->emit WindowPropertyChanged( ((xcb_map_notify_event_t *)ev)->window, NativeWindow::Visible, false);
		break;
//==============================
	    case XCB_DESTROY_NOTIFY:
		//qDebug() << "Window Closed Event:" << ((xcb_destroy_notify_event_t *)ev)->window;
                  obj->emit WindowDestroyed( ((xcb_destroy_notify_event_t *) ev)->window );
	        break;
//==============================
	    case XCB_FOCUS_IN:
		//qDebug() << "Focus In Event:";
		break;
//==============================
	    case XCB_FOCUS_OUT:
		//qDebug() << "Focus Out Event:";
		break;
//==============================
	    case XCB_PROPERTY_NOTIFY:
		//qDebug() << "Property Notify Event:";
		ParsePropertyEvent((xcb_property_notify_event_t*)ev, obj);
		break;
//==============================
	    case XCB_CLIENT_MESSAGE:
		//qDebug() << "Client Message Event";
		//qDebug() << " - Given Window:" << ((xcb_client_message_event_t*)ev)->window;
		if( ((xcb_client_message_event_t*)ev)->type == _NET_SYSTEM_TRAY_OPCODE && ((xcb_client_message_event_t*)ev)->format == 32){
		  //data32[0] is timestamp, [1] is opcode, [2] is  window handle
		  if(SYSTEM_TRAY_REQUEST_DOCK == ((xcb_client_message_event_t*)ev)->data.data32[1]){
                        obj->emit TrayWindowCreated( ((xcb_client_message_event_t*)ev)->data.data32[2] );
		      //addTrayApp( ((xcb_client_message_event_t*)ev)->data.data32[2] );
		  }
		  //Ignore the System Tray messages at the moment
	        }else if(((xcb_client_message_event_t*)ev)->window != QX11Info::appRootWindow()){
		  ParseClientMessageEvent((xcb_client_message_event_t*)ev, obj);
		}
	        break;
//==============================
	    case XCB_CONFIGURE_NOTIFY:
		//qDebug() << "Configure Notify Event";
		/*obj->emit WindowPropertiesChanged( ((xcb_configure_notify_event_t*)ev)->window,
			QList<NativeWindow::Property>() << NativeWindow::GlobalPos << NativeWindow::Size,
			QList<QVariant>() << QPoint(((xcb_configure_notify_event_t*)ev)->x, ((xcb_configure_notify_event_t*)ev)->y) <<
				QSize(((xcb_configure_notify_event_t*)ev)->width, ((xcb_configure_notify_event_t*)ev)->height) );*/
		obj->emit WindowPropertyChanged( ((xcb_configure_notify_event_t*)ev)->window, NativeWindow::Size,
			QSize(((xcb_configure_notify_event_t*)ev)->width, ((xcb_configure_notify_event_t*)ev)->height) );
	        break;
//==============================
	    case XCB_CONFIGURE_REQUEST:
		//qDebug() << "Configure Request Event";
		obj->emit RequestWindowPropertiesChange( ((xcb_configure_request_event_t*)ev)->window,
			QList<NativeWindow::Property>() << NativeWindow::GlobalPos << NativeWindow::Size,
			QList<QVariant>() << QPoint(((xcb_configure_request_event_t*)ev)->x, ((xcb_configure_request_event_t*)ev)->y) <<
				QSize(((xcb_configure_request_event_t*)ev)->width, ((xcb_configure_request_event_t*)ev)->height) );
	        break;
//==============================
	    case XCB_RESIZE_REQUEST:
		//qDebug() << "Resize Request Event";
		obj->emit RequestWindowPropertyChange( ((xcb_resize_request_event_t*)ev)->window,
			NativeWindow::Size, QSize(((xcb_resize_request_event_t*)ev)->width, ((xcb_resize_request_event_t*)ev)->height) );
		break;
//==============================
	    case XCB_SELECTION_CLEAR:
		//qDebug() << "Selection Clear Event";
	        break;
//==============================
	    case 85: //not sure what event this is - but it seems to come up very often (just hide the notice)
	    case 0:
	    case XCB_GE_GENERIC:
		break; //generic event - don't do anything special
	    default:
		//if( (ev->response_type & ~0x80)==TrayDmgID){
                    obj->emit PossibleDamageEvent( ((xcb_damage_notify_event_t*)ev)->drawable );
		  //checkDamageID( ((xcb_damage_notify_event_t*)ev)->drawable );
		//}else{
		  //qDebug() << "Default Event:" << (ev->response_type & ~0x80);
		//}
//==============================
	  }
	}
	return false;
	//never stop event handling (this will not impact the X events themselves - just the internal Qt application)
}
bgstack15