Store the window caption text in the server.
Maintain a paint count separately for each window.

diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 41dba2e..b83ca78 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -1325,15 +1325,6 @@
 
 
 
-struct inc_queue_paint_count_request
-{
-    struct request_header __header;
-    void*           id;
-    int             incr;
-};
-
-
-
 struct set_queue_mask_request
 {
     struct request_header __header;
@@ -1721,6 +1712,33 @@
 
 
 
+struct get_window_text_request
+{
+    struct request_header __header;
+    user_handle_t  handle;
+    /* VARARG(text,unicode_str); */
+};
+
+
+
+struct set_window_text_request
+{
+    struct request_header __header;
+    user_handle_t  handle;
+    /* VARARG(text,unicode_str); */
+};
+
+
+
+struct inc_window_paint_count_request
+{
+    struct request_header __header;
+    user_handle_t  handle;
+    int             incr;
+};
+
+
+
 struct get_windows_offset_request
 {
     struct request_header __header;
@@ -1875,7 +1893,6 @@
     REQ_get_atom_name,
     REQ_init_atom_table,
     REQ_get_msg_queue,
-    REQ_inc_queue_paint_count,
     REQ_set_queue_mask,
     REQ_get_queue_status,
     REQ_wait_input_idle,
@@ -1906,6 +1923,9 @@
     REQ_get_window_tree,
     REQ_set_window_rectangles,
     REQ_get_window_rectangles,
+    REQ_get_window_text,
+    REQ_set_window_text,
+    REQ_inc_window_paint_count,
     REQ_get_windows_offset,
     REQ_set_window_property,
     REQ_remove_window_property,
@@ -2020,7 +2040,6 @@
     struct get_atom_name_request get_atom_name;
     struct init_atom_table_request init_atom_table;
     struct get_msg_queue_request get_msg_queue;
-    struct inc_queue_paint_count_request inc_queue_paint_count;
     struct set_queue_mask_request set_queue_mask;
     struct get_queue_status_request get_queue_status;
     struct wait_input_idle_request wait_input_idle;
@@ -2051,6 +2070,9 @@
     struct get_window_tree_request get_window_tree;
     struct set_window_rectangles_request set_window_rectangles;
     struct get_window_rectangles_request get_window_rectangles;
+    struct get_window_text_request get_window_text;
+    struct set_window_text_request set_window_text;
+    struct inc_window_paint_count_request inc_window_paint_count;
     struct get_windows_offset_request get_windows_offset;
     struct set_window_property_request set_window_property;
     struct remove_window_property_request remove_window_property;
@@ -2058,6 +2080,6 @@
     struct get_window_properties_request get_window_properties;
 };
 
-#define SERVER_PROTOCOL_VERSION 63
+#define SERVER_PROTOCOL_VERSION 64
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/protocol.def b/server/protocol.def
index 5e6c8a0..e44ef52 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1188,13 +1188,6 @@
 @END
 
 
-/* Increment the message queue paint count */
-@REQ(inc_queue_paint_count)
-    void*           id;        /* thread id */
-    int             incr;      /* increment (can be negative) */
-@END
-
-
 /* Set the current message queue wakeup mask */
 @REQ(set_queue_mask)
     unsigned int wake_mask;    /* wakeup bits mask */
@@ -1542,6 +1535,28 @@
 @END
 
 
+/* Get the window text */
+@REQ(get_window_text)
+    user_handle_t  handle;        /* handle to the window */
+@REPLY
+    VARARG(text,unicode_str);     /* window text */
+@END
+
+
+/* Set the window text */
+@REQ(set_window_text)
+    user_handle_t  handle;        /* handle to the window */
+    VARARG(text,unicode_str);     /* window text */
+@END
+
+
+/* Increment the window paint count */
+@REQ(inc_window_paint_count)
+    user_handle_t  handle;        /* handle to the window */
+    int             incr;         /* increment (can be negative) */
+@END
+
+
 /* Get the coordinates offset between two windows */
 @REQ(get_windows_offset)
     user_handle_t  from;          /* handle to the first window */
diff --git a/server/queue.c b/server/queue.c
index c1c0e78..76da322 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -175,7 +175,7 @@
 }
 
 /* get the current thread queue, creating it if needed */
