1 //===========================================
2 //  Lumina-desktop source code
3 //  Copyright (c) 2015-2017, Ken Moore
4 //  Available under the 3-clause BSD license
5 //  See the LICENSE file for full details
6 //===========================================
7 #include "NativeEventFilter.h"
8 #include <QCoreApplication>
9 #include <QDebug>
10 
11 //#include <xcb/xcb_aux.h>
12 //#include <xcb/damage.h>
13 
14 //==================================================
15 // NOTE: All the XCB interactions and atoms are accessed via:
16 //    obj->XCB->EWMH.(atom name)
17 //    obj->XCB->(do something)
18 //==================================================
19 
20 /*
21 List of XCB response types (since almost impossible to find good docs on XCB)
22 switch (xcb_generic_event_t*->response_type  & ~0x80)
23 case values:
24 XCB_KEY_[PRESS | RELEASE]
25 XCB_BUTTON_[PRESS | RELEASE]
26 XCB_MOTION_NOTIFY
27 XCB_ENTER_NOTIFY
28 XCB_LEAVE_NOTIFY
29 XCB_FOCUS_[IN | OUT]
30 XCB_KEYMAP_NOTIFY
31 XCB_EXPOSE
32 XCB_GRAPHICS_EXPOSURE
33 XCB_VISIBILITY_NOTIFY
34 XCB_CREATE_NOTIFY
35 XCB_DESTROY_NOTIFY
36 XCB_UNMAP_NOTIFY
37 XCB_MAP_[NOTIFY | REQUEST]
38 XCB_REPARENT_NOTIFY
39 XCB_CONFIGURE_[NOTIFY | REQUEST]
40 XCB_GRAVITY_NOTIFY
41 XCB_RESIZE_REQUEST
42 XCB_CIRCULATE_[NOTIFY | REQUEST]
43 XCB_PROPERTY_NOTIFY
44 XCB_SELECTION_[CLEAR | REQUEST | NOTIFY]
45 XCB_COLORMAP_NOTIFY
46 XCB_CLIENT_MESSAGE
47 */
48 
49 //SYSTEM TRAY STANDARD DEFINITIONS
50 #define SYSTEM_TRAY_REQUEST_DOCK 0
51 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
52 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
53 
54 //#include <LuminaX11.h>
55 #include <QX11Info>
56 #include <xcb/xcb_ewmh.h>
57 #include <xcb/xcb_keysyms.h>
58 #include <xcb/damage.h>
59 
60 #define DEBUG 0
61 
62 //Special objects/variables for XCB parsing
63 static xcb_ewmh_connection_t EWMH;
64 //static LXCB *XCB = 0;
65 static xcb_atom_t _NET_SYSTEM_TRAY_OPCODE = 0;
66 
ParsePropertyEvent(xcb_property_notify_event_t * ev,NativeEventFilter * obj)67 inline void ParsePropertyEvent(xcb_property_notify_event_t *ev, NativeEventFilter *obj){
68   //qDebug() << "Got Property Event:" << ev->window << ev->atom;
69   NativeWindowObject::Property prop = NativeWindowObject::None;
70   //Now determine which properties are getting changed, and update the native window as appropriate
71   if(ev->atom == EWMH._NET_WM_NAME){ prop = NativeWindowObject::Title; }
72   else if(ev->atom == EWMH._NET_WM_ICON){ prop = NativeWindowObject::Icon; }
73   else if(ev->atom == EWMH._NET_WM_ICON_NAME){ prop = NativeWindowObject::ShortTitle; }
74   else if(ev->atom == EWMH._NET_WM_DESKTOP){ prop = NativeWindowObject::Workspace; }
75   else if(ev->atom == EWMH._NET_WM_WINDOW_TYPE ){ prop = NativeWindowObject::WinTypes; }
76   else if( ev->atom == EWMH._NET_WM_STATE){ prop = NativeWindowObject::States; }
77   //Send out the signal if necessary
78   if(prop!=NativeWindowObject::None){
79     //if(DEBUG){
80       //qDebug() << "Detected Property Change:" << ev->window << prop;
81     //}
82     obj->emit WindowPropertyChanged(ev->window, prop);
83   }else{
84     //Quick re-check of the simple properties (nothing like the icon or other graphics)
85     obj->emit WindowPropertiesChanged(ev->window, QList<NativeWindowObject::Property>() << NativeWindowObject::Title
86 		<< NativeWindowObject::ShortTitle << NativeWindowObject::Workspace );
87     //qDebug() << "Unknown Property Change:" << ev->window << ev->atom;
88   }
89 }
90 
ParseClientMessageEvent(xcb_client_message_event_t * ev,NativeEventFilter * obj)91 inline void ParseClientMessageEvent(xcb_client_message_event_t *ev, NativeEventFilter *obj){
92   NativeWindowObject::Property prop = NativeWindowObject::None;
93   QVariant val;
94   if(ev->type==EWMH._NET_WM_NAME){ prop = NativeWindowObject::Title; }
95   else if(ev->type==EWMH._NET_WM_ICON){ prop = NativeWindowObject::Icon; }
96   else if(ev->type==EWMH._NET_WM_ICON_NAME){ prop = NativeWindowObject::ShortTitle; }
97   else if(ev->type==EWMH._NET_WM_DESKTOP){
98 		prop = NativeWindowObject::Workspace;
99 		val = QVariant( (int) ev->data.data32[0] );
100   }else if(ev->type==EWMH._NET_WM_WINDOW_TYPE){ prop = NativeWindowObject::WinTypes; }
101   else if(ev->type==EWMH._NET_WM_STATE){ prop = NativeWindowObject::States; }
102 
103   if(prop!=NativeWindowObject::None){
104     if(DEBUG){ qDebug() << "Detected Property Change Request:" << ev->window << prop << val; }
105     if(val.isNull()){ obj->emit WindowPropertyChanged(ev->window, prop); }
106     else{ obj->emit RequestWindowPropertyChange(ev->window, prop, val); }
107   }else{
108     //Quick re-check of the simple properties (nothing like the icon or other graphics)
109     obj->emit WindowPropertiesChanged(ev->window, QList<NativeWindowObject::Property>() << NativeWindowObject::Title
110 		<< NativeWindowObject::ShortTitle << NativeWindowObject::Workspace );
111   }
112 
113 }
114 
115 
116 //Constructor for the Event Filter wrapper
NativeEventFilter()117 NativeEventFilter::NativeEventFilter() : QObject(){
118   EF = new EventFilter(this);
119   if(EWMH.nb_screens <=0){
120    xcb_intern_atom_cookie_t *cookie = xcb_ewmh_init_atoms(QX11Info::connection(), &EWMH);
121    if(!xcb_ewmh_init_atoms_replies(&EWMH, cookie, NULL) ){
122      qDebug() << "Error with XCB atom initializations";
123    }
124   }
125   if(_NET_SYSTEM_TRAY_OPCODE==0){
126     //_NET_SYSTEM_TRAY_OPCODE
127     xcb_intern_atom_cookie_t cookie = xcb_intern_atom(QX11Info::connection(), 0, 23,"_NET_SYSTEM_TRAY_OPCODE");
128     xcb_intern_atom_reply_t *r = xcb_intern_atom_reply(QX11Info::connection(), cookie, NULL);
129     if(r){
130       _NET_SYSTEM_TRAY_OPCODE = r->atom;
131       free(r);
132     }
133   }
134 }
135 
start()136 void NativeEventFilter::start(){
137   if(DEBUG){ qDebug() << " - Install event filter..."; }
138   QCoreApplication::instance()->installNativeEventFilter(EF);
139    if(DEBUG){ qDebug() << " - Run request check..."; }
140 
141 }
142 
stop()143 void NativeEventFilter::stop(){
144   QCoreApplication::instance()->installNativeEventFilter(0);
145 }
146 
147 //=============================
148 //  EventFilter Class
149 //=============================
150 
151 //Constructor for the XCB event filter
EventFilter(NativeEventFilter * parent)152 EventFilter::EventFilter(NativeEventFilter *parent) : QAbstractNativeEventFilter(){
153   obj = parent;
154 }
155 
156 //This function format taken directly from the Qt5.3 documentation
nativeEventFilter(const QByteArray & eventType,void * message,long *)157 bool EventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *){
158 	//qDebug() << "New Event";
159 	if(eventType=="xcb_generic_event_t"){
160 	  //Convert to known event type (for X11 systems)
161 	   xcb_generic_event_t *ev = static_cast<xcb_generic_event_t *>(message);
162 	  //Now parse the event and emit signals as necessary
163 	  switch( ev->response_type & ~0x80){
164 //==============================
165 //  INTERACTIVITY EVENTS
166 //==============================
167 	    case XCB_KEY_PRESS:
168 		//This is a keyboard key press
169 	 	//qDebug() << "Key Press Event"
170 	        obj->emit KeyPressed( ((xcb_key_press_event_t *) ev)->detail, ((xcb_key_press_event_t *) ev)->root );
171 		break;
172 	    case XCB_KEY_RELEASE:
173 		//This is a keyboard key release
174 		//qDebug() << "Key Release Event";
175 	        obj->emit KeyReleased( ((xcb_key_release_event_t *) ev)->detail, ((xcb_key_release_event_t *) ev)->root );
176 		break;
177 	    case XCB_BUTTON_PRESS:
178 		//This is a mouse button press
179 		//qDebug() << "Button Press Event";
180                   obj->emit MousePressed( ((xcb_button_press_event_t *) ev)->detail, ((xcb_button_press_event_t *) ev)->root );
181 		break;
182 	    case XCB_BUTTON_RELEASE:
183 		//This is a mouse button release
184 		//qDebug() << "Button Release Event";
185                   obj->emit MouseReleased( ((xcb_button_release_event_t *) ev)->detail, ((xcb_button_release_event_t *) ev)->root );
186 		break;
187 	    case XCB_MOTION_NOTIFY:
188 		//This is a mouse movement event
189 		if(DEBUG){ qDebug() << "Motion Notify Event"; }
190                  obj->emit MouseMovement();
191 	        break;
192 	    case XCB_ENTER_NOTIFY:
193 		//This is a mouse movement event when mouse goes over a new window
194 		//qDebug() << "Enter Notify Event";
195                  obj->emit MouseEnterWindow( ((xcb_enter_notify_event_t *) ev)->root );
196 	        break;
197 	    case XCB_LEAVE_NOTIFY:
198 		//This is a mouse movement event when mouse goes leaves a window
199 		//qDebug() << "Leave Notify Event";
200                  obj->emit MouseLeaveWindow( ((xcb_leave_notify_event_t *) ev)->root );
201 	        break;
202 //==============================
203 	    case XCB_EXPOSE:
204 		//qDebug() << "Expose Notify Event:";
205 		//qDebug() << " - Given Window:" << ((xcb_property_notify_event_t*)ev)->window;
206 		break;
207 //==============================
208 	    case XCB_MAP_NOTIFY:
209 		//qDebug() << "Window Map Event:" << ((xcb_map_notify_event_t *)ev)->window;
210                    obj->emit WindowPropertyChanged( ((xcb_map_notify_event_t *)ev)->window, NativeWindowObject::Visible, true);
211 		break; //This is just a notification that a window was mapped - nothing needs to change here
212 	    case XCB_MAP_REQUEST:
213 		//qDebug() << "Window Map Request Event";
214                    obj->emit WindowCreated( ((xcb_map_request_event_t *) ev)->window );
215 		break;
216 //==============================
217 	    case XCB_CREATE_NOTIFY:
218 		//qDebug() << "Window Create Event";
219 	        break;
220 //==============================
221 	    case XCB_UNMAP_NOTIFY:
222 		//qDebug() << "Window Unmap Event:" << ((xcb_unmap_notify_event_t *)ev)->window;
223                   obj->emit WindowPropertyChanged( ((xcb_map_notify_event_t *)ev)->window, NativeWindowObject::Visible, false);
224 		break;
225 //==============================
226 	    case XCB_DESTROY_NOTIFY:
227 		   qDebug() << "Window Closed Event:" << ((xcb_destroy_notify_event_t *)ev)->window;
228                   obj->emit WindowDestroyed( ((xcb_destroy_notify_event_t *) ev)->window );
229 	        break;
230 //==============================
231 	    case XCB_FOCUS_IN:
232 		//qDebug() << "Focus In Event:";
233 		break;
234 //==============================
235 	    case XCB_FOCUS_OUT:
236 		//qDebug() << "Focus Out Event:";
237 		break;
238 //==============================
239 	    case XCB_PROPERTY_NOTIFY:
240 		//qDebug() << "Property Notify Event:";
241 		ParsePropertyEvent((xcb_property_notify_event_t*)ev, obj);
242 		break;
243 //==============================
244 	    case XCB_CLIENT_MESSAGE:
245 		//qDebug() << "Client Message Event";
246 		//qDebug() << " - Given Window:" << ((xcb_client_message_event_t*)ev)->window;
247 		if( ((xcb_client_message_event_t*)ev)->type == _NET_SYSTEM_TRAY_OPCODE && ((xcb_client_message_event_t*)ev)->format == 32){
248 		  //data32[0] is timestamp, [1] is opcode, [2] is  window handle
249 		  if(SYSTEM_TRAY_REQUEST_DOCK == ((xcb_client_message_event_t*)ev)->data.data32[1]){
250                         obj->emit TrayWindowCreated( ((xcb_client_message_event_t*)ev)->data.data32[2] );
251 		      //addTrayApp( ((xcb_client_message_event_t*)ev)->data.data32[2] );
252 		  }
253 		  //Ignore the System Tray messages at the moment
254 	        }else if(((xcb_client_message_event_t*)ev)->window != QX11Info::appRootWindow()){
255 		  ParseClientMessageEvent((xcb_client_message_event_t*)ev, obj);
256 		}
257 	        break;
258 //==============================
259 	    case XCB_CONFIGURE_NOTIFY:
260 		//qDebug() << "Configure Notify Event";
261 		/*obj->emit WindowPropertiesChanged( ((xcb_configure_notify_event_t*)ev)->window,
262 			QList<NativeWindowObject::Property>() << NativeWindowObject::GlobalPos << NativeWindowObject::Size,
263 			QList<QVariant>() << QPoint(((xcb_configure_notify_event_t*)ev)->x, ((xcb_configure_notify_event_t*)ev)->y) <<
264 				QSize(((xcb_configure_notify_event_t*)ev)->width, ((xcb_configure_notify_event_t*)ev)->height) );*/
265 		obj->emit WindowPropertyChanged( ((xcb_configure_notify_event_t*)ev)->window, NativeWindowObject::Size,
266 			QSize(((xcb_configure_notify_event_t*)ev)->width, ((xcb_configure_notify_event_t*)ev)->height) );
267 	        break;
268 //==============================
269 	    case XCB_CONFIGURE_REQUEST:
270 		//qDebug() << "Configure Request Event";
271 		obj->emit RequestWindowPropertiesChange( ((xcb_configure_request_event_t*)ev)->window,
272 			QList<NativeWindowObject::Property>() << NativeWindowObject::GlobalPos << NativeWindowObject::Size,
273 			QList<QVariant>() << QPoint(((xcb_configure_request_event_t*)ev)->x, ((xcb_configure_request_event_t*)ev)->y) <<
274 				QSize(((xcb_configure_request_event_t*)ev)->width, ((xcb_configure_request_event_t*)ev)->height) );
275 	        break;
276 //==============================
277 	    case XCB_RESIZE_REQUEST:
278 		//qDebug() << "Resize Request Event";
279 		obj->emit RequestWindowPropertyChange( ((xcb_resize_request_event_t*)ev)->window,
280 			NativeWindowObject::Size, QSize(((xcb_resize_request_event_t*)ev)->width, ((xcb_resize_request_event_t*)ev)->height) );
281 		break;
282 //==============================
283 	    case XCB_SELECTION_CLEAR:
284 		//qDebug() << "Selection Clear Event";
285 	        break;
286 //==============================
287 	    case 85: //not sure what event this is - but it seems to come up very often (just hide the notice)
288 	    case 0:
289 	    case XCB_GE_GENERIC:
290 		break; //generic event - don't do anything special
291 	    default:
292 		//if( (ev->response_type & ~0x80)==TrayDmgID){
293                     obj->emit PossibleDamageEvent( ((xcb_damage_notify_event_t*)ev)->drawable );
294 		  //checkDamageID( ((xcb_damage_notify_event_t*)ev)->drawable );
295 		//}else{
296 		  //qDebug() << "Default Event:" << (ev->response_type & ~0x80);
297 		//}
298 //==============================
299 	  }
300 	}
301 	return false;
302 	//never stop event handling (this will not impact the X events themselves - just the internal Qt application)
303 }
304