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 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; } } } } }