From 98b24dcb5059e471673ece96eb33b31052d29867 Mon Sep 17 00:00:00 2001
From: Alexandre Julliard <julliard@winehq.org>
Date: Mon, 22 Aug 2016 19:26:55 +0900
Subject: [PATCH] server: Add a separate request to set the clipboard viewer.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
---
 dlls/user32/clipboard.c        | 68 ++++++++++++++++------------------
 dlls/user32/tests/msg.c        |  2 +-
 include/wine/server_protocol.h | 24 ++++++++++--
 server/clipboard.c             | 19 +++++++++-
 server/protocol.def            | 12 +++++-
 server/request.h               |  9 ++++-
 server/trace.c                 | 16 +++++++-
 7 files changed, 104 insertions(+), 46 deletions(-)

diff --git a/dlls/user32/clipboard.c b/dlls/user32/clipboard.c
index 915c370ea62..dcb317210a9 100644
--- a/dlls/user32/clipboard.c
+++ b/dlls/user32/clipboard.c
@@ -109,25 +109,6 @@ void CLIPBOARD_ReleaseOwner( HWND hwnd )
 }
 
 
-/**************************************************************************
- *		CLIPBOARD_SetClipboardViewer
- */
-static HWND CLIPBOARD_SetClipboardViewer( HWND hWnd )
-{
-    HWND hwndPrev = 0;
-
-    SERVER_START_REQ( set_clipboard_info )
-    {
-        req->flags = SET_CB_VIEWER;
-        req->viewer = wine_server_user_handle( hWnd );
-        if (!wine_server_call_err( req ))
-            hwndPrev = wine_server_ptr_handle( reply->old_viewer );
-    }
-    SERVER_END_REQ;
-
-    return hwndPrev;
-}
-
 /**************************************************************************
  *                WIN32 Clipboard implementation
  **************************************************************************/
@@ -298,15 +279,25 @@ HWND WINAPI GetOpenClipboardWindow(void)
 /**************************************************************************
  *		SetClipboardViewer (USER32.@)
  */
-HWND WINAPI SetClipboardViewer( HWND hWnd )
+HWND WINAPI SetClipboardViewer( HWND hwnd )
 {
-    HWND hwndPrev = CLIPBOARD_SetClipboardViewer(hWnd);
+    HWND prev = 0, owner = 0;
 
-    if (hWnd)
-        SendNotifyMessageW(hWnd, WM_DRAWCLIPBOARD, (WPARAM) GetClipboardOwner(), 0);
-    TRACE("(%p): returning %p\n", hWnd, hwndPrev);
+    SERVER_START_REQ( set_clipboard_viewer )
+    {
+        req->viewer = wine_server_user_handle( hwnd );
+        if (!wine_server_call_err( req ))
+        {
+            prev = wine_server_ptr_handle( reply->old_viewer );
+            owner = wine_server_ptr_handle( reply->owner );
+        }
+    }
+    SERVER_END_REQ;
 
-    return hwndPrev;
+    if (hwnd) SendNotifyMessageW( hwnd, WM_DRAWCLIPBOARD, (WPARAM)owner, 0 );
+
+    TRACE( "(%p): returning %p\n", hwnd, prev );
+    return prev;
 }
 
 
@@ -333,22 +324,27 @@ HWND WINAPI GetClipboardViewer(void)
 /**************************************************************************
  *              ChangeClipboardChain (USER32.@)
  */
