- use global atoms for the format ids
- add timeout when calling XCheckTypedWindowEvent
- fix broken IsClipboardFormatAvailable; it tried to do a trick with
  EnumClipboardFormats by making incorrect assumptions
- in X11DRV_IsClipboardFormatAvailable do a quick exit if no one owns
  the selection
- add 1 second *minimum* time lapse between XSelectionOwner calls
- sync clipboard ownership between different wine processes
- prevents apps from getting into wierd state where they thought they
  didn't own the selection but they did and as a result queried
  themselves for available selection data

diff --git a/server/Makefile.in b/server/Makefile.in
index 1d6211c..828b37f 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -9,6 +9,7 @@
 	async.c \
 	atom.c \
 	change.c \
+	clipboard.c \
 	console.c \
 	context_i386.c \
 	context_powerpc.c \
diff --git a/server/clipboard.c b/server/clipboard.c
new file mode 100644
index 0000000..5a885a0
--- /dev/null
+++ b/server/clipboard.c
@@ -0,0 +1,160 @@
+/*
+ * Server-side clipboard management
+ *
+ * Copyright (C) 2002 Ulrich Czekalla
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+#include "wine/port.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "request.h"
+#include "object.h"
+#include "user.h"
+
+struct thread *cbthread;        /* thread id that has clipboard open */
+static user_handle_t clipboard; /* window that has clipboard open */
+
+struct thread *cbowner;         /* thread id that owns the clipboard */
+static user_handle_t owner;     /* window that owns the clipboard data */
+
+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 */
+
+#define MINUPDATELAPSE 2
+
+/* Called when thread terminates to allow release of clipboard */
+void cleanup_clipboard_thread(struct thread *thread)
+{
+    if (thread == cbthread)
+    {
+        clipboard = 0;
+        cbthread = NULL;
+    }
+    if (thread == cbowner)
+    {
+        owner = 0;
+        cbowner = NULL;
+    }
+}
+
+static int set_clipboard_window(user_handle_t win, int clear)
+{
+    if (cbthread && cbthread != current)
+    {
+        set_error(STATUS_WAS_LOCKED);
+        return 0;
+    }
+    else if (!clear)
+    {
+        clipboard = win;
+        cbthread = current;
+    }
+    else
+    {
+        cbthread = NULL;
+        clipboard = 0;
+    }
+    return 1;
+}
+
+
+static int set_clipboard_owner(user_handle_t win, int clear)
+{
+    if (cbthread == current)
+    {
+        if (!clear)
+        {
+            cbowner = current;
+            owner = win;
+        }
+        else
+        {
+            cbowner = 0;
+            owner = 0;
+        }
+        seqno++;
+        return 1;
+    }
+    else
+    {
+        set_error(STATUS_WAS_LOCKED);
+        return 0;
+    }
+}
+
+
+static int get_seqno(void)
+{
+    time_t tm = time(NULL);
+
+    if (!cbowner && (tm > (seqnots + MINUPDATELAPSE)))
+    {
+        seqnots = tm;
+        seqno++;
+    }
+    return seqno;
+}
+
+
+DECL_HANDLER(set_clipboard_info)
+{
+    reply->old_clipboard = clipboard;
+    reply->old_owner = owner;
+    reply->old_viewer = viewer;
+
+    if (req->flags & SET_CB_OPEN)
+    {
+        if (!set_clipboard_window(req->clipboard, 0))
+            return;
+    }
+    else if (req->flags & SET_CB_CLOSE)
+    {
+        if (!set_clipboard_window(0, 1))
+            return;
+    }
+
+    if (req->flags & SET_CB_OWNER)
+    {
+        if (!set_clipboard_owner(req->owner, 0))
+            return;
+    }
+    else if (req->flags & SET_CB_RELOWNER)
+    {
+        if (!set_clipboard_owner(0, 1))
+            return;
+    }
+
+    if (req->flags & SET_CB_VIEWER)
+        viewer = req->viewer;
+
+    if (req->flags & SET_CB_SEQNO)
+        seqno++;
+
+    reply->seqno = get_seqno();
+
+    if (cbthread == current)
+        reply->flags |= CB_OPEN;
+
+    if (cbowner == current)
+        reply->flags |= CB_OWNER;
+}
diff --git a/server/protocol.def b/server/protocol.def
index fb820a8..bb3e37a 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -2124,3 +2124,28 @@
     int            next_unicode;   /* is the next a unicode hook? */
     VARARG(module,unicode_str);    /* module name */
 @END
