OSDN Git Service

Re-enabled 'async' mode for play_sound_file() function + fixed possible handle leak...
[mutilities/MUtilities.git] / src / GUI_Win32.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2014 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 //
19 // http://www.gnu.org/licenses/lgpl-2.1.txt
20 //////////////////////////////////////////////////////////////////////////////////
21
22 //Win32 API
23 #define WIN32_LEAN_AND_MEAN 1
24 #include <Windows.h>
25
26 //MUtils
27 #include <MUtils/GUI.h>
28 #include <MUtils/OSSupport.h>
29
30 //Internal
31 #include "Utils_Win32.h"
32
33 //Qt
34 #include <QIcon>
35 #include <QApplication>
36 #include <QWidget>
37 #include <QReadWriteLock>
38 #include <QLibrary>
39 #include <Dwmapi.h>
40
41 ///////////////////////////////////////////////////////////////////////////////
42 // THEME SUPPORT
43 ///////////////////////////////////////////////////////////////////////////////
44
45 static QReadWriteLock g_themes_lock;
46 static bool g_themes_initialized = false;
47 static bool g_themes_enabled = false;
48
49 typedef int (WINAPI *IsAppThemedFunction)(void);
50
51 bool MUtils::GUI::themes_enabled(void)
52 {
53         QReadLocker readLock(&g_themes_lock);
54
55         if(g_themes_initialized)
56         {
57                 return g_themes_enabled;
58         }
59
60         readLock.unlock();
61         QWriteLocker writeLock(&g_themes_lock);
62
63         if(g_themes_initialized)
64         {
65                 return g_themes_enabled;
66         }
67
68         const MUtils::OS::Version::os_version_t &osVersion = MUtils::OS::os_version();
69         if(osVersion >= MUtils::OS::Version::WINDOWS_WINXP)
70         {
71                 IsAppThemedFunction IsAppThemedPtr = NULL;
72                 QLibrary uxTheme("UxTheme.dll");
73                 if(uxTheme.load())
74                 {
75                         IsAppThemedPtr = (IsAppThemedFunction) uxTheme.resolve("IsAppThemed");
76                 }
77                 if(IsAppThemedPtr)
78                 {
79                         g_themes_enabled = IsAppThemedPtr();
80                         if(!g_themes_enabled)
81                         {
82                                 qWarning("Theme support is disabled for this process!");
83                         }
84                 }
85         }
86
87         g_themes_initialized = true;
88         return g_themes_enabled;
89 }
90
91 ///////////////////////////////////////////////////////////////////////////////
92 // SYSTEM MENU
93 ///////////////////////////////////////////////////////////////////////////////
94
95 bool MUtils::GUI::sysmenu_append(const QWidget *win, const unsigned int identifier, const QString &text)
96 {
97         bool ok = false;
98         
99         if(HMENU hMenu = GetSystemMenu(win->winId(), FALSE))
100         {
101                 ok = (AppendMenuW(hMenu, MF_SEPARATOR, 0, 0) == TRUE);
102                 ok = (AppendMenuW(hMenu, MF_STRING, identifier, MUTILS_WCHR(text)) == TRUE);
103         }
104
105         return ok;
106 }
107
108 bool MUtils::GUI::sysmenu_update(const QWidget *win, const unsigned int identifier, const QString &text)
109 {
110         bool ok = false;
111         
112         if(HMENU hMenu = ::GetSystemMenu(win->winId(), FALSE))
113         {
114                 ok = (ModifyMenu(hMenu, identifier, MF_STRING | MF_BYCOMMAND, identifier, MUTILS_WCHR(text)) == TRUE);
115         }
116         return ok;
117 }
118
119 bool MUtils::GUI::sysmenu_check_msg(void *const message, const unsigned int &identifier)
120 {
121         return (((MSG*)message)->message == WM_SYSCOMMAND) && ((((MSG*)message)->wParam & 0xFFF0) == identifier);
122 }
123
124 ///////////////////////////////////////////////////////////////////////////////
125 // CLOSE BUTTON
126 ///////////////////////////////////////////////////////////////////////////////
127
128 bool MUtils::GUI::enable_close_button(const QWidget *win, const bool &bEnable)
129 {
130         bool ok = false;
131
132         if(HMENU hMenu = GetSystemMenu(win->winId(), FALSE))
133         {
134                 ok = (EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | (bEnable ? MF_ENABLED : MF_GRAYED)) == TRUE);
135         }
136
137         return ok;
138 }
139
140 ///////////////////////////////////////////////////////////////////////////////
141 // BRING WINDOW TO FRONT
142 ///////////////////////////////////////////////////////////////////////////////
143
144 static BOOL CALLBACK bring_process_to_front_helper(HWND hwnd, LPARAM lParam)
145 {
146         DWORD processId = *reinterpret_cast<WORD*>(lParam);
147         DWORD windowProcessId = NULL;
148         GetWindowThreadProcessId(hwnd, &windowProcessId);
149         if(windowProcessId == processId)
150         {
151                 SwitchToThisWindow(hwnd, TRUE);
152                 SetForegroundWindow(hwnd);
153                 return FALSE;
154         }
155
156         return TRUE;
157 }
158
159 bool MUtils::GUI::bring_to_front(const QWidget *window)
160 {
161         bool ret = false;
162         
163         if(window)
164         {
165                 for(int i = 0; (i < 5) && (!ret); i++)
166                 {
167                         ret = (SetForegroundWindow(window->winId()) != FALSE);
168                         SwitchToThisWindow(window->winId(), TRUE);
169                 }
170                 LockSetForegroundWindow(LSFW_LOCK);
171         }
172
173         return ret;
174 }
175
176 bool MUtils::GUI::bring_to_front(const unsigned long pid)
177 {
178         return EnumWindows(bring_process_to_front_helper, reinterpret_cast<LPARAM>(&pid)) == TRUE;
179 }
180
181 ///////////////////////////////////////////////////////////////////////////////
182 // SHEET OF GLASS EFFECT
183 ///////////////////////////////////////////////////////////////////////////////
184
185 static QReadWriteLock g_dwmapi_lock;
186 static QScopedPointer<QLibrary> g_dwmapi_library;
187 static bool g_dwmapi_initialized = false;
188
189 static struct
190 {
191         HRESULT (__stdcall *dwmIsCompositionEnabled)(BOOL *bEnabled);
192         HRESULT (__stdcall *dwmExtendFrameIntoClientArea)(HWND hWnd, const MARGINS* pMarInset);
193         HRESULT (__stdcall *dwmEnableBlurBehindWindow)(HWND hWnd, const DWM_BLURBEHIND* pBlurBehind);
194 }
195 g_dwmapi_pointers = { NULL, NULL, NULL };
196
197 static void initialize_dwmapi(void)
198 {
199         QReadLocker writeLock(&g_dwmapi_lock);
200
201         //Not initialized yet?
202         if(g_dwmapi_initialized)
203         {
204                 return;
205         }
206         
207         //Reset function pointers
208         g_dwmapi_pointers.dwmIsCompositionEnabled      = NULL;
209         g_dwmapi_pointers.dwmExtendFrameIntoClientArea = NULL;
210         g_dwmapi_pointers.dwmEnableBlurBehindWindow    = NULL;
211                         
212         //Does OS support DWM?
213         const MUtils::OS::Version::os_version_t &osVersion = MUtils::OS::os_version();
214         if(osVersion >= MUtils::OS::Version::WINDOWS_VISTA)
215         {
216                 //Load DWMAPI.DLL
217                 g_dwmapi_library.reset(new QLibrary("dwmapi.dll"));
218                 if(g_dwmapi_library->load())
219                 {
220                         //Initialize function pointers
221                         g_dwmapi_pointers.dwmIsCompositionEnabled      = (HRESULT (__stdcall*)(BOOL*))                       g_dwmapi_library->resolve("DwmIsCompositionEnabled");
222                         g_dwmapi_pointers.dwmExtendFrameIntoClientArea = (HRESULT (__stdcall*)(HWND, const MARGINS*))        g_dwmapi_library->resolve("DwmExtendFrameIntoClientArea");
223                         g_dwmapi_pointers.dwmEnableBlurBehindWindow    = (HRESULT (__stdcall*)(HWND, const DWM_BLURBEHIND*)) g_dwmapi_library->resolve("DwmEnableBlurBehindWindow");
224                 }
225                 else
226                 {
227                         g_dwmapi_library.reset(NULL);
228                         qWarning("Failed to load DWMAPI.DLL on a DWM-enabled system!");
229                 }
230         }
231
232         g_dwmapi_initialized = true;
233 }
234
235 bool MUtils::GUI::sheet_of_glass(QWidget *const window)
236 {
237         QReadLocker readLock(&g_dwmapi_lock);
238
239         //Initialize the DWM API
240         if(!g_dwmapi_initialized)
241         {
242                 readLock.unlock();
243                 initialize_dwmapi();
244                 readLock.relock();
245         }
246
247         //Required functions available?
248         BOOL bCompositionEnabled = FALSE;
249         if(g_dwmapi_pointers.dwmIsCompositionEnabled && g_dwmapi_pointers.dwmExtendFrameIntoClientArea && g_dwmapi_pointers.dwmEnableBlurBehindWindow)
250         {
251                 //Check if composition is currently enabled
252                 if(HRESULT hr = g_dwmapi_pointers.dwmIsCompositionEnabled(&bCompositionEnabled))
253                 {
254                         qWarning("DwmIsCompositionEnabled function has failed! (error %d)", hr);
255                         return false;
256                 }
257         }
258         
259         //All functions available *and* composition enabled?
260         if(!bCompositionEnabled)
261         {
262                 return false;
263         }
264
265         //Enable the "sheet of glass" effect on this window
266         MARGINS margins = {-1, -1, -1, -1};
267         if(HRESULT hr = g_dwmapi_pointers.dwmExtendFrameIntoClientArea(window->winId(), &margins))
268         {
269                 qWarning("DwmExtendFrameIntoClientArea function has failed! (error %d)", hr);
270                 return false;
271         }
272
273         //Create and populate the Blur Behind structure
274         DWM_BLURBEHIND bb;
275         memset(&bb, 0, sizeof(DWM_BLURBEHIND));
276         bb.fEnable = TRUE;
277         bb.dwFlags = DWM_BB_ENABLE;
278         if(HRESULT hr = g_dwmapi_pointers.dwmEnableBlurBehindWindow(window->winId(), &bb))
279         {
280                 qWarning("DwmEnableBlurBehindWindow function has failed! (error %d)", hr);
281                 return false;
282         }
283
284         //Required for Qt
285         window->setAutoFillBackground(false);
286         window->setAttribute(Qt::WA_TranslucentBackground);
287         window->setAttribute(Qt::WA_NoSystemBackground);
288
289         return true;
290 }
291
292 bool MUtils::GUI::sheet_of_glass_update(QWidget *const window)
293 {
294         QReadLocker readLock(&g_dwmapi_lock);
295
296         //Initialize the DWM API
297         if(!g_dwmapi_initialized)
298         {
299                 readLock.unlock();
300                 initialize_dwmapi();
301                 readLock.relock();
302         }
303
304         //Required functions available?
305         BOOL bCompositionEnabled = FALSE;
306         if(g_dwmapi_pointers.dwmIsCompositionEnabled && g_dwmapi_pointers.dwmExtendFrameIntoClientArea && g_dwmapi_pointers.dwmEnableBlurBehindWindow)
307         {
308                 //Check if composition is currently enabled
309                 if(HRESULT hr = g_dwmapi_pointers.dwmIsCompositionEnabled(&bCompositionEnabled))
310                 {
311                         qWarning("DwmIsCompositionEnabled function has failed! (error %d)", hr);
312                         return false;
313                 }
314         }
315         
316         //All functions available *and* composition enabled?
317         if(!bCompositionEnabled)
318         {
319                 return false;
320         }
321
322         //Create and populate the Blur Behind structure
323         DWM_BLURBEHIND bb;
324         memset(&bb, 0, sizeof(DWM_BLURBEHIND));
325         bb.fEnable = TRUE;
326         bb.dwFlags = DWM_BB_ENABLE;
327         if(HRESULT hr = g_dwmapi_pointers.dwmEnableBlurBehindWindow(window->winId(), &bb))
328         {
329                 qWarning("DwmEnableBlurBehindWindow function has failed! (error %d)", hr);
330                 return false;
331         }
332
333         return true;
334 }
335
336 ///////////////////////////////////////////////////////////////////////////////
337 // SYSTEM COLORS
338 ///////////////////////////////////////////////////////////////////////////////
339
340 QColor MUtils::GUI::system_color(const int &color_id)
341 {
342         int nIndex = -1;
343
344         switch(color_id)
345         {
346         case SYSCOLOR_TEXT:
347                 nIndex = COLOR_WINDOWTEXT;              /*Text in windows*/
348                 break;
349         case SYSCOLOR_BACKGROUND:
350                 nIndex = COLOR_WINDOW;                  /*Window background*/
351                 break;
352         case SYSCOLOR_CAPTION:
353                 nIndex = COLOR_CAPTIONTEXT;             /*Text in caption, size box, and scroll bar arrow box*/
354                 break;
355         default:
356                 qWarning("Unknown system color id (%d) specified!", color_id);
357                 nIndex = COLOR_WINDOWTEXT;
358         }
359         
360         const DWORD rgb = GetSysColor(nIndex);
361         QColor color(GetRValue(rgb), GetGValue(rgb), GetBValue(rgb));
362         return color;
363 }