-BOOL WINAPI ChangeClipboardChain(HWND hWnd, HWND hWndNext)
+BOOL WINAPI ChangeClipboardChain( HWND hwnd, HWND next )
 {
-    BOOL bRet = TRUE;
-    HWND hWndViewer = GetClipboardViewer();
+    NTSTATUS status;
+    HWND viewer;
+
+    if (!hwnd) return FALSE;
 
-    if (hWndViewer)
+    SERVER_START_REQ( set_clipboard_viewer )
     {
-        if (WIN_GetFullHandle(hWnd) == hWndViewer)
-            CLIPBOARD_SetClipboardViewer(WIN_GetFullHandle(hWndNext));
-        else
-            bRet = !SendMessageW(hWndViewer, WM_CHANGECBCHAIN, (WPARAM)hWnd, (LPARAM)hWndNext);
+        req->viewer = wine_server_user_handle( next );
+        req->previous = wine_server_user_handle( hwnd );
+        status = wine_server_call( req );
+        viewer = wine_server_ptr_handle( reply->old_viewer );
     }
-    else
-        ERR("hWndViewer is lost\n");
+    SERVER_END_REQ;
 
-    return bRet;
+    if (status == STATUS_PENDING)
+        return !SendMessageW( viewer, WM_CHANGECBCHAIN, (WPARAM)hwnd, (LPARAM)next );
+
+    if (status) SetLastError( RtlNtStatusToDosError( status ));
+    return !status;
 }
 
 
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c
index 07d10b4e29a..61980dfb81b 100644
--- a/dlls/user32/tests/msg.c
+++ b/dlls/user32/tests/msg.c
@@ -14027,7 +14027,7 @@ static void test_clipboard_viewers(void)
     expect_HWND(hWnd1, GetClipboardViewer());
 
     ChangeClipboardChain(NULL, hWnd2);
-    ok_sequence(WmEmptySeq, "change chain (viewer=1, remove=NULL, next=2)", TRUE);
+    ok_sequence(WmEmptySeq, "change chain (viewer=1, remove=NULL, next=2)", FALSE);
     expect_HWND(hWnd1, GetClipboardViewer());
 
     /* Actually change clipboard viewer with ChangeClipboardChain. */
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 6576eb901c9..c865bb8c1c3 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -4493,7 +4493,7 @@ struct set_clipboard_info_request
     struct request_header __header;
     unsigned int   flags;
     user_handle_t  owner;
-    user_handle_t  viewer;
+    char __pad_20[4];
 };
 struct set_clipboard_info_reply
 {
@@ -4506,7 +4506,6 @@ struct set_clipboard_info_reply
     char __pad_28[4];
 };
 
-#define SET_CB_VIEWER    0x004
 #define SET_CB_SEQNO     0x008
 #define SET_CB_RELOWNER  0x010
 #define CB_OPEN          0x040
@@ -4527,6 +4526,22 @@ struct empty_clipboard_reply
 
 
 
+struct set_clipboard_viewer_request
+{
+    struct request_header __header;
+    user_handle_t  viewer;
+    user_handle_t  previous;
+    char __pad_20[4];
+};
+struct set_clipboard_viewer_reply
+{
+    struct reply_header __header;
+    user_handle_t  old_viewer;
+    user_handle_t  owner;
+};
+
+
+
 struct open_token_request
 {
     struct request_header __header;
@@ -5659,6 +5674,7 @@ enum request
     REQ_close_clipboard,
     REQ_set_clipboard_info,
     REQ_empty_clipboard,
+    REQ_set_clipboard_viewer,
     REQ_open_token,
     REQ_set_global_windows,
     REQ_adjust_token_privileges,
@@ -5941,6 +5957,7 @@ union generic_request
     struct close_clipboard_request close_clipboard_request;
     struct set_clipboard_info_request set_clipboard_info_request;
     struct empty_clipboard_request empty_clipboard_request;
+    struct set_clipboard_viewer_request set_clipboard_viewer_request;
     struct open_token_request open_token_request;
     struct set_global_windows_request set_global_windows_request;
     struct adjust_token_privileges_request adjust_token_privileges_request;
@@ -6221,6 +6238,7 @@ union generic_reply
     struct close_clipboard_reply close_clipboard_reply;
     struct set_clipboard_info_reply set_clipboard_info_reply;
     struct empty_clipboard_reply empty_clipboard_reply;
+    struct set_clipboard_viewer_reply set_clipboard_viewer_reply;
     struct open_token_reply open_token_reply;
     struct set_global_windows_reply set_global_windows_reply;
     struct adjust_token_privileges_reply adjust_token_privileges_reply;
@@ -6280,6 +6298,6 @@ union generic_reply
     struct terminate_job_reply terminate_job_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 510
+#define SERVER_PROTOCOL_VERSION 511
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/clipboard.c b/server/clipboard.c
index 423892531c6..c906e702324 100644
--- a/server/clipboard.c
+++ b/server/clipboard.c
@@ -219,8 +219,6 @@ DECL_HANDLER(set_clipboard_info)
         if (!release_clipboard_owner( clipboard, req->owner )) return;
     }
 
