//=========================================== // 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 #include //#include //#include //================================================== // 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 #include #include #include #include #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::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(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::GlobalPos << NativeWindow::Size, QList() << 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::GlobalPos << NativeWindow::Size, QList() << 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) }