From 4616dcb9a792083f0baf4ceb06252d092c8dd218 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 20 Jul 2004 22:17:38 +0000 Subject: [PATCH] Moved the WindowFromPoint functionality to the server so that we can properly take into account the window region. --- include/wine/server_protocol.h | 21 +++- server/protocol.def | 11 ++ server/region.c | 17 +++ server/request.h | 2 + server/trace.c | 17 +++ server/user.h | 1 + server/window.c | 138 ++++++++++++++++++++++--- windows/winpos.c | 182 ++++++++++++--------------------- 8 files changed, 257 insertions(+), 132 deletions(-) diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 7bcfd73bb36..76fa6bed8b2 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -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 */ diff --git a/server/protocol.def b/server/protocol.def index df2bf6850a4..a2864be0bc4 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -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 */ diff --git a/server/region.c b/server/region.c index ab6645c06cb..e0035e96521 100644 --- a/server/region.c +++ b/server/region.c @@ -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; +} diff --git a/server/request.h b/server/request.h index 86c741986c9..92e6d572329 100644 --- a/server/request.h +++ b/server/request.h @@ -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, diff --git a/server/trace.c b/server/trace.c index f9445bc2789..13896081f8f 100644 --- a/server/trace.c +++ b/server/trace.c @@ -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", diff --git a/server/user.h b/server/user.h index 80ccddfca37..638ffc68f8b 100644 --- a/server/user.h +++ b/server/user.h @@ -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 */ diff --git a/server/window.c b/server/window.c index fc085319b8f..00a004eff56 100644 --- a/server/window.c +++ b/server/window.c @@ -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) { diff --git a/windows/winpos.c b/windows/winpos.c index 4140a8d79df..fa1610a3e09 100644 --- a/windows/winpos.c +++ b/windows/winpos.c @@ -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; }