ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

win32-taskbar.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 // We cannot use common/scummsys.h directly as it will include
00024 // windows.h and we need to do it by hand to allow excluded functions
00025 #if defined(HAVE_CONFIG_H)
00026 #include "config.h"
00027 #endif
00028 
00029 #if defined(WIN32) && defined(USE_TASKBAR)
00030 
00031 // HACK: To get __MINGW64_VERSION_foo defines we need to manually include
00032 // _mingw.h in this file because we do not include any system headers at this
00033 // point on purpose. The defines are required to detect whether this is a
00034 // classic MinGW toolchain or a MinGW-w64 based one.
00035 #if defined(__MINGW32__)
00036 #include <_mingw.h>
00037 #endif
00038 
00039 // Needed for taskbar functions
00040 // HACK: MinGW-w64 based toolchains include the symbols we require in their
00041 // headers. The 32 bit incarnation only defines __MINGW32__. This leads to
00042 // build breakage due to clashes with our compat header. Luckily MinGW-w64
00043 // based toolchains define __MINGW64_VERSION_foo macros inside _mingw.h,
00044 // which is included from all system headers. Thus we abuse that to detect
00045 // them.
00046 #if defined(__GNUC__) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
00047     #include "backends/taskbar/win32/mingw-compat.h"
00048 #else
00049     // We use functionality introduced with Win7 in this file.
00050     // To assure that including the respective system headers gives us all
00051     // required definitions we set Win7 as minimum version we target.
00052     // See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa383745%28v=vs.85%29.aspx#macros_for_conditional_declarations
00053     #include <sdkddkver.h>
00054     #undef _WIN32_WINNT
00055     #define _WIN32_WINNT _WIN32_WINNT_WIN7
00056 
00057     #include <windows.h>
00058 #endif
00059 
00060 #include <shlobj.h>
00061 
00062 #include "common/scummsys.h"
00063 
00064 #include "backends/taskbar/win32/win32-taskbar.h"
00065 #include "backends/platform/sdl/win32/win32-window.h"
00066 #include "backends/platform/sdl/win32/win32_wrapper.h"
00067 
00068 #include "common/config-manager.h"
00069 #include "common/textconsole.h"
00070 #include "common/file.h"
00071 
00072 // System.Title property key, values taken from http://msdn.microsoft.com/en-us/library/bb787584.aspx
00073 const PROPERTYKEY PKEY_Title = { /* fmtid = */ { 0xF29F85E0, 0x4FF9, 0x1068, { 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9 } }, /* propID = */ 2 };
00074 
00075 Win32TaskbarManager::Win32TaskbarManager(SdlWindow_Win32 *window) : _window(window), _taskbar(NULL), _count(0), _icon(NULL) {
00076     // Do nothing if not running on Windows 7 or later
00077     if (!Win32::confirmWindowsVersion(6, 1))
00078         return;
00079 
00080     CoInitialize(NULL);
00081 
00082     // Try creating instance (on fail, _taskbar will contain NULL)
00083     HRESULT hr = CoCreateInstance(CLSID_TaskbarList,
00084                                   0,
00085                                   CLSCTX_INPROC_SERVER,
00086                                   IID_ITaskbarList3,
00087                                   reinterpret_cast<void **> (&(_taskbar)));
00088 
00089     if (SUCCEEDED(hr)) {
00090         // Initialize taskbar object
00091         if (FAILED(_taskbar->HrInit())) {
00092             _taskbar->Release();
00093             _taskbar = NULL;
00094         }
00095     } else {
00096         warning("[Win32TaskbarManager::init] Cannot create taskbar instance");
00097     }
00098 }
00099 
00100 Win32TaskbarManager::~Win32TaskbarManager() {
00101     if (_taskbar)
00102         _taskbar->Release();
00103     _taskbar = NULL;
00104 
00105     if (_icon)
00106         DestroyIcon(_icon);
00107 
00108     CoUninitialize();
00109 }
00110 
00111 void Win32TaskbarManager::setOverlayIcon(const Common::String &name, const Common::String &description) {
00112     //warning("[Win32TaskbarManager::setOverlayIcon] Setting overlay icon to: %s (%s)", name.c_str(), description.c_str());
00113 
00114     if (_taskbar == NULL)
00115         return;
00116 
00117     if (name.empty()) {
00118         _taskbar->SetOverlayIcon(_window->getHwnd(), NULL, L"");
00119         return;
00120     }
00121 
00122     // Compute full icon path
00123     Common::String path = getIconPath(name);
00124     if (path.empty())
00125         return;
00126 
00127     HICON pIcon = (HICON)::LoadImage(NULL, path.c_str(), IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
00128     if (!pIcon) {
00129         warning("[Win32TaskbarManager::setOverlayIcon] Cannot load icon!");
00130         return;
00131     }
00132 
00133     // Sets the overlay icon
00134     LPWSTR desc = Win32::ansiToUnicode(description.c_str());
00135     _taskbar->SetOverlayIcon(_window->getHwnd(), pIcon, desc);
00136 
00137     DestroyIcon(pIcon);
00138 
00139     delete[] desc;
00140 }
00141 
00142 void Win32TaskbarManager::setProgressValue(int completed, int total) {
00143     if (_taskbar == NULL)
00144         return;
00145 
00146     _taskbar->SetProgressValue(_window->getHwnd(), completed, total);
00147 }
00148 
00149 void Win32TaskbarManager::setProgressState(TaskbarProgressState state) {
00150     if (_taskbar == NULL)
00151         return;
00152 
00153     _taskbar->SetProgressState(_window->getHwnd(), (TBPFLAG)state);
00154 }
00155 
00156 void Win32TaskbarManager::setCount(int count) {
00157     if (_taskbar == NULL)
00158         return;
00159 
00160     if (count == 0) {
00161         _taskbar->SetOverlayIcon(_window->getHwnd(), NULL, L"");
00162         return;
00163     }
00164 
00165     // FIXME: This isn't really nice and could use a cleanup.
00166     //        The only good thing is that it doesn't use GDI+
00167     //        and thus does not have a dependancy on it,
00168     //        with the downside of being a lot more ugly.
00169     //        Maybe replace it by a Graphic::Surface, use
00170     //        ScummVM font drawing and extract the contents at
00171     //        the end?
00172 
00173     if (_count != count || _icon == NULL) {
00174         // Cleanup previous icon
00175         _count = count;
00176         if (_icon)
00177             DestroyIcon(_icon);
00178 
00179         Common::String countString = (count < 100 ? Common::String::format("%d", count) : "9+");
00180 
00181         // Create transparent background
00182         BITMAPV5HEADER bi;
00183         ZeroMemory(&bi, sizeof(BITMAPV5HEADER));
00184         bi.bV5Size        = sizeof(BITMAPV5HEADER);
00185         bi.bV5Width       = 16;
00186         bi.bV5Height      = 16;
00187         bi.bV5Planes      = 1;
00188         bi.bV5BitCount    = 32;
00189         bi.bV5Compression = BI_RGB;
00190         // Set 32 BPP alpha format
00191         bi.bV5RedMask     = 0x00FF0000;
00192         bi.bV5GreenMask   = 0x0000FF00;
00193         bi.bV5BlueMask    = 0x000000FF;
00194         bi.bV5AlphaMask   = 0xFF000000;
00195 
00196         // Get DC
00197         HDC hdc;
00198         hdc = GetDC(NULL);
00199         HDC hMemDC = CreateCompatibleDC(hdc);
00200         ReleaseDC(NULL, hdc);
00201 
00202         // Create a bitmap mask
00203         HBITMAP hBitmapMask = CreateBitmap(16, 16, 1, 1, NULL);
00204 
00205         // Create the DIB section with an alpha channel
00206         void *lpBits;
00207         HBITMAP hBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void **)&lpBits, NULL, 0);
00208         HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);
00209 
00210         // Load the icon background
00211         HICON hIconBackground = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(1002 /* IDI_COUNT */));
00212         DrawIconEx(hMemDC, 0, 0, hIconBackground, 16, 16, 0, 0, DI_NORMAL);
00213         DeleteObject(hIconBackground);
00214 
00215         // Draw the count
00216         LOGFONT lFont;
00217         memset(&lFont, 0, sizeof(LOGFONT));
00218         lFont.lfHeight = 10;
00219         lFont.lfWeight = FW_BOLD;
00220         lFont.lfItalic = 1;
00221         strcpy(lFont.lfFaceName, "Arial");
00222 
00223         HFONT hFont = CreateFontIndirect(&lFont);
00224         SelectObject(hMemDC, hFont);
00225 
00226         RECT rect;
00227         SetRect(&rect, 4, 4, 12, 12);
00228         SetTextColor(hMemDC, RGB(48, 48, 48));
00229         SetBkMode(hMemDC, TRANSPARENT);
00230         DrawText(hMemDC, countString.c_str(), -1, &rect, DT_NOCLIP|DT_CENTER);
00231 
00232         // Set the text alpha to fully opaque (we consider the data inside the text rect)
00233         DWORD *lpdwPixel = (DWORD *)lpBits;
00234         for (int x = 3; x < 12; x++) {
00235             for(int y = 3; y < 12; y++) {
00236                 unsigned char *p = (unsigned char *)(lpdwPixel + x * 16 + y);
00237 
00238                 if (p[0] != 0 && p[1] != 0 && p[2] != 0)
00239                     p[3] = 255;
00240             }
00241         }
00242 
00243         // Cleanup DC
00244         DeleteObject(hFont);
00245         SelectObject(hMemDC, hOldBitmap);
00246         DeleteDC(hMemDC);
00247 
00248         // Prepare our new icon
00249         ICONINFO ii;
00250         ii.fIcon    = FALSE;
00251         ii.xHotspot = 0;
00252         ii.yHotspot = 0;
00253         ii.hbmMask  = hBitmapMask;
00254         ii.hbmColor = hBitmap;
00255 
00256         _icon = CreateIconIndirect(&ii);
00257 
00258         DeleteObject(hBitmap);
00259         DeleteObject(hBitmapMask);
00260 
00261         if (!_icon) {
00262             warning("[Win32TaskbarManager::setCount] Cannot create icon for count");
00263             return;
00264         }
00265     }
00266 
00267     // Sets the overlay icon
00268     LPWSTR desc = Win32::ansiToUnicode(Common::String::format("Found games: %d", count).c_str());
00269     _taskbar->SetOverlayIcon(_window->getHwnd(), _icon, desc);
00270     delete[] desc;
00271 }
00272 
00273 void Win32TaskbarManager::addRecent(const Common::String &name, const Common::String &description) {
00274     //warning("[Win32TaskbarManager::addRecent] Adding recent list entry: %s (%s)", name.c_str(), description.c_str());
00275 
00276     if (_taskbar == NULL)
00277         return;
00278 
00279     // ANSI version doesn't seem to work correctly with Win7 jump lists, so explicitly use Unicode interface.
00280     IShellLinkW *link;
00281 
00282     // Get the ScummVM executable path.
00283     WCHAR path[MAX_PATH];
00284     GetModuleFileNameW(NULL, path, MAX_PATH);
00285 
00286     // Create a shell link.
00287     if (SUCCEEDED(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC, IID_IShellLinkW, reinterpret_cast<void **> (&link)))) {
00288         // Convert game name and description to Unicode.
00289         LPWSTR game = Win32::ansiToUnicode(name.c_str());
00290         LPWSTR desc = Win32::ansiToUnicode(description.c_str());
00291 
00292         // Set link properties.
00293         link->SetPath(path);
00294         link->SetArguments(game);
00295 
00296         Common::String iconPath = getIconPath(name);
00297         if (iconPath.empty()) {
00298             link->SetIconLocation(path, 0); // No game-specific icon available
00299         } else {
00300             LPWSTR icon = Win32::ansiToUnicode(iconPath.c_str());
00301 
00302             link->SetIconLocation(icon, 0);
00303 
00304             delete[] icon;
00305         }
00306 
00307         // The link's display name must be set via property store.
00308         IPropertyStore* propStore;
00309         HRESULT hr = link->QueryInterface(IID_IPropertyStore, reinterpret_cast<void **> (&(propStore)));
00310         if (SUCCEEDED(hr)) {
00311             PROPVARIANT pv;
00312             pv.vt = VT_LPWSTR;
00313             pv.pwszVal = desc;
00314 
00315             hr = propStore->SetValue(PKEY_Title, pv);
00316 
00317             propStore->Commit();
00318             propStore->Release();
00319         }
00320 
00321         // SHAddToRecentDocs will cause the games to be added to the Recent list, allowing the user to pin them.
00322         SHAddToRecentDocs(SHARD_LINK, link);
00323         link->Release();
00324         delete[] game;
00325         delete[] desc;
00326     }
00327 }
00328 
00329 void Win32TaskbarManager::notifyError() {
00330     setProgressState(Common::TaskbarManager::kTaskbarError);
00331     setProgressValue(1, 1);
00332 }
00333 
00334 void Win32TaskbarManager::clearError() {
00335     setProgressState(kTaskbarNoProgress);
00336 }
00337 
00338 Common::String Win32TaskbarManager::getIconPath(Common::String target) {
00339     // We first try to look for a iconspath configuration variable then
00340     // fallback to the extra path
00341     //
00342     // Icons can be either in a subfolder named "icons" or directly in the path
00343 
00344     Common::String iconsPath = ConfMan.get("iconspath");
00345     Common::String extraPath = ConfMan.get("extrapath");
00346 
00347 #define TRY_ICON_PATH(path) { \
00348     Common::FSNode node((path)); \
00349     if (node.exists()) \
00350         return (path); \
00351 }
00352 
00353     if (!iconsPath.empty()) {
00354         TRY_ICON_PATH(iconsPath + "/" + target + ".ico");
00355         TRY_ICON_PATH(iconsPath + "/" + ConfMan.get("gameid") + ".ico");
00356         TRY_ICON_PATH(iconsPath + "/icons/" + target + ".ico");
00357         TRY_ICON_PATH(iconsPath + "/icons/" + ConfMan.get("gameid") + ".ico");
00358     }
00359 
00360     if (!extraPath.empty()) {
00361         TRY_ICON_PATH(extraPath + "/" + target + ".ico");
00362         TRY_ICON_PATH(extraPath + "/" + ConfMan.get("gameid") + ".ico");
00363         TRY_ICON_PATH(extraPath + "/icons/" + target + ".ico");
00364         TRY_ICON_PATH(extraPath + "/icons/" + ConfMan.get("gameid") + ".ico");
00365     }
00366 
00367     return "";
00368 }
00369 
00370 #endif


Generated on Sat May 18 2019 05:01:30 for ResidualVM by doxygen 1.7.1
curved edge   curved edge