Make the clipboard information local to the process window station.

diff --git a/server/clipboard.c b/server/clipboard.c
index 3f9db20..9b3cac0 100644
--- a/server/clipboard.c
+++ b/server/clipboard.c
@@ -29,145 +29,204 @@
 #include "request.h"
 #include "object.h"
 #include "user.h"
+#include "winuser.h"
 
-static struct thread *cbthread; /* thread id that has clipboard open */
-static user_handle_t clipboard; /* window that has clipboard open */
+struct clipboard
+{
+    struct object  obj;              /* object header */
+    struct thread *open_thread;      /* thread id that has clipboard open */
+    user_handle_t  open_win;         /* window that has clipboard open */
+    struct thread *owner_thread;     /* thread id that owns the clipboard */
+    user_handle_t  owner_win;        /* window that owns the clipboard data */
+    user_handle_t  viewer;           /* first window in clipboard viewer list */
+    unsigned int   seqno;            /* clipboard change sequence number */
+    time_t         seqno_timestamp;  /* time stamp of last seqno increment */
+};
 
-static struct thread *cbowner;  /* thread id that owns the clipboard */
-static user_handle_t owner;     /* window that owns the clipboard data */
+static void clipboard_dump( struct object *obj, int verbose );
 
-static user_handle_t viewer;    /* first window in clipboard viewer list */
-static unsigned int seqno;      /* clipboard change sequence number */
-static time_t seqnots;          /* time stamp of last seqno increment */
+static const struct object_ops clipboard_ops =
+{
+    sizeof(struct clipboard),     /* size */
+    clipboard_dump,               /* dump */
+    no_add_queue,                 /* add_queue */
+    NULL,                         /* remove_queue */
+    NULL,                         /* signaled */
+    NULL,                         /* satisfied */
+    no_signal,                    /* signal */
+    no_get_fd,                    /* get_fd */
+    no_close_handle,              /* close_handle */
+    no_destroy                    /* destroy */
+};
+
 
 #define MINUPDATELAPSE 2
 
+/* dump a clipboard object */
+static void clipboard_dump( struct object *obj, int verbose )
+{
+    struct clipboard *clipboard = (struct clipboard *)obj;
+
+    fprintf( stderr, "Clipboard open_thread=%p open_win=%p owner_thread=%p owner_win=%p viewer=%p seq=%u\n",
+             clipboard->open_thread, clipboard->open_win, clipboard->owner_thread,
+             clipboard->owner_win, clipboard->viewer, clipboard->seqno );
+}
+
+/* retrieve the clipboard info for the current process, allocating it if needed */
+static struct clipboard *get_process_clipboard(void)
+{
+    struct clipboard *clipboard;
+    struct winstation *winstation = get_process_winstation( current->process, WINSTA_ACCESSCLIPBOARD );
+
+    if (!winstation) return NULL;
+
+    if (!(clipboard = get_winstation_clipboard( winstation )))
+    {
+        if ((clipboard = alloc_object( &clipboard_ops )))
+        {
+            clipboard->open_thread = NULL;
+            clipboard->open_win = 0;
+            clipboard->owner_thread = NULL;
+            clipboard->owner_win = 0;
+            clipboard->viewer = 0;
+            clipboard->seqno = 0;
+            clipboard->seqno_timestamp = 0;
+            set_winstation_clipboard( winstation, clipboard );
+        }
+    }
+    release_object( winstation );
+    return clipboard;
+}
+
+
 /* Called when thread terminates to allow release of clipboard */
 void cleanup_clipboard_thread(struct thread *thread)
 {
-    if (thread == cbthread)
+    struct clipboard *clipboard;
+    struct winstation *winstation = get_process_winstation( thread->process, WINSTA_ACCESSCLIPBOARD );
+
+    if (!winstation) return;
+
+    if ((clipboard = get_winstation_clipboard( winstation )))
     {
-        clipboard = 0;
-        cbthread = NULL;
+        if (thread == clipboard->open_thread)
+        {
+            clipboard->open_win = 0;
+            clipboard->open_thread = NULL;
+        }
+        if (thread == clipboard->owner_thread)
+        {
+            clipboard->owner_win = 0;
+            clipboard->owner_thread = NULL;
+        }
     }
-    if (thread == cbowner)
-    {
-        owner = 0;
-        cbowner = NULL;
-    }
+    release_object( winstation );
 }
 
