xcb: Use XShape for DnD when a compositing manager is not running

Otherwise transparent areas of the drag'n'drop pixmap are painted
with the black color.

Task-number: QTBUG-45193
Change-Id: I55b7c7caababe13584fa1c7a52835f112e20f920
Reviewed-by: Lars Knoll <lars.knoll@theqtcompany.com>
This commit is contained in:
Alexander Volkov 2015-04-13 14:13:34 +03:00
parent 4b9cdf90ca
commit e912132886
10 changed files with 106 additions and 28 deletions

View File

@ -35,12 +35,16 @@
#include <QtGui/QPainter>
#include <QtGui/QCursor>
#include <QtGui/QGuiApplication>
#include <QtGui/QPalette>
#include <QtGui/QBitmap>
QT_BEGIN_NAMESPACE
QShapedPixmapWindow::QShapedPixmapWindow(QScreen *screen)
: QWindow(screen),
m_backingStore(0)
m_backingStore(0),
m_useCompositing(true)
{
QSurfaceFormat format;
format.setAlphaBufferSize(8);
@ -68,7 +72,10 @@ void QShapedPixmapWindow::render()
{
QPainter p(device);
p.setCompositionMode(QPainter::CompositionMode_Source);
if (m_useCompositing)
p.setCompositionMode(QPainter::CompositionMode_Source);
else
p.fillRect(rect, QGuiApplication::palette().base());
p.drawPixmap(0, 0, m_pixmap);
}
@ -79,6 +86,8 @@ void QShapedPixmapWindow::render()
void QShapedPixmapWindow::setPixmap(const QPixmap &pixmap)
{
m_pixmap = pixmap;
if (!m_useCompositing)
setMask(m_pixmap.mask());
}
void QShapedPixmapWindow::setHotspot(const QPoint &hotspot)

View File

@ -60,6 +60,7 @@ public:
void render();
void setUseCompositing(bool on) { m_useCompositing = on; }
void setPixmap(const QPixmap &pixmap);
void setHotspot(const QPoint &hotspot);
@ -72,6 +73,7 @@ private:
QBackingStore *m_backingStore;
QPixmap m_pixmap;
QPoint m_hotSpot;
bool m_useCompositing;
};
QT_END_NAMESPACE

View File

@ -88,7 +88,7 @@ static QWindow* topLevelAt(const QPoint &pos)
QBasicDrag::QBasicDrag() :
m_restoreCursor(false), m_eventLoop(0),
m_executed_drop_action(Qt::IgnoreAction), m_can_drop(false),
m_drag(0), m_drag_icon_window(0)
m_drag(0), m_drag_icon_window(0), m_useCompositing(true)
{
}
@ -226,6 +226,7 @@ void QBasicDrag::recreateShapedPixmapWindow(QScreen *screen, const QPoint &pos)
// when QDrag is used without a pixmap - QDrag::setPixmap()
m_drag_icon_window = new QShapedPixmapWindow(screen);
m_drag_icon_window->setUseCompositing(m_useCompositing);
m_drag_icon_window->setPixmap(m_drag->pixmap());
m_drag_icon_window->setHotspot(m_drag->hotSpot());
m_drag_icon_window->updateGeometry(pos);

View File

@ -87,6 +87,9 @@ protected:
bool canDrop() const { return m_can_drop; }
void setCanDrop(bool c) { m_can_drop = c; }
bool useCompositing() const { return m_useCompositing; }
void setUseCompositing(bool on) { m_useCompositing = on; }
Qt::DropAction executedDropAction() const { return m_executed_drop_action; }
void setExecutedDropAction(Qt::DropAction da) { m_executed_drop_action = da; }
@ -104,6 +107,7 @@ private:
bool m_can_drop;
QDrag *m_drag;
QShapedPixmapWindow *m_drag_icon_window;
bool m_useCompositing;
};
class Q_GUI_EXPORT QSimpleDrag : public QBasicDrag

View File

