Module: wine Branch: master Commit: 81caccbf840d14abede843d4399b650e73bc52ba URL: https://gitlab.winehq.org/wine/wine/-/commit/81caccbf840d14abede843d4399b650...
Author: Connor McAdams cmcadams@codeweavers.com Date: Wed Sep 13 13:49:31 2023 -0400
uiautomationcore: Track HWNDs that need WinEvent translation.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com
---
dlls/uiautomationcore/uia_client.c | 4 ++ dlls/uiautomationcore/uia_event.c | 25 ++++++++++++ dlls/uiautomationcore/uia_private.h | 7 ++++ dlls/uiautomationcore/uia_utils.c | 79 +++++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+)
diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 6ec675e197e..09e0d179172 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -1870,6 +1870,9 @@ static HRESULT WINAPI uia_provider_attach_event(IWineUiaProvider *iface, LONG_PT &IID_IProxyProviderWinEventHandler, (void **)&winevent_handler))) { FIXME("MSAA to UIA event bridge currently unimplemented\n"); + hr = uia_event_add_win_event_hwnd(event, prov->hwnd); + if (FAILED(hr)) + WARN("Failed to add hwnd for win_event, hr %#lx\n", hr); IProxyProviderWinEventHandler_Release(winevent_handler); } else if (SUCCEEDED(IRawElementProviderFragmentRoot_QueryInterface(elroot, &IID_IRawElementProviderAdviseEvents, @@ -1954,6 +1957,7 @@ static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProvid prov->IWineUiaProvider_iface.lpVtbl = &uia_provider_vtbl; prov->elprov = elprov; prov->ref = 1; + prov->hwnd = node->hwnd; node->prov[prov_type] = &prov->IWineUiaProvider_iface; if (!node->prov_count) node->creator_prov_type = prov_type; diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 04067f39e0e..796a74d86fc 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -64,6 +64,29 @@ static int win_event_to_uia_event_id(int win_event) return 0; }
+static BOOL CALLBACK uia_win_event_enum_top_level_hwnds(HWND hwnd, LPARAM lparam) +{ + struct rb_tree *hwnd_map = (struct rb_tree *)lparam; + HRESULT hr; + + if (!uia_hwnd_is_visible(hwnd)) + return TRUE; + + hr = uia_hwnd_map_add_hwnd(hwnd_map, hwnd); + if (FAILED(hr)) + WARN("Failed to add hwnd to map, hr %#lx\n", hr); + + return TRUE; +} + +HRESULT uia_event_add_win_event_hwnd(struct uia_event *event, HWND hwnd) +{ + if (hwnd == GetDesktopWindow()) + EnumWindows(uia_win_event_enum_top_level_hwnds, (LPARAM)&event->u.clientside.win_event_hwnd_map); + + return uia_hwnd_map_add_hwnd(&event->u.clientside.win_event_hwnd_map, hwnd); +} + /* * UI Automation event map. */ @@ -658,6 +681,7 @@ static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface) uia_cache_request_destroy(&event->u.clientside.cache_req); if (event->u.clientside.git_cookie) uia_stop_event_thread(); + uia_hwnd_map_destroy(&event->u.clientside.win_event_hwnd_map); } else { @@ -834,6 +858,7 @@ static HRESULT create_clientside_uia_event(struct uia_event **out_event, int eve event->scope = scope; event->u.clientside.event_callback = cback; event->u.clientside.callback_data = cback_data; + uia_hwnd_map_init(&event->u.clientside.win_event_hwnd_map);
*out_event = event; return S_OK; diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index e8bb4845189..bd88dff30e3 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -95,6 +95,7 @@ struct uia_provider { BOOL return_nested_node; BOOL parent_check_ran; BOOL has_parent; + HWND hwnd; };
static inline struct uia_provider *impl_from_IWineUiaProvider(IWineUiaProvider *iface) @@ -140,6 +141,7 @@ struct uia_event HRESULT (*event_callback)(struct uia_event *, struct uia_event_args *, SAFEARRAY *, BSTR); void *callback_data;
+ struct rb_tree win_event_hwnd_map; DWORD git_cookie; } clientside; struct { @@ -218,6 +220,7 @@ BOOL uia_condition_matched(HRESULT hr) DECLSPEC_HIDDEN; HRESULT create_uia_iface(IUnknown **iface, BOOL is_cui8) DECLSPEC_HIDDEN;
/* uia_event.c */ +HRESULT uia_event_add_win_event_hwnd(struct uia_event *event, HWND hwnd) DECLSPEC_HIDDEN; HRESULT create_serverside_uia_event(struct uia_event **out_event, LONG process_id, LONG event_cookie) DECLSPEC_HIDDEN; HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *advise_events, struct uia_event *event) DECLSPEC_HIDDEN; @@ -250,3 +253,7 @@ HRESULT uia_cache_request_clone(struct UiaCacheRequest *dst, struct UiaCacheRequ HRESULT get_safearray_dim_bounds(SAFEARRAY *sa, UINT dim, LONG *lbound, LONG *elems) DECLSPEC_HIDDEN; HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems) DECLSPEC_HIDDEN; int uia_compare_safearrays(SAFEARRAY *sa1, SAFEARRAY *sa2, int prop_type) DECLSPEC_HIDDEN; +BOOL uia_hwnd_is_visible(HWND hwnd) DECLSPEC_HIDDEN; +HRESULT uia_hwnd_map_add_hwnd(struct rb_tree *hwnd_map, HWND hwnd) DECLSPEC_HIDDEN; +void uia_hwnd_map_init(struct rb_tree *hwnd_map) DECLSPEC_HIDDEN; +void uia_hwnd_map_destroy(struct rb_tree *hwnd_map) DECLSPEC_HIDDEN; diff --git a/dlls/uiautomationcore/uia_utils.c b/dlls/uiautomationcore/uia_utils.c index 9f27d6a0b34..9840fe4fe40 100644 --- a/dlls/uiautomationcore/uia_utils.c +++ b/dlls/uiautomationcore/uia_utils.c @@ -384,3 +384,82 @@ int uia_compare_safearrays(SAFEARRAY *sa1, SAFEARRAY *sa2, int prop_type)
return 0; } + +/* + * HWND related helper functions. + */ +BOOL uia_hwnd_is_visible(HWND hwnd) +{ + RECT rect; + + if (!IsWindowVisible(hwnd)) + return FALSE; + + if (!GetWindowRect(hwnd, &rect)) + return FALSE; + + if ((rect.right - rect.left) <= 0 || (rect.bottom - rect.top) <= 0) + return FALSE; + + return TRUE; +} + +/* + * rbtree to efficiently store a collection of HWNDs. + */ +struct uia_hwnd_map_entry +{ + struct rb_entry entry; + HWND hwnd; +}; + +static int uia_hwnd_map_hwnd_compare(const void *key, const struct rb_entry *entry) +{ + struct uia_hwnd_map_entry *hwnd_entry = RB_ENTRY_VALUE(entry, struct uia_hwnd_map_entry, entry); + HWND hwnd = (HWND)key; + + return (hwnd_entry->hwnd > hwnd) - (hwnd_entry->hwnd < hwnd); +} + +static void uia_hwnd_map_free(struct rb_entry *entry, void *context) +{ + struct uia_hwnd_map_entry *hwnd_entry = RB_ENTRY_VALUE(entry, struct uia_hwnd_map_entry, entry); + + TRACE("Removing hwnd %p from map %p\n", hwnd_entry->hwnd, context); + free(hwnd_entry); +} + +static BOOL uia_hwnd_map_check_hwnd(struct rb_tree *hwnd_map, HWND hwnd) +{ + return !!rb_get(hwnd_map, hwnd); +} + +HRESULT uia_hwnd_map_add_hwnd(struct rb_tree *hwnd_map, HWND hwnd) +{ + struct uia_hwnd_map_entry *entry; + + if (uia_hwnd_map_check_hwnd(hwnd_map, hwnd)) + { + TRACE("hwnd %p already in map %p\n", hwnd, hwnd_map); + return S_OK; + } + + if (!(entry = calloc(1, sizeof(*entry)))) + return E_OUTOFMEMORY; + + TRACE("Adding hwnd %p to map %p\n", hwnd, hwnd_map); + entry->hwnd = hwnd; + rb_put(hwnd_map, hwnd, &entry->entry); + + return S_OK; +} + +void uia_hwnd_map_init(struct rb_tree *hwnd_map) +{ + rb_init(hwnd_map, uia_hwnd_map_hwnd_compare); +} + +void uia_hwnd_map_destroy(struct rb_tree *hwnd_map) +{ + rb_destroy(hwnd_map, uia_hwnd_map_free, hwnd_map); +}