[PATCH 14/15] winemac: Implement the UpdateClipboard entry point to have the clipboard manager update its status.

Ken Thomases ken at codeweavers.com
Sun Oct 23 13:03:33 CDT 2016


Signed-off-by: Ken Thomases <ken at codeweavers.com>
---
 dlls/winemac.drv/clipboard.c      | 291 +++++++++++++++++++++++++++++++++++++-
 dlls/winemac.drv/winemac.drv.spec |   1 +
 2 files changed, 290 insertions(+), 2 deletions(-)

diff --git a/dlls/winemac.drv/clipboard.c b/dlls/winemac.drv/clipboard.c
index 936fa21..358794d 100644
--- a/dlls/winemac.drv/clipboard.c
+++ b/dlls/winemac.drv/clipboard.c
@@ -59,6 +59,8 @@ typedef struct _WINE_CLIPFORMAT
  *              Constants
  **************************************************************************/
 
+#define CLIPBOARD_UPDATE_DELAY 2000   /* delay between checks of the Mac pasteboard */
+
 
 /**************************************************************************
  *              Forward Function Declarations
@@ -195,6 +197,7 @@ static macdrv_window clipboard_cocoa_window;
 static ULONG64 last_clipboard_update;
 static WINE_CLIPFORMAT **current_mac_formats;
 static unsigned int nb_current_mac_formats;
+static WCHAR clipboard_pipe_name[256];
 
 
 /**************************************************************************
@@ -1633,6 +1636,36 @@ static BOOL grab_win32_clipboard(void)
 
 
 /**************************************************************************
+ *              update_clipboard
+ *
+ * Periodically update the clipboard while the clipboard is owned by a
+ * Mac app.
+ */
+static BOOL update_clipboard(void)
+{
+    static BOOL updating;
+    BOOL ret = TRUE;
+
+    TRACE("is_clipboard_owner %d last_clipboard_update %llu now %llu\n",
+          is_clipboard_owner, last_clipboard_update, GetTickCount64());
+
+    if (updating) return TRUE;
+    updating = TRUE;
+
+    if (is_clipboard_owner)
+    {
+        if (GetTickCount64() - last_clipboard_update > CLIPBOARD_UPDATE_DELAY)
+            ret = grab_win32_clipboard();
+    }
+    else if (!macdrv_is_pasteboard_owner())
+        ret = grab_win32_clipboard();
+
+    updating = FALSE;
+    return ret;
+}
+
+
+/**************************************************************************
  *              clipboard_wndproc
  *
  * Window procedure for the clipboard manager.
@@ -1689,6 +1722,42 @@ static BOOL wait_clipboard_mutex(void)
 
 
 /**************************************************************************
+ *              init_pipe_name
+ *
+ * Init-once helper for get_pipe_name.
+ */
+static BOOL CALLBACK init_pipe_name(INIT_ONCE* once, void* param, void** context)
+{
+    static const WCHAR prefix[] = {'\\','\\','.','\\','p','i','p','e','\\','_','_','w','i','n','e','_','c','l','i','p','b','o','a','r','d','_'};
+
+    memcpy(clipboard_pipe_name, prefix, sizeof(prefix));
+    if (!GetUserObjectInformationW(GetProcessWindowStation(), UOI_NAME,
+                                   clipboard_pipe_name + sizeof(prefix) / sizeof(WCHAR),
+                                   sizeof(clipboard_pipe_name) - sizeof(prefix), NULL))
+    {
+        ERR("failed to get winstation name\n");
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+/**************************************************************************
+ *              get_pipe_name
+ *
+ * Get the name of the pipe used to communicate with the per-window-station
+ * clipboard manager thread.
+ */
+static const WCHAR* get_pipe_name(void)
+{
+    static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
+    InitOnceExecuteOnce(&once, init_pipe_name, NULL, NULL);
+    return clipboard_pipe_name[0] ? clipboard_pipe_name : NULL;
+}
+
+
+/**************************************************************************
  *              clipboard_thread
  *
  * Thread running inside the desktop process to manage the clipboard
@@ -1698,6 +1767,11 @@ static DWORD WINAPI clipboard_thread(void *arg)
     static const WCHAR clipboard_classname[] = {'_','_','w','i','n','e','_','c','l','i','p','b','o','a','r','d','_','m','a','n','a','g','e','r',0};
     WNDCLASSW class;
     struct macdrv_window_features wf;
+    const WCHAR* pipe_name;
+    HANDLE pipe = NULL;
+    HANDLE event = NULL;
+    OVERLAPPED overlapped;
+    BOOL need_connect = TRUE, pending = FALSE;
     MSG msg;
 
     if (!wait_clipboard_mutex()) return 0;
@@ -1727,16 +1801,103 @@ static DWORD WINAPI clipboard_thread(void *arg)
         goto done;
     }
 
+    pipe_name = get_pipe_name();
+    if (!pipe_name)
+    {
+        ERR("failed to get pipe name\n");
+        goto done;
+    }
+
+    pipe = CreateNamedPipeW(pipe_name, PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
+                            PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, 1, 1, 0, NULL);
+    if (!pipe)
+    {
+        ERR("failed to create named pipe: %u\n", GetLastError());
+        goto done;
+    }
+
+    event = CreateEventW(NULL, TRUE, FALSE, NULL);
+    if (!event)
+    {
+        ERR("failed to create event: %d\n", GetLastError());
+        goto done;
+    }
+
     clipboard_thread_id = GetCurrentThreadId();
     AddClipboardFormatListener(clipboard_hwnd);
     register_builtin_formats();
     grab_win32_clipboard();
 
     TRACE("clipboard thread %04x running\n", GetCurrentThreadId());
-    while (GetMessageW(&msg, NULL, 0, 0))
-        DispatchMessageW(&msg);
+    while (1)
+    {
+        DWORD result;
+
+        if (need_connect)
+        {
+            pending = FALSE;
+            memset(&overlapped, 0, sizeof(overlapped));
+            overlapped.hEvent = event;
+            if (ConnectNamedPipe(pipe, &overlapped))
+            {
+                ERR("asynchronous ConnectNamedPipe unexpectedly returned true: %d\n", GetLastError());
+                ResetEvent(event);
+            }
+            else
+            {
+                result = GetLastError();
+                switch (result)
+                {
+                    case ERROR_PIPE_CONNECTED:
+                    case ERROR_NO_DATA:
+                        SetEvent(event);
+                        need_connect = FALSE;
+                        break;
+                    case ERROR_IO_PENDING:
+                        need_connect = FALSE;
+                        pending = TRUE;
+                        break;
+                    default:
+                        ERR("failed to initiate pipe connection: %d\n", result);
+                        break;
+                }
+            }
+        }
+
+        result = MsgWaitForMultipleObjectsEx(1, &event, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
+        switch (result)
+        {
+            case WAIT_OBJECT_0:
+            {
+                DWORD written;
+
+                if (pending && !GetOverlappedResult(pipe, &overlapped, &written, FALSE))
+                    ERR("failed to connect pipe: %d\n", GetLastError());
+
+                update_clipboard();
+                DisconnectNamedPipe(pipe);
+                need_connect = TRUE;
+                break;
+            }
+            case WAIT_OBJECT_0 + 1:
+                while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
+                {
+                    if (msg.message == WM_QUIT)
+                        goto done;
+                    DispatchMessageW(&msg);
+                }
+                break;
+            case WAIT_IO_COMPLETION:
+                break;
+            default:
+                ERR("failed to wait for connection or input: %d\n", GetLastError());
+                break;
+        }
+    }
 
 done:
+    if (event) CloseHandle(event);
+    if (pipe) CloseHandle(pipe);
     macdrv_destroy_cocoa_window(clipboard_cocoa_window);
     DestroyWindow(clipboard_hwnd);
     return 0;
@@ -1749,6 +1910,132 @@ done:
 
 
 /**************************************************************************
+ *              macdrv_UpdateClipboard
+ */
+void CDECL macdrv_UpdateClipboard(void)
+{
+    static ULONG last_update;
+    ULONG now, end;
+    const WCHAR* pipe_name;
+    HANDLE pipe;
+    BYTE dummy;
+    DWORD count;
+    OVERLAPPED overlapped = { 0 };
+    BOOL canceled = FALSE;
+
+    if (GetCurrentThreadId() == clipboard_thread_id) return;
+
+    TRACE("\n");
+
+    now = GetTickCount();
+    if ((int)(now - last_update) <= CLIPBOARD_UPDATE_DELAY) return;
+    last_update = now;
+
+    if (!(pipe_name = get_pipe_name())) return;
+    pipe = CreateFileW(pipe_name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
+    if (!pipe)
+    {
+        WARN("failed to open pipe to clipboard manager: %d\n", GetLastError());
+        return;
+    }
+
+    overlapped.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
+    if (!overlapped.hEvent)
+    {
+        ERR("failed to create event: %d\n", GetLastError());
+        goto done;
+    }
+
+    /* We expect the read to fail because the server just closes our connection.  This
+       is just waiting for that close to happen. */
+    if (ReadFile(pipe, &dummy, sizeof(dummy), NULL, &overlapped))
+    {
+        WARN("asynchronous ReadFile unexpectedly returned true: %d\n", GetLastError());
+        goto done;
+    }
+    else
+    {
+        DWORD error = GetLastError();
+        if (error == ERROR_PIPE_NOT_CONNECTED || error == ERROR_BROKEN_PIPE)
+        {
+            /* The server accepted, handled, and closed our connection before we
+               attempted the read, which is fine. */
+            goto done;
+        }
+        else if (error != ERROR_IO_PENDING)
+        {
+            ERR("failed to initiate read from pipe: %d\n", error);
+            goto done;
+        }
+    }
+
+    end = now + 500;
+    while (1)
+    {
+        DWORD result, timeout;
+
+        if (canceled)
+            timeout = INFINITE;
+        else
+        {
+            now = GetTickCount();
+            timeout = end - now;
+            if ((int)timeout < 0)
+                timeout = 0;
+        }
+
+        result = MsgWaitForMultipleObjectsEx(1, &overlapped.hEvent, timeout, QS_SENDMESSAGE, MWMO_ALERTABLE);
+        switch (result)
+        {
+            case WAIT_OBJECT_0:
+            {
+                if (GetOverlappedResult(pipe, &overlapped, &count, FALSE))
+                    WARN("unexpectedly succeeded in reading from pipe\n");
+                else
+                {
+                    result = GetLastError();
+                    if (result != ERROR_BROKEN_PIPE && result != ERROR_OPERATION_ABORTED)
+                        WARN("failed to read from pipe: %d\n", result);
+                }
+
+                goto done;
+            }
+            case WAIT_OBJECT_0 + 1:
+            {
+                MSG msg;
+                while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_SENDMESSAGE))
+                    DispatchMessageW(&msg);
+                break;
+            }
+            case WAIT_IO_COMPLETION:
+                break;
+            case WAIT_TIMEOUT:
+                WARN("timed out waiting for read\n");
+                CancelIoEx(pipe, &overlapped);
+                canceled = TRUE;
+                break;
+            default:
+                if (canceled)
+                {
+                    ERR("failed to wait for cancel: %d\n", GetLastError());
+                    goto done;
+                }
+
+                ERR("failed to wait for read: %d\n", GetLastError());
+                CancelIoEx(pipe, &overlapped);
+                canceled = TRUE;
+                break;
+        }
+    }
+
+done:
+    if (overlapped.hEvent) CloseHandle(overlapped.hEvent);
+    CloseHandle(pipe);
+}
+
+
+/**************************************************************************
  *              MACDRV Private Clipboard Exports
  **************************************************************************/
 
diff --git a/dlls/winemac.drv/winemac.drv.spec b/dlls/winemac.drv/winemac.drv.spec
index 3e6619e..6fa723a 100644
--- a/dlls/winemac.drv/winemac.drv.spec
+++ b/dlls/winemac.drv/winemac.drv.spec
@@ -38,6 +38,7 @@
 @ cdecl ThreadDetach() macdrv_ThreadDetach
 @ cdecl ToUnicodeEx(long long ptr ptr long long long) macdrv_ToUnicodeEx
 @ cdecl UnregisterHotKey(long long long) macdrv_UnregisterHotKey
+@ cdecl UpdateClipboard() macdrv_UpdateClipboard
 @ cdecl UpdateLayeredWindow(long ptr ptr) macdrv_UpdateLayeredWindow
 @ cdecl VkKeyScanEx(long long) macdrv_VkKeyScanEx
 @ cdecl WindowMessage(long long long long) macdrv_WindowMessage
-- 
2.8.2




More information about the wine-patches mailing list