Added global management of user handles in the server.
Very preliminary support for shared window handles.
diff --git a/server/Makefile.in b/server/Makefile.in
index b03708b..1bcfaaa 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -36,7 +36,9 @@
thread.c \
timer.c \
trace.c \
- unicode.c
+ unicode.c \
+ user.c \
+ window.c
PROGRAMS = wineserver
diff --git a/server/protocol.def b/server/protocol.def
index d6c6cd3..c386dd9 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -40,6 +40,7 @@
#define REQUEST_MAX_VAR_SIZE 1024
typedef int handle_t;
+typedef unsigned int user_handle_t;
/* definitions of the event data depending on the event code */
struct debug_event_exception
@@ -1197,7 +1198,7 @@
@REQ(send_message)
void* id; /* thread id */
int type; /* message type (see below) */
- handle_t win; /* window handle */
+ user_handle_t win; /* window handle */
unsigned int msg; /* message code */
unsigned int wparam; /* parameters */
unsigned int lparam; /* parameters */
@@ -1225,12 +1226,12 @@
/* Get a message from the current queue */
@REQ(get_message)
int flags; /* see below */
- handle_t get_win; /* window handle to get */
+ user_handle_t get_win; /* window handle to get */
unsigned int get_first; /* first message code to get */
unsigned int get_last; /* last message code to get */
@REPLY
int type; /* message type */
- handle_t win; /* window handle */
+ user_handle_t win; /* window handle */
unsigned int msg; /* message code */
unsigned int wparam; /* parameters */
unsigned int lparam; /* parameters */
@@ -1263,13 +1264,13 @@
/* Cleanup a queue when a window is deleted */
@REQ(cleanup_window_queue)
- handle_t win; /* window handle */
+ user_handle_t win; /* window handle */
@END
/* Set a window timer */
@REQ(set_win_timer)
- handle_t win; /* window handle */
+ user_handle_t win; /* window handle */
unsigned int msg; /* message to post */
unsigned int id; /* timer id */
unsigned int rate; /* timer rate in ms */
@@ -1279,7 +1280,7 @@
/* Kill a window timer */
@REQ(kill_win_timer)
- handle_t win; /* window handle */
+ user_handle_t win; /* window handle */
unsigned int msg; /* message to post */
unsigned int id; /* timer id */
@END
@@ -1383,6 +1384,7 @@
handle_t handle;
@END
+
@REQ(get_named_pipe_info)
handle_t handle;
@REPLY
@@ -1392,3 +1394,41 @@
unsigned int insize;
@END
+
+/* Create the desktop window */
+@REQ(create_desktop_window)
+@REPLY
+ user_handle_t handle; /* handle to the window */
+@END
+
+
+/* Create a window */
+@REQ(create_window)
+@REPLY
+ user_handle_t handle; /* handle to the window */
+@END
+
+
+/* Link a window into the tree */
+@REQ(link_window)
+ user_handle_t handle; /* handle to the window */
+ user_handle_t parent; /* handle to the parent */
+ user_handle_t previous; /* previous child in Z-order */
+@REPLY
+@END
+
+
+/* Destroy a window */
+@REQ(destroy_window)
+ user_handle_t handle; /* handle to the window */
+@END
+
+
+/* Get information from a window handle */
+@REQ(get_window_info)
+ user_handle_t handle; /* handle to the window */
+@REPLY
+ user_handle_t full_handle; /* full 32-bit handle */
+ void* pid; /* process owning the window */
+ void* tid; /* thread owning the window */
+@END
diff --git a/server/queue.c b/server/queue.c
index 238e3a9..e575325 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -16,6 +16,7 @@
#include "thread.h"
#include "process.h"
#include "request.h"
+#include "user.h"
enum message_kind { SEND_MESSAGE, POST_MESSAGE, COOKED_HW_MESSAGE, RAW_HW_MESSAGE };
#define NB_MSG_KINDS (RAW_HW_MESSAGE+1)
@@ -40,7 +41,7 @@
struct message *next; /* next message in list */
struct message *prev; /* prev message in list */
enum message_type type; /* message type */
- handle_t win; /* window handle */
+ user_handle_t win; /* window handle */
unsigned int msg; /* message code */
unsigned int wparam; /* parameters */
unsigned int lparam; /* parameters */
@@ -65,7 +66,7 @@
struct timer *prev; /* prev timer in list */
struct timeval when; /* next expiration */
unsigned int rate; /* timer rate in ms */
- handle_t win; /* window handle */
+ user_handle_t win; /* window handle */
unsigned int msg; /* message to post */
unsigned int id; /* timer id */
unsigned int lparam; /* lparam for message */
@@ -550,7 +551,7 @@
}
/* find an expired timer matching the filtering parameters */
-static struct timer *find_expired_timer( struct msg_queue *queue, handle_t win,
+static struct timer *find_expired_timer( struct msg_queue *queue, user_handle_t win,
unsigned int get_first, unsigned int get_last,
int remove )
{
@@ -568,7 +569,8 @@
}
/* kill a timer */
-static int kill_timer( struct msg_queue *queue, handle_t win, unsigned int msg, unsigned int id )
+static int kill_timer( struct msg_queue *queue, user_handle_t win,
+ unsigned int msg, unsigned int id )
{
struct timer *timer;
@@ -597,12 +599,15 @@
}
/* remove all messages and timers belonging to a certain window */
-static void cleanup_window( struct msg_queue *queue, handle_t win )
+void queue_cleanup_window( struct thread *thread, user_handle_t win )
{
+ struct msg_queue *queue = thread->queue;
struct timer *timer;
struct message *msg;
int i;
+ if (!queue) return;
+
/* remove timers */
timer = queue->first_timer;
while (timer)
@@ -818,7 +823,8 @@
}
-inline static struct message *find_matching_message( const struct message_list *list, handle_t win,
+inline static struct message *find_matching_message( const struct message_list *list,
+ user_handle_t win,
unsigned int first, unsigned int last )
{
struct message *msg;
@@ -981,7 +987,7 @@
/* cleanup a queue when a window is deleted */
DECL_HANDLER(cleanup_window_queue)
{
- if (current->queue) cleanup_window( current->queue, req->win );
+ queue_cleanup_window( current, req->win );
}
diff --git a/server/request.h b/server/request.h
index 3187fe5..97bc21e 100644
--- a/server/request.h
+++ b/server/request.h
@@ -188,6 +188,11 @@
DECL_HANDLER(wait_named_pipe);
DECL_HANDLER(disconnect_named_pipe);
DECL_HANDLER(get_named_pipe_info);
+DECL_HANDLER(create_desktop_window);
+DECL_HANDLER(create_window);
+DECL_HANDLER(link_window);
+DECL_HANDLER(destroy_window);
+DECL_HANDLER(get_window_info);
#ifdef WANT_REQUEST_HANDLERS
@@ -317,6 +322,11 @@
(req_handler)req_wait_named_pipe,
(req_handler)req_disconnect_named_pipe,
(req_handler)req_get_named_pipe_info,
+ (req_handler)req_create_desktop_window,
+ (req_handler)req_create_window,
+ (req_handler)req_link_window,
+ (req_handler)req_destroy_window,
+ (req_handler)req_get_window_info,
};
#endif /* WANT_REQUEST_HANDLERS */
diff --git a/server/thread.c b/server/thread.c
index 918c842..0679d9d 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -27,6 +27,7 @@
#include "process.h"
#include "thread.h"
#include "request.h"
+#include "user.h"
/* thread queues */
@@ -179,6 +180,7 @@
release_object( thread->queue );
thread->queue = NULL;
}
+ destroy_thread_windows( thread );
for (i = 0; i < MAX_INFLIGHT_FDS; i++)
{
if (thread->inflight[i].client != -1)
diff --git a/server/trace.c b/server/trace.c
index f44d0ad..5dc05bc 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1459,7 +1459,7 @@
{
fprintf( stderr, " id=%p,", req->id );
fprintf( stderr, " type=%d,", req->type );
- fprintf( stderr, " win=%d,", req->win );
+ fprintf( stderr, " win=%08x,", req->win );
fprintf( stderr, " msg=%08x,", req->msg );
fprintf( stderr, " wparam=%08x,", req->wparam );
fprintf( stderr, " lparam=%08x,", req->lparam );
@@ -1475,7 +1475,7 @@
static void dump_get_message_request( const struct get_message_request *req )
{
fprintf( stderr, " flags=%d,", req->flags );
- fprintf( stderr, " get_win=%d,", req->get_win );
+ fprintf( stderr, " get_win=%08x,", req->get_win );
fprintf( stderr, " get_first=%08x,", req->get_first );
fprintf( stderr, " get_last=%08x", req->get_last );
}
@@ -1483,7 +1483,7 @@
static void dump_get_message_reply( const struct get_message_request *req )
{
fprintf( stderr, " type=%d,", req->type );
- fprintf( stderr, " win=%d,", req->win );
+ fprintf( stderr, " win=%08x,", req->win );
fprintf( stderr, " msg=%08x,", req->msg );
fprintf( stderr, " wparam=%08x,", req->wparam );
fprintf( stderr, " lparam=%08x,", req->lparam );
@@ -1517,12 +1517,12 @@
static void dump_cleanup_window_queue_request( const struct cleanup_window_queue_request *req )
{
- fprintf( stderr, " win=%d", req->win );
+ fprintf( stderr, " win=%08x", req->win );
}
static void dump_set_win_timer_request( const struct set_win_timer_request *req )
{
- fprintf( stderr, " win=%d,", req->win );
+ fprintf( stderr, " win=%08x,", req->win );
fprintf( stderr, " msg=%08x,", req->msg );
fprintf( stderr, " id=%08x,", req->id );
fprintf( stderr, " rate=%08x,", req->rate );
@@ -1531,7 +1531,7 @@
static void dump_kill_win_timer_request( const struct kill_win_timer_request *req )
{
- fprintf( stderr, " win=%d,", req->win );
+ fprintf( stderr, " win=%08x,", req->win );
fprintf( stderr, " msg=%08x,", req->msg );
fprintf( stderr, " id=%08x", req->id );
}
@@ -1652,6 +1652,48 @@
fprintf( stderr, " insize=%08x", req->insize );
}
+static void dump_create_desktop_window_request( const struct create_desktop_window_request *req )
+{
+}
+
+static void dump_create_desktop_window_reply( const struct create_desktop_window_request *req )
+{
+ fprintf( stderr, " handle=%08x", req->handle );
+}
+
+static void dump_create_window_request( const struct create_window_request *req )
+{
+}
+
+static void dump_create_window_reply( const struct create_window_request *req )
+{
+ fprintf( stderr, " handle=%08x", req->handle );
+}
+
+static void dump_link_window_request( const struct link_window_request *req )
+{
+ fprintf( stderr, " handle=%08x,", req->handle );
+ fprintf( stderr, " parent=%08x,", req->parent );
+ fprintf( stderr, " previous=%08x", req->previous );
+}
+
+static void dump_destroy_window_request( const struct destroy_window_request *req )
+{
+ fprintf( stderr, " handle=%08x", req->handle );
+}
+
+static void dump_get_window_info_request( const struct get_window_info_request *req )
+{
+ fprintf( stderr, " handle=%08x", req->handle );
+}
+
+static void dump_get_window_info_reply( const struct get_window_info_request *req )
+{
+ fprintf( stderr, " full_handle=%08x,", req->full_handle );
+ fprintf( stderr, " pid=%p,", req->pid );
+ fprintf( stderr, " tid=%p", req->tid );
+}
+
static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_new_process_request,
(dump_func)dump_get_new_process_info_request,
@@ -1776,6 +1818,11 @@
(dump_func)dump_wait_named_pipe_request,
(dump_func)dump_disconnect_named_pipe_request,
(dump_func)dump_get_named_pipe_info_request,
+ (dump_func)dump_create_desktop_window_request,
+ (dump_func)dump_create_window_request,
+ (dump_func)dump_link_window_request,
+ (dump_func)dump_destroy_window_request,
+ (dump_func)dump_get_window_info_request,
};
static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
@@ -1902,6 +1949,11 @@
(dump_func)0,
(dump_func)0,
(dump_func)dump_get_named_pipe_info_reply,
+ (dump_func)dump_create_desktop_window_reply,
+ (dump_func)dump_create_window_reply,
+ (dump_func)0,
+ (dump_func)0,
+ (dump_func)dump_get_window_info_reply,
};
static const char * const req_names[REQ_NB_REQUESTS] = {
@@ -2028,6 +2080,11 @@
"wait_named_pipe",
"disconnect_named_pipe",
"get_named_pipe_info",
+ "create_desktop_window",
+ "create_window",
+ "link_window",
+ "destroy_window",
+ "get_window_info",
};
/* ### make_requests end ### */
diff --git a/server/user.c b/server/user.c
new file mode 100644
index 0000000..ec0584c
--- /dev/null
+++ b/server/user.c
@@ -0,0 +1,149 @@
+/*
+ * Server-side USER handles
+ *
+ * Copyright (C) 2001 Alexandre Julliard
+ */
+
+#include "thread.h"
+#include "user.h"
+
+struct user_handle
+{
+ void *ptr; /* pointer to object */
+ unsigned short type; /* object type (0 if free) */
+ unsigned short generation; /* generation counter */
+};
+
+static struct user_handle *handles;
+static struct user_handle *freelist;
+static int nb_handles;
+static int allocated_handles;
+
+#define FIRST_HANDLE 32 /* handle value for first table entry */
+#define MAX_HANDLES (65536-FIRST_HANDLE)
+
+static struct user_handle *handle_to_entry( user_handle_t handle )
+{
+ int index = (handle & 0xffff) - FIRST_HANDLE;
+ if (index < 0 || index >= nb_handles) return NULL;
+ if (!handles[index].type) return NULL;
+ if ((handle >> 16) && (handle >> 16 != handles[index].generation)) return NULL;
+ return &handles[index];
+}
+
+inline static user_handle_t entry_to_handle( struct user_handle *ptr )
+{
+ int index = ptr - handles;
+ return (index + FIRST_HANDLE) + (ptr->generation << 16);
+}
+
+inline static struct user_handle *alloc_user_entry(void)
+{
+ struct user_handle *handle;
+
+ if (freelist)
+ {
+ handle = freelist;
+ freelist = handle->ptr;
+ return handle;
+ }
+ if (nb_handles >= allocated_handles) /* need to grow the array */
+ {
+ struct user_handle *new_handles;
+ /* grow array by 50% (but at minimum 32 entries) */
+ int growth = max( 32, allocated_handles / 2 );
+ int new_size = min( allocated_handles + growth, MAX_HANDLES );
+ if (new_size <= allocated_handles) return NULL;
+ if (!(new_handles = realloc( handles, new_size * sizeof(*handles) )))
+ return NULL;
+ handles = new_handles;
+ allocated_handles = new_size;
+ }
+ handle = &handles[nb_handles++];
+ handle->generation = 0;
+ return handle;
+}
+
+inline static void *free_user_entry( struct user_handle *ptr )
+{
+ void *ret;
+ ret = ptr->ptr;
+ ptr->ptr = freelist;
+ ptr->type = 0;
+ freelist = ptr;
+ return ret;
+}
+
+/* allocate a user handle for a given object */
+user_handle_t alloc_user_handle( void *ptr, enum user_object type )
+{
+ struct user_handle *entry = alloc_user_entry();
+ if (!entry) return 0;
+ entry->ptr = ptr;
+ entry->type = type;
+ if (++entry->generation >= 0xffff) entry->generation = 1;
+ return entry_to_handle( entry );
+}
+
+/* return a pointer to a user object from its handle */
+void *get_user_object( user_handle_t handle, enum user_object type )
+{
+ struct user_handle *entry;
+
+ if (!(entry = handle_to_entry( handle )) || entry->type != type)
+ {
+ set_error( STATUS_INVALID_HANDLE );
+ return NULL;
+ }
+ return entry->ptr;
+}
+
+/* same as get_user_object plus set the handle to the full 32-bit value */
+void *get_user_object_handle( user_handle_t *handle, enum user_object type )
+{
+ struct user_handle *entry;
+
+ if (!(entry = handle_to_entry( *handle )) || entry->type != type)
+ {
+ set_error( STATUS_INVALID_HANDLE );
+ return NULL;
+ }
+ *handle = entry_to_handle( entry );
+ return entry->ptr;
+}
+
+/* free a user handle and return a pointer to the object */
+void *free_user_handle( user_handle_t handle )
+{
+ struct user_handle *entry;
+
+ if (!(entry = handle_to_entry( handle )))
+ {
+ set_error( STATUS_INVALID_HANDLE );
+ return NULL;
+ }
+ return free_user_entry( entry );
+}
+
+/* return the next user handle after 'handle' that is of a given type */
+void *next_user_handle( user_handle_t *handle, enum user_object type )
+{
+ struct user_handle *entry;
+
+ if (!*handle) entry = handles;
+ else
+ {
+ if (!(entry = handle_to_entry( *handle ))) return NULL;
+ entry++; /* start from the next one */
+ }
+ while (entry < handles + nb_handles)
+ {
+ if (!type || entry->type == type)
+ {
+ *handle = entry_to_handle( entry );
+ return entry->ptr;
+ }
+ entry++;
+ }
+ return NULL;
+}
diff --git a/server/user.h b/server/user.h
new file mode 100644
index 0000000..810d60c
--- /dev/null
+++ b/server/user.h
@@ -0,0 +1,36 @@
+/*
+ * Wine server USER definitions
+ *
+ * Copyright (C) 2001 Alexandre Julliard
+ */
+
+#ifndef __WINE_SERVER_USER_H
+#define __WINE_SERVER_USER_H
+
+#include "wine/server_protocol.h"
+
+struct thread;
+struct window;
+
+enum user_object
+{
+ USER_WINDOW = 1
+};
+
+/* user handles functions */
+
+extern user_handle_t alloc_user_handle( void *ptr, enum user_object type );
+extern void *get_user_object( user_handle_t handle, enum user_object type );
+extern void *get_user_object_handle( user_handle_t *handle, enum user_object type );
+extern void *free_user_handle( user_handle_t handle );
+extern void *next_user_handle( user_handle_t *handle, enum user_object type );
+
+/* queue functions */
+
+extern void queue_cleanup_window( struct thread *thread, user_handle_t win );
+
+/* window functions */
+
+extern void destroy_thread_windows( struct thread *thread );
+
+#endif /* __WINE_SERVER_USER_H */
diff --git a/server/window.c b/server/window.c
new file mode 100644
index 0000000..f6e853c
--- /dev/null
+++ b/server/window.c
@@ -0,0 +1,179 @@
+/*
+ * Server-side window handling
+ *
+ * Copyright (C) 2001 Alexandre Julliard
+ */
+
+#include <assert.h>
+
+#include "object.h"
+#include "request.h"
+#include "thread.h"
+#include "process.h"
+#include "user.h"
+
+struct window
+{
+ struct window *parent; /* parent window */
+ struct window *child; /* first child window */
+ struct window *next; /* next window in Z-order */
+ struct window *prev; /* prev window in Z-order */
+ user_handle_t handle; /* full handle for this window */
+ struct thread *thread; /* thread owning the window */
+};
+
+static struct window *top_window; /* top-level (desktop) window */
+
+/* link a window into the tree (or unlink it if the new parent is NULL) */
+static void link_window( struct window *win, struct window *parent, struct window *previous )
+{
+ if (win->parent) /* unlink it from the previous location */
+ {
+ if (win->next) win->next->prev = win->prev;
+ if (win->prev) win->prev->next = win->next;
+ else win->parent->child = win->next;
+ }
+ if ((win->parent = parent))
+ {
+ if ((win->prev = previous))
+ {
+ if ((win->next = previous->next)) win->next->prev = win;
+ win->prev->next = win;
+ }
+ else
+ {
+ if ((win->next = parent->child)) win->next->prev = win;
+ parent->child = win;
+ }
+ }
+ else win->next = win->prev = NULL;
+
+}
+
+/* free a window structure */
+static void free_window( struct window *win )
+{
+ assert( win != top_window );
+
+ while (win->child) free_window( win->child );
+ if (win->thread->queue) queue_cleanup_window( win->thread, win->handle );
+ free_user_handle( win->handle );
+ if (win->parent) link_window( win, NULL, NULL );
+ memset( win, 0x55, sizeof(*win) );
+ free( win );
+}
+
+/* create a new window structure */
+static struct window *create_window(void)
+{
+ struct window *win = mem_alloc( sizeof(*win) );
+ if (!win) return NULL;
+
+ if (!(win->handle = alloc_user_handle( win, USER_WINDOW )))
+ {
+ free( win );
+ return NULL;
+ }
+ win->parent = NULL;
+ win->child = NULL;
+ win->next = NULL;
+ win->prev = NULL;
+ win->thread = current;
+ return win;
+}
+
+/* destroy all windows belonging to a given thread */
+void destroy_thread_windows( struct thread *thread )
+{
+ user_handle_t handle = 0;
+ struct window *win;
+
+ while ((win = next_user_handle( &handle, USER_WINDOW )))
+ {
+ if (win->thread != thread) continue;
+ free_window( win );
+ }
+}
+
+/* create a window */
+DECL_HANDLER(create_window)
+{
+ struct window *win;
+
+ req->handle = 0;
+ if ((win = create_window())) req->handle = win->handle;
+}
+
+
+/* create the desktop window */
+DECL_HANDLER(create_desktop_window)
+{
+ req->handle = 0;
+ if (!top_window)
+ {
+ if (!(top_window = create_window())) return;
+ top_window->thread = NULL; /* no thread owns the desktop */
+ }
+ req->handle = top_window->handle;
+}
+
+
+/* link a window into the tree */
+DECL_HANDLER(link_window)
+{
+ struct window *win, *parent = NULL, *previous = NULL;
+
+ if (!(win = get_user_object( req->handle, USER_WINDOW ))) return;
+ if (req->parent && !(parent = get_user_object( req->parent, USER_WINDOW ))) return;
+ if (parent && req->previous)
+ {
+ if (req->previous == 1) /* special case: HWND_BOTTOM */
+ {
+ if ((previous = parent->child))
+ {
+ /* make it the last of the list */
+ while (previous->next) previous = previous->next;
+ }
+ }
+ else if (!(previous = get_user_object( req->previous, USER_WINDOW ))) return;
+
+ if (previous && previous->parent != parent) /* previous must be a child of parent */
+ {
+ set_error( STATUS_INVALID_PARAMETER );
+ return;
+ }
+ }
+ link_window( win, parent, previous );
+}
+
+
+/* destroy a window */
+DECL_HANDLER(destroy_window)
+{
+ struct window *win = get_user_object( req->handle, USER_WINDOW );
+ if (win)
+ {
+ if (win != top_window) free_window( win );
+ else set_error( STATUS_ACCESS_DENIED );
+ }
+}
+
+
+/* Get information from a window handle */
+DECL_HANDLER(get_window_info)
+{
+ user_handle_t handle = req->handle;
+ struct window *win = get_user_object_handle( &handle, USER_WINDOW );
+
+ req->full_handle = 0;
+ req->tid = req->pid = 0;
+ if (win)
+ {
+ req->full_handle = handle;
+ if (win->thread)
+ {
+ req->tid = get_thread_id( win->thread );
+ req->pid = get_process_id( win->thread->process );
+ }
+ }
+}