DisplayMagician/HeliosDisplayManagement/Program.cs

493 lines
18 KiB
C#
Raw Normal View History

2017-02-26 19:23:31 +00:00
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Invocation;
//using System.CommandLine.DragonFruit;
2017-02-26 19:23:31 +00:00
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
2017-02-26 19:23:31 +00:00
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using HeliosDisplayManagement.InterProcess;
using HeliosDisplayManagement.Resources;
using HeliosDisplayManagement.Shared;
using HeliosDisplayManagement.Steam;
using HeliosDisplayManagement.UIForms;
namespace HeliosDisplayManagement
{
internal static class Program
{
2017-02-26 19:23:31 +00:00
internal static bool GoProfile(Profile profile)
{
if (profile.IsActive)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
return true;
2018-10-20 00:27:25 +00:00
}
2017-08-10 14:21:45 +00:00
var instanceStatus = IPCService.GetInstance().Status;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
try
{
IPCService.GetInstance().Status = InstanceStatus.Busy;
var failed = false;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if (new SplashForm(() =>
{
Task.Factory.StartNew(() =>
{
if (!profile.Apply())
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
failed = true;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
}, TaskCreationOptions.LongRunning);
2018-10-20 00:27:25 +00:00
}, 3, 30).ShowDialog() !=
DialogResult.Cancel)
2017-02-26 19:23:31 +00:00
{
if (failed)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception(Language.Profile_is_invalid_or_not_possible_to_apply);
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
return true;
}
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
return false;
}
finally
{
2017-08-10 14:21:45 +00:00
IPCService.GetInstance().Status = instanceStatus;
2017-02-26 19:23:31 +00:00
}
}
private static void CreateShortcut(IReadOnlyList<Profile> profiles, int profileIndex)
{
if (profileIndex < 0)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception(Language.Selected_profile_is_invalid_or_not_found);
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
IPCService.GetInstance().Status = InstanceStatus.User;
new ShortcutForm(profiles[profileIndex])
{
FileName = CommandLineOptions.ExecuteFilename,
SteamAppId = CommandLineOptions.ExecuteSteamApp,
Arguments = CommandLineOptions.ExecuteArguments,
ProcessName = CommandLineOptions.ExecuteProcessName,
Timeout = CommandLineOptions.ExecuteProcessTimeout
2017-02-26 19:23:31 +00:00
}.ShowDialog();
}
private static void EditProfile(IList<Profile> profiles, int profileIndex)
{
if (profileIndex < 0)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception(Language.Selected_profile_is_invalid_or_not_found);
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
IPCService.GetInstance().Status = InstanceStatus.User;
var editForm = new EditForm(profiles[profileIndex]);
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if (editForm.ShowDialog() == DialogResult.OK)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
profiles[profileIndex] = editForm.Profile;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
if (!Profile.SetAllProfiles(profiles))
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception(Language.Failed_to_save_profile);
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
}
2017-02-26 19:23:31 +00:00
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
private static int Main(string[] args)
2017-02-26 19:23:31 +00:00
{
var cmd = new RootCommand();
cmd.Description = "This is an application and things.";
cmd.Add(new Option<HeliosStartupAction>(
aliases: new string[] {"--action", "-a"},
getDefaultValue :() => HeliosStartupAction.None,
description: "(required) The startup action to perform: None (Do nothing), SwitchProfile (Change to another profile and optionally run an application/game), CreateShortcut (Create a Desktop Shortcut), EditProfile (Edit a profile)"
)
);
cmd.Add(new Option<string>(
aliases: new string[] { "--profile-id", "-p" },
description: "(required) UUID string that selects the profile to use."
)
);
cmd.Add(new Option<string>(
aliases: new string[] { "--arguments" },
description: "(optional) Extra arguments to pass to the application/game when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut."
)
);
cmd.Add(new Option<string>(
aliases: new string[] { "--execute", "-e" },
description: "(optional) The application/game to start when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut."
)
);
cmd.Add(new Option<string>(
aliases: new string[] { "--waitfor", "-w" },
description: "(optional) The process name to wait for when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut."
)
);
cmd.Add(new Option<uint>(
aliases: new string[] { "--timeout", "-t" },
description: "(optional) The time in seconds we should delay starting the application/game when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut."
)
);
cmd.Add(new Option<uint>(
aliases: new string[] { "--steam", "-s" },
description: "(optional)The Steam AppID to run for when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut."
)
);
/*cmd.Add(new Option<uint>(
aliases: new string[] { "--uplay" },
description: "(optional)The Uplay ID to run when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut."
)
);
cmd.Add(new Option<uint>(
aliases: new string[] { "--epic" },
description: "(optional)The Epic ID to run when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut."
)
);
cmd.Add(new Option<uint>(
aliases: new string[] { "--origin" },
description: "(optional)The Origin ID to run when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut."
)
);*/
cmd.Handler = CommandHandler.Create<HeliosStartupAction, string, string, string, string, uint, uint>((action, profileId, arguments, execute, waitfor, timeout, steam) =>
2017-08-10 14:21:45 +00:00
{
// Validate combinations of Command line options
// If a profileId is supplied then we need an action other than None
if (action == HeliosStartupAction.None && !String.IsNullOrEmpty(profileId))
2018-10-20 00:27:25 +00:00
{
Console.WriteLine("Error - If you supply a Profile ID or Name then you must also provide an Action.");
return 1;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
//var help = new System.CommandLine.Help.HelpBuilder(invocationContext.Console);
//Console.WriteLine(help);
2017-02-26 19:23:31 +00:00
// Save these in CommandLineOptions for easy access from other parts of the application
CommandLineOptions.Action = action;
CommandLineOptions.ProfileId = profileId;
CommandLineOptions.ExecuteArguments = arguments;
CommandLineOptions.ExecuteFilename = execute;
CommandLineOptions.ExecuteProcessName = waitfor;
CommandLineOptions.ExecuteProcessTimeout = timeout;
CommandLineOptions.ExecuteSteamApp = steam;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
try
2017-02-26 19:23:31 +00:00
{
if (!IPCService.StartService())
{
throw new Exception(Language.Can_not_open_a_named_pipe_for_Inter_process_communication);
}
2018-10-20 00:27:25 +00:00
// Create an array of profiles
var profiles = Profile.GetAllProfiles().ToArray();
// Show the user the profiles if they want to look
foreach (Profile profile in profiles)
{
Console.WriteLine($"Found Profile: {profile.Name} (ID:{profile.Id})");
}
// Try and lookup the profileId in the profileIndex
var profileIndex = profiles.Length > 0 ? Array.FindIndex(profiles, p => p.Id.Equals(CommandLineOptions.ProfileId, StringComparison.InvariantCultureIgnoreCase)) : -1;
// If the profileID wasn't there, maybe they used the profile name?
if (profileIndex == -1)
{
profileIndex = profiles.Length > 0 ? Array.FindIndex(profiles, p => p.Name.Equals(CommandLineOptions.ProfileId, StringComparison.InvariantCultureIgnoreCase)) : -1;
}
Console.WriteLine($"Using Profile: {profiles[profileIndex].Name} (ID:{profiles[profileIndex].Id})");
2018-10-20 00:27:25 +00:00
switch (CommandLineOptions.Action)
{
case HeliosStartupAction.SwitchProfile:
SwitchProfile(profiles, profileIndex);
break;
case HeliosStartupAction.EditProfile:
EditProfile(profiles, profileIndex);
break;
case HeliosStartupAction.CreateShortcut:
CreateShortcut(profiles, profileIndex);
break;
default:
IPCService.GetInstance().Status = InstanceStatus.User;
Application.Run(new MainForm());
break;
}
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
}
catch (Exception e)
{
MessageBox.Show(
string.Format(Language.Operation_Failed, e.Message),
Language.Fatal_Error,
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
return 0;
});
var result = cmd.InvokeAsync(args).Result;
return result;
2017-02-26 19:23:31 +00:00
}
// ReSharper disable once CyclomaticComplexity
2017-02-26 19:23:31 +00:00
private static void SwitchProfile(IReadOnlyList<Profile> profiles, int profileIndex)
{
var rollbackProfile = Profile.GetCurrent(string.Empty);
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if (profileIndex < 0)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception(Language.Selected_profile_is_invalid_or_not_found);
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
if (!profiles[profileIndex].IsPossible)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception(Language.Selected_profile_is_not_possible);
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
if (
IPCClient.QueryAll()
.Any(
client =>
2018-10-20 00:27:25 +00:00
client.Status == InstanceStatus.Busy ||
client.Status == InstanceStatus.OnHold))
{
2017-02-26 19:23:31 +00:00
throw new Exception(
Language
.Another_instance_of_this_program_is_in_working_state_Please_close_other_instances_before_trying_to_switch_profile);
2018-10-20 00:27:25 +00:00
}
if (!string.IsNullOrWhiteSpace(CommandLineOptions.ExecuteFilename))
2017-02-26 19:23:31 +00:00
{
if (!File.Exists(CommandLineOptions.ExecuteFilename))
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception(Language.Executable_file_not_found);
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
if (!GoProfile(profiles[profileIndex]))
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception(Language.Can_not_change_active_profile);
2018-10-20 00:27:25 +00:00
}
var process = System.Diagnostics.Process.Start(CommandLineOptions.ExecuteFilename,
CommandLineOptions.ExecuteArguments);
var processes = new System.Diagnostics.Process[0];
2018-10-20 00:27:25 +00:00
if (!string.IsNullOrWhiteSpace(CommandLineOptions.ExecuteProcessName))
2017-02-26 19:23:31 +00:00
{
var ticks = 0;
2018-10-20 00:27:25 +00:00
while (ticks < CommandLineOptions.ExecuteProcessTimeout * 1000)
2017-02-26 19:23:31 +00:00
{
processes = System.Diagnostics.Process.GetProcessesByName(CommandLineOptions.ExecuteProcessName);
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if (processes.Length > 0)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
break;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
Thread.Sleep(300);
ticks += 300;
}
}
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if (processes.Length == 0)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
processes = new[] {process};
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
IPCService.GetInstance().HoldProcessId = processes.FirstOrDefault()?.Id ?? 0;
IPCService.GetInstance().Status = InstanceStatus.OnHold;
NotifyIcon notify = null;
2018-10-20 00:27:25 +00:00
try
{
notify = new NotifyIcon
{
Icon = Properties.Resources.Icon,
Text = string.Format(
Language.Waiting_for_the_0_to_terminate,
processes[0].ProcessName),
Visible = true
};
Application.DoEvents();
}
catch
{
// ignored
}
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
foreach (var p in processes)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
try
{
p.WaitForExit();
}
catch
{
// ignored
}
2018-10-20 00:27:25 +00:00
}
if (notify != null)
{
notify.Visible = false;
notify.Dispose();
Application.DoEvents();
}
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
IPCService.GetInstance().Status = InstanceStatus.Busy;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if (!rollbackProfile.IsActive)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
if (!GoProfile(rollbackProfile))
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception(Language.Can_not_change_active_profile);
2018-10-20 00:27:25 +00:00
}
}
2017-02-26 19:23:31 +00:00
}
else if (CommandLineOptions.ExecuteSteamApp > 0)
2017-02-26 19:23:31 +00:00
{
var steamGame = new SteamGame(CommandLineOptions.ExecuteSteamApp);
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if (!SteamGame.SteamInstalled)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception(Language.Steam_is_not_installed);
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
if (!File.Exists(SteamGame.SteamAddress))
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception(Language.Steam_executable_file_not_found);
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
if (!steamGame.IsInstalled)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception(Language.Steam_game_is_not_installed);
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
if (!steamGame.IsOwned)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception(Language.Steam_game_is_not_owned);
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
if (!GoProfile(profiles[profileIndex]))
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception(Language.Can_not_change_active_profile);
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
var address = $"steam://rungameid/{steamGame.AppId}";
2018-10-20 00:27:25 +00:00
if (!string.IsNullOrWhiteSpace(CommandLineOptions.ExecuteArguments))
2018-10-20 00:27:25 +00:00
{
address += "/" + CommandLineOptions.ExecuteArguments;
2018-10-20 00:27:25 +00:00
}
var steamProcess = System.Diagnostics.Process.Start(address);
2017-02-26 19:23:31 +00:00
// Wait for steam game to update and then run
var ticks = 0;
2018-10-20 00:27:25 +00:00
while (ticks < CommandLineOptions.ExecuteProcessTimeout * 1000)
2017-02-26 19:23:31 +00:00
{
if (steamGame.IsRunning)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
break;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
Thread.Sleep(300);
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if (!steamGame.IsUpdating)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
ticks += 300;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
}
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
IPCService.GetInstance().HoldProcessId = steamProcess?.Id ?? 0;
IPCService.GetInstance().Status = InstanceStatus.OnHold;
NotifyIcon notify = null;
2018-10-20 00:27:25 +00:00
try
{
notify = new NotifyIcon
{
Icon = Properties.Resources.Icon,
Text = string.Format(
Language.Waiting_for_the_0_to_terminate,
steamGame.Name),
Visible = true
};
Application.DoEvents();
}
catch
{
// ignored
}
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
// Wait for the game to exit
if (steamGame.IsRunning)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
while (true)
{
if (!steamGame.IsRunning)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
break;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
Thread.Sleep(300);
}
2018-10-20 00:27:25 +00:00
}
if (notify != null)
{
notify.Visible = false;
notify.Dispose();
Application.DoEvents();
}
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
IPCService.GetInstance().Status = InstanceStatus.Busy;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if (!rollbackProfile.IsActive)
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
if (!GoProfile(rollbackProfile))
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception(Language.Can_not_change_active_profile);
2018-10-20 00:27:25 +00:00
}
}
2017-02-26 19:23:31 +00:00
}
else
{
if (!GoProfile(profiles[profileIndex]))
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception(Language.Can_not_change_active_profile);
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
}
}
}
}