+
+
+/* Set/get clipboard information */
+@REQ(set_clipboard_info)
+    unsigned int   flags;       /* flags for fields to set (see below) */
+    user_handle_t  clipboard;   /* clipboard window */
+    user_handle_t  owner;       /* clipboard owner */
+    user_handle_t  viewer;      /* first clipboard viewer */
+    unsigned int   seqno;       /* change sequence number */
+@REPLY
+    unsigned int   flags;           /* status flags (see below) */
+    user_handle_t  old_clipboard;   /* old clipboard window */
+    user_handle_t  old_owner;       /* old clipboard owner */
+    user_handle_t  old_viewer;      /* old clipboard viewer */
+    unsigned int   seqno;           /* current sequence number */
+@END
+
+#define SET_CB_OPEN      0x001
+#define SET_CB_OWNER     0x002
+#define SET_CB_VIEWER    0x004
+#define SET_CB_SEQNO     0x008
+#define SET_CB_RELOWNER  0x010
+#define SET_CB_CLOSE     0x020
+#define CB_OPEN          0x040
+#define CB_OWNER         0x080
diff --git a/server/request.h b/server/request.h
index 60d9439..e28594e 100644
--- a/server/request.h
+++ b/server/request.h
@@ -278,6 +278,7 @@
 DECL_HANDLER(start_hook_chain);
 DECL_HANDLER(finish_hook_chain);
 DECL_HANDLER(get_next_hook);
+DECL_HANDLER(set_clipboard_info);
 
 #ifdef WANT_REQUEST_HANDLERS
 
@@ -459,6 +460,7 @@
     (req_handler)req_start_hook_chain,
     (req_handler)req_finish_hook_chain,
     (req_handler)req_get_next_hook,
+    (req_handler)req_set_clipboard_info,
 };
 #endif  /* WANT_REQUEST_HANDLERS */
 
diff --git a/server/thread.c b/server/thread.c
index c6ef4ac..a600bf1 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -210,6 +210,7 @@
     if (thread->wait_fd) release_object( thread->wait_fd );
     if (thread->hooks) release_object( thread->hooks );
     free_msg_queue( thread );
+    cleanup_clipboard_thread(thread);
     destroy_thread_windows( thread );
     for (i = 0; i < MAX_INFLIGHT_FDS; i++)
     {
diff --git a/server/trace.c b/server/trace.c
index 1e93cb1..2067695 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -2458,6 +2458,24 @@
     dump_varargs_unicode_str( cur_size );
 }
 
+static void dump_set_clipboard_info_request( const struct set_clipboard_info_request *req )
+{
+    fprintf( stderr, " flags=%08x,", req->flags );
+    fprintf( stderr, " clipboard=%p,", req->clipboard );
+    fprintf( stderr, " owner=%p,", req->owner );
+    fprintf( stderr, " viewer=%p,", req->viewer );
+    fprintf( stderr, " seqno=%08x", req->seqno );
+}
+
+static void dump_set_clipboard_info_reply( const struct set_clipboard_info_reply *req )
+{
+    fprintf( stderr, " flags=%08x,", req->flags );
+    fprintf( stderr, " old_clipboard=%p,", req->old_clipboard );
+    fprintf( stderr, " old_owner=%p,", req->old_owner );
+    fprintf( stderr, " old_viewer=%p,", req->old_viewer );
+    fprintf( stderr, " seqno=%08x", req->seqno );
+}
+
 static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_new_process_request,
     (dump_func)dump_get_new_process_info_request,
@@ -2634,6 +2652,7 @@
     (dump_func)dump_start_hook_chain_request,
     (dump_func)dump_finish_hook_chain_request,
     (dump_func)dump_get_next_hook_request,
+    (dump_func)dump_set_clipboard_info_request,
 };
 
 static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
@@ -2812,6 +2831,7 @@
     (dump_func)dump_start_hook_chain_reply,
     (dump_func)0,
     (dump_func)dump_get_next_hook_reply,
+    (dump_func)dump_set_clipboard_info_reply,
 };
 
 static const char * const req_names[REQ_NB_REQUESTS] = {
@@ -2990,6 +3010,7 @@
     "start_hook_chain",
     "finish_hook_chain",
     "get_next_hook",
+    "set_clipboard_info",
 };
 
 /* ### make_requests end ### */
diff --git a/server/user.h b/server/user.h
index 24c18d5..0d038b6 100644
--- a/server/user.h
+++ b/server/user.h
@@ -42,6 +42,10 @@
 extern void *free_user_handle( user_handle_t handle );
 extern void *next_user_handle( user_handle_t *handle, enum user_object type );
 
+/* clipboard functions */
+
+extern void cleanup_clipboard_thread( struct thread *thread );
+
 /* hook functions */
 
 extern void close_global_hooks(void);