-inline struct msg_queue *get_current_queue(void)
+inline static struct msg_queue *get_current_queue(void)
 {
     struct msg_queue *queue = current->queue;
     if (!queue) queue = create_msg_queue( current );
@@ -598,6 +598,23 @@
     return timer;
 }
 
+
+/* increment (or decrement if 'incr' is negative) the queue paint count */
+void inc_queue_paint_count( struct thread *thread, int incr )
+{
+    struct msg_queue *queue = thread->queue;
+
+    assert( queue );
+
+    if ((queue->paint_count += incr) < 0) queue->paint_count = 0;
+
+    if (queue->paint_count)
+        set_queue_bits( queue, QS_PAINT );
+    else
+        clear_queue_bits( queue, QS_PAINT );
+}
+
+
 /* remove all messages and timers belonging to a certain window */
 void queue_cleanup_window( struct thread *thread, user_handle_t win )
 {
@@ -644,30 +661,6 @@
 }
 
 
-/* increment the message queue paint count */
-DECL_HANDLER(inc_queue_paint_count)
-{
-    struct msg_queue *queue;
-    struct thread *thread = get_thread_from_id( req->id );
-
-    if (!thread) return;
-
-    if ((queue = thread->queue))
-    {
-        if ((queue->paint_count += req->incr) < 0) queue->paint_count = 0;
-
-        if (queue->paint_count)
-            set_queue_bits( queue, QS_PAINT );
-        else
-            clear_queue_bits( queue, QS_PAINT );
-    }
-    else set_error( STATUS_INVALID_PARAMETER );
-
-    release_object( thread );
-
-}
-
-
 /* set the current message queue wakeup mask */
 DECL_HANDLER(set_queue_mask)
 {
diff --git a/server/request.h b/server/request.h
index 2d1204f..87c224e 100644
--- a/server/request.h
+++ b/server/request.h
@@ -168,7 +168,6 @@
 DECL_HANDLER(get_atom_name);
 DECL_HANDLER(init_atom_table);
 DECL_HANDLER(get_msg_queue);
-DECL_HANDLER(inc_queue_paint_count);
 DECL_HANDLER(set_queue_mask);
 DECL_HANDLER(get_queue_status);
 DECL_HANDLER(wait_input_idle);
@@ -199,6 +198,9 @@
 DECL_HANDLER(get_window_tree);
 DECL_HANDLER(set_window_rectangles);
 DECL_HANDLER(get_window_rectangles);
+DECL_HANDLER(get_window_text);
+DECL_HANDLER(set_window_text);
+DECL_HANDLER(inc_window_paint_count);
 DECL_HANDLER(get_windows_offset);
 DECL_HANDLER(set_window_property);
 DECL_HANDLER(remove_window_property);
@@ -312,7 +314,6 @@
     (req_handler)req_get_atom_name,
     (req_handler)req_init_atom_table,
     (req_handler)req_get_msg_queue,
-    (req_handler)req_inc_queue_paint_count,
     (req_handler)req_set_queue_mask,
     (req_handler)req_get_queue_status,
     (req_handler)req_wait_input_idle,
@@ -343,6 +344,9 @@
     (req_handler)req_get_window_tree,
     (req_handler)req_set_window_rectangles,
     (req_handler)req_get_window_rectangles,
+    (req_handler)req_get_window_text,
+    (req_handler)req_set_window_text,
+    (req_handler)req_inc_window_paint_count,
     (req_handler)req_get_windows_offset,
     (req_handler)req_set_window_property,
     (req_handler)req_remove_window_property,
diff --git a/server/trace.c b/server/trace.c
index 012286b..5deb7ee 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1456,12 +1456,6 @@
     fprintf( stderr, " handle=%d", req->handle );
 }
 
