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