Save and load preferences using the registry

Preferences are now saved and loaded using the registry. They are loaded at application startup and written to the registry every time a change is made. If no preferences are present in the registry yet, the current display mode is used for both battery and AC power.
This commit is contained in:
2025-08-09 11:56:59 -04:00
parent 046e8fd2eb
commit 4b46eafd35
2 changed files with 76 additions and 26 deletions

View File

@ -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. 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 ## TODO
- [ ] Save preferences to the Windows registry. - [x] Save preferences to the Windows registry.
- [ ] Add a checkbox to preferences for starting at login. - [ ] Add a checkbox to preferences for starting at login.
- [ ] Add an icon. - [ ] Add an icon.
- [ ] Add behavior for when multiple displays are connected. - [ ] Add behavior for when multiple displays are connected.
- [ ] Block program from running when the system has no battery. - [ ] 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] Make the default display mode the current one instead of the highest resolution if there are no preferences set.
- [ ] Make an actual about dialog. - [ ] Make an actual about dialog.

View File

@ -14,14 +14,11 @@ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#define TRAY_ICON_ID 1001 #define TRAY_ICON_ID 1001
typedef struct { typedef struct {
int width; DWORD width;
int height; DWORD height;
int refresh; DWORD refresh;
} DISPLAY_MODE; } DISPLAY_MODE;
static DISPLAY_MODE modeBatt = { 0 };
static DISPLAY_MODE modeAC = { 0 };
BOOL DisplayModeEquals(const DISPLAY_MODE* a, const DISPLAY_MODE* b) { BOOL DisplayModeEquals(const DISPLAY_MODE* a, const DISPLAY_MODE* b) {
return a->width == b->width && a->height == b->height && a->refresh == b->refresh; return a->width == b->width && a->height == b->height && a->refresh == b->refresh;
} }
@ -125,6 +122,52 @@ static void TestDisplayMode(HWND hDlg, DISPLAY_MODE* mode) {
} }
} }
typedef struct {
DISPLAY_MODE modeBatt;
DISPLAY_MODE modeAC;
} WINPOWERDMS_PREFS;
static WINPOWERDMS_PREFS userPrefs = { 0 };
// Save user preferences to registry.
static BOOL SavePrefs(void) {
HKEY regKey;
if (!RegCreateKeyEx(
HKEY_CURRENT_USER, L"SOFTWARE\\WinPowerDMS", 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &regKey, 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));
return TRUE;
}
else return FALSE;
}
// Load user preferences from registry.
static BOOL LoadPrefs(void) {
HKEY regKey;
if (!RegOpenKeyEx(HKEY_CURRENT_USER, L"SOFTWARE\\WinPowerDMS", 0, KEY_READ, &regKey)) {
DWORD keySize = sizeof(DWORD);
RegGetValue(regKey, L"Battery", L"Width", RRF_RT_REG_DWORD, NULL, &userPrefs.modeBatt.width, &keySize);
keySize = sizeof(DWORD);
RegGetValue(regKey, L"Battery", L"Height", RRF_RT_REG_DWORD, NULL, &userPrefs.modeBatt.height, &keySize);
keySize = sizeof(DWORD);
RegGetValue(regKey, L"Battery", L"Refresh Rate", RRF_RT_REG_DWORD, NULL, &userPrefs.modeBatt.refresh, &keySize);
keySize = sizeof(DWORD);
RegGetValue(regKey, L"AC Power", L"Width", RRF_RT_REG_DWORD, NULL, &userPrefs.modeAC.width, &keySize);
keySize = sizeof(DWORD);
RegGetValue(regKey, L"AC Power", L"Height", RRF_RT_REG_DWORD, NULL, &userPrefs.modeAC.height, &keySize);
keySize = sizeof(DWORD);
RegGetValue(regKey, L"AC Power", L"Refresh Rate", RRF_RT_REG_DWORD, NULL, &userPrefs.modeAC.refresh, &keySize);
return TRUE;
}
else return FALSE;
}
static INT_PTR CALLBACK PrefsDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static INT_PTR CALLBACK PrefsDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) { switch (message) {
case WM_INITDIALOG: { case WM_INITDIALOG: {
@ -154,21 +197,17 @@ 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. // Add to both combo boxes and check if this is the mode that was set.
SendMessage(hComboBatt, CB_ADDSTRING, 0, (LPARAM)resText); SendMessage(hComboBatt, CB_ADDSTRING, 0, (LPARAM)resText);
if (DisplayModeEquals(&modeBatt, &currentMode)) if (DisplayModeEquals(&userPrefs.modeBatt, &currentMode))
SendMessage(hComboBatt, CB_SETCURSEL, modeCount, 0); SendMessage(hComboBatt, CB_SETCURSEL, modeCount, 0);
SendMessage(hComboAC, CB_ADDSTRING, 0, (LPARAM)resText); SendMessage(hComboAC, CB_ADDSTRING, 0, (LPARAM)resText);
if (DisplayModeEquals(&modeAC, &currentMode)) if (DisplayModeEquals(&userPrefs.modeAC, &currentMode))
SendMessage(hComboAC, CB_SETCURSEL, modeCount, 0); SendMessage(hComboAC, CB_SETCURSEL, modeCount, 0);
++modeCount; ++modeCount;
lastMode = currentMode; lastMode = currentMode;
} }
} }
// 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);
return TRUE; return TRUE;
} }
@ -176,8 +215,9 @@ static INT_PTR CALLBACK PrefsDialogProc(HWND hDlg, UINT message, WPARAM wParam,
switch (LOWORD(wParam)) { switch (LOWORD(wParam)) {
case IDC_BUTTON_APPLY: case IDC_BUTTON_APPLY:
case IDOK: { case IDOK: {
modeBatt = GetModeFromCB(hDlg, IDC_COMBO_BATT); userPrefs.modeBatt = GetModeFromCB(hDlg, IDC_COMBO_BATT);
modeAC = GetModeFromCB(hDlg, IDC_COMBO_AC); userPrefs.modeAC = GetModeFromCB(hDlg, IDC_COMBO_AC);
SavePrefs();
if (LOWORD(wParam) == IDC_BUTTON_APPLY) return TRUE; if (LOWORD(wParam) == IDC_BUTTON_APPLY) return TRUE;
} }
case IDCANCEL: { case IDCANCEL: {
@ -208,18 +248,17 @@ enum TRAY_IDS {
}; };
static NOTIFYICONDATA nid; static NOTIFYICONDATA nid;
static HWND hWnd;
static HMENU hMenu; 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) { switch (uMsg) {
case WM_TRAYICON: case WM_TRAYICON:
if (lParam == WM_RBUTTONUP) { if (lParam == WM_RBUTTONUP) {
POINT pt; POINT pt;
GetCursorPos(&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; break;
@ -232,7 +271,7 @@ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM l
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(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);
break; break;
case ID_TRAY_EXIT: case ID_TRAY_EXIT:
DestroyWindow(hwnd); DestroyWindow(hWnd);
break; break;
} }
break; break;
@ -247,17 +286,28 @@ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM l
OutputDebugString(L"Power status changed\n"); OutputDebugString(L"Power status changed\n");
SYSTEM_POWER_STATUS powerStatus; SYSTEM_POWER_STATUS powerStatus;
if (GetSystemPowerStatus(&powerStatus)) { if (GetSystemPowerStatus(&powerStatus)) {
ChangeDisplayMode(powerStatus.ACLineStatus ? &modeAC : &modeBatt, CDS_UPDATEREGISTRY); ChangeDisplayMode(powerStatus.ACLineStatus ? &userPrefs.modeAC : &userPrefs.modeBatt, CDS_UPDATEREGISTRY);
} }
} }
break; break;
} }
return DefWindowProc(hwnd, uMsg, wParam, lParam); return DefWindowProc(hWnd, uMsg, wParam, lParam);
} }
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// Initialize the controls if (!LoadPrefs()) { // set both battery and AC to current display mode if there are no preferences set
InitCommonControls(); DEVMODE currentMode = { .dmSize = sizeof(currentMode) };
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &currentMode);
DISPLAY_MODE mode = {
.width = currentMode.dmPelsWidth,
.height = currentMode.dmPelsHeight,
.refresh = currentMode.dmDisplayFrequency
};
userPrefs.modeAC = mode;
userPrefs.modeBatt = mode;
}
InitCommonControls(); // Initialize modern controls
WNDCLASS wc = { 0 }; WNDCLASS wc = { 0 };
wc.lpfnWndProc = WindowProc; wc.lpfnWndProc = WindowProc;
@ -266,7 +316,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
RegisterClass(&wc); 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); HWND_MESSAGE, NULL, hInstance, NULL);
RegisterPowerSettingNotification(hWnd, &GUID_ACDC_POWER_SOURCE, DEVICE_NOTIFY_WINDOW_HANDLE); RegisterPowerSettingNotification(hWnd, &GUID_ACDC_POWER_SOURCE, DEVICE_NOTIFY_WINDOW_HANDLE);