-static void dump_inc_queue_paint_count_request( const struct inc_queue_paint_count_request *req )
-{
-    fprintf( stderr, " id=%p,", req->id );
-    fprintf( stderr, " incr=%d", req->incr );
-}
-
 static void dump_set_queue_mask_request( const struct set_queue_mask_request *req )
 {
     fprintf( stderr, " wake_mask=%08x,", req->wake_mask );
@@ -1832,6 +1826,30 @@
     dump_rectangle( req, &req->client );
 }
 
+static void dump_get_window_text_request( const struct get_window_text_request *req )
+{
+    fprintf( stderr, " handle=%08x", req->handle );
+}
+
+static void dump_get_window_text_reply( const struct get_window_text_request *req )
+{
+    fprintf( stderr, " text=" );
+    cur_pos += dump_varargs_unicode_str( req );
+}
+
+static void dump_set_window_text_request( const struct set_window_text_request *req )
+{
+    fprintf( stderr, " handle=%08x,", req->handle );
+    fprintf( stderr, " text=" );
+    cur_pos += dump_varargs_unicode_str( req );
+}
+
+static void dump_inc_window_paint_count_request( const struct inc_window_paint_count_request *req )
+{
+    fprintf( stderr, " handle=%08x,", req->handle );
+    fprintf( stderr, " incr=%d", req->incr );
+}
+
 static void dump_get_windows_offset_request( const struct get_windows_offset_request *req )
 {
     fprintf( stderr, " from=%08x,", req->from );
@@ -1988,7 +2006,6 @@
     (dump_func)dump_get_atom_name_request,
     (dump_func)dump_init_atom_table_request,
     (dump_func)dump_get_msg_queue_request,
-    (dump_func)dump_inc_queue_paint_count_request,
     (dump_func)dump_set_queue_mask_request,
     (dump_func)dump_get_queue_status_request,
     (dump_func)dump_wait_input_idle_request,
@@ -2019,6 +2036,9 @@
     (dump_func)dump_get_window_tree_request,
     (dump_func)dump_set_window_rectangles_request,
     (dump_func)dump_get_window_rectangles_request,
+    (dump_func)dump_get_window_text_request,
+    (dump_func)dump_set_window_text_request,
+    (dump_func)dump_inc_window_paint_count_request,
     (dump_func)dump_get_windows_offset_request,
     (dump_func)dump_set_window_property_request,
     (dump_func)dump_remove_window_property_request,
@@ -2129,7 +2149,6 @@
     (dump_func)dump_get_atom_name_reply,
     (dump_func)0,
     (dump_func)dump_get_msg_queue_reply,
-    (dump_func)0,
     (dump_func)dump_set_queue_mask_reply,
     (dump_func)dump_get_queue_status_reply,
     (dump_func)dump_wait_input_idle_reply,
@@ -2160,6 +2179,9 @@
     (dump_func)dump_get_window_tree_reply,
     (dump_func)0,
     (dump_func)dump_get_window_rectangles_reply,
+    (dump_func)dump_get_window_text_reply,
+    (dump_func)0,
+    (dump_func)0,
     (dump_func)dump_get_windows_offset_reply,
     (dump_func)0,
     (dump_func)dump_remove_window_property_reply,
@@ -2270,7 +2292,6 @@
     "get_atom_name",
     "init_atom_table",
     "get_msg_queue",
-    "inc_queue_paint_count",
     "set_queue_mask",
     "get_queue_status",
     "wait_input_idle",
@@ -2301,6 +2322,9 @@
     "get_window_tree",
     "set_window_rectangles",
     "get_window_rectangles",
+    "get_window_text",
+    "set_window_text",
+    "inc_window_paint_count",
     "get_windows_offset",
     "set_window_property",
     "remove_window_property",
diff --git a/server/unicode.h b/server/unicode.h
index dc8f99f..c6b600d 100644
--- a/server/unicode.h
+++ b/server/unicode.h
@@ -7,6 +7,8 @@
 #ifndef __WINE_SERVER_UNICODE_H
 #define __WINE_SERVER_UNICODE_H
 
+#include <stdio.h>
+
 #include "windef.h"
 #include "wine/unicode.h"
 #include "object.h"
diff --git a/server/user.h b/server/user.h
index d53cc53..5c6c4ce 100644
--- a/server/user.h
+++ b/server/user.h
@@ -11,6 +11,7 @@
 
 struct thread;
 struct window;
+struct msg_queue;
 
 enum user_object
 {
@@ -28,6 +29,7 @@
 
 /* queue functions */
 
+extern void inc_queue_paint_count( struct thread *thread, int incr );
 extern void queue_cleanup_window( struct thread *thread, user_handle_t win );
 
 /* window functions */
diff --git a/server/window.c b/server/window.c
index 701c9e8..430e7c5 100644
--- a/server/window.c
+++ b/server/window.c
@@ -11,6 +11,7 @@
 #include "thread.h"
 #include "process.h"
 #include "user.h"
+#include "unicode.h"
 
 /* a window property */
 struct property
@@ -47,6 +48,8 @@
     unsigned int     id;              /* window id */
     void*            instance;        /* creator instance */
     void*            user_data;       /* user-specific data */
+    WCHAR           *text;            /* window caption text */
+    int              paint_count;     /* count of pending paints for this window */
     int              prop_inuse;      /* number of in-use window properties */
     int              prop_alloc;      /* number of allocated window properties */
     struct property *properties;      /* window properties array */
@@ -244,10 +247,12 @@
             if (ptr->owner == win) ptr->owner = NULL;
     }
 
-    if (win->thread->queue) queue_cleanup_window( win->thread, win->handle );
+    if (win->paint_count) inc_queue_paint_count( win->thread, -win->paint_count );
+    queue_cleanup_window( win->thread, win->handle );
     free_user_handle( win->handle );
     destroy_properties( win );
     unlink_window( win );
+    if (win->text) free( win->text );
     memset( win, 0x55, sizeof(*win) );
     free( win );
 }
