Compare commits

..

17 Commits

Author SHA1 Message Date
d29f57fb13 Change all instances of DEVMODE to DEVMODEW since the program is unicode 2025-08-15 22:38:03 -07:00
5048cafb9f Add display name to display mode changing and testing functions 2025-08-15 19:20:12 -07:00
0f66725951 Separate DISPLAY_MODE struct and associated functions into another file. 2025-08-15 18:36:55 -07:00
863471e08e Add UI items for selecting a display 2025-08-15 18:32:47 -07:00
cedb800012 Fix annotations for WinMain
Fix "C28251 Inconsistent annotation for function" compiler warning by correctly defining WinMain.
2025-08-15 11:44:51 -07:00
5911ca0100 Update README.md
Update the readme to reflect the current state of the project. The program is much more complete now, and has been tested and works flawlessly on both Windows Vista and 7.
2025-08-14 23:20:15 -07:00
e4157e70b3 Fix 32-bit profiles 2025-08-12 12:40:59 -04:00
a5555b4ee1 Guarantee Vista support
Define NT version headers so that Windows Vista support should reasonably be guaranteed, since we will not be using any newer APIs.
2025-08-12 09:16:34 -04:00
01727c04f0 Remove the battery warning
The battery warning could be annoying, and the option to disable it is confusing to somebody who may never see it.
2025-08-11 15:33:32 -04:00
e5dbac5879 Fix Debug and Release profiles for both x86 and x64
Application now builds with debug and release profiles on both x86 and x64.
2025-08-11 12:55:04 -04:00
4dc933d4bc Update README.md 2025-08-10 13:12:01 -04:00
0295d289cf Create an application icon.
It's quick and dirty but it works well enough.
2025-08-10 01:52:20 -04:00
350e2960b8 Add functionality for running at startup and disabling the battery warning 2025-08-09 16:03:40 -04:00
036a20df3f Display a warning when there is no battery detected 2025-08-09 14:32:15 -04:00
6a586dedb7 Only change video mode if the current video mode matches one of the preferences.
This avoids the program resetting the video mode if you're doing something like testing different resolutions or playing a game in fullscreen.
2025-08-09 13:14:10 -04:00
4b46eafd35 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.
2025-08-09 11:56:59 -04:00
046e8fd2eb Actually enter name in the license. 2025-08-09 11:52:05 -04:00
10 changed files with 297 additions and 150 deletions

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) [year] [fullname] Copyright (c) 2025 Mina Brown
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -2,15 +2,15 @@
A utility for switching the resolution of your laptop's display based on the current power state. A utility for switching the resolution of your laptop's display based on the current power state.
Currently a work in progress. Not very useful yet since it doesn't save your preferences. It doesn't even have an icon. Currently a work in progress. It works reasonably well for a standard single display configuration, but other scenarios have not been extensively tested, and there are some edge cases that may need to be worked out.
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 supports Windows Vista or higher.
## TODO ## TODO
- [ ] Save preferences to the Windows registry. - [x] Save preferences to the Windows registry.
- [ ] Add a checkbox to preferences for starting at login. - [x] Add a checkbox to preferences for starting at login.
- [ ] Add an icon. - [x] 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. - [x] Make the default display mode the current one instead of the highest resolution if there are no preferences set.
- [ ] 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.
- [ ] Support dark mode.

109
WinPowerDMS/DisplayMode.c Normal file
View File