-static int set_clipboard_window(user_handle_t win, int clear)
+static int set_clipboard_window( struct clipboard *clipboard, user_handle_t win, int clear )
 {
-    if (cbthread && cbthread != current)
+    if (clipboard->open_thread && clipboard->open_thread != current)
     {
         set_error(STATUS_WAS_LOCKED);
         return 0;
     }
     else if (!clear)
     {
-        clipboard = win;
-        cbthread = current;
+        clipboard->open_win = win;
+        clipboard->open_thread = current;
     }
     else
     {
-        cbthread = NULL;
-        clipboard = 0;
+        clipboard->open_thread = NULL;
+        clipboard->open_win = 0;
     }
     return 1;
 }
 
 
-static int set_clipboard_owner(user_handle_t win, int clear)
+static int set_clipboard_owner( struct clipboard *clipboard, user_handle_t win, int clear )
 {
-    if (cbthread && cbthread->process != current->process)
+    if (clipboard->open_thread && clipboard->open_thread->process != current->process)
     {
         set_error(STATUS_WAS_LOCKED);
         return 0;
     }
     else if (!clear)
     {
-        owner = win;
-        cbowner = current;
+        clipboard->owner_win = win;
+        clipboard->owner_thread = current;
     }
     else
     {
-        owner = 0;
-        cbowner = NULL;
+        clipboard->owner_win = 0;
+        clipboard->owner_thread = NULL;
     }
     return 1;
 }
 
 
-static int get_seqno(void)
+static int get_seqno( struct clipboard *clipboard )
 {
     time_t tm = time(NULL);
 
-    if (!cbowner && (tm > (seqnots + MINUPDATELAPSE)))
+    if (!clipboard->owner_thread && (tm > (clipboard->seqno_timestamp + MINUPDATELAPSE)))
     {
-        seqnots = tm;
-        seqno++;
+        clipboard->seqno_timestamp = tm;
+        clipboard->seqno++;
     }
-    return seqno;
+    return clipboard->seqno;
 }
 
 
 DECL_HANDLER(set_clipboard_info)
 {
-    reply->old_clipboard = clipboard;
-    reply->old_owner = owner;
-    reply->old_viewer = viewer;
+    struct clipboard *clipboard = get_process_clipboard();
+
+    if (!clipboard) return;
+
+    reply->old_clipboard = clipboard->open_win;
+    reply->old_owner     = clipboard->owner_win;
+    reply->old_viewer    = clipboard->viewer;
 
     if (req->flags & SET_CB_OPEN)
     {
-        if (cbthread)
+        if (clipboard->open_thread)
         {
             /* clipboard already opened */
             set_error(STATUS_WAS_LOCKED);
             return;
         }
 
-        if (!set_clipboard_window(req->clipboard, 0))
-            return;
+        if (!set_clipboard_window( clipboard, req->clipboard, 0 )) return;
     }
     else if (req->flags & SET_CB_CLOSE)
     {
-        if (cbthread != current)
+        if (clipboard->open_thread != current)
         {
             set_win32_error(ERROR_CLIPBOARD_NOT_OPEN);
             return;
         }
 
-        if (!set_clipboard_window(0, 1))
-            return;
+        if (!set_clipboard_window( clipboard, 0, 1 )) return;
     }
 
     if (req->flags & SET_CB_OWNER)
     {
-        if (!set_clipboard_owner(req->owner, 0))
-            return;
+        if (!set_clipboard_owner( clipboard, req->owner, 0 )) return;
     }
     else if (req->flags & SET_CB_RELOWNER)
     {
-        if (!set_clipboard_owner(0, 1))
-            return;
+        if (!set_clipboard_owner( clipboard, 0, 1 )) return;
     }
 
-    if (req->flags & SET_CB_VIEWER)
-        viewer = req->viewer;
+    if (req->flags & SET_CB_VIEWER) clipboard->viewer = req->viewer;
 
-    if (req->flags & SET_CB_SEQNO)
-        seqno++;
+    if (req->flags & SET_CB_SEQNO) clipboard->seqno++;
 
-    reply->seqno = get_seqno();
+    reply->seqno = get_seqno( clipboard );
 
-    if (cbthread == current)
-        reply->flags |= CB_OPEN;
-
-    if (cbowner == current)
-        reply->flags |= CB_OWNER;
-
-    if (cbowner &&
-        cbowner->process == current->process)
+    if (clipboard->open_thread == current) reply->flags |= CB_OPEN;
+    if (clipboard->owner_thread == current) reply->flags |= CB_OWNER;
+    if (clipboard->owner_thread && clipboard->owner_thread->process == current->process)
         reply->flags |= CB_PROCESS;
 }
