1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2014 LoRd_MuldeR <MuldeR2@GMX.de>
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version, but always including the *additional*
9 // restrictions defined in the "License.txt" file.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
26 #include "Targetver.h"
28 //Visual Leaks Detector
33 #define WIN32_LEAN_AND_MEAN
44 #include <QApplication>
49 #include <QImageReader>
51 #include <QLibraryInfo>
53 #include <QMessageBox>
54 #include <QPlastiqueStyle>
56 #include <QReadWriteLock>
61 #include <QTranslator>
66 #define LAMEXP_INC_CONFIG
71 #include <MUtils/Global.h>
72 #include <MUtils/OSSupport.h>
73 #include <MUtils/Terminal.h>
87 //Initialize static Qt plugins
89 #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
93 Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
94 Q_IMPORT_PLUGIN(QICOPlugin)
98 ///////////////////////////////////////////////////////////////////////////////
100 ///////////////////////////////////////////////////////////////////////////////
102 #define _LAMEXP_MAKE_STR(STR) #STR
103 #define LAMEXP_MAKE_STR(STR) _LAMEXP_MAKE_STR(STR)
105 ///////////////////////////////////////////////////////////////////////////////
107 ///////////////////////////////////////////////////////////////////////////////
118 //Win32 Theme support
125 g_lamexp_themes_enabled;
127 //Win32 DWM API functions
131 HRESULT (__stdcall *dwmIsCompositionEnabled)(BOOL *bEnabled);
132 HRESULT (__stdcall *dwmExtendFrameIntoClientArea)(HWND hWnd, const MARGINS* pMarInset);
133 HRESULT (__stdcall *dwmEnableBlurBehindWindow)(HWND hWnd, const DWM_BLURBEHIND* pBlurBehind);
134 QLibrary *dwmapi_dll;
142 QHash<const QString, const unsigned char*> *sound_db;
148 static const DWORD g_main_thread_id = GetCurrentThreadId();
151 const char* LAMEXP_DEFAULT_LANGID = "en";
152 const char* LAMEXP_DEFAULT_TRANSLATION = "LameXP_EN.qm";
154 ///////////////////////////////////////////////////////////////////////////////
156 ///////////////////////////////////////////////////////////////////////////////
159 * Convert QIcon to HICON -> caller is responsible for destroying the HICON!
161 static HICON lamexp_qicon2hicon(const QIcon &icon, const int w, const int h)
165 QPixmap pixmap = icon.pixmap(w, h);
168 return pixmap.toWinHICON();
175 * Check if visual themes are enabled (WinXP and later)
177 bool lamexp_themes_enabled(void)
179 typedef int (WINAPI *IsAppThemedFun)(void);
181 QReadLocker readLock(&g_lamexp_themes_enabled.lock);
182 if(g_lamexp_themes_enabled.bInitialized)
184 return g_lamexp_themes_enabled.bThemesEnabled;
188 QWriteLocker writeLock(&g_lamexp_themes_enabled.lock);
190 if(!g_lamexp_themes_enabled.bInitialized)
192 g_lamexp_themes_enabled.bThemesEnabled = false;
193 const MUtils::OS::Version::os_version_t &osVersion = MUtils::OS::os_version();
194 if(osVersion >= MUtils::OS::Version::WINDOWS_WINXP)
196 IsAppThemedFun IsAppThemedPtr = NULL;
197 QLibrary uxTheme(QString("%1/UxTheme.dll").arg(MUtils::OS::known_folder(MUtils::OS::FOLDER_SYSTEMFOLDER)));
200 IsAppThemedPtr = (IsAppThemedFun) uxTheme.resolve("IsAppThemed");
204 g_lamexp_themes_enabled.bThemesEnabled = IsAppThemedPtr();
205 if(!g_lamexp_themes_enabled.bThemesEnabled)
207 qWarning("Theme support is disabled for this process!");
211 g_lamexp_themes_enabled.bInitialized = true;
214 return g_lamexp_themes_enabled.bThemesEnabled;
218 * Block window "move" message
220 bool lamexp_block_window_move(void *message)
224 MSG *msg = reinterpret_cast<MSG*>(message);
225 if((msg->message == WM_SYSCOMMAND) && (msg->wParam == SC_MOVE))
229 if((msg->message == WM_NCLBUTTONDOWN) && (msg->wParam == HTCAPTION))
237 bool lamexp_beep(int beepType)
241 case lamexp_beep_info: return MessageBeep(MB_ICONASTERISK) == TRUE; break;
242 case lamexp_beep_warning: return MessageBeep(MB_ICONEXCLAMATION) == TRUE; break;
243 case lamexp_beep_error: return MessageBeep(MB_ICONHAND) == TRUE; break;
244 default: return false;
249 * Play a sound (from resources)
251 bool lamexp_play_sound(const QString &name, const bool bAsync)
253 const unsigned char *data = NULL;
255 //Try to look-up the sound in the cache first
258 QReadLocker readLock(&g_lamexp_sounds.lock);
259 if(g_lamexp_sounds.sound_db && g_lamexp_sounds.sound_db->contains(name))
261 data = g_lamexp_sounds.sound_db->value(name);
265 //If data not found in cache, try to load from resource!
266 if((!data) && (!name.isEmpty()))
268 QResource resource(QString(":/sounds/%1.wav").arg(name));
269 if(resource.isValid() && (data = resource.data()))
271 QWriteLocker writeLock(&g_lamexp_sounds.lock);
272 if(!g_lamexp_sounds.sound_db)
274 g_lamexp_sounds.sound_db = new QHash<const QString, const unsigned char*>();
276 g_lamexp_sounds.sound_db->insert(name, data);
280 qWarning("Sound effect \"%s\" not found!", MUTILS_UTF8(name));
284 //Play the sound, if availbale
287 return PlaySound(LPCWSTR(data), NULL, (SND_MEMORY | (bAsync ? SND_ASYNC : SND_SYNC))) != FALSE;
294 * Play a sound (system alias)
296 bool lamexp_play_sound_alias(const QString &alias, const bool bAsync)
298 return PlaySound(MUTILS_WCHR(alias), GetModuleHandle(NULL), (SND_ALIAS | (bAsync ? SND_ASYNC : SND_SYNC))) != FALSE;
302 * Play a sound (from external DLL)
304 bool lamexp_play_sound_file(const QString &library, const unsigned short uiSoundIdx, const bool bAsync)
308 QFileInfo libraryFile(library);
309 if(!libraryFile.isAbsolute())
311 const QString &systemDir = MUtils::OS::known_folder(MUtils::OS::FOLDER_SYSTEMFOLDER);
312 if(!systemDir.isEmpty())
314 libraryFile.setFile(QDir(systemDir), libraryFile.fileName());
318 if(libraryFile.exists() && libraryFile.isFile())
320 if(HMODULE module = LoadLibraryW(MUTILS_WCHR(QDir::toNativeSeparators(libraryFile.canonicalFilePath()))))
322 result = (PlaySound(MAKEINTRESOURCE(uiSoundIdx), module, (SND_RESOURCE | (bAsync ? SND_ASYNC : SND_SYNC))) != FALSE);
328 qWarning("PlaySound: File \"%s\" could not be found!", MUTILS_UTF8(libraryFile.absoluteFilePath()));
335 * Insert entry to the window's system menu
337 bool lamexp_append_sysmenu(const QWidget *win, const unsigned int identifier, const QString &text)
341 if(HMENU hMenu = GetSystemMenu(win->winId(), FALSE))
343 ok = (AppendMenuW(hMenu, MF_SEPARATOR, 0, 0) == TRUE);
344 ok = (AppendMenuW(hMenu, MF_STRING, identifier, MUTILS_WCHR(text)) == TRUE);
351 * Insert entry to the window's system menu
353 bool lamexp_check_sysmenu_msg(void *message, const unsigned int identifier)
355 return (((MSG*)message)->message == WM_SYSCOMMAND) && ((((MSG*)message)->wParam & 0xFFF0) == identifier);
359 * Update system menu entry
361 bool lamexp_update_sysmenu(const QWidget *win, const unsigned int identifier, const QString &text)
365 if(HMENU hMenu = ::GetSystemMenu(win->winId(), FALSE))
367 ok = (ModifyMenu(hMenu, identifier, MF_STRING | MF_BYCOMMAND, identifier, MUTILS_WCHR(text)) == TRUE);
373 * Display the window's close button
375 bool lamexp_enable_close_button(const QWidget *win, const bool bEnable)
379 if(HMENU hMenu = GetSystemMenu(win->winId(), FALSE))
381 ok = (EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | (bEnable ? MF_ENABLED : MF_GRAYED)) == TRUE);
388 * Check whether ESC key has been pressed since the previous call to this function
390 bool lamexp_check_escape_state(void)
392 return (GetAsyncKeyState(VK_ESCAPE) & 0x0001) != 0;
396 * Bring the specifed window to the front
398 bool lamexp_bring_to_front(const QWidget *window)
404 for(int i = 0; (i < 5) && (!ret); i++)
406 ret = (SetForegroundWindow(window->winId()) != FALSE);
407 SwitchToThisWindow(window->winId(), TRUE);
409 LockSetForegroundWindow(LSFW_LOCK);
416 * Bring window of the specifed process to the front (callback)
418 static BOOL CALLBACK lamexp_bring_process_to_front_helper(HWND hwnd, LPARAM lParam)
420 DWORD processId = *reinterpret_cast<WORD*>(lParam);
421 DWORD windowProcessId = NULL;
422 GetWindowThreadProcessId(hwnd, &windowProcessId);
423 if(windowProcessId == processId)
425 SwitchToThisWindow(hwnd, TRUE);
426 SetForegroundWindow(hwnd);
434 * Bring window of the specifed process to the front
436 bool lamexp_bring_process_to_front(const unsigned long pid)
438 return EnumWindows(lamexp_bring_process_to_front_helper, reinterpret_cast<LPARAM>(&pid)) == TRUE;
441 static void lamexp_init_dwmapi(void)
443 QReadLocker writeLock(&g_lamexp_dwmapi.lock);
445 //Not initialized yet?
446 if(g_lamexp_dwmapi.bInitialized)
451 //Reset function pointers
452 g_lamexp_dwmapi.dwmIsCompositionEnabled = NULL;
453 g_lamexp_dwmapi.dwmExtendFrameIntoClientArea = NULL;
454 g_lamexp_dwmapi.dwmEnableBlurBehindWindow = NULL;
456 //Does OS support DWM?
457 const MUtils::OS::Version::os_version_t &osVersion = MUtils::OS::os_version();
458 if(osVersion >= MUtils::OS::Version::WINDOWS_VISTA)
461 g_lamexp_dwmapi.dwmapi_dll = new QLibrary("dwmapi.dll");
462 if(g_lamexp_dwmapi.dwmapi_dll->load())
464 //Initialize function pointers
465 g_lamexp_dwmapi.dwmIsCompositionEnabled = (HRESULT (__stdcall*)(BOOL*)) g_lamexp_dwmapi.dwmapi_dll->resolve("DwmIsCompositionEnabled");
466 g_lamexp_dwmapi.dwmExtendFrameIntoClientArea = (HRESULT (__stdcall*)(HWND, const MARGINS*)) g_lamexp_dwmapi.dwmapi_dll->resolve("DwmExtendFrameIntoClientArea");
467 g_lamexp_dwmapi.dwmEnableBlurBehindWindow = (HRESULT (__stdcall*)(HWND, const DWM_BLURBEHIND*)) g_lamexp_dwmapi.dwmapi_dll->resolve("DwmEnableBlurBehindWindow");
471 MUTILS_DELETE(g_lamexp_dwmapi.dwmapi_dll);
472 qWarning("Failed to load DWMAPI.DLL on a DWM-enabled system!");
476 g_lamexp_dwmapi.bInitialized = true;
480 * Enable "sheet of glass" effect on the given Window
482 bool lamexp_sheet_of_glass(QWidget *window)
484 QReadLocker readLock(&g_lamexp_dwmapi.lock);
486 //Initialize the DWM API
487 while(!g_lamexp_dwmapi.bInitialized)
490 lamexp_init_dwmapi();
494 BOOL bCompositionEnabled = FALSE;
496 //Required functions available?
497 if((g_lamexp_dwmapi.dwmIsCompositionEnabled != NULL) && (g_lamexp_dwmapi.dwmExtendFrameIntoClientArea != NULL) && (g_lamexp_dwmapi.dwmEnableBlurBehindWindow != NULL))
499 //Check if composition is currently enabled
500 if(HRESULT hr = g_lamexp_dwmapi.dwmIsCompositionEnabled(&bCompositionEnabled))
502 qWarning("DwmIsCompositionEnabled function has failed! (error %d)", hr);
507 //All functions available *and* composition enabled?
508 if(!bCompositionEnabled)
513 //Enable the "sheet of glass" effect on this window
514 MARGINS margins = {-1, -1, -1, -1};
515 if(HRESULT hr = g_lamexp_dwmapi.dwmExtendFrameIntoClientArea(window->winId(), &margins))
517 qWarning("DwmExtendFrameIntoClientArea function has failed! (error %d)", hr);
521 //Create and populate the Blur Behind structure
523 memset(&bb, 0, sizeof(DWM_BLURBEHIND));
525 bb.dwFlags = DWM_BB_ENABLE;
526 if(HRESULT hr = g_lamexp_dwmapi.dwmEnableBlurBehindWindow(window->winId(), &bb))
528 qWarning("DwmEnableBlurBehindWindow function has failed! (error %d)", hr);
533 window->setAutoFillBackground(false);
534 window->setAttribute(Qt::WA_TranslucentBackground);
535 window->setAttribute(Qt::WA_NoSystemBackground);
541 * Update "sheet of glass" effect on the given Window
543 bool lamexp_sheet_of_glass_update(QWidget *window)
545 QReadLocker readLock(&g_lamexp_dwmapi.lock);
547 //Initialize the DWM API
548 while(!g_lamexp_dwmapi.bInitialized)
551 lamexp_init_dwmapi();
555 BOOL bCompositionEnabled = FALSE;
557 //Required functions available?
558 if((g_lamexp_dwmapi.dwmIsCompositionEnabled != NULL) && (g_lamexp_dwmapi.dwmEnableBlurBehindWindow != NULL))
560 //Check if composition is currently enabled
561 if(HRESULT hr = g_lamexp_dwmapi.dwmIsCompositionEnabled(&bCompositionEnabled))
563 qWarning("DwmIsCompositionEnabled function has failed! (error %d)", hr);
568 //All functions available *and* composition enabled?
569 if(!bCompositionEnabled)
574 //Create and populate the Blur Behind structure
576 memset(&bb, 0, sizeof(DWM_BLURBEHIND));
578 bb.dwFlags = DWM_BB_ENABLE;
579 if(HRESULT hr = g_lamexp_dwmapi.dwmEnableBlurBehindWindow(window->winId(), &bb))
581 qWarning("DwmEnableBlurBehindWindow function has failed! (error %d)", hr);
589 * Update the window icon
591 lamexp_icon_t *lamexp_set_window_icon(QWidget *window, const QIcon &icon, const bool bIsBigIcon)
595 const int extend = (bIsBigIcon ? 32 : 16);
596 if(HICON hIcon = lamexp_qicon2hicon(icon, extend, extend))
598 SendMessage(window->winId(), WM_SETICON, (bIsBigIcon ? ICON_BIG : ICON_SMALL), LPARAM(hIcon));
599 return reinterpret_cast<lamexp_icon_t*>(hIcon);
608 void lamexp_free_window_icon(lamexp_icon_t *icon)
610 if(HICON hIcon = reinterpret_cast<HICON>(icon))
617 * Get system color info
619 QColor lamexp_system_color(const int color_id)
625 case lamexp_syscolor_text:
626 nIndex = COLOR_WINDOWTEXT; /*Text in windows*/
628 case lamexp_syscolor_background:
629 nIndex = COLOR_WINDOW; /*Window background*/
631 case lamexp_syscolor_caption:
632 nIndex = COLOR_CAPTIONTEXT; /*Text in caption, size box, and scroll bar arrow box*/
635 qWarning("Unknown system color id (%d) specified!", color_id);
636 nIndex = COLOR_WINDOWTEXT;
639 const DWORD rgb = GetSysColor(nIndex);
640 QColor color(GetRValue(rgb), GetGValue(rgb), GetBValue(rgb));
644 ///////////////////////////////////////////////////////////////////////////////
646 ///////////////////////////////////////////////////////////////////////////////
648 extern "C" void _lamexp_global_init_win32(void)
650 //Zero *before* constructors are called
651 MUTILS_ZERO_MEMORY(g_lamexp_wine);
652 MUTILS_ZERO_MEMORY(g_lamexp_themes_enabled);
653 MUTILS_ZERO_MEMORY(g_lamexp_dwmapi);
654 MUTILS_ZERO_MEMORY(g_lamexp_sounds);
657 ///////////////////////////////////////////////////////////////////////////////
659 ///////////////////////////////////////////////////////////////////////////////
661 extern "C" void _lamexp_global_free_win32(void)
664 g_lamexp_dwmapi.dwmIsCompositionEnabled = NULL;
665 g_lamexp_dwmapi.dwmExtendFrameIntoClientArea = NULL;
666 g_lamexp_dwmapi.dwmEnableBlurBehindWindow = NULL;
667 MUTILS_DELETE(g_lamexp_dwmapi.dwmapi_dll);
670 MUTILS_DELETE(g_lamexp_sounds.sound_db);