@@ -275,6 +280,8 @@
     win->id             = 0;
     win->instance       = NULL;
     win->user_data      = NULL;
+    win->text           = NULL;
+    win->paint_count    = 0;
     win->prop_inuse     = 0;
     win->prop_alloc     = 0;
     win->properties     = NULL;
@@ -563,6 +570,57 @@
 }
 
 
+/* get the window text */
+DECL_HANDLER(get_window_text)
+{
+    struct window *win = get_window( req->handle );
+    size_t len = 0;
+
+    if (win && win->text)
+    {
+        len = strlenW( win->text ) * sizeof(WCHAR);
+        if (len > get_req_data_size(req)) len = get_req_data_size(req);
+        memcpy( get_req_data(req), win->text, len );
+    }
+    set_req_data_size( req, len );
+}
+
+
+/* set the window text */
+DECL_HANDLER(set_window_text)
+{
+    struct window *win = get_window( req->handle );
+
+    if (win)
+    {
+        WCHAR *text = NULL;
+        size_t len = get_req_data_size(req) / sizeof(WCHAR);
+        if (len)
+        {
+            if (!(text = mem_alloc( (len+1) * sizeof(WCHAR) ))) return;
+            memcpy( text, get_req_data(req), len * sizeof(WCHAR) );
+            text[len] = 0;
+        }
+        if (win->text) free( win->text );
+        win->text = text;
+    }
+}
+
+
+/* increment the window paint count */
+DECL_HANDLER(inc_window_paint_count)
+{
+    struct window *win = get_window( req->handle );
+
+    if (win)
+    {
+        int old = win->paint_count;
+        if ((win->paint_count += req->incr) < 0) win->paint_count = 0;
+        inc_queue_paint_count( win->thread, win->paint_count - old );
+    }
+}
+
+
 /* get the coordinates offset between two windows */
 DECL_HANDLER(get_windows_offset)
 {
diff --git a/windows/defwnd.c b/windows/defwnd.c
index af998c9..b12c756 100644
--- a/windows/defwnd.c
+++ b/windows/defwnd.c
@@ -19,6 +19,7 @@
 #include "winnls.h"
 #include "wine/unicode.h"
 #include "wine/winuser16.h"
+#include "wine/server.h"
 #include "imm.h"
 
 DEFAULT_DEBUG_CHANNEL(win);
@@ -71,13 +72,23 @@
     if (!text) text = "";
     count = MultiByteToWideChar( CP_ACP, 0, text, -1, NULL, 0 );
 
-    if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return;
-    if (wndPtr->text) HeapFree(GetProcessHeap(), 0, wndPtr->text);
-    if ((wndPtr->text = textW = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR))))
-        MultiByteToWideChar( CP_ACP, 0, text, -1, wndPtr->text, count );
+    if (!(wndPtr = WIN_GetPtr( hwnd ))) return;
+    if ((textW = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR))))
+    {
+        if (wndPtr->text) HeapFree(GetProcessHeap(), 0, wndPtr->text);
+        wndPtr->text = textW;
+        MultiByteToWideChar( CP_ACP, 0, text, -1, textW, count );
+        SERVER_START_VAR_REQ( set_window_text, (count-1) * sizeof(WCHAR) )
+        {
+            req->handle = hwnd;
+            memcpy( server_data_ptr(req), textW, (count-1) * sizeof(WCHAR) );
+            SERVER_CALL();
+        }
+        SERVER_END_VAR_REQ;
+    }
     else
         ERR("Not enough memory for window text\n");
