Files
Bedtimer/Config.cs
2025-10-26 10:40:21 -05:00

122 lines
4.7 KiB
C#

using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Threading;
namespace Bedtimer
{
public sealed class Config
{
public int BedMinutes { get; set; } = 22 * 60 + 30; // 10:30 PM
public int WarnLead { get; set; } = 60;
public int FlashLead { get; set; } = 20;
public int LockDelay { get; set; } = 5;
private const string RegPath = @"SOFTWARE\Whitlocktech\Bedtimer";
// --- Public API -------------------------------------------------------
public static Config LoadOrCreate()
{
// Read only; do NOT write defaults here.
using var k = OpenHKLM().OpenSubKey(RegPath, writable: false);
if (k == null) return new Config(); // defaults in memory only
int Get(string n, int d) => (k.GetValue(n) is int v) ? v : d;
return new Config
{
BedMinutes = Get(nameof(BedMinutes), 22 * 60 + 30),
WarnLead = Get(nameof(WarnLead), 60),
FlashLead = Get(nameof(FlashLead), 20),
LockDelay = Get(nameof(LockDelay), 5),
};
}
public void Save()
{
try
{
using var root = OpenHKLM();
using var k = root.CreateSubKey(RegPath, writable: true);
k.SetValue(nameof(BedMinutes), BedMinutes, RegistryValueKind.DWord);
k.SetValue(nameof(WarnLead), WarnLead, RegistryValueKind.DWord);
k.SetValue(nameof(FlashLead), FlashLead, RegistryValueKind.DWord);
k.SetValue(nameof(LockDelay), LockDelay, RegistryValueKind.DWord);
Signals.RaiseConfigChanged();
}
catch (UnauthorizedAccessException)
{
// Relaunch self elevated to write HKLM in the SAME registry view
var exe = Environment.ProcessPath!;
var args = $"--save-admin {BedMinutes} {WarnLead} {FlashLead} {LockDelay}";
Process.Start(new ProcessStartInfo(exe, args)
{
UseShellExecute = true,
Verb = "runas"
});
}
}
public static void SaveFromArgs(string[] args)
{
// Accept: Bedtimer.exe --save-admin <bed> <warn> <flash> <delay>
// Find the flag and read the 4 numbers after it.
int i = Array.FindIndex(args, a => string.Equals(a, "--save-admin", StringComparison.OrdinalIgnoreCase));
if (i < 0 || args.Length < i + 5) return;
var cfg = new Config
{
BedMinutes = int.Parse(args[i + 1]),
WarnLead = int.Parse(args[i + 2]),
FlashLead = int.Parse(args[i + 3]),
LockDelay = int.Parse(args[i + 4]),
};
using var root = OpenHKLM();
using var k = root.CreateSubKey(RegPath, writable: true);
k.SetValue(nameof(BedMinutes), cfg.BedMinutes, RegistryValueKind.DWord);
k.SetValue(nameof(WarnLead), cfg.WarnLead, RegistryValueKind.DWord);
k.SetValue(nameof(FlashLead), cfg.FlashLead, RegistryValueKind.DWord);
k.SetValue(nameof(LockDelay), cfg.LockDelay, RegistryValueKind.DWord);
Signals.RaiseConfigChanged();
}
// Keep %AppData% for state/logs
public static string Dir => System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"Whitlocktech", "Bedtimer");
// --- Helpers ----------------------------------------------------------
private static RegistryKey OpenHKLM()
{
// Always open the correct view explicitly to avoid WOW6432 redirection issues
var view = Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32;
return RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view);
}
public static bool IsProcessElevated()
{
using var id = WindowsIdentity.GetCurrent();
var p = new WindowsPrincipal(id);
return p.IsInRole(WindowsBuiltInRole.Administrator);
}
}
public static class Signals
{
public const string ConfigChangedName = @"Local\Bedtimer_Config_Changed";
public static void RaiseConfigChanged()
{
using var ev = new EventWaitHandle(false, EventResetMode.AutoReset, ConfigChangedName);
ev.Set();
}
public static EventWaitHandle SubscribeConfigChanged()
=> new EventWaitHandle(false, EventResetMode.AutoReset, ConfigChangedName);
}
}