Преглед изворни кода

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.
Camilla Löwy пре 5 година
родитељ
комит
29885c6942
3 измењених фајлова са 38 додато и 10 уклоњено
  1. 1 0
      README.md
  2. 10 0
      include/GLFW/glfw3.h
  3. 27 10
      src/win32_window.c

+ 1 - 0
README.md

@@ -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: Fix pkg-config for dynamic library on Windows (#2386, #2420)
  - [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 locating the Vulkan loader at runtime in an application bundle
  - [Cocoa] Moved main menu creation to GLFW initialization time (#1649)

+ 10 - 0
include/GLFW/glfw3.h

@@ -5814,6 +5814,11 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state);
  *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED 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 specified string is copied before this function
  *  returns.
  *
@@ -5842,6 +5847,11 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string);
  *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
  *  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
  *  should not free it yourself.  It is valid until the next call to @ref
  *  glfwGetClipboardString or @ref glfwSetClipboardString, or until the library

+ 27 - 10
src/win32_window.c

@@ -2293,7 +2293,7 @@ void _glfwSetCursorWin32(_GLFWwindow* window, _GLFWcursor* cursor)
 
 void _glfwSetClipboardStringWin32(const char* string)
 {
-    int characterCount;
+    int characterCount, tries = 0;
     HANDLE object;
     WCHAR* buffer;
 
@@ -2321,12 +2321,20 @@ void _glfwSetClipboardStringWin32(const char* string)
     MultiByteToWideChar(CP_UTF8, 0, string, -1, buffer, characterCount);
     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))
     {
-        _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
-                             "Win32: Failed to open clipboard");
-        GlobalFree(object);
-        return;
+        Sleep(1);
+        tries++;
+
+        if (tries == 3)
+        {
+            _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
+                                 "Win32: Failed to open clipboard");
+            GlobalFree(object);
+            return;
+        }
     }
 
     EmptyClipboard();
@@ -2338,12 +2346,21 @@ const char* _glfwGetClipboardStringWin32(void)
 {
     HANDLE object;
     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))
     {
-        _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
-                             "Win32: Failed to open clipboard");
-        return NULL;
+        Sleep(1);
+        tries++;
+
+        if (tries == 3)
+        {
+            _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
+                                 "Win32: Failed to open clipboard");
+            return NULL;
+        }
     }
 
     object = GetClipboardData(CF_UNICODETEXT);