-    WIN_ReleaseWndPtr( wndPtr );
+    WIN_ReleasePtr( wndPtr );
 
     if (USER_Driver.pSetWindowText) USER_Driver.pSetWindowText( hwnd, textW );
 }
@@ -99,7 +110,16 @@
     if (!(wndPtr = WIN_GetPtr( hwnd ))) return;
     if (wndPtr->text) HeapFree(GetProcessHeap(), 0, wndPtr->text);
     if ((wndPtr->text = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR))))
-	strcpyW( wndPtr->text, text );
+    {
+        strcpyW( wndPtr->text, text );
+        SERVER_START_VAR_REQ( set_window_text, (count-1) * sizeof(WCHAR) )
+        {
+            req->handle = hwnd;
+            memcpy( server_data_ptr(req), wndPtr->text, (count-1) * sizeof(WCHAR) );
+            SERVER_CALL();
+        }
+        SERVER_END_VAR_REQ;
+    }
     else
         ERR("Not enough memory for window text\n");
     text = wndPtr->text;
diff --git a/windows/painting.c b/windows/painting.c
index 450d8af..fa579eb 100644
--- a/windows/painting.c
+++ b/windows/painting.c
@@ -50,10 +50,10 @@
  */
 static void add_paint_count( HWND hwnd, int incr )
 {
-    SERVER_START_REQ( inc_queue_paint_count )
+    SERVER_START_REQ( inc_window_paint_count )
     {
-        req->id   = (void *)GetWindowThreadProcessId( hwnd, NULL );
-        req->incr = incr;
+        req->handle = hwnd;
+        req->incr   = incr;
         SERVER_CALL();
     }
     SERVER_END_REQ;
diff --git a/windows/win.c b/windows/win.c
index 469416e..e92f0bb 100644
--- a/windows/win.c
+++ b/windows/win.c
@@ -187,6 +187,30 @@
 }
 
 