@ -0,0 +1,109 @@
#include <stdio.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "DisplayMode.h"
BOOL DisplayModeEquals(const DISPLAY_MODE* a, const DISPLAY_MODE* b) {
return a->width == b->width && a->height == b->height && a->refresh == b->refresh;
}
// I hate parsing the string here, but the alternative requires extra memory management.
DISPLAY_MODE GetModeFromCB(HWND hComboBox) {
DISPLAY_MODE mode = { 0 };
LRESULT selectedIndex = SendMessage(hComboBox, CB_GETCURSEL, 0, 0);
if (selectedIndex != CB_ERR) {
LRESULT len = SendMessage(hComboBox, CB_GETLBTEXTLEN, selectedIndex, 0);
if (len != CB_ERR) {
LPWSTR text = HeapAlloc(GetProcessHeap(), 0, len * sizeof(*text));
if (text) {
SendMessage(hComboBox, CB_GETLBTEXT, selectedIndex, (LPARAM)text);
swscanf_s(text, L"%dx%d @ %d Hz", &mode.width, &mode.height, &mode.refresh);
HeapFree(GetProcessHeap(), 0, text);
}
}
}
return mode;
}
// returns the result of the ChangeDisplaySettings call that this results in.
LONG ChangeDisplayMode(LPCWSTR displayName, const DISPLAY_MODE* mode, DWORD dwFlags) {
DEVMODEW devMode = {
.dmSize = sizeof(devMode),
.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY,
.dmPelsWidth = mode->width,
.dmPelsHeight = mode->height,
.dmDisplayFrequency = mode->refresh
};
if (displayName) wcscpy_s(devMode.dmDeviceName, sizeof(devMode.dmDeviceName) / sizeof(devMode.dmDeviceName[0]), displayName);
return ChangeDisplaySettings(&devMode, dwFlags);
}
struct MessageBoxParams {
HWND hWnd;
LPCWSTR lpText;
LPCWSTR lpCaption;
UINT uType;
};
static DWORD WINAPI MessageBoxAsync(LPVOID lpParam) {
struct MessageBoxParams* params = lpParam;
return MessageBox(params->hWnd, params->lpText, params->lpCaption, params->uType);
}
struct TestDisplayModeParams {
HWND hDlg;
WCHAR displayName[32];
DISPLAY_MODE mode;
};
static DWORD WINAPI TestDisplayModeThread(LPVOID lpParam) {
struct TestDisplayModeParams* params = lpParam;
DEVMODEW originalMode = { .dmSize = sizeof(originalMode) };
EnumDisplaySettings(params->displayName, ENUM_CURRENT_SETTINGS, &originalMode);
ChangeDisplayMode(params->displayName, &params->mode, CDS_FULLSCREEN);
// Create the message box about the resolution change
WCHAR msgText[96];
swprintf_s(msgText, sizeof(msgText) / sizeof(msgText[0]),
L"Testing %dx%d @ %d Hz\nThe display mode will reset back in 10 seconds.",
params->mode.width, params->mode.height, params->mode.refresh);
struct MessageBoxParams mbParams = {
.hWnd = params->hDlg,
.lpText = msgText,
.lpCaption = L"Resolution Test",
.uType = MB_OK | MB_ICONINFORMATION
};
HANDLE mbThread = CreateThread(NULL, 0, MessageBoxAsync, &mbParams, 0, NULL);
if (mbThread) {
CloseHandle(mbThread);
Sleep(10000);
ChangeDisplaySettings(&originalMode, 0);
}
else {
MessageBox(params->hDlg, L"Failed to test resolution.", L"Error", MB_OK | MB_ICONERROR);
}
HeapFree(GetProcessHeap(), 0, params);
return 0;
}
void TestDisplayMode(HWND hDlg, LPCWSTR displayName, DISPLAY_MODE* mode) {
struct TestDisplayModeParams* tdmParams = HeapAlloc(GetProcessHeap(), 0, sizeof(*tdmParams));
if (tdmParams) {
tdmParams->hDlg = hDlg;
if (tdmParams->displayName) wcscpy_s(tdmParams->displayName, sizeof(tdmParams->displayName) / sizeof(tdmParams->displayName[0]), displayName);
tdmParams->mode = *mode;
HANDLE tdmThread = CreateThread(NULL, 0, TestDisplayModeThread, tdmParams, 0, NULL);
if (tdmThread) CloseHandle(tdmThread);
else {
HeapFree(GetProcessHeap(), 0, tdmParams);
MessageBox(hDlg, L"Failed to test resolution.", L"Error", MB_OK | MB_ICONERROR);
}
}
else {
MessageBox(hDlg, L"Failed to test resolution.", L"Error", MB_OK | MB_ICONERROR);
}
}

