Win32: Improve clipboard contention issue

This is primarily a workaround for a GLFW application reading and/or
writing to the clipboard in rapid succession and catching up with the
Windows Clipboard History, which also has to contend for the lock.
This commit is contained in:
Camilla Löwy 2020-01-17 03:25:54 +01:00
parent 2c3eb75748
commit 29885c6942
3 changed files with 38 additions and 10 deletions

View File

@ -234,6 +234,7 @@ information on what to include when reporting a bug.
- [Win32] Bugfix: `glfwWaitEventsTimeout` did not return for some sent messages (#2408) - [Win32] Bugfix: `glfwWaitEventsTimeout` did not return for some sent messages (#2408)
- [Win32] Bugfix: Fix pkg-config for dynamic library on Windows (#2386, #2420) - [Win32] Bugfix: Fix pkg-config for dynamic library on Windows (#2386, #2420)
- [Win32] Bugfix: XInput could reportedly provide invalid DPad bit masks (#2291) - [Win32] Bugfix: XInput could reportedly provide invalid DPad bit masks (#2291)
- [Win32] Bugfix: Rapid clipboard calls could fail due to Clipboard History
- [Cocoa] Added support for `VK_EXT_metal_surface` (#1619) - [Cocoa] Added support for `VK_EXT_metal_surface` (#1619)
- [Cocoa] Added locating the Vulkan loader at runtime in an application bundle - [Cocoa] Added locating the Vulkan loader at runtime in an application bundle
- [Cocoa] Moved main menu creation to GLFW initialization time (#1649) - [Cocoa] Moved main menu creation to GLFW initialization time (#1649)

View File

@ -5814,6 +5814,11 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state);
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR. * GLFW_PLATFORM_ERROR.
* *
* @remark @win32 The clipboard on Windows has a single global lock for reading and
* writing. GLFW tries to acquire it a few times, which is almost always enough. If it
* cannot acquire the lock then this function emits @ref GLFW_PLATFORM_ERROR and returns.
* It is safe to try this multiple times.
*
* @pointer_lifetime The specified string is copied before this function * @pointer_lifetime The specified string is copied before this function
* returns. * returns.
* *
@ -5842,6 +5847,11 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string);
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
* GLFW_FORMAT_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. * GLFW_FORMAT_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR.
* *
* @remark @win32 The clipboard on Windows has a single global lock for reading and
* writing. GLFW tries to acquire it a few times, which is almost always enough. If it
* cannot acquire the lock then this function emits @ref GLFW_PLATFORM_ERROR and returns.
* It is safe to try this multiple times.
*
* @pointer_lifetime The returned string is allocated and freed by GLFW. You * @pointer_lifetime The returned string is allocated and freed by GLFW. You
* should not free it yourself. It is valid until the next call to @ref * should not free it yourself. It is valid until the next call to @ref
* glfwGetClipboardString or @ref glfwSetClipboardString, or until the library * glfwGetClipboardString or @ref glfwSetClipboardString, or until the library

View File

@ -2293,7 +2293,7 @@ void _glfwSetCursorWin32(_GLFWwindow* window, _GLFWcursor* cursor)
void _glfwSetClipboardStringWin32(const char* string) void _glfwSetClipboardStringWin32(const char* string)
{ {
int characterCount; int characterCount, tries = 0;
HANDLE object; HANDLE object;
WCHAR* buffer; WCHAR* buffer;
@ -2321,13 +2321,21 @@ void _glfwSetClipboardStringWin32(const char* string)
MultiByteToWideChar(CP_UTF8, 0, string, -1, buffer, characterCount); MultiByteToWideChar(CP_UTF8, 0, string, -1, buffer, characterCount);
GlobalUnlock(object); GlobalUnlock(object);
if (!OpenClipboard(_glfw.win32.helperWindowHandle)) // NOTE: Retry clipboard opening a few times as some other application may have it
// open and also the Windows Clipboard History reads it after each update
while (!OpenClipboard(_glfw.win32.helperWindowHandle))
{
Sleep(1);
tries++;
if (tries == 3)
{ {
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR, _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"Win32: Failed to open clipboard"); "Win32: Failed to open clipboard");
GlobalFree(object); GlobalFree(object);
return; return;
} }
}
EmptyClipboard(); EmptyClipboard();
SetClipboardData(CF_UNICODETEXT, object); SetClipboardData(CF_UNICODETEXT, object);
@ -2338,13 +2346,22 @@ const char* _glfwGetClipboardStringWin32(void)
{ {
HANDLE object; HANDLE object;
WCHAR* buffer; WCHAR* buffer;
int tries = 0;
if (!OpenClipboard(_glfw.win32.helperWindowHandle)) // NOTE: Retry clipboard opening a few times as some other application may have it
// open and also the Windows Clipboard History reads it after each update
while (!OpenClipboard(_glfw.win32.helperWindowHandle))
{
Sleep(1);
tries++;
if (tries == 3)
{ {
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR, _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"Win32: Failed to open clipboard"); "Win32: Failed to open clipboard");
return NULL; return NULL;
} }
}
object = GetClipboardData(CF_UNICODETEXT); object = GetClipboardData(CF_UNICODETEXT);
if (!object) if (!object)