From c3f9de62966d32d8e33d62eb374fe2657a4cfebe Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 5 Jun 2011 23:29:26 +0200 Subject: 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 --- src/plugins/platforms/xcb/qxcbclipboard.cpp | 8 +- src/plugins/platforms/xcb/qxcbconnection.cpp | 37 +- src/plugins/platforms/xcb/qxcbconnection.h | 8 +- src/plugins/platforms/xcb/qxcbdrag.cpp | 1191 +++++++++++--------------- src/plugins/platforms/xcb/qxcbdrag.h | 45 +- src/plugins/platforms/xcb/qxcbwindow.cpp | 10 + 6 files changed, 601 insertions(+), 698 deletions(-) (limited to 'src/plugins/platforms') diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp index 8857f280db..6ae0299547 100644 --- a/src/plugins/platforms/xcb/qxcbclipboard.cpp +++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp @@ -228,7 +228,7 @@ void QXcbClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) *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) { 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)) { d = m_clientClipboard; } 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); return; } @@ -505,7 +505,7 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property, format = &dummy_format; // 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); if (!reply || reply->type == XCB_NONE) { buffer->resize(0); @@ -546,7 +546,7 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property, while (bytes_left) { // 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); if (!reply || reply->type == XCB_NONE) { free(reply); diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index b719f9d7ed..fa595215a1 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -109,6 +109,8 @@ QXcbConnection::QXcbConnection(const char *displayName) initializeAllAtoms(); + m_time = XCB_CURRENT_TIME; + xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup); int screenNumber = 0; @@ -450,7 +452,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) case XCB_UNMAP_NOTIFY: HANDLE_PLATFORM_WINDOW_EVENT(xcb_unmap_notify_event_t, event, handleUnmapNotifyEvent); 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: HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent); 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); break; 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; + } case XCB_SELECTION_CLEAR: + setTime(((xcb_selection_clear_event_t *)event)->time); qDebug() << "XCB_SELECTION_CLEAR"; handled = false; break; @@ -477,6 +486,11 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) qDebug() << "XCB_SELECTION_NOTIFY"; handled = false; break; + case XCB_PROPERTY_NOTIFY: + setTime(((xcb_property_notify_event_t *)event)->time); +// qDebug() << "XCB_PROPERTY_NOTIFY"; + handled = false; + break; default: handled = false; break; @@ -533,6 +547,25 @@ void QXcbConnection::processXcbEvents() 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) { while (xcb_generic_event_t *event = xcb_poll_for_event(xcb_connection())) diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 136fbca8f8..401d4465b3 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -238,7 +238,7 @@ public: QXcbConnection *connection() const { return const_cast(this); } - QList screens() const { return m_screens; } + const QList &screens() const { return m_screens; } int primaryScreen() const { return m_primaryScreen; } xcb_atom_t atom(QXcbAtom::Atom atom); @@ -290,6 +290,9 @@ public: typedef bool (*PeekFunc)(xcb_generic_event_t *); 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: void processXcbEvents(); @@ -299,6 +302,7 @@ private: #ifdef XCB_USE_DRI2 void initializeDri2(); #endif + void handleClientMessageEvent(const xcb_client_message_event_t *event); xcb_connection_t *m_connection; const xcb_setup_t *m_setup; @@ -308,6 +312,8 @@ private: xcb_atom_t m_allAtoms[QXcbAtom::NAtoms]; + xcb_timestamp_t m_time; + QByteArray m_displayName; QXcbKeyboard *m_keyboard; diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp index bb515c37ad..bb2ed4797a 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.cpp +++ b/src/plugins/platforms/xcb/qxcbdrag.cpp @@ -45,6 +45,7 @@ #include "qxcbclipboard.h" #include "qxcbmime.h" #include "qxcbwindow.h" +#include "qxcbscreen.h" #include "qwindow.h" #include #include @@ -74,6 +75,41 @@ static inline xcb_window_t xcb_window(QWindow *w) return static_cast(w->handle())->xcb_window(); } + +static xcb_window_t xdndProxy(QXcbConnection *c, xcb_window_t w) +{ + xcb_window_t proxy = XCB_NONE; + + xcb_get_property_cookie_t cookie = Q_XCB_CALL2(xcb_get_property(c->xcb_connection(), false, w, c->atom(QXcbAtom::XdndProxy), + QXcbAtom::XA_WINDOW, 0, 1), c); + xcb_get_property_reply_t *reply = xcb_get_property_reply(c->xcb_connection(), cookie, 0); + + if (reply && reply->type == QXcbAtom::XA_WINDOW) + proxy = *((xcb_window_t *)xcb_get_property_value(reply)); + free(reply); + + if (proxy == XCB_NONE) + return proxy; + + // exists and is real? + cookie = Q_XCB_CALL2(xcb_get_property(c->xcb_connection(), false, proxy, c->atom(QXcbAtom::XdndProxy), + QXcbAtom::XA_WINDOW, 0, 1), c); + reply = xcb_get_property_reply(c->xcb_connection(), cookie, 0); + + if (reply && reply->type == QXcbAtom::XA_WINDOW) { + xcb_window_t p = *((xcb_window_t *)xcb_get_property_value(reply)); + if (proxy != p) + proxy = 0; + } else { + proxy = 0; + } + + free(reply); + + return proxy; +} + + class QDropData : public QXcbMime { public: @@ -96,7 +132,10 @@ QXcbDrag::QXcbDrag(QXcbConnection *c) m_connection = c; dropData = new QDropData(this); - startDrag(); // init variables + init(); + heartbeat = -1; + + transaction_expiry_timer = -1; } QXcbDrag::~QXcbDrag() @@ -104,6 +143,24 @@ QXcbDrag::~QXcbDrag() delete dropData; } +void QXcbDrag::init() +{ + currentWindow.clear(); + + xdnd_dragsource = XCB_NONE; + last_target_accepted_action = Qt::IgnoreAction; + + waiting_for_status = false; + current_target = XCB_NONE; + current_proxy_target = XCB_NONE; + xdnd_dragging = false; + + source_time = XCB_CURRENT_TIME; + target_time = XCB_CURRENT_TIME; + + current_screen = 0; + drag_types.clear(); +} QMimeData *QXcbDrag::platformDropData() { @@ -112,28 +169,317 @@ QMimeData *QXcbDrag::platformDropData() void QXcbDrag::startDrag() { - currentWindow.clear(); + // #### +// if (object) { +// // the last drag and drop operation hasn't finished, so we are going to wait +// // for one second to see if it does... if the finish message comes after this, +// // then we could still have problems, but this is highly unlikely +// QGuiApplication::flush(); + +// QElapsedTimer timer; +// timer.start(); +// do { +// XEvent event; +// if (XCheckTypedEvent(X11->display, ClientMessage, &event)) +// qApp->x11ProcessEvent(&event); + +// // sleep 50 ms, so we don't use up CPU cycles all the time. +// struct timeval usleep_tv; +// usleep_tv.tv_sec = 0; +// usleep_tv.tv_usec = 50000; +// select(0, 0, 0, 0, &usleep_tv); +// } while (object && timer.hasExpired(1000)); +// } - xdnd_dragsource = XCB_NONE; - last_target_accepted_action = Qt::IgnoreAction; + init(); - waiting_for_status = false; - current_proxy_target = XCB_NONE; + heartbeat = startTimer(200); + xdnd_dragging = true; + + xcb_set_selection_owner(connection()->xcb_connection(), connection()->clipboard()->owner(), + connection()->atom(QXcbAtom::XdndSelection), connection()->time()); + + QDragManager *manager = QDragManager::self(); + QStringList fmts = QXcbMime::formatsHelper(manager->dropData()); + for (int i = 0; i < fmts.size(); ++i) { + QList atoms = QXcbMime::mimeAtomsForFormat(connection(), fmts.at(i)); + for (int j = 0; j < atoms.size(); ++j) { + if (!drag_types.contains(atoms.at(j))) + drag_types.append(atoms.at(j)); + } + } + if (drag_types.size() > 3) + xcb_change_property(connection()->xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->clipboard()->owner(), + connection()->atom(QXcbAtom::XdndTypelist), + QXcbAtom::XA_ATOM, 32, drag_types.size(), (const void *)drag_types.constData()); + + QMouseEvent me(QEvent::MouseMove, QCursor::pos(), QCursor::pos(), Qt::LeftButton, + QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers()); + move(&me); + +// if (!QWidget::mouseGrabber()) +// manager->shapedPixmapWindow->grabMouse(); } -void QXcbDrag::cancel() +void QXcbDrag::endDrag() +{ + Q_ASSERT(heartbeat != -1); + killTimer(heartbeat); + heartbeat = -1; + + xdnd_dragging = false; +} + +static xcb_translate_coordinates_reply_t * +translateCoordinates(QXcbConnection *c, xcb_window_t from, xcb_window_t to, int x, int y) { - //### + xcb_translate_coordinates_cookie_t cookie = + xcb_translate_coordinates(c->xcb_connection(), from, to, x, y); + return xcb_translate_coordinates_reply(c->xcb_connection(), cookie, 0); } void QXcbDrag::move(const QMouseEvent *me) { + DEBUG() << "QDragManager::move enter"; + // ### + QPoint globalPos = me->globalPos(); + + if (source_sameanswer.contains(globalPos) && source_sameanswer.isValid()) + return; + + const QList &screens = connection()->screens(); + QXcbScreen *screen = screens.at(connection()->primaryScreen()); + for (int i = 0; i < screens.size(); ++i) { + if (screens.at(i)->geometry().contains(globalPos)) { + screen = screens.at(i); + break; + } + } + if (screen != current_screen) { + // ### need to recreate the shaped pixmap window? +// int screen = QCursor::x11Screen(); +// if ((qt_xdnd_current_screen == -1 && screen != X11->defaultScreen) || (screen != qt_xdnd_current_screen)) { +// // recreate the pixmap on the new screen... +// delete xdnd_data.deco; +// QWidget* parent = object->source()->window()->x11Info().screen() == screen +// ? object->source()->window() : QApplication::desktop()->screen(screen); +// xdnd_data.deco = new QShapedPixmapWidget(parent); +// if (!QWidget::mouseGrabber()) { +// updatePixmap(); +// xdnd_data.deco->grabMouse(); +// } +// } +// xdnd_data.deco->move(QCursor::pos() - xdnd_data.deco->pm_hot); + current_screen = screen; + } + + +// qt_xdnd_current_screen = screen; + xcb_window_t rootwin = current_screen->root(); + xcb_translate_coordinates_reply_t *translate = + ::translateCoordinates(connection(), rootwin, rootwin, globalPos.x(), globalPos.y()); + if (!translate) + return; + xcb_window_t target = translate->child; + int lx = translate->dst_x; + int ly = translate->dst_y; + free (translate); + + if (target == rootwin) { + // Ok. + } else if (target) { + //me + xcb_window_t src = rootwin; + while (target != 0) { + DNDDEBUG << "checking target for XdndAware" << target; + + // translate coordinates + translate = ::translateCoordinates(connection(), src, target, lx, ly); + if (!translate) { + target = 0; + break; + } + lx = translate->dst_x; + ly = translate->dst_y; + src = translate->child; + free(translate); + + // check if it has XdndAware + xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(connection()->xcb_connection(), false, target, + connection()->atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0)); + xcb_get_property_reply_t *reply = xcb_get_property_reply(m_connection->xcb_connection(), cookie, 0); + bool aware = reply && reply->type != XCB_NONE; + free(reply); + if (aware) { + DNDDEBUG << "Found XdndAware on " << target; + break; + } + + // find child at the coordinates + translate = ::translateCoordinates(connection(), src, src, lx, ly); + if (!translate) { + target = 0; + break; + } + target = translate->child; + free(translate); + } + // #### +// if (xdnd_data.deco && (!target || target == xdnd_data.deco->effectiveWinId())) { +// DNDDEBUG << "need to find real window"; +// target = findRealWindow(globalPos, rootwin, 6); +// DNDDEBUG << "real window found" << QWidget::find(target) << target; +// } + } + + QXcbWindow *w = 0; + if (target) { + w = connection()->platformWindowFromId(target); + if (w && (w->window()->windowType() == Qt::Desktop) /*&& !w->acceptDrops()*/) + w = 0; + } else { + w = 0; + target = rootwin; + } + + DNDDEBUG << "and the final target is " << target; + DNDDEBUG << "the widget w is" << (w ? w->window() : 0); + + xcb_window_t proxy_target = xdndProxy(connection(), target); + if (!proxy_target) + proxy_target = target; + int target_version = 1; + + if (proxy_target) { + xcb_get_property_cookie_t cookie = xcb_get_property(connection()->xcb_connection(), false, target, + connection()->atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 1); + xcb_get_property_reply_t *reply = xcb_get_property_reply(m_connection->xcb_connection(), cookie, 0); + if (!reply || reply->type == XCB_NONE) + target = 0; + target_version = xcb_get_property_value_length(reply) == 1 ? *(uint32_t *)xcb_get_property_value(reply) : 1; + if (target_version > xdnd_version) + target_version = xdnd_version; + + free(reply); + } + + DEBUG() << "target=" << target << "current_target=" << current_target; + if (target != current_target) { + if (current_target) + send_leave(); + + current_target = target; + current_proxy_target = proxy_target; + if (target) { + int flags = target_version << 24; + if (drag_types.size() > 3) + flags |= 0x0001; + + xcb_client_message_event_t enter; + enter.response_type = XCB_CLIENT_MESSAGE; + enter.window = target; + enter.format = 32; + enter.type = connection()->atom(QXcbAtom::XdndEnter); + enter.data.data32[0] = connection()->clipboard()->owner(); + enter.data.data32[1] = flags; + enter.data.data32[2] = drag_types.size()>0 ? drag_types.at(0) : 0; + enter.data.data32[3] = drag_types.size()>1 ? drag_types.at(1) : 0; + enter.data.data32[4] = drag_types.size()>2 ? drag_types.at(2) : 0; + // provisionally set the rectangle to 5x5 pixels... + source_sameanswer = QRect(globalPos.x() - 2, globalPos.y() -2 , 5, 5); + + DEBUG() << "sending Xdnd enter source=" << enter.data.data32[0]; + if (w) + handleEnter(w->window(), &enter); + else if (target) + xcb_send_event(connection()->xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&enter); + waiting_for_status = false; + } + } + if (waiting_for_status) + return; + + QDragManager *m = QDragManager::self(); + + if (target) { + waiting_for_status = true; + + xcb_client_message_event_t move; + move.response_type = XCB_CLIENT_MESSAGE; + move.window = target; + move.format = 32; + move.type = connection()->atom(QXcbAtom::XdndPosition); + move.window = target; + move.data.data32[0] = connection()->clipboard()->owner(); + move.data.data32[1] = 0; // flags + move.data.data32[2] = (globalPos.x() << 16) + globalPos.y(); + move.data.data32[3] = connection()->time(); + move.data.data32[4] = toXdndAction(m->defaultAction(m->dragPrivate()->possible_actions, QGuiApplication::keyboardModifiers())); + DEBUG() << "sending Xdnd position source=" << move.data.data32[0] << "target=" << move.window; + + source_time = connection()->time(); + + if (w) + handle_xdnd_position(w->window(), &move, false); + else + xcb_send_event(connection()->xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&move); + } else { + if (m->willDrop) { + m->willDrop = false; + m->updateCursor(); + } + } + DEBUG() << "QDragManager::move leave"; } -void QXcbDrag::drop(const QMouseEvent *me) +void QXcbDrag::drop(const QMouseEvent *) { - // ### + endDrag(); + + if (!current_target) + return; + + xcb_client_message_event_t drop; + drop.response_type = XCB_CLIENT_MESSAGE; + drop.window = current_target; + drop.format = 32; + drop.type = connection()->atom(QXcbAtom::XdndDrop); + drop.data.data32[0] = connection()->clipboard()->owner(); + drop.data.data32[1] = 0; // flags + drop.data.data32[2] = connection()->time(); + + drop.data.data32[3] = 0; + drop.data.data32[4] = 0; + + QXcbWindow *w = connection()->platformWindowFromId(current_proxy_target); + + if (w && (w->window()->windowType() == Qt::Desktop) /*&& !w->acceptDrops()*/) + w = 0; + + QDragManager *manager = QDragManager::self(); + + Transaction t = { + connection()->time(), + current_target, + current_proxy_target, + (w ? w->window() : 0), +// current_embedding_widget, + manager->object + }; + transactions.append(t); + restartDropExpiryTimer(); + + if (w) + handleDrop(w->window(), &drop, false); + else + xcb_send_event(connection()->xcb_connection(), false, current_proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&drop); + + current_target = 0; + current_proxy_target = 0; + source_time = 0; +// current_embedding_widget = 0; + manager->object = 0; } #define ATOM(x) connection()->atom(QXcbAtom::x) @@ -168,12 +514,21 @@ xcb_atom_t QXcbDrag::toXdndAction(Qt::DropAction a) const #undef ATOM -#if 0 -static int findXdndDropTransactionByWindow(Window window) +// timer used to discard old XdndDrop transactions +enum { XdndDropTransactionTimeout = 5000 }; // 5 seconds + +void QXcbDrag::restartDropExpiryTimer() +{ + if (transaction_expiry_timer != -1) + killTimer(transaction_expiry_timer); + transaction_expiry_timer = startTimer(XdndDropTransactionTimeout); +} + +int QXcbDrag::findTransactionByWindow(xcb_window_t window) { int at = -1; - for (int i = 0; i < X11->dndDropTransactions.count(); ++i) { - const QXdndDropTransaction &t = X11->dndDropTransactions.at(i); + for (int i = 0; i < transactions.count(); ++i) { + const Transaction &t = transactions.at(i); if (t.target == window || t.proxy_target == window) { at = i; break; @@ -182,11 +537,11 @@ static int findXdndDropTransactionByWindow(Window window) return at; } -static int findXdndDropTransactionByTime(Time timestamp) +int QXcbDrag::findTransactionByTime(xcb_timestamp_t timestamp) { int at = -1; - for (int i = 0; i < X11->dndDropTransactions.count(); ++i) { - const QXdndDropTransaction &t = X11->dndDropTransactions.at(i); + for (int i = 0; i < transactions.count(); ++i) { + const Transaction &t = transactions.at(i); if (t.timestamp == timestamp) { at = i; break; @@ -195,17 +550,7 @@ static int findXdndDropTransactionByTime(Time timestamp) return at; } -// timer used to discard old XdndDrop transactions -static int transaction_expiry_timer = -1; -enum { XdndDropTransactionTimeout = 5000 }; // 5 seconds - -static void restartXdndDropExpiryTimer() -{ - if (transaction_expiry_timer != -1) - QDragManager::self()->killTimer(transaction_expiry_timer); - transaction_expiry_timer = QDragManager::self()->startTimer(XdndDropTransactionTimeout); -} - +#if 0 // find an ancestor with XdndAware on it static Window findXdndAwareParent(Window window) @@ -244,49 +589,15 @@ static Window findXdndAwareParent(Window window) } - - -// clean up the stuff used. -static void qt_xdnd_cleanup(); - -static void qt_xdnd_send_leave(); - - -// timer used when target wants "continuous" move messages (eg. scroll) -static int heartbeat = -1; -// top-level window we sent position to last. -static Window qt_xdnd_current_target; -// window to send events to (always valid if qt_xdnd_current_target) -static Window current_proxy_target; - -// widget we forwarded position to last, and local position -static QPointer currentWindow; -static QPoint currentPosition; -// timestamp from the XdndPosition and XdndDrop -static Time target_time; -// screen number containing the pointer... -1 means default -static int qt_xdnd_current_screen = -1; -// state of dragging... true if dragging, false if not -bool qt_xdnd_dragging = false; - static bool waiting_for_status = false; // used to preset each new QDragMoveEvent -// Shift/Ctrl handling, and final drop status -static Qt::DropAction global_accepted_action = Qt::CopyAction; -static Qt::DropActions possible_actions = Qt::IgnoreAction; // for embedding only static QWidget* current_embedding_widget = 0; static xcb_client_message_event_t last_enter_event; -// cursors -static QCursor *noDropCursor = 0; -static QCursor *moveCursor = 0; -static QCursor *copyCursor = 0; -static QCursor *linkCursor = 0; - class QExtraWidget : public QWidget { @@ -300,32 +611,6 @@ inline QWExtra* QExtraWidget::extraData() { return d_func()->extraData(); } inline QTLWExtra* QExtraWidget::topData() { return d_func()->topData(); } - -void QX11Data::xdndSetup() { - QCursorData::initialize(); - qAddPostRoutine(qt_xdnd_cleanup); -} - - -void qt_xdnd_cleanup() -{ - delete noDropCursor; - noDropCursor = 0; - delete copyCursor; - copyCursor = 0; - delete moveCursor; - moveCursor = 0; - delete linkCursor; - linkCursor = 0; - delete defaultPm; - defaultPm = 0; - delete xdnd_data.desktop_proxy; - xdnd_data.desktop_proxy = 0; - delete xdnd_data.deco; - xdnd_data.deco = 0; -} - - static QWidget *find_child(QWidget *tlw, QPoint & p) { QWidget *widget = tlw; @@ -367,10 +652,10 @@ static bool checkEmbedded(QWidget* w, const XEvent* xe) return false; if (current_embedding_widget != 0 && current_embedding_widget != w) { - qt_xdnd_current_target = ((QExtraWidget*)current_embedding_widget)->extraData()->xDndProxy; - current_proxy_target = qt_xdnd_current_target; + current_target = ((QExtraWidget*)current_embedding_widget)->extraData()->xDndProxy; + current_proxy_target = current_target; qt_xdnd_send_leave(); - qt_xdnd_current_target = 0; + current_target = 0; current_proxy_target = 0; current_embedding_widget = 0; } @@ -403,6 +688,7 @@ void QXcbDrag::handleEnter(QWindow *window, const xcb_client_message_event_t *ev Q_UNUSED(window); DEBUG() << "handleEnter" << window; + xdnd_types.clear(); // motifdnd_active = false; // last_enter_event.xclient = xe->xclient; @@ -447,6 +733,8 @@ void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t p -= geometry.topLeft(); + qDebug() << "handle_xdnd_position" << p; + // #### // if (!passive && checkEmbedded(w, e)) // return; @@ -460,7 +748,7 @@ void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t } // timestamp from the source - if (e->data.data32[3] != 0) + if (e->data.data32[3] != XCB_NONE) target_time /*= X11->userTime*/ = e->data.data32[3]; QDragManager *manager = QDragManager::self(); @@ -538,21 +826,18 @@ void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t if (answerRect.height() < 0) answerRect.setHeight(0); - response.data.data32[2] = (answerRect.x() << 16) + answerRect.y(); - response.data.data32[3] = (answerRect.width() << 16) + answerRect.height(); +// response.data.data32[2] = (answerRect.x() << 16) + answerRect.y(); +// response.data.data32[3] = (answerRect.width() << 16) + answerRect.height(); response.data.data32[4] = toXdndAction(accepted_action); } // reset target_time = XCB_CURRENT_TIME; - QXcbWindow *source = connection()->platformWindowFromId(xdnd_dragsource); - if (source && (source->window()->windowType() == Qt::Desktop) /*&& !source->acceptDrops()*/) - source = 0; - - DEBUG() << "sending XdndStatus"; - if (source) - handle_xdnd_status(source->window(), &response, passive); + DEBUG() << "sending XdndStatus" << (xdnd_dragsource == connection()->clipboard()->owner()) << xdnd_dragsource + << response.data.data32[1] << connection()->atomName(response.data.data32[4]); + if (xdnd_dragsource == connection()->clipboard()->owner()) + handle_xdnd_status(&response, passive); else Q_XCB_CALL(xcb_send_event(connection()->xcb_connection(), false, xdnd_dragsource, XCB_EVENT_MASK_NO_EVENT, (const char *)&response)); @@ -590,8 +875,9 @@ void QXcbDrag::handlePosition(QWindow * w, const xcb_client_message_event_t *eve free(lastEvent); } -void QXcbDrag::handle_xdnd_status(QWindow *, const xcb_client_message_event_t *event, bool) +void QXcbDrag::handle_xdnd_status(const xcb_client_message_event_t *event, bool) { + DEBUG("xdndHandleStatus"); // ignore late status messages if (event->data.data32[0] && event->data.data32[0] != current_proxy_target) return; @@ -611,14 +897,18 @@ void QXcbDrag::handle_xdnd_status(QWindow *, const xcb_client_message_event_t *e manager->global_accepted_action = newAction; manager->emitActionChanged(newAction); } + DEBUG() << "willDrop=" << manager->willDrop << "action=" << newAction; manager->updateCursor(); waiting_for_status = false; } -void QXcbDrag::handleStatus(QWindow *w, const xcb_client_message_event_t *event, bool passive) +void QXcbDrag::handleStatus(const xcb_client_message_event_t *event, bool passive) { - DEBUG("xdndHandleStatus"); + if (event->window != connection()->clipboard()->owner()) + return; + xcb_client_message_event_t *lastEvent = const_cast(event); + qDebug() << "handleStatus" << lastEvent->window << lastEvent->data.data32[0]; xcb_generic_event_t *nextEvent; ClientMessageScanner scanner(connection()->atom(QXcbAtom::XdndStatus)); while ((nextEvent = connection()->checkEvent(scanner))) { @@ -627,7 +917,7 @@ void QXcbDrag::handleStatus(QWindow *w, const xcb_client_message_event_t *event, lastEvent = (xcb_client_message_event_t *)nextEvent; } - handle_xdnd_status(w, lastEvent, passive); + handle_xdnd_status(lastEvent, passive); if (lastEvent != event) free(lastEvent); DEBUG("xdndHandleStatus end"); @@ -659,47 +949,47 @@ void QXcbDrag::handleLeave(QWindow *w, const xcb_client_message_event_t *event, currentWindow.clear(); } -#if 0 -void qt_xdnd_send_leave() +void QXcbDrag::send_leave() { - if (!qt_xdnd_current_target) + if (!current_target) return; QDragManager *manager = QDragManager::self(); - XClientMessageEvent leave; - leave.type = ClientMessage; - leave.window = qt_xdnd_current_target; + xcb_client_message_event_t leave; + leave.response_type = XCB_CLIENT_MESSAGE; + leave.window = current_target; leave.format = 32; - leave.message_type = ATOM(XdndLeave); - leave.data.l[0] = manager->dragPrivate()->source->effectiveWinId(); - leave.data.l[1] = 0; // flags - leave.data.l[2] = 0; // x, y - leave.data.l[3] = 0; // w, h - leave.data.l[4] = 0; // just null + leave.type = connection()->atom(QXcbAtom::XdndLeave); + leave.data.data32[0] = connection()->clipboard()->owner(); + leave.data.data32[1] = 0; // flags + leave.data.data32[2] = 0; // x, y + leave.data.data32[3] = 0; // w, h + leave.data.data32[4] = 0; // just null - QWidget * w = QWidget::find(current_proxy_target); + QXcbWindow *w = connection()->platformWindowFromId(current_proxy_target); - if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops()) + if (w && (w->window()->windowType() == Qt::Desktop) /*&& !w->acceptDrops()*/) w = 0; if (w) - X11->xdndHandleLeave(w, (const XEvent *)&leave, false); + handleLeave(w->window(), (const xcb_client_message_event_t *)&leave, false); else - XSendEvent(X11->display, current_proxy_target, False, - NoEventMask, (XEvent*)&leave); + xcb_send_event(connection()->xcb_connection(), false,current_proxy_target, + XCB_EVENT_MASK_NO_EVENT, (const char *)&leave); // reset the drag manager state manager->willDrop = false; - if (global_accepted_action != Qt::IgnoreAction) + if (manager->global_accepted_action != Qt::IgnoreAction) manager->emitActionChanged(Qt::IgnoreAction); - global_accepted_action = Qt::IgnoreAction; + manager->global_accepted_action = Qt::IgnoreAction; manager->updateCursor(); - qt_xdnd_current_target = 0; + current_target = 0; current_proxy_target = 0; - qt_xdnd_source_current_time = 0; + source_time = XCB_CURRENT_TIME; waiting_for_status = false; } +#if 0 // TODO: remove and use QApplication::currentKeyboardModifiers() in Qt 4.8. static Qt::KeyboardModifiers currentKeyboardModifiers() @@ -774,12 +1064,12 @@ void QXcbDrag::handleDrop(QWindow *, const xcb_client_message_event_t *event, bo manager->global_accepted_action = de.dropAction(); } xcb_client_message_event_t finished; - finished.type = XCB_CLIENT_MESSAGE; + finished.response_type = XCB_CLIENT_MESSAGE; finished.window = xdnd_dragsource; finished.format = 32; finished.type = connection()->atom(QXcbAtom::XdndFinished); DNDDEBUG << "xdndHandleDrop" - << "currentWindow" << currentWindow + << "currentWindow" << currentWindow.data() << (currentWindow ? xcb_window(currentWindow.data()) : 0); finished.data.data32[0] = currentWindow ? xcb_window(currentWindow.data()) : XCB_NONE; finished.data.data32[1] = de.isAccepted() ? 1 : 0; // flags @@ -798,68 +1088,72 @@ void QXcbDrag::handleDrop(QWindow *, const xcb_client_message_event_t *event, bo target_time = XCB_CURRENT_TIME; } -#if 0 -void QX11Data::xdndHandleFinished(QWidget *, const XEvent * xe, bool passive) +void QXcbDrag::handleFinished(const xcb_client_message_event_t *event, bool) { DEBUG("xdndHandleFinished"); - const unsigned long *l = (const unsigned long *)xe->xclient.data.l; + if (event->window != connection()->clipboard()->owner()) + return; + + const unsigned long *l = (const unsigned long *)event->data.data32; DNDDEBUG << "xdndHandleFinished, l[0]" << l[0] - << "qt_xdnd_current_target" << qt_xdnd_current_target + << "current_target" << current_target << "qt_xdnd_current_proxy_targe" << current_proxy_target; if (l[0]) { - int at = findXdndDropTransactionByWindow(l[0]); + int at = findTransactionByWindow(l[0]); if (at != -1) { - restartXdndDropExpiryTimer(); + restartDropExpiryTimer(); - QXdndDropTransaction t = X11->dndDropTransactions.takeAt(at); - QDragManager *manager = QDragManager::self(); + Transaction t = transactions.takeAt(at); +// QDragManager *manager = QDragManager::self(); - Window target = qt_xdnd_current_target; - Window proxy_target = current_proxy_target; - QWidget *embedding_widget = current_embedding_widget; - QDrag *currentObject = manager->object; +// Window target = current_target; +// Window proxy_target = current_proxy_target; +// QWidget *embedding_widget = current_embedding_widget; +// QDrag *currentObject = manager->object; - qt_xdnd_current_target = t.target; - current_proxy_target = t.proxy_target; - current_embedding_widget = t.embedding_widget; - manager->object = t.object; +// current_target = t.target; +// current_proxy_target = t.proxy_target; +// current_embedding_widget = t.embedding_widget; +// manager->object = t.object; - if (!passive) - (void) checkEmbedded(currentWindow, xe); +// if (!passive) +// (void) checkEmbedded(currentWindow, xe); - current_embedding_widget = 0; - qt_xdnd_current_target = 0; - current_proxy_target = 0; +// current_embedding_widget = 0; +// current_target = 0; +// current_proxy_target = 0; if (t.object) t.object->deleteLater(); - qt_xdnd_current_target = target; - current_proxy_target = proxy_target; - current_embedding_widget = embedding_widget; - manager->object = currentObject; +// current_target = target; +// current_proxy_target = proxy_target; +// current_embedding_widget = embedding_widget; +// manager->object = currentObject; } } waiting_for_status = false; } -void QDragManager::timerEvent(QTimerEvent* e) +void QXcbDrag::timerEvent(QTimerEvent* e) { if (e->timerId() == heartbeat && source_sameanswer.isNull()) { - move(QCursor::pos()); + QMouseEvent me(QEvent::MouseMove, QCursor::pos(), QCursor::pos(), Qt::LeftButton, + QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers()); + move(&me); } else if (e->timerId() == transaction_expiry_timer) { - for (int i = 0; i < X11->dndDropTransactions.count(); ++i) { - const QXdndDropTransaction &t = X11->dndDropTransactions.at(i); - if (t.targetWidget) { + for (int i = 0; i < transactions.count(); ++i) { + const Transaction &t = transactions.at(i); + if (t.targetWindow) { // dnd within the same process, don't delete these continue; } t.object->deleteLater(); - X11->dndDropTransactions.removeAt(i--); + transactions.removeAt(i--); } killTimer(transaction_expiry_timer); @@ -867,142 +1161,18 @@ void QDragManager::timerEvent(QTimerEvent* e) } } -bool QDragManager::eventFilter(QObject * o, QEvent * e) +void QXcbDrag::cancel() { - if (beingCancelled) { - if (e->type() == QEvent::KeyRelease && ((QKeyEvent*)e)->key() == Qt::Key_Escape) { - qApp->removeEventFilter(this); - Q_ASSERT(object == 0); - beingCancelled = false; - eventLoop->exit(); - return true; // block the key release - } - return false; - } + DEBUG("QXcbDrag::cancel"); + endDrag(); - Q_ASSERT(object != 0); + if (current_target) + send_leave(); - if (!o->isWidgetType()) - return false; - - if (e->type() == QEvent::MouseMove) { - QMouseEvent* me = (QMouseEvent *)e; - move(me->globalPos()); - return true; - } else if (e->type() == QEvent::MouseButtonRelease) { - DEBUG("pre drop"); - qApp->removeEventFilter(this); - if (willDrop) - drop(); - else - cancel(); - DEBUG("drop, resetting object"); - beingCancelled = false; - eventLoop->exit(); - return true; - } - - if (e->type() == QEvent::ShortcutOverride) { - // prevent accelerators from firing while dragging - e->accept(); - return true; - } - - if (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease) { - QKeyEvent *ke = ((QKeyEvent*)e); - if (ke->key() == Qt::Key_Escape && e->type() == QEvent::KeyPress) { - cancel(); - qApp->removeEventFilter(this); - beingCancelled = false; - eventLoop->exit(); - } else { - source_sameanswer = QRect(); // force move - move(QCursor::pos()); - } - return true; // Eat all key events - } - - // ### We bind modality to widgets, so we have to do this - // ### "manually". - // DnD is modal - eat all other interactive events - switch (e->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseButtonDblClick: - case QEvent::MouseMove: - case QEvent::KeyPress: - case QEvent::KeyRelease: - case QEvent::Wheel: - case QEvent::ShortcutOverride: - return true; - default: - return false; - } + current_target = 0; } -void QDragManager::updateCursor() -{ - if (!noDropCursor) { -#ifndef QT_NO_CURSOR - noDropCursor = new QCursor(Qt::ForbiddenCursor); - moveCursor = new QCursor(Qt::DragMoveCursor); - copyCursor = new QCursor(Qt::DragCopyCursor); - linkCursor = new QCursor(Qt::DragLinkCursor); -#endif - } - - QCursor *c; - if (willDrop) { - if (global_accepted_action == Qt::CopyAction) { - c = copyCursor; - } else if (global_accepted_action == Qt::LinkAction) { - c = linkCursor; - } else { - c = moveCursor; - } - if (xdnd_data.deco) { - xdnd_data.deco->show(); - xdnd_data.deco->raise(); - } - } else { - c = noDropCursor; - //if (qt_xdnd_deco) - // qt_xdnd_deco->hide(); - } -#ifndef QT_NO_CURSOR - if (c) - qApp->changeOverrideCursor(*c); -#endif -} - - -void QDragManager::cancel(bool deleteSource) -{ - DEBUG("QDragManager::cancel"); - Q_ASSERT(heartbeat != -1); - killTimer(heartbeat); - heartbeat = -1; - beingCancelled = true; - qt_xdnd_dragging = false; - - if (qt_xdnd_current_target) - qt_xdnd_send_leave(); - -#ifndef QT_NO_CURSOR - if (restoreCursor) { - QApplication::restoreOverrideCursor(); - restoreCursor = false; - } -#endif - - if (deleteSource && object) - object->deleteLater(); - object = 0; - qDeleteInEventHandler(xdnd_data.deco); - xdnd_data.deco = 0; - - global_accepted_action = Qt::IgnoreAction; -} +#if 0 static Window findRealWindow(const QPoint & pos, Window w, int md) @@ -1056,218 +1226,11 @@ Window findRealWindow(const QPoint & pos, Window w, int md) return 0; } -void QDragManager::move(const QPoint & globalPos) -{ -#ifdef QT_NO_CURSOR - Q_UNUSED(globalPos); - return; -#else - DEBUG() << "QDragManager::move enter"; - if (!object) { - // perhaps the target crashed? - return; - } - - int screen = QCursor::x11Screen(); - if ((qt_xdnd_current_screen == -1 && screen != X11->defaultScreen) || (screen != qt_xdnd_current_screen)) { - // recreate the pixmap on the new screen... - delete xdnd_data.deco; - QWidget* parent = object->source()->window()->x11Info().screen() == screen - ? object->source()->window() : QApplication::desktop()->screen(screen); - xdnd_data.deco = new QShapedPixmapWidget(parent); - if (!QWidget::mouseGrabber()) { - updatePixmap(); - xdnd_data.deco->grabMouse(); - } - } - xdnd_data.deco->move(QCursor::pos() - xdnd_data.deco->pm_hot); - - if (source_sameanswer.contains(globalPos) && source_sameanswer.isValid()) - return; - - qt_xdnd_current_screen = screen; - Window rootwin = QX11Info::appRootWindow(qt_xdnd_current_screen); - Window target = 0; - int lx = 0, ly = 0; - if (!XTranslateCoordinates(X11->display, rootwin, rootwin, globalPos.x(), globalPos.y(), &lx, &ly, &target)) - // some weird error... - return; - - if (target == rootwin) { - // Ok. - } else if (target) { - //me - Window src = rootwin; - while (target != 0) { - DNDDEBUG << "checking target for XdndAware" << QWidget::find(target) << target; - int lx2, ly2; - Window t; - // translate coordinates - if (!XTranslateCoordinates(X11->display, src, target, lx, ly, &lx2, &ly2, &t)) { - target = 0; - break; - } - lx = lx2; - ly = ly2; - src = target; - - // check if it has XdndAware - Atom type = 0; - int f; - unsigned long n, a; - unsigned char *data = 0; - XGetWindowProperty(X11->display, target, ATOM(XdndAware), 0, 0, False, - AnyPropertyType, &type, &f,&n,&a,&data); - if (data) - XFree(data); - if (type) { - DNDDEBUG << "Found XdndAware on " << QWidget::find(target) << target; - break; - } - - // find child at the coordinates - if (!XTranslateCoordinates(X11->display, src, src, lx, ly, &lx2, &ly2, &target)) { - target = 0; - break; - } - } - if (xdnd_data.deco && (!target || target == xdnd_data.deco->effectiveWinId())) { - DNDDEBUG << "need to find real window"; - target = findRealWindow(globalPos, rootwin, 6); - DNDDEBUG << "real window found" << QWidget::find(target) << target; - } - } - - QWidget* w; - if (target) { - w = QWidget::find((WId)target); - if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops()) - w = 0; - } else { - w = 0; - target = rootwin; - } - - DNDDEBUG << "and the final target is " << QWidget::find(target) << target; - DNDDEBUG << "the widget w is" << w; - - WId proxy_target = xdndProxy(target); - if (!proxy_target) - proxy_target = target; - int target_version = 1; - - if (proxy_target) { - Atom type = XNone; - int r, f; - unsigned long n, a; - unsigned char *retval; - X11->ignoreBadwindow(); - r = XGetWindowProperty(X11->display, proxy_target, ATOM(XdndAware), 0, - 1, False, AnyPropertyType, &type, &f,&n,&a,&retval); - int *tv = (int *)retval; - if (r != Success || X11->badwindow()) { - target = 0; - } else { - target_version = qMin(xdnd_version,tv ? *tv : 1); - if (tv) - XFree(tv); -// if (!(!X11->badwindow() && type)) -// target = 0; - } - } - - if (target != qt_xdnd_current_target) { - if (qt_xdnd_current_target) - qt_xdnd_send_leave(); - - qt_xdnd_current_target = target; - current_proxy_target = proxy_target; - if (target) { - QVector types; - int flags = target_version << 24; - QStringList fmts = QInternalMimeData::formatsHelper(dragPrivate()->data); - for (int i = 0; i < fmts.size(); ++i) { - QList atoms = X11->xdndMimeAtomsForFormat(fmts.at(i)); - for (int j = 0; j < atoms.size(); ++j) { - if (!types.contains(atoms.at(j))) - types.append(atoms.at(j)); - } - } - if (types.size() > 3) { - XChangeProperty(X11->display, - dragPrivate()->source->effectiveWinId(), ATOM(XdndTypelist), - XA_ATOM, 32, PropModeReplace, - (unsigned char *)types.data(), - types.size()); - flags |= 0x0001; - } - XClientMessageEvent enter; - enter.type = ClientMessage; - enter.window = target; - enter.format = 32; - enter.message_type = ATOM(XdndEnter); - enter.data.l[0] = dragPrivate()->source->effectiveWinId(); - enter.data.l[1] = flags; - enter.data.l[2] = types.size()>0 ? types.at(0) : 0; - enter.data.l[3] = types.size()>1 ? types.at(1) : 0; - enter.data.l[4] = types.size()>2 ? types.at(2) : 0; - // provisionally set the rectangle to 5x5 pixels... - source_sameanswer = QRect(globalPos.x() - 2, - globalPos.y() -2 , 5, 5); - - DEBUG("sending Xdnd enter"); - if (w) - X11->xdndHandleEnter(w, (const XEvent *)&enter, false); - else if (target) - XSendEvent(X11->display, proxy_target, False, NoEventMask, (XEvent*)&enter); - waiting_for_status = false; - } - } - if (waiting_for_status) - return; - - if (target) { - waiting_for_status = true; - - XClientMessageEvent move; - move.type = ClientMessage; - move.window = target; - move.format = 32; - move.message_type = ATOM(XdndPosition); - move.window = target; - move.data.l[0] = dragPrivate()->source->effectiveWinId(); - move.data.l[1] = 0; // flags - move.data.l[2] = (globalPos.x() << 16) + globalPos.y(); - move.data.l[3] = X11->time; - move.data.l[4] = qtaction_to_xdndaction(defaultAction(dragPrivate()->possible_actions, QApplication::keyboardModifiers())); - DEBUG("sending Xdnd position"); - - qt_xdnd_source_current_time = X11->time; - - if (w) - handle_xdnd_position(w, (const XEvent *)&move, false); - else - XSendEvent(X11->display, proxy_target, False, NoEventMask, - (XEvent*)&move); - } else { - if (willDrop) { - willDrop = false; - updateCursor(); - } - } - DEBUG() << "QDragManager::move leave"; -#endif -} - - void QDragManager::drop() { - Q_ASSERT(heartbeat != -1); - killTimer(heartbeat); - heartbeat = -1; - qt_xdnd_dragging = false; + endDrag(); - if (!qt_xdnd_current_target) + if (!current_target) return; qDeleteInEventHandler(xdnd_data.deco); @@ -1275,12 +1238,12 @@ void QDragManager::drop() XClientMessageEvent drop; drop.type = ClientMessage; - drop.window = qt_xdnd_current_target; + drop.window = current_target; drop.format = 32; drop.message_type = ATOM(XdndDrop); drop.data.l[0] = dragPrivate()->source->effectiveWinId(); drop.data.l[1] = 0; // flags - drop.data.l[2] = X11->time; + drop.data.l[2] = connection()->time(); drop.data.l[3] = 0; drop.data.l[4] = 0; @@ -1291,8 +1254,8 @@ void QDragManager::drop() w = 0; QXdndDropTransaction t = { - X11->time, - qt_xdnd_current_target, + connection()->time(), + current_target, current_proxy_target, w, current_embedding_widget, @@ -1307,9 +1270,9 @@ void QDragManager::drop() XSendEvent(X11->display, current_proxy_target, False, NoEventMask, (XEvent*)&drop); - qt_xdnd_current_target = 0; + current_target = 0; current_proxy_target = 0; - qt_xdnd_source_current_time = 0; + source_time = 0; current_embedding_widget = 0; object = 0; @@ -1325,10 +1288,10 @@ void QDragManager::drop() bool QX11Data::xdndHandleBadwindow() { - if (qt_xdnd_current_target) { + if (current_target) { QDragManager *manager = QDragManager::self(); if (manager->object) { - qt_xdnd_current_target = 0; + current_target = 0; current_proxy_target = 0; manager->object->deleteLater(); manager->object = 0; @@ -1347,19 +1310,17 @@ bool QX11Data::xdndHandleBadwindow() } return false; } +#endif -void QX11Data::xdndHandleSelectionRequest(const XSelectionRequestEvent * req) +void QXcbDrag::handleSelectionRequest(const xcb_selection_request_event_t *event) { - if (!req) - return; - XEvent evt; - evt.xselection.type = SelectionNotify; - evt.xselection.display = req->display; - evt.xselection.requestor = req->requestor; - evt.xselection.selection = req->selection; - evt.xselection.target = XNone; - evt.xselection.property = XNone; - evt.xselection.time = req->time; + xcb_selection_notify_event_t notify; + notify.response_type = XCB_SELECTION_NOTIFY; + notify.requestor = event->requestor; + notify.selection = event->selection; + notify.target = XCB_NONE; + notify.property = XCB_NONE; + notify.time = event->time; QDragManager *manager = QDragManager::self(); QDrag *currentObject = manager->object; @@ -1368,207 +1329,61 @@ void QX11Data::xdndHandleSelectionRequest(const XSelectionRequestEvent * req) int at = -1; // figure out which data the requestor is really interested in - if (manager->object && req->time == qt_xdnd_source_current_time) { + if (manager->object && event->time == source_time) { // requestor wants the current drag data at = -2; } else { // if someone has requested data in response to XdndDrop, find the corresponding transaction. the // spec says to call XConvertSelection() using the timestamp from the XdndDrop - at = findXdndDropTransactionByTime(req->time); + at = findTransactionByTime(event->time); if (at == -1) { // no dice, perhaps the client was nice enough to use the same window id in XConvertSelection() // that we sent the XdndDrop event to. - at = findXdndDropTransactionByWindow(req->requestor); - } - if (at == -1 && req->time == CurrentTime) { - // previous Qt versions always requested the data on a child of the target window - // using CurrentTime... but it could be asking for either drop data or the current drag's data - Window target = findXdndAwareParent(req->requestor); - if (target) { - if (qt_xdnd_current_target && qt_xdnd_current_target == target) - at = -2; - else - at = findXdndDropTransactionByWindow(target); - } + at = findTransactionByWindow(event->requestor); } +// if (at == -1 && event->time == XCB_CURRENT_TIME) { +// // previous Qt versions always requested the data on a child of the target window +// // using CurrentTime... but it could be asking for either drop data or the current drag's data +// Window target = findXdndAwareParent(event->requestor); +// if (target) { +// if (current_target && current_target == target) +// at = -2; +// else +// at = findXdndDropTransactionByWindow(target); +// } +// } } if (at >= 0) { - restartXdndDropExpiryTimer(); + restartDropExpiryTimer(); // use the drag object from an XdndDrop tansaction - manager->object = X11->dndDropTransactions.at(at).object; + manager->object = transactions.at(at).object; } else if (at != -2) { // no transaction found, we'll have to reject the request manager->object = 0; } if (manager->object) { - Atom atomFormat = req->target; + xcb_atom_t atomFormat = event->target; int dataFormat = 0; QByteArray data; - if (X11->xdndMimeDataForAtom(req->target, manager->dragPrivate()->data, + if (QXcbMime::mimeDataForAtom(connection(), event->target, manager->dragPrivate()->data, &data, &atomFormat, &dataFormat)) { int dataSize = data.size() / (dataFormat / 8); - XChangeProperty (X11->display, req->requestor, req->property, - atomFormat, dataFormat, PropModeReplace, - (unsigned char *)data.data(), dataSize); - evt.xselection.property = req->property; - evt.xselection.target = atomFormat; + xcb_change_property(connection()->xcb_connection(), XCB_PROP_MODE_REPLACE, event->requestor, event->property, + atomFormat, dataFormat, dataSize, (const void *)data.constData()); + notify.property = event->property; + notify.target = atomFormat; } } // reset manager->object in case we modified it above manager->object = currentObject; - // ### this can die if req->requestor crashes at the wrong + // ### this can die if event->requestor crashes at the wrong // ### moment - XSendEvent(X11->display, req->requestor, False, 0, &evt); -} - - - -/* - Enable drag and drop for widget w by installing the proper - properties on w's toplevel widget. -*/ -bool QX11Data::dndEnable(QWidget* w, bool on) -{ - w = w->window(); - - if (bool(((QExtraWidget*)w)->topData()->dnd) == on) - return true; // been there, done that - ((QExtraWidget*)w)->topData()->dnd = on ? 1 : 0; - - motifdndEnable(w, on); - return xdndEnable(w, on); -} - -Qt::DropAction QDragManager::drag(QDrag * o) -{ - if (object == o || !o || !o->d_func()->source) - return Qt::IgnoreAction; - - if (object) { - cancel(); - qApp->removeEventFilter(this); - beingCancelled = false; - } - - if (object) { - // the last drag and drop operation hasn't finished, so we are going to wait - // for one second to see if it does... if the finish message comes after this, - // then we could still have problems, but this is highly unlikely - QApplication::flush(); - - QElapsedTimer timer; - timer.start(); - do { - XEvent event; - if (XCheckTypedEvent(X11->display, ClientMessage, &event)) - qApp->x11ProcessEvent(&event); - - // sleep 50 ms, so we don't use up CPU cycles all the time. - struct timeval usleep_tv; - usleep_tv.tv_sec = 0; - usleep_tv.tv_usec = 50000; - select(0, 0, 0, 0, &usleep_tv); - } while (object && timer.hasExpired(1000)); - } - - object = o; - object->d_func()->target = 0; - xdnd_data.deco = new QShapedPixmapWidget(object->source()->window()); - - willDrop = false; - - updatePixmap(); - - qApp->installEventFilter(this); - XSetSelectionOwner(X11->display, ATOM(XdndSelection), dragPrivate()->source->window()->internalWinId(), X11->time); - global_accepted_action = Qt::CopyAction; - source_sameanswer = QRect(); -#ifndef QT_NO_CURSOR - // set the override cursor (must be done here, since it is updated - // in the call to move() below) - qApp->setOverrideCursor(Qt::ArrowCursor); - restoreCursor = true; -#endif - move(QCursor::pos()); - heartbeat = startTimer(200); - - qt_xdnd_dragging = true; - - if (!QWidget::mouseGrabber()) - xdnd_data.deco->grabMouse(); - - eventLoop = new QEventLoop; - (void) eventLoop->exec(); - delete eventLoop; - eventLoop = 0; - -#ifndef QT_NO_CURSOR - if (restoreCursor) { - qApp->restoreOverrideCursor(); - restoreCursor = false; - } -#endif - - // delete cursors as they may be different next drag. - delete noDropCursor; - noDropCursor = 0; - delete copyCursor; - copyCursor = 0; - delete moveCursor; - moveCursor = 0; - delete linkCursor; - linkCursor = 0; - - delete xdnd_data.deco; - xdnd_data.deco = 0; - if (heartbeat != -1) - killTimer(heartbeat); - heartbeat = -1; - qt_xdnd_current_screen = -1; - qt_xdnd_dragging = false; - - return global_accepted_action; - // object persists until we get an xdnd_finish message + xcb_send_event(connection()->xcb_connection(), false, event->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)¬ify); } -#endif - - -static xcb_window_t xdndProxy(QXcbConnection *c, xcb_window_t w) -{ - xcb_window_t proxy = XCB_NONE; - - xcb_get_property_cookie_t cookie = xcb_get_property(c->xcb_connection(), false, w, c->atom(QXcbAtom::XdndProxy), - QXcbAtom::XA_WINDOW, 0, 1); - xcb_get_property_reply_t *reply = xcb_get_property_reply(c->xcb_connection(), cookie, 0); - - if (reply && reply->type == QXcbAtom::XA_WINDOW) - proxy = *((xcb_window_t *)xcb_get_property_value(reply)); - free(reply); - - if (proxy == XCB_NONE) - return proxy; - - // exists and is real? - cookie = xcb_get_property(c->xcb_connection(), false, proxy, c->atom(QXcbAtom::XdndProxy), - QXcbAtom::XA_WINDOW, 0, 1); - reply = xcb_get_property_reply(c->xcb_connection(), cookie, 0); - - if (reply && reply->type == QXcbAtom::XA_WINDOW) { - xcb_window_t p = *((xcb_window_t *)xcb_get_property_value(reply)); - if (proxy != p) - proxy = 0; - } else { - proxy = 0; - } - - free(reply); - - return proxy; -} bool QXcbDrag::dndEnable(QXcbWindow *w, bool on) { diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h index 4aab35b7b1..82d634d592 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.h +++ b/src/plugins/platforms/xcb/qxcbdrag.h @@ -49,6 +49,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -57,8 +58,10 @@ class QWindow; class QXcbConnection; class QXcbWindow; class QDropData; +class QXcbScreen; +class QDrag; -class QXcbDrag : public QPlatformDrag +class QXcbDrag : public QObject, public QPlatformDrag { public: QXcbDrag(QXcbConnection *c); @@ -72,22 +75,32 @@ public: virtual void cancel(); virtual void move(const QMouseEvent *me); virtual void drop(const QMouseEvent *me); + void endDrag(); 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 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 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); QXcbConnection *connection() const { return m_connection; } +protected: + void timerEvent(QTimerEvent* e); + private: friend class QDropData; + void init(); + 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; xcb_atom_t toXdndAction(Qt::DropAction a) const; @@ -107,14 +120,40 @@ private: QList xdnd_types; xcb_timestamp_t target_time; + xcb_timestamp_t source_time; Qt::DropAction last_target_accepted_action; // rectangle in which the answer will be the same QRect source_sameanswer; 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) xcb_window_t current_proxy_target; + + QXcbScreen *current_screen; + + int heartbeat; + bool xdnd_dragging; + + QVector 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 transactions; + + int transaction_expiry_timer; + void restartDropExpiryTimer(); + int findTransactionByWindow(xcb_window_t window); + int findTransactionByTime(xcb_timestamp_t timestamp); }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index b254fd5f13..2aaa2c48b1 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -944,6 +944,9 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even if (event->type == atom(QXcbAtom::WM_PROTOCOLS)) { if (event->data.data32[0] == atom(QXcbAtom::WM_DELETE_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)) { 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_flush(xcb_connection()); } else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_SYNC_REQUEST)) { + connection()->setTime(event->data.data32[1]); if (!m_hasReceivedSyncRequest) { m_hasReceivedSyncRequest = true; 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) { + connection()->setTime(time); + Qt::MouseButtons buttons = translateMouseButtons(state); 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) { + connection()->setTime(event->time); + if ((event->mode != XCB_NOTIFY_MODE_NORMAL && event->mode != XCB_NOTIFY_MODE_UNGRAB) || event->detail == XCB_NOTIFY_DETAIL_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) { + connection()->setTime(event->time); + if ((event->mode != XCB_NOTIFY_MODE_NORMAL && event->mode != XCB_NOTIFY_MODE_UNGRAB) || event->detail == XCB_NOTIFY_DETAIL_INFERIOR) { -- cgit v1.2.3