Skip to content

Commit 4e7702a

Browse files
authored
[EXPLORER] Add fullscreen handling (reactos#8105)
Adds support for handling fullscreen applications (a.k.a. “rude apps”) so the taskbar can hide or show appropriately. JIRA issue: CORE-11242, CORE-15681, CORE-16056, CORE-16063, CORE-16131, CORE-16132, CORE-16192, CORE-16196, CORE-16249, CORE-16290, CORE-16313, CORE-16320, CORE-16322, CORE-16347, CORE-16584, CORE-19795, CORE-9862, CORE-16230, CORE-10738, and CORE-12263. - Removes legacy pulse/timer logic in the tray window and adds a fullscreen notification workflow. - Implements fullscreen detection across monitors in the task switcher, using a sequence of validation timers. - Defines a new ITrayWindow:: NotifyFullScreenToAppBars interface method to broadcast fullscreen state to appbars.
1 parent 7ec3a7e commit 4e7702a

3 files changed

Lines changed: 258 additions & 77 deletions

File tree

base/shell/explorer/precomp.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ HRESULT WINAPI _CBandSite_CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void
133133
#define TWM_OPENSTARTMENU (WM_USER + 260)
134134
#define TWM_SETTINGSCHANGED (WM_USER + 300)
135135
#define TWM_SETZORDER (WM_USER + 338)
136-
#define TWM_PULSE (WM_USER + 400)
137136

138137
extern const GUID IID_IShellDesktopTray;
139138

@@ -154,6 +153,7 @@ DECLARE_INTERFACE_(ITrayWindow, IUnknown)
154153
STDMETHOD_(BOOL, ExecContextMenuCmd) (THIS_ UINT uiCmd) PURE;
155154
STDMETHOD_(BOOL, Lock) (THIS_ BOOL bLock) PURE;
156155
STDMETHOD_(BOOL, IsTaskWnd) (THIS_ HWND hWnd) PURE;
156+
STDMETHOD_(HRESULT, NotifyFullScreenToAppBars)(THIS_ HMONITOR hMonitor, BOOL bFullOpening) PURE;
157157
};
158158
#undef INTERFACE
159159

@@ -172,6 +172,7 @@ DECLARE_INTERFACE_(ITrayWindow, IUnknown)
172172
#define ITrayWindow_ExecContextMenuCmd(p,a) (p)->lpVtbl->ExecContextMenuCmd(p,a)
173173
#define ITrayWindow_Lock(p,a) (p)->lpVtbl->Lock(p,a)
174174
#define ITrayWindow_IsTaskWnd(p,a) (p)->lpVtbl->IsTaskWnd(p,a)
175+
#define ITrayWindow_NotifyFullScreenToAppBars(p,a,b) (p)->lpVtbl->NotifyFullScreenToAppBars(p,a,b)
175176
#endif
176177

177178
HRESULT CreateTrayWindow(ITrayWindow ** ppTray);

base/shell/explorer/taskswnd.cpp

Lines changed: 239 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,89 @@
3131
#define MAX_TASKS_COUNT (0x7FFF)
3232
#define TASK_ITEM_ARRAY_ALLOC 64
3333

