feat: Windows, Use a clipboard format listener to monitor the clipboard

On Windows monitor the clipboard using a Clipboard format listener instead
of the legacy clipboard viewer chain, which was unreliable as it required
other programs to maintain the clipboard chain properly and couldn't
recover it any window in the clipboard viewer chain stopped responding
to messages.

Monitor the clipboard sequence number to not process the clipboard more
than once.
This commit is contained in:
Pedro Navarro
2024-12-07 14:19:57 -08:00
committed by Nick Bolton
parent 5026eea60a
commit 252d11a316
2 changed files with 21 additions and 23 deletions

View File

@ -119,7 +119,7 @@ MSWindowsScreen::MSWindowsScreen(
m_screensaverNotify(false),
m_screensaverActive(false),
m_window(NULL),
m_nextClipboardWindow(NULL),
m_clipboardSequenceNumber(0),
m_ownClipboard(false),
m_desks(NULL),
m_keyState(NULL),
@ -237,7 +237,9 @@ void MSWindowsScreen::enable()
);
// install our clipboard snooper
m_nextClipboardWindow = SetClipboardViewer(m_window);
if (!AddClipboardFormatListener(m_window)) {
LOG((CLOG_DEBUG "failed to add the clipboard format listener: %d", GetLastError()));
}
// track the active desk and (re)install the hooks
m_desks->enable();
@ -268,8 +270,9 @@ void MSWindowsScreen::disable()
m_keyState->disable();
// stop snooping the clipboard
ChangeClipboardChain(m_window, m_nextClipboardWindow);
m_nextClipboardWindow = NULL;
if (!RemoveClipboardFormatListener(m_window)) {
LOG((CLOG_DEBUG "failed to remove the clipboard format listener: %d", GetLastError()));
}
// uninstall fix timer
if (m_fixTimer != NULL) {
@ -1021,23 +1024,20 @@ bool MSWindowsScreen::onPreDispatchPrimary(HWND, UINT message, WPARAM wParam, LP
bool MSWindowsScreen::onEvent(HWND, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *result)
{
switch (msg) {
case WM_DRAWCLIPBOARD:
// first pass on the message
if (m_nextClipboardWindow != NULL) {
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
}
// now handle the message
return onClipboardChange();
case WM_CLIPBOARDUPDATE: {
DWORD clipboardSequenceNumber = GetClipboardSequenceNumber();
LOG(
(CLOG_DEBUG "clipboard update: sequence number %d, current %d", clipboardSequenceNumber,
m_clipboardSequenceNumber)
);
case WM_CHANGECBCHAIN:
if (m_nextClipboardWindow == (HWND)wParam) {
m_nextClipboardWindow = (HWND)lParam;
LOG((CLOG_DEBUG "clipboard chain: new next: 0x%08x", m_nextClipboardWindow));
} else if (m_nextClipboardWindow != NULL) {
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
if (clipboardSequenceNumber && (clipboardSequenceNumber != m_clipboardSequenceNumber)) {
m_clipboardSequenceNumber = clipboardSequenceNumber;
onClipboardChange();
}
return true;
return 0; // message processed
}
case WM_DISPLAYCHANGE:
return onDisplayChange();
@ -1430,7 +1430,7 @@ bool MSWindowsScreen::onDisplayChange()
return true;
}
bool MSWindowsScreen::onClipboardChange()
void MSWindowsScreen::onClipboardChange()
{
// now notify client that somebody changed the clipboard (unless
// we're the owner).
@ -1445,8 +1445,6 @@ bool MSWindowsScreen::onClipboardChange()
LOG((CLOG_DEBUG "clipboard changed: %s owned", kAppId));
m_ownClipboard = true;
}
return true;
}
void MSWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y)

View File

@ -192,7 +192,7 @@ private: // HACK
bool onMouseWheel(SInt32 xDelta, SInt32 yDelta);
bool onScreensaver(bool activated);
bool onDisplayChange();
bool onClipboardChange();
void onClipboardChange();
// warp cursor without discarding queued events
void warpCursorNoFlush(SInt32 x, SInt32 y);
@ -314,7 +314,7 @@ private:
// clipboard stuff. our window is used mainly as a clipboard
// owner and as a link in the clipboard viewer chain.
HWND m_window;
HWND m_nextClipboardWindow;
DWORD m_clipboardSequenceNumber;
bool m_ownClipboard;
// one desk per desktop and a cond var to communicate with it