mirror of
https://github.com/chibicitiberiu/rainmeter-studio.git
synced 2024-02-24 04:33:31 +00:00
64888434c9
!RainmeterWriteKeyValue "Section" "Key" "Value" ("FileSpec"), where "FileSpec" is an optional parameter. If FileSpec is not present, the change is automatically done in the currently running skin file. If the bang is executed from command line, FileSpec is required. - Some improvements of MouseOver/Leave detection. - Fixed the issue that window dragging brings on MouseLeave if "Show window contents while dragging" is not set. - Fixed the issue that window dragging can't be begun by dragging the Button. - Fixed the issue that a reaction of the upper Button penetrates to the lower Buttons or the other window.
1144 lines
29 KiB
C++
1144 lines
29 KiB
C++
/*
|
|
Copyright (C) 2010 spx
|
|
|
|
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 2
|
|
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, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "StdAfx.h"
|
|
#include "System.h"
|
|
#include "Litestep.h"
|
|
#include "Rainmeter.h"
|
|
#include "MeasureNet.h"
|
|
#include "Error.h"
|
|
|
|
#define DEBUG_VERBOSE (0) // Set 1 if you need verbose logging.
|
|
|
|
enum TIMER
|
|
{
|
|
TIMER_SHOWDESKTOP = 1,
|
|
TIMER_COMPOSITION = 2,
|
|
TIMER_NETSTATS = 3,
|
|
TIMER_DELETELATER = 4
|
|
};
|
|
enum INTERVAL
|
|
{
|
|
INTERVAL_SHOWDESKTOP = 250,
|
|
INTERVAL_COMPOSITION = 250,
|
|
INTERVAL_NETSTATS = 10000,
|
|
INTERVAL_DELETELATER = 1000
|
|
};
|
|
|
|
MULTIMONITOR_INFO CSystem::c_Monitors = { 0 };
|
|
|
|
HWND CSystem::c_Window = NULL;
|
|
HWND CSystem::c_HelperWindow = NULL;
|
|
|
|
HWINEVENTHOOK CSystem::c_WinEventHook = NULL;
|
|
|
|
bool CSystem::c_DwmCompositionEnabled = false;
|
|
bool CSystem::c_ShowDesktop = false;
|
|
|
|
extern CRainmeter* Rainmeter;
|
|
|
|
/*
|
|
** Initialize
|
|
**
|
|
** Creates a helper window to detect changes in the system.
|
|
**
|
|
*/
|
|
void CSystem::Initialize(HINSTANCE instance)
|
|
{
|
|
WNDCLASS wc = {0};
|
|
wc.lpfnWndProc = (WNDPROC)WndProc;
|
|
wc.hInstance = instance;
|
|
wc.lpszClassName = L"RainmeterSystemClass";
|
|
|
|
RegisterClass(&wc);
|
|
|
|
c_Window = CreateWindowEx(
|
|
WS_EX_TOOLWINDOW,
|
|
L"RainmeterSystemClass",
|
|
L"SystemWindow",
|
|
WS_POPUP,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
NULL,
|
|
NULL,
|
|
instance,
|
|
NULL);
|
|
|
|
c_HelperWindow = CreateWindowEx(
|
|
WS_EX_TOOLWINDOW,
|
|
L"RainmeterSystemClass",
|
|
L"PositioningHelperWindow",
|
|
WS_POPUP,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
NULL,
|
|
NULL,
|
|
instance,
|
|
NULL);
|
|
|
|
#ifndef _WIN64
|
|
SetWindowLong(c_Window, GWL_USERDATA, magicDWord);
|
|
SetWindowLong(c_HelperWindow, GWL_USERDATA, magicDWord);
|
|
#endif
|
|
|
|
SetWindowPos(c_Window, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
|
|
SetWindowPos(c_HelperWindow, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
|
|
|
|
SetMultiMonitorInfo();
|
|
|
|
c_DwmCompositionEnabled = (DwmIsCompositionEnabled() == TRUE);
|
|
|
|
c_WinEventHook = SetWinEventHook(
|
|
EVENT_SYSTEM_FOREGROUND,
|
|
EVENT_SYSTEM_FOREGROUND,
|
|
NULL,
|
|
MyWinEventProc,
|
|
0,
|
|
0,
|
|
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
|
|
|
|
SetTimer(c_Window, TIMER_SHOWDESKTOP, INTERVAL_SHOWDESKTOP, NULL);
|
|
SetTimer(c_Window, TIMER_NETSTATS, INTERVAL_NETSTATS, NULL);
|
|
SetTimer(c_Window, TIMER_DELETELATER, INTERVAL_DELETELATER, NULL);
|
|
}
|
|
|
|
/*
|
|
** Finalize
|
|
**
|
|
** Destroys a window.
|
|
**
|
|
*/
|
|
void CSystem::Finalize()
|
|
{
|
|
KillTimer(c_Window, TIMER_SHOWDESKTOP);
|
|
KillTimer(c_Window, TIMER_COMPOSITION);
|
|
KillTimer(c_Window, TIMER_NETSTATS);
|
|
KillTimer(c_Window, TIMER_DELETELATER);
|
|
|
|
if (c_WinEventHook)
|
|
{
|
|
UnhookWinEvent(c_WinEventHook);
|
|
c_WinEventHook = NULL;
|
|
}
|
|
|
|
if (c_HelperWindow)
|
|
{
|
|
DestroyWindow(c_HelperWindow);
|
|
c_HelperWindow = NULL;
|
|
}
|
|
|
|
if (c_Window)
|
|
{
|
|
DestroyWindow(c_Window);
|
|
c_Window = NULL;
|
|
}
|
|
}
|
|
|
|
/* MyInfoEnumProc
|
|
**
|
|
** Retrieves the multi-monitor information.
|
|
**
|
|
*/
|
|
BOOL CALLBACK MyInfoEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
|
|
{
|
|
MULTIMONITOR_INFO* m = (MULTIMONITOR_INFO*)dwData;
|
|
|
|
MONITORINFOEX info;
|
|
info.cbSize = sizeof(MONITORINFOEX);
|
|
GetMonitorInfo(hMonitor, &info);
|
|
|
|
if (CRainmeter::GetDebug())
|
|
{
|
|
DebugLog(info.szDevice);
|
|
DebugLog(L" Flags : %s(0x%08X)", (info.dwFlags & MONITORINFOF_PRIMARY) ? L"PRIMARY " : L"", info.dwFlags);
|
|
DebugLog(L" Handle : 0x%08X", hMonitor);
|
|
DebugLog(L" ScrArea : L=%i, T=%i, R=%i, B=%i (W=%i, H=%i)",
|
|
lprcMonitor->left, lprcMonitor->top, lprcMonitor->right, lprcMonitor->bottom,
|
|
lprcMonitor->right - lprcMonitor->left, lprcMonitor->bottom - lprcMonitor->top);
|
|
DebugLog(L" WorkArea : L=%i, T=%i, R=%i, B=%i (W=%i, H=%i)",
|
|
info.rcWork.left, info.rcWork.top, info.rcWork.right, info.rcWork.bottom,
|
|
info.rcWork.right - info.rcWork.left, info.rcWork.bottom - info.rcWork.top);
|
|
}
|
|
if (m == NULL) return TRUE;
|
|
|
|
if (m->useEnumDisplayDevices)
|
|
{
|
|
for (size_t i = 0; i < m->monitors.size(); ++i)
|
|
{
|
|
if (m->monitors[i].handle == NULL && _wcsnicmp(info.szDevice, m->monitors[i].deviceName, 32) == 0)
|
|
{
|
|
m->monitors[i].handle = hMonitor;
|
|
m->monitors[i].screen = *lprcMonitor;
|
|
m->monitors[i].work = info.rcWork;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else // use only EnumDisplayMonitors
|
|
{
|
|
MONITOR_INFO monitor = {0};
|
|
monitor.active = true;
|
|
|
|
monitor.handle = hMonitor;
|
|
monitor.screen = *lprcMonitor;
|
|
monitor.work = info.rcWork;
|
|
|
|
wcsncpy(monitor.deviceName, info.szDevice, 32); // E.g. "\\.\DISPLAY1"
|
|
|
|
// Get the monitor name (E.g. "Generic Non-PnP Monitor")
|
|
DISPLAY_DEVICE ddm = {0};
|
|
ddm.cb = sizeof(DISPLAY_DEVICE);
|
|
DWORD dwMon = 0;
|
|
while (EnumDisplayDevices(info.szDevice, dwMon++, &ddm, 0))
|
|
{
|
|
if (ddm.StateFlags & DISPLAY_DEVICE_ACTIVE && ddm.StateFlags & DISPLAY_DEVICE_ATTACHED)
|
|
{
|
|
wcsncpy(monitor.monitorName, ddm.DeviceString, 128);
|
|
break;
|
|
}
|
|
}
|
|
|
|
m->monitors.push_back(monitor);
|
|
|
|
if (info.dwFlags & MONITORINFOF_PRIMARY)
|
|
{
|
|
// It's primary monitor!
|
|
m->primary = (int)m->monitors.size();
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* GetMonitorCount
|
|
**
|
|
** Returns the number of monitors.
|
|
**
|
|
*/
|
|
size_t CSystem::GetMonitorCount()
|
|
{
|
|
if (c_Monitors.monitors.size() == 0)
|
|
{
|
|
SetMultiMonitorInfo();
|
|
}
|
|
return c_Monitors.monitors.size();
|
|
}
|
|
|
|
/* SetMultiMonitorInfo
|
|
**
|
|
** Sets the multi-monitor information.
|
|
**
|
|
*/
|
|
void CSystem::SetMultiMonitorInfo()
|
|
{
|
|
std::vector<MONITOR_INFO>& monitors = c_Monitors.monitors;
|
|
bool logging = CRainmeter::GetDebug();
|
|
|
|
if (monitors.capacity() < 16) { monitors.reserve(16); }
|
|
|
|
c_Monitors.vsT = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
|
c_Monitors.vsL = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
|
c_Monitors.vsH = GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
|
c_Monitors.vsW = GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
|
|
|
c_Monitors.primary = 1; // If primary screen is not found, 1st screen is assumed as primary screen.
|
|
|
|
c_Monitors.useEnumDisplayDevices = true;
|
|
c_Monitors.useEnumDisplayMonitors = false;
|
|
|
|
if (logging)
|
|
{
|
|
DebugLog(L"------------------------------");
|
|
DebugLog(L"* EnumDisplayDevices / EnumDisplaySettings API");
|
|
}
|
|
|
|
DISPLAY_DEVICE dd = {0};
|
|
dd.cb = sizeof(DISPLAY_DEVICE);
|
|
|
|
if (EnumDisplayDevices(NULL, 0, &dd, 0))
|
|
{
|
|
DWORD dwDevice = 0;
|
|
|
|
do
|
|
{
|
|
std::wstring msg;
|
|
|
|
if (logging)
|
|
{
|
|
DebugLog(dd.DeviceName);
|
|
|
|
if (dd.StateFlags & DISPLAY_DEVICE_ACTIVE)
|
|
{
|
|
msg += L"ACTIVE ";
|
|
}
|
|
if (dd.StateFlags & DISPLAY_DEVICE_MULTI_DRIVER)
|
|
{
|
|
msg += L"MULTI ";
|
|
}
|
|
if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
|
|
{
|
|
msg += L"PRIMARY ";
|
|
}
|
|
if (dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)
|
|
{
|
|
msg += L"MIRROR ";
|
|
}
|
|
if (dd.StateFlags & DISPLAY_DEVICE_VGA_COMPATIBLE)
|
|
{
|
|
msg += L"VGA ";
|
|
}
|
|
if (dd.StateFlags & DISPLAY_DEVICE_REMOVABLE)
|
|
{
|
|
msg += L"REMOVABLE ";
|
|
}
|
|
if (dd.StateFlags & DISPLAY_DEVICE_MODESPRUNED)
|
|
{
|
|
msg += L"PRUNED ";
|
|
}
|
|
if (dd.StateFlags & DISPLAY_DEVICE_REMOTE)
|
|
{
|
|
msg += L"REMOTE ";
|
|
}
|
|
if (dd.StateFlags & DISPLAY_DEVICE_DISCONNECT)
|
|
{
|
|
msg += L"DISCONNECT ";
|
|
}
|
|
}
|
|
|
|
if ((dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) == 0)
|
|
{
|
|
MONITOR_INFO monitor = {0};
|
|
|
|
monitor.handle = NULL;
|
|
wcsncpy(monitor.deviceName, dd.DeviceName, 32); // E.g. "\\.\DISPLAY1"
|
|
|
|
// Get the monitor name (E.g. "Generic Non-PnP Monitor")
|
|
DISPLAY_DEVICE ddm = {0};
|
|
ddm.cb = sizeof(DISPLAY_DEVICE);
|
|
DWORD dwMon = 0;
|
|
while (EnumDisplayDevices(dd.DeviceName, dwMon++, &ddm, 0))
|
|
{
|
|
if (ddm.StateFlags & DISPLAY_DEVICE_ACTIVE && ddm.StateFlags & DISPLAY_DEVICE_ATTACHED)
|
|
{
|
|
wcsncpy(monitor.monitorName, ddm.DeviceString, 128);
|
|
|
|
if (logging)
|
|
{
|
|
DebugLog(L" Name : %s", ddm.DeviceString);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (logging)
|
|
{
|
|
DebugLog(L" Adapter : %s", dd.DeviceString);
|
|
DebugLog(L" Flags : %s(0x%08X)", msg.c_str(), dd.StateFlags);
|
|
}
|
|
|
|
if (dd.StateFlags & DISPLAY_DEVICE_ACTIVE)
|
|
{
|
|
monitor.active = true;
|
|
|
|
DEVMODE dm = {0};
|
|
dm.dmSize = sizeof(DEVMODE);
|
|
|
|
if (EnumDisplaySettings(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm))
|
|
{
|
|
POINT pos = {dm.dmPosition.x, dm.dmPosition.y};
|
|
monitor.handle = MonitorFromPoint(pos, MONITOR_DEFAULTTONULL);
|
|
|
|
if (logging)
|
|
{
|
|
DebugLog(L" Handle : 0x%08X", monitor.handle);
|
|
}
|
|
}
|
|
|
|
if (monitor.handle != NULL)
|
|
{
|
|
MONITORINFO info = {0};
|
|
info.cbSize = sizeof(MONITORINFO);
|
|
GetMonitorInfo(monitor.handle, &info);
|
|
|
|
monitor.screen = info.rcMonitor;
|
|
monitor.work = info.rcWork;
|
|
|
|
if (logging)
|
|
{
|
|
DebugLog(L" ScrArea : L=%i, T=%i, R=%i, B=%i (W=%i, H=%i)",
|
|
info.rcMonitor.left, info.rcMonitor.top, info.rcMonitor.right, info.rcMonitor.bottom,
|
|
info.rcMonitor.right - info.rcMonitor.left, info.rcMonitor.bottom - info.rcMonitor.top);
|
|
DebugLog(L" WorkArea : L=%i, T=%i, R=%i, B=%i (W=%i, H=%i)",
|
|
info.rcWork.left, info.rcWork.top, info.rcWork.right, info.rcWork.bottom,
|
|
info.rcWork.right - info.rcWork.left, info.rcWork.bottom - info.rcWork.top);
|
|
}
|
|
}
|
|
else // monitor not found
|
|
{
|
|
c_Monitors.useEnumDisplayMonitors = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
monitor.active = false;
|
|
}
|
|
|
|
monitors.push_back(monitor);
|
|
|
|
if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
|
|
{
|
|
// It's primary monitor!
|
|
c_Monitors.primary = (int)monitors.size();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (logging)
|
|
{
|
|
DebugLog(L" Adapter : %s", dd.DeviceString);
|
|
DebugLog(L" Flags : %s(0x%08X)", msg.c_str(), dd.StateFlags);
|
|
}
|
|
}
|
|
++dwDevice;
|
|
} while (EnumDisplayDevices(NULL, dwDevice, &dd, 0));
|
|
}
|
|
|
|
if (monitors.empty()) // Failed to enumerate the non-mirroring monitors
|
|
{
|
|
DebugLog(L"Failed to enumerate the non-mirroring monitors. Only EnumDisplayMonitors is used instead.");
|
|
c_Monitors.useEnumDisplayDevices = false;
|
|
c_Monitors.useEnumDisplayMonitors = true;
|
|
}
|
|
|
|
if (logging)
|
|
{
|
|
DebugLog(L"------------------------------");
|
|
DebugLog(L"* EnumDisplayMonitors API");
|
|
}
|
|
|
|
if (c_Monitors.useEnumDisplayMonitors)
|
|
{
|
|
EnumDisplayMonitors(NULL, NULL, MyInfoEnumProc, (LPARAM)(&c_Monitors));
|
|
|
|
if (monitors.empty()) // Failed to enumerate the monitors
|
|
{
|
|
DebugLog(L"Failed to enumerate the monitors. Prepares the dummy monitor information.");
|
|
c_Monitors.useEnumDisplayMonitors = false;
|
|
|
|
MONITOR_INFO monitor = {0};
|
|
wcscpy(monitor.deviceName, L"DUMMY");
|
|
POINT pos = {0, 0};
|
|
monitor.handle = MonitorFromPoint(pos, MONITOR_DEFAULTTOPRIMARY);
|
|
monitor.screen.left = 0;
|
|
monitor.screen.top = 0;
|
|
monitor.screen.right = GetSystemMetrics(SM_CXSCREEN);
|
|
monitor.screen.bottom = GetSystemMetrics(SM_CYSCREEN);
|
|
SystemParametersInfo(SPI_GETWORKAREA, 0, &(monitor.work), 0);
|
|
monitor.active = true;
|
|
|
|
monitors.push_back(monitor);
|
|
|
|
c_Monitors.primary = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (logging)
|
|
{
|
|
EnumDisplayMonitors(NULL, NULL, MyInfoEnumProc, (LPARAM)NULL); // Only logging
|
|
}
|
|
}
|
|
|
|
if (logging)
|
|
{
|
|
DebugLog(L"------------------------------");
|
|
|
|
std::wstring method = L"* METHOD: ";
|
|
if (c_Monitors.useEnumDisplayDevices)
|
|
{
|
|
method += L"EnumDisplayDevices + ";
|
|
method += c_Monitors.useEnumDisplayMonitors ? L"EnumDisplayMonitors Mode" : L"EnumDisplaySettings Mode";
|
|
}
|
|
else
|
|
{
|
|
method += c_Monitors.useEnumDisplayMonitors ? L"EnumDisplayMonitors Mode" : L"Dummy Mode";
|
|
}
|
|
DebugLog(method.c_str());
|
|
|
|
DebugLog(L"* MONITORS: Count=%i, Primary=@%i", monitors.size(), c_Monitors.primary);
|
|
DebugLog(L"@0: Virtual screen");
|
|
DebugLog(L" L=%i, T=%i, R=%i, B=%i (W=%i, H=%i)",
|
|
c_Monitors.vsL, c_Monitors.vsT, c_Monitors.vsL + c_Monitors.vsW, c_Monitors.vsT + c_Monitors.vsH,
|
|
c_Monitors.vsW, c_Monitors.vsH);
|
|
|
|
for (size_t i = 0; i < monitors.size(); ++i)
|
|
{
|
|
if (monitors[i].active)
|
|
{
|
|
DebugLog(L"@%i: %s (active), MonitorName: %s", i + 1, monitors[i].deviceName, monitors[i].monitorName);
|
|
DebugLog(L" L=%i, T=%i, R=%i, B=%i (W=%i, H=%i)",
|
|
monitors[i].screen.left, monitors[i].screen.top, monitors[i].screen.right, monitors[i].screen.bottom,
|
|
monitors[i].screen.right - monitors[i].screen.left, monitors[i].screen.bottom - monitors[i].screen.top);
|
|
}
|
|
else
|
|
{
|
|
DebugLog(L"@%i: %s (inactive), MonitorName: %s", i + 1, monitors[i].deviceName, monitors[i].monitorName);
|
|
}
|
|
}
|
|
DebugLog(L"------------------------------");
|
|
}
|
|
}
|
|
|
|
/* UpdateWorkareaInfo
|
|
**
|
|
** Updates the workarea information.
|
|
**
|
|
*/
|
|
void CSystem::UpdateWorkareaInfo()
|
|
{
|
|
std::vector<MONITOR_INFO>& monitors = c_Monitors.monitors;
|
|
|
|
if (monitors.empty())
|
|
{
|
|
SetMultiMonitorInfo();
|
|
return;
|
|
}
|
|
|
|
for (size_t i = 0; i < monitors.size(); ++i)
|
|
{
|
|
if (monitors[i].active && monitors[i].handle != NULL)
|
|
{
|
|
MONITORINFO info = {0};
|
|
info.cbSize = sizeof(MONITORINFO);
|
|
GetMonitorInfo(monitors[i].handle, &info);
|
|
|
|
monitors[i].work = info.rcWork;
|
|
|
|
if (CRainmeter::GetDebug())
|
|
{
|
|
DebugLog(L"WorkArea@%i : L=%i, T=%i, R=%i, B=%i (W=%i, H=%i)",
|
|
i + 1,
|
|
info.rcWork.left, info.rcWork.top, info.rcWork.right, info.rcWork.bottom,
|
|
info.rcWork.right - info.rcWork.left, info.rcWork.bottom - info.rcWork.top);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** GetDefaultShellWindow
|
|
**
|
|
** Finds the Default Shell's window.
|
|
**
|
|
*/
|
|
HWND CSystem::GetDefaultShellWindow()
|
|
{
|
|
HWND ShellW = GetShellWindow();
|
|
|
|
if (ShellW)
|
|
{
|
|
HWND hwnd = NULL;
|
|
while (hwnd = FindWindowEx(NULL, hwnd, L"Progman", NULL))
|
|
{
|
|
if (hwnd == ShellW) return ShellW;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
** GetShellDesktopWindow
|
|
**
|
|
** Finds the Shell's desktop window or WorkerW window.
|
|
** If the window is not found, this function returns NULL.
|
|
**
|
|
** Note for WorkerW:
|
|
**
|
|
** In Earlier Windows / 7 (without Aero):
|
|
** This function returns a topmost window handle which is visible.
|
|
**
|
|
** In Windows 7 (with Aero):
|
|
** This function returns a window handle which has the "SHELLDLL_DefView".
|
|
**
|
|
*/
|
|
HWND CSystem::GetShellDesktopWindow(bool findWorkerW)
|
|
{
|
|
HWND DesktopW = NULL, hwnd;
|
|
|
|
HWND ShellW = GetDefaultShellWindow();
|
|
if (!ShellW) return NULL; // Default Shell (Explorer) not running
|
|
|
|
if ((hwnd = FindWindowEx(ShellW, NULL, L"SHELLDLL_DefView", L"")) &&
|
|
(DesktopW = FindWindowEx(hwnd, NULL, L"SysListView32", NULL))) // In Earlier Windows / 7 (without Aero)
|
|
{
|
|
if (findWorkerW)
|
|
{
|
|
HWND WorkerW = NULL;
|
|
while (WorkerW = FindWindowEx(NULL, WorkerW, L"WorkerW", L""))
|
|
{
|
|
if (IsWindowVisible(WorkerW) && BelongToSameProcess(ShellW, WorkerW))
|
|
{
|
|
// Check whether WorkerW covers whole of the screens
|
|
WINDOWPLACEMENT wp = {sizeof(WINDOWPLACEMENT)};
|
|
GetWindowPlacement(WorkerW, &wp);
|
|
|
|
if (wp.rcNormalPosition.left == c_Monitors.vsL &&
|
|
wp.rcNormalPosition.top == c_Monitors.vsT &&
|
|
(wp.rcNormalPosition.right - wp.rcNormalPosition.left) == c_Monitors.vsW &&
|
|
(wp.rcNormalPosition.bottom - wp.rcNormalPosition.top) == c_Monitors.vsH)
|
|
{
|
|
return WorkerW;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (BelongToSameProcess(ShellW, DesktopW))
|
|
{
|
|
return DesktopW;
|
|
}
|
|
}
|
|
}
|
|
else // In Windows 7 (with Aero)
|
|
{
|
|
HWND WorkerW = NULL;
|
|
while (WorkerW = FindWindowEx(NULL, WorkerW, L"WorkerW", L""))
|
|
{
|
|
if (BelongToSameProcess(ShellW, WorkerW))
|
|
{
|
|
if ((hwnd = FindWindowEx(WorkerW, NULL, L"SHELLDLL_DefView", L"")) &&
|
|
(DesktopW = FindWindowEx(hwnd, NULL, L"SysListView32", NULL)))
|
|
{
|
|
if (findWorkerW)
|
|
{
|
|
return WorkerW;
|
|
}
|
|
else
|
|
{
|
|
return DesktopW;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
** BelongToSameProcess
|
|
**
|
|
** Checks whether the given windows belong to the same process.
|
|
**
|
|
*/
|
|
bool CSystem::BelongToSameProcess(HWND hwndA, HWND hwndB)
|
|
{
|
|
DWORD procAId = 0, procBId = 0;
|
|
|
|
GetWindowThreadProcessId(hwndA, &procAId);
|
|
GetWindowThreadProcessId(hwndB, &procBId);
|
|
|
|
return (procAId == procBId);
|
|
}
|
|
|
|
/*
|
|
** MyEnumWindowsProc
|
|
**
|
|
** Retrieves the Rainmeter's meter windows in Z-order.
|
|
**
|
|
*/
|
|
BOOL CALLBACK MyEnumWindowsProc(HWND hwnd, LPARAM lParam)
|
|
{
|
|
bool logging = CRainmeter::GetDebug() && DEBUG_VERBOSE;
|
|
WCHAR className[128] = {0};
|
|
CMeterWindow* Window;
|
|
|
|
if (GetClassName(hwnd, className, 128) > 0 &&
|
|
wcscmp(className, L"RainmeterMeterWindow") == 0 &&
|
|
Rainmeter && (Window = Rainmeter->GetMeterWindow(hwnd)))
|
|
{
|
|
ZPOSITION zPos = Window->GetWindowZPosition();
|
|
if (zPos == ZPOSITION_ONDESKTOP)
|
|
{
|
|
if (logging) DebugLog(L"+ [%c] 0x%08X : %s (Name: \"%s\", zPos=%i)", IsWindowVisible(hwnd) ? L'V' : L'H', hwnd, className, Window->GetSkinName().c_str(), (int)zPos);
|
|
|
|
if (lParam)
|
|
{
|
|
((std::vector<CMeterWindow*>*)lParam)->push_back(Window);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (logging) DebugLog(L"- [%c] 0x%08X : %s (Name: \"%s\", zPos=%i)", IsWindowVisible(hwnd) ? L'V' : L'H', hwnd, className, Window->GetSkinName().c_str(), (int)zPos);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (logging) DebugLog(L" [%c] 0x%08X : %s", IsWindowVisible(hwnd) ? L'V' : L'H', hwnd, className);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
** ChangeZPosInOrder
|
|
**
|
|
** Arranges the meter window in Z-order.
|
|
**
|
|
*/
|
|
void CSystem::ChangeZPosInOrder()
|
|
{
|
|
if (Rainmeter)
|
|
{
|
|
bool logging = CRainmeter::GetDebug() && DEBUG_VERBOSE;
|
|
std::vector<CMeterWindow*> windowsInZOrder;
|
|
|
|
if (logging) LSLog(LOG_DEBUG, L"Rainmeter", L"1: ----- BEFORE -----");
|
|
|
|
// Retrieve the Rainmeter's meter windows in Z-order
|
|
EnumWindows(MyEnumWindowsProc, (LPARAM)(&windowsInZOrder));
|
|
|
|
// Reset ZPos in Z-order
|
|
for (size_t i = 0; i < windowsInZOrder.size(); ++i)
|
|
{
|
|
windowsInZOrder[i]->ChangeZPos(windowsInZOrder[i]->GetWindowZPosition()); // reset
|
|
}
|
|
|
|
if (logging)
|
|
{
|
|
LSLog(LOG_DEBUG, L"Rainmeter", L"2: ----- AFTER -----");
|
|
|
|
// Log all windows in Z-order
|
|
EnumWindows(MyEnumWindowsProc, (LPARAM)NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** PrepareHelperWindow
|
|
**
|
|
** Moves the helper window to the reference position.
|
|
**
|
|
*/
|
|
void CSystem::PrepareHelperWindow(HWND WorkerW)
|
|
{
|
|
bool logging = CRainmeter::GetDebug() && DEBUG_VERBOSE;
|
|
|
|
if (c_ShowDesktop && WorkerW)
|
|
{
|
|
// Set WS_EX_TOPMOST flag
|
|
SetWindowPos(c_HelperWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
|
|
|
|
// Find the "backmost" topmost window
|
|
HWND hwnd = WorkerW;
|
|
while (hwnd = ::GetNextWindow(hwnd, GW_HWNDPREV))
|
|
{
|
|
if (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)
|
|
{
|
|
WCHAR className[128], windowText[128];
|
|
|
|
if (logging)
|
|
{
|
|
GetClassName(hwnd, className, 128);
|
|
GetWindowText(hwnd, windowText, 128);
|
|
}
|
|
|
|
// Insert the helper window after the found window
|
|
if (0 != SetWindowPos(c_HelperWindow, hwnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING))
|
|
{
|
|
if (logging)
|
|
{
|
|
DebugLog(L"System: HelperWindow: hwnd=0x%08X (WorkerW=0x%08X), hwndInsertAfter=0x%08X (\"%s\" %s) - %s",
|
|
c_HelperWindow, WorkerW, hwnd, windowText, className, (GetWindowLong(c_HelperWindow, GWL_EXSTYLE) & WS_EX_TOPMOST) ? L"TOPMOST" : L"NORMAL");
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (logging)
|
|
{
|
|
DebugLog(L"System: HelperWindow: hwnd=0x%08X (WorkerW=0x%08X), hwndInsertAfter=0x%08X (\"%s\" %s) - FAILED",
|
|
c_HelperWindow, WorkerW, hwnd, windowText, className);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (logging)
|
|
{
|
|
DebugLog(L"System: HelperWindow: hwnd=0x%08X (WorkerW=0x%08X), hwndInsertAfter=HWND_TOPMOST - %s",
|
|
c_HelperWindow, WorkerW, (GetWindowLong(c_HelperWindow, GWL_EXSTYLE) & WS_EX_TOPMOST) ? L"TOPMOST" : L"NORMAL");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Insert the helper window to the bottom
|
|
SetWindowPos(c_HelperWindow, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
|
|
|
|
if (logging)
|
|
{
|
|
DebugLog(L"System: HelperWindow: hwnd=0x%08X (WorkerW=0x%08X), hwndInsertAfter=HWND_BOTTOM - %s",
|
|
c_HelperWindow, WorkerW, (GetWindowLong(c_HelperWindow, GWL_EXSTYLE) & WS_EX_TOPMOST) ? L"TOPMOST" : L"NORMAL");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** CheckDesktopState
|
|
**
|
|
** Changes the "Show Desktop" state.
|
|
**
|
|
*/
|
|
void CSystem::CheckDesktopState(HWND WorkerW)
|
|
{
|
|
HWND hwnd = NULL;
|
|
|
|
if (WorkerW)
|
|
{
|
|
hwnd = FindWindowEx(NULL, WorkerW, L"RainmeterSystemClass", L"SystemWindow");
|
|
}
|
|
|
|
if ((hwnd && !c_ShowDesktop) || (!hwnd && c_ShowDesktop)) // State changed
|
|
{
|
|
c_ShowDesktop = !c_ShowDesktop;
|
|
|
|
if (CRainmeter::GetDebug())
|
|
{
|
|
DebugLog(L"System: %s",
|
|
c_ShowDesktop ? L"\"Show the desktop\" has been detected." : L"\"Show open windows\" has been detected.");
|
|
}
|
|
|
|
PrepareHelperWindow(WorkerW);
|
|
|
|
ChangeZPosInOrder();
|
|
}
|
|
}
|
|
|
|
/*
|
|
** MyWinEventHook
|
|
**
|
|
** The event hook procedure
|
|
**
|
|
*/
|
|
void CALLBACK CSystem::MyWinEventProc(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime)
|
|
{
|
|
if (event == EVENT_SYSTEM_FOREGROUND)
|
|
{
|
|
if (!c_ShowDesktop)
|
|
{
|
|
WCHAR className[128];
|
|
if (GetClassName(hwnd, className, 128) > 0 &&
|
|
wcscmp(className, L"WorkerW") == 0 &&
|
|
hwnd == GetWorkerW())
|
|
{
|
|
CheckDesktopState(hwnd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** WndProc
|
|
**
|
|
** The window procedure
|
|
**
|
|
*/
|
|
LRESULT CALLBACK CSystem::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static int DesktopCompositionCheckCount = 0;
|
|
|
|
if (uMsg == WM_CREATE || hWnd != c_Window)
|
|
{
|
|
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_WINDOWPOSCHANGING:
|
|
((LPWINDOWPOS)lParam)->flags |= SWP_NOZORDER;
|
|
return 0;
|
|
|
|
case WM_TIMER:
|
|
switch (wParam)
|
|
{
|
|
case TIMER_SHOWDESKTOP:
|
|
CheckDesktopState(GetWorkerW());
|
|
return 0;
|
|
|
|
case TIMER_COMPOSITION:
|
|
{
|
|
if (GetShellDesktopWindow() || DesktopCompositionCheckCount >= 10) // 250ms * 10 = 2.5s
|
|
{
|
|
KillTimer(c_Window, TIMER_COMPOSITION);
|
|
|
|
c_WinEventHook = SetWinEventHook(
|
|
EVENT_SYSTEM_FOREGROUND,
|
|
EVENT_SYSTEM_FOREGROUND,
|
|
NULL,
|
|
MyWinEventProc,
|
|
0,
|
|
0,
|
|
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
|
|
|
|
SetTimer(c_Window, TIMER_SHOWDESKTOP, INTERVAL_SHOWDESKTOP, NULL);
|
|
}
|
|
else
|
|
{
|
|
++DesktopCompositionCheckCount;
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
case TIMER_NETSTATS:
|
|
CMeasureNet::UpdateIFTable();
|
|
|
|
// Statistics
|
|
CMeasureNet::UpdateStats();
|
|
if (Rainmeter) Rainmeter->WriteStats(false);
|
|
|
|
return 0;
|
|
|
|
case TIMER_DELETELATER:
|
|
if (Rainmeter) Rainmeter->ClearDeleteLaterList();
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case WM_DWMCOMPOSITIONCHANGED:
|
|
DebugLog(L"System: DWM desktop composition has been changed.");
|
|
|
|
KillTimer(c_Window, TIMER_SHOWDESKTOP);
|
|
KillTimer(c_Window, TIMER_COMPOSITION);
|
|
|
|
if (c_WinEventHook)
|
|
{
|
|
UnhookWinEvent(c_WinEventHook);
|
|
c_WinEventHook = NULL;
|
|
}
|
|
|
|
c_DwmCompositionEnabled = (DwmIsCompositionEnabled() == TRUE);
|
|
|
|
DesktopCompositionCheckCount = 0;
|
|
SetTimer(c_Window, TIMER_COMPOSITION, INTERVAL_COMPOSITION, NULL);
|
|
|
|
return 0;
|
|
|
|
case WM_DISPLAYCHANGE:
|
|
DebugLog(L"System: Display setting has been changed.");
|
|
ClearMultiMonitorInfo();
|
|
CConfigParser::ClearMultiMonitorVariables();
|
|
case WM_SETTINGCHANGE:
|
|
if (uMsg == WM_DISPLAYCHANGE || (uMsg == WM_SETTINGCHANGE && wParam == SPI_SETWORKAREA))
|
|
{
|
|
if (uMsg == WM_SETTINGCHANGE) // SPI_SETWORKAREA
|
|
{
|
|
DebugLog(L"System: Work area has been changed.");
|
|
UpdateWorkareaInfo();
|
|
CConfigParser::UpdateWorkareaVariables();
|
|
}
|
|
|
|
if (Rainmeter)
|
|
{
|
|
// Deliver WM_DISPLAYCHANGE / WM_SETTINGCHANGE message to all meter windows
|
|
std::map<std::wstring, CMeterWindow*>& windows = Rainmeter->GetAllMeterWindows();
|
|
std::map<std::wstring, CMeterWindow*>::const_iterator iter = windows.begin();
|
|
for( ; iter != windows.end(); ++iter)
|
|
{
|
|
PostMessage((*iter).second->GetWindow(), WM_DELAYED_MOVE, (WPARAM)uMsg, (LPARAM)0);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
/*
|
|
** DwmIsCompositionEnabled
|
|
**
|
|
** Returns TRUE if the DWM desktop composition is enabled.
|
|
**
|
|
*/
|
|
BOOL CSystem::DwmIsCompositionEnabled()
|
|
{
|
|
BOOL fEnabled = FALSE;
|
|
|
|
typedef HRESULT (WINAPI * FPDWMISCOMPOSITIONENABLED)(BOOL* pfEnabled);
|
|
HINSTANCE h = LoadLibrary(L"dwmapi.dll");
|
|
if (h)
|
|
{
|
|
FPDWMISCOMPOSITIONENABLED DwmIsCompositionEnabled = (FPDWMISCOMPOSITIONENABLED)GetProcAddress(h, "DwmIsCompositionEnabled");
|
|
if (DwmIsCompositionEnabled)
|
|
{
|
|
if (DwmIsCompositionEnabled(&fEnabled) != S_OK)
|
|
{
|
|
fEnabled = FALSE;
|
|
}
|
|
}
|
|
FreeLibrary(h);
|
|
}
|
|
return fEnabled;
|
|
}
|
|
|
|
/*
|
|
** CopyFiles
|
|
**
|
|
** Copies files and folders from one location to another.
|
|
**
|
|
*/
|
|
bool CSystem::CopyFiles(const std::wstring& strFrom, const std::wstring& strTo, bool bMove)
|
|
{
|
|
std::wstring tmpFrom(strFrom), tmpTo(strTo);
|
|
|
|
// The strings must end with double nul
|
|
tmpFrom.append(L"0");
|
|
tmpFrom[tmpFrom.size() - 1] = L'\0';
|
|
tmpTo.append(L"0");
|
|
tmpTo[tmpTo.size() - 1] = L'\0';
|
|
|
|
SHFILEOPSTRUCT fo = {0};
|
|
fo.wFunc = bMove ? FO_MOVE : FO_COPY;
|
|
fo.pFrom = tmpFrom.c_str();
|
|
fo.pTo = tmpTo.c_str();
|
|
fo.fFlags = FOF_NO_UI | FOF_NOCONFIRMATION | FOF_ALLOWUNDO;
|
|
|
|
int result = SHFileOperation(&fo);
|
|
if (result != 0)
|
|
{
|
|
DebugLog(L"Unable to copy files from %s to %s (%i)", strFrom.c_str(), strTo.c_str(), result);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
** RemoveFile
|
|
**
|
|
** Removes a file even if a file is read-only.
|
|
**
|
|
*/
|
|
bool CSystem::RemoveFile(const std::wstring& file)
|
|
{
|
|
DWORD attr = GetFileAttributes(file.c_str());
|
|
if (attr == -1 || (attr & FILE_ATTRIBUTE_READONLY))
|
|
{
|
|
// Unset read-only
|
|
SetFileAttributes(file.c_str(), (attr == -1) ? FILE_ATTRIBUTE_NORMAL : attr - FILE_ATTRIBUTE_READONLY);
|
|
}
|
|
|
|
return (DeleteFile(file.c_str()) != 0);
|
|
}
|
|
|
|
/*
|
|
** GetIniFileMappingList
|
|
**
|
|
** Retrieves the "IniFileMapping" entries from Registry.
|
|
**
|
|
*/
|
|
void CSystem::GetIniFileMappingList(std::vector<std::wstring>& iniFileMappings)
|
|
{
|
|
HKEY hKey;
|
|
LONG ret;
|
|
|
|
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\IniFileMapping", 0, KEY_ENUMERATE_SUB_KEYS, &hKey);
|
|
if (ret == ERROR_SUCCESS)
|
|
{
|
|
WCHAR buffer[MAX_PATH];
|
|
DWORD index = 0, cch = MAX_PATH;
|
|
|
|
while (true)
|
|
{
|
|
ret = RegEnumKeyEx(hKey, index++, buffer, &cch, NULL, NULL, NULL, NULL);
|
|
if (ret == ERROR_NO_MORE_ITEMS) break;
|
|
|
|
if (ret == ERROR_SUCCESS)
|
|
{
|
|
iniFileMappings.push_back(buffer);
|
|
}
|
|
cch = MAX_PATH;
|
|
}
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** GetTemporaryFile
|
|
**
|
|
** Prepares a temporary file if iniFile is included in the "IniFileMapping" entries.
|
|
** If iniFile is not included, returns a empty string. If error occurred, returns "<>".
|
|
** Note that a temporary file must be deleted by caller.
|
|
**
|
|
*/
|
|
std::wstring CSystem::GetTemporaryFile(const std::vector<std::wstring>& iniFileMappings, const std::wstring &iniFile)
|
|
{
|
|
std::wstring temporary;
|
|
|
|
if (!iniFileMappings.empty())
|
|
{
|
|
std::wstring::size_type pos = iniFile.find_last_of(L'\\');
|
|
std::wstring filename;
|
|
|
|
if (pos != std::wstring::npos)
|
|
{
|
|
filename = iniFile.substr(pos + 1);
|
|
}
|
|
else
|
|
{
|
|
filename = iniFile;
|
|
}
|
|
|
|
for (size_t i = 0; i < iniFileMappings.size(); ++i)
|
|
{
|
|
if (wcsicmp(iniFileMappings[i].c_str(), filename.c_str()) == 0)
|
|
{
|
|
WCHAR buffer[MAX_PATH];
|
|
|
|
GetTempPath(MAX_PATH, buffer);
|
|
temporary = buffer;
|
|
if (GetTempFileName(temporary.c_str(), L"cfg", 0, buffer) != 0)
|
|
{
|
|
temporary = buffer;
|
|
|
|
std::wstring tmp = GetTemporaryFile(iniFileMappings, temporary);
|
|
if (tmp.empty() && CopyFiles(iniFile, temporary))
|
|
{
|
|
return temporary;
|
|
}
|
|
else // alternate is reserved or failed
|
|
{
|
|
RemoveFile(temporary);
|
|
return tmp.empty() ? L"<>" : tmp;
|
|
}
|
|
}
|
|
else // failed
|
|
{
|
|
DebugLog(L"Unable to create a temporary file to: %s", temporary.c_str());
|
|
return L"<>";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return temporary;
|
|
}
|