-    if (req->flags & SET_CB_VIEWER) clipboard->viewer = get_user_full_handle( req->viewer );
-
     if (req->flags & SET_CB_SEQNO) clipboard->seqno++;
 
     reply->seqno = get_seqno( clipboard );
@@ -248,3 +246,20 @@ DECL_HANDLER(empty_clipboard)
     clipboard->owner_thread = clipboard->open_thread;
     clipboard->seqno++;
 }
+
+
+/* set the clipboard viewer window */
+DECL_HANDLER(set_clipboard_viewer)
+{
+    struct clipboard *clipboard = get_process_clipboard();
+
+    if (!clipboard) return;
+
+    reply->old_viewer = clipboard->viewer;
+    reply->owner      = clipboard->owner_win;
+
+    if (!req->previous || clipboard->viewer == get_user_full_handle( req->previous ))
+        clipboard->viewer = get_user_full_handle( req->viewer );
+    else
+        set_error( STATUS_PENDING );  /* need to send message instead */
+}
diff --git a/server/protocol.def b/server/protocol.def
index 4b800de59f7..56145469c8a 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -3179,7 +3179,6 @@ enum caret_state
 @REQ(set_clipboard_info)
     unsigned int   flags;       /* flags for fields to set (see below) */
     user_handle_t  owner;       /* clipboard owner */
-    user_handle_t  viewer;      /* first clipboard viewer */
 @REPLY
     unsigned int   flags;           /* status flags (see below) */
     user_handle_t  old_clipboard;   /* old clipboard window */
@@ -3188,7 +3187,6 @@ enum caret_state
     unsigned int   seqno;           /* current sequence number */
 @END
 
-#define SET_CB_VIEWER    0x004
 #define SET_CB_SEQNO     0x008
 #define SET_CB_RELOWNER  0x010
 #define CB_OPEN          0x040
@@ -3201,6 +3199,16 @@ enum caret_state
 @END
 
 
+/* Set the clipboard viewer window */
+@REQ(set_clipboard_viewer)
+    user_handle_t  viewer;         /* clipboard viewer */
+    user_handle_t  previous;       /* if non-zero, check that this was the previous viewer */
+@REPLY
+    user_handle_t  old_viewer;     /* previous clipboard viewer */
+    user_handle_t  owner;          /* clipboard owner */
+@END
+
+
 /* Open a security token */
 @REQ(open_token)
     obj_handle_t   handle;    /* handle to the thread or process */
diff --git a/server/request.h b/server/request.h
index 4f0055c26f4..b4f25a7513d 100644
--- a/server/request.h
+++ b/server/request.h
@@ -330,6 +330,7 @@ DECL_HANDLER(open_clipboard);
 DECL_HANDLER(close_clipboard);
 DECL_HANDLER(set_clipboard_info);
 DECL_HANDLER(empty_clipboard);
+DECL_HANDLER(set_clipboard_viewer);
 DECL_HANDLER(open_token);
 DECL_HANDLER(set_global_windows);
 DECL_HANDLER(adjust_token_privileges);
@@ -611,6 +612,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
     (req_handler)req_close_clipboard,
     (req_handler)req_set_clipboard_info,
     (req_handler)req_empty_clipboard,
+    (req_handler)req_set_clipboard_viewer,
     (req_handler)req_open_token,
     (req_handler)req_set_global_windows,
     (req_handler)req_adjust_token_privileges,
@@ -2025,7 +2027,6 @@ C_ASSERT( FIELD_OFFSET(struct close_clipboard_reply, owner) == 12 );
 C_ASSERT( sizeof(struct close_clipboard_reply) == 16 );
 C_ASSERT( FIELD_OFFSET(struct set_clipboard_info_request, flags) == 12 );
 C_ASSERT( FIELD_OFFSET(struct set_clipboard_info_request, owner) == 16 );
