/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // For _tcscpy #define FORBIDDEN_SYMBOL_EXCEPTION_strcpy // We cannot use common/scummsys.h directly as it will include // windows.h and we need to do it by hand to allow excluded functions #if defined(HAVE_CONFIG_H) #include "config.h" #endif #if defined(WIN32) && defined(USE_TASKBAR) // HACK: To get __MINGW64_VERSION_foo defines we need to manually include // _mingw.h in this file because we do not include any system headers at this // point on purpose. The defines are required to detect whether this is a // classic MinGW toolchain or a MinGW-w64 based one. #if defined(__MINGW32__) #include <_mingw.h> #endif // Needed for taskbar functions // HACK: MinGW-w64 based toolchains include the symbols we require in their // headers. The 32 bit incarnation only defines __MINGW32__. This leads to // build breakage due to clashes with our compat header. Luckily MinGW-w64 // based toolchains define __MINGW64_VERSION_foo macros inside _mingw.h, // which is included from all system headers. Thus we abuse that to detect // them. #if defined(__GNUC__) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) #include "backends/taskbar/win32/mingw-compat.h" #else // We use functionality introduced with Win7 in this file. // To assure that including the respective system headers gives us all // required definitions we set Win7 as minimum version we target. // See: https://docs.microsoft.com/en-us/windows/win32/winprog/using-the-windows-headers#macros-for-conditional-declarations #include #undef _WIN32_WINNT #define _WIN32_WINNT _WIN32_WINNT_WIN7 #include #endif #include #include #include "common/scummsys.h" #include "backends/taskbar/win32/win32-taskbar.h" #include "backends/platform/sdl/win32/win32-window.h" #include "backends/platform/sdl/win32/win32_wrapper.h" #include "common/textconsole.h" // System.Title property key, values taken from https://docs.microsoft.com/en-us/windows/win32/properties/props-system-title const PROPERTYKEY PKEY_Title = { /* fmtid = */ { 0xF29F85E0, 0x4FF9, 0x1068, { 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9 } }, /* propID = */ 2 }; Win32TaskbarManager::Win32TaskbarManager(SdlWindow_Win32 *window) : _window(window), _taskbar(nullptr), _count(0), _icon(nullptr) { // Do nothing if not running on Windows 7 or later if (!Win32::confirmWindowsVersion(6, 1)) return; CoInitialize(nullptr); // Try creating instance (on fail, _taskbar will contain NULL) HRESULT hr = CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, reinterpret_cast (&(_taskbar))); if (SUCCEEDED(hr)) { // Initialize taskbar object if (FAILED(_taskbar->HrInit())) { _taskbar->Release(); _taskbar = nullptr; } } else { warning("[Win32TaskbarManager::init] Cannot create taskbar instance"); } } Win32TaskbarManager::~Win32TaskbarManager() { if (_taskbar) _taskbar->Release(); _taskbar = nullptr; if (_icon) DestroyIcon(_icon); CoUninitialize(); } void Win32TaskbarManager::setOverlayIcon(const Common::String &name, const Common::String &description) { //warning("[Win32TaskbarManager::setOverlayIcon] Setting overlay icon to: %s (%s)", name.c_str(), description.c_str()); if (_taskbar == nullptr) return; if (name.empty()) { _taskbar->SetOverlayIcon(_window->getHwnd(), nullptr, L""); return; } // Compute full icon path Common::String iconPath = getIconPath(name, ".ico"); if (iconPath.empty()) return; TCHAR *tIconPath = Win32::stringToTchar(iconPath); HICON pIcon = (HICON)::LoadImage(nullptr, tIconPath, IMAGE_ICON, 16, 16, LR_LOADFROMFILE); free(tIconPath); if (!pIcon) { warning("[Win32TaskbarManager::setOverlayIcon] Cannot load icon!"); return; } // Sets the overlay icon LPWSTR desc = Win32::ansiToUnicode(description.c_str()); _taskbar->SetOverlayIcon(_window->getHwnd(), pIcon, desc); DestroyIcon(pIcon); free(desc); } void Win32TaskbarManager::setProgressValue(int completed, int total) { if (_taskbar == nullptr) return; _taskbar->SetProgressValue(_window->getHwnd(), completed, total); } void Win32TaskbarManager::setProgressState(TaskbarProgressState state) { if (_taskbar == nullptr) return; _taskbar->SetProgressState(_window->getHwnd(), (TBPFLAG)state); } void Win32TaskbarManager::setCount(int count) { if (_taskbar == nullptr) return; if (count == 0) { _taskbar->SetOverlayIcon(_window->getHwnd(), nullptr, L""); return; } // FIXME: This isn't really nice and could use a cleanup. // The only good thing is that it doesn't use GDI+ // and thus does not have a dependancy on it, // with the downside of being a lot more ugly. // Maybe replace it by a Graphic::Surface, use // ScummVM font drawing and extract the contents at // the end? if (_count != count || _icon == nullptr) { // Cleanup previous icon _count = count; if (_icon) DestroyIcon(_icon); Common::String countString = (count < 100 ? Common::String::format("%d", count) : "9+"); // Create transparent background BITMAPV5HEADER bi; ZeroMemory(&bi, sizeof(BITMAPV5HEADER)); bi.bV5Size = sizeof(BITMAPV5HEADER); bi.bV5Width = 16; bi.bV5Height = 16; bi.bV5Planes = 1; bi.bV5BitCount = 32; bi.bV5Compression = BI_RGB; // Set 32 BPP alpha format bi.bV5RedMask = 0x00FF0000; bi.bV5GreenMask = 0x0000FF00; bi.bV5BlueMask = 0x000000FF; bi.bV5AlphaMask = 0xFF000000; // Get DC HDC hdc; hdc = GetDC(nullptr); HDC hMemDC = CreateCompatibleDC(hdc); ReleaseDC(nullptr, hdc); // Create a bitmap mask HBITMAP hBitmapMask = CreateBitmap(16, 16, 1, 1, nullptr); // Create the DIB section with an alpha channel void *lpBits; HBITMAP hBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void **)&lpBits, nullptr, 0); HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap); // Load the icon background HICON hIconBackground = LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(1002 /* IDI_COUNT */)); DrawIconEx(hMemDC, 0, 0, hIconBackground, 16, 16, 0, nullptr, DI_NORMAL); DeleteObject(hIconBackground); // Draw the count LOGFONT lFont; memset(&lFont, 0, sizeof(LOGFONT)); lFont.lfHeight = 10; lFont.lfWeight = FW_BOLD; lFont.lfItalic = 1; _tcscpy(lFont.lfFaceName, TEXT("Arial")); HFONT hFont = CreateFontIndirect(&lFont); SelectObject(hMemDC, hFont); RECT rect; SetRect(&rect, 4, 4, 12, 12); SetTextColor(hMemDC, RGB(48, 48, 48)); SetBkMode(hMemDC, TRANSPARENT); TCHAR *tCountString = Win32::stringToTchar(countString); DrawText(hMemDC, tCountString, -1, &rect, DT_NOCLIP|DT_CENTER); free(tCountString); // Set the text alpha to fully opaque (we consider the data inside the text rect) DWORD *lpdwPixel = (DWORD *)lpBits; for (int x = 3; x < 12; x++) { for(int y = 3; y < 12; y++) { unsigned char *p = (unsigned char *)(lpdwPixel + x * 16 + y); if (p[0] != 0 && p[1] != 0 && p[2] != 0) p[3] = 255; } } // Cleanup DC DeleteObject(hFont); SelectObject(hMemDC, hOldBitmap); DeleteDC(hMemDC); // Prepare our new icon ICONINFO ii; ii.fIcon = FALSE; ii.xHotspot = 0; ii.yHotspot = 0; ii.hbmMask = hBitmapMask; ii.hbmColor = hBitmap; _icon = CreateIconIndirect(&ii); DeleteObject(hBitmap); DeleteObject(hBitmapMask); if (!_icon) { warning("[Win32TaskbarManager::setCount] Cannot create icon for count"); return; } } // Sets the overlay icon LPWSTR desc = Win32::ansiToUnicode(Common::String::format("Found games: %d", count).c_str()); _taskbar->SetOverlayIcon(_window->getHwnd(), _icon, desc); free(desc); } void Win32TaskbarManager::addRecent(const Common::String &name, const Common::String &description) { //warning("[Win32TaskbarManager::addRecent] Adding recent list entry: %s (%s)", name.c_str(), description.c_str()); if (_taskbar == nullptr) return; // ANSI version doesn't seem to work correctly with Win7 jump lists, so explicitly use Unicode interface. IShellLinkW *link; // Get the ScummVM executable path. WCHAR path[MAX_PATH]; GetModuleFileNameW(nullptr, path, MAX_PATH); // Create a shell link. if (SUCCEEDED(CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC, IID_IShellLinkW, reinterpret_cast (&link)))) { // Convert game name and description to Unicode. LPWSTR game = Win32::ansiToUnicode(name.c_str()); LPWSTR desc = Win32::ansiToUnicode(description.c_str()); // Set link properties. link->SetPath(path); link->SetArguments(game); Common::String iconPath = getIconPath(name, ".ico"); if (iconPath.empty()) { link->SetIconLocation(path, 0); // No game-specific icon available } else { LPWSTR icon = Win32::ansiToUnicode(iconPath.c_str()); link->SetIconLocation(icon, 0); free(icon); } // The link's display name must be set via property store. IPropertyStore* propStore; HRESULT hr = link->QueryInterface(IID_IPropertyStore, reinterpret_cast (&(propStore))); if (SUCCEEDED(hr)) { PROPVARIANT pv; pv.vt = VT_LPWSTR; pv.pwszVal = desc; hr = propStore->SetValue(PKEY_Title, pv); propStore->Commit(); propStore->Release(); } // SHAddToRecentDocs will cause the games to be added to the Recent list, allowing the user to pin them. SHAddToRecentDocs(SHARD_LINK, link); link->Release(); free(game); free(desc); } } void Win32TaskbarManager::notifyError() { setProgressState(Common::TaskbarManager::kTaskbarError); setProgressValue(1, 1); } void Win32TaskbarManager::clearError() { setProgressState(kTaskbarNoProgress); } #endif