19
WinPowerDMS/DisplayMode.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef struct {
DWORD width;
DWORD height;
DWORD refresh;
} DISPLAY_MODE;
BOOL DisplayModeEquals(const DISPLAY_MODE* a, const DISPLAY_MODE* b);
DISPLAY_MODE GetModeFromCB(HWND hComboBox);
// returns the result of the ChangeDisplaySettings call that this results in.
LONG ChangeDisplayMode(LPCWSTR displayName, const DISPLAY_MODE* mode, DWORD dwFlags);
void TestDisplayMode(HWND hDlg, LPCWSTR displayName, DISPLAY_MODE* mode);

View File

@ -1,9 +1,14 @@
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT _WIN32_WINNT_VISTA
#define NTDDI_VERSION NTDDI_VISTA
#include <stdio.h> #include <stdio.h>
#include <windows.h> #include <windows.h>
#include <shellapi.h> #include <shellapi.h>
#include <commctrl.h> #include <commctrl.h>
#include "resource.h" #include "resource.h"
#include "DisplayMode.h"
// Link Common Controls v6 for visual styling // Link Common Controls v6 for visual styling
#pragma comment(linker,"\"/manifestdependency:type='win32' \ #pragma comment(linker,"\"/manifestdependency:type='win32' \
@ -14,130 +19,97 @@ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#define TRAY_ICON_ID 1001 #define TRAY_ICON_ID 1001
typedef struct { typedef struct {
int width; DISPLAY_MODE modeBatt;
int height; DISPLAY_MODE modeAC;
int refresh; BOOL runAtStartup;
} DISPLAY_MODE; } WINPOWERDMS_PREFS;
static DISPLAY_MODE modeBatt = { 0 }; static WINPOWERDMS_PREFS userPrefs = { 0 };
static DISPLAY_MODE modeAC = { 0 };
BOOL DisplayModeEquals(const DISPLAY_MODE* a, const DISPLAY_MODE* b) { // Save user preferences to registry.
return a->width == b->width && a->height == b->height && a->refresh == b->refresh; static BOOL SavePrefs(void) {
} BOOL saved = FALSE;
#define MODE_SELECTED(mode) (!DisplayModeEquals(&mode, &(DISPLAY_MODE) { 0 })) HKEY regKey;
if (!RegCreateKeyEx(
// I hate parsing the string here, but the alternative requires extra memory management. HKEY_CURRENT_USER, L"SOFTWARE\\WinPowerDMS", 0, NULL,
static DISPLAY_MODE GetModeFromCB(HWND hDlg, int nIDDlgItem) { REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &regKey, NULL
DISPLAY_MODE mode = { 0 }; )) {
HWND hComboBox = GetDlgItem(hDlg, nIDDlgItem); RegSetKeyValue(regKey, L"Battery", L"Width", REG_DWORD, &userPrefs.modeBatt.width, sizeof(userPrefs.modeBatt.width));
size_t selectedIndex = SendMessage(hComboBox, CB_GETCURSEL, 0, 0); RegSetKeyValue(regKey, L"Battery", L"Height", REG_DWORD, &userPrefs.modeBatt.height, sizeof(userPrefs.modeBatt.height));
if (selectedIndex != CB_ERR) { RegSetKeyValue(regKey, L"Battery", L"Refresh Rate", REG_DWORD, &userPrefs.modeBatt.refresh, sizeof(userPrefs.modeBatt.refresh));
LRESULT len = SendMessage(hComboBox, CB_GETLBTEXTLEN, selectedIndex, 0); RegSetKeyValue(regKey, L"AC Power", L"Width", REG_DWORD, &userPrefs.modeAC.width, sizeof(userPrefs.modeAC.width));
if (len != CB_ERR) { RegSetKeyValue(regKey, L"AC Power", L"Height", REG_DWORD, &userPrefs.modeAC.height, sizeof(userPrefs.modeAC.height));
LPWSTR text = HeapAlloc(GetProcessHeap(), 0, len * sizeof(*text)); RegSetKeyValue(regKey, L"AC Power", L"Refresh Rate", REG_DWORD, &userPrefs.modeAC.refresh, sizeof(userPrefs.modeAC.refresh));
if (text) { RegCloseKey(regKey);
SendMessage(hComboBox, CB_GETLBTEXT, selectedIndex, (LPARAM) text); saved = TRUE;
swscanf_s(text, L"%dx%d @ %d Hz", &mode.width, &mode.height, &mode.refresh);
HeapFree(GetProcessHeap(), 0, text);
}
}
} }
return mode; // 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;
} }
// returns the result of the ChangeDisplaySettings call that this results in. // Load user preferences from registry.
static LONG ChangeDisplayMode(const DISPLAY_MODE* mode, DWORD dwFlags) { static BOOL LoadPrefs(void) {
DEVMODE devMode = { BOOL loaded = FALSE;
.dmSize = sizeof(devMode),
.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY, HKEY regKey;
.dmPelsWidth = mode->width, if (!RegOpenKeyEx(HKEY_CURRENT_USER, L"SOFTWARE\\WinPowerDMS", 0, KEY_READ, &regKey)) {
.dmPelsHeight = mode->height, DWORD keySize = sizeof(userPrefs.modeBatt.width);
.dmDisplayFrequency = mode->refresh 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);
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) {
DEVMODEW currentMode = { .dmSize = sizeof(currentMode) };
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &currentMode);
return (DISPLAY_MODE) {
.width = currentMode.dmPelsWidth,
.height = currentMode.dmPelsHeight,
.refresh = currentMode.dmDisplayFrequency
}; };
return ChangeDisplaySettings(&devMode, dwFlags);
} }
struct MessageBoxParams { static INT_PTR CALLBACK PrefsDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
HWND hWnd; HWND hComboBatt = GetDlgItem(hDlg, IDC_COMBO_BATT);
LPCWSTR lpText; HWND hComboAC = GetDlgItem(hDlg, IDC_COMBO_AC);
LPCWSTR lpCaption; HWND hCheckStartup = GetDlgItem(hDlg, IDC_CHECK_STARTUP);
UINT uType;
};
static DWORD WINAPI MessageBoxAsync(LPVOID lpParam) { switch (uMsg) {
struct MessageBoxParams* params = lpParam;
return MessageBox(params->hWnd, params->lpText, params->lpCaption, params->uType);
}
struct TestDisplayModeParams {
HWND hDlg;
DISPLAY_MODE mode;
};
static DWORD WINAPI TestDisplayModeThread(LPVOID lpParam) {
struct TestDisplayModeParams* params = lpParam;
DEVMODE originalMode = { .dmSize = sizeof(originalMode) };
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &originalMode);
ChangeDisplayMode(&params->mode, CDS_FULLSCREEN);
// Create the message box about the resolution change
WCHAR msgText[96];
swprintf_s(msgText, sizeof(msgText) / sizeof(msgText[0]),
L"Testing %dx%d @ %d Hz\nThe display mode will reset back in 10 seconds.",
params->mode.width, params->mode.height, params->mode.refresh);
struct MessageBoxParams mbParams = {
.hWnd = params->hDlg,
.lpText = msgText,
.lpCaption = L"Resolution Test",
.uType = MB_OK | MB_ICONINFORMATION
};
HANDLE mbThread = CreateThread(NULL, 0, MessageBoxAsync, &mbParams, 0, NULL);
if (mbThread) {
CloseHandle(mbThread);
Sleep(10000);
ChangeDisplaySettings(&originalMode, 0);
}
else {
MessageBox(params->hDlg, L"Failed to test resolution.", L"Error", MB_OK | MB_ICONERROR);
}
HeapFree(GetProcessHeap(), 0, params);
return 0;
}
static void TestDisplayMode(HWND hDlg, DISPLAY_MODE* mode) {
struct TestDisplayModeParams* tdmParams = HeapAlloc(GetProcessHeap(), 0, sizeof(*tdmParams));
if (tdmParams) {
tdmParams->hDlg = hDlg;
tdmParams->mode = *mode;
HANDLE tdmThread = CreateThread(NULL, 0, TestDisplayModeThread, tdmParams, 0, NULL);
if (tdmThread) CloseHandle(tdmThread);
else {
HeapFree(GetProcessHeap(), 0, tdmParams);
MessageBox(hDlg, L"Failed to test resolution.", L"Error", MB_OK | MB_ICONERROR);
}
}
else {
MessageBox(hDlg, L"Failed to test resolution.", L"Error", MB_OK | MB_ICONERROR);
}
}
static INT_PTR CALLBACK PrefsDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_INITDIALOG: { case WM_INITDIALOG: {
HWND hComboBatt = GetDlgItem(hDlg, IDC_COMBO_BATT);
HWND hComboAC = GetDlgItem(hDlg, IDC_COMBO_AC);
// devMode object that will be enumerated // devMode object that will be enumerated
DEVMODE devMode; DEVMODEW devMode;
ZeroMemory(&devMode, sizeof(devMode)); ZeroMemory(&devMode, sizeof(devMode));
devMode.dmSize = sizeof(devMode); devMode.dmSize = sizeof(devMode);
size_t modeCount = 0; size_t modeCount = 0;
int modeNum = 0; DWORD modeNum = 0;
DISPLAY_MODE lastMode = { 0 }; DISPLAY_MODE lastMode = { 0 };
while (EnumDisplaySettings(NULL, modeNum++, &devMode)) { while (EnumDisplaySettings(NULL, modeNum++, &devMode)) {
DISPLAY_MODE currentMode = { DISPLAY_MODE currentMode = {
@ -154,11 +126,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. // 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;
@ -166,9 +138,7 @@ static INT_PTR CALLBACK PrefsDialogProc(HWND hDlg, UINT message, WPARAM wParam,
} }
} }
// select the highest display mode if one hasn't been selected yet SendMessage(hCheckStartup, BM_SETCHECK, userPrefs.runAtStartup ? BST_CHECKED : BST_UNCHECKED, 0);
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 +146,10 @@ 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(hComboBatt);
modeAC = GetModeFromCB(hDlg, IDC_COMBO_AC); userPrefs.modeAC = GetModeFromCB(hComboAC);
userPrefs.runAtStartup = SendMessage(hCheckStartup, BM_GETCHECK, 0, 0) == BST_CHECKED;
SavePrefs();
if (LOWORD(wParam) == IDC_BUTTON_APPLY) return TRUE; if (LOWORD(wParam) == IDC_BUTTON_APPLY) return TRUE;
} }
case IDCANCEL: { case IDCANCEL: {
@ -185,13 +157,13 @@ static INT_PTR CALLBACK PrefsDialogProc(HWND hDlg, UINT message, WPARAM wParam,
return TRUE; return TRUE;
} }
case IDC_BUTTON_TEST_BATT: { case IDC_BUTTON_TEST_BATT: {
DISPLAY_MODE mode = GetModeFromCB(hDlg, IDC_COMBO_BATT); DISPLAY_MODE mode = GetModeFromCB(hComboBatt);
TestDisplayMode(hDlg, &mode); TestDisplayMode(hDlg, NULL, &mode);
return TRUE; return TRUE;
} }
case IDC_BUTTON_TEST_AC: { case IDC_BUTTON_TEST_AC: {
DISPLAY_MODE mode = GetModeFromCB(hDlg, IDC_COMBO_AC); DISPLAY_MODE mode = GetModeFromCB(hComboAC);
TestDisplayMode(hDlg, &mode); TestDisplayMode(hDlg, NULL, &mode);
return TRUE; return TRUE;
} }
break; break;
@ -208,18 +180,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;
@ -229,10 +200,10 @@ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM l
DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_PREFSDIALOG), hWnd, PrefsDialogProc); DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_PREFSDIALOG), hWnd, PrefsDialogProc);
break; break;
case ID_TRAY_ABOUT: 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; break;
case ID_TRAY_EXIT: case ID_TRAY_EXIT:
DestroyWindow(hwnd); DestroyWindow(hWnd);
break; break;
} }
break; break;
@ -245,19 +216,28 @@ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM l
case WM_POWERBROADCAST: case WM_POWERBROADCAST:
if (LOWORD(wParam) == PBT_POWERSETTINGCHANGE) { if (LOWORD(wParam) == PBT_POWERSETTINGCHANGE) {
OutputDebugString(L"Power status changed\n"); OutputDebugString(L"Power status changed\n");
DISPLAY_MODE currentMode = GetCurrentDisplayMode();
SYSTEM_POWER_STATUS powerStatus; SYSTEM_POWER_STATUS powerStatus;
if (GetSystemPowerStatus(&powerStatus)) { // Change display mode only if the current display mode is one of the configured options.
ChangeDisplayMode(powerStatus.ACLineStatus ? &modeAC : &modeBatt, CDS_UPDATEREGISTRY); if (GetSystemPowerStatus(&powerStatus) &&
(DisplayModeEquals(&currentMode, &userPrefs.modeBatt) || DisplayModeEquals(&currentMode, &userPrefs.modeAC)))
{
ChangeDisplayMode(NULL, 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(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) {
// Initialize the controls if (!LoadPrefs()) { // set both battery and AC to current display mode if there are no preferences set
InitCommonControls(); DISPLAY_MODE currentMode = GetCurrentDisplayMode();
userPrefs.modeAC = currentMode;
userPrefs.modeBatt = currentMode;
}
InitCommonControls(); // Initialize modern controls
WNDCLASS wc = { 0 }; WNDCLASS wc = { 0 };
wc.lpfnWndProc = WindowProc; wc.lpfnWndProc = WindowProc;
@ -266,7 +246,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);
@ -277,12 +257,13 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
AppendMenu(hMenu, MF_STRING, ID_TRAY_EXIT, L"Exit"); AppendMenu(hMenu, MF_STRING, ID_TRAY_EXIT, L"Exit");
// Setup tray icon // Setup tray icon
HICON hTrayIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPICON));
nid.cbSize = sizeof(NOTIFYICONDATA); nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWnd; nid.hWnd = hWnd;
nid.uID = TRAY_ICON_ID; nid.uID = TRAY_ICON_ID;
nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
nid.uCallbackMessage = WM_TRAYICON; nid.uCallbackMessage = WM_TRAYICON;
nid.hIcon = LoadIcon(NULL, IDI_APPLICATION); // Replace this with my own icon at some point nid.hIcon = hTrayIcon;
wcscpy_s(nid.szTip, sizeof(nid.szTip) / sizeof(nid.szTip[0]), L"WinPowerDMS"); wcscpy_s(nid.szTip, sizeof(nid.szTip) / sizeof(nid.szTip[0]), L"WinPowerDMS");
Shell_NotifyIcon(NIM_ADD, &nid); Shell_NotifyIcon(NIM_ADD, &nid);
@ -294,5 +275,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
DispatchMessage(&msg); DispatchMessage(&msg);
} }
DestroyIcon(hTrayIcon);
return 0; return 0;
} }

