Moved the WindowFromPoint functionality to the server so that we can

properly take into account the window region.
This commit is contained in:
Alexandre Julliard 2004-07-20 22:17:38 +00:00
parent 2c90b52bbc
commit 4616dcb9a7
8 changed files with 257 additions and 132 deletions

View File

@ -2515,6 +2515,22 @@ struct get_window_children_reply
struct get_window_children_from_point_request
{
struct request_header __header;
user_handle_t parent;
int x;
int y;
};
struct get_window_children_from_point_reply
{
struct reply_header __header;
int count;
/* VARARG(children,user_handles); */
};
struct get_window_tree_request
{
struct request_header __header;
@ -3227,6 +3243,7 @@ enum request
REQ_set_window_info,
REQ_get_window_parents,
REQ_get_window_children,
REQ_get_window_children_from_point,
REQ_get_window_tree,
REQ_set_window_rectangles,
REQ_get_window_rectangles,
@ -3410,6 +3427,7 @@ union generic_request
struct set_window_info_request set_window_info_request;
struct get_window_parents_request get_window_parents_request;
struct get_window_children_request get_window_children_request;
struct get_window_children_from_point_request get_window_children_from_point_request;
struct get_window_tree_request get_window_tree_request;
struct set_window_rectangles_request set_window_rectangles_request;
struct get_window_rectangles_request get_window_rectangles_request;
@ -3591,6 +3609,7 @@ union generic_reply
struct set_window_info_reply set_window_info_reply;
struct get_window_parents_reply get_window_parents_reply;
struct get_window_children_reply get_window_children_reply;
struct get_window_children_from_point_reply get_window_children_from_point_reply;
struct get_window_tree_reply get_window_tree_reply;
struct set_window_rectangles_reply set_window_rectangles_reply;
struct get_window_rectangles_reply get_window_rectangles_reply;
@ -3628,6 +3647,6 @@ union generic_reply
struct set_global_windows_reply set_global_windows_reply;
};
#define SERVER_PROTOCOL_VERSION 146
#define SERVER_PROTOCOL_VERSION 147
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */

View File

@ -1777,6 +1777,17 @@ enum message_type
@END
/* Get a list of the window children that contain a given point */
@REQ(get_window_children_from_point)
user_handle_t parent; /* parent window */
int x; /* point in parent coordinates */
int y;
@REPLY
int count; /* total count of children */
VARARG(children,user_handles); /* children handles */
@END
/* Get window tree information from a window handle */
@REQ(get_window_tree)
user_handle_t handle; /* handle to the window */

View File

@ -758,3 +758,20 @@ struct region *union_region( struct region *dst, const struct region *src1,
dst->extents.bottom = max(src1->extents.bottom, src2->extents.bottom);
return dst;
}
/* check if the given point is inside the region */
int point_in_region( struct region *region, int x, int y )
{
const rectangle_t *ptr, *end;
for (ptr = region->rects, end = region->rects + region->num_rects; ptr < end; ptr++)
{
if (ptr->top > y) return 0;
if (ptr->bottom <= y) continue;
/* now we are in the correct band */
if (ptr->left > x) return 0;
if (ptr->right <= x) continue;
return 1;
}
return 0;
}

View File

@ -244,6 +244,7 @@ DECL_HANDLER(get_window_info);
DECL_HANDLER(set_window_info);
DECL_HANDLER(get_window_parents);
DECL_HANDLER(get_window_children);
DECL_HANDLER(get_window_children_from_point);
DECL_HANDLER(get_window_tree);
DECL_HANDLER(set_window_rectangles);
DECL_HANDLER(get_window_rectangles);
@ -426,6 +427,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
(req_handler)req_set_window_info,
(req_handler)req_get_window_parents,
(req_handler)req_get_window_children,
(req_handler)req_get_window_children_from_point,
(req_handler)req_get_window_tree,
(req_handler)req_set_window_rectangles,
(req_handler)req_get_window_rectangles,

View File