-C_ASSERT( FIELD_OFFSET(struct set_clipboard_info_request, viewer) == 20 );
 C_ASSERT( sizeof(struct set_clipboard_info_request) == 24 );
 C_ASSERT( FIELD_OFFSET(struct set_clipboard_info_reply, flags) == 8 );
 C_ASSERT( FIELD_OFFSET(struct set_clipboard_info_reply, old_clipboard) == 12 );
@@ -2034,6 +2035,12 @@ C_ASSERT( FIELD_OFFSET(struct set_clipboard_info_reply, old_viewer) == 20 );
 C_ASSERT( FIELD_OFFSET(struct set_clipboard_info_reply, seqno) == 24 );
 C_ASSERT( sizeof(struct set_clipboard_info_reply) == 32 );
 C_ASSERT( sizeof(struct empty_clipboard_request) == 16 );
+C_ASSERT( FIELD_OFFSET(struct set_clipboard_viewer_request, viewer) == 12 );
+C_ASSERT( FIELD_OFFSET(struct set_clipboard_viewer_request, previous) == 16 );
+C_ASSERT( sizeof(struct set_clipboard_viewer_request) == 24 );
+C_ASSERT( FIELD_OFFSET(struct set_clipboard_viewer_reply, old_viewer) == 8 );
+C_ASSERT( FIELD_OFFSET(struct set_clipboard_viewer_reply, owner) == 12 );
+C_ASSERT( sizeof(struct set_clipboard_viewer_reply) == 16 );
 C_ASSERT( FIELD_OFFSET(struct open_token_request, handle) == 12 );
 C_ASSERT( FIELD_OFFSET(struct open_token_request, access) == 16 );
 C_ASSERT( FIELD_OFFSET(struct open_token_request, attributes) == 20 );
diff --git a/server/trace.c b/server/trace.c
index dea9dce2f12..b3b6793aaae 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -3759,7 +3759,6 @@ static void dump_set_clipboard_info_request( const struct set_clipboard_info_req
 {
     fprintf( stderr, " flags=%08x", req->flags );
     fprintf( stderr, ", owner=%08x", req->owner );
-    fprintf( stderr, ", viewer=%08x", req->viewer );
 }
 
 static void dump_set_clipboard_info_reply( const struct set_clipboard_info_reply *req )
@@ -3775,6 +3774,18 @@ static void dump_empty_clipboard_request( const struct empty_clipboard_request *
 {
 }
 
+static void dump_set_clipboard_viewer_request( const struct set_clipboard_viewer_request *req )
+{
+    fprintf( stderr, " viewer=%08x", req->viewer );
+    fprintf( stderr, ", previous=%08x", req->previous );
+}
+
+static void dump_set_clipboard_viewer_reply( const struct set_clipboard_viewer_reply *req )
+{
+    fprintf( stderr, " old_viewer=%08x", req->old_viewer );
+    fprintf( stderr, ", owner=%08x", req->owner );
+}
+
 static void dump_open_token_request( const struct open_token_request *req )
 {
     fprintf( stderr, " handle=%04x", req->handle );
@@ -4602,6 +4613,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_close_clipboard_request,
     (dump_func)dump_set_clipboard_info_request,
     (dump_func)dump_empty_clipboard_request,
+    (dump_func)dump_set_clipboard_viewer_request,
     (dump_func)dump_open_token_request,
     (dump_func)dump_set_global_windows_request,
     (dump_func)dump_adjust_token_privileges_request,
@@ -4880,6 +4892,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_close_clipboard_reply,
     (dump_func)dump_set_clipboard_info_reply,
     NULL,
+    (dump_func)dump_set_clipboard_viewer_reply,
     (dump_func)dump_open_token_reply,
     (dump_func)dump_set_global_windows_reply,
     (dump_func)dump_adjust_token_privileges_reply,
@@ -5158,6 +5171,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
     "close_clipboard",
     "set_clipboard_info",
     "empty_clipboard",
+    "set_clipboard_viewer",
     "open_token",
     "set_global_windows",
     "adjust_token_privileges",
-- 
GitLab