BIN
WinPowerDMS/WinPowerDMS.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

View File

@ -79,9 +79,14 @@
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ComCtl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<MinimumRequiredVersion>6.0</MinimumRequiredVersion>
</Link> </Link>
<Manifest>
<EnableDpiAwareness>true</EnableDpiAwareness>
</Manifest>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile> <ClCompile>
@ -93,9 +98,14 @@
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ComCtl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<MinimumRequiredVersion>6.0</MinimumRequiredVersion>
</Link> </Link>
<Manifest>
<EnableDpiAwareness>true</EnableDpiAwareness>
</Manifest>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile> <ClCompile>
@ -108,9 +118,10 @@
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ComCtl32.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>ComCtl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<MinimumRequiredVersion>6.0</MinimumRequiredVersion>
</Link> </Link>
<Manifest> <Manifest>
<EnableDpiAwareness>PerMonitorHighDPIAware</EnableDpiAwareness> <EnableDpiAwareness>true</EnableDpiAwareness>
</Manifest> </Manifest>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -123,19 +134,29 @@
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ComCtl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<MinimumRequiredVersion>6.0</MinimumRequiredVersion>
</Link> </Link>
<Manifest>
<EnableDpiAwareness>true</EnableDpiAwareness>
</Manifest>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="DisplayMode.c" />
<ClCompile Include="WinPowerDMS.c" /> <ClCompile Include="WinPowerDMS.c" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="DisplayMode.h" />
<ClInclude Include="resource.h" /> <ClInclude Include="resource.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="WinPowerDMS.rc" /> <ResourceCompile Include="WinPowerDMS.rc" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Image Include="WinPowerDMS.ico" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>

