2017-02-26 19:23:31 +00:00
using System ;
using System.Collections.Generic ;
2020-04-13 03:47:38 +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 ;
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 03:47:38 +00:00
[STAThread]
2017-02-26 19:23:31 +00:00
/// <summary>
/// The main entry point for the application.
/// </summary>
2020-04-13 03:47:38 +00:00
/// <param name="action">(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)</param>
/// <param name="profileId">(required) UUID string that selects the profile to use.</param>
/// <param name="arguments">(optional) Extra arguments to pass to the application/game when we're switching profile and running the application/game. Also can be used when creating a shortcut.</param>
/// <param name="execute">(optional) The application/game to start when we're switching profile and running the application/game. Also can be used when creating a shortcut.</param>
/// <param name="processName">(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.</param>
/// <param name="processTimeout">(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.</param>
/// <param name="steamId">(optional) The Steam AppID wait for when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut.</param>
private static void Main (
HeliosStartupAction action ,
string profileId = null ,
string arguments = null ,
string execute = null ,
string processName = null ,
uint processTimeout = 30 u ,
uint steamId = 0 u )
2017-02-26 19:23:31 +00:00
{
2020-04-13 03:47:38 +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 = processName ;
CommandLineOptions . ExecuteProcessTimeout = processTimeout ;
CommandLineOptions . ExecuteSteamApp = steamId ;
2017-02-26 19:23:31 +00:00
Application . EnableVisualStyles ( ) ;
Application . SetCompatibleTextRenderingDefault ( false ) ;
2018-10-20 00:20:53 +00:00
ServicePointManager . SecurityProtocol = SecurityProtocolType . Tls12 ;
2018-10-20 00:27:25 +00:00
2017-08-10 14:21:45 +00:00
try
{
2017-02-26 19:23:31 +00:00
if ( ! IPCService . StartService ( ) )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception ( Language . Can_not_open_a_named_pipe_for_Inter_process_communication ) ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
var profiles = Profile . GetAllProfiles ( ) . ToArray ( ) ;
2020-04-13 03:47:38 +00:00
var profileIndex = ! string . IsNullOrWhiteSpace ( CommandLineOptions . ProfileId ) & &
2018-10-20 00:27:25 +00:00
profiles . Length > 0
2017-02-26 19:23:31 +00:00
? Array . FindIndex ( profiles ,
p = >
2020-04-13 03:47:38 +00:00
p . Id . Equals ( null ,
2017-02-26 19:23:31 +00:00
StringComparison . InvariantCultureIgnoreCase ) )
: - 1 ;
2020-04-13 03:47:38 +00:00
switch ( CommandLineOptions . Action )
2017-02-26 19:23:31 +00:00
{
case HeliosStartupAction . SwitchProfile :
SwitchProfile ( profiles , profileIndex ) ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
break ;
case HeliosStartupAction . EditProfile :
EditProfile ( profiles , profileIndex ) ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
break ;
case HeliosStartupAction . CreateShortcut :
CreateShortcut ( profiles , profileIndex ) ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
break ;
default :
IPCService . GetInstance ( ) . Status = InstanceStatus . User ;
Application . Run ( new MainForm ( ) ) ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
break ;
}
2020-04-13 03:47:38 +00:00
2017-08-10 14:21:45 +00:00
}
catch ( Exception e )
{
MessageBox . Show (
string . Format ( Language . Operation_Failed , e . Message ) ,
Language . Fatal_Error ,
MessageBoxButtons . OK ,
MessageBoxIcon . Error ) ;
}
2017-02-26 19:23:31 +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
}
}
}
}