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

View file

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