Implement XDnD in the xcb plugin

Ported most of the code to support dragging
from qdnd_x11.cpp to xcb. Some features are still
not working 100% correct, but it's becoming
usable.

Reviewed-by: Samuel
This commit is contained in:
Lars Knoll 2011-06-05 23:29:26 +02:00
parent 30b7c6512c
commit c3f9de6296
8 changed files with 656 additions and 752 deletions

View File

@ -143,6 +143,7 @@ QDragManager::QDragManager()
#ifdef Q_WS_X11 #ifdef Q_WS_X11
xdndMimeTransferedPixmapIndex = 0; xdndMimeTransferedPixmapIndex = 0;
#endif #endif
shapedPixmapWindow = 0;
possible_actions = Qt::IgnoreAction; possible_actions = Qt::IgnoreAction;
@ -298,50 +299,9 @@ static const char *const default_pm[] = {
static Qt::KeyboardModifiers oldstate; static Qt::KeyboardModifiers oldstate;
class QShapedPixmapWindow : public QWindow {
QPixmap pixmap;
public:
QShapedPixmapWindow() :
QWindow(0)
{
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint);
// ### Should we set the surface type to raster?
// ### FIXME
// setAttribute(Qt::WA_TransparentForMouseEvents);
}
void move(const QPoint &p) {
QRect g = geometry();
g.setTopLeft(p);
setGeometry(g);
}
void setPixmap(QPixmap pm)
{
pixmap = pm;
// ###
// if (!pixmap.mask().isNull()) {
// setMask(pixmap.mask());
// } else {
// clearMask();
// }
setGeometry(QRect(geometry().topLeft(), pm.size()));
}
// ### Get it painted again!
// void paintEvent(QPaintEvent*)
// {
// QPainter p(this);
// p.drawPixmap(0,0,pixmap);
// }
};
static QShapedPixmapWindow *qt_qws_dnd_deco = 0;
void QDragManager::updatePixmap() void QDragManager::updatePixmap()
{ {
if (qt_qws_dnd_deco) { if (shapedPixmapWindow) {
QPixmap pm; QPixmap pm;
QPoint pm_hot(default_pm_hotx,default_pm_hoty); QPoint pm_hot(default_pm_hotx,default_pm_hoty);
if (object) { if (object) {
@ -354,12 +314,12 @@ void QDragManager::updatePixmap()
defaultPm = new QPixmap(default_pm); defaultPm = new QPixmap(default_pm);
pm = *defaultPm; pm = *defaultPm;
} }
qt_qws_dnd_deco->setPixmap(pm); shapedPixmapWindow->setPixmap(pm);
qt_qws_dnd_deco->move(QCursor::pos()-pm_hot); shapedPixmapWindow->move(QCursor::pos()-pm_hot);
if (willDrop) { if (willDrop) {
qt_qws_dnd_deco->show(); shapedPixmapWindow->show();
} else { } else {
qt_qws_dnd_deco->hide(); shapedPixmapWindow->hide();
} }
} }
} }
@ -368,8 +328,8 @@ void QDragManager::updateCursor()
{ {
#ifndef QT_NO_CURSOR #ifndef QT_NO_CURSOR
if (willDrop) { if (willDrop) {
if (qt_qws_dnd_deco) if (shapedPixmapWindow)
qt_qws_dnd_deco->show(); shapedPixmapWindow->show();
if (currentActionForOverrideCursor != global_accepted_action) { if (currentActionForOverrideCursor != global_accepted_action) {
QGuiApplication::changeOverrideCursor(QCursor(dragCursor(global_accepted_action), 0, 0)); QGuiApplication::changeOverrideCursor(QCursor(dragCursor(global_accepted_action), 0, 0));
currentActionForOverrideCursor = global_accepted_action; currentActionForOverrideCursor = global_accepted_action;
@ -380,8 +340,8 @@ void QDragManager::updateCursor()
QGuiApplication::changeOverrideCursor(QCursor(Qt::ForbiddenCursor)); QGuiApplication::changeOverrideCursor(QCursor(Qt::ForbiddenCursor));
currentActionForOverrideCursor = Qt::IgnoreAction; currentActionForOverrideCursor = Qt::IgnoreAction;
} }
if (qt_qws_dnd_deco) if (shapedPixmapWindow)
qt_qws_dnd_deco->hide(); shapedPixmapWindow->hide();
} }
#endif #endif
} }
@ -468,8 +428,8 @@ Qt::DropAction QDragManager::drag(QDrag *o)
} }
object = o; object = o;
if (!qt_qws_dnd_deco) if (!shapedPixmapWindow)
qt_qws_dnd_deco = new QShapedPixmapWindow(); shapedPixmapWindow = new QShapedPixmapWindow();
oldstate = Qt::NoModifier; // #### Should use state that caused the drag oldstate = Qt::NoModifier; // #### Should use state that caused the drag
// drag_mode = mode; // drag_mode = mode;
@ -494,8 +454,8 @@ Qt::DropAction QDragManager::drag(QDrag *o)
delete eventLoop; delete eventLoop;
eventLoop = 0; eventLoop = 0;
delete qt_qws_dnd_deco; delete shapedPixmapWindow;
qt_qws_dnd_deco = 0; shapedPixmapWindow = 0;
return global_accepted_action; return global_accepted_action;
} }

