1 /* 2 * Shell Desktop 3 * 4 * Copyright 2008 Thomas Bluemel 5 * Copyright 2020 Katayama Hirofumi MZ 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22 #include "shelldesktop.h" 23 24 // Support for multiple monitors is disabled till LVM_SETWORKAREAS gets implemented 25 #ifdef MULTIMONITOR_SUPPORT 26 #include <atlcoll.h> 27 #endif 28 29 WINE_DEFAULT_DEBUG_CHANNEL(desktop); 30 31 static const WCHAR szProgmanClassName[] = L"Progman"; 32 static const WCHAR szProgmanWindowName[] = L"Program Manager"; 33 34 class CDesktopBrowser : 35 public CWindowImpl<CDesktopBrowser, CWindow, CFrameWinTraits>, 36 public CComObjectRootEx<CComMultiThreadModelNoCS>, 37 public IShellBrowser, 38 public IServiceProvider 39 { 40 private: 41 HACCEL m_hAccel; 42 HWND m_hWndShellView; 43 CComPtr<IShellDesktopTray> m_Tray; 44 CComPtr<IShellView> m_ShellView; 45 46 CComPtr<IOleWindow> m_ChangeNotifyServer; 47 HWND m_hwndChangeNotifyServer; 48 49 LRESULT _NotifyTray(UINT uMsg, WPARAM wParam, LPARAM lParam); 50 HRESULT _Resize(); 51 52 public: 53 CDesktopBrowser(); 54 ~CDesktopBrowser(); 55 HRESULT Initialize(IShellDesktopTray *ShellDeskx); 56 57 // *** IOleWindow methods *** 58 virtual HRESULT STDMETHODCALLTYPE GetWindow(HWND *lphwnd); 59 virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode); 60 61 // *** IShellBrowser methods *** 62 virtual HRESULT STDMETHODCALLTYPE InsertMenusSB(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths); 63 virtual HRESULT STDMETHODCALLTYPE SetMenuSB(HMENU hmenuShared, HOLEMENU holemenuRes, HWND hwndActiveObject); 64 virtual HRESULT STDMETHODCALLTYPE RemoveMenusSB(HMENU hmenuShared); 65 virtual HRESULT STDMETHODCALLTYPE SetStatusTextSB(LPCOLESTR pszStatusText); 66 virtual HRESULT STDMETHODCALLTYPE EnableModelessSB(BOOL fEnable); 67 virtual HRESULT STDMETHODCALLTYPE TranslateAcceleratorSB(MSG *pmsg, WORD wID); 68 virtual HRESULT STDMETHODCALLTYPE BrowseObject(LPCITEMIDLIST pidl, UINT wFlags); 69 virtual HRESULT STDMETHODCALLTYPE GetViewStateStream(DWORD grfMode, IStream **ppStrm); 70 virtual HRESULT STDMETHODCALLTYPE GetControlWindow(UINT id, HWND *lphwnd); 71 virtual HRESULT STDMETHODCALLTYPE SendControlMsg(UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *pret); 72 virtual HRESULT STDMETHODCALLTYPE QueryActiveShellView(struct IShellView **ppshv); 73 virtual HRESULT STDMETHODCALLTYPE OnViewWindowActive(struct IShellView *ppshv); 74 virtual HRESULT STDMETHODCALLTYPE SetToolbarItems(LPTBBUTTON lpButtons, UINT nButtons, UINT uFlags); 75 76 // *** IServiceProvider methods *** 77 virtual HRESULT STDMETHODCALLTYPE QueryService(REFGUID guidService, REFIID riid, void **ppvObject); 78 79 // message handlers 80 LRESULT OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); 81 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); 82 LRESULT OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); 83 LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); 84 LRESULT OnOpenNewWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); 85 LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); 86 LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); 87 LRESULT OnGetChangeNotifyServer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); 88 89 DECLARE_WND_CLASS_EX(szProgmanClassName, CS_DBLCLKS, COLOR_DESKTOP) 90 91 BEGIN_MSG_MAP(CBaseBar) 92 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) 93 MESSAGE_HANDLER(WM_SIZE, OnSize) 94 MESSAGE_HANDLER(WM_SYSCOLORCHANGE, OnSettingChange) 95 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange) 96 MESSAGE_HANDLER(WM_CLOSE, OnClose) 97 MESSAGE_HANDLER(WM_EXPLORER_OPEN_NEW_WINDOW, OnOpenNewWindow) 98 MESSAGE_HANDLER(WM_COMMAND, OnCommand) 99 MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) 100 MESSAGE_HANDLER(WM_DESKTOP_GET_CNOTIFY_SERVER, OnGetChangeNotifyServer) 101 END_MSG_MAP() 102 103 BEGIN_COM_MAP(CDesktopBrowser) 104 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow) 105 COM_INTERFACE_ENTRY_IID(IID_IShellBrowser, IShellBrowser) 106 COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProvider) 107 END_COM_MAP() 108 }; 109 110 CDesktopBrowser::CDesktopBrowser(): 111 m_hAccel(NULL), 112 m_hWndShellView(NULL), 113 m_hwndChangeNotifyServer(NULL) 114 { 115 } 116 117 CDesktopBrowser::~CDesktopBrowser() 118 { 119 if (m_ShellView.p != NULL && m_hWndShellView != NULL) 120 { 121 m_ShellView->DestroyViewWindow(); 122 } 123 124 if (m_hwndChangeNotifyServer) 125 { 126 ::DestroyWindow(m_hwndChangeNotifyServer); 127 } 128 } 129 130 #ifdef MULTIMONITOR_SUPPORT 131 BOOL CALLBACK MonitorEnumProc( 132 _In_ HMONITOR hMonitor, 133 _In_ HDC hdcMonitor, 134 _In_ LPRECT lprcMonitor, 135 _In_ LPARAM dwData 136 ) 137 { 138 CAtlList<RECT> *list = (CAtlList<RECT>*)dwData; 139 MONITORINFO MonitorInfo; 140 MonitorInfo.cbSize = sizeof(MonitorInfo); 141 if (::GetMonitorInfoW(hMonitor, &MonitorInfo)) 142 { 143 list->AddTail(MonitorInfo.rcWork); 144 } 145 146 return TRUE; 147 } 148 #endif 149 150 HRESULT CDesktopBrowser::_Resize() 151 { 152 RECT rcNewSize; 153 154 #ifdef MULTIMONITOR_SUPPORT 155 156 UINT cMonitors = GetSystemMetrics(SM_CMONITORS); 157 if (cMonitors == 1) 158 { 159 SystemParametersInfoW(SPI_GETWORKAREA, 0, &rcNewSize, 0); 160 } 161 else 162 { 163 SetRect(&rcNewSize, 164 GetSystemMetrics(SM_XVIRTUALSCREEN), 165 GetSystemMetrics(SM_YVIRTUALSCREEN), 166 GetSystemMetrics(SM_XVIRTUALSCREEN) + GetSystemMetrics(SM_CXVIRTUALSCREEN), 167 GetSystemMetrics(SM_YVIRTUALSCREEN) + GetSystemMetrics(SM_CYVIRTUALSCREEN)); 168 } 169 170 ::MoveWindow(m_hWnd, rcNewSize.left, rcNewSize.top, rcNewSize.right - rcNewSize.left, rcNewSize.bottom - rcNewSize.top, TRUE); 171 ::MoveWindow(m_hWndShellView, 0, 0, rcNewSize.right - rcNewSize.left, rcNewSize.bottom - rcNewSize.top, TRUE); 172 173 if (cMonitors != 1) 174 { 175 CAtlList<RECT> list; 176 EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&list); 177 RECT* prcWorkAreas = new RECT[list.GetCount()]; 178 int i = 0; 179 for (POSITION it = list.GetHeadPosition(); it; list.GetNext(it)) 180 prcWorkAreas[i++] = list.GetAt(it); 181 182 HWND hwndListView = FindWindowExW(m_hWndShellView, NULL, WC_LISTVIEW, NULL); 183 184 ::SendMessageW(hwndListView, LVM_SETWORKAREAS , i, (LPARAM)prcWorkAreas); 185 } 186 187 #else 188 SystemParametersInfoW(SPI_GETWORKAREA, 0, &rcNewSize, 0); 189 ::MoveWindow(m_hWnd, rcNewSize.left, rcNewSize.top, rcNewSize.right - rcNewSize.left, rcNewSize.bottom - rcNewSize.top, TRUE); 190 ::MoveWindow(m_hWndShellView, 0, 0, rcNewSize.right - rcNewSize.left, rcNewSize.bottom - rcNewSize.top, TRUE); 191 192 #endif 193 return S_OK; 194 } 195 196 HRESULT CDesktopBrowser::Initialize(IShellDesktopTray *ShellDesk) 197 { 198 CComPtr<IShellFolder> psfDesktop; 199 HRESULT hRet; 200 hRet = SHGetDesktopFolder(&psfDesktop); 201 if (FAILED_UNEXPECTEDLY(hRet)) 202 return hRet; 203 204 m_Tray = ShellDesk; 205 206 Create(NULL, NULL, szProgmanWindowName, WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, WS_EX_TOOLWINDOW); 207 if (!m_hWnd) 208 return E_FAIL; 209 210 CSFV csfv = {sizeof(CSFV), psfDesktop}; 211 hRet = SHCreateShellFolderViewEx(&csfv, &m_ShellView); 212 if (FAILED_UNEXPECTEDLY(hRet)) 213 return hRet; 214 215 m_Tray->RegisterDesktopWindow(m_hWnd); 216 if (FAILED_UNEXPECTEDLY(hRet)) 217 return hRet; 218 219 FOLDERSETTINGS fs; 220 RECT rcShellView = {0,0,0,0}; 221 fs.ViewMode = FVM_ICON; 222 fs.fFlags = FWF_DESKTOP | FWF_NOCLIENTEDGE | FWF_NOSCROLL | FWF_TRANSPARENT; 223 hRet = m_ShellView->CreateViewWindow(NULL, &fs, (IShellBrowser *)this, &rcShellView, &m_hWndShellView); 224 if (FAILED_UNEXPECTEDLY(hRet)) 225 return hRet; 226 227 _Resize(); 228 229 HWND hwndListView = FindWindowExW(m_hWndShellView, NULL, WC_LISTVIEW, NULL); 230 231 m_hAccel = LoadAcceleratorsW(shell32_hInstance, MAKEINTRESOURCEW(IDA_DESKBROWSER)); 232 233 #if 1 234 /* A Windows8+ specific hack */ 235 ::ShowWindow(m_hWndShellView, SW_SHOW); 236 ::ShowWindow(hwndListView, SW_SHOW); 237 #endif 238 ShowWindow(SW_SHOW); 239 UpdateWindow(); 240 241 return hRet; 242 } 243 244 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetWindow(HWND *lphwnd) 245 { 246 if (lphwnd == NULL) 247 return E_POINTER; 248 *lphwnd = m_hWnd; 249 return S_OK; 250 } 251 252 HRESULT STDMETHODCALLTYPE CDesktopBrowser::ContextSensitiveHelp(BOOL fEnterMode) 253 { 254 return E_NOTIMPL; 255 } 256 257 HRESULT STDMETHODCALLTYPE CDesktopBrowser::InsertMenusSB(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths) 258 { 259 return E_NOTIMPL; 260 } 261 262 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetMenuSB(HMENU hmenuShared, HOLEMENU holemenuRes, HWND hwndActiveObject) 263 { 264 return E_NOTIMPL; 265 } 266 267 HRESULT STDMETHODCALLTYPE CDesktopBrowser::RemoveMenusSB(HMENU hmenuShared) 268 { 269 return E_NOTIMPL; 270 } 271 272 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetStatusTextSB(LPCOLESTR lpszStatusText) 273 { 274 return E_NOTIMPL; 275 } 276 277 HRESULT STDMETHODCALLTYPE CDesktopBrowser::EnableModelessSB(BOOL fEnable) 278 { 279 return E_NOTIMPL; 280 } 281 282 HRESULT STDMETHODCALLTYPE CDesktopBrowser::TranslateAcceleratorSB(LPMSG lpmsg, WORD wID) 283 { 284 if (!::TranslateAcceleratorW(m_hWnd, m_hAccel, lpmsg)) 285 return S_FALSE; 286 return S_OK; 287 } 288 289 HRESULT STDMETHODCALLTYPE CDesktopBrowser::BrowseObject(LPCITEMIDLIST pidl, UINT wFlags) 290 { 291 /* 292 * We should use IShellWindows interface here in order to attempt to 293 * find an open shell window that shows the requested pidl and activate it 294 */ 295 296 DWORD dwFlags = ((wFlags & SBSP_EXPLOREMODE) != 0) ? SH_EXPLORER_CMDLINE_FLAG_E : 0; 297 return SHOpenNewFrame(ILClone(pidl), NULL, 0, dwFlags); 298 } 299 300 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetViewStateStream(DWORD grfMode, IStream **ppStrm) 301 { 302 return E_NOTIMPL; 303 } 304 305 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetControlWindow(UINT id, HWND *lphwnd) 306 { 307 if (lphwnd == NULL) 308 return E_POINTER; 309 return E_NOTIMPL; 310 } 311 312 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SendControlMsg(UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *pret) 313 { 314 if (pret == NULL) 315 return E_POINTER; 316 return E_NOTIMPL; 317 } 318 319 HRESULT STDMETHODCALLTYPE CDesktopBrowser::QueryActiveShellView(IShellView **ppshv) 320 { 321 if (ppshv == NULL) 322 return E_POINTER; 323 *ppshv = m_ShellView; 324 if (m_ShellView != NULL) 325 m_ShellView->AddRef(); 326 327 return S_OK; 328 } 329 330 HRESULT STDMETHODCALLTYPE CDesktopBrowser::OnViewWindowActive(IShellView *ppshv) 331 { 332 return E_NOTIMPL; 333 } 334 335 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetToolbarItems(LPTBBUTTON lpButtons, UINT nButtons, UINT uFlags) 336 { 337 return E_NOTIMPL; 338 } 339 340 HRESULT STDMETHODCALLTYPE CDesktopBrowser::QueryService(REFGUID guidService, REFIID riid, PVOID *ppv) 341 { 342 /* FIXME - handle guidService */ 343 return QueryInterface(riid, ppv); 344 } 345 346 LRESULT CDesktopBrowser::_NotifyTray(UINT uMsg, WPARAM wParam, LPARAM lParam) 347 { 348 HWND hWndTray; 349 HRESULT hRet; 350 351 hRet = m_Tray->GetTrayWindow(&hWndTray); 352 if (SUCCEEDED(hRet)) 353 ::PostMessageW(hWndTray, uMsg, wParam, lParam); 354 355 return 0; 356 } 357 358 LRESULT CDesktopBrowser::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) 359 { 360 switch (LOWORD(wParam)) 361 { 362 case FCIDM_DESKBROWSER_CLOSE: 363 return _NotifyTray(TWM_DOEXITWINDOWS, 0, 0); 364 case FCIDM_DESKBROWSER_FOCUS: 365 if (GetKeyState(VK_SHIFT)) 366 return _NotifyTray(TWM_CYCLEFOCUS, 1, 0xFFFFFFFF); 367 else 368 return _NotifyTray(TWM_CYCLEFOCUS, 1, 1); 369 case FCIDM_DESKBROWSER_SEARCH: 370 SHFindFiles(NULL, NULL); 371 break; 372 case FCIDM_DESKBROWSER_REFRESH: 373 if (m_ShellView) 374 m_ShellView->Refresh(); 375 break; 376 } 377 378 return 0; 379 } 380 381 382 LRESULT CDesktopBrowser::OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) 383 { 384 return (LRESULT)PaintDesktop((HDC)wParam); 385 } 386 387 LRESULT CDesktopBrowser::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) 388 { 389 if (wParam == SIZE_MINIMIZED) 390 { 391 /* Hey, we're the desktop!!! */ 392 ::ShowWindow(m_hWnd, SW_RESTORE); 393 } 394 395 ::InvalidateRect(m_hWndShellView, NULL, TRUE); 396 397 return 0; 398 } 399 400 LRESULT CDesktopBrowser::OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) 401 { 402 if (uMsg == WM_SETTINGCHANGE /* == WM_WININICHANGE */ && 403 lstrcmpiW((LPCWSTR)lParam, L"Environment") == 0) 404 { 405 LPVOID lpEnvironment; 406 RegenerateUserEnvironment(&lpEnvironment, TRUE); 407 } 408 409 if (m_hWndShellView) 410 { 411 /* Forward the message */ 412 ::SendMessageW(m_hWndShellView, uMsg, wParam, lParam); 413 } 414 415 if (uMsg == WM_SETTINGCHANGE && wParam == SPI_SETWORKAREA && m_hWndShellView != NULL) 416 { 417 _Resize(); 418 } 419 420 return 0; 421 } 422 423 LRESULT CDesktopBrowser::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) 424 { 425 return _NotifyTray(TWM_DOEXITWINDOWS, 0, 0); 426 } 427 428 LRESULT CDesktopBrowser::OnOpenNewWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) 429 { 430 TRACE("Proxy Desktop message 1035 received.\n"); 431 SHOnCWMCommandLine((HANDLE)lParam); 432 return 0; 433 } 434 435 LRESULT CDesktopBrowser::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) 436 { 437 ::SetFocus(m_hWndShellView); 438 return 0; 439 } 440 441 // Message WM_DESKTOP_GET_CNOTIFY_SERVER: Get or create the change notification server. 442 // wParam: BOOL bCreate; The flag whether it creates or not. 443 // lParam: Ignored. 444 // return: The window handle of the server window. 445 LRESULT CDesktopBrowser::OnGetChangeNotifyServer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) 446 { 447 BOOL bCreate = (BOOL)wParam; 448 if (bCreate && !::IsWindow(m_hwndChangeNotifyServer)) 449 { 450 HRESULT hres = CChangeNotifyServer_CreateInstance(IID_PPV_ARG(IOleWindow, &m_ChangeNotifyServer)); 451 if (FAILED_UNEXPECTEDLY(hres)) 452 return NULL; 453 454 hres = m_ChangeNotifyServer->GetWindow(&m_hwndChangeNotifyServer); 455 if (FAILED_UNEXPECTEDLY(hres)) 456 return NULL; 457 } 458 return (LRESULT)m_hwndChangeNotifyServer; 459 } 460 461 HRESULT CDesktopBrowser_CreateInstance(IShellDesktopTray *Tray, REFIID riid, void **ppv) 462 { 463 return ShellObjectCreatorInit<CDesktopBrowser, IShellDesktopTray*>(Tray, riid, ppv); 464 } 465 466 /************************************************************************* 467 * SHCreateDesktop [SHELL32.200] 468 * 469 */ 470 HANDLE WINAPI SHCreateDesktop(IShellDesktopTray *Tray) 471 { 472 if (Tray == NULL) 473 { 474 SetLastError(ERROR_INVALID_PARAMETER); 475 return NULL; 476 } 477 478 CComPtr<IShellBrowser> Browser; 479 HRESULT hr = CDesktopBrowser_CreateInstance(Tray, IID_PPV_ARG(IShellBrowser, &Browser)); 480 if (FAILED_UNEXPECTEDLY(hr)) 481 return NULL; 482 483 return static_cast<HANDLE>(Browser.Detach()); 484 } 485 486 /************************************************************************* 487 * SHCreateDesktop [SHELL32.201] 488 * 489 */ 490 BOOL WINAPI SHDesktopMessageLoop(HANDLE hDesktop) 491 { 492 if (hDesktop == NULL) 493 { 494 SetLastError(ERROR_INVALID_PARAMETER); 495 return FALSE; 496 } 497 498 MSG Msg; 499 BOOL bRet; 500 501 CComPtr<IShellBrowser> browser; 502 CComPtr<IShellView> shellView; 503 504 browser.Attach(static_cast<IShellBrowser*>(hDesktop)); 505 HRESULT hr = browser->QueryActiveShellView(&shellView); 506 if (FAILED_UNEXPECTEDLY(hr)) 507 return FALSE; 508 509 while ((bRet = ::GetMessageW(&Msg, NULL, 0, 0)) != 0) 510 { 511 if (bRet != -1) 512 { 513 if (shellView->TranslateAcceleratorW(&Msg) != S_OK) 514 { 515 ::TranslateMessage(&Msg); 516 ::DispatchMessageW(&Msg); 517 } 518 } 519 } 520 521 return TRUE; 522 } 523