@ -2105,6 +2105,20 @@ static void dump_get_window_children_reply( const struct get_window_children_rep
dump_varargs_user_handles( cur_size );
}
static void dump_get_window_children_from_point_request( const struct get_window_children_from_point_request *req )
{
fprintf( stderr, " parent=%p,", req->parent );
fprintf( stderr, " x=%d,", req->x );
fprintf( stderr, " y=%d", req->y );
}
static void dump_get_window_children_from_point_reply( const struct get_window_children_from_point_reply *req )
{
fprintf( stderr, " count=%d,", req->count );
fprintf( stderr, " children=" );
dump_varargs_user_handles( cur_size );
}
static void dump_get_window_tree_request( const struct get_window_tree_request *req )
{
fprintf( stderr, " handle=%p", req->handle );
@ -2677,6 +2691,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_set_window_info_request,
(dump_func)dump_get_window_parents_request,
(dump_func)dump_get_window_children_request,
(dump_func)dump_get_window_children_from_point_request,
(dump_func)dump_get_window_tree_request,
(dump_func)dump_set_window_rectangles_request,
(dump_func)dump_get_window_rectangles_request,
@ -2856,6 +2871,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_set_window_info_reply,
(dump_func)dump_get_window_parents_reply,
(dump_func)dump_get_window_children_reply,
(dump_func)dump_get_window_children_from_point_reply,
(dump_func)dump_get_window_tree_reply,
(dump_func)0,
(dump_func)dump_get_window_rectangles_reply,
@ -3035,6 +3051,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
"set_window_info",
"get_window_parents",
"get_window_children",
"get_window_children_from_point",
"get_window_tree",
"set_window_rectangles",
"get_window_rectangles",

View File

@ -83,6 +83,7 @@ extern struct region *subtract_region( struct region *dst, const struct region *
const struct region *src2 );
extern struct region *union_region( struct region *dst, const struct region *src1,
const struct region *src2 );
extern int point_in_region( struct region *region, int x, int y );
static inline struct region *create_empty_region(void) { return create_region( NULL, 0 ); }
/* window functions */

View File

@ -66,9 +66,9 @@ struct window
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 @@ struct window
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 @@ static void link_window( struct window *win, struct window *parent, struct windo
}
}
/* 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 @@ int make_window_active( user_handle_t window )
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 @@ static struct window *child_window_from_point( struct window *parent, int x, int
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 @@ static struct window *child_window_from_point( struct window *parent, int x, int
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 @@ user_handle_t window_from_point( int x, int y )
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 @@ DECL_HANDLER(get_window_children)
}
/* 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)
{

View File

@ -338,92 +338,42 @@ BOOL WINAPI ScreenToClient( HWND hwnd, LPPOINT lppnt )
/***********************************************************************
* find_child_from_point
* list_children_from_point
*
* Find the child that contains pt. Helper for WindowFromPoint.
* pt is in parent client coordinates.
* lparam is the param to pass in the WM_NCHITTEST message.
* Get the list of children that can contain point from the server.
* Point is in screen coordinates.
* Returned list must be freed by caller.
*/
static HWND find_child_from_point( HWND parent, POINT pt, INT *hittest, LPARAM lparam )
static HWND *list_children_from_point( HWND hwnd, POINT pt )
{
int i, res;
LONG style, exstyle;
RECT rectWindow, rectClient;
WND *wndPtr;
HWND *list = WIN_ListChildren( parent );
HWND retvalue = 0;
HWND *list;
int size = 32;
if (!list) return 0;
for (i = 0; list[i]; i++)
for (;;)
{
/* If point is in window, and window is visible, and it */
/* is enabled (or it's a top-level window), then explore */
/* its children. Otherwise, go to the next window. */
int count = 0;
style = GetWindowLongW( list[i], GWL_STYLE );
if (!(style & WS_VISIBLE)) continue; /* not visible -> skip */
if ((style & (WS_POPUP | WS_CHILD | WS_DISABLED)) == (WS_CHILD | WS_DISABLED))
continue; /* disabled child -> skip */
exstyle = GetWindowLongW( list[i], GWL_EXSTYLE );
if ((exstyle & (WS_EX_LAYERED | WS_EX_TRANSPARENT)) == (WS_EX_LAYERED | WS_EX_TRANSPARENT))
continue; /* transparent -> skip */
if (!(list = HeapAlloc( GetProcessHeap(), 0, size * sizeof(HWND) ))) break;
if (!WIN_GetRectangles( list[i], &rectWindow, &rectClient )) continue;
if (!PtInRect( &rectWindow, pt )) continue; /* not in window -> skip */
/* FIXME: check window region for other processes too */
if ((wndPtr = WIN_GetPtr( list[i] )) && wndPtr != WND_OTHER_PROCESS)
SERVER_START_REQ( get_window_children_from_point )
{
if (wndPtr->hrgnWnd && !PtInRegion( wndPtr->hrgnWnd,
pt.x - rectWindow.left, pt.y - rectWindow.top ))
{
WIN_ReleasePtr( wndPtr );
continue; /* point outside window region -> skip */
}
WIN_ReleasePtr( wndPtr );
req->parent = hwnd;
req->x = pt.x;
req->y = pt.y;
wine_server_set_reply( req, list, (size-1) * sizeof(HWND) );
if (!wine_server_call( req )) count = reply->count;
}
/* If window is minimized or disabled, return at once */
if (style & WS_MINIMIZE)
SERVER_END_REQ;
if (count && count < size)
{
*hittest = HTCAPTION;
retvalue = list[i];
break;
list[count] = 0;
return list;
}
if (style & WS_DISABLED)
{
*hittest = HTERROR;
retvalue = list[i];
break;
}
/* If point is in client area, explore children */
if (PtInRect( &rectClient, pt ))
{
POINT new_pt;
new_pt.x = pt.x - rectClient.left;
new_pt.y = pt.y - rectClient.top;
if ((retvalue = find_child_from_point( list[i], new_pt, hittest, lparam ))) break;
}
/* Now it's inside window, send WM_NCCHITTEST (if same thread) */
if (!WIN_IsCurrentThread( list[i] ))
{
*hittest = HTCLIENT;
retvalue = list[i];
break;
}
if ((res = SendMessageA( list[i], WM_NCHITTEST, 0, lparam )) != HTTRANSPARENT)
{
*hittest = res; /* Found the window */
retvalue = list[i];
break;
}
/* continue search with next sibling */
HeapFree( GetProcessHeap(), 0, list );
if (!count) break;
size = count + 1; /* restart with a large enough buffer */
}
HeapFree( GetProcessHeap(), 0, list );
return retvalue;
return NULL;
}
@ -434,54 +384,50 @@ static HWND find_child_from_point( HWND parent, POINT pt, INT *hittest, LPARAM l
*/
HWND WINPOS_WindowFromPoint( HWND hwndScope, POINT pt, INT *hittest )
{
POINT xy = pt;
int res;
LONG style;
TRACE("scope %p %ld,%ld\n", hwndScope, pt.x, pt.y);
int i, res;
HWND ret, *list;
if (!hwndScope) hwndScope = GetDesktopWindow();
style = GetWindowLongW( hwndScope, GWL_STYLE );
*hittest = HTERROR;
if (style & WS_DISABLED) return 0;
MapWindowPoints( GetDesktopWindow(), GetAncestor( hwndScope, GA_PARENT ), &xy, 1 );
if (!(style & WS_MINIMIZE))
{
RECT rectClient;
if (WIN_GetRectangles( hwndScope, NULL, &rectClient ) && PtInRect( &rectClient, xy ))
{
HWND ret;
xy.x -= rectClient.left;
xy.y -= rectClient.top;
if ((ret = find_child_from_point( hwndScope, xy, hittest, MAKELONG( pt.x, pt.y ) )))
{
TRACE( "found child %p\n", ret );
return ret;
}
}
}
/* If nothing found, try the scope window */
if (!WIN_IsCurrentThread( hwndScope ))
{
*hittest = HTCLIENT;
TRACE( "returning %p\n", hwndScope );
return hwndScope;
}
res = SendMessageA( hwndScope, WM_NCHITTEST, 0, MAKELONG( pt.x, pt.y ) );
if (res != HTTRANSPARENT)
{
*hittest = res; /* Found the window */
TRACE( "returning %p\n", hwndScope );
return hwndScope;
}
*hittest = HTNOWHERE;
TRACE( "nothing found\n" );
return 0;
if (!(list = list_children_from_point( hwndScope, pt ))) return 0;
/* now determine the hittest */
for (i = 0; list[i]; i++)
{
LONG style = GetWindowLongW( list[i], GWL_STYLE );
/* If window is minimized or disabled, return at once */
if (style & WS_MINIMIZE)
{
*hittest = HTCAPTION;
break;
}
if (style & WS_DISABLED)
{
*hittest = HTERROR;
break;
}
/* Send WM_NCCHITTEST (if same thread) */
if (!WIN_IsCurrentThread( list[i] ))
{
*hittest = HTCLIENT;
break;
}
res = SendMessageA( list[i], WM_NCHITTEST, 0, MAKELONG(pt.x,pt.y) );
if (res != HTTRANSPARENT)
{
*hittest = res; /* Found the window */
break;
}
/* continue search with next window in z-order */
}
ret = list[i];
HeapFree( GetProcessHeap(), 0, list );
TRACE( "scope %p (%ld,%ld) returning %p\n", hwndScope, pt.x, pt.y, ret );
return ret;
}