mirror of
https://github.com/WCBROW01/WinPowerDMS.git
synced 2026-06-27 20:12:33 -04:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 350e2960b8 | |||
| 036a20df3f | |||
| 6a586dedb7 | |||
| 4b46eafd35 | |||
| 046e8fd2eb |
+1
-1
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) [year] [fullname]
|
||||
Copyright (c) 2025 Mina Brown
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -7,10 +7,10 @@ Currently a work in progress. Not very useful yet since it doesn't save your pre
|
||||
This is a simple C program written using Visual Studio 2022 that has no external dependencies. It should support Windows Vista or higher, but this hasn't been tested yet.
|
||||
|
||||
## TODO
|
||||
- [ ] Save preferences to the Windows registry.
|
||||
- [ ] Add a checkbox to preferences for starting at login.
|
||||
- [x] Save preferences to the Windows registry.
|
||||
- [x] Add a checkbox to preferences for starting at login.
|
||||
- [ ] Add an icon.
|
||||
- [ ] Add behavior for when multiple displays are connected.
|
||||
- [ ] Block program from running when the system has no battery.
|
||||
- [ ] Make the default display mode the current one instead of the highest resolution if there are no preferences set.
|
||||
- [x] Warn the user when the system has no battery.
|
||||
- [x] Make the default display mode the current one instead of the highest resolution if there are no preferences set.
|
||||
- [ ] Make an actual about dialog.
|
||||
+127
-32
@@ -14,14 +14,11 @@ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
|
||||
#define TRAY_ICON_ID 1001
|
||||
|
||||
typedef struct {
|
||||
int width;
|
||||
int height;
|
||||
int refresh;
|
||||
DWORD width;
|
||||
DWORD height;
|
||||
DWORD refresh;
|
||||
} DISPLAY_MODE;
|
||||
|
||||
static DISPLAY_MODE modeBatt = { 0 };
|
||||
static DISPLAY_MODE modeAC = { 0 };
|
||||
|
||||
BOOL DisplayModeEquals(const DISPLAY_MODE* a, const DISPLAY_MODE* b) {
|
||||
return a->width == b->width && a->height == b->height && a->refresh == b->refresh;
|
||||
}
|
||||
@@ -29,9 +26,8 @@ BOOL DisplayModeEquals(const DISPLAY_MODE* a, const DISPLAY_MODE* b) {
|
||||
#define MODE_SELECTED(mode) (!DisplayModeEquals(&mode, &(DISPLAY_MODE) { 0 }))
|
||||
|
||||
// I hate parsing the string here, but the alternative requires extra memory management.
|
||||
static DISPLAY_MODE GetModeFromCB(HWND hDlg, int nIDDlgItem) {
|
||||
static DISPLAY_MODE GetModeFromCB(HWND hComboBox) {
|
||||
DISPLAY_MODE mode = { 0 };
|
||||
HWND hComboBox = GetDlgItem(hDlg, nIDDlgItem);
|
||||
size_t selectedIndex = SendMessage(hComboBox, CB_GETCURSEL, 0, 0);
|
||||
if (selectedIndex != CB_ERR) {
|
||||
LRESULT len = SendMessage(hComboBox, CB_GETLBTEXTLEN, selectedIndex, 0);
|
||||
@@ -125,12 +121,96 @@ static void TestDisplayMode(HWND hDlg, DISPLAY_MODE* mode) {
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
DISPLAY_MODE modeBatt;
|
||||
DISPLAY_MODE modeAC;
|
||||
DWORD disableBatteryWarning;
|
||||
BOOL runAtStartup;
|
||||
} WINPOWERDMS_PREFS;
|
||||
|
||||
static WINPOWERDMS_PREFS userPrefs = { 0 };
|
||||
|
||||
// Save user preferences to registry.
|
||||
static BOOL SavePrefs(void) {
|
||||
BOOL saved = FALSE;
|
||||
|
||||
HKEY regKey;
|
||||
if (!RegCreateKeyEx(
|
||||
HKEY_CURRENT_USER, L"SOFTWARE\\WinPowerDMS", 0, NULL,
|
||||
REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, ®Key, NULL
|
||||
)) {
|
||||
RegSetKeyValue(regKey, L"Battery", L"Width", REG_DWORD, &userPrefs.modeBatt.width, sizeof(userPrefs.modeBatt.width));
|
||||
RegSetKeyValue(regKey, L"Battery", L"Height", REG_DWORD, &userPrefs.modeBatt.height, sizeof(userPrefs.modeBatt.height));
|
||||
RegSetKeyValue(regKey, L"Battery", L"Refresh Rate", REG_DWORD, &userPrefs.modeBatt.refresh, sizeof(userPrefs.modeBatt.refresh));
|
||||
RegSetKeyValue(regKey, L"AC Power", L"Width", REG_DWORD, &userPrefs.modeAC.width, sizeof(userPrefs.modeAC.width));
|
||||
RegSetKeyValue(regKey, L"AC Power", L"Height", REG_DWORD, &userPrefs.modeAC.height, sizeof(userPrefs.modeAC.height));
|
||||
RegSetKeyValue(regKey, L"AC Power", L"Refresh Rate", REG_DWORD, &userPrefs.modeAC.refresh, sizeof(userPrefs.modeAC.refresh));
|
||||
RegSetValueEx(regKey, L"Disable Battery Warning", 0, REG_DWORD, &userPrefs.disableBatteryWarning, sizeof(userPrefs.disableBatteryWarning));
|
||||
RegCloseKey(regKey);
|
||||
saved = TRUE;
|
||||
}
|
||||
|
||||
// Create the startup entry
|
||||
if (userPrefs.runAtStartup) {
|
||||
WCHAR exePath[MAX_PATH];
|
||||
DWORD exePathLen = GetModuleFileName(NULL, exePath, sizeof(exePath) / sizeof(exePath[0])) + 1;
|
||||
RegSetKeyValue(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", L"WinPowerDMS", REG_SZ, exePath, exePathLen);
|
||||
}
|
||||
else { // Delete the startup entry
|
||||
RegDeleteKeyValue(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", L"WinPowerDMS");
|
||||
}
|
||||
return saved;
|
||||
}
|
||||
|
||||
// Load user preferences from registry.
|
||||
static BOOL LoadPrefs(void) {
|
||||
BOOL loaded = FALSE;
|
||||
|
||||
HKEY regKey;
|
||||
if (!RegOpenKeyEx(HKEY_CURRENT_USER, L"SOFTWARE\\WinPowerDMS", 0, KEY_READ, ®Key)) {
|
||||
DWORD keySize = sizeof(userPrefs.modeBatt.width);
|
||||
RegGetValue(regKey, L"Battery", L"Width", RRF_RT_REG_DWORD, NULL, &userPrefs.modeBatt.width, &keySize);
|
||||
keySize = sizeof(userPrefs.modeBatt.height);
|
||||
RegGetValue(regKey, L"Battery", L"Height", RRF_RT_REG_DWORD, NULL, &userPrefs.modeBatt.height, &keySize);
|
||||
keySize = sizeof(userPrefs.modeBatt.refresh);
|
||||
RegGetValue(regKey, L"Battery", L"Refresh Rate", RRF_RT_REG_DWORD, NULL, &userPrefs.modeBatt.refresh, &keySize);
|
||||
keySize = sizeof(userPrefs.modeAC.width);
|
||||
RegGetValue(regKey, L"AC Power", L"Width", RRF_RT_REG_DWORD, NULL, &userPrefs.modeAC.width, &keySize);
|
||||
keySize = sizeof(userPrefs.modeAC.height);
|
||||
RegGetValue(regKey, L"AC Power", L"Height", RRF_RT_REG_DWORD, NULL, &userPrefs.modeAC.height, &keySize);
|
||||
keySize = sizeof(userPrefs.modeAC.refresh);
|
||||
RegGetValue(regKey, L"AC Power", L"Refresh Rate", RRF_RT_REG_DWORD, NULL, &userPrefs.modeAC.refresh, &keySize);
|
||||
keySize = sizeof(userPrefs.disableBatteryWarning);
|
||||
RegGetValue(regKey, NULL, L"Disable Battery Warning", RRF_RT_REG_DWORD, NULL, &userPrefs.disableBatteryWarning, &keySize);
|
||||
RegCloseKey(regKey);
|
||||
loaded = TRUE;
|
||||
}
|
||||
|
||||
// Figure out if application has a startup entry
|
||||
if (!RegGetValue(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", L"WinPowerDMS", RRF_RT_ANY, NULL, NULL, NULL))
|
||||
userPrefs.runAtStartup = TRUE;
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
static DISPLAY_MODE GetCurrentDisplayMode(void) {
|
||||
DEVMODE currentMode = { .dmSize = sizeof(currentMode) };
|
||||
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, ¤tMode);
|
||||
return (DISPLAY_MODE) {
|
||||
.width = currentMode.dmPelsWidth,
|
||||
.height = currentMode.dmPelsHeight,
|
||||
.refresh = currentMode.dmDisplayFrequency
|
||||
};
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK PrefsDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
HWND hComboBatt = GetDlgItem(hDlg, IDC_COMBO_BATT);
|
||||
HWND hComboAC = GetDlgItem(hDlg, IDC_COMBO_AC);
|
||||
HWND hCheckBattWarning = GetDlgItem(hDlg, IDC_CHECK_BATT_WARNING);
|
||||
HWND hCheckStartup = GetDlgItem(hCheckBattWarning, IDC_CHECK_STARTUP);
|
||||
|
||||
switch (message) {
|
||||
case WM_INITDIALOG: {
|
||||
HWND hComboBatt = GetDlgItem(hDlg, IDC_COMBO_BATT);
|
||||
HWND hComboAC = GetDlgItem(hDlg, IDC_COMBO_AC);
|
||||
|
||||
// devMode object that will be enumerated
|
||||
DEVMODE devMode;
|
||||
ZeroMemory(&devMode, sizeof(devMode));
|
||||
@@ -154,11 +234,11 @@ static INT_PTR CALLBACK PrefsDialogProc(HWND hDlg, UINT message, WPARAM wParam,
|
||||
|
||||
// Add to both combo boxes and check if this is the mode that was set.
|
||||
SendMessage(hComboBatt, CB_ADDSTRING, 0, (LPARAM)resText);
|
||||
if (DisplayModeEquals(&modeBatt, ¤tMode))
|
||||
if (DisplayModeEquals(&userPrefs.modeBatt, ¤tMode))
|
||||
SendMessage(hComboBatt, CB_SETCURSEL, modeCount, 0);
|
||||
|
||||
SendMessage(hComboAC, CB_ADDSTRING, 0, (LPARAM)resText);
|
||||
if (DisplayModeEquals(&modeAC, ¤tMode))
|
||||
if (DisplayModeEquals(&userPrefs.modeAC, ¤tMode))
|
||||
SendMessage(hComboAC, CB_SETCURSEL, modeCount, 0);
|
||||
|
||||
++modeCount;
|
||||
@@ -166,9 +246,8 @@ static INT_PTR CALLBACK PrefsDialogProc(HWND hDlg, UINT message, WPARAM wParam,
|
||||
}
|
||||
}
|
||||
|
||||
// select the highest display mode if one hasn't been selected yet
|
||||
if (!MODE_SELECTED(modeBatt)) SendMessage(hComboBatt, CB_SETCURSEL, modeCount - 1, 0);
|
||||
if (!MODE_SELECTED(modeAC)) SendMessage(hComboAC, CB_SETCURSEL, modeCount - 1, 0);
|
||||
SendMessage(hCheckBattWarning, BM_SETCHECK, userPrefs.disableBatteryWarning ? BST_CHECKED : BST_UNCHECKED, 0);
|
||||
SendMessage(hCheckStartup, BM_SETCHECK, userPrefs.runAtStartup ? BST_CHECKED : BST_UNCHECKED, 0);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -176,8 +255,11 @@ static INT_PTR CALLBACK PrefsDialogProc(HWND hDlg, UINT message, WPARAM wParam,
|
||||
switch (LOWORD(wParam)) {
|
||||
case IDC_BUTTON_APPLY:
|
||||
case IDOK: {
|
||||
modeBatt = GetModeFromCB(hDlg, IDC_COMBO_BATT);
|
||||
modeAC = GetModeFromCB(hDlg, IDC_COMBO_AC);
|
||||
userPrefs.modeBatt = GetModeFromCB(hComboBatt);
|
||||
userPrefs.modeAC = GetModeFromCB(hComboAC);
|
||||
userPrefs.disableBatteryWarning = SendMessage(hCheckBattWarning, BM_GETCHECK, 0, 0) == BST_CHECKED;
|
||||
userPrefs.runAtStartup = SendMessage(hCheckStartup, BM_GETCHECK, 0, 0) == BST_CHECKED;
|
||||
SavePrefs();
|
||||
if (LOWORD(wParam) == IDC_BUTTON_APPLY) return TRUE;
|
||||
}
|
||||
case IDCANCEL: {
|
||||
@@ -185,12 +267,12 @@ static INT_PTR CALLBACK PrefsDialogProc(HWND hDlg, UINT message, WPARAM wParam,
|
||||
return TRUE;
|
||||
}
|
||||
case IDC_BUTTON_TEST_BATT: {
|
||||
DISPLAY_MODE mode = GetModeFromCB(hDlg, IDC_COMBO_BATT);
|
||||
DISPLAY_MODE mode = GetModeFromCB(hComboBatt);
|
||||
TestDisplayMode(hDlg, &mode);
|
||||
return TRUE;
|
||||
}
|
||||
case IDC_BUTTON_TEST_AC: {
|
||||
DISPLAY_MODE mode = GetModeFromCB(hDlg, IDC_COMBO_AC);
|
||||
DISPLAY_MODE mode = GetModeFromCB(hComboAC);
|
||||
TestDisplayMode(hDlg, &mode);
|
||||
return TRUE;
|
||||
}
|
||||
@@ -208,18 +290,17 @@ enum TRAY_IDS {
|
||||
};
|
||||
|
||||
static NOTIFYICONDATA nid;
|
||||
static HWND hWnd;
|
||||
static HMENU hMenu;
|
||||
|
||||
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||
switch (uMsg) {
|
||||
case WM_TRAYICON:
|
||||
if (lParam == WM_RBUTTONUP) {
|
||||
POINT pt;
|
||||
GetCursorPos(&pt);
|
||||
SetForegroundWindow(hwnd); // Required for menu to disappear correctly
|
||||
SetForegroundWindow(hWnd); // Required for menu to disappear correctly
|
||||
|
||||
TrackPopupMenu(hMenu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, NULL);
|
||||
TrackPopupMenu(hMenu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hWnd, NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -229,10 +310,10 @@ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM l
|
||||
DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_PREFSDIALOG), hWnd, PrefsDialogProc);
|
||||
break;
|
||||
case ID_TRAY_ABOUT:
|
||||
MessageBoxW(NULL, L"WinPowerDMS\nA utility for switching the resolution of your laptop's display based on the current power state.", L"About", MB_OK | MB_ICONINFORMATION);
|
||||
MessageBoxW(hWnd, L"WinPowerDMS\nA utility for switching the resolution of your laptop's display based on the current power state.", L"About", MB_OK | MB_ICONINFORMATION);
|
||||
break;
|
||||
case ID_TRAY_EXIT:
|
||||
DestroyWindow(hwnd);
|
||||
DestroyWindow(hWnd);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -245,19 +326,28 @@ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM l
|
||||
case WM_POWERBROADCAST:
|
||||
if (LOWORD(wParam) == PBT_POWERSETTINGCHANGE) {
|
||||
OutputDebugString(L"Power status changed\n");
|
||||
DISPLAY_MODE currentMode = GetCurrentDisplayMode();
|
||||
SYSTEM_POWER_STATUS powerStatus;
|
||||
if (GetSystemPowerStatus(&powerStatus)) {
|
||||
ChangeDisplayMode(powerStatus.ACLineStatus ? &modeAC : &modeBatt, CDS_UPDATEREGISTRY);
|
||||
// Change display mode only if the current display mode is one of the configured options.
|
||||
if (GetSystemPowerStatus(&powerStatus) &&
|
||||
(DisplayModeEquals(¤tMode, &userPrefs.modeBatt) || DisplayModeEquals(¤tMode, &userPrefs.modeAC)))
|
||||
{
|
||||
ChangeDisplayMode(powerStatus.ACLineStatus ? &userPrefs.modeAC : &userPrefs.modeBatt, CDS_UPDATEREGISTRY);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
|
||||
// Initialize the controls
|
||||
InitCommonControls();
|
||||
if (!LoadPrefs()) { // set both battery and AC to current display mode if there are no preferences set
|
||||
DISPLAY_MODE currentMode = GetCurrentDisplayMode();
|
||||
userPrefs.modeAC = currentMode;
|
||||
userPrefs.modeBatt = currentMode;
|
||||
}
|
||||
|
||||
InitCommonControls(); // Initialize modern controls
|
||||
|
||||
WNDCLASS wc = { 0 };
|
||||
wc.lpfnWndProc = WindowProc;
|
||||
@@ -266,10 +356,15 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
|
||||
RegisterClass(&wc);
|
||||
|
||||
hWnd = CreateWindowEx(0, L"TrayAppClass", NULL, 0, 0, 0, 0, 0,
|
||||
HWND hWnd = CreateWindowEx(0, L"TrayAppClass", NULL, 0, 0, 0, 0, 0,
|
||||
HWND_MESSAGE, NULL, hInstance, NULL);
|
||||
RegisterPowerSettingNotification(hWnd, &GUID_ACDC_POWER_SOURCE, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
|
||||
// Check if there is a battery in the system and display a warning message if there isn't one.
|
||||
SYSTEM_POWER_STATUS powerStatus;
|
||||
if (!userPrefs.disableBatteryWarning && GetSystemPowerStatus(&powerStatus) && powerStatus.BatteryFlag == 128)
|
||||
MessageBox(hWnd, L"There is no battery present in the system. The program will start and set your display mode to what you have set for AC power, but do nothing else afterwards.", L"WinPowerDMS", MB_OK | MB_ICONWARNING);
|
||||
|
||||
// Create context menu
|
||||
hMenu = CreatePopupMenu();
|
||||
AppendMenu(hMenu, MF_STRING, ID_TRAY_PREFS, L"Preferences");
|
||||
|
||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by PowerDMS.rc
|
||||
// Used by WinPowerDMS.rc
|
||||
//
|
||||
#define IDD_PREFSDIALOG 101
|
||||
#define IDC_COMBO_BATT 1001
|
||||
@@ -9,6 +9,8 @@
|
||||
#define IDC_BUTTON_TEST_AC 1004
|
||||
#define IDC_BUTTON_APPLY 1005
|
||||
#define IDC_LIST1 1006
|
||||
#define IDC_CHECK_BATT_WARNING 1007
|
||||
#define IDC_CHECK_STARTUP 1008
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
@@ -16,7 +18,7 @@
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 104
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1007
|
||||
#define _APS_NEXT_CONTROL_VALUE 1008
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user