This little plugin can address something that you might want from time to time, to detect if a config (a skin) is currently loaded / active in Rainmeter.
This plugin requires Rainmeter 4.3 r3298 or better.
Example skin that installs the plugin in Rainmeter.
Here are the 32bit and 64bit versions of the .dll if you don't want or need the example skin.
Checking active status of a skin
Using a measure option
ConfigName;;Default: #CURRENTCONFIG#;;The name of a config you wish to test. This is case-insensitive.
Example:
- The number value of the measure will be 1 if the config is active and -1 if not.
- The string value of the measure will be the currently running skin .ini file name.
Note: All other measure specific options are ignored when ConfigName is set.
Using inline plugin section variablesExample:
Code: Select all
[MeasureName]
Measure=Plugin
Plugin=ConfigActive
ConfigName=Illustro\System
- The string value of the measure will be the currently running skin .ini file name.
Note: All other measure specific options are ignored when ConfigName is set.
[&MeasureName:IsActive(ConfigName)];;;;The section variable will be resolved to 1 if the config is active and -1 if not.
Example IfCondition=[&MeasureName:IsActive(Illustro\System)] = 1
Note: DynamicVariables=1 must be set on the measure or meter where this is used.
Example IfCondition=[&MeasureName:IsActive(Illustro\System)] = 1
Note: DynamicVariables=1 must be set on the measure or meter where this is used.
[&MeasureName:ConfigVariantName(ConfigName)];;;;The section variable will be resolved to the name of the currently running skin .ini file name.
Example IfTrueAction=[!SetOption SomeMeter Text "Variant [&MeasureName:ConfigVariantName(Illustro\System)] is running."]
Note: DynamicVariables=1 must be set on the measure or meter where this is used.
Example IfTrueAction=[!SetOption SomeMeter Text "Variant [&MeasureName:ConfigVariantName(Illustro\System)] is running."]
Note: DynamicVariables=1 must be set on the measure or meter where this is used.
[&MeasureName:IsHidden(ConfigName)];;;;The section variable will be resolved to 1 if the config is "hidden" and -1 if not.
Example IfCondition=[&MeasureName:IsHidden(Illustro\System)] = 1
Note: DynamicVariables=1 must be set on the measure or meter where this is used.
Note: The section variable will be resolved to 0 if the config is not active.
Getting a count of running skinsExample IfCondition=[&MeasureName:IsHidden(Illustro\System)] = 1
Note: DynamicVariables=1 must be set on the measure or meter where this is used.
Note: The section variable will be resolved to 0 if the config is not active.
Using a measure option
Type;;Default: Skin;;In addition to the basic functionality supported by the ConfigName option, the plugin will obtain a "count" of the number of active configs. This will create a numbered "index" of all active configs, ordered by the current z-position (front to back) of the skin windows.
This count can be returned as the number value by setting Type=Count on the measure.
Type can also used in conjunction with Index, to return either the config name or skin .ini file name of the config referenced with Index. This is done with Type=Config or Type=Skin and Index=x
Note: The intent of this is to be able to use Count, and then, perhaps in Lua, iterate through Index from 1 to the value of Count, to obtain a list of configs and / or skins currently active. In and of itself, Index is not terribly useful, as the position of any given config in this list is really unreliable on any given user's system at any given time.
This count can be returned as the number value by setting Type=Count on the measure.
Type can also used in conjunction with Index, to return either the config name or skin .ini file name of the config referenced with Index. This is done with Type=Config or Type=Skin and Index=x
Note: The intent of this is to be able to use Count, and then, perhaps in Lua, iterate through Index from 1 to the value of Count, to obtain a list of configs and / or skins currently active. In and of itself, Index is not terribly useful, as the position of any given config in this list is really unreliable on any given user's system at any given time.
Index;;Default: 1;;Used in conjunction with Type=Config or Type=Skin to return ether the config name or skin .ini name for a particular indexed config.
Using inline plugin section variables[&MeasureName:LoadedCount()];;;;The section variable will be resolved to the number of currently active configs.
Example Formula=[&MeasureName:LoadedCount()]
Note: DynamicVariables=1 must be set on the measure or meter where this is used.
Skin:
Example Formula=[&MeasureName:LoadedCount()]
Note: DynamicVariables=1 must be set on the measure or meter where this is used.
Code: Select all
[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1
[Metadata]
Name=ConfigActive
Author=JSMorley
Information=Demonstrates the ConfigActive plugin | See comments in the skin
License=Creative Commons Attribution-Non-Commercial-Share Alike 3.0
Version=July 22, 2018
; ConfigActive Plugin
; Returns 1 (Active/Loaded) or -1 (not Active/Loaded) as the number value for a named skin config.
; Returns the "Skin.ini" name of the "variant" running for the named skin config.
; This can be used two ways:
; Set the ConfigName option on the measure, and it will return the state
; of the named config on each measure update as the measure's number and string value.
; Use the IsActive(config name) function as an inline section variable.
; The return value of 1 or -1 will replace the function call where used.
; DynamicVariables=1 must be used with inline function calls.
; Do not enclose the config name passed to the isActive() function in quotes.
; No ConfigName option is needed with this method.
; Installing this .rmskin will install the proper ConfigActive.dll in Rainmeter.
; The 32bit and 64bit versions of the .dll are also included in the
; @Resources\ConfigActive Plugin for Distribution folder in the skin.
[Variables]
; Set the config names to test here
Config1=illustro\System
Config2=illustro\Clock
Config3=illustro\Network
; Using the [MeasureActive] measure's number and string return values when ConfigName is set
[MeasureActive]
Measure=Plugin
Plugin=ConfigActive
ConfigName=#Config1#
DynamicVariables=1
IfCondition=MeasureActive = 1
IfTrueAction=[!SetOption MeterActive1 Text "[#Config1#] is active, and [MeasureActive] is running."]
IfFalseAction=[!SetOption MeterActive1 Text "[#Config1#] is not active"]
; Using [MeasureActive] as a host for a inline isActive() function call and testing the number value
; Using [MeasureActive] as a host for a inline ConfigVariantName() function call.
[MeasureActive2]
Measure=Calc
DynamicVariables=1
IfCondition=[&MeasureActive:IsActive(#Config2#)] = 1
IfTrueAction=[!SetOption MeterActive2 Text "[#Config2#] is active, and [&MeasureActive:ConfigVariantName(#Config2#)] is running."]
IfFalseAction=[!SetOption MeterActive2 Text "[#Config2#] is not active"]
; Using [MeasureActive] as a host for a inline isActive() function call and Substituting the string value
; Using [MeasureActive] as a host for a inline ConfigVariantName() function call.
[MeasureActive3]
Measure=String
String=[&MeasureActive:IsActive(#Config3#)]
DynamicVariables=1
Substitute="-1":"[#Config3#] is not active","1":"[#Config3#] is active, and [&MeasureActive:ConfigVariantName(#Config3#)] is running."
[MeterActive1]
Meter=String
W=400
FontSize=11
FontWeight=400
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
[MeterActive2]
Meter=String
Y=5R
W=400
FontSize=11
FontWeight=400
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
[MeterActive3]
Meter=String
MeasureName=MeasureActive3
Y=5R
W=400
FontSize=11
FontWeight=400
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Here is the plugin C++ code in case you want to see what it is doing. It simply sends a message to Rainmeter, asking if a particular skin config "window" exists. If it does, it gets the window title of the config window, and extracts the skin .ini name from it.
Code: Select all
#include <Windows.h>
#include "../../API/RainmeterAPI.h"
#include <string>
#define RAINMETER_QUERY_ID_SKIN_WINDOWHANDLE 5101
#define RAINMETER_SKIN_CLASSNAME L"RainmeterMeterWindow"
#define RAINMETER_TRAY_CLASSNAME L"RainmeterTrayClass"
enum class Type
{
SKIN,
CONFIG,
LOADEDCOUNT
};
struct Measure
{
Measure() {}
std::wstring m_SkinPath;
std::wstring m_ConfigName;
std::wstring m_SkinName;
int m_Index = 0;
Type m_Type = Type::SKIN;
std::wstring m_String;
std::wstring m_SectionVariable;
};
PLUGIN_EXPORT void Initialize(void** data, void* rm)
{
Measure* measure = new Measure;
*data = measure;
}
PLUGIN_EXPORT void Reload(void* data, void* rm, double* maxValue)
{
Measure* measure = (Measure*)data;
measure->m_SkinPath = RmReplaceVariables(rm, L"#SKINSPATH#");
measure->m_ConfigName = RmReadString(rm, L"ConfigName", L"");
measure->m_Index = RmReadInt(rm, L"Index", 1);
std::wstring type = RmReadString(rm, L"Type", L"Skin");
if (_wcsicmp(type.c_str(), L"SKIN") == 0) measure->m_Type = Type::SKIN;
else if (_wcsicmp(type.c_str(), L"CONFIG") == 0) measure->m_Type = Type::CONFIG;
else if (_wcsicmp(type.c_str(), L"COUNT") == 0) measure->m_Type = Type::LOADEDCOUNT;
}
HWND GetLoadedConfig(const std::wstring& configName)
{
HWND trayHWND = FindWindow(RAINMETER_TRAY_CLASSNAME, NULL);
if (trayHWND == NULL) return NULL;
std::wstring config = configName;
for(int i = 0; i < config.size()-1; ++i)
{
if (config[i] == L'\\' && config[i + 1] == L'\\') {
config.erase(i, 1);
}
}
COPYDATASTRUCT cds = {};
cds.dwData = RAINMETER_QUERY_ID_SKIN_WINDOWHANDLE;
cds.cbData = (DWORD)(config.size() + 1) * sizeof(WCHAR);
cds.lpData = (void*)config.c_str();
return (HWND)SendMessage(trayHWND, WM_COPYDATA, 0, (LPARAM)&cds);;
}
PLUGIN_EXPORT LPCWSTR IsHidden(void* data, const int argc, const WCHAR* argv[])
{
HWND trayHWND = FindWindow(RAINMETER_TRAY_CLASSNAME, NULL);
if (trayHWND == NULL) return NULL;
std::wstring config = argv[0];
for (int i = 0; i < config.size() - 1; ++i)
{
if (config[i] == L'\\' && config[i + 1] == L'\\') {
config.erase(i, 1);
}
}
COPYDATASTRUCT cds = {};
cds.dwData = RAINMETER_QUERY_ID_SKIN_WINDOWHANDLE;
cds.cbData = (DWORD)(config.size() + 1) * sizeof(WCHAR);
cds.lpData = (void*)config.c_str();
HWND configHandle = (HWND)SendMessage(trayHWND, WM_COPYDATA, 0, (LPARAM)&cds);
if (configHandle == NULL) return L"0";
BOOL visibleState = IsWindowVisible(configHandle);
if (!visibleState) {
return L"1";
}
return L"-1";
}
std::wstring GetWindowTitle(HWND hwnd)
{
const int length = GetWindowTextLength(hwnd);
std::wstring buff;
buff.resize(length+1);
GetWindowText(hwnd, &buff[0], (int)buff.size());
return buff;
}
HWND QueryConfig(int index)
{
HWND hwnd = NULL;
int current = index;
do
{
hwnd = FindWindowEx(NULL, hwnd, RAINMETER_SKIN_CLASSNAME, NULL);
// limit reached
if (hwnd == NULL) break;
current--;
} while (current != 0);
return hwnd;
}
int LoadedSkinCount()
{
HWND hwnd = NULL;
int count = 0;
do
{
hwnd = FindWindowEx(NULL, hwnd, RAINMETER_SKIN_CLASSNAME, NULL);
if (hwnd == NULL) break;
count++;
} while (true);
return count;
}
PLUGIN_EXPORT double Update(void* data)
{
Measure* measure = (Measure*)data;
HWND config;
if(!measure->m_ConfigName.empty())
{
// index is irrelevant here as there can only be one config
config = GetLoadedConfig(measure->m_ConfigName);
}
else
{
// Fetch the config based on index instead
config = QueryConfig(measure->m_Index);
}
if (config == NULL)
{
// Specified config is not loaded, so clear the return string
measure->m_String.clear();
return -1;
}
std::wstring title = GetWindowTitle(config);
const size_t last = title.find_last_of(L"\\");
switch(measure->m_Type)
{
case Type::SKIN:
// Title is not set correctly, so we can't retrieve the skin. The config is still loaded.
if (last == std::wstring::npos && last + 1 < title.size()) return 1;
measure->m_String = title.substr(last + 1);
break;
case Type::CONFIG:
{
measure->m_String = measure->m_ConfigName;
// Title is not set correctly, so we can't retrieve the skin. The config is still loaded.
if (last == std::wstring::npos && last + 1 < title.size()) return 1;
if (!measure->m_ConfigName.empty()) return 1;
size_t start = measure->m_SkinPath.size();
measure->m_String = title.substr(start, last - start);
}
break;
case Type::LOADEDCOUNT:
measure->m_String.clear();
return LoadedSkinCount();
}
return 1;
}
PLUGIN_EXPORT LPCWSTR GetString(void* data)
{
Measure* measure = (Measure*)data;
return measure->m_String.c_str();
}
PLUGIN_EXPORT void Finalize(void* data)
{
Measure* measure = (Measure*)data;
delete measure;
}
PLUGIN_EXPORT LPCWSTR IsActive(void* data, const int argc, const WCHAR* argv[])
{
if (argc > 0) return GetLoadedConfig(argv[0]) ? L"1" : L"-1";
return nullptr;
}
PLUGIN_EXPORT LPCWSTR ConfigVariantName(void* data, const int argc, const WCHAR* argv[])
{
if (argc > 0)
{
Measure* measure = (Measure*)data;
HWND config = GetLoadedConfig(argv[0]);
if (!config) return L"";
std::wstring title = GetWindowTitle(config);
size_t last = title.find_last_of(L"\\");
// Title is not set correctly, so we can't retrieve the skin. The config is still loaded.
if (last == std::wstring::npos && last + 1 < title.size()) return L"";
measure->m_SectionVariable = title.substr(last + 1);
return measure->m_SectionVariable.c_str();
}
return nullptr;
}
PLUGIN_EXPORT LPCWSTR LoadedCount(void* data, const int argc, const WCHAR* argv[])
{
Measure* measure = (Measure*)data;
measure->m_SectionVariable = std::to_wstring(LoadedSkinCount());
return measure->m_SectionVariable.c_str();
}
Edit: Version 2.2.1 adds support for \\ characters (escaped slash) in IsHidden(ConfigName) functionality.
Edit: Version 2.2 adds the IsHidden(ConfigName) inline section variable functionality.
Edit: Version 2.1 is changed to ignore extra back-slashes in the ConfigName parameter, to allow you to pass "escaped" versions that you might also want to use in Lua pattern matching. So either Illustro\System or Illustro\\System will work.