@ -275,22 +275,8 @@ QXcbClipboard::QXcbClipboard(QXcbConnection *c)
m_clientClipboard[QClipboard::Selection] = 0;
m_timestamp[QClipboard::Clipboard] = XCB_CURRENT_TIME;
m_timestamp[QClipboard::Selection] = XCB_CURRENT_TIME;
m_owner = connection()->getQtSelectionOwner();
QXcbScreen *platformScreen = screen();
int x = 0, y = 0, w = 3, h = 3;
m_owner = xcb_generate_id(xcb_connection());
Q_XCB_CALL(xcb_create_window(xcb_connection(),
XCB_COPY_FROM_PARENT, // depth -- same as root
m_owner, // window id
platformScreen->screen()->root, // parent window id
x, y, w, h,
0, // border width
XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
platformScreen->screen()->root_visual, // visual
0, // value mask
0)); // value list
#ifndef QT_NO_DEBUG
QByteArray ba("Qt clipboard window");
Q_XCB_CALL(xcb_change_property(xcb_connection(),
@ -353,13 +339,7 @@ void QXcbClipboard::incrTransactionPeeker(xcb_generic_event_t *ge, bool &accepte
xcb_window_t QXcbClipboard::getSelectionOwner(xcb_atom_t atom) const
{
xcb_connection_t *c = xcb_connection();
xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(c, atom);
xcb_get_selection_owner_reply_t *reply;
reply = xcb_get_selection_owner_reply(c, cookie, 0);
xcb_window_t win = reply->owner;
free(reply);
return win;
return connection()->getSelectionOwner(atom);
}
xcb_atom_t QXcbClipboard::atomForMode(QClipboard::Mode mode) const

View File

@ -449,6 +449,9 @@ void QXcbConnection::initializeScreens()
++xcbScreenNumber;
} // for each xcb screen
foreach (QXcbVirtualDesktop *virtualDesktop, m_virtualDesktops)
virtualDesktop->subscribeToXFixesSelectionNotify();
// If there's no randr extension, or there was some error above, or we found a
// screen which doesn't have outputs for some other reason (e.g. on VNC or ssh -X),
// but the dimensions are known anyway, and we don't already have any lingering
@ -507,6 +510,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
, m_systemTrayTracker(0)
, m_glIntegration(Q_NULLPTR)
, m_xiGrab(false)
, m_qtSelectionOwner(0)
{
#ifdef XCB_USE_XLIB
Display *dpy = XOpenDisplay(m_displayName.constData());
@ -551,12 +555,12 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
m_netWmUserTime = XCB_CURRENT_TIME;
initializeXRandr();
initializeXFixes();
initializeScreens();
if (m_screens.isEmpty())
qFatal("QXcbConnection: no screens available");
initializeXFixes();
initializeXRender();
m_xi2Enabled = false;
#if defined(XCB_USE_XINPUT2)
@ -1139,10 +1143,14 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
if (!handled) {
if (response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) {
setTime(((xcb_xfixes_selection_notify_event_t *)event)->timestamp);
xcb_xfixes_selection_notify_event_t *notify_event = (xcb_xfixes_selection_notify_event_t *)event;
setTime(notify_event->timestamp);
#ifndef QT_NO_CLIPBOARD
m_clipboard->handleXFixesSelectionRequest((xcb_xfixes_selection_notify_event_t *)event);
m_clipboard->handleXFixesSelectionRequest(notify_event);
#endif
foreach (QXcbVirtualDesktop *virtualDesktop, m_virtualDesktops)
virtualDesktop->handleXFixesSelectionNotify(notify_event);
handled = true;
} else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_NOTIFY) {
updateScreens((xcb_randr_notify_event_t *)event);
@ -1371,6 +1379,37 @@ xcb_timestamp_t QXcbConnection::getTimestamp()
return timestamp;
}
xcb_window_t QXcbConnection::getSelectionOwner(xcb_atom_t atom) const
{
xcb_connection_t *c = xcb_connection();
xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(c, atom);
xcb_get_selection_owner_reply_t *reply;
reply = xcb_get_selection_owner_reply(c, cookie, 0);
xcb_window_t win = reply->owner;
free(reply);
return win;
}
xcb_window_t QXcbConnection::getQtSelectionOwner()
{
if (!m_qtSelectionOwner) {
xcb_screen_t *xcbScreen = primaryVirtualDesktop()->screen();
int x = 0, y = 0, w = 3, h = 3;
m_qtSelectionOwner = xcb_generate_id(xcb_connection());
Q_XCB_CALL(xcb_create_window(xcb_connection(),
XCB_COPY_FROM_PARENT, // depth -- same as root
m_qtSelectionOwner, // window id
xcbScreen->root, // parent window id
x, y, w, h,
0, // border width
XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
xcbScreen->root_visual, // visual
0, // value mask
0)); // value list
}
return m_qtSelectionOwner;
}
xcb_window_t QXcbConnection::rootWindow()
{
QXcbScreen *s = primaryScreen();

View File

@ -460,6 +460,8 @@ public:
bool threadedEventHandling() const { return m_reader->isRunning(); }
xcb_timestamp_t getTimestamp();
xcb_window_t getSelectionOwner(xcb_atom_t atom) const;
xcb_window_t getQtSelectionOwner();
void setButton(Qt::MouseButton button, bool down) { if (down) m_buttons |= button; else m_buttons &= ~button; }
Qt::MouseButtons buttons() const { return m_buttons; }
@ -650,6 +652,8 @@ private:
QXcbGlIntegration *m_glIntegration;
bool m_xiGrab;
xcb_window_t m_qtSelectionOwner;
friend class QXcbEventReader;
};

View File

@ -191,6 +191,8 @@ void QXcbDrag::startDrag()
xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->clipboard()->owner(),
atom(QXcbAtom::XdndTypelist),
XCB_ATOM_ATOM, 32, drag_types.size(), (const void *)drag_types.constData());
setUseCompositing(current_virtual_desktop->compositingActive());
QBasicDrag::startDrag();
}
@ -316,6 +318,7 @@ void QXcbDrag::move(const QPoint &globalPos)
QPoint deviceIndependentPos = QHighDpiScaling::mapPositionFromNative(globalPos, screen);
if (virtualDesktop != current_virtual_desktop) {
setUseCompositing(virtualDesktop->compositingActive());
recreateShapedPixmapWindow(static_cast<QPlatformScreen*>(screen)->screen(), deviceIndependentPos);
current_virtual_desktop = virtualDesktop;
} else {

View File

@ -54,6 +54,10 @@ QXcbVirtualDesktop::QXcbVirtualDesktop(QXcbConnection *connection, xcb_screen_t
, m_number(number)
, m_xSettings(Q_NULLPTR)
{
QByteArray cmAtomName("_NET_WM_CM_S");
cmAtomName += QByteArray::number(m_number);
m_net_wm_cm_atom = connection->internAtom(cmAtomName.constData());
m_compositingActive = connection->getSelectionOwner(m_net_wm_cm_atom);
}
QXcbVirtualDesktop::~QXcbVirtualDesktop()
@ -79,6 +83,30 @@ QXcbXSettings *QXcbVirtualDesktop::xSettings() const
return m_xSettings;
}
bool QXcbVirtualDesktop::compositingActive() const
{
if (connection()->hasXFixes())
return m_compositingActive;
else
return connection()->getSelectionOwner(m_net_wm_cm_atom);
}
void QXcbVirtualDesktop::handleXFixesSelectionNotify(xcb_xfixes_selection_notify_event_t *notify_event)
{
if (notify_event->selection == m_net_wm_cm_atom)
m_compositingActive = notify_event->owner;
}
void QXcbVirtualDesktop::subscribeToXFixesSelectionNotify()
{
if (connection()->hasXFixes()) {
const uint32_t mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
Q_XCB_CALL(xcb_xfixes_select_selection_input_checked(xcb_connection(), connection()->getQtSelectionOwner(), m_net_wm_cm_atom, mask));
}
}
QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop,
xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output,
QString outputName)

View File

@ -39,6 +39,7 @@
#include <xcb/xcb.h>
#include <xcb/randr.h>
#include <xcb/xfixes.h>
#include "qxcbobject.h"
#include "qxcbscreen.h"
@ -69,11 +70,18 @@ public:
QXcbXSettings *xSettings() const;
bool compositingActive() const;
void handleXFixesSelectionNotify(xcb_xfixes_selection_notify_event_t *notify_event);
void subscribeToXFixesSelectionNotify();
private:
xcb_screen_t *m_screen;
int m_number;
QXcbXSettings *m_xSettings;
xcb_atom_t m_net_wm_cm_atom;
bool m_compositingActive;
};
class Q_XCB_EXPORT QXcbScreen : public QXcbObject, public QPlatformScreen