Unsolved How to port QApplication::x11EventFilter to XCB QAbstractNativeEventFilter
-
Hi,
We are currently porting an application from Qt4 to Qt5 and there is not much documentation on how to replace MyApplication::x11EventFilter implementation to a QAbstractNativeEventFilter installed with QCoreApplication::installNativeEventFilter (as explained in https://doc.qt.io/qt-5/sourcebreaks.html#changes-to-qcoreapplication).
We just started the implementation ofMyXcbEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *)
as explained in https://doc.qt.io/qt-5/qabstractnativeeventfilter.html#nativeEventFilter in "Linux example" section.
And, with what we found on the web, for example replacing this in MyApplication:bool MyApplication::x11EventFilter(XEvent* p_event) { int eventType = p_event->type; QSharedPointer<QEvent> eventToSend; if (eventType == ButtonPress) { XButtonPressedEvent& bpe = p_event->xbutton; const QPoint pos(bpe.x_root,bpe.y_root); QWidget* widget = qApp->widgetAt(pos); if (widget && widget->window() != qApp->activeWindow()) { QPoint local_pos = widget->mapFromGlobal(pos); eventToSend = QSharedPointer<QEvent>((QEvent*)new QMouseEvent(QEvent::MouseButtonPress, local_pos, pos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); } } return false; }
To something like this (with installNativeEventFilter(m_myXcbEventFilter) called in MyApplication constructor):
bool MyXcbEventFilter::nativeEventFilter(const QByteArray& p_eventType, void* p_message, long*) { if (p_eventType == "xcb_generic_event_t") { xcb_generic_event_t* ev = static_cast<xcb_generic_event_t *>(p_message); uint16_t xcbEventType = ev->response_type & ~0x80; if (xcbEventType == XCB_BUTTON_PRESS) { xcb_button_press_event_t* buttonPressEvent = static_cast<xcb_button_press_event_t*>(p_message); // Do stuff } } }
But we never fall in xcbEventType == XCB_BUTTON_PRESS, it seems instead there is (at least for button press/release) a XCB_GE_GENERIC that we can cast to xcb_ge_generic_event_t that has an event_type member that contains for example a XCB_BUTTON_PRESS, the code then changed to:
bool MyXcbEventFilter::nativeEventFilter(const QByteArray& p_eventType, void* p_message, long*) { if (p_eventType == "xcb_generic_event_t") { xcb_generic_event_t* ev = static_cast<xcb_generic_event_t *>(p_message); uint16_t xcbEventType = ev->response_type & ~0x80; if (xcb_eventType == XCB_GE_GENERIC) { xcb_ge_generic_event_t* genericEvent = static_cast<xcb_ge_generic_event_t*>(p_message); xcbEventType = genericEvent->event_type; } if (xcbEventType == XCB_BUTTON_PRESS) { xcb_button_press_event_t* buttonPressEvent = static_cast<xcb_button_press_event_t*>(p_message); // Do stuff } } }
Now we fall in xcbEventType == XCB_BUTTON_PRESS on button press but casting to xcb_button_press_event_t seems to have no meaning since root_x, root_y, event_y and event_y members have always the same values anywhere we click.
Anyone knows how to properly implement this ?
Thanks
-
Since Qt is opensource you can take a look to the place where the eventFilter is called:
https://code.woboq.org/qt5/qtbase/src/plugins/platforms/xcb/qxcbconnection.cpp.html#_ZN14QXcbConnection14handleXcbEventEP19xcb_generic_event_t -
@Christian-Ehrlicher Thanks, but if I follow what is happening from the *QXcbConnection::handleXcbEvent(xcb_generic_event_t event) function you pointed, I can see first there is the same
uint response_type = event->response_type & ~0x80
From which I never get a XCB_BUTTON_PRESS, and, if I follow XCB_GE_GENERIC I get, it goes to:
QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
That, for XCB_BUTTON_PRESS calls an event filter got from:
QXcbWindowEventListener *QXcbConnection::windowEventListenerFromId(xcb_window_t id) { return m_mapper.value(id, 0); }
And here I'm lost since QXcbWindowEventListener are added to m_mapper with QXcbConnection::addWindowEventListener I can't seen where it is called, I tried searching where this function is called in other Qt classes and I either found dead ends or classes calling functions just returning false.
-
And in addition, I just tried to implement key press filter and it works fine (we directly got the XCB_KEY_PRESS event type from event->response_type & ~0x80 instead of getting a XCB_GE_GENERIC):
if (xcbEventType == XCB_KEY_PRESS) { xcb_key_press_event_t* keyPressEvent = reinterpret_cast<xcb_key_press_event_t*>(ev); // Here keyPressEvent->detail has its value changing according to key press }
-
Since XCB_BUTTON_PRESS is directly evaluated in handleXcbEvent() I would guess you must be doing something wrong.
-
Indeed, but I'm not sure I'm doing something wrong, since, all I've done is following Qt's instruction on how to replace QApplication::x11EventFilter with a QAbstractNativeEventFilter class implementing its nativeEventFilter and catching eventType == "xcb_generic_event_t", after that, what comes in nativeEventFilter parameters is definitively not something I have the hand on, and then, in my opinion, the real problem here is, for some reason, I got a XCB_GE_GENERIC event instead of XCB_BUTTON_PRESS event, and I would like to know either how to be able, with current behaviour to manage XCB_BUTTON_PRESS event as a XCB_GE_GENERIC event or to have directly XCB_BUTTON_PRESS event (changing a parameter on Qt for example ?) in nativeEventFilter.
-
Hi again,
I came back on this subject after a while since updated to Qt5.13 but unfortunately with no amelioration on behaviour, and I then created a small application to reproduce the issue if it can help (https://we.tl/t-SUhYHVWOTj), and if it can help too, here are which event are logged on mouse click:
virtual bool XcbEventFilter::nativeEventFilter(const QByteArray&, void*, long int*) ; Event type is ["XCB_ALLOC_NAMED_COLOR (uint16_t: 85)"] virtual bool XcbEventFilter::nativeEventFilter(const QByteArray&, void*, long int*) ; Event type is ["XCB_CLIENT_MESSAGE (uint16_t: 33)"] virtual bool XcbEventFilter::nativeEventFilter(const QByteArray&, void*, long int*) ; Event type is ["XCB_PROPERTY_NOTIFY (uint16_t: 28)"] virtual bool XcbEventFilter::nativeEventFilter(const QByteArray&, void*, long int*) ; Event type is ["XCB_GE_GENERIC (uint16_t: 35)"] virtual bool XcbEventFilter::nativeEventFilter(const QByteArray&, void*, long int*) ; Event type is ["XCB_PROPERTY_NOTIFY (uint16_t: 28)"]
And on mouse release:
virtual bool XcbEventFilter::nativeEventFilter(const QByteArray&, void*, long int*) ; Event type is ["XCB_GE_GENERIC (uint16_t: 35)"] virtual bool XcbEventFilter::nativeEventFilter(const QByteArray&, void*, long int*) ; Event type is ["XCB_ALLOC_NAMED_COLOR (uint16_t: 85)"]
I somebody can try (notice I put a timer that writes endl to cout every half second in order to properly separate click from release), is it the same result ?
Or should I follow the handleXcbEvent ? That for what I see is only in QXcbConnection for which I found no documentation. Or follow another way ?
Thanks
-
@Gluttony
I am also doing a QT4 to QT5 port and ran into the same issue. But I have also found the answer.The problem lies in that QT5 will use the XInput extension if it is present (and I think it almost always is). This means your button presses will come via the xcb_input_button_press_event_t (defined in xcb/xinput.h) as opposed to xcb_button_press_event_t. The good news is that most of the fields in the the xcb_button_press_event_t are in the xcb_input_button_press_event_t (I did not find state and same_screen), with the same name and meaning. The bad news is that they are not at the same offsets, so you cannot just do a structure map. I ended up creating a helper function that I passed the fields I was interested in.
Now the even is delivered via the XCB_GE_GENERIC event. This is one of the common ways for extensions to pass in events. To get to it properly you need to do the following :
- Check if XInput is installed and query xcb for its major opcode. QXcbBasicConnection::initializeXInput2() has a good example of this.
- Check to see if the XCB_GE_GENERIC event is from XInput. This is done by comparing the extension field with the major opcode you got from (1)
- Now the event_type field will contain the extension specific opcodes. For XInput, they are defined in xcb/xinput.h. You then recast and go like you do for normal events.
As an example
void HandleButtonPress(uint8_t button) { ...DoStuff... } // Main thread DoXInputInitialize(); // gets bHasXi2 and iXi2Opcode switch( eventType ) { case XCB_BUTTON_PRESS: auto *pButtonEvent = reinterpret_cast<xcb_button_press_event_t *>(pEvent); HandleButtonPress(pButtonEvent->detail); return true; /// More Stuff case XCB_GE_GENERIC: auto *pGeEvent = reinterpret_cast<xcb_ge_generic_event_t *>(pEvent); if( bHasXi2 && iXi2Opcode == pGeEvent->extension ) { // This is from XInput switch( pGeEvent->event_type ) { case XCB_INPUT_BUTTON_PRESS: auto *pXiButtonEvent = reinterpret_cast<xcb_input_button_press_event_t *>(pGeEvent); HandleButtonPress(pXiButtonEvent->detail); return true; } } }
I hope this answers your issues.
Dale Pennington