diff --git a/server/user.h b/server/user.h
index ec83199..2c84f06 100644
--- a/server/user.h
+++ b/server/user.h
@@ -29,6 +29,7 @@
 struct msg_queue;
 struct hook_table;
 struct window_class;
+struct clipboard;
 
 enum user_object
 {
@@ -120,6 +121,9 @@
 
 /* windows station functions */
 
+extern struct winstation *get_process_winstation( struct process *process, unsigned int access );
+extern void set_winstation_clipboard( struct winstation *winstation, struct clipboard *clipboard );
+extern struct clipboard *get_winstation_clipboard( struct winstation *winstation );
 extern void connect_process_winstation( struct process *process, const WCHAR *name, size_t len );
 extern void connect_process_desktop( struct process *process, const WCHAR *name, size_t len );
 extern void close_thread_desktop( struct thread *thread );
diff --git a/server/winstation.c b/server/winstation.c
index ed61df9..54d95d0 100644
--- a/server/winstation.c
+++ b/server/winstation.c
@@ -41,6 +41,7 @@
     unsigned int       flags;              /* winstation flags */
     struct list        entry;              /* entry in global winstation list */
     struct list        desktops;           /* list of desktops of this winstation */
+    struct clipboard  *clipboard;          /* clipboard information */
 };
 
 struct desktop
@@ -113,6 +114,7 @@
         {
             /* initialize it if it didn't already exist */
             winstation->flags = flags;
+            winstation->clipboard = NULL;
             list_add_tail( &winstation_list, &winstation->entry );
             list_init( &winstation->desktops );
         }
@@ -140,16 +142,28 @@
 
     if (winstation == interactive_winstation) interactive_winstation = NULL;
     list_remove( &winstation->entry );
+    if (winstation->clipboard) release_object( winstation->clipboard );
 }
 
 /* retrieve the process window station, checking the handle access rights */
-inline static struct winstation *get_process_winstation( struct process *process,
-                                                         unsigned int access )
+struct winstation *get_process_winstation( struct process *process, unsigned int access )
 {
     return (struct winstation *)get_handle_obj( process, process->winstation,
                                                 access, &winstation_ops );
 }
 
+/* set the pointer to the (opaque) clipboard info */
+void set_winstation_clipboard( struct winstation *winstation, struct clipboard *clipboard )
+{
+    winstation->clipboard = clipboard;
+}
+
+/* retrieve the pointer to the (opaque) clipboard info */
+struct clipboard *get_winstation_clipboard( struct winstation *winstation )
+{
+    return winstation->clipboard;
+}
+
 /* build the full name of a desktop object */
 static WCHAR *build_desktop_name( const WCHAR *name, size_t len,
                                   struct winstation *winstation, size_t *res_len )