Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/dxgi/swapchain.c | 163 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+)
diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c index a859baffc8..759d697915 100644 --- a/dlls/dxgi/swapchain.c +++ b/dlls/dxgi/swapchain.c @@ -39,6 +39,159 @@ WINE_DEFAULT_DEBUG_CHANNEL(dxgi); WINE_DECLARE_DEBUG_CHANNEL(winediag);
+extern const WCHAR wine_dxgi_associated_factory_propW[]; +extern const WCHAR wine_dxgi_associated_flags_propW[]; + +static struct list dxgi_swapchain_list = LIST_INIT(dxgi_swapchain_list); +static struct list dxgi_hook_list = LIST_INIT(dxgi_hook_list); + +struct dxgi_swapchain_entry +{ + IDXGISwapChain1 *swapchain; + DWORD thread_id; + struct list entry; +}; + +struct dxgi_hook_entry +{ + HHOOK hook; + DWORD thread_id; + struct list entry; +}; + +static LRESULT CALLBACK associated_window_hook_proc(int code, WPARAM wparam, LPARAM lparam) +{ + struct dxgi_swapchain_entry *swapchain_entry; + IDXGIFactory *dxgi_factory; + MSG *msg = (MSG *)lparam; + HANDLE factory_handle; + BOOL skip = FALSE; + BOOL fullscreen; + DWORD flags; + HWND hwnd; + + if (code == HC_ACTION && msg->message == WM_SYSKEYDOWN && msg->wParam == VK_RETURN && (msg->lParam & (KF_ALTDOWN << 16))) + { + wined3d_mutex_lock(); + LIST_FOR_EACH_ENTRY(swapchain_entry, &dxgi_swapchain_list, struct dxgi_swapchain_entry, entry) + { + if (FAILED(IDXGISwapChain1_GetHwnd(swapchain_entry->swapchain, &hwnd)) || hwnd != msg->hwnd) + continue; + + if (FAILED(IDXGISwapChain1_GetParent(swapchain_entry->swapchain, &IID_IDXGIFactory, (void **)&dxgi_factory))) + continue; + IDXGIFactory_Release(dxgi_factory); + + factory_handle = GetPropW(hwnd, wine_dxgi_associated_factory_propW); + flags = (DWORD)(UINT_PTR)GetPropW(hwnd, wine_dxgi_associated_flags_propW); + + if (factory_handle && factory_handle != dxgi_factory) + continue; + + if (flags & (DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER)) + break; + + if (SUCCEEDED(IDXGISwapChain1_GetFullscreenState(swapchain_entry->swapchain, &fullscreen, NULL))) + IDXGISwapChain1_SetFullscreenState(swapchain_entry->swapchain, !fullscreen, NULL); + skip = TRUE; + break; + } + wined3d_mutex_unlock(); + } + + return skip ? 1 : CallNextHookEx(0, code, wparam, lparam); +} + +static void add_associated_window_hook(IDXGISwapChain1 *swapchain, HWND hwnd) +{ + struct dxgi_swapchain_entry *swapchain_entry; + struct dxgi_hook_entry *hook_entry; + BOOL has_hook = FALSE; + + if (!hwnd) + return; + + if (!(swapchain_entry = heap_alloc_zero(sizeof(*swapchain_entry)))) + return; + + swapchain_entry->swapchain = swapchain; + swapchain_entry->thread_id = GetWindowThreadProcessId(hwnd, NULL); + wined3d_mutex_lock(); + list_add_head(&dxgi_swapchain_list, &swapchain_entry->entry); + /* Add hook for current thread if needed */ + LIST_FOR_EACH_ENTRY(hook_entry, &dxgi_hook_list, struct dxgi_hook_entry, entry) + { + if (hook_entry->thread_id == swapchain_entry->thread_id) + { + has_hook = TRUE; + break; + } + } + wined3d_mutex_unlock(); + + if (!has_hook) + { + if (!(hook_entry = heap_alloc_zero(sizeof(*hook_entry)))) + { + wined3d_mutex_lock(); + list_remove(&swapchain_entry->entry); + wined3d_mutex_unlock(); + heap_free(swapchain_entry); + return; + } + + hook_entry->thread_id = swapchain_entry->thread_id; + hook_entry->hook = SetWindowsHookExW(WH_GETMESSAGE, associated_window_hook_proc, 0, hook_entry->thread_id); + wined3d_mutex_lock(); + list_add_head(&dxgi_hook_list, &hook_entry->entry); + wined3d_mutex_unlock(); + } +} + +static void remove_associated_window_hook(IDXGISwapChain1 *swapchain) +{ + struct dxgi_swapchain_entry *swapchain_entry, *swapchain_entry2; + struct dxgi_hook_entry *hook_entry, *hook_entry2; + BOOL hook_in_use = FALSE; + DWORD thread_id = 0; + + wined3d_mutex_lock(); + LIST_FOR_EACH_ENTRY_SAFE(swapchain_entry, swapchain_entry2, &dxgi_swapchain_list, struct dxgi_swapchain_entry, entry) + { + if (swapchain_entry->swapchain == swapchain) + { + thread_id = swapchain_entry->thread_id; + list_remove(&swapchain_entry->entry); + heap_free(swapchain_entry); + break; + } + } + /* Check if hook is still used by other swapchains */ + LIST_FOR_EACH_ENTRY(swapchain_entry, &dxgi_swapchain_list, struct dxgi_swapchain_entry, entry) + { + if (swapchain_entry->thread_id == thread_id) + { + hook_in_use = TRUE; + break; + } + } + /* Remove hook if it's not longer used by any thread */ + if (!hook_in_use) + { + LIST_FOR_EACH_ENTRY_SAFE(hook_entry, hook_entry2, &dxgi_hook_list, struct dxgi_hook_entry, entry) + { + if (hook_entry->thread_id == thread_id) + { + list_remove(&hook_entry->entry); + UnhookWindowsHookEx(hook_entry->hook); + heap_free(hook_entry); + break; + } + } + } + wined3d_mutex_unlock(); +} + static DXGI_SWAP_EFFECT dxgi_swap_effect_from_wined3d(enum wined3d_swap_effect swap_effect) { switch (swap_effect) @@ -214,6 +367,9 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) if (!refcount) { IWineDXGIDevice *device = swapchain->device; + + remove_associated_window_hook(iface); + if (swapchain->target) { WARN("Releasing fullscreen swapchain.\n"); @@ -842,8 +998,11 @@ HRESULT d3d11_swapchain_init(struct d3d11_swapchain *swapchain, struct dxgi_devi goto cleanup; } } + wined3d_mutex_unlock();
+ add_associated_window_hook(&swapchain->IDXGISwapChain1_iface, desc->device_window); + return S_OK;
cleanup: @@ -1770,6 +1929,8 @@ static void d3d12_swapchain_destroy(struct d3d12_swapchain *swapchain) const struct dxgi_vk_funcs *vk_funcs = &swapchain->vk_funcs; void *vulkan_module = vk_funcs->vulkan_module;
+ remove_associated_window_hook((IDXGISwapChain1*)&swapchain->IDXGISwapChain3_iface); + d3d12_swapchain_destroy_buffers(swapchain, TRUE);
if (swapchain->command_queue) @@ -2731,6 +2892,8 @@ static HRESULT d3d12_swapchain_init(struct d3d12_swapchain *swapchain, IWineDXGI
IWineDXGIFactory_AddRef(swapchain->factory = factory);
+ add_associated_window_hook((IDXGISwapChain1*)&swapchain->IDXGISwapChain3_iface, window); + return S_OK; }