summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@digia.com>2014-05-06 17:24:23 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-05-15 15:31:07 +0200
commit6a61a00ddb21e79412e82069dfef50192bfd724d (patch)
treee922d83a1d27c4a00390d4f0e2a6c4b459c5a544
parentf618dbdd04d1efa087dfa9a5aa792c315a5bc500 (diff)
Windows: Use new clipboard API for listening to changes.
The currently used clipboard chain API has various problems with non- responsive applications and requires checks for hung/debugged applications when sending on notifications. The new clipboard format listener API available from Windows Vista onwards requires less code and does not have these problems, however the change notifications now arrive asynchronously. Change the tst_qclipboard to be able to deal with asynchronous change notifications. Task-number: QTBUG-38670 Task-number: QTBUG-33492 Change-Id: I3c49e346a34310431c20f3051d12eaabf330a3ad Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
-rw-r--r--src/plugins/platforms/windows/qwindowsclipboard.cpp40
-rw-r--r--src/plugins/platforms/windows/qwindowsclipboard.h3
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp8
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.h6
-rw-r--r--tests/auto/gui/kernel/qclipboard/tst_qclipboard.cpp46
5 files changed, 88 insertions, 15 deletions
diff --git a/src/plugins/platforms/windows/qwindowsclipboard.cpp b/src/plugins/platforms/windows/qwindowsclipboard.cpp
index dcfeba12fa..e43b524aa8 100644
--- a/src/plugins/platforms/windows/qwindowsclipboard.cpp
+++ b/src/plugins/platforms/windows/qwindowsclipboard.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
@@ -145,7 +145,7 @@ static void cleanClipboardPostRoutine()
QWindowsClipboard *QWindowsClipboard::m_instance = 0;
QWindowsClipboard::QWindowsClipboard() :
- m_data(0), m_clipboardViewer(0), m_nextClipboardViewer(0)
+ m_data(0), m_clipboardViewer(0), m_nextClipboardViewer(0), m_formatListenerRegistered(false)
{
QWindowsClipboard::m_instance = this;
qAddPostRoutine(cleanClipboardPostRoutine);
@@ -178,20 +178,40 @@ void QWindowsClipboard::registerViewer()
m_clipboardViewer = QWindowsContext::instance()->
createDummyWindow(QStringLiteral("Qt5ClipboardView"), L"Qt5ClipboardView",
qClipboardViewerWndProc, WS_OVERLAPPED);
- m_nextClipboardViewer = SetClipboardViewer(m_clipboardViewer);
- qCDebug(lcQpaMime) << __FUNCTION__ << "m_clipboardViewer: " << m_clipboardViewer << "next: " << m_nextClipboardViewer;
+ // Try format listener API (Vista onwards) first.
+ if (QWindowsContext::user32dll.addClipboardFormatListener && QWindowsContext::user32dll.removeClipboardFormatListener) {
+ m_formatListenerRegistered = QWindowsContext::user32dll.addClipboardFormatListener(m_clipboardViewer);
+ if (!m_formatListenerRegistered)
+ qErrnoWarning("AddClipboardFormatListener() failed.");
+ }
+
+ if (!m_formatListenerRegistered)
+ m_nextClipboardViewer = SetClipboardViewer(m_clipboardViewer);
+
+ qCDebug(lcQpaMime) << __FUNCTION__ << "m_clipboardViewer:" << m_clipboardViewer
+ << "format listener:" << m_formatListenerRegistered
+ << "next:" << m_nextClipboardViewer;
}
void QWindowsClipboard::unregisterViewer()
{
if (m_clipboardViewer) {
- ChangeClipboardChain(m_clipboardViewer, m_nextClipboardViewer);
+ if (m_formatListenerRegistered) {
+ QWindowsContext::user32dll.removeClipboardFormatListener(m_clipboardViewer);
+ m_formatListenerRegistered = false;
+ } else {
+ ChangeClipboardChain(m_clipboardViewer, m_nextClipboardViewer);
+ m_nextClipboardViewer = 0;
+ }
DestroyWindow(m_clipboardViewer);
- m_clipboardViewer = m_nextClipboardViewer = 0;
+ m_clipboardViewer = 0;
}
}
+// ### FIXME: Qt 6: Remove the clipboard chain handling code and make the
+// format listener the default.
+
static bool isProcessBeingDebugged(HWND hwnd)
{
DWORD pid = 0;
@@ -232,6 +252,8 @@ void QWindowsClipboard::propagateClipboardMessage(UINT message, WPARAM wParam, L
bool QWindowsClipboard::clipboardViewerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result)
{
+ enum { wMClipboardUpdate = 0x031D };
+
*result = 0;
if (QWindowsContext::verbose)
qCDebug(lcQpaMime) << __FUNCTION__ << hwnd << message << QWindowsGuiEventDispatcher::windowsMessageName(message);
@@ -246,14 +268,16 @@ bool QWindowsClipboard::clipboardViewerWndProc(HWND hwnd, UINT message, WPARAM w
}
}
return true;
- case WM_DRAWCLIPBOARD: {
+ case wMClipboardUpdate: // Clipboard Format listener (Vista onwards)
+ case WM_DRAWCLIPBOARD: { // Clipboard Viewer Chain handling (up to XP)
const bool owned = ownsClipboard();
qCDebug(lcQpaMime) << "Clipboard changed owned " << owned;
emitChanged(QClipboard::Clipboard);
// clean up the clipboard object if we no longer own the clipboard
if (!owned && m_data)
releaseIData();
- propagateClipboardMessage(message, wParam, lParam);
+ if (!m_formatListenerRegistered)
+ propagateClipboardMessage(message, wParam, lParam);
}
return true;
case WM_DESTROY:
diff --git a/src/plugins/platforms/windows/qwindowsclipboard.h b/src/plugins/platforms/windows/qwindowsclipboard.h
index 30bc0143f4..61c5967e98 100644
--- a/src/plugins/platforms/windows/qwindowsclipboard.h
+++ b/src/plugins/platforms/windows/qwindowsclipboard.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
@@ -88,6 +88,7 @@ private:
QWindowsOleDataObject *m_data;
HWND m_clipboardViewer;
HWND m_nextClipboardViewer;
+ bool m_formatListenerRegistered;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index ccff2d3e9f..63615dd4bf 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -191,7 +191,8 @@ QWindowsUser32DLL::QWindowsUser32DLL() :
updateLayeredWindowIndirect(0),
isHungAppWindow(0),
registerTouchWindow(0), unregisterTouchWindow(0),
- getTouchInputInfo(0), closeTouchInputHandle(0), setProcessDPIAware(0)
+ getTouchInputInfo(0), closeTouchInputHandle(0), setProcessDPIAware(0),
+ addClipboardFormatListener(0), removeClipboardFormatListener(0)
{
}
@@ -207,6 +208,11 @@ void QWindowsUser32DLL::init()
updateLayeredWindowIndirect = (UpdateLayeredWindowIndirect)(library.resolve("UpdateLayeredWindowIndirect"));
isHungAppWindow = (IsHungAppWindow)library.resolve("IsHungAppWindow");
setProcessDPIAware = (SetProcessDPIAware)library.resolve("SetProcessDPIAware");
+
+ if (QSysInfo::windowsVersion() >= QSysInfo::WV_VISTA) {
+ addClipboardFormatListener = (AddClipboardFormatListener)library.resolve("AddClipboardFormatListener");
+ removeClipboardFormatListener = (RemoveClipboardFormatListener)library.resolve("RemoveClipboardFormatListener");
+ }
}
bool QWindowsUser32DLL::initTouch()
diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h
index d565a5feab..086b968255 100644
--- a/src/plugins/platforms/windows/qwindowscontext.h
+++ b/src/plugins/platforms/windows/qwindowscontext.h
@@ -94,6 +94,8 @@ struct QWindowsUser32DLL
typedef BOOL (WINAPI *UpdateLayeredWindowIndirect)(HWND, const UPDATELAYEREDWINDOWINFO *);
typedef BOOL (WINAPI *IsHungAppWindow)(HWND);
typedef BOOL (WINAPI *SetProcessDPIAware)();
+ typedef BOOL (WINAPI *AddClipboardFormatListener)(HWND);
+ typedef BOOL (WINAPI *RemoveClipboardFormatListener)(HWND);
// Functions missing in Q_CC_GNU stub libraries.
SetLayeredWindowAttributes setLayeredWindowAttributes;
@@ -111,6 +113,10 @@ struct QWindowsUser32DLL
// Windows Vista onwards
SetProcessDPIAware setProcessDPIAware;
+
+ // Clipboard listeners, Windows Vista onwards
+ AddClipboardFormatListener addClipboardFormatListener;
+ RemoveClipboardFormatListener removeClipboardFormatListener;
};
struct QWindowsShell32DLL
diff --git a/tests/auto/gui/kernel/qclipboard/tst_qclipboard.cpp b/tests/auto/gui/kernel/qclipboard/tst_qclipboard.cpp
index cf786c1dca..8a2009a601 100644
--- a/tests/auto/gui/kernel/qclipboard/tst_qclipboard.cpp
+++ b/tests/auto/gui/kernel/qclipboard/tst_qclipboard.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the test suite of the Qt Toolkit.
@@ -134,6 +134,29 @@ void tst_QClipboard::modes()
}
}
+// A predicate to be used with a QSignalSpy / QTRY_VERIFY to ensure all delayed
+// notifications are eaten. It waits at least one cycle and returns true when
+// no new signals arrive.
+class EatSignalSpyNotificationsPredicate
+{
+public:
+ explicit EatSignalSpyNotificationsPredicate(QSignalSpy &spy) : m_spy(spy) { reset(); }
+
+ operator bool() const
+ {
+ if (m_timer.elapsed() && !m_spy.count())
+ return true;
+ m_spy.clear();
+ return false;
+ }
+
+ inline void reset() { m_timer.start(); }
+
+private:
+ QSignalSpy &m_spy;
+ QElapsedTimer m_timer;
+};
+
/*
Test that the appropriate signals are emitted when the clipboard
contents is changed by calling the qt functions.
@@ -149,6 +172,13 @@ void tst_QClipboard::testSignals()
QSignalSpy changedSpy(clipboard, SIGNAL(changed(QClipboard::Mode)));
QSignalSpy dataChangedSpy(clipboard, SIGNAL(dataChanged()));
+ // Clipboard notifications are asynchronous with the new AddClipboardFormatListener
+ // in Windows Vista (5.4). Eat away all signals to ensure they don't interfere
+ // with the QTRY_COMPARE below.
+ EatSignalSpyNotificationsPredicate noLeftOverDataChanges(dataChangedSpy);
+ EatSignalSpyNotificationsPredicate noLeftOverChanges(changedSpy);
+ QTRY_VERIFY(noLeftOverChanges && noLeftOverDataChanges);
+
QSignalSpy searchChangedSpy(clipboard, SIGNAL(findBufferChanged()));
QSignalSpy selectionChangedSpy(clipboard, SIGNAL(selectionChanged()));
@@ -156,7 +186,7 @@ void tst_QClipboard::testSignals()
// Test the default mode signal.
clipboard->setText(text);
- QCOMPARE(dataChangedSpy.count(), 1);
+ QTRY_COMPARE(dataChangedSpy.count(), 1);
QCOMPARE(searchChangedSpy.count(), 0);
QCOMPARE(selectionChangedSpy.count(), 0);
QCOMPARE(changedSpy.count(), 1);
@@ -296,6 +326,11 @@ void tst_QClipboard::setMimeData()
QSignalSpy spySelection(QGuiApplication::clipboard(), SIGNAL(selectionChanged()));
QSignalSpy spyData(QGuiApplication::clipboard(), SIGNAL(dataChanged()));
+ // Clipboard notifications are asynchronous with the new AddClipboardFormatListener
+ // in Windows Vista (5.4). Eat away all signals to ensure they don't interfere
+ // with the QTRY_COMPARE below.
+ EatSignalSpyNotificationsPredicate noLeftOverDataChanges(spyData);
+ QTRY_VERIFY(noLeftOverDataChanges);
QSignalSpy spyFindBuffer(QGuiApplication::clipboard(), SIGNAL(findBufferChanged()));
QGuiApplication::clipboard()->clear(QClipboard::Clipboard);
@@ -312,7 +347,7 @@ void tst_QClipboard::setMimeData()
else
QCOMPARE(spyFindBuffer.count(), 0);
- QCOMPARE(spyData.count(), 1);
+ QTRY_COMPARE(spyData.count(), 1);
// an other crash test
data = new QMimeData;
@@ -326,7 +361,8 @@ void tst_QClipboard::setMimeData()
newData->setText("bar");
spySelection.clear();
- spyData.clear();
+ noLeftOverDataChanges.reset();
+ QTRY_VERIFY(noLeftOverDataChanges);
spyFindBuffer.clear();
QGuiApplication::clipboard()->setMimeData(newData, QClipboard::Clipboard);
@@ -343,7 +379,7 @@ void tst_QClipboard::setMimeData()
else
QCOMPARE(spyFindBuffer.count(), 0);
- QCOMPARE(spyData.count(), 1);
+ QTRY_COMPARE(spyData.count(), 1);
}
void tst_QClipboard::clearBeforeSetText()