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+
34117const WCHAR szTaskSwitchWndClass[] = L" MSTaskSwWClass" ;
35118const 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