Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Build

on:
push:
branches: [ main ]
tags: [ 'v*' ]
pull_request:
branches: [ main ]
workflow_dispatch:

jobs:
build:
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: true
install: >-
mingw-w64-ucrt-x86_64-gcc
make

- name: Build with MinGW-w64
run: |
windres resource.rc -O coff -o resource.res
g++ -O2 -s -Wall -Wextra -Wpedantic \
-Wno-unknown-pragmas -Wno-missing-field-initializers \
main.cpp resource.res -o AutoResChanger.exe \
-mwindows -municode \
-luser32 -lgdi32 -lshell32 -lcomdlg32 -ladvapi32 \
-ldwmapi -luxtheme -lole32 -lcomctl32

- name: Build and run unit tests
run: |
g++ -O2 -std=c++17 -Wall -Wextra \
-Wno-unknown-pragmas -Wno-missing-field-initializers \
tests/test_main.cpp -o tests/test_runner.exe \
-luser32 -lgdi32 -lshell32 -lcomdlg32 -ladvapi32 \
-ldwmapi -luxtheme -lole32 -lcomctl32
./tests/test_runner.exe

- name: Run GUI end-to-end tests
shell: pwsh
run: ./tests/gui_save_test.ps1

- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: AutoResChanger
path: AutoResChanger.exe
if-no-files-found: error

- name: Attach to release
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v2
with:
files: AutoResChanger.exe
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Build artifacts
*.exe
*.res
*.o
*.obj
*.pdb
*.ilk
build/
out/