34+
//************************************************************************
35+
// Fullscreen windows (a.k.a. rude apps) checker
36+
37+
#define TIMER_ID_VALIDATE_RUDE_APP 5
38+
#define VALIDATE_RUDE_INTERVAL 1000
39+
#define VALIDATE_RUDE_MAX_COUNT 5
40+
41+
static BOOL
42+
SHELL_GetMonitorRect(
43+
_In_opt_ HMONITOR hMonitor,
44+
_Out_opt_ PRECT prcDest,
45+
_In_ BOOL bWorkAreaOnly)
46+
{
47+
MONITORINFO mi = { sizeof(mi) };
48+
if (!hMonitor || !::GetMonitorInfoW(hMonitor, &mi))
49+
{
50+
if (!prcDest)
51+
return FALSE;
52+
53+
if (bWorkAreaOnly)
54+
::SystemParametersInfoW(SPI_GETWORKAREA, 0, prcDest, 0);
55+
else
56+
::SetRect(prcDest, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
57+
58+
return FALSE;
59+
}
60+
61+
if (prcDest)
62+
*prcDest = (bWorkAreaOnly ? mi.rcWork : mi.rcMonitor);
63+
return TRUE;
64+
}
65+
66+
static BOOL
67+
SHELL_IsParentOwnerOrSelf(_In_ HWND hwndTarget, _In_ HWND hWnd)
68+
{
69+
for (; hWnd; hWnd = ::GetParent(hWnd))
70+
{
71+
if (hWnd == hwndTarget)
72+
return TRUE;
73+
}
74+
return FALSE;
75+
}
76+
77+
static BOOL
78+
SHELL_IsRudeWindowActive(_In_ HWND hWnd)
79+
{
80+
HWND hwndFore = ::GetForegroundWindow();
81+
DWORD dwThreadId = ::GetWindowThreadProcessId(hWnd, NULL);
82+
return dwThreadId == ::GetWindowThreadProcessId(hwndFore, NULL) ||
83+
SHELL_IsParentOwnerOrSelf(hWnd, hwndFore);
84+
}
85+
86+
static BOOL
87+
SHELL_IsRudeWindow(_In_opt_ HMONITOR hMonitor, _In_ HWND hWnd, _In_ BOOL bDontCheckActive)
88+
{
89+
if (!::IsWindowVisible(hWnd) || hWnd == ::GetDesktopWindow())
90+
return FALSE;
91+
92+
RECT rcMonitor;
93+
SHELL_GetMonitorRect(hMonitor, &rcMonitor, FALSE);
94+
95+
DWORD style = ::GetWindowLongPtrW(hWnd, GWL_STYLE);
96+
97+
RECT rcWnd;
98+
enum { CHECK_STYLE = WS_THICKFRAME | WS_DLGFRAME | WS_BORDER };
99+
if ((style & CHECK_STYLE) == CHECK_STYLE)
100+
{
101+
::GetClientRect(hWnd, &rcWnd); // Ignore frame
102+
::MapWindowPoints(hWnd, NULL, (PPOINT)&rcWnd, sizeof(RECT) / sizeof(POINT));
103+
}
104+
else
105+
{
106+
::GetWindowRect(hWnd, &rcWnd);
107+
}
108+
109+
RECT rcUnion;
110+
::UnionRect(&rcUnion, &rcWnd, &rcMonitor);
111+
112+
return ::EqualRect(&rcUnion, &rcWnd) && (bDontCheckActive || SHELL_IsRudeWindowActive(hWnd));
113+
}
114+
115+
////////////////////////////////////////////////////////////////
116+
34117
const WCHAR szTaskSwitchWndClass[] = L"MSTaskSwWClass";
35118
const WCHAR szRunningApps[] = L"Running Applications";
36119

@@ -322,6 +405,8 @@ class CTaskSwitchWnd :
322405
BOOL m_IsGroupingEnabled;
323406
BOOL m_IsDestroying;
324407

408+
INT m_nRudeAppValidationCounter;
409+
325410
SIZE m_ButtonSize;
326411

327412
UINT m_uHardErrorMsg;
@@ -340,7 +425,8 @@ class CTaskSwitchWnd :
340425
m_ButtonCount(0),
341426
m_ImageList(NULL),
342427
m_IsGroupingEnabled(FALSE),
343-
m_IsDestroying(FALSE)
428+
m_IsDestroying(FALSE),
429+
m_nRudeAppValidationCounter(0)
344430
{
345431
ZeroMemory(&m_ButtonSize, sizeof(m_ButtonSize));
346432
m_uHardErrorMsg = RegisterWindowMessageW(L"HardError");
@@ -1464,6 +1550,8 @@ class CTaskSwitchWnd :
14641550
{
14651551
m_IsDestroying = TRUE;
14661552

1553+
KillTimer(TIMER_ID_VALIDATE_RUDE_APP);
1554+
14671555
/* Unregister the shell hook */
14681556
RegisterShellHook(m_hWnd, FALSE);
14691557

@@ -1472,12 +1560,6 @@ class CTaskSwitchWnd :
14721560
return TRUE;
14731561
}
14741562

1475-
VOID SendPulseToTray(BOOL bDelete, HWND hwndActive)
1476-
{
1477-
HWND hwndTray = m_Tray->GetHWND();
1478-
::SendMessage(hwndTray, TWM_PULSE, bDelete, (LPARAM)hwndActive);
1479-
}
1480-
14811563
static BOOL InvokeRegistryAppKeyCommand(UINT uAppCmd)
14821564
{
14831565
BOOL bResult = FALSE;
@@ -1554,19 +1636,18 @@ class CTaskSwitchWnd :
15541636
break;
15551637

15561638
case HSHELL_WINDOWCREATED:
1557-
SendPulseToTray(FALSE, (HWND)lParam);
15581639
AddTask((HWND) lParam);
15591640
break;
15601641

15611642
case HSHELL_WINDOWDESTROYED:
15621643
/* The window still exists! Delay destroying it a bit */
1563-
SendPulseToTray(TRUE, (HWND)lParam);
1644+
OnWindowDestroyed((HWND)lParam);
15641645
DeleteTask((HWND)lParam);
15651646
break;
15661647

15671648
case HSHELL_RUDEAPPACTIVATED:
15681649
case HSHELL_WINDOWACTIVATED:
1569-
SendPulseToTray(FALSE, (HWND)lParam);
1650+
OnWindowActivated((HWND)lParam);
15701651
ActivateTask((HWND)lParam);
15711652
break;
15721653

@@ -1674,7 +1755,6 @@ class CTaskSwitchWnd :
16741755
TaskItem = FindTaskItemByIndex((INT) wIndex);
16751756
if (TaskItem != NULL)
16761757
{
1677-
SendPulseToTray(FALSE, TaskItem->hWnd);
16781758
HandleTaskItemClick(TaskItem);
16791759
return TRUE;
16801760
}
@@ -1810,6 +1890,130 @@ class CTaskSwitchWnd :
18101890
return Ret;
18111891
}
18121892

1893+
// Internal structure for IsRudeEnumProc
1894+
typedef struct tagRUDEAPPDATA
1895+
{
1896+
HMONITOR hTargetMonitor;
1897+
HWND hwndFound;
1898+
HWND hwndFirstCheck;
1899+
} RUDEAPPDATA, *PRUDEAPPDATA;
1900+
1901+
// Find any rude app
1902+
static BOOL CALLBACK
1903+
IsRudeEnumProc(_In_ HWND hwnd, _In_ LPARAM lParam)
1904+
{
1905+
PRUDEAPPDATA pData = (PRUDEAPPDATA)lParam;
1906+
1907+
HMONITOR hMon = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
1908+
if (!hMon ||
1909+
(pData->hTargetMonitor && pData->hTargetMonitor != hMon) ||
1910+
!SHELL_IsRudeWindow(hMon, hwnd, (hwnd == pData->hwndFirstCheck)))
1911+
{
1912+
return TRUE; // Continue
1913+
}
1914+
1915+
pData->hwndFound = hwnd;
1916+
return FALSE; // Finish
1917+
}
1918+
1919+
// Internal structure for FullScreenEnumProc
1920+
typedef struct tagFULLSCREENDATA
1921+
{
1922+
const RECT *pRect;
1923+
HMONITOR hTargetMonitor;
1924+
ITrayWindow *pTray;
1925+
} FULLSCREENDATA, *PFULLSCREENDATA;
1926+
1927+
// Notify ABN_FULLSCREENAPP for each monitor
1928+
static BOOL CALLBACK
1929+
FullScreenEnumProc(_In_ HMONITOR hMonitor, _In_opt_ HDC hDC, _In_ LPRECT prc, _In_ LPARAM lParam)
1930+
{
1931+
PFULLSCREENDATA pData = (PFULLSCREENDATA)lParam;
1932+
1933+
BOOL bFullOpening = (pData->hTargetMonitor == hMonitor);
1934+
if (!bFullOpening && pData->pRect)
1935+
{
1936+
RECT rc, rcMon;
1937+
SHELL_GetMonitorRect(hMonitor, &rcMon, FALSE);
1938+
::IntersectRect(&rc, &rcMon, pData->pRect);
1939+
bFullOpening = ::EqualRect(&rc, &rcMon);
1940+
}
1941+
1942+
// Notify ABN_FULLSCREENAPP to appbars
1943+
pData->pTray->NotifyFullScreenToAppBars(hMonitor, bFullOpening);
1944+
return TRUE;
1945+
}
1946+
1947+
void HandleFullScreenApp(_In_opt_ HWND hwndRude)
1948+
{
1949+
// Notify ABN_FULLSCREENAPP for every monitor
1950+
RECT rc;
1951+
FULLSCREENDATA Data = { NULL, NULL, NULL };
1952+
if (hwndRude && ::GetWindowRect(hwndRude, &rc))
1953+
{
1954+
Data.pRect = &rc;
1955+
Data.hTargetMonitor = ::MonitorFromWindow(hwndRude, MONITOR_DEFAULTTONULL);
1956+
}
1957+
Data.pTray = m_Tray;
1958+
::EnumDisplayMonitors(NULL, NULL, FullScreenEnumProc, (LPARAM)&Data);
1959+
1960+
// Make the taskbar bottom or top
1961+
UINT uFlags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER;
1962+
HWND hwndTray = m_Tray->GetHWND();
1963+
::SetWindowPos(hwndTray, (hwndRude ? HWND_BOTTOM : HWND_TOP), 0, 0, 0, 0, uFlags);
1964+
1965+
if (hwndRude)
1966+
{
1967+
DWORD exstyle = (DWORD)::GetWindowLongPtrW(hwndRude, GWL_EXSTYLE);
1968+
if (!(exstyle & WS_EX_TOPMOST) && !SHELL_IsRudeWindowActive(hwndRude))
1969+
::SwitchToThisWindow(hwndRude, TRUE);
1970+
}
1971+
1972+
// FIXME: NIN_BALLOONHIDE
1973+
// FIXME: NIN_POPUPCLOSE
1974+
}
1975+
1976+
HWND FindRudeApp(_In_opt_ HWND hwndFirstCheck)
1977+
{
1978+
// Quick check
1979+
HMONITOR hMon = MonitorFromWindow(hwndFirstCheck, MONITOR_DEFAULTTONEAREST);
1980+
RUDEAPPDATA data = { hMon, NULL, hwndFirstCheck };
1981+
if (::IsWindow(hwndFirstCheck) && !IsRudeEnumProc(hwndFirstCheck, (LPARAM)&data))
1982+
return hwndFirstCheck;
1983+
1984+
// Slow check
1985+
::EnumWindows(IsRudeEnumProc, (LPARAM)&data);
1986+
1987+
return data.hwndFound;
1988+
}
1989+
1990+
// WM_WINDOWPOSCHANGED
1991+
LRESULT OnWindowPosChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1992+
{
1993+
// Re-start rude app validation
1994+
KillTimer(TIMER_ID_VALIDATE_RUDE_APP);
1995+
SetTimer(TIMER_ID_VALIDATE_RUDE_APP, VALIDATE_RUDE_INTERVAL, NULL);
1996+
m_nRudeAppValidationCounter = 0;
1997+
bHandled = FALSE;
1998+
return 0;
1999+
}
2000+
2001+
// HSHELL_WINDOWACTIVATED, HSHELL_RUDEAPPACTIVATED
2002+
void OnWindowActivated(_In_ HWND hwndTarget)
2003+
{
2004+
// Re-start rude app validation
2005+
KillTimer(TIMER_ID_VALIDATE_RUDE_APP);
2006+
SetTimer(TIMER_ID_VALIDATE_RUDE_APP, VALIDATE_RUDE_INTERVAL, NULL);
2007+
m_nRudeAppValidationCounter = 0;
2008+
}
2009+
2010+
// HSHELL_WINDOWDESTROYED
2011+
void OnWindowDestroyed(_In_ HWND hwndTarget)
2012+
{
2013+
HWND hwndRude = FindRudeApp(hwndTarget);
2014+
HandleFullScreenApp(hwndRude);
2015+
}
2016+
18132017
LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
18142018
{
18152019
HDC hdc = (HDC) wParam;
@@ -1963,14 +2167,32 @@ class CTaskSwitchWnd :
19632167

19642168
LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
19652169
{
1966-
#if DUMP_TASKS != 0
19672170
switch (wParam)
19682171
{
1969-
case 1:
1970-
DumpTasks();
1971-
break;
1972-
}
2172+
#if DUMP_TASKS != 0
2173+
case 1:
2174+
DumpTasks();
2175+
break;
19732176
#endif
2177+
case TIMER_ID_VALIDATE_RUDE_APP:
2178+
{
2179+
// Real activation of rude app might take some time after HSHELL_...ACTIVATED.
2180+
// Wait up to 5 seconds with validating the rude app at each second.
2181+
HWND hwndRude = FindRudeApp(NULL);
2182+
HandleFullScreenApp(hwndRude);
2183+
2184+
KillTimer(wParam);
2185+
++m_nRudeAppValidationCounter;
2186+
if (m_nRudeAppValidationCounter < VALIDATE_RUDE_MAX_COUNT && !hwndRude)
2187+
SetTimer(wParam, VALIDATE_RUDE_INTERVAL, NULL);
2188+
break;
2189+
}
2190+
default:
2191+
{
2192+
WARN("Unknown timer ID: %p\n", wParam);
2193+
break;
2194+
}
2195+
}
19742196
return TRUE;
19752197
}
19762198

@@ -2054,6 +2276,7 @@ class CTaskSwitchWnd :
20542276
MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate)
20552277
MESSAGE_HANDLER(WM_KLUDGEMINRECT, OnKludgeItemRect)
20562278
MESSAGE_HANDLER(WM_COPYDATA, OnCopyData)
2279+
MESSAGE_HANDLER(WM_WINDOWPOSCHANGED, OnWindowPosChanged)
20572280
END_MSG_MAP()
20582281

20592282
DECLARE_NOT_AGGREGATABLE(CTaskSwitchWnd)

0 commit comments

Comments
 (0)