View File

@ -59,6 +59,7 @@
#include "QtGui/qdrag.h" #include "QtGui/qdrag.h"
#include "QtGui/qpixmap.h" #include "QtGui/qpixmap.h"
#include "QtGui/qcursor.h" #include "QtGui/qcursor.h"
#include "QtGui/qwindow.h"
#include "QtCore/qpoint.h" #include "QtCore/qpoint.h"
#include "private/qobject_p.h" #include "private/qobject_p.h"
@ -108,6 +109,44 @@ public:
Qt::DropAction defaultDropAction; Qt::DropAction defaultDropAction;
}; };
class QShapedPixmapWindow : public QWindow {
QPixmap pixmap;
public:
QShapedPixmapWindow() :
QWindow(0)
{
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint);
// ### Should we set the surface type to raster?
// ### FIXME
// setAttribute(Qt::WA_TransparentForMouseEvents);
}
void move(const QPoint &p) {
QRect g = geometry();
g.setTopLeft(p);
setGeometry(g);
}
void setPixmap(QPixmap pm)
{
pixmap = pm;
// ###
// if (!pixmap.mask().isNull()) {
// setMask(pixmap.mask());
// } else {
// clearMask();
// }
setGeometry(QRect(geometry().topLeft(), pm.size()));
}
// ### Get it painted again!
// void paintEvent(QPaintEvent*)
// {
// QPainter p(this);
// p.drawPixmap(0,0,pixmap);
// }
};
class Q_GUI_EXPORT QDragManager : public QObject { class Q_GUI_EXPORT QDragManager : public QObject {
Q_OBJECT Q_OBJECT
@ -159,6 +198,8 @@ public:
// Shift/Ctrl handling, and final drop status // Shift/Ctrl handling, and final drop status
Qt::DropAction global_accepted_action; Qt::DropAction global_accepted_action;
QShapedPixmapWindow *shapedPixmapWindow;
private: private:
QMimeData *platformDropData; QMimeData *platformDropData;

View File

@ -228,7 +228,7 @@ void QXcbClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
*d = data; *d = data;
} }
xcb_set_selection_owner(m_connection->xcb_connection(), newOwner, modeAtom, XCB_CURRENT_TIME); xcb_set_selection_owner(m_connection->xcb_connection(), newOwner, modeAtom, m_connection->time());
if (getSelectionOwner(modeAtom) != newOwner) { if (getSelectionOwner(modeAtom) != newOwner) {
qWarning("QClipboard::setData: Cannot set X11 selection owner"); qWarning("QClipboard::setData: Cannot set X11 selection owner");
@ -390,7 +390,7 @@ void QXcbClipboard::handleSelectionRequest(xcb_selection_request_event_t *req)
} else if (req->selection == m_connection->atom(QXcbAtom::CLIPBOARD)) { } else if (req->selection == m_connection->atom(QXcbAtom::CLIPBOARD)) {
d = m_clientClipboard; d = m_clientClipboard;
} else { } else {
qWarning("QClipboard: Unknown selection '%lx'", (long)req->selection); qWarning() << "QClipboard: Unknown selection" << m_connection->atomName(req->selection);
xcb_send_event(m_connection->xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event); xcb_send_event(m_connection->xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event);
return; return;
} }
@ -505,7 +505,7 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
format = &dummy_format; format = &dummy_format;
// Don't read anything, just get the size of the property data // Don't read anything, just get the size of the property data
xcb_get_property_cookie_t cookie = xcb_get_property(m_connection->xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, 0, 0); xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(m_connection->xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, 0, 0));
xcb_get_property_reply_t *reply = xcb_get_property_reply(m_connection->xcb_connection(), cookie, 0); xcb_get_property_reply_t *reply = xcb_get_property_reply(m_connection->xcb_connection(), cookie, 0);
if (!reply || reply->type == XCB_NONE) { if (!reply || reply->type == XCB_NONE) {
buffer->resize(0); buffer->resize(0);
@ -546,7 +546,7 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
while (bytes_left) { while (bytes_left) {
// more to read... // more to read...
xcb_get_property_cookie_t cookie = xcb_get_property(m_connection->xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, offset, maxsize/4); xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(m_connection->xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, offset, maxsize/4));
reply = xcb_get_property_reply(m_connection->xcb_connection(), cookie, 0); reply = xcb_get_property_reply(m_connection->xcb_connection(), cookie, 0);
if (!reply || reply->type == XCB_NONE) { if (!reply || reply->type == XCB_NONE) {
free(reply); free(reply);

View File

@ -109,6 +109,8 @@ QXcbConnection::QXcbConnection(const char *displayName)
initializeAllAtoms(); initializeAllAtoms();
m_time = XCB_CURRENT_TIME;
xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup); xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup);
int screenNumber = 0; int screenNumber = 0;
@ -450,7 +452,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
case XCB_UNMAP_NOTIFY: case XCB_UNMAP_NOTIFY:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_unmap_notify_event_t, event, handleUnmapNotifyEvent); HANDLE_PLATFORM_WINDOW_EVENT(xcb_unmap_notify_event_t, event, handleUnmapNotifyEvent);
case XCB_CLIENT_MESSAGE: case XCB_CLIENT_MESSAGE:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_client_message_event_t, window, handleClientMessageEvent); handleClientMessageEvent((xcb_client_message_event_t *)event);
case XCB_ENTER_NOTIFY: case XCB_ENTER_NOTIFY:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent); HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent);
case XCB_LEAVE_NOTIFY: case XCB_LEAVE_NOTIFY:
@ -467,9 +469,16 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
m_keyboard->handleMappingNotifyEvent((xcb_mapping_notify_event_t *)event); m_keyboard->handleMappingNotifyEvent((xcb_mapping_notify_event_t *)event);
break; break;
case XCB_SELECTION_REQUEST: case XCB_SELECTION_REQUEST:
m_clipboard->handleSelectionRequest((xcb_selection_request_event_t *)event); {
xcb_selection_request_event_t *sr = (xcb_selection_request_event_t *)event;
if (sr->selection == atom(QXcbAtom::XdndSelection))
m_drag->handleSelectionRequest(sr);
else
m_clipboard->handleSelectionRequest(sr);
break; break;
}
case XCB_SELECTION_CLEAR: case XCB_SELECTION_CLEAR:
setTime(((xcb_selection_clear_event_t *)event)->time);
qDebug() << "XCB_SELECTION_CLEAR"; qDebug() << "XCB_SELECTION_CLEAR";
handled = false; handled = false;
break; break;
@ -477,6 +486,11 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
qDebug() << "XCB_SELECTION_NOTIFY"; qDebug() << "XCB_SELECTION_NOTIFY";
handled = false; handled = false;
break; break;
case XCB_PROPERTY_NOTIFY:
setTime(((xcb_property_notify_event_t *)event)->time);
// qDebug() << "XCB_PROPERTY_NOTIFY";
handled = false;
break;
default: default:
handled = false; handled = false;
break; break;
@ -533,6 +547,25 @@ void QXcbConnection::processXcbEvents()
xcb_flush(xcb_connection()); xcb_flush(xcb_connection());
} }
void QXcbConnection::handleClientMessageEvent(const xcb_client_message_event_t *event)
{
if (event->format != 32)
return;
if (event->type == atom(QXcbAtom::XdndStatus)) {
drag()->handleStatus(event, false);
} else if (event->type == atom(QXcbAtom::XdndFinished)) {
drag()->handleFinished(event, false);
}
QXcbWindow *window = platformWindowFromId(event->window);
if (!window)
return;
window->handleClientMessageEvent(event);
}
xcb_generic_event_t *QXcbConnection::checkEvent(int type) xcb_generic_event_t *QXcbConnection::checkEvent(int type)
{ {
while (xcb_generic_event_t *event = xcb_poll_for_event(xcb_connection())) while (xcb_generic_event_t *event = xcb_poll_for_event(xcb_connection()))

View File

@ -238,7 +238,7 @@ public:
QXcbConnection *connection() const { return const_cast<QXcbConnection *>(this); } QXcbConnection *connection() const { return const_cast<QXcbConnection *>(this); }
QList<QXcbScreen *> screens() const { return m_screens; } const QList<QXcbScreen *> &screens() const { return m_screens; }
int primaryScreen() const { return m_primaryScreen; } int primaryScreen() const { return m_primaryScreen; }
xcb_atom_t atom(QXcbAtom::Atom atom); xcb_atom_t atom(QXcbAtom::Atom atom);
@ -290,6 +290,9 @@ public:
typedef bool (*PeekFunc)(xcb_generic_event_t *); typedef bool (*PeekFunc)(xcb_generic_event_t *);
void addPeekFunc(PeekFunc f); void addPeekFunc(PeekFunc f);
inline xcb_timestamp_t time() const { return m_time; }
inline void setTime(xcb_timestamp_t t) { if (t > m_time) m_time = t; }
private slots: private slots:
void processXcbEvents(); void processXcbEvents();
@ -299,6 +302,7 @@ private:
#ifdef XCB_USE_DRI2 #ifdef XCB_USE_DRI2
void initializeDri2(); void initializeDri2();
#endif #endif
void handleClientMessageEvent(const xcb_client_message_event_t *event);
xcb_connection_t *m_connection; xcb_connection_t *m_connection;
const xcb_setup_t *m_setup; const xcb_setup_t *m_setup;
@ -308,6 +312,8 @@ private:
xcb_atom_t m_allAtoms[QXcbAtom::NAtoms]; xcb_atom_t m_allAtoms[QXcbAtom::NAtoms];
xcb_timestamp_t m_time;
QByteArray m_displayName; QByteArray m_displayName;
QXcbKeyboard *m_keyboard; QXcbKeyboard *m_keyboard;

File diff suppressed because it is too large Load Diff

View File

@ -49,6 +49,7 @@
#include <qpoint.h> #include <qpoint.h>
#include <qrect.h> #include <qrect.h>
#include <qsharedpointer.h> #include <qsharedpointer.h>
#include <qvector.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -57,8 +58,10 @@ class QWindow;
class QXcbConnection; class QXcbConnection;
class QXcbWindow; class QXcbWindow;
class QDropData; class QDropData;
class QXcbScreen;
class QDrag;
class QXcbDrag : public QPlatformDrag class QXcbDrag : public QObject, public QPlatformDrag
{ {
public: public:
QXcbDrag(QXcbConnection *c); QXcbDrag(QXcbConnection *c);
@ -72,22 +75,32 @@ public:
virtual void cancel(); virtual void cancel();
virtual void move(const QMouseEvent *me); virtual void move(const QMouseEvent *me);
virtual void drop(const QMouseEvent *me); virtual void drop(const QMouseEvent *me);
void endDrag();
void handleEnter(QWindow *window, const xcb_client_message_event_t *event); void handleEnter(QWindow *window, const xcb_client_message_event_t *event);
void handlePosition(QWindow *w, const xcb_client_message_event_t *event, bool passive); void handlePosition(QWindow *w, const xcb_client_message_event_t *event, bool passive);
void handleStatus(QWindow *w, const xcb_client_message_event_t *event, bool passive);
void handleLeave(QWindow *w, const xcb_client_message_event_t *event, bool /*passive*/); void handleLeave(QWindow *w, const xcb_client_message_event_t *event, bool /*passive*/);
void handleDrop(QWindow *, const xcb_client_message_event_t *event, bool passive); void handleDrop(QWindow *, const xcb_client_message_event_t *event, bool passive);
void handleStatus(const xcb_client_message_event_t *event, bool passive);
void handleSelectionRequest(const xcb_selection_request_event_t *event);
void handleFinished(const xcb_client_message_event_t *event, bool passive);
bool dndEnable(QXcbWindow *win, bool on); bool dndEnable(QXcbWindow *win, bool on);
QXcbConnection *connection() const { return m_connection; } QXcbConnection *connection() const { return m_connection; }
protected:
void timerEvent(QTimerEvent* e);
private: private:
friend class QDropData; friend class QDropData;
void init();
void handle_xdnd_position(QWindow *w, const xcb_client_message_event_t *event, bool passive); void handle_xdnd_position(QWindow *w, const xcb_client_message_event_t *event, bool passive);
void handle_xdnd_status(QWindow *, const xcb_client_message_event_t *event, bool); void handle_xdnd_status(const xcb_client_message_event_t *event, bool);
void send_leave();
Qt::DropAction toDropAction(xcb_atom_t atom) const; Qt::DropAction toDropAction(xcb_atom_t atom) const;
xcb_atom_t toXdndAction(Qt::DropAction a) const; xcb_atom_t toXdndAction(Qt::DropAction a) const;
@ -107,14 +120,40 @@ private:
QList<xcb_atom_t> xdnd_types; QList<xcb_atom_t> xdnd_types;
xcb_timestamp_t target_time; xcb_timestamp_t target_time;
xcb_timestamp_t source_time;
Qt::DropAction last_target_accepted_action; Qt::DropAction last_target_accepted_action;
// rectangle in which the answer will be the same // rectangle in which the answer will be the same
QRect source_sameanswer; QRect source_sameanswer;
bool waiting_for_status; bool waiting_for_status;
// top-level window we sent position to last.
xcb_window_t current_target;
// window to send events to (always valid if current_target) // window to send events to (always valid if current_target)
xcb_window_t current_proxy_target; xcb_window_t current_proxy_target;
QXcbScreen *current_screen;
int heartbeat;
bool xdnd_dragging;
QVector<xcb_atom_t> drag_types;
struct Transaction
{
xcb_timestamp_t timestamp;
xcb_window_t target;
xcb_window_t proxy_target;
QWindow *targetWindow;
// QWidget *embedding_widget;
QDrag *object;
};
QList<Transaction> transactions;
int transaction_expiry_timer;
void restartDropExpiryTimer();
int findTransactionByWindow(xcb_window_t window);
int findTransactionByTime(xcb_timestamp_t timestamp);
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -944,6 +944,9 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
if (event->type == atom(QXcbAtom::WM_PROTOCOLS)) { if (event->type == atom(QXcbAtom::WM_PROTOCOLS)) {
if (event->data.data32[0] == atom(QXcbAtom::WM_DELETE_WINDOW)) { if (event->data.data32[0] == atom(QXcbAtom::WM_DELETE_WINDOW)) {
QWindowSystemInterface::handleCloseEvent(window()); QWindowSystemInterface::handleCloseEvent(window());
} else if (event->data.data32[0] == atom(QXcbAtom::WM_TAKE_FOCUS)) {
connection()->setTime(event->data.data32[1]);
// ### handle take focus!
} else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_PING)) { } else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_PING)) {
xcb_client_message_event_t reply = *event; xcb_client_message_event_t reply = *event;
@ -953,6 +956,7 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
xcb_send_event(xcb_connection(), 0, m_screen->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&reply); xcb_send_event(xcb_connection(), 0, m_screen->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&reply);
xcb_flush(xcb_connection()); xcb_flush(xcb_connection());
} else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_SYNC_REQUEST)) { } else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_SYNC_REQUEST)) {
connection()->setTime(event->data.data32[1]);
if (!m_hasReceivedSyncRequest) { if (!m_hasReceivedSyncRequest) {
m_hasReceivedSyncRequest = true; m_hasReceivedSyncRequest = true;
printf("Window manager supports _NET_WM_SYNC_REQUEST, syncing resizes\n"); printf("Window manager supports _NET_WM_SYNC_REQUEST, syncing resizes\n");
@ -1077,6 +1081,8 @@ void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event)
void QXcbWindow::handleMouseEvent(xcb_button_t detail, uint16_t state, xcb_timestamp_t time, const QPoint &local, const QPoint &global) void QXcbWindow::handleMouseEvent(xcb_button_t detail, uint16_t state, xcb_timestamp_t time, const QPoint &local, const QPoint &global)
{ {
connection()->setTime(time);
Qt::MouseButtons buttons = translateMouseButtons(state); Qt::MouseButtons buttons = translateMouseButtons(state);
Qt::MouseButton button = translateMouseButton(detail); Qt::MouseButton button = translateMouseButton(detail);
@ -1087,6 +1093,8 @@ void QXcbWindow::handleMouseEvent(xcb_button_t detail, uint16_t state, xcb_times
void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event) void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event)
{ {
connection()->setTime(event->time);
if ((event->mode != XCB_NOTIFY_MODE_NORMAL && event->mode != XCB_NOTIFY_MODE_UNGRAB) if ((event->mode != XCB_NOTIFY_MODE_NORMAL && event->mode != XCB_NOTIFY_MODE_UNGRAB)
|| event->detail == XCB_NOTIFY_DETAIL_VIRTUAL || event->detail == XCB_NOTIFY_DETAIL_VIRTUAL
|| event->detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL) || event->detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL)
@ -1099,6 +1107,8 @@ void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event)
void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event) void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event)
{ {
connection()->setTime(event->time);
if ((event->mode != XCB_NOTIFY_MODE_NORMAL && event->mode != XCB_NOTIFY_MODE_UNGRAB) if ((event->mode != XCB_NOTIFY_MODE_NORMAL && event->mode != XCB_NOTIFY_MODE_UNGRAB)
|| event->detail == XCB_NOTIFY_DETAIL_INFERIOR) || event->detail == XCB_NOTIFY_DETAIL_INFERIOR)
{ {