Signed-off-by: Zhiyi Zhang <zzhang(a)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 53da2081e8..9f8539fd28 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;
}
--
2.20.1