From: Zhiyi Zhang Subject: [PATCH 5/5] dxgi: Implement fullscreen window occlusion detection. Message-Id: Date: Tue, 23 Apr 2019 20:59:18 +0800 When Windows detected a fullscreen window occlusion, it will set the fullscreen window to windowed and return DXGI_STATUS_OCCLUDED in IDXGISwapChain::Present. Signed-off-by: Zhiyi Zhang --- dlls/dxgi/swapchain.c | 64 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c index 9f8539fd28..d1df1ad726 100644 --- a/dlls/dxgi/swapchain.c +++ b/dlls/dxgi/swapchain.c @@ -265,6 +265,42 @@ BOOL dxgi_validate_swapchain_desc(const DXGI_SWAP_CHAIN_DESC1 *desc) return TRUE; } +/* A correct occlusion detection method must consider following scenarios: + * + * 1. On multiple monitors system. For example, the full-screen is on primary display, any window on + * on secondary monitor shouldn't be able to occlude the full-screen window, regardless being foreground or active. + * 2. A non-Wine window, e.g., a X window occludes the full-screen window should be detected. + * + * Current method will incorrectly report occluded status where a window on a secondary monitor becomes foreground, + * which shouldn't be too harmful though. The actual implementation on Window probably has help from DWM. + * + * X11 VisibilityNotify is not useful here. The events doesn't work as expected. + */ +static BOOL dxgi_is_window_occluded(HWND hwnd) +{ + RECT fullscreen_rect; + RECT intersect_rect; + RECT window_rect; + HWND previous_hwnd; + + if (GetForegroundWindow() != hwnd) + return TRUE; + + /* Check if windows with higher z-order intersect with the full-screen window */ + GetWindowRect(hwnd, &fullscreen_rect); + previous_hwnd = GetWindow(hwnd, GW_HWNDPREV); + while (previous_hwnd) + { + GetWindowRect(previous_hwnd, &window_rect); + if (IntersectRect(&intersect_rect, &window_rect, &fullscreen_rect)) + return TRUE; + + previous_hwnd = GetWindow(previous_hwnd, GW_HWNDPREV); + } + + return FALSE; +} + static HRESULT dxgi_get_output_from_window(IDXGIAdapter *adapter, HWND window, IDXGIOutput **dxgi_output) { DXGI_OUTPUT_DESC desc; @@ -456,12 +492,27 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, unsigned int sync_interval, unsigned int flags) { + BOOL fullscreen = FALSE; + HRESULT hr; + HWND hwnd; + if (sync_interval > 4) { WARN("Invalid sync interval %u.\n", sync_interval); return DXGI_ERROR_INVALID_CALL; } + hr = IDXGISwapChain1_GetFullscreenState(&swapchain->IDXGISwapChain1_iface, &fullscreen, NULL); + if (SUCCEEDED(hr) && fullscreen) + { + hr = IDXGISwapChain1_GetHwnd(&swapchain->IDXGISwapChain1_iface, &hwnd); + if (SUCCEEDED(hr) && dxgi_is_window_occluded(hwnd)) + { + IDXGISwapChain1_SetFullscreenState(&swapchain->IDXGISwapChain1_iface, FALSE, NULL); + return DXGI_STATUS_OCCLUDED; + } + } + if (flags & ~DXGI_PRESENT_TEST) FIXME("Unimplemented flags %#x.\n", flags); if (flags & DXGI_PRESENT_TEST) @@ -2118,8 +2169,10 @@ static VkResult d3d12_swapchain_queue_present(struct d3d12_swapchain *swapchain, static HRESULT d3d12_swapchain_present(struct d3d12_swapchain *swapchain, unsigned int sync_interval, unsigned int flags) { + BOOL fullscreen = FALSE; VkQueue vk_queue; VkResult vr; + HWND hwnd; HRESULT hr; if (sync_interval > 4) @@ -2128,6 +2181,17 @@ static HRESULT d3d12_swapchain_present(struct d3d12_swapchain *swapchain, return DXGI_ERROR_INVALID_CALL; } + hr = IDXGISwapChain3_GetFullscreenState(&swapchain->IDXGISwapChain3_iface, &fullscreen, NULL); + if (SUCCEEDED(hr) && fullscreen) + { + hr = IDXGISwapChain3_GetHwnd(&swapchain->IDXGISwapChain3_iface, &hwnd); + if (SUCCEEDED(hr) && dxgi_is_window_occluded(hwnd)) + { + IDXGISwapChain3_SetFullscreenState(&swapchain->IDXGISwapChain3_iface, FALSE, NULL); + return DXGI_STATUS_OCCLUDED; + } + } + if (flags & ~DXGI_PRESENT_TEST) FIXME("Unimplemented flags %#x.\n", flags); if (flags & DXGI_PRESENT_TEST) -- 2.20.1