Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwindowsclipboard.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qwindowsclipboard.h"
5#include "qwindowscontext.h"
6#include "qwindowsole.h"
7
8#include <QtGui/qguiapplication.h>
9#include <QtGui/qclipboard.h>
10#include <QtGui/qcolor.h>
11#include <QtGui/qimage.h>
12
13#include <QtCore/qdebug.h>
14#include <QtCore/qmimedata.h>
15#include <QtCore/qstringlist.h>
16#include <QtCore/qthread.h>
17#include <QtCore/qvariant.h>
18#include <QtCore/qurl.h>
19#include <QtCore/private/qsystemerror_p.h>
20
21#include <QtGui/private/qwindowsguieventdispatcher_p.h>
22
24
40#ifndef QT_NO_DEBUG_STREAM
42{
43 QDebugStateSaver saver(d);
44 d.nospace();
45 d << "QMimeData(";
46 if (mimeData) {
48 d << "formats=" << formats.join(u", ");
49 if (mimeData->hasText())
50 d << ", text=" << mimeData->text();
51 if (mimeData->hasHtml())
52 d << ", html=" << mimeData->html();
53 if (mimeData->hasColor())
54 d << ", colorData=" << qvariant_cast<QColor>(mimeData->colorData());
55 if (mimeData->hasImage())
56 d << ", imageData=" << qvariant_cast<QImage>(mimeData->imageData());
57 if (mimeData->hasUrls())
58 d << ", urls=" << mimeData->urls();
59 } else {
60 d << '0';
61 }
62 d << ')';
63 return d;
64}
65#endif // !QT_NO_DEBUG_STREAM
66
79{
80 enum : int { attempts = 3 };
81 IDataObject * pDataObj = nullptr;
82 // QTBUG-53979, retry in case the other application has clipboard locked
83 for (int i = 1; i <= attempts; ++i) {
84 if (SUCCEEDED(OleGetClipboard(&pDataObj))) {
86 qCDebug(lcQpaMime) << __FUNCTION__ << pDataObj;
87 return pDataObj;
88 }
89 qCWarning(lcQpaMime, i == attempts
90 ? "Unable to obtain clipboard."
91 : "Retrying to obtain clipboard.");
93 }
94
95 return nullptr;
96}
97
99{
100 dataObject->Release();
101}
102
103extern "C" LRESULT QT_WIN_CALLBACK qClipboardViewerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
104{
105 LRESULT result = 0;
107 && QWindowsClipboard::instance()->clipboardViewerWndProc(hwnd, message, wParam, lParam, &result))
108 return result;
109 return DefWindowProc(hwnd, message, wParam, lParam);
110}
111
112// QTBUG-36958, ensure the clipboard is flushed before
113// QGuiApplication is destroyed since OleFlushClipboard()
114// might query the data again which causes problems
115// for QMimeData-derived classes using QPixmap/QImage.
117{
119 cl->cleanup();
120}
121
122QWindowsClipboard *QWindowsClipboard::m_instance = nullptr;
123
125{
126 QWindowsClipboard::m_instance = this;
128}
129
131{
132 cleanup();
133 QWindowsClipboard::m_instance = nullptr;
134}
135
137{
138 unregisterViewer(); // Should release data if owner.
139 releaseIData();
140}
141
142void QWindowsClipboard::releaseIData()
143{
144 if (m_data) {
145 delete m_data->mimeData();
146 m_data->releaseQt();
147 m_data->Release();
148 m_data = nullptr;
149 }
150}
151
153{
154 m_clipboardViewer = QWindowsContext::instance()->
155 createDummyWindow(QStringLiteral("ClipboardView"), L"QtClipboardView",
156 qClipboardViewerWndProc, WS_OVERLAPPED);
157
158 m_formatListenerRegistered = AddClipboardFormatListener(m_clipboardViewer);
159 if (!m_formatListenerRegistered)
160 qErrnoWarning("AddClipboardFormatListener() failed.");
161
162 if (!m_formatListenerRegistered)
163 m_nextClipboardViewer = SetClipboardViewer(m_clipboardViewer);
164
165 qCDebug(lcQpaMime) << __FUNCTION__ << "m_clipboardViewer:" << m_clipboardViewer
166 << "format listener:" << m_formatListenerRegistered
167 << "next:" << m_nextClipboardViewer;
168}
169
170void QWindowsClipboard::unregisterViewer()
171{
172 if (m_clipboardViewer) {
173 if (m_formatListenerRegistered) {
174 RemoveClipboardFormatListener(m_clipboardViewer);
175 m_formatListenerRegistered = false;
176 } else {
177 ChangeClipboardChain(m_clipboardViewer, m_nextClipboardViewer);
178 m_nextClipboardViewer = nullptr;
179 }
180 DestroyWindow(m_clipboardViewer);
181 m_clipboardViewer = nullptr;
182 }
183}
184
185// ### FIXME: Qt 6: Remove the clipboard chain handling code and make the
186// format listener the default.
187
188static bool isProcessBeingDebugged(HWND hwnd)
189{
190 DWORD pid = 0;
191 if (!GetWindowThreadProcessId(hwnd, &pid) || !pid)
192 return false;
193 const HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
194 if (!processHandle)
195 return false;
196 BOOL debugged = FALSE;
197 CheckRemoteDebuggerPresent(processHandle, &debugged);
198 CloseHandle(processHandle);
199 return debugged != FALSE;
200}
201
202void QWindowsClipboard::propagateClipboardMessage(UINT message, WPARAM wParam, LPARAM lParam) const
203{
204 if (!m_nextClipboardViewer)
205 return;
206 // In rare cases, a clipboard viewer can hang (application crashed,
207 // suspended by a shell prompt 'Select' or debugger).
208 if (IsHungAppWindow(m_nextClipboardViewer)) {
209 qWarning("Cowardly refusing to send clipboard message to hung application...");
210 return;
211 }
212 // Do not block if the process is being debugged, specifically, if it is
213 // displaying a runtime assert, which is not caught by isHungAppWindow().
214 if (isProcessBeingDebugged(m_nextClipboardViewer))
215 PostMessage(m_nextClipboardViewer, message, wParam, lParam);
216 else
217 SendMessage(m_nextClipboardViewer, message, wParam, lParam);
218}
219
225bool QWindowsClipboard::clipboardViewerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result)
226{
227 enum { wMClipboardUpdate = 0x031D };
228
229 *result = 0;
231 qCDebug(lcQpaMime) << __FUNCTION__ << hwnd << message << QWindowsGuiEventDispatcher::windowsMessageName(message);
232
233 switch (message) {
234 case WM_CHANGECBCHAIN: {
235 const HWND toBeRemoved = reinterpret_cast<HWND>(wParam);
236 if (toBeRemoved == m_nextClipboardViewer) {
237 m_nextClipboardViewer = reinterpret_cast<HWND>(lParam);
238 } else {
239 propagateClipboardMessage(message, wParam, lParam);
240 }
241 }
242 return true;
243 case wMClipboardUpdate: // Clipboard Format listener (Vista onwards)
244 case WM_DRAWCLIPBOARD: { // Clipboard Viewer Chain handling (up to XP)
245 const bool owned = ownsClipboard();
246 qCDebug(lcQpaMime) << "Clipboard changed owned " << owned;
248 // clean up the clipboard object if we no longer own the clipboard
249 if (!owned && m_data)
250 releaseIData();
251 if (!m_formatListenerRegistered)
252 propagateClipboardMessage(message, wParam, lParam);
253 }
254 return true;
255 case WM_DESTROY:
256 // Recommended shutdown
257 if (ownsClipboard()) {
258 qCDebug(lcQpaMime) << "Clipboard owner on shutdown, releasing.";
259 OleFlushClipboard();
260 releaseIData();
261 }
262 return true;
263 } // switch (message)
264 return false;
265}
266
268{
269 qCDebug(lcQpaMime) << __FUNCTION__ << mode;
271 return nullptr;
272 if (ownsClipboard())
273 return m_data->mimeData();
274 return &m_retrievalData;
275}
276
278{
279 qCDebug(lcQpaMime) << __FUNCTION__ << mode << mimeData;
281 return;
282
283 const bool newData = !m_data || m_data->mimeData() != mimeData;
284 if (newData) {
285 releaseIData();
286 if (mimeData)
287 m_data = new QWindowsOleDataObject(mimeData);
288 }
289
290 HRESULT src = S_FALSE;
291 int attempts = 0;
292 for (; attempts < 3; ++attempts) {
293 src = OleSetClipboard(m_data);
294 if (src != CLIPBRD_E_CANT_OPEN || QWindowsContext::isSessionLocked())
295 break;
296 QThread::msleep(100);
297 }
298
299 if (src != S_OK) {
300 QString mimeDataFormats = mimeData ?
301 mimeData->formats().join(u", ") : QString(QStringLiteral("NULL"));
302 qErrnoWarning("OleSetClipboard: Failed to set mime data (%s) on clipboard: %s",
303 qPrintable(mimeDataFormats),
304 qPrintable(QSystemError::windowsComString(src)));
305 releaseIData();
306 return;
307 }
308}
309
310void QWindowsClipboard::clear()
311{
312 const HRESULT src = OleSetClipboard(nullptr);
313 if (src != S_OK)
314 qErrnoWarning("OleSetClipboard: Failed to clear the clipboard: 0x%lx", src);
315}
316
321
322// Need a non-virtual in destructor.
323bool QWindowsClipboard::ownsClipboard() const
324{
325 return m_data && OleIsCurrentClipboard(m_data) == S_OK;
326}
327
329{
330 const bool result = mode == QClipboard::Clipboard ?
331 ownsClipboard() : false;
332 qCDebug(lcQpaMime) << __FUNCTION__ << mode << result;
333 return result;
334}
335
Mode
\keyword clipboard mode
Definition qclipboard.h:27
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
Definition qmimedata.h:16
bool hasUrls() const
Returns true if the object can return a list of urls; otherwise returns false.
QVariant colorData() const
Returns a color if the data stored in the object represents a color (MIME type application/x-color); ...
QVariant imageData() const
Returns a QVariant storing a QImage if the object can return an image; otherwise returns a null varia...
bool hasHtml() const
Returns true if the object can return HTML (MIME type text/html); otherwise returns false.
bool hasImage() const
Returns true if the object can return an image; otherwise returns false.
bool hasText() const
Returns true if the object can return plain text (MIME type text/plain); otherwise returns false.
QString html() const
Returns a string if the data stored in the object is HTML (MIME type text/html); otherwise returns an...
QList< QUrl > urls() const
Returns a list of URLs contained within the MIME data object.
bool hasColor() const
Returns true if the object can return a color (MIME type application/x-color); otherwise returns fals...
virtual QStringList formats() const
Returns a list of formats supported by the object.
QString text() const
Returns a plain text (MIME type text/plain) representation of the data.
void emitChanged(QClipboard::Mode mode)
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static void msleep(unsigned long)
void releaseDataObject(IDataObject *) const override
IDataObject * retrieveDataObject() const override
Clipboard implementation.
void setMimeData(QMimeData *data, QClipboard::Mode mode=QClipboard::Clipboard) override
bool supportsMode(QClipboard::Mode mode) const override
QMimeData * mimeData(QClipboard::Mode mode=QClipboard::Clipboard) override
bool ownsMode(QClipboard::Mode mode) const override
static QWindowsClipboard * instance()
bool clipboardViewerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result)
Windows procedure of the clipboard viewer.
static bool isSessionLocked()
static QWindowsContext * instance()
static const char * windowsMessageName(UINT msg)
OLE data container.
Definition qwindowsole.h:22
QMimeData * mimeData() const
void qErrnoWarning(const char *msg,...)
EGLint EGLint * formats
Combined button and popup list for selecting options.
void qAddPostRoutine(QtCleanUpFunction p)
static Window createDummyWindow(Display *dpy, XVisualInfo *visualInfo, int screenNumber, Window rootWin)
#define qWarning
Definition qlogging.h:166
#define qCWarning(category,...)
#define qCDebug(category,...)
GLenum mode
GLenum src
GLuint GLsizei const GLchar * message
GLuint64EXT * result
[6]
#define qPrintable(string)
Definition qstring.h:1531
#define QStringLiteral(str)
long HRESULT
LRESULT QT_WIN_CALLBACK qClipboardViewerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
static bool isProcessBeingDebugged(HWND hwnd)
static void cleanClipboardPostRoutine()
static QDebug operator<<(QDebug d, const QMimeData *mimeData)
QMimeData * mimeData