Files

695 lines
21 KiB
C#

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Input;
using FluentTerminal.App.Services;
using FluentTerminal.Models.Enums;
namespace FluentTerminal.SystemTray
{
public static class Utilities
{
public static Key ExtendVirtualKeyToInputKey(ExtendedVirtualKey key)
{
switch (key)
{
case ExtendedVirtualKey.Cancel:
return Key.Cancel;
case ExtendedVirtualKey.Back:
return Key.Back;
case ExtendedVirtualKey.Tab:
return Key.Tab;
case ExtendedVirtualKey.Clear:
return Key.Clear;
case ExtendedVirtualKey.Enter:
return Key.Enter;
case ExtendedVirtualKey.Shift:
return Key.LeftShift;
case ExtendedVirtualKey.Menu:
return Key.LeftAlt;
case ExtendedVirtualKey.Pause:
return Key.Pause;
case ExtendedVirtualKey.CapitalLock:
return Key.CapsLock;
case ExtendedVirtualKey.Kana:
return Key.KanaMode;
case ExtendedVirtualKey.Junja:
return Key.JunjaMode;
case ExtendedVirtualKey.Final:
return Key.FinalMode;
case ExtendedVirtualKey.Kanji:
return Key.KanjiMode;
case ExtendedVirtualKey.Escape:
return Key.Escape;
case ExtendedVirtualKey.Convert:
return Key.ImeConvert;
case ExtendedVirtualKey.NonConvert:
return Key.ImeNonConvert;
case ExtendedVirtualKey.Accept:
return Key.ImeAccept;
case ExtendedVirtualKey.ModeChange:
return Key.ImeModeChange;
case ExtendedVirtualKey.Space:
return Key.Space;
case ExtendedVirtualKey.PageUp:
return Key.PageUp;
case ExtendedVirtualKey.PageDown:
return Key.PageDown;
case ExtendedVirtualKey.End:
return Key.End;
case ExtendedVirtualKey.Home:
return Key.Home;
case ExtendedVirtualKey.Left:
return Key.Left;
case ExtendedVirtualKey.Up:
return Key.Up;
case ExtendedVirtualKey.Right:
return Key.Right;
case ExtendedVirtualKey.Down:
return Key.Down;
case ExtendedVirtualKey.Select:
return Key.Select;
case ExtendedVirtualKey.Print:
return Key.Print;
case ExtendedVirtualKey.Execute:
return Key.Execute;
case ExtendedVirtualKey.Snapshot:
return Key.Snapshot;
case ExtendedVirtualKey.Insert:
return Key.Insert;
case ExtendedVirtualKey.Delete:
return Key.Delete;
case ExtendedVirtualKey.Help:
return Key.Help;
case ExtendedVirtualKey.Number0:
return Key.D0;
case ExtendedVirtualKey.Number1:
return Key.D1;
case ExtendedVirtualKey.Number2:
return Key.D2;
case ExtendedVirtualKey.Number3:
return Key.D3;
case ExtendedVirtualKey.Number4:
return Key.D4;
case ExtendedVirtualKey.Number5:
return Key.D5;
case ExtendedVirtualKey.Number6:
return Key.D6;
case ExtendedVirtualKey.Number7:
return Key.D7;
case ExtendedVirtualKey.Number8:
return Key.D8;
case ExtendedVirtualKey.Number9:
return Key.D9;
case ExtendedVirtualKey.A:
return Key.A;
case ExtendedVirtualKey.B:
return Key.B;
case ExtendedVirtualKey.C:
return Key.C;
case ExtendedVirtualKey.D:
return Key.D;
case ExtendedVirtualKey.E:
return Key.E;
case ExtendedVirtualKey.F:
return Key.F;
case ExtendedVirtualKey.G:
return Key.G;
case ExtendedVirtualKey.H:
return Key.H;
case ExtendedVirtualKey.I:
return Key.I;
case ExtendedVirtualKey.J:
return Key.J;
case ExtendedVirtualKey.K:
return Key.K;
case ExtendedVirtualKey.L:
return Key.L;
case ExtendedVirtualKey.M:
return Key.M;
case ExtendedVirtualKey.N:
return Key.N;
case ExtendedVirtualKey.O:
return Key.O;
case ExtendedVirtualKey.P:
return Key.P;
case ExtendedVirtualKey.Q:
return Key.Q;
case ExtendedVirtualKey.R:
return Key.R;
case ExtendedVirtualKey.S:
return Key.S;
case ExtendedVirtualKey.T:
return Key.T;
case ExtendedVirtualKey.U:
return Key.U;
case ExtendedVirtualKey.V:
return Key.V;
case ExtendedVirtualKey.W:
return Key.W;
case ExtendedVirtualKey.X:
return Key.X;
case ExtendedVirtualKey.Y:
return Key.Y;
case ExtendedVirtualKey.Z:
return Key.Z;
case ExtendedVirtualKey.LeftWindows:
return Key.LWin;
case ExtendedVirtualKey.RightWindows:
return Key.RWin;
case ExtendedVirtualKey.Application:
return Key.Apps;
case ExtendedVirtualKey.Sleep:
return Key.Sleep;
case ExtendedVirtualKey.NumberPad0:
return Key.NumPad0;
case ExtendedVirtualKey.NumberPad1:
return Key.NumPad1;
case ExtendedVirtualKey.NumberPad2:
return Key.NumPad2;
case ExtendedVirtualKey.NumberPad3:
return Key.NumPad3;
case ExtendedVirtualKey.NumberPad4:
return Key.NumPad4;
case ExtendedVirtualKey.NumberPad5:
return Key.NumPad5;
case ExtendedVirtualKey.NumberPad6:
return Key.NumPad6;
case ExtendedVirtualKey.NumberPad7:
return Key.NumPad7;
case ExtendedVirtualKey.NumberPad8:
return Key.NumPad8;
case ExtendedVirtualKey.NumberPad9:
return Key.NumPad9;
case ExtendedVirtualKey.Multiply:
return Key.Multiply;
case ExtendedVirtualKey.Add:
return Key.Add;
case ExtendedVirtualKey.Separator:
return Key.Separator;
case ExtendedVirtualKey.Subtract:
return Key.Subtract;
case ExtendedVirtualKey.Decimal:
return Key.Decimal;
case ExtendedVirtualKey.Divide:
return Key.Divide;
case ExtendedVirtualKey.F1:
return Key.F1;
case ExtendedVirtualKey.F2:
return Key.F2;
case ExtendedVirtualKey.F3:
return Key.F3;
case ExtendedVirtualKey.F4:
return Key.F4;
case ExtendedVirtualKey.F5:
return Key.F5;
case ExtendedVirtualKey.F6:
return Key.F6;
case ExtendedVirtualKey.F7:
return Key.F7;
case ExtendedVirtualKey.F8:
return Key.F8;
case ExtendedVirtualKey.F9:
return Key.F9;
case ExtendedVirtualKey.F10:
return Key.F10;
case ExtendedVirtualKey.F11:
return Key.F11;
case ExtendedVirtualKey.F12:
return Key.F12;
case ExtendedVirtualKey.F13:
return Key.F13;
case ExtendedVirtualKey.F14:
return Key.F14;
case ExtendedVirtualKey.F15:
return Key.F15;
case ExtendedVirtualKey.F16:
return Key.F16;
case ExtendedVirtualKey.F17:
return Key.F17;
case ExtendedVirtualKey.F18:
return Key.F18;
case ExtendedVirtualKey.F19:
return Key.F19;
case ExtendedVirtualKey.F20:
return Key.F20;
case ExtendedVirtualKey.F21:
return Key.F21;
case ExtendedVirtualKey.F22:
return Key.F22;
case ExtendedVirtualKey.F23:
return Key.F23;
case ExtendedVirtualKey.F24:
return Key.F24;
case ExtendedVirtualKey.NumberKeyLock:
return Key.NumLock;
case ExtendedVirtualKey.Scroll:
return Key.Scroll;
case ExtendedVirtualKey.LeftShift:
return Key.LeftShift;
case ExtendedVirtualKey.RightShift:
return Key.RightShift;
case ExtendedVirtualKey.LeftControl:
return Key.LeftCtrl;
case ExtendedVirtualKey.RightControl:
return Key.RightCtrl;
case ExtendedVirtualKey.LeftMenu:
return Key.LeftAlt;
case ExtendedVirtualKey.RightMenu:
return Key.RightAlt;
case ExtendedVirtualKey.GoBack:
return Key.BrowserBack;
case ExtendedVirtualKey.GoForward:
return Key.BrowserForward;
case ExtendedVirtualKey.Refresh:
return Key.BrowserRefresh;
case ExtendedVirtualKey.Stop:
return Key.BrowserStop;
case ExtendedVirtualKey.Search:
return Key.BrowserSearch;
case ExtendedVirtualKey.Favorites:
return Key.BrowserFavorites;
case ExtendedVirtualKey.GoHome:
return Key.BrowserHome;
case ExtendedVirtualKey.VolumeMute:
return Key.VolumeMute;
case ExtendedVirtualKey.VolumeDown:
return Key.VolumeDown;
case ExtendedVirtualKey.VolumeUp:
return Key.VolumeUp;
case ExtendedVirtualKey.MediaNext:
return Key.MediaNextTrack;
case ExtendedVirtualKey.MediaPrevious:
return Key.MediaPreviousTrack;
case ExtendedVirtualKey.MediaStop:
return Key.MediaStop;
case ExtendedVirtualKey.MediaPlayPause:
return Key.MediaPlayPause;
case ExtendedVirtualKey.Plus:
return Key.OemPlus;
case ExtendedVirtualKey.Comma:
return Key.OemComma;
case ExtendedVirtualKey.Minus:
return Key.OemMinus;
case ExtendedVirtualKey.Period:
return Key.OemPeriod;
case ExtendedVirtualKey.OEM_1:
return Key.Oem1;
case ExtendedVirtualKey.OEM_2:
return Key.Oem1;
case ExtendedVirtualKey.OEM_3:
return Key.Oem3;
case ExtendedVirtualKey.OEM_5:
return Key.Oem5;
case ExtendedVirtualKey.OEM_6:
return Key.Oem6;
case ExtendedVirtualKey.OEM_7:
return Key.Oem7;
case ExtendedVirtualKey.OEM_102:
return Key.Oem102;
default:
return Key.None;
}
}
#region Mosh/SSH locator
public static string ResolveLocation(string location)
{
if (string.IsNullOrWhiteSpace(location))
{
return location;
}
location = location.Trim();
if (location.Equals(Constants.MoshCommandName, StringComparison.OrdinalIgnoreCase) ||
location.Equals($"{Constants.MoshCommandName}.exe", StringComparison.OrdinalIgnoreCase))
{
return GetMoshPath();
}
else if (location.Equals(Constants.SshCommandName, StringComparison.OrdinalIgnoreCase) ||
location.Equals($"{Constants.SshCommandName}.exe", StringComparison.OrdinalIgnoreCase))
{
return GetSshPath();
}
return location;
}
private static string GetSshPath()
{
//
// See https://stackoverflow.com/a/25919981
//
string path;
if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess)
{
path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), @"Sysnative");
}
else
{
path = Environment.GetFolderPath(Environment.SpecialFolder.System);
}
path = Path.Combine(path, @"OpenSSH\ssh.exe");
return System.IO.File.Exists(path) ? path : null;
}
private static string GetMoshPath()
{
var directory = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory;
while (directory != null)
{
string path = Path.Combine(directory.FullName, "mosh.exe");
if (File.Exists(path))
{
return path;
}
directory = directory.Parent;
}
return null;
}
#endregion Mosh/SSH locator
internal static void SaveFile(string path, string content)
{
using (StreamWriter writer = new StreamWriter(path, false))
{
writer.Write(content);
}
}
internal static string ReadFile(string path)
{
return File.ReadAllText(path);
}
private static bool? MuteProcess(int id, bool mute, bool force = false)
{
bool? isAudioSessionMuted = VolumeControl.GetAudioSessionMute(id);
if (isAudioSessionMuted != null &&
(force || isAudioSessionMuted != mute))
{
VolumeControl.SetAudioSessionMute(id, mute);
}
return isAudioSessionMuted;
}
private static bool MuteConhost(bool mute)
{
bool audioSessionFound = false;
foreach (Process conhost in Process.GetProcessesByName("conhost"))
{
bool? isAudioSessionMuted = MuteProcess(conhost.Id, mute);
if (isAudioSessionMuted != null)
{
audioSessionFound = true;
}
}
return audioSessionFound;
}
private static void Calculate(Func<bool> eval, TimeSpan timeout)
{
var watch = Stopwatch.StartNew();
do
{
if (eval()) return;
} while (watch.Elapsed < timeout);
}
private static void SpawnConhostProcess(bool mute)
{
Logger.Instance.Debug($"SpawnConhostProcess starts mute={mute}");
Process cmdProcess = new Process();
cmdProcess.StartInfo.FileName = "cmd.exe";
cmdProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
cmdProcess.StartInfo.Arguments = "/k timeout 1 && \x07";
if (cmdProcess.Start() == false)
{
Logger.Instance.Debug($"Can't start {cmdProcess.StartInfo.FileName} {cmdProcess.StartInfo.Arguments}");
return;
}
const int sTimeout = 3;
int conhostProcessId = 0;
Calculate(() =>
{
if (!cmdProcess.HasExited)
{
foreach (Process conhost in Process.GetProcessesByName("conhost"))
{
try
{
Process parent = ProcessUtils.ResolveParent(conhost.Id);
if (parent != null && cmdProcess.Id == parent.Id)
{
conhostProcessId = conhost.Id;
return true;
}
}
catch (Win32Exception e)
{
Logger.Instance.Debug($"Exception with message \"{e.Message}\" on " +
$"getting process parent id for conhost process {conhost.Id}");
}
}
}
return false;
}, new TimeSpan(0, 0, sTimeout));
if (conhostProcessId == 0)
{
Logger.Instance.Debug($"Can't find child conhost process");
return;
}
Logger.Instance.Debug($"Spawned conhost process id={conhostProcessId}. Starting muting of generated bell sound.");
Calculate(() =>
{
return MuteProcess(conhostProcessId, true, true) != null;
}, new TimeSpan(0, 0, sTimeout));
cmdProcess.WaitForExit(2000);
Logger.Instance.Debug($"Set conhost process mute state to {mute}.");
MuteProcess(conhostProcessId, mute);
cmdProcess.Kill();
Logger.Instance.Debug($"SpawnConhostProcess finishes mute={mute}");
}
internal static void MuteTerminal(bool mute)
{
bool audioSessionFound = MuteConhost(mute);
if (audioSessionFound == true)
{
return;
}
SpawnConhostProcess(mute);
MuteConhost(mute);
}
[StructLayout(LayoutKind.Sequential)]
public struct ProcessUtils
{
// PROCESS_BASIC_INFORMATION fields start
internal IntPtr Reserved1;
internal IntPtr PebBaseAddress;
internal IntPtr Reserved2_0;
internal IntPtr Reserved2_1;
internal IntPtr UniqueProcessId;
internal IntPtr InheritedFromUniqueProcessId;
// PROCESS_BASIC_INFORMATION fields end
[DllImport("ntdll.dll")]
private static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, ref ProcessUtils processInformation, int processInformationLength, out int returnLength);
public static Process ResolveParent(int processId)
{
Process process = Process.GetProcessById(processId);
ProcessUtils pbi = new ProcessUtils();
int result = NtQueryInformationProcess(process.Handle, 0, ref pbi, Marshal.SizeOf(pbi), out var returnLength);
if (result != 0)
{
throw new Win32Exception(result);
}
try
{
return Process.GetProcessById(pbi.InheritedFromUniqueProcessId.ToInt32());
}
catch (ArgumentException)
{
return null;
}
}
}
}
}