View File

@ -15,7 +15,10 @@
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="main.c"> <ClCompile Include="WinPowerDMS.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DisplayMode.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
</ItemGroup> </ItemGroup>
@ -23,10 +26,18 @@
<ClInclude Include="resource.h"> <ClInclude Include="resource.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="DisplayMode.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="WinPowerDMS.rc"> <ResourceCompile Include="WinPowerDMS.rc">
<Filter>Resource Files</Filter> <Filter>Resource Files</Filter>
</ResourceCompile> </ResourceCompile>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Image Include="WinPowerDMS.ico">
<Filter>Resource Files</Filter>
</Image>
</ItemGroup>
</Project> </Project>

View File

@ -1,22 +1,27 @@
//{{NO_DEPENDENCIES}} //{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file. // Microsoft Visual C++ generated include file.
// Used by PowerDMS.rc // Used by WinPowerDMS.rc
// //
#define IDD_PREFSDIALOG 101 #define IDD_PREFSDIALOG 101
#define IDI_APPICON 106
#define IDC_COMBO_BATT 1001 #define IDC_COMBO_BATT 1001
#define IDC_COMBO_AC 1002 #define IDC_COMBO_AC 1002
#define IDC_BUTTON_TEST_BATT 1003 #define IDC_BUTTON_TEST_BATT 1003
#define IDC_BUTTON_TEST_AC 1004 #define IDC_BUTTON_TEST_AC 1004
#define IDC_BUTTON_APPLY 1005 #define IDC_BUTTON_APPLY 1005
#define IDC_LIST1 1006 #define IDC_LIST1 1006
#define IDC_CHECK_STARTUP 1007
#define IDC_COMBO_DISPLAY 1008
#define IDC_CHECK_ENABLE_DISPLAY 1009
#define IDC_CHECK_DISPLAY 1009
// Next default values for new objects // Next default values for new objects
// //
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 104 #define _APS_NEXT_RESOURCE_VALUE 107
#define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1007 #define _APS_NEXT_CONTROL_VALUE 1012
#define _APS_NEXT_SYMED_VALUE 101 #define _APS_NEXT_SYMED_VALUE 101
#endif #endif
#endif #endif