Moved the WindowFromPoint functionality to the server so that we can
properly take into account the window region.
diff --git a/server/window.c b/server/window.c
index fc08531..00a004e 100644
--- a/server/window.c
+++ b/server/window.c
@@ -66,9 +66,9 @@
struct window_class *class; /* window class */
atom_t atom; /* class atom */
user_handle_t last_active; /* last active popup */
- rectangle_t window_rect; /* window rectangle */
- rectangle_t client_rect; /* client rectangle */
- struct region *win_region; /* window region (for shaped windows) */
+ rectangle_t window_rect; /* window rectangle (relative to parent client area) */
+ rectangle_t client_rect; /* client rectangle (relative to parent client area) */
+ struct region *win_region; /* region for shaped windows (relative to window rect) */
unsigned int style; /* window style */
unsigned int ex_style; /* window extended style */
unsigned int id; /* window id */
@@ -83,6 +83,14 @@
char extra_bytes[1]; /* extra bytes storage */
};
+/* growable array of user handles */
+struct user_handle_array
+{
+ user_handle_t *handles;
+ int count;
+ int total;
+};
+
static struct window *top_window; /* top-level (desktop) window */
/* global window pointers */
@@ -145,6 +153,26 @@
}
}
+/* append a user handle to a handle array */
+static int add_handle_to_array( struct user_handle_array *array, user_handle_t handle )
+{
+ if (array->count >= array->total)
+ {
+ int new_total = max( array->total * 2, 32 );
+ user_handle_t *new_array = realloc( array->handles, new_total * sizeof(*new_array) );
+ if (!new_array)
+ {
+ free( array->handles );
+ set_error( STATUS_NO_MEMORY );
+ return 0;
+ }
+ array->handles = new_array;
+ array->total = new_total;
+ }
+ array->handles[array->count++] = handle;
+ return 1;
+}
+
/* set a window property */
static void set_property( struct window *win, atom_t atom, obj_handle_t handle,
enum property_type type )
@@ -377,6 +405,23 @@
return 1;
}
+/* check if point is inside the window */
+static inline int is_point_in_window( struct window *win, int x, int y )
+{
+ if (!(win->style & WS_VISIBLE)) return 0; /* not visible */
+ if ((win->style & (WS_POPUP|WS_CHILD|WS_DISABLED)) == (WS_CHILD|WS_DISABLED))
+ return 0; /* disabled child */
+ if ((win->ex_style & (WS_EX_LAYERED|WS_EX_TRANSPARENT)) == (WS_EX_LAYERED|WS_EX_TRANSPARENT))
+ return 0; /* transparent */
+ if (x < win->window_rect.left || x >= win->window_rect.right ||
+ y < win->window_rect.top || y >= win->window_rect.bottom)
+ return 0; /* not in window */
+ if (win->win_region &&
+ !point_in_region( win->win_region, x - win->window_rect.left, y - win->window_rect.top ))
+ return 0; /* not in window region */
+ return 1;
+}
+
/* find child of 'parent' that contains the given point (in parent-relative coords) */
static struct window *child_window_from_point( struct window *parent, int x, int y )
{
@@ -384,16 +429,7 @@
for (ptr = parent->first_child; ptr; ptr = ptr->next)
{
- if (!(ptr->style & WS_VISIBLE)) continue; /* not visible -> skip */
- if ((ptr->style & (WS_POPUP|WS_CHILD|WS_DISABLED)) == (WS_CHILD|WS_DISABLED))
- continue; /* disabled child -> skip */
- if ((ptr->ex_style & (WS_EX_LAYERED|WS_EX_TRANSPARENT)) == (WS_EX_LAYERED|WS_EX_TRANSPARENT))
- continue; /* transparent -> skip */
- if (x < ptr->window_rect.left || x >= ptr->window_rect.right ||
- y < ptr->window_rect.top || y >= ptr->window_rect.bottom)
- continue; /* not in window -> skip */
-
- /* FIXME: check window region here */
+ if (!is_point_in_window( ptr, x, y )) continue; /* skip it */
/* if window is minimized or disabled, return at once */
if (ptr->style & (WS_MINIMIZE|WS_DISABLED)) return ptr;
@@ -408,6 +444,32 @@
return parent; /* not found any child */
}
+/* find all children of 'parent' that contain the given point */
+static int get_window_children_from_point( struct window *parent, int x, int y,
+ struct user_handle_array *array )
+{
+ struct window *ptr;
+
+ for (ptr = parent->first_child; ptr; ptr = ptr->next)
+ {
+ if (!is_point_in_window( ptr, x, y )) continue; /* skip it */
+
+ /* if point is in client area, and window is not minimized or disabled, check children */
+ if (!(ptr->style & (WS_MINIMIZE|WS_DISABLED)) &&
+ x >= ptr->client_rect.left && x < ptr->client_rect.right &&
+ y >= ptr->client_rect.top && y < ptr->client_rect.bottom)
+ {
+ if (!get_window_children_from_point( ptr, x - ptr->client_rect.left,
+ y - ptr->client_rect.top, array ))
+ return 0;
+ }
+
+ /* now add window to the array */
+ if (!add_handle_to_array( array, ptr->handle )) return 0;
+ }
+ return 1;
+}
+
/* find window containing point (in absolute coords) */
user_handle_t window_from_point( int x, int y )
{
@@ -418,6 +480,35 @@
return ret->handle;
}
+/* return list of all windows containing point (in absolute coords) */
+static int all_windows_from_point( struct window *top, int x, int y, struct user_handle_array *array )
+{
+ struct window *ptr;
+
+ /* make point relative to top window */
+ for (ptr = top->parent; ptr && ptr != top_window; ptr = ptr->parent)
+ {
+ x -= ptr->client_rect.left;
+ y -= ptr->client_rect.top;
+ }
+
+ if (!is_point_in_window( top, x, y )) return 1;
+
+ /* if point is in client area, and window is not minimized or disabled, check children */
+ if (!(top->style & (WS_MINIMIZE|WS_DISABLED)) &&
+ x >= top->client_rect.left && x < top->client_rect.right &&
+ y >= top->client_rect.top && y < top->client_rect.bottom)
+ {
+ if (!get_window_children_from_point( top, x - top->client_rect.left,
+ y - top->client_rect.top, array ))
+ return 0;
+ }
+ /* now add window to the array */
+ if (!add_handle_to_array( array, top->handle )) return 0;
+ return 1;
+}
+
+
/* return the thread owning a window */
struct thread *get_window_thread( user_handle_t handle )
{
@@ -825,6 +916,27 @@
}
+/* get a list of the window children that contain a given point */
+DECL_HANDLER(get_window_children_from_point)
+{
+ struct user_handle_array array;
+ struct window *parent = get_window( req->parent );
+ size_t len;
+
+ if (!parent) return;
+
+ array.handles = NULL;
+ array.count = 0;
+ array.total = 0;
+ if (!all_windows_from_point( parent, req->x, req->y, &array )) return;
+
+ reply->count = array.count;
+ len = min( get_reply_max_size(), array.count * sizeof(user_handle_t) );
+ if (len) set_reply_data_ptr( array.handles, len );
+ else free( array.handles );
+}
+
+
/* get window tree information from a window handle */
DECL_HANDLER(get_window_tree)
{