# Test artifacts
tests/test_runner.exe
tests/*.dSYM/

# MSVC / MinGW
*.dSYM/
52 changes: 52 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
cmake_minimum_required(VERSION 3.16)
project(AutoResChanger LANGUAGES CXX RC)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

if(NOT WIN32)
message(FATAL_ERROR "AutoRes Changer targets Windows only.")
endif()

# Prefer MinGW-w64 (MSYS2) or MSVC. The code is portable across both, but the
# #pragma comment(lib, ...) directives are MSVC-only and simply ignored by GCC,
# so the libraries are listed explicitly below for the MinGW path.
add_executable(AutoResChanger WIN32 main.cpp resource.rc)

target_compile_definitions(AutoResChanger PRIVATE UNICODE _UNICODE)

if(MSVC)
target_compile_options(AutoResChanger PRIVATE /W4 /permissive-)
target_link_libraries(AutoResChanger PRIVATE
user32 gdi32 shell32 comdlg32 advapi32 dwmapi uxtheme)
set_target_properties(AutoResChanger PROPERTIES
LINK_FLAGS "/ENTRY:wWinMainCRTStartup")
else()
# MinGW-w64
target_compile_options(AutoResChanger PRIVATE
-Wall -Wextra -Wpedantic
-Wno-unknown-pragmas
-Wno-missing-field-initializers)
# -municode selects wWinMain as the entry point.
target_link_options(AutoResChanger PRIVATE -municode)
target_link_libraries(AutoResChanger PRIVATE
user32 gdi32 shell32 comdlg32 advapi32 dwmapi uxtheme comctl32 ole32)
endif()

# Emit the binary next to the source tree for convenience.
set_target_properties(AutoResChanger PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")

# ---- Tests --------------------------------------------------------------
# Headless unit tests (compiled with -DTESTING, which excludes the GUI).
enable_testing()
add_executable(autores_tests tests/test_main.cpp)
target_compile_definitions(autores_tests PRIVATE UNICODE _UNICODE)
if(MSVC)
target_compile_options(autores_tests PRIVATE /W4 /permissive-)
target_link_libraries(autores_tests PRIVATE user32 gdi32 shell32 comdlg32 advapi32 dwmapi ole32 comctl32)
else()
target_compile_options(autores_tests PRIVATE -Wall -Wextra -Wno-unknown-pragmas -Wno-missing-field-initializers)
target_link_libraries(autores_tests PRIVATE user32 gdi32 shell32 comdlg32 advapi32 dwmapi uxtheme ole32 comctl32)
endif()
add_test(NAME unit COMMAND autores_tests)
71 changes: 56 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,44 @@ If you prefer to compile the application yourself using a lightweight compiler l
4. Place any custom icon file you want to use inside the folder and name it `app.ico`.

#### Step 2: Compile the Program
1. Open your terminal in the folder where your files are located.
2. Run the resource compiler to prepare your custom icon:
```bash
windres resource.rc -O coff -o resource.res
```
3. Run the C++ compiler to build the final executable:
```bash
g++ -O2 main.cpp resource.res -o AutoResChanger.exe -mwindows -municode -luser32 -lgdi32 -lshell32 -lcomdlg32 -ladvapi32
```
Pick whichever build method you prefer.

**Quick build (Windows PowerShell script).** The bundled `build.ps1` auto-detects the MinGW toolchain and runs everything for you:
```powershell
.\build.ps1 # release build
.\build.ps1 -Debug # debug build with symbols
.\build.ps1 -Clean # remove build artifacts first
```

**CMake.** A `CMakeLists.txt` is provided for both MinGW-w64 and MSVC:
```bash
cmake -S . -B build -G "MinGW Makefiles"
cmake --build build
ctest --test-dir build --output-on-failure # run unit tests
```

**Manual (MinGW-w64).** Run the resource compiler, then the C++ compiler. Note that `-municode` and linking `resource.res` are required so the Unicode resource APIs resolve correctly:
```bash
windres resource.rc -O coff -o resource.res
g++ -O2 -Wall -Wextra main.cpp resource.res -o AutoResChanger.exe -mwindows -municode -luser32 -lgdi32 -lshell32 -lcomdlg32 -ladvapi32 -ldwmapi -luxtheme -lole32 -lcomctl32
```

A GitHub Actions workflow (`.github/workflows/build.yml`) automatically builds the executable and runs both the unit and GUI tests on every push, then attaches the binary to a release whenever you push a `v*` tag.

---

## Testing

AutoRes Changer ships with two layers of automated tests:

* **Headless unit tests** (`tests/test_main.cpp`) cover the pure logic: path parsing, config save/load round-trips, atomic-write cleanliness, mode enumeration, and process detection. They compile with `-DTESTING`, which excludes the GUI. Run them via `ctest` (see above) or:
```powershell
.\tests\run_tests.ps1
```
* **GUI end-to-end tests** (`tests/gui_save_test.ps1`) drive the real window through Win32 messages to prove the full Save/Delete/validation flow writes the correct `config.ini` with no leftover temp files:
```powershell
.\tests\run_tests.ps1 -Gui
```

<hr>

Expand All @@ -49,17 +78,21 @@ Managing your custom application profiles is straightforward and handled entirel

### Creating a Profile
1. Launch `AutoResChanger.exe`.
2. Click the **`...`** browse button next to the **Executable Path** field and select the `.exe` file of the game or program you want to configure.
2. Click the **`...`** browse button next to the **Executable Path** field and select the `.exe` file of the game or program you want to configure. You can also simply **drag and drop** an `.exe` from Explorer onto the window.
3. Select which screen you want to modify from the **Target Display** drop-down list (supports multi-monitor setups).
4. Choose a pre-defined layout from the **Resolution Template / Presets** drop-down menu, or type custom dimensions manually into the **Width**, **Height**, and **Hz** input fields.
5. Click **Save Profile**. Your new configuration will appear in the left-hand profile list.
4. Pick a resolution in one of two ways:
* **Supported Modes** — a live list read directly from the selected monitor, so every entry is guaranteed to be supported.
* **Resolution Template / Presets** — a curated list of common resolutions, or type custom dimensions manually into the **Width**, **Height**, and **Hz** input fields.
5. Click **Save Profile**. Your new configuration will appear in the left-hand profile list (with the target app's own icon).

### Testing Modes Safely
If you want to verify whether a custom resolution or refresh rate is supported by your monitor before saving it:
1. Input your target dimensions or select an existing profile.
2. Click the **Test Display Settings** button.
3. Your screen will temporarily transition to the selected mode.
4. A prompt will appear on your screen. Clicking **OK** or waiting will safely revert your monitor to its original desktop settings.
2. Click the **Test Display Settings** button. The button becomes **Keep This Mode** and a countdown starts.
3. Your screen temporarily transitions to the selected mode.
4. Click **Keep This Mode** to confirm, or simply do nothing — after 15 seconds the utility automatically reverts to your original desktop settings. This guarantees you can always recover even if the tested mode renders your display unreadable.

> Validation feedback (missing path, invalid resolution, unsupported mode) is shown in a non-blocking status line beneath the buttons rather than a pop-up dialog, so the interface never traps you.

---

Expand All @@ -68,14 +101,21 @@ If you want to verify whether a custom resolution or refresh rate is supported b
AutoRes Changer includes several features to accommodate complex setups and game behaviors:

* <span style="color:#2980b9"><b>Multi-Monitor Routing:</b></span> Instead of changing settings globally, you can assign target resolutions to specific displays. The utility reads your active hardware configuration to target individual monitors cleanly.
* <span style="color:#16a085"><b>Live Supported Modes:</b></span> The **Supported Modes** drop-down is populated on the fly from your selected monitor via `EnumDisplaySettings`, so you can see exactly which resolutions and refresh rates your hardware actually offers before saving.
* <span style="color:#27ae60"><b>Startup Delay (Grace Period):</b></span> Some games load an initial splash screen or configuration launcher before launching the actual game window. Setting a **Delay** (in seconds) tells the utility to wait until the primary game window is fully loaded before executing the resolution override.
* <span style="color:#8e44ad"><b>Input Validation:</b></span> Width and Height are validated before a profile is saved or tested, preventing accidental zero-sized or malformed modes.
* <span style="color:#c0392b"><b>Single Instance:</b></span> Only one copy of AutoRes Changer can run at a time, so launching it twice never leaves duplicate tray icons or competing monitors fighting over your display settings.
* <span style="color:#d35400"><b>Auto-Start on Boot:</b></span> Checking **Start automatically with Windows** registers the application in your local user workspace. Upon system boot, it launches silently in the background and rests minimized in your system tray without interrupting you.
* <span style="color:#2c3e50"><b>Dark Mode &amp; Diagnostic Logging:</b></span> Toggle a dark UI theme and an optional diagnostic log (written to `%APPDATA%\AutoResChanger\log.txt`) that records each detection, mode change, and failure — handy when troubleshooting a stubborn game.
* <span style="color:#7f8c8d"><b>Per-Monitor DPI Aware:</b></span> Uses the modern per-monitor-v2 DPI context so the interface stays crisp on mixed-DPI and high-refresh setups.

---

## Background Behavior & Reversion Safety

* **Minimizing to Tray:** Closing the configuration window via the standard close button does not exit the utility. It hides the interface to the system tray so that monitoring remains active. To restore the window, simply double-click the system tray icon near your clock.
* **Tray Quick Actions:** Right-clicking the tray icon offers **Open window**, **Restore resolution now** (instantly reverts if a profile change is currently active, enabled only when one is), **Open config folder**, and **Exit AutoRes Changer**.
* **Guaranteed Restoration:** If a profile is active (i.e. your resolution is currently changed) and you exit the utility — or log off / shut down Windows — the original desktop resolution is restored automatically first. You will never be left stuck at a game resolution after closing AutoRes Changer.
* **Emergency Reversion:** The display modifications are applied using standard Windows dynamic sessions (`CDS_FULLSCREEN`). This design choice means that your custom resolutions are not permanently written to your Windows registry. If a game crashes or your system restarts unexpectedly, Windows will natively restore your default desktop resolution automatically.

---
Expand All @@ -85,6 +125,7 @@ AutoRes Changer includes several features to accommodate complex setups and game
### Managing Configurations
Your application profiles are saved cleanly in a standard configuration file on your system.
* You can find your saved settings at: `%APPDATA%\AutoResChanger\config.ini`
* An optional diagnostic log (when **Enable diagnostic logging** is checked) is written to `%APPDATA%\AutoResChanger\log.txt`.
* To clear all profiles or start fresh, you can simply delete the `config.ini` file or the parent `AutoResChanger` directory inside your AppData folder.

### Complete Removal
Expand Down
55 changes: 55 additions & 0 deletions build.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# build.ps1 - one-shot build script for AutoRes Changer (MinGW-w64 / MSYS2).
#
# Usage:
# .\build.ps1 # release build
# .\build.ps1 -Debug # debug build with symbols
# .\build.ps1 -Clean # remove build artifacts first
#
# Requires g++ and windres on PATH (e.g. MSYS2 UCRT64: C:\msys64\ucrt64\bin).

param(
[switch]$Debug,
[switch]$Clean
)

$ErrorActionPreference = "Stop"
Set-Location -LiteralPath $PSScriptRoot

if ($Clean) {
Remove-Item -Force -ErrorAction SilentlyContinue `
AutoResChanger.exe, resource.res, baseline.exe
Write-Host "Cleaned build artifacts." -ForegroundColor Yellow
}

# Make sure the MinGW toolchain is reachable even if it's not on PATH.
if (-not (Get-Command g++ -ErrorAction SilentlyContinue)) {
foreach ($p in @("C:\msys64\ucrt64\bin", "C:\msys64\mingw64\bin")) {
if (Test-Path (Join-Path $p "g++.exe")) {
$env:PATH = "$p;$env:PATH"
break
}
}
}

if (-not (Get-Command g++ -ErrorAction SilentlyContinue)) {
throw "g++ not found. Install MSYS2 (UCRT64) or add MinGW-w64 to PATH."
}

$cxxflags = if ($Debug) {
@("-O0", "-g")
} else {
@("-O2", "-s")
}
$warn = @("-Wall", "-Wextra", "-Wpedantic", "-Wno-unknown-pragmas", "-Wno-missing-field-initializers")
$libs = @("-luser32", "-lgdi32", "-lshell32", "-lcomdlg32", "-ladvapi32", "-ldwmapi", "-luxtheme", "-lole32", "-lcomctl32")

Write-Host "Compiling resources..." -ForegroundColor Cyan
& windres resource.rc -O coff -o resource.res
if ($LASTEXITCODE -ne 0) { throw "windres failed." }

Write-Host "Compiling AutoResChanger.exe..." -ForegroundColor Cyan
& g++ @cxxflags @warn main.cpp resource.res -o AutoResChanger.exe -mwindows -municode @libs
if ($LASTEXITCODE -ne 0) { throw "g++ failed." }

$size = [math]::Round((Get-Item AutoResChanger.exe).Length / 1KB)
Write-Host "Build succeeded: AutoResChanger.exe ($size KB)" -ForegroundColor Green
Loading
Loading