Created
May 16, 2025 17:55
-
-
Save macintoxic/6cd8c0fcfc71f661e3941a3a5fbd2c0c to your computer and use it in GitHub Desktop.
mopheus.override
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Runtime.InteropServices; | |
namespace MorpheusOverride; | |
class MorpheusApp | |
{ | |
private NotifyIcon trayIcon; | |
private System.Timers.Timer keepAwakeTimer; | |
private AwakeMode currentMode = AwakeMode.Disabled; | |
private DateTime expirationTime = DateTime.MaxValue; | |
private bool keepDisplayOn = false; | |
private int timerHours = 2; | |
private int timerMinutes = 0; | |
private CancellationTokenSource tokenSource; | |
// Custom tray times in seconds | |
private readonly (string label, int seconds)[] customTrayTimes = new[] | |
{ | |
("30 minutes", 1800), | |
("1 hour", 3600), | |
("2 hours", 7200), | |
("4 hours", 14400), | |
("8 hours", 28800) | |
}; | |
// Initialize application | |
public void Initialize(string[] args) | |
{ | |
// Create tray icon and context menu | |
trayIcon = new NotifyIcon | |
{ | |
Icon = SystemIcons.Information, | |
Visible = true, | |
Text = "Morpheus Override" | |
}; | |
// Parse command-line arguments | |
ParseCommandLineArgs(args); | |
// Create context menu | |
UpdateContextMenu(); | |
// Initialize keep-awake timer | |
keepAwakeTimer = new System.Timers.Timer(1000); | |
keepAwakeTimer.Elapsed += (s, e) => UpdateKeepAwake(); | |
keepAwakeTimer.Start(); | |
// Start in the appropriate mode if specified by arguments | |
if (currentMode != AwakeMode.Disabled) | |
{ | |
StartKeepAwake(); | |
} | |
} | |
// Parse command-line arguments | |
private void ParseCommandLineArgs(string[] args) | |
{ | |
if (args.Length == 0) | |
{ | |
// Default: indefinite mode if no args provided | |
currentMode = AwakeMode.Indefinite; | |
return; | |
} | |
for (var i = 0; i < args.Length; i++) | |
{ | |
var arg = args[i].ToLower(); | |
if (arg == "--help" || arg == "-h") | |
{ | |
ShowHelp(); | |
Environment.Exit(0); | |
} | |
else if (arg == "--display-on" || arg == "-d") | |
{ | |
keepDisplayOn = true; | |
} | |
else if (arg == "--display-off" || arg == "-o") | |
{ | |
keepDisplayOn = false; | |
} | |
else if ((arg == "--time-limit" || arg == "-t") && i + 1 < args.Length) | |
{ | |
if (TryParseTimeLimit(args[++i], out int hours, out int minutes)) | |
{ | |
currentMode = AwakeMode.Timer; | |
timerHours = hours; | |
timerMinutes = minutes; | |
} | |
} | |
else if ((arg == "--expire-at" || arg == "-e") && i + 1 < args.Length) | |
{ | |
if (TryParseExpireAt(args[++i], out DateTime expireTime)) | |
{ | |
currentMode = AwakeMode.Expirable; | |
expirationTime = expireTime; | |
} | |
} | |
else if (arg == "--disable" || arg == "-x") | |
{ | |
currentMode = AwakeMode.Disabled; | |
} | |
} | |
} | |
// Parse time limit argument (h:mm format) | |
private bool TryParseTimeLimit(string timeLimit, out int hours, out int minutes) | |
{ | |
hours = 0; | |
minutes = 0; | |
// Try parse h:mm format | |
var parts = timeLimit.Split(':'); | |
if (parts.Length == 2 && int.TryParse(parts[0], out hours) && int.TryParse(parts[1], out minutes)) | |
{ | |
return true; | |
} | |
// Try parse as total minutes | |
if (int.TryParse(timeLimit, out int totalMinutes)) | |
{ | |
hours = totalMinutes / 60; | |
minutes = totalMinutes % 60; | |
return true; | |
} | |
Console.WriteLine($"Invalid time format: {timeLimit}. Use h:mm or total minutes."); | |
return false; | |
} | |
// Parse expiration time argument | |
private bool TryParseExpireAt(string expireAt, out DateTime expireTime) | |
{ | |
// Try parse as time (HH:mm:ss) | |
if (TimeSpan.TryParse(expireAt, out TimeSpan timeSpan)) | |
{ | |
DateTime now = DateTime.Now; | |
expireTime = new DateTime(now.Year, now.Month, now.Day, | |
timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds); | |
// If the time is already past for today, set it for tomorrow | |
if (expireTime < now) | |
{ | |
expireTime = expireTime.AddDays(1); | |
} | |
return true; | |
} | |
// Try parse as full datetime | |
if (DateTime.TryParse(expireAt, out expireTime)) | |
{ | |
return true; | |
} | |
Console.WriteLine($"Invalid expiration time format: {expireAt}"); | |
return false; | |
} | |
// Show help text | |
private void ShowHelp() | |
{ | |
Console.WriteLine("Morpheus Override - Keep your PC awake"); | |
Console.WriteLine("Usage: MorpheusOverride.exe [options]"); | |
Console.WriteLine(); | |
Console.WriteLine("Options:"); | |
Console.WriteLine(" -h, --help Show this help message"); | |
Console.WriteLine(" -d, --display-on Keep display on"); | |
Console.WriteLine(" -o, --display-off Allow display to turn off (default)"); | |
Console.WriteLine(" -t, --time-limit <time> Keep awake for specified time (h:mm or total minutes)"); | |
Console.WriteLine(" -e, --expire-at <time> Keep awake until specified time (HH:mm:ss or full datetime)"); | |
Console.WriteLine(" -x, --disable Disable keep-awake functionality"); | |
Console.WriteLine(); | |
Console.WriteLine("If no arguments are provided, the computer will be kept awake indefinitely."); | |
} | |
// Update the tray icon context menu | |
private void UpdateContextMenu() | |
{ | |
var menu = new ContextMenuStrip(); | |
// Add mode items | |
var keepScreenOnItem = new ToolStripMenuItem("Keep screen on"); | |
keepScreenOnItem.Click += (s, e) => { | |
keepDisplayOn = !keepDisplayOn; | |
UpdateContextMenu(); | |
UpdateKeepAwake(); | |
}; | |
keepScreenOnItem.Checked = keepDisplayOn; | |
menu.Items.Add(keepScreenOnItem); | |
menu.Items.Add(new ToolStripSeparator()); | |
var modeMenu = new ToolStripMenuItem("Mode"); | |
var disabledItem = new ToolStripMenuItem("Disabled"); | |
disabledItem.Click += (s, e) => SwitchMode(AwakeMode.Disabled); | |
disabledItem.Checked = currentMode == AwakeMode.Disabled; | |
modeMenu.DropDownItems.Add(disabledItem); | |
var indefiniteItem = new ToolStripMenuItem("Indefinite"); | |
indefiniteItem.Click += (s, e) => SwitchMode(AwakeMode.Indefinite); | |
indefiniteItem.Checked = currentMode == AwakeMode.Indefinite; | |
modeMenu.DropDownItems.Add(indefiniteItem); | |
// Add timed mode option | |
var timedMenuItem = new ToolStripMenuItem("Timed"); | |
timedMenuItem.Click += (s, e) => SwitchMode(AwakeMode.Timer); | |
timedMenuItem.Checked = currentMode == AwakeMode.Timer; | |
modeMenu.DropDownItems.Add(timedMenuItem); | |
menu.Items.Add(modeMenu); | |
// Add custom time selections | |
var customTimeMenu = new ToolStripMenuItem("Keep awake temporarily"); | |
foreach (var (label, seconds) in customTrayTimes) | |
{ | |
var tempItem = new ToolStripMenuItem(label); | |
tempItem.Click += (s, e) => { | |
SetTempAwakeTime(seconds); | |
}; | |
customTimeMenu.DropDownItems.Add(tempItem); | |
} | |
menu.Items.Add(customTimeMenu); | |
menu.Items.Add(new ToolStripSeparator()); | |
var exitItem = new ToolStripMenuItem("Exit"); | |
exitItem.Click += (s, e) => ExitApplication(); | |
menu.Items.Add(exitItem); | |
// Update tray icon | |
trayIcon.ContextMenuStrip = menu; | |
UpdateTrayIcon(); | |
} | |
// Switch between different awake modes | |
private void SwitchMode(AwakeMode newMode) | |
{ | |
currentMode = newMode; | |
switch (newMode) | |
{ | |
case AwakeMode.Disabled: | |
StopKeepAwake(); | |
break; | |
case AwakeMode.Indefinite: | |
case AwakeMode.Timer: | |
case AwakeMode.Expirable: | |
StartKeepAwake(); | |
break; | |
} | |
UpdateContextMenu(); | |
} | |
// Set temporary awake time | |
private void SetTempAwakeTime(int seconds) | |
{ | |
currentMode = AwakeMode.Timer; | |
timerHours = seconds / 3600; | |
timerMinutes = (seconds % 3600) / 60; | |
expirationTime = DateTime.Now.AddSeconds(seconds); | |
StartKeepAwake(); | |
UpdateContextMenu(); | |
} | |
// Start keep-awake functionality | |
private void StartKeepAwake() | |
{ | |
// Create a new cancellation token to stop any previous keep-awake task | |
tokenSource?.Cancel(); | |
tokenSource = new CancellationTokenSource(); | |
// Start a background task to keep the system awake | |
Task.Run(() => KeepSystemAwake(tokenSource.Token), tokenSource.Token); | |
} | |
// Stop keep-awake functionality | |
private void StopKeepAwake() | |
{ | |
// Cancel any running keep-awake task | |
tokenSource?.Cancel(); | |
// Reset execution state to allow normal power management | |
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS); | |
} | |
// Keep the system awake | |
private async Task KeepSystemAwake(CancellationToken cancellationToken) | |
{ | |
try | |
{ | |
while (!cancellationToken.IsCancellationRequested) | |
{ | |
// Check if we've reached the expiration time for timer/expirable modes | |
if ((currentMode == AwakeMode.Timer || currentMode == AwakeMode.Expirable) && | |
DateTime.Now >= expirationTime) | |
{ | |
// Timer expired, switch to disabled mode | |
currentMode = AwakeMode.Disabled; | |
StopKeepAwake(); | |
// Show notification (runs on UI thread since NotifyIcon handles this internally) | |
trayIcon.ShowBalloonTip(3000, "Morpheus Override", | |
"Keep-awake timer has expired.", ToolTipIcon.Info); | |
// Update context menu on next access | |
UpdateContextMenu(); | |
break; | |
} | |
// Set thread execution state based on current settings | |
UpdateKeepAwake(); | |
// Wait before checking again | |
await Task.Delay(1000, cancellationToken); | |
} | |
} | |
catch (OperationCanceledException) | |
{ | |
// Task was cancelled, which is fine | |
} | |
} | |
// Update the thread execution state based on current settings | |
private void UpdateKeepAwake() | |
{ | |
if (currentMode == AwakeMode.Disabled) | |
{ | |
// Allow system to enter sleep mode normally | |
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS); | |
return; | |
} | |
// Set flags based on current mode | |
EXECUTION_STATE flags = EXECUTION_STATE.ES_CONTINUOUS | EXECUTION_STATE.ES_SYSTEM_REQUIRED; | |
if (keepDisplayOn) | |
{ | |
flags |= EXECUTION_STATE.ES_DISPLAY_REQUIRED; | |
} | |
// Set thread execution state | |
SetThreadExecutionState(flags); | |
} | |
// Update tray icon based on current state | |
private void UpdateTrayIcon() | |
{ | |
var tooltip = "Morpheus Override - "; | |
switch (currentMode) | |
{ | |
case AwakeMode.Disabled: | |
trayIcon.Icon = SystemIcons.Information; | |
tooltip += "Disabled"; | |
break; | |
case AwakeMode.Indefinite: | |
trayIcon.Icon = keepDisplayOn ? SystemIcons.Warning : SystemIcons.Shield; | |
tooltip += "Keeping PC awake indefinitely"; | |
break; | |
case AwakeMode.Timer: | |
trayIcon.Icon = keepDisplayOn ? SystemIcons.Warning : SystemIcons.Shield; | |
TimeSpan remaining = expirationTime - DateTime.Now; | |
tooltip += $"Keeping PC awake for {remaining.Hours}h {remaining.Minutes}m"; | |
break; | |
case AwakeMode.Expirable: | |
trayIcon.Icon = keepDisplayOn ? SystemIcons.Warning : SystemIcons.Shield; | |
tooltip += $"Keeping PC awake until {expirationTime:HH:mm:ss}"; | |
break; | |
} | |
if (keepDisplayOn) | |
{ | |
tooltip += " (display on)"; | |
} | |
trayIcon.Text = tooltip; | |
} | |
// Exit the application | |
private void ExitApplication() | |
{ | |
// Make sure to stop keep-awake before exiting | |
StopKeepAwake(); | |
// Clean up tray icon | |
trayIcon.Visible = false; | |
trayIcon.Dispose(); | |
// Exit application | |
Application.Exit(); | |
} | |
#region Native Windows API | |
// Execution state flags | |
[Flags] | |
public enum EXECUTION_STATE : uint | |
{ | |
ES_AWAYMODE_REQUIRED = 0x00000040, | |
ES_CONTINUOUS = 0x80000000, | |
ES_DISPLAY_REQUIRED = 0x00000002, | |
ES_SYSTEM_REQUIRED = 0x00000001 | |
} | |
// Import SetThreadExecutionState API from kernel32.dll | |
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] | |
private static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags); | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment