diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c index 6912fc180ea..a0038b4fe77 100644 --- a/dlls/dxgi/factory.c +++ b/dlls/dxgi/factory.c @@ -174,7 +174,21 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_EnumAdapters(IWineDXGIFactory *ifa static HRESULT STDMETHODCALLTYPE dxgi_factory_MakeWindowAssociation(IWineDXGIFactory *iface, HWND window, UINT flags) { - FIXME("iface %p, window %p, flags %#x stub!\n", iface, window, flags); + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + + TRACE("iface %p, window %p, flags %#x.\n", iface, window, flags); + + if (flags > DXGI_MWA_VALID) + return DXGI_ERROR_INVALID_CALL; + + if (!window) + { + wined3d_unregister_windows(factory->wined3d); + return S_OK; + } + + if (!wined3d_register_window(factory->wined3d, window, NULL, flags)) + return E_FAIL; return S_OK; } diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index d0581223a6a..bff894773a5 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -5086,7 +5086,7 @@ static void test_window_association(void) } hr = IDXGIFactory_MakeWindowAssociation(factory, swapchain_desc.OutputWindow, DXGI_MWA_VALID + 1); - todo_wine ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr); + ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr); /* Alt+Enter tests. */ hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown *)device, &swapchain_desc, &swapchain); @@ -5134,10 +5134,9 @@ static void test_window_association(void) flush_events(); hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr); - todo_wine_if(tests[i].expect_fullscreen) - ok(fullscreen == tests[i].expect_fullscreen - || broken(tests[i].broken_d3d10 && fullscreen), - "Test %u: Got unexpected fullscreen %#x.\n", i, fullscreen); + ok(fullscreen == tests[i].expect_fullscreen + || broken(tests[i].broken_d3d10 && fullscreen), + "Test %u: Got unexpected fullscreen %#x.\n", i, fullscreen); wndproc = GetWindowLongPtrW(swapchain_desc.OutputWindow, GWLP_WNDPROC); ok(wndproc == original_wndproc, "Text %u: Got unexpected wndproc %#lx, expected %#lx.\n", diff --git a/dlls/dxgi/utils.c b/dlls/dxgi/utils.c index 23f5188b011..be94bde8478 100644 --- a/dlls/dxgi/utils.c +++ b/dlls/dxgi/utils.c @@ -511,7 +511,7 @@ unsigned int wined3d_bind_flags_from_dxgi_usage(DXGI_USAGE dxgi_usage) } #define DXGI_WINED3D_SWAPCHAIN_FLAGS \ - (WINED3D_SWAPCHAIN_USE_CLOSEST_MATCHING_MODE | WINED3D_SWAPCHAIN_RESTORE_WINDOW_RECT) + (WINED3D_SWAPCHAIN_USE_CLOSEST_MATCHING_MODE | WINED3D_SWAPCHAIN_RESTORE_WINDOW_RECT | WINED3D_SWAPCHAIN_HOOK) unsigned int dxgi_swapchain_flags_from_wined3d(unsigned int wined3d_flags) { diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c index 09e3da31854..81b5a3ab620 100644 --- a/dlls/wined3d/device.c +++ b/dlls/wined3d/device.c @@ -1015,7 +1015,7 @@ HRESULT CDECL wined3d_device_acquire_focus_window(struct wined3d_device *device, { TRACE("device %p, window %p.\n", device, window); - if (!wined3d_register_window(window, device)) + if (!wined3d_register_window(NULL, window, device, 0)) { ERR("Failed to register window %p.\n", window); return E_FAIL; diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c index e4882bd4007..780734112fe 100644 --- a/dlls/wined3d/swapchain.c +++ b/dlls/wined3d/swapchain.c @@ -121,6 +121,7 @@ ULONG CDECL wined3d_swapchain_decref(struct wined3d_swapchain *swapchain) device = swapchain->device; if (device->swapchain_count && device->swapchains[0] == swapchain) wined3d_device_uninit_3d(device); + wined3d_unhook_swapchain(swapchain); wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); swapchain_cleanup(swapchain); @@ -1037,6 +1038,9 @@ HRESULT CDECL wined3d_swapchain_create(struct wined3d_device *device, struct win return hr; } + if (desc->flags & WINED3D_SWAPCHAIN_HOOK) + wined3d_hook_swapchain(object); + if (desc->flags & WINED3D_SWAPCHAIN_IMPLICIT) { wined3d_mutex_lock(); diff --git a/dlls/wined3d/wined3d.spec b/dlls/wined3d/wined3d.spec index 040b9fc4ac1..59f99c91e07 100644 --- a/dlls/wined3d/wined3d.spec +++ b/dlls/wined3d/wined3d.spec @@ -20,7 +20,9 @@ @ cdecl wined3d_get_output_desc(ptr long ptr) @ cdecl wined3d_incref(ptr) @ cdecl wined3d_register_software_device(ptr ptr) +@ cdecl wined3d_register_window(ptr ptr ptr long) @ cdecl wined3d_set_adapter_display_mode(ptr long ptr) +@ cdecl wined3d_unregister_windows(ptr) @ cdecl wined3d_blend_state_create(ptr ptr ptr ptr ptr) @ cdecl wined3d_blend_state_decref(ptr) diff --git a/dlls/wined3d/wined3d_main.c b/dlls/wined3d/wined3d_main.c index 180e2d262e2..716a2b088c8 100644 --- a/dlls/wined3d/wined3d_main.c +++ b/dlls/wined3d/wined3d_main.c @@ -33,20 +33,47 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag); struct wined3d_wndproc { + struct wined3d *wined3d; HWND window; BOOL unicode; WNDPROC proc; struct wined3d_device *device; + uint32_t flags; }; struct wined3d_wndproc_table { struct wined3d_wndproc *entries; - unsigned int count; + SIZE_T count; SIZE_T size; }; +struct wined3d_window_hook +{ + HHOOK hook; + DWORD thread_id; + unsigned int count; +}; + +struct wined3d_hooked_swapchain +{ + struct wined3d_swapchain *swapchain; + DWORD thread_id; +}; + +struct wined3d_hook_table +{ + struct wined3d_window_hook *hooks; + SIZE_T hooks_size; + SIZE_T hook_count; + + struct wined3d_hooked_swapchain *swapchains; + SIZE_T swapchains_size; + SIZE_T swapchain_count; +}; + static struct wined3d_wndproc_table wndproc_table; +static struct wined3d_hook_table hook_table; static CRITICAL_SECTION wined3d_cs; static CRITICAL_SECTION_DEBUG wined3d_cs_debug = @@ -396,6 +423,14 @@ static BOOL wined3d_dll_destroy(HINSTANCE hInstDLL) } heap_free(wndproc_table.entries); + heap_free(hook_table.swapchains); + for (i = 0; i < hook_table.hook_count; ++i) + { + WARN("Leftover hook table entry %p.\n", &hook_table.hooks[i]); + UnhookWindowsHookEx(hook_table.hooks[i].hook); + } + heap_free(hook_table.hooks); + heap_free(wined3d_settings.logo); UnregisterClassA(WINED3D_OPENGL_WINDOW_CLASS_NAME, hInstDLL); @@ -424,16 +459,16 @@ static void wined3d_wndproc_mutex_unlock(void) LeaveCriticalSection(&wined3d_wndproc_cs); } -static struct wined3d_wndproc *wined3d_find_wndproc(HWND window) +static struct wined3d_wndproc *wined3d_find_wndproc(HWND window, struct wined3d *wined3d) { unsigned int i; for (i = 0; i < wndproc_table.count; ++i) { - if (wndproc_table.entries[i].window == window) - { - return &wndproc_table.entries[i]; - } + struct wined3d_wndproc *entry = &wndproc_table.entries[i]; + + if (entry->window == window && entry->wined3d == wined3d) + return entry; } return NULL; @@ -447,9 +482,8 @@ static LRESULT CALLBACK wined3d_wndproc(HWND window, UINT message, WPARAM wparam WNDPROC proc; wined3d_wndproc_mutex_lock(); - entry = wined3d_find_wndproc(window); - if (!entry) + if (!(entry = wined3d_find_wndproc(window, NULL))) { wined3d_wndproc_mutex_unlock(); ERR("Window %p is not registered with wined3d.\n", window); @@ -468,16 +502,62 @@ static LRESULT CALLBACK wined3d_wndproc(HWND window, UINT message, WPARAM wparam return CallWindowProcA(proc, window, message, wparam, lparam); } -BOOL wined3d_register_window(HWND window, struct wined3d_device *device) +static LRESULT CALLBACK wined3d_hook_proc(int code, WPARAM wparam, LPARAM lparam) +{ + struct wined3d_swapchain_desc swapchain_desc; + struct wined3d_swapchain *swapchain; + struct wined3d_wndproc *entry; + MSG *msg = (MSG *)lparam; + unsigned int i; + + /* Handle Alt+Enter. */ + if (code == HC_ACTION && msg->message == WM_SYSKEYDOWN + && msg->wParam == VK_RETURN && (msg->lParam & (KF_ALTDOWN << 16))) + { + wined3d_wndproc_mutex_lock(); + + for (i = 0; i < hook_table.swapchain_count; ++i) + { + swapchain = hook_table.swapchains[i].swapchain; + + if (swapchain->device_window != msg->hwnd) + continue; + + if ((entry = wined3d_find_wndproc(msg->hwnd, swapchain->device->wined3d)) + && (entry->flags & (WINED3D_REGISTER_WINDOW_NO_WINDOW_CHANGES + | WINED3D_REGISTER_WINDOW_NO_ALT_ENTER))) + continue; + + wined3d_swapchain_get_desc(swapchain, &swapchain_desc); + swapchain_desc.windowed = !swapchain_desc.windowed; + wined3d_swapchain_set_fullscreen(swapchain, &swapchain_desc, NULL); + + wined3d_wndproc_mutex_unlock(); + + return 1; + } + + wined3d_wndproc_mutex_unlock(); + } + + return CallNextHookEx(0, code, wparam, lparam); +} + +BOOL CDECL wined3d_register_window(struct wined3d *wined3d, HWND window, + struct wined3d_device *device, unsigned int flags) { struct wined3d_wndproc *entry; + TRACE("wined3d %p, window %p, device %p, flags %#x.\n", wined3d, window, device, flags); + wined3d_wndproc_mutex_lock(); - if (wined3d_find_wndproc(window)) + if ((entry = wined3d_find_wndproc(window, wined3d))) { + if (!wined3d) + WARN("Window %p is already registered with wined3d.\n", window); + entry->flags = flags; wined3d_wndproc_mutex_unlock(); - WARN("Window %p is already registered with wined3d.\n", window); return TRUE; } @@ -492,61 +572,71 @@ BOOL wined3d_register_window(HWND window, struct wined3d_device *device) entry = &wndproc_table.entries[wndproc_table.count++]; entry->window = window; entry->unicode = IsWindowUnicode(window); - /* Set a window proc that matches the window. Some applications (e.g. NoX) - * replace the window proc after we've set ours, and expect to be able to - * call the previous one (ours) directly, without using CallWindowProc(). */ - if (entry->unicode) - entry->proc = (WNDPROC)SetWindowLongPtrW(window, GWLP_WNDPROC, (LONG_PTR)wined3d_wndproc); + if (!wined3d) + { + /* Set a window proc that matches the window. Some applications (e.g. + * NoX) replace the window proc after we've set ours, and expect to be + * able to call the previous one (ours) directly, without using + * CallWindowProc(). */ + if (entry->unicode) + entry->proc = (WNDPROC)SetWindowLongPtrW(window, GWLP_WNDPROC, (LONG_PTR)wined3d_wndproc); + else + entry->proc = (WNDPROC)SetWindowLongPtrA(window, GWLP_WNDPROC, (LONG_PTR)wined3d_wndproc); + } else - entry->proc = (WNDPROC)SetWindowLongPtrA(window, GWLP_WNDPROC, (LONG_PTR)wined3d_wndproc); + { + entry->proc = NULL; + } entry->device = device; + entry->wined3d = wined3d; + entry->flags = flags; wined3d_wndproc_mutex_unlock(); return TRUE; } +static BOOL restore_wndproc(struct wined3d_wndproc *entry) +{ + LONG_PTR proc; + + if (entry->unicode) + { + proc = GetWindowLongPtrW(entry->window, GWLP_WNDPROC); + if (proc != (LONG_PTR)wined3d_wndproc) + return FALSE; + SetWindowLongPtrW(entry->window, GWLP_WNDPROC, (LONG_PTR)entry->proc); + } + else + { + proc = GetWindowLongPtrA(entry->window, GWLP_WNDPROC); + if (proc != (LONG_PTR)wined3d_wndproc) + return FALSE; + SetWindowLongPtrA(entry->window, GWLP_WNDPROC, (LONG_PTR)entry->proc); + } + + return TRUE; +} + void wined3d_unregister_window(HWND window) { struct wined3d_wndproc *entry, *last; - LONG_PTR proc; wined3d_wndproc_mutex_lock(); - if (!(entry = wined3d_find_wndproc(window))) + if (!(entry = wined3d_find_wndproc(window, NULL))) { wined3d_wndproc_mutex_unlock(); ERR("Window %p is not registered with wined3d.\n", window); return; } - if (entry->unicode) + if (entry->proc && !restore_wndproc(entry)) { - proc = GetWindowLongPtrW(window, GWLP_WNDPROC); - if (proc != (LONG_PTR)wined3d_wndproc) - { - entry->device = NULL; - wined3d_wndproc_mutex_unlock(); - WARN("Not unregistering window %p, window proc %#lx doesn't match wined3d window proc %p.\n", - window, proc, wined3d_wndproc); - return; - } - - SetWindowLongPtrW(window, GWLP_WNDPROC, (LONG_PTR)entry->proc); - } - else - { - proc = GetWindowLongPtrA(window, GWLP_WNDPROC); - if (proc != (LONG_PTR)wined3d_wndproc) - { - entry->device = NULL; - wined3d_wndproc_mutex_unlock(); - WARN("Not unregistering window %p, window proc %#lx doesn't match wined3d window proc %p.\n", - window, proc, wined3d_wndproc); - return; - } - - SetWindowLongPtrA(window, GWLP_WNDPROC, (LONG_PTR)entry->proc); + entry->device = NULL; + WARN("Not unregistering window %p, current window proc doesn't match wined3d window proc.\n", window); + wined3d_wndproc_mutex_unlock(); + return; } last = &wndproc_table.entries[--wndproc_table.count]; @@ -555,6 +645,131 @@ void wined3d_unregister_window(HWND window) wined3d_wndproc_mutex_unlock(); } +void CDECL wined3d_unregister_windows(struct wined3d *wined3d) +{ + struct wined3d_wndproc *entry, *last; + unsigned int i = 0; + + TRACE("wined3d %p.\n", wined3d); + + wined3d_wndproc_mutex_lock(); + + while (i < wndproc_table.count) + { + entry = &wndproc_table.entries[i]; + + if (entry->wined3d != wined3d) + { + ++i; + continue; + } + + if (entry->proc && !restore_wndproc(entry)) + { + entry->device = NULL; + WARN("Not unregistering window %p, current window proc doesn't match wined3d window proc.\n", + entry->window); + ++i; + continue; + } + + last = &wndproc_table.entries[--wndproc_table.count]; + if (entry != last) + *entry = *last; + else + ++i; + } + + wined3d_wndproc_mutex_unlock(); +} + +static struct wined3d_window_hook *wined3d_find_hook(DWORD thread_id) +{ + unsigned int i; + + for (i = 0; i < hook_table.hook_count; ++i) + { + if (hook_table.hooks[i].thread_id == thread_id) + return &hook_table.hooks[i]; + } + + return NULL; +} + +void wined3d_hook_swapchain(struct wined3d_swapchain *swapchain) +{ + struct wined3d_hooked_swapchain *swapchain_entry; + struct wined3d_window_hook *hook; + + wined3d_wndproc_mutex_lock(); + + if (!wined3d_array_reserve((void **)&hook_table.swapchains, &hook_table.swapchains_size, + hook_table.swapchain_count + 1, sizeof(*swapchain_entry))) + { + wined3d_wndproc_mutex_unlock(); + return; + } + + swapchain_entry = &hook_table.swapchains[hook_table.swapchain_count++]; + swapchain_entry->swapchain = swapchain; + swapchain_entry->thread_id = GetWindowThreadProcessId(swapchain->device_window, NULL); + + if ((hook = wined3d_find_hook(swapchain_entry->thread_id))) + { + ++hook->count; + wined3d_wndproc_mutex_unlock(); + return; + } + + if (!wined3d_array_reserve((void **)&hook_table.hooks, &hook_table.hooks_size, + hook_table.hook_count + 1, sizeof(*hook))) + { + --hook_table.swapchain_count; + wined3d_wndproc_mutex_unlock(); + return; + } + + hook = &hook_table.hooks[hook_table.hook_count++]; + hook->thread_id = swapchain_entry->thread_id; + hook->hook = SetWindowsHookExW(WH_GETMESSAGE, wined3d_hook_proc, 0, hook->thread_id); + hook->count = 1; + + wined3d_wndproc_mutex_unlock(); +} + +void wined3d_unhook_swapchain(struct wined3d_swapchain *swapchain) +{ + struct wined3d_hooked_swapchain *swapchain_entry, *last_swapchain_entry; + struct wined3d_window_hook *hook, *last_hook; + unsigned int i; + + wined3d_wndproc_mutex_lock(); + + for (i = 0; i < hook_table.swapchain_count; ++i) + { + swapchain_entry = &hook_table.swapchains[i]; + + if (swapchain_entry->swapchain != swapchain) + continue; + + if ((hook = wined3d_find_hook(swapchain_entry->thread_id)) && !--hook->count) + { + UnhookWindowsHookEx(hook->hook); + last_hook = &hook_table.hooks[--hook_table.hook_count]; + if (hook != last_hook) + *hook = *last_hook; + } + + last_swapchain_entry = &hook_table.swapchains[--hook_table.swapchain_count]; + if (swapchain_entry != last_swapchain_entry) + *swapchain_entry = *last_swapchain_entry; + + break; + } + + wined3d_wndproc_mutex_unlock(); +} + /* At process attach */ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) { diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index ea18b0794cb..51a4f64d192 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -2978,8 +2978,9 @@ struct wined3d struct wined3d_adapter *adapters[1]; }; +void wined3d_hook_swapchain(struct wined3d_swapchain *swapchain) DECLSPEC_HIDDEN; HRESULT wined3d_init(struct wined3d *wined3d, DWORD flags) DECLSPEC_HIDDEN; -BOOL wined3d_register_window(HWND window, struct wined3d_device *device) DECLSPEC_HIDDEN; +void wined3d_unhook_swapchain(struct wined3d_swapchain *swapchain) DECLSPEC_HIDDEN; void wined3d_unregister_window(HWND window) DECLSPEC_HIDDEN; BOOL wined3d_get_app_name(char *app_name, unsigned int app_name_size) DECLSPEC_HIDDEN; diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h index ada56365505..a0ff3f13354 100644 --- a/include/wine/wined3d.h +++ b/include/wine/wined3d.h @@ -903,6 +903,7 @@ enum wined3d_shader_type #define WINED3D_SWAPCHAIN_RESTORE_WINDOW_RECT 0x00004000u #define WINED3D_SWAPCHAIN_GDI_COMPATIBLE 0x00008000u #define WINED3D_SWAPCHAIN_IMPLICIT 0x00010000u +#define WINED3D_SWAPCHAIN_HOOK 0x00020000u #define WINED3DDP_MAXTEXCOORD 8 @@ -1584,6 +1585,10 @@ enum wined3d_shader_type #define WINED3D_MAX_VIEWPORTS 16 +#define WINED3D_REGISTER_WINDOW_NO_WINDOW_CHANGES 0x00000001u +#define WINED3D_REGISTER_WINDOW_NO_ALT_ENTER 0x00000002u +#define WINED3D_REGISTER_WINDOW_NO_PRINT_SCREEN 0x00000004u + struct wined3d_display_mode { UINT width; @@ -2210,8 +2215,11 @@ HRESULT __cdecl wined3d_get_output_desc(const struct wined3d *wined3d, unsigned struct wined3d_output_desc *desc); ULONG __cdecl wined3d_incref(struct wined3d *wined3d); HRESULT __cdecl wined3d_register_software_device(struct wined3d *wined3d, void *init_function); +BOOL __cdecl wined3d_register_window(struct wined3d *wined3d, HWND window, + struct wined3d_device *device, unsigned int flags); HRESULT __cdecl wined3d_set_adapter_display_mode(struct wined3d *wined3d, UINT adapter_idx, const struct wined3d_display_mode *mode); +void __cdecl wined3d_unregister_windows(struct wined3d *wined3d); HRESULT __cdecl wined3d_buffer_create(struct wined3d_device *device, const struct wined3d_buffer_desc *desc, const struct wined3d_sub_resource_data *data, void *parent, const struct wined3d_parent_ops *parent_ops,