2017-02-26 19:23:31 +00:00
using System ;
using System.Collections.Generic ;
2020-04-13 10:10:35 +00:00
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 ;
2018-10-20 00:20:53 +00:00
using System.Net ;
2020-04-13 10:10:35 +00:00
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
{
2020-04-13 03:47:38 +00:00
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 ] )
{
2020-04-13 03:47:38 +00:00
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
}
2020-04-13 10:10:35 +00:00
2017-02-26 19:23:31 +00:00
/// <summary>
/// The main entry point for the application.
/// </summary>
2020-04-13 10:10:35 +00:00
[STAThread]
private static int Main ( string [ ] args )
2017-02-26 19:23:31 +00:00
{
2020-04-13 10:10:35 +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."
)
) ; * /
2020-04-13 03:47:38 +00:00
2020-04-13 10:10:35 +00:00
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
{
2020-04-13 10:10:35 +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
{
2020-04-13 10:10:35 +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
2020-04-13 10:10:35 +00:00
//var help = new System.CommandLine.Help.HelpBuilder(invocationContext.Console);
//Console.WriteLine(help);
2017-02-26 19:23:31 +00:00
2020-04-13 10:10:35 +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
{
2020-04-13 10:10:35 +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
2020-04-13 10:10:35 +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
2020-04-13 10:10:35 +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
}
2020-04-13 10:10:35 +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 ;
2020-04-13 03:47:38 +00:00
2017-02-26 19:23:31 +00:00
}
2020-04-13 10:10:35 +00:00
2017-08-10 14:12:44 +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
}
2020-04-13 03:47:38 +00:00
if ( ! string . IsNullOrWhiteSpace ( CommandLineOptions . ExecuteFilename ) )
2017-02-26 19:23:31 +00:00
{
2020-04-13 03:47:38 +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
}
2020-04-13 03:47:38 +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
2020-04-13 03:47:38 +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
2020-04-13 03:47:38 +00:00
while ( ticks < CommandLineOptions . ExecuteProcessTimeout * 1000 )
2017-02-26 19:23:31 +00:00
{
2020-04-13 03:47:38 +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 ;
2017-08-10 14:12:44 +00:00
NotifyIcon notify = null ;
2018-10-20 00:27:25 +00:00
2017-08-10 14:12:44 +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
}
2017-08-10 14:12:44 +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
}
2020-04-13 03:47:38 +00:00
else if ( CommandLineOptions . ExecuteSteamApp > 0 )
2017-02-26 19:23:31 +00:00
{
2020-04-13 03:47:38 +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
2020-04-13 03:47:38 +00:00
if ( ! string . IsNullOrWhiteSpace ( CommandLineOptions . ExecuteArguments ) )
2018-10-20 00:27:25 +00:00
{
2020-04-13 03:47:38 +00:00
address + = "/" + CommandLineOptions . ExecuteArguments ;
2018-10-20 00:27:25 +00:00
}
2020-04-13 03:47:38 +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
2020-04-13 03:47:38 +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 ;
2017-08-10 14:12:44 +00:00
NotifyIcon notify = null ;
2018-10-20 00:27:25 +00:00
2017-08-10 14:12:44 +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
}
2017-08-10 14:12:44 +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
}
}
}
}