+/*******************************************************************
+ *		get_server_window_text
+ *
+ * Retrieve the window text from the server.
+ */
+static void get_server_window_text( HWND hwnd, LPWSTR text, INT count )
+{
+    size_t len = (count - 1) * sizeof(WCHAR);
+    len = min( len, REQUEST_MAX_VAR_SIZE );
+    SERVER_START_VAR_REQ( get_window_text, len )
+    {
+        req->handle = hwnd;
+        if (!SERVER_CALL_ERR())
+        {
+            len = server_data_size(req);
+            memcpy( text, server_data_ptr(req), len );
+        }
+        else len = 0;
+    }
+    SERVER_END_VAR_REQ;
+    text[len / sizeof(WCHAR)] = 0;
+}
+
+
 /***********************************************************************
  *           WIN_GetPtr
  *
@@ -2240,20 +2264,41 @@
  */
 INT WINAPI GetWindowTextA( HWND hwnd, LPSTR lpString, INT nMaxCount )
 {
-    return (INT)SendMessageA( hwnd, WM_GETTEXT, nMaxCount,
-                                  (LPARAM)lpString );
+    WCHAR *buffer;
+
+    if (WIN_IsCurrentProcess( hwnd ))
+        return (INT)SendMessageA( hwnd, WM_GETTEXT, nMaxCount, (LPARAM)lpString );
+
+    /* when window belongs to other process, don't send a message */
+    if (nMaxCount <= 0) return 0;
+    if (!(buffer = HeapAlloc( GetProcessHeap(), 0, nMaxCount * sizeof(WCHAR) ))) return 0;
+    get_server_window_text( hwnd, buffer, nMaxCount );
+    if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, lpString, nMaxCount, NULL, NULL ))
+        lpString[nMaxCount-1] = 0;
+    HeapFree( GetProcessHeap(), 0, buffer );
+    return strlen(lpString);
 }
 
+
 /*******************************************************************
  *		InternalGetWindowText (USER32.@)
  */
 INT WINAPI InternalGetWindowText(HWND hwnd,LPWSTR lpString,INT nMaxCount )
 {
-    WND *win = WIN_FindWndPtr( hwnd );
-    if (!win) return 0;
-    if (win->text) lstrcpynW( lpString, win->text, nMaxCount );
-    else lpString[0] = 0;
-    WIN_ReleaseWndPtr( win );
+    WND *win;
+
+    if (nMaxCount <= 0) return 0;
+    if (!(win = WIN_GetPtr( hwnd ))) return 0;
+    if (win != WND_OTHER_PROCESS)
+    {
+        if (win->text) lstrcpynW( lpString, win->text, nMaxCount );
+        else lpString[0] = 0;
+        WIN_ReleasePtr( win );
+    }
+    else
+    {
+        get_server_window_text( hwnd, lpString, nMaxCount );
+    }
     return strlenW(lpString);
 }
 
@@ -2263,8 +2308,13 @@
  */
 INT WINAPI GetWindowTextW( HWND hwnd, LPWSTR lpString, INT nMaxCount )
 {
-    return (INT)SendMessageW( hwnd, WM_GETTEXT, nMaxCount,
-                                  (LPARAM)lpString );
+    if (WIN_IsCurrentProcess( hwnd ))
+        return (INT)SendMessageW( hwnd, WM_GETTEXT, nMaxCount, (LPARAM)lpString );
+
+    /* when window belongs to other process, don't send a message */
+    if (nMaxCount <= 0) return 0;
+    get_server_window_text( hwnd, lpString, nMaxCount );
+    return strlenW(lpString);
 }
 
 
@@ -2274,6 +2324,12 @@
  */
 BOOL WINAPI SetWindowTextA( HWND hwnd, LPCSTR lpString )
 {
+    if (!WIN_IsCurrentProcess( hwnd ))
+    {
+        FIXME( "cannot set text %s of other process window %x\n", debugstr_a(lpString), hwnd );
+        SetLastError( ERROR_ACCESS_DENIED );
+        return FALSE;
+    }
     return (BOOL)SendMessageA( hwnd, WM_SETTEXT, 0, (LPARAM)lpString );
 }
 
@@ -2283,6 +2339,12 @@
  */
 BOOL WINAPI SetWindowTextW( HWND hwnd, LPCWSTR lpString )
 {
+    if (!WIN_IsCurrentProcess( hwnd ))
+    {
+        FIXME( "cannot set text %s of other process window %x\n", debugstr_w(lpString), hwnd );
+        SetLastError( ERROR_ACCESS_DENIED );
+        return FALSE;
+    }
     return (BOOL)SendMessageW( hwnd, WM_SETTEXT, 0, (LPARAM)lpString );
 }