diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8f310e0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,352 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +*.idea/ + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3b428f1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Josh Schwartzberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..343a782 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +WinTermPlus +Windows Terminal PLUS (Unofficial) \ No newline at end of file diff --git a/WinTermPlus.sln b/WinTermPlus.sln new file mode 100644 index 0000000..f0d91d5 --- /dev/null +++ b/WinTermPlus.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29418.71 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinTermPlus", "WinTermPlus\WinTermPlus.csproj", "{48E4707C-1E4C-4D61-9909-70360890ADA1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {48E4707C-1E4C-4D61-9909-70360890ADA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {48E4707C-1E4C-4D61-9909-70360890ADA1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48E4707C-1E4C-4D61-9909-70360890ADA1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48E4707C-1E4C-4D61-9909-70360890ADA1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0DB81991-258B-40D5-B790-7895C5BFE1A1} + EndGlobalSection +EndGlobal diff --git a/WinTermPlus/App.config b/WinTermPlus/App.config new file mode 100644 index 0000000..01d15b1 --- /dev/null +++ b/WinTermPlus/App.config @@ -0,0 +1,27 @@ + + + + +
+ + + + + + + + + False + + + True + + + 50 + + + 80 + + + + \ No newline at end of file diff --git a/WinTermPlus/App.ico b/WinTermPlus/App.ico new file mode 100644 index 0000000..32e0d26 Binary files /dev/null and b/WinTermPlus/App.ico differ diff --git a/WinTermPlus/App.xaml b/WinTermPlus/App.xaml new file mode 100644 index 0000000..e2c0b49 --- /dev/null +++ b/WinTermPlus/App.xaml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WinTermPlus/App.xaml.cs b/WinTermPlus/App.xaml.cs new file mode 100644 index 0000000..cba19d8 --- /dev/null +++ b/WinTermPlus/App.xaml.cs @@ -0,0 +1,25 @@ +using System.Windows; +using Hardcodet.Wpf.TaskbarNotification; + +namespace WinTermPlus +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App + { + private TaskbarIcon _notifyIcon; + + protected override void OnStartup(StartupEventArgs e) + { + base.OnStartup(e); + _notifyIcon = (TaskbarIcon) FindResource("NotifyIcon"); + } + + protected override void OnExit(ExitEventArgs e) + { + _notifyIcon.Dispose(); //the icon would clean up automatically, but this is cleaner + base.OnExit(e); + } + } +} \ No newline at end of file diff --git a/WinTermPlus/Bootstrap.cs b/WinTermPlus/Bootstrap.cs new file mode 100644 index 0000000..2c19c1c --- /dev/null +++ b/WinTermPlus/Bootstrap.cs @@ -0,0 +1,87 @@ +using Caliburn.Micro; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Windows; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Threading; +using WinTermPlus.Infrastructure; +using WinTermPlus.UI.Converters; +using WinTermPlus.UI.ViewModels; + +namespace WinTermPlus +{ + public class Bootstrap : BootstrapperBase + { + private SimpleContainer _container; + + public Bootstrap() + { + Initialize(); + } + protected override void Configure() + { + _container = new SimpleContainer(); + + _container.Instance(_container); + + _container + .Singleton() + .Singleton() + .Singleton(); + + _container + .PerRequest() + .PerRequest(); + + ConventionManager.ApplyValueConverter = CreateApplyValueConverter(); + } + + private static IValueConverter RangeBaseToPercentageConverter = new RangeBaseToPercentageConverter(); + + private Action CreateApplyValueConverter() + { + var original = ConventionManager.ApplyValueConverter; + return (binding, wpfProperty, targetProperty) => + { + if (wpfProperty == RangeBase.ValueProperty && typeof(Percentage).IsAssignableFrom(targetProperty.PropertyType)) + { + binding.Converter = RangeBaseToPercentageConverter; + } + else + { + original(binding, wpfProperty, targetProperty); + } + }; + } + + protected override void OnStartup(object sender, StartupEventArgs e) + { + DisplayRootViewFor(); + + Application.Current.MainWindow.Hide(); + } + + protected override object GetInstance(Type service, string key) + { + return _container.GetInstance(service, key); + } + + protected override IEnumerable GetAllInstances(Type service) + { + return _container.GetAllInstances(service); + } + + protected override void BuildUp(object instance) + { + _container.BuildUp(instance); + } + + protected override void OnUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) + { + e.Handled = true; + MessageBox.Show(e.Exception.Message, "An error as occurred", MessageBoxButton.OK); + } + } +} diff --git a/WinTermPlus/Config.cs b/WinTermPlus/Config.cs new file mode 100644 index 0000000..372a7fa --- /dev/null +++ b/WinTermPlus/Config.cs @@ -0,0 +1,54 @@ +using WinTermPlus.Infrastructure; + +namespace WinTermPlus +{ + public class Config + { + public WindowSize Size => new WindowSize(new Percentage(Width), new Percentage(Height)); + + public bool QuakeMode + { + get => Properties.Settings.Default.QuakeMode; + set + { + Properties.Settings.Default.QuakeMode = value; + Save(); + } + } + + public int Height + { + get => Properties.Settings.Default.Height; + set + { + Properties.Settings.Default.Height = value; + Save(); + } + } + + public int Width + { + get => Properties.Settings.Default.Width; + set + { + Properties.Settings.Default.Width = value; + Save(); + } + } + + public bool StartWithWindows + { + get => Properties.Settings.Default.StartWithWindows; + set + { + Properties.Settings.Default.StartWithWindows = value; + Save(); + } + } + + public void Save() + { + Properties.Settings.Default.Save(); + } + } +} \ No newline at end of file diff --git a/WinTermPlus/Infrastructure/DelegateCommand.cs b/WinTermPlus/Infrastructure/DelegateCommand.cs new file mode 100644 index 0000000..671416e --- /dev/null +++ b/WinTermPlus/Infrastructure/DelegateCommand.cs @@ -0,0 +1,27 @@ +using System; +using System.Windows.Input; + +namespace WinTermPlus.Infrastructure +{ + public class DelegateCommand : ICommand + { + private readonly Action _action; + + public event EventHandler CanExecuteChanged; + + public DelegateCommand(Action action) + { + _action = action; + } + + public bool CanExecute(object parameter) + { + return true; + } + + public void Execute(object parameter) + { + _action(); + } + } +} \ No newline at end of file diff --git a/WinTermPlus/Infrastructure/Percentage.cs b/WinTermPlus/Infrastructure/Percentage.cs new file mode 100644 index 0000000..24bbb63 --- /dev/null +++ b/WinTermPlus/Infrastructure/Percentage.cs @@ -0,0 +1,25 @@ +using System; + +namespace WinTermPlus.Infrastructure +{ + public class Percentage + { + private Percentage() { } + private int _val; + + public Percentage(int val) + { + _val = val; + } + + public double ToDouble() + { + return _val * .01; + } + + public int ToInt() + { + return _val; + } + } +} \ No newline at end of file diff --git a/WinTermPlus/Infrastructure/WindowSize.cs b/WinTermPlus/Infrastructure/WindowSize.cs new file mode 100644 index 0000000..d897fad --- /dev/null +++ b/WinTermPlus/Infrastructure/WindowSize.cs @@ -0,0 +1,15 @@ +namespace WinTermPlus.Infrastructure +{ + public class WindowSize + { + private WindowSize() { } + public WindowSize(Percentage width, Percentage height) + { + Width = width; + Height = height; + } + + public Percentage Width { get; } + public Percentage Height { get; } + } +} \ No newline at end of file diff --git a/WinTermPlus/Interop/PInvoke.cs b/WinTermPlus/Interop/PInvoke.cs new file mode 100644 index 0000000..44d5e51 --- /dev/null +++ b/WinTermPlus/Interop/PInvoke.cs @@ -0,0 +1,58 @@ +using System; +using System.Runtime.InteropServices; + +namespace WinTermPlus.Interop +{ + public class PInvoke + { + public delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam); + + [DllImport("user32.dll")] + public static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam); + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow); + + [DllImport("user32.dll")] + public static extern bool SetForegroundWindow(IntPtr hWnd); + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool MoveWindow(IntPtr hWnd, int x, int y, int nWidth, int nHeight, bool bRepaint); + + [DllImport("user32.dll", SetLastError=true)] + public static extern IntPtr SetActiveWindow(IntPtr hWnd); + + [DllImport("user32.dll")] + public static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll")] + public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam); + + public const uint WM_LBUTTONDOWN = 0x0201; + public const uint WM_LBUTTONUP = 0x0202; + public static void SendMouseClick(IntPtr iHandle) + { + + GetWindowRect(iHandle, out var rect); + + int x = 100 - rect.Left; + int y = 100 - rect.Top; + int lparm = (y << 16) + x; + SendMessage(iHandle, WM_LBUTTONDOWN, 0, lparm); + SendMessage(iHandle, WM_LBUTTONUP, 0, lparm); + } + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); + + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int Left; // x position of upper-left corner + public int Top; // y position of upper-left corner + public int Right; // x position of lower-right corner + public int Bottom; // y position of lower-right corner + } + } +} \ No newline at end of file diff --git a/WinTermPlus/Interop/ShowWindowCommands.cs b/WinTermPlus/Interop/ShowWindowCommands.cs new file mode 100644 index 0000000..632f01f --- /dev/null +++ b/WinTermPlus/Interop/ShowWindowCommands.cs @@ -0,0 +1,86 @@ +namespace WinTermPlus.Interop +{ + public enum ShowWindowCommands + { + /// + /// Hides the window and activates another window. + /// + Hide = 0, + + /// + /// Activates and displays a window. If the window is minimized or + /// maximized, the system restores it to its original size and position. + /// An application should specify this flag when displaying the window + /// for the first time. + /// + Normal = 1, + + /// + /// Activates the window and displays it as a minimized window. + /// + ShowMinimized = 2, + + /// + /// Maximizes the specified window. + /// + Maximize = 3, // is this the right value? + + /// + /// Activates the window and displays it as a maximized window. + /// + ShowMaximized = 3, + + /// + /// Displays a window in its most recent size and position. This value + /// is similar to , except + /// the window is not activated. + /// + ShowNoActivate = 4, + + /// + /// Activates the window and displays it in its current size and position. + /// + Show = 5, + + /// + /// Minimizes the specified window and activates the next top-level + /// window in the Z order. + /// + Minimize = 6, + + /// + /// Displays the window as a minimized window. This value is similar to + /// , except the + /// window is not activated. + /// + ShowMinNoActive = 7, + + /// + /// Displays the window in its current size and position. This value is + /// similar to , except the + /// window is not activated. + /// + ShowNA = 8, + + /// + /// Activates and displays the window. If the window is minimized or + /// maximized, the system restores it to its original size and position. + /// An application should specify this flag when restoring a minimized window. + /// + Restore = 9, + + /// + /// Sets the show state based on the SW_* value specified in the + /// STARTUPINFO structure passed to the CreateProcess function by the + /// program that started the application. + /// + ShowDefault = 10, + + /// + /// Windows 2000/XP: Minimizes a window, even if the thread + /// that owns the window is not responding. This flag should only be + /// used when minimizing windows from a different thread. + /// + ForceMinimize = 11 + } +} \ No newline at end of file diff --git a/WinTermPlus/Interop/WindowHandles.cs b/WinTermPlus/Interop/WindowHandles.cs new file mode 100644 index 0000000..c380a10 --- /dev/null +++ b/WinTermPlus/Interop/WindowHandles.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace WinTermPlus.Interop +{ + public class WindowHandles + { + private readonly Process _process; + + public WindowHandles(Process process) + { + _process = process; + } + + public List Handles + { + get + { + var handles = new List(); + + var threads = _process.Threads; + foreach (ProcessThread thread in threads) + PInvoke.EnumThreadWindows(thread.Id, + (hWnd, lParam) => + { + handles.Add(hWnd); + return true; + }, IntPtr.Zero); + + return handles; + } + } + + public void ApplyToAll(Action action) + { + foreach (var handle in Handles) + { + action(handle); + } + } + } +} \ No newline at end of file diff --git a/WinTermPlus/Interop/WindowsStartup.cs b/WinTermPlus/Interop/WindowsStartup.cs new file mode 100644 index 0000000..93a30b0 --- /dev/null +++ b/WinTermPlus/Interop/WindowsStartup.cs @@ -0,0 +1,41 @@ +using System.Reflection; +using Microsoft.Win32; +using System.Linq; + +namespace WinTermPlus.Interop +{ + public class WindowsStartup + { + public const string AppName = "WinTermPlus"; + + public static void UpdateStartupKey(bool add) + { + var startupKey = GetStartupKey(); + if (startupKey.GetValueNames().Contains(AppName)) + { + startupKey.DeleteValue(AppName); + } + if (add) + { + startupKey.SetValue(AppName, $"\"{GetStartupKeyValue()}\""); + } + } + + public static bool IsStartupKeySet() + { + var startupKey = GetStartupKey(); + return startupKey.GetValueNames().Contains(AppName) + && startupKey.GetValue(AppName).ToString().Replace("\"", "") == GetStartupKeyValue(); + } + + private static string GetStartupKeyValue() + { + return Assembly.GetExecutingAssembly().Location; + } + + private static RegistryKey GetStartupKey() + { + return Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true); + } + } +} \ No newline at end of file diff --git a/WinTermPlus/Interop/WindowsTerminalProcess.cs b/WinTermPlus/Interop/WindowsTerminalProcess.cs new file mode 100644 index 0000000..df835ce --- /dev/null +++ b/WinTermPlus/Interop/WindowsTerminalProcess.cs @@ -0,0 +1,115 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using System.Windows.Forms; +using WinTermPlus.Infrastructure; + +namespace WinTermPlus.Interop +{ + public partial class WindowsTerminalProcess + { + private readonly Process _process; + + private WindowsTerminalProcess(Process process) + { + _process = process; + } + + public bool IsRunning => _process != null; + + public bool IsFocused + { + get + { + var handles = new WindowHandles(_process); + return handles.Handles.Any(handle => PInvoke.GetForegroundWindow() == handle); + } + } + + private bool RunOnHandle(Action action) + { + if (!IsRunning) + { + return false; + } + + var handles = new WindowHandles(_process); + if (handles.Handles.Count == 0) + { + return false; + } + + var handle = handles.Handles[0]; + action(handle); + return true; + } + + public void ToggleVisibility(WindowSize size) + { + if (IsFocused) + { + Hide(); + } + else + { + Show(size); + } + } + + public void Show(WindowSize size) + { + RunOnHandle(handle => + { + PInvoke.ShowWindow(handle, ShowWindowCommands.Restore); + PInvoke.SetForegroundWindow(handle); + ResizeAndPositionWindow(handle, size); + }); + } + + public void Hide() + { + RunOnHandle(handle => + PInvoke.ShowWindow(handle, ShowWindowCommands.Minimize) + ); + } + + private void ResizeAndPositionWindow(IntPtr handle, WindowSize size) + { + var primaryScreenBounds = Screen.PrimaryScreen.Bounds; + var width = (int)Math.Floor(primaryScreenBounds.Width * size.Width.ToDouble()); + var x = (primaryScreenBounds.Width - width) / 2; + var height = (int)Math.Floor(primaryScreenBounds.Height * size.Height.ToDouble()); + + PInvoke.MoveWindow(handle, x, 0, width, height, true); + } + + public static WindowsTerminalProcess Get() + { + var process = Process.GetProcessesByName("WindowsTerminal").FirstOrDefault(); + if (process != null) + { + return new WindowsTerminalProcess(process); + } + return null; + } + + public static WindowsTerminalProcess Launch() + { + var localAppDataPath = Environment.GetEnvironmentVariable("LocalAppData"); + var wtFullPath = Path.Combine(localAppDataPath, @"Microsoft\WindowsApps\wt.exe"); + + var process = new Process(); + process.StartInfo.FileName = wtFullPath; + process.Start(); + + return new WindowsTerminalProcess(process); + } + + public void ResizeAndPositionWindow(WindowSize windowSize) + { + RunOnHandle(handle => ResizeAndPositionWindow(handle, windowSize)); + } + } +} \ No newline at end of file diff --git a/WinTermPlus/Properties/AssemblyInfo.cs b/WinTermPlus/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..93f29dd --- /dev/null +++ b/WinTermPlus/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WinTermPlus")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("WinTermPlus")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/WinTermPlus/Properties/Resources.Designer.cs b/WinTermPlus/Properties/Resources.Designer.cs new file mode 100644 index 0000000..5a7d90b --- /dev/null +++ b/WinTermPlus/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WinTermPlus.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WinTermPlus.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/WinTermPlus/Properties/Resources.resx b/WinTermPlus/Properties/Resources.resx new file mode 100644 index 0000000..ffecec8 --- /dev/null +++ b/WinTermPlus/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/WinTermPlus/Properties/Settings.Designer.cs b/WinTermPlus/Properties/Settings.Designer.cs new file mode 100644 index 0000000..136d5e8 --- /dev/null +++ b/WinTermPlus/Properties/Settings.Designer.cs @@ -0,0 +1,74 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WinTermPlus.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.3.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool StartWithWindows { + get { + return ((bool)(this["StartWithWindows"])); + } + set { + this["StartWithWindows"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool QuakeMode { + get { + return ((bool)(this["QuakeMode"])); + } + set { + this["QuakeMode"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("50")] + public int Height { + get { + return ((int)(this["Height"])); + } + set { + this["Height"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("80")] + public int Width { + get { + return ((int)(this["Width"])); + } + set { + this["Width"] = value; + } + } + } +} diff --git a/WinTermPlus/Properties/Settings.settings b/WinTermPlus/Properties/Settings.settings new file mode 100644 index 0000000..d90474d --- /dev/null +++ b/WinTermPlus/Properties/Settings.settings @@ -0,0 +1,18 @@ + + + + + + False + + + True + + + 50 + + + 80 + + + \ No newline at end of file diff --git a/WinTermPlus/UI/Converters/RangeBaseToPercentageConverter.cs b/WinTermPlus/UI/Converters/RangeBaseToPercentageConverter.cs new file mode 100644 index 0000000..6c23b5f --- /dev/null +++ b/WinTermPlus/UI/Converters/RangeBaseToPercentageConverter.cs @@ -0,0 +1,30 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using WinTermPlus.Infrastructure; + +namespace WinTermPlus.UI.Converters +{ + public class RangeBaseToPercentageConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null) + { + return null; + } + + return ((Percentage)value).ToInt(); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null) + { + return null; + } + + return new Percentage((int)Math.Floor((double)value)); + } + } +} \ No newline at end of file diff --git a/WinTermPlus/UI/Converters/StringToPercentageConverter.cs b/WinTermPlus/UI/Converters/StringToPercentageConverter.cs new file mode 100644 index 0000000..d8c471b --- /dev/null +++ b/WinTermPlus/UI/Converters/StringToPercentageConverter.cs @@ -0,0 +1,25 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using WinTermPlus.Infrastructure; + +namespace WinTermPlus.UI.Converters +{ + public class StringToPercentageConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null) + { + return null; + } + + return ((Percentage)value).ToInt().ToString(); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return null; + } + } +} \ No newline at end of file diff --git a/WinTermPlus/UI/ViewModels/ConfigViewModel.cs b/WinTermPlus/UI/ViewModels/ConfigViewModel.cs new file mode 100644 index 0000000..3cddbf8 --- /dev/null +++ b/WinTermPlus/UI/ViewModels/ConfigViewModel.cs @@ -0,0 +1,70 @@ +using Caliburn.Micro; +using System.Windows; +using WinTermPlus.Infrastructure; +using WinTermPlus.Interop; + +namespace WinTermPlus.UI.ViewModels +{ + public class ConfigViewModel : Screen + { + private readonly Config _config; + + public ConfigViewModel(Config config) + { + _config = config; + } + + public bool QuakeMode + { + get => _config.QuakeMode; + set + { + _config.QuakeMode = value; + NotifyOfPropertyChange(nameof(QuakeMode)); + } + } + + public Percentage Width + { + get => _config.Size.Width; + set + { + _config.Width = value.ToInt(); + NotifyOfPropertyChange(nameof(Width)); + } + } + + public Percentage Height + { + get => _config.Size.Height; + set + { + _config.Height = value.ToInt(); + NotifyOfPropertyChange(nameof(Height)); + } + } + public bool StartWithWindows + { + get => _config.StartWithWindows; + set + { + _config.StartWithWindows = value; + WindowsStartup.UpdateStartupKey(value); + NotifyOfPropertyChange(nameof(Height)); + } + } + + public override void NotifyOfPropertyChange(string propertyName = null) + { + base.NotifyOfPropertyChange(propertyName); + + var process = WindowsTerminalProcess.Get(); + process?.ResizeAndPositionWindow(_config.Size); + } + + public void Done() + { + Application.Current.MainWindow.Hide(); + } + } +} diff --git a/WinTermPlus/UI/ViewModels/ShellViewModel.cs b/WinTermPlus/UI/ViewModels/ShellViewModel.cs new file mode 100644 index 0000000..f8c12c3 --- /dev/null +++ b/WinTermPlus/UI/ViewModels/ShellViewModel.cs @@ -0,0 +1,51 @@ +using Caliburn.Micro; +using NHotkey; +using NHotkey.Wpf; +using System.Windows.Controls; +using System.Windows.Input; +using WinTermPlus.Interop; + +namespace WinTermPlus.UI.ViewModels +{ + public class ShellViewModel + { + private readonly SimpleContainer _container; + private INavigationService _navigationService; + private readonly Config _settings; + + public ShellViewModel(SimpleContainer container, Config settings) + { + HotkeyManager.Current.Remove("Increment"); + HotkeyManager.Current.AddOrReplace("Increment", Key.OemTilde, ModifierKeys.Control, true, OnQuakeModeKey); + + _container = container; + _settings = settings; + } + + public void RegisterFrame(Frame frame) + { + _navigationService = new FrameAdapter(frame); + _container.Instance(_navigationService); + _navigationService.NavigateToViewModel(typeof(ConfigViewModel)); + } + + private void OnQuakeModeKey(object sender, HotkeyEventArgs e) + { + if (!_settings.QuakeMode) + { + return; + } + + var windowsTerminalProcess = WindowsTerminalProcess.Get(); + if(windowsTerminalProcess == null) + { + windowsTerminalProcess = WindowsTerminalProcess.Launch(); + windowsTerminalProcess.Show(_settings.Size); + } + else + { + windowsTerminalProcess.ToggleVisibility(_settings.Size); + } + } + } +} diff --git a/WinTermPlus/UI/ViewModels/TaskbarIconViewModel.cs b/WinTermPlus/UI/ViewModels/TaskbarIconViewModel.cs new file mode 100644 index 0000000..26197e2 --- /dev/null +++ b/WinTermPlus/UI/ViewModels/TaskbarIconViewModel.cs @@ -0,0 +1,25 @@ +using System.Windows; +using System.Windows.Input; +using WinTermPlus.Infrastructure; + +namespace WinTermPlus.UI.ViewModels +{ + public class TaskbarIconViewModel + { + public ICommand ExitApplicationCommand + { + get + { + return new DelegateCommand(() => Application.Current.Shutdown()); + } + } + public ICommand ConfigCommand + { + get + { + return new DelegateCommand(() => Application.Current.MainWindow.Show()); + } + } + } + +} \ No newline at end of file diff --git a/WinTermPlus/UI/Views/ConfigView.xaml b/WinTermPlus/UI/Views/ConfigView.xaml new file mode 100644 index 0000000..f42bb9e --- /dev/null +++ b/WinTermPlus/UI/Views/ConfigView.xaml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WinTermPlus/UI/Views/ConfigView.xaml.cs b/WinTermPlus/UI/Views/ConfigView.xaml.cs new file mode 100644 index 0000000..ff37e55 --- /dev/null +++ b/WinTermPlus/UI/Views/ConfigView.xaml.cs @@ -0,0 +1,20 @@ +using System.Windows; +using System.Windows.Input; +using Hardcodet.Wpf.TaskbarNotification; +using NHotkey; +using NHotkey.Wpf; +using WinTermPlus.Interop; + +namespace WinTermPlus.UI.Views +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class ConfigView + { + public ConfigView() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/WinTermPlus/UI/Views/ShellView.xaml b/WinTermPlus/UI/Views/ShellView.xaml new file mode 100644 index 0000000..398a62d --- /dev/null +++ b/WinTermPlus/UI/Views/ShellView.xaml @@ -0,0 +1,14 @@ + + + + + diff --git a/WinTermPlus/UI/Views/ShellView.xaml.cs b/WinTermPlus/UI/Views/ShellView.xaml.cs new file mode 100644 index 0000000..e9112b6 --- /dev/null +++ b/WinTermPlus/UI/Views/ShellView.xaml.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace WinTermPlus.UI.Views +{ + /// + /// Interaction logic for ShellView.xaml + /// + public partial class ShellView : Window + { + public ShellView() + { + InitializeComponent(); + + WindowStyle = WindowStyle.None; + ResizeMode = ResizeMode.NoResize; + + var desktopWorkingArea = SystemParameters.WorkArea; + Left = desktopWorkingArea.Right - Width; + Top = desktopWorkingArea.Bottom - Height; + + Topmost = true; + + Hide(); + } + } +} diff --git a/WinTermPlus/UI/Views/TaskbarIconView.xaml b/WinTermPlus/UI/Views/TaskbarIconView.xaml new file mode 100644 index 0000000..7a373ca --- /dev/null +++ b/WinTermPlus/UI/Views/TaskbarIconView.xaml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/WinTermPlus/WinTermPlus.csproj b/WinTermPlus/WinTermPlus.csproj new file mode 100644 index 0000000..a0ba0ec --- /dev/null +++ b/WinTermPlus/WinTermPlus.csproj @@ -0,0 +1,157 @@ + + + + + Debug + AnyCPU + {48E4707C-1E4C-4D61-9909-70360890ADA1} + WinExe + WinTermPlus + WinTermPlus + v4.8 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + App.ico + + + WinTermPlus.App + + + + ..\packages\Caliburn.Micro.Core.3.2.0\lib\net45\Caliburn.Micro.dll + + + ..\packages\Caliburn.Micro.3.2.0\lib\net45\Caliburn.Micro.Platform.dll + + + ..\packages\Caliburn.Micro.3.2.0\lib\net45\Caliburn.Micro.Platform.Core.dll + + + ..\packages\Hardcodet.NotifyIcon.Wpf.1.0.8\lib\net451\Hardcodet.Wpf.TaskbarNotification.dll + True + + + ..\packages\NHotkey.1.2.1\lib\net20\NHotkey.dll + True + + + ..\packages\NHotkey.Wpf.1.2.1\lib\net35\NHotkey.Wpf.dll + True + + + + + + + + ..\packages\Caliburn.Micro.3.2.0\lib\net45\System.Windows.Interactivity.dll + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + + + + + + + + True + True + Settings.settings + + + + ShellView.xaml + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + + + + + + + ConfigView.xaml + Code + + + Designer + MSBuild:Compile + + + Designer + + + + + + Code + + + True + True + Resources.resx + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + PreserveNewest + + + + + \ No newline at end of file diff --git a/WinTermPlus/packages.config b/WinTermPlus/packages.config new file mode 100644 index 0000000..6fe6562 --- /dev/null +++ b/WinTermPlus/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file