Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b61d11f6a | ||
|
|
49ee8489d6 | ||
|
|
7b485ce407 | ||
|
|
66465b525f | ||
|
|
9c5d8c27fd | ||
|
|
4ae3be544b | ||
|
|
e32e0398ee | ||
|
|
fab22f2479 | ||
|
|
b2151c69ab | ||
|
|
f5fba8517a |
No files matched your search
@@ -1,6 +1,9 @@
|
||||
using FluentTerminal.App.Views;
|
||||
using Autofac;
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Services.Implementation;
|
||||
using FluentTerminal.App.ViewModels;
|
||||
using FluentTerminal.App.Views;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
@@ -11,56 +14,91 @@ using Windows.UI.Core;
|
||||
using Windows.UI.ViewManagement;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace FluentTerminal.App
|
||||
{
|
||||
sealed partial class App : Application
|
||||
{
|
||||
public static BackgroundTaskDeferral AppServiceDeferral = null;
|
||||
public static AppServiceConnection Connection = null;
|
||||
public static App Instance;
|
||||
private bool _alreadyLaunched;
|
||||
private IContainer _container;
|
||||
private int _openTerminalWindows;
|
||||
private int? _settingsWindowId;
|
||||
|
||||
public App()
|
||||
{
|
||||
InitializeComponent();
|
||||
Instance = this;
|
||||
|
||||
var builder = new ContainerBuilder();
|
||||
builder.RegisterType<SettingsService>().As<ISettingsService>().InstancePerDependency();
|
||||
builder.RegisterType<DefaultValueProvider>().As<IDefaultValueProvider>().SingleInstance();
|
||||
builder.RegisterType<TerminalService>().As<ITerminalService>().SingleInstance();
|
||||
builder.RegisterType<MainViewModel>().InstancePerDependency();
|
||||
builder.RegisterType<SettingsViewModel>().InstancePerDependency();
|
||||
|
||||
_container = builder.Build();
|
||||
}
|
||||
|
||||
public static BackgroundTaskDeferral AppServiceDeferral = null;
|
||||
public static AppServiceConnection Connection = null;
|
||||
public static App Instance;
|
||||
int? _settingsWindowId;
|
||||
int idCreate = 0;
|
||||
List<int> idSaved = new List<int>();
|
||||
|
||||
protected override async void OnLaunched(LaunchActivatedEventArgs e)
|
||||
public async Task CreateNewTerminalWindow()
|
||||
{
|
||||
var launch = FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync().AsTask();
|
||||
var clearCache = WebView.ClearTemporaryWebDataAsync().AsTask();
|
||||
var id = await CreateSecondaryView(typeof(MainPage), true);
|
||||
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(id);
|
||||
}
|
||||
|
||||
await Task.WhenAll(launch, clearCache);
|
||||
public void SettingsWindowClosed()
|
||||
{
|
||||
_settingsWindowId = null;
|
||||
}
|
||||
|
||||
Frame rootFrame = Window.Current.Content as Frame;
|
||||
if (rootFrame == null)
|
||||
public async Task ShowSettings()
|
||||
{
|
||||
if (_settingsWindowId == null)
|
||||
{
|
||||
rootFrame = new Frame();
|
||||
rootFrame.NavigationFailed += OnNavigationFailed;
|
||||
Window.Current.Content = rootFrame;
|
||||
_settingsWindowId = await CreateSecondaryView(typeof(SettingsPage), true);
|
||||
}
|
||||
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(_settingsWindowId.Value);
|
||||
}
|
||||
|
||||
if (rootFrame.Content == null)
|
||||
public void TerminalWindowClosed()
|
||||
{
|
||||
_openTerminalWindows--;
|
||||
}
|
||||
|
||||
protected override async void OnActivated(IActivatedEventArgs args)
|
||||
{
|
||||
if (args is CommandLineActivatedEventArgs commandLineActivated)
|
||||
{
|
||||
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = true;
|
||||
rootFrame.Navigate(typeof(MainPage), e.Arguments);
|
||||
idSaved.Add(ApplicationView.GetForCurrentView().Id);
|
||||
var argument = commandLineActivated.Operation.Arguments.Trim();
|
||||
if (_alreadyLaunched)
|
||||
{
|
||||
if (argument == "settings")
|
||||
{
|
||||
await ShowSettings();
|
||||
}
|
||||
else if (argument == "new")
|
||||
{
|
||||
await CreateNewTerminalWindow();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (argument == "settings")
|
||||
{
|
||||
CreateMainView(typeof(SettingsPage), true);
|
||||
}
|
||||
else if (argument == "new")
|
||||
{
|
||||
CreateMainView(typeof(MainPage), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (false) //todo: add option to create new windows on launch
|
||||
{
|
||||
await CreateNewWindow(typeof(MainPage), true);
|
||||
}
|
||||
Window.Current.Activate();
|
||||
}
|
||||
|
||||
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
|
||||
{
|
||||
// connection established from the fulltrust process
|
||||
base.OnBackgroundActivated(args);
|
||||
if (args.TaskInstance.TriggerDetails is AppServiceTriggerDetails details)
|
||||
{
|
||||
@@ -71,6 +109,81 @@ namespace FluentTerminal.App
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void OnLaunched(LaunchActivatedEventArgs e)
|
||||
{
|
||||
var launch = FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync().AsTask();
|
||||
var clearCache = WebView.ClearTemporaryWebDataAsync().AsTask();
|
||||
await Task.WhenAll(launch, clearCache);
|
||||
|
||||
if (!_alreadyLaunched)
|
||||
{
|
||||
CreateMainView(typeof(MainPage), true);
|
||||
Window.Current.Activate();
|
||||
}
|
||||
else if (_openTerminalWindows == 0)
|
||||
{
|
||||
await CreateSecondaryView(typeof(MainPage), true);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateMainView(Type pageType, bool extendViewIntoTitleBar)
|
||||
{
|
||||
Frame rootFrame = Window.Current.Content as Frame;
|
||||
if (rootFrame == null)
|
||||
{
|
||||
rootFrame = new Frame();
|
||||
Window.Current.Content = rootFrame;
|
||||
}
|
||||
|
||||
if (rootFrame.Content == null)
|
||||
{
|
||||
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = extendViewIntoTitleBar;
|
||||
rootFrame.Navigate(pageType, GetViewModelForPageType(pageType));
|
||||
|
||||
if (pageType == typeof(MainPage))
|
||||
{
|
||||
_openTerminalWindows++;
|
||||
}
|
||||
}
|
||||
_alreadyLaunched = true;
|
||||
Window.Current.Activate();
|
||||
}
|
||||
|
||||
private async Task<int> CreateSecondaryView(Type pageType, bool ExtendViewIntoTitleBar)
|
||||
{
|
||||
int windowId = 0;
|
||||
await CoreApplication.CreateNewView().Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = ExtendViewIntoTitleBar;
|
||||
var frame = new Frame();
|
||||
frame.Navigate(pageType, GetViewModelForPageType(pageType));
|
||||
Window.Current.Content = frame;
|
||||
Window.Current.Activate();
|
||||
|
||||
windowId = ApplicationView.GetForCurrentView().Id;
|
||||
});
|
||||
|
||||
if (pageType == typeof(MainPage))
|
||||
{
|
||||
_openTerminalWindows++;
|
||||
}
|
||||
|
||||
return windowId;
|
||||
}
|
||||
|
||||
private object GetViewModelForPageType(Type pageType)
|
||||
{
|
||||
if (pageType == typeof(MainPage))
|
||||
{
|
||||
return _container.Resolve<MainViewModel>();
|
||||
}
|
||||
else if (pageType == typeof(SettingsPage))
|
||||
{
|
||||
return _container.Resolve<SettingsViewModel>();
|
||||
}
|
||||
throw new Exception("No ViewModel defined for Page: " + pageType.Name);
|
||||
}
|
||||
|
||||
private void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
|
||||
{
|
||||
if (args.Request.Message.ContainsKey("exit"))
|
||||
@@ -86,49 +199,5 @@ namespace FluentTerminal.App
|
||||
AppServiceDeferral.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<int> CreateNewWindow(Type pageType, bool ExtendViewIntoTitleBar)
|
||||
{
|
||||
var create = CoreApplication.CreateNewView();
|
||||
await create.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = ExtendViewIntoTitleBar;
|
||||
var frame = new Frame();
|
||||
frame.Navigate(pageType, null);
|
||||
Window.Current.Content = frame;
|
||||
Window.Current.Activate();
|
||||
|
||||
idCreate = ApplicationView.GetForCurrentView().Id;
|
||||
});
|
||||
|
||||
for (int i = idSaved.Count - 1; i >= 0; i--)
|
||||
if (await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
|
||||
idCreate, ViewSizePreference.Default,
|
||||
idSaved[i], ViewSizePreference.Default)
|
||||
) break;
|
||||
|
||||
idSaved.Add(idCreate);
|
||||
return idCreate;
|
||||
}
|
||||
|
||||
public async Task ShowNew()
|
||||
{
|
||||
var id = await CreateNewWindow(typeof(MainPage), true);
|
||||
bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(id);
|
||||
}
|
||||
|
||||
public async Task ShowSettings()
|
||||
{
|
||||
if (_settingsWindowId == null)
|
||||
{
|
||||
_settingsWindowId = await CreateNewWindow(typeof(Views.SettingsPage), false);
|
||||
}
|
||||
bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(_settingsWindowId.Value);
|
||||
}
|
||||
|
||||
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
|
||||
{
|
||||
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace FluentTerminal.App.Converters
|
||||
{
|
||||
public class FalseToVisibleConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is Boolean collapsed)
|
||||
{
|
||||
return collapsed ? Visibility.Collapsed : Visibility.Visible;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is Visibility visibility)
|
||||
{
|
||||
return visibility == Visibility.Collapsed;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using FluentTerminal.App.ViewModels;
|
||||
using FluentTerminal.App.Views;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace FluentTerminal.App.Converters
|
||||
{
|
||||
internal class TerminalViewModelToViewConverter : IValueConverter
|
||||
{
|
||||
private Dictionary<int, TerminalView> _viewDictionary;
|
||||
|
||||
public TerminalViewModelToViewConverter()
|
||||
{
|
||||
_viewDictionary = new Dictionary<int, TerminalView>();
|
||||
}
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is TerminalViewModel terminal)
|
||||
{
|
||||
if (_viewDictionary.TryGetValue(terminal.Id, out TerminalView view))
|
||||
{
|
||||
return view;
|
||||
}
|
||||
|
||||
var newView = new TerminalView(terminal);
|
||||
_viewDictionary.Add(terminal.Id, newView);
|
||||
return newView;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace FluentTerminal.App.Converters
|
||||
{
|
||||
public class TrueToVisibleConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is Boolean visible)
|
||||
{
|
||||
return visible ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is Visibility visibility)
|
||||
{
|
||||
return visibility == Visibility.Visible;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
<WindowsXamlEnableOverview>true</WindowsXamlEnableOverview>
|
||||
<PackageCertificateKeyFile>FluentTerminal.App_StoreKey.pfx</PackageCertificateKeyFile>
|
||||
<PackageCertificateThumbprint>8FA8EF727E767FAE5409DDD1694C1CEE054A2962</PackageCertificateThumbprint>
|
||||
<AppxAutoIncrementPackageRevision>True</AppxAutoIncrementPackageRevision>
|
||||
<AppxAutoIncrementPackageRevision>False</AppxAutoIncrementPackageRevision>
|
||||
<AppxBundle>Always</AppxBundle>
|
||||
<AppxBundlePlatforms>x86|x64</AppxBundlePlatforms>
|
||||
</PropertyGroup>
|
||||
@@ -99,6 +99,22 @@
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Converters\FalseToVisibleConverter.cs" />
|
||||
<Compile Include="Converters\TerminalViewModelToViewConverter.cs" />
|
||||
<Compile Include="Converters\TrueToVisibleConverter.cs" />
|
||||
<Compile Include="Utilities\ApplicationDataContainerExtensions.cs" />
|
||||
<Compile Include="Services\IDefaultValueProvider.cs" />
|
||||
<Compile Include="Services\Implementation\DefaultValueProvider.cs" />
|
||||
<Compile Include="Services\Implementation\SettingsService.cs" />
|
||||
<Compile Include="Services\Implementation\TerminalService.cs" />
|
||||
<Compile Include="Services\ISettingsService.cs" />
|
||||
<Compile Include="Services\ITerminalService.cs" />
|
||||
<Compile Include="Utilities\HttpJsonContent.cs" />
|
||||
<Compile Include="Utilities\IHttpContentExtensions.cs" />
|
||||
<Compile Include="ViewModels\MainViewModel.cs" />
|
||||
<Compile Include="ViewModels\SettingsViewModel.cs" />
|
||||
<Compile Include="ViewModels\TerminalViewModel.cs" />
|
||||
<Compile Include="Views\ITerminalView.cs" />
|
||||
<Compile Include="Views\MainPage.xaml.cs">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
@@ -106,8 +122,23 @@
|
||||
<Compile Include="Views\SettingsPage.xaml.cs">
|
||||
<DependentUpon>SettingsPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\TerminalPage.xaml.cs">
|
||||
<DependentUpon>TerminalPage.xaml</DependentUpon>
|
||||
<Compile Include="Views\SettingsPages\ColorSettings.xaml.cs">
|
||||
<DependentUpon>ColorSettings.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\SettingsPages\FontSettings.xaml.cs">
|
||||
<DependentUpon>FontSettings.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\SettingsPages\GeneralSettings.xaml.cs">
|
||||
<DependentUpon>GeneralSettings.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\SettingsPages\KeyBindingSettings.xaml.cs">
|
||||
<DependentUpon>KeyBindingSettings.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\SettingsPages\ShellSettings.xaml.cs">
|
||||
<DependentUpon>ShellSettings.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\TerminalView.xaml.cs">
|
||||
<DependentUpon>TerminalView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -116,6 +147,8 @@
|
||||
</AppxManifest>
|
||||
<None Include="FluentTerminal.App_StoreKey.pfx" />
|
||||
<None Include="FluentTerminal.App_TemporaryKey.pfx" />
|
||||
<Content Include="Win32\FluentTerminal.Models.dll" />
|
||||
<Content Include="Win32\FluentTerminal.Models.pdb" />
|
||||
<Content Include="Win32\FluentTerminal.SystemTray.exe.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -166,13 +199,6 @@
|
||||
<Content Include="Assets\AppIcons\Wide310x150Logo.scale-400.png" />
|
||||
<None Include="Package.StoreAssociation.xml" />
|
||||
<Content Include="Properties\Default.rd.xml" />
|
||||
<Content Include="Assets\LockScreenLogo.scale-200.png" />
|
||||
<Content Include="Assets\SplashScreen.scale-200.png" />
|
||||
<Content Include="Assets\Square150x150Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square44x44Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
|
||||
<Content Include="Assets\StoreLogo.png" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
|
||||
<Content Include="Win32\Client\bundle.js" />
|
||||
<Content Include="Win32\Client\index.html" />
|
||||
<Content Include="Win32\Client\style.css" />
|
||||
@@ -187,11 +213,107 @@
|
||||
<Content Include="Win32\Microsoft.Owin.Host.HttpListener.dll" />
|
||||
<Content Include="Win32\Microsoft.Owin.Hosting.dll" />
|
||||
<Content Include="Win32\Microsoft.Owin.StaticFiles.dll" />
|
||||
<Content Include="Win32\Microsoft.Win32.Primitives.dll" />
|
||||
<Content Include="Win32\netstandard.dll" />
|
||||
<Content Include="Win32\Newtonsoft.Json.dll" />
|
||||
<Content Include="Win32\Owin.dll" />
|
||||
<Content Include="Win32\System.AppContext.dll" />
|
||||
<Content Include="Win32\System.Collections.Concurrent.dll" />
|
||||
<Content Include="Win32\System.Collections.dll" />
|
||||
<Content Include="Win32\System.Collections.NonGeneric.dll" />
|
||||
<Content Include="Win32\System.Collections.Specialized.dll" />
|
||||
<Content Include="Win32\System.ComponentModel.dll" />
|
||||
<Content Include="Win32\System.ComponentModel.EventBasedAsync.dll" />
|
||||
<Content Include="Win32\System.ComponentModel.Primitives.dll" />
|
||||
<Content Include="Win32\System.ComponentModel.TypeConverter.dll" />
|
||||
<Content Include="Win32\System.Console.dll" />
|
||||
<Content Include="Win32\System.Data.Common.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.Contracts.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.Debug.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.FileVersionInfo.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.Process.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.StackTrace.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.TextWriterTraceListener.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.Tools.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.TraceSource.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.Tracing.dll" />
|
||||
<Content Include="Win32\System.Drawing.Primitives.dll" />
|
||||
<Content Include="Win32\System.Dynamic.Runtime.dll" />
|
||||
<Content Include="Win32\System.Globalization.Calendars.dll" />
|
||||
<Content Include="Win32\System.Globalization.dll" />
|
||||
<Content Include="Win32\System.Globalization.Extensions.dll" />
|
||||
<Content Include="Win32\System.IO.Compression.dll" />
|
||||
<Content Include="Win32\System.IO.Compression.ZipFile.dll" />
|
||||
<Content Include="Win32\System.IO.dll" />
|
||||
<Content Include="Win32\System.IO.FileSystem.dll" />
|
||||
<Content Include="Win32\System.IO.FileSystem.DriveInfo.dll" />
|
||||
<Content Include="Win32\System.IO.FileSystem.Primitives.dll" />
|
||||
<Content Include="Win32\System.IO.FileSystem.Watcher.dll" />
|
||||
<Content Include="Win32\System.IO.IsolatedStorage.dll" />
|
||||
<Content Include="Win32\System.IO.MemoryMappedFiles.dll" />
|
||||
<Content Include="Win32\System.IO.Pipes.dll" />
|
||||
<Content Include="Win32\System.IO.UnmanagedMemoryStream.dll" />
|
||||
<Content Include="Win32\System.Linq.dll" />
|
||||
<Content Include="Win32\System.Linq.Expressions.dll" />
|
||||
<Content Include="Win32\System.Linq.Parallel.dll" />
|
||||
<Content Include="Win32\System.Linq.Queryable.dll" />
|
||||
<Content Include="Win32\System.Net.Http.dll" />
|
||||
<Content Include="Win32\System.Net.Http.Formatting.dll" />
|
||||
<Content Include="Win32\System.Net.NameResolution.dll" />
|
||||
<Content Include="Win32\System.Net.NetworkInformation.dll" />
|
||||
<Content Include="Win32\System.Net.Ping.dll" />
|
||||
<Content Include="Win32\System.Net.Primitives.dll" />
|
||||
<Content Include="Win32\System.Net.Requests.dll" />
|
||||
<Content Include="Win32\System.Net.Security.dll" />
|
||||
<Content Include="Win32\System.Net.Sockets.dll" />
|
||||
<Content Include="Win32\System.Net.WebHeaderCollection.dll" />
|
||||
<Content Include="Win32\System.Net.WebSockets.Client.dll" />
|
||||
<Content Include="Win32\System.Net.WebSockets.dll" />
|
||||
<Content Include="Win32\System.ObjectModel.dll" />
|
||||
<Content Include="Win32\System.Reflection.dll" />
|
||||
<Content Include="Win32\System.Reflection.Extensions.dll" />
|
||||
<Content Include="Win32\System.Reflection.Primitives.dll" />
|
||||
<Content Include="Win32\System.Resources.Reader.dll" />
|
||||
<Content Include="Win32\System.Resources.ResourceManager.dll" />
|
||||
<Content Include="Win32\System.Resources.Writer.dll" />
|
||||
<Content Include="Win32\System.Runtime.CompilerServices.VisualC.dll" />
|
||||
<Content Include="Win32\System.Runtime.dll" />
|
||||
<Content Include="Win32\System.Runtime.Extensions.dll" />
|
||||
<Content Include="Win32\System.Runtime.Handles.dll" />
|
||||
<Content Include="Win32\System.Runtime.InteropServices.dll" />
|
||||
<Content Include="Win32\System.Runtime.InteropServices.RuntimeInformation.dll" />
|
||||
<Content Include="Win32\System.Runtime.Numerics.dll" />
|
||||
<Content Include="Win32\System.Runtime.Serialization.Formatters.dll" />
|
||||
<Content Include="Win32\System.Runtime.Serialization.Json.dll" />
|
||||
<Content Include="Win32\System.Runtime.Serialization.Primitives.dll" />
|
||||
<Content Include="Win32\System.Runtime.Serialization.Xml.dll" />
|
||||
<Content Include="Win32\System.Security.Claims.dll" />
|
||||
<Content Include="Win32\System.Security.Cryptography.Algorithms.dll" />
|
||||
<Content Include="Win32\System.Security.Cryptography.Csp.dll" />
|
||||
<Content Include="Win32\System.Security.Cryptography.Encoding.dll" />
|
||||
<Content Include="Win32\System.Security.Cryptography.Primitives.dll" />
|
||||
<Content Include="Win32\System.Security.Cryptography.X509Certificates.dll" />
|
||||
<Content Include="Win32\System.Security.Principal.dll" />
|
||||
<Content Include="Win32\System.Security.SecureString.dll" />
|
||||
<Content Include="Win32\System.Text.Encoding.dll" />
|
||||
<Content Include="Win32\System.Text.Encoding.Extensions.dll" />
|
||||
<Content Include="Win32\System.Text.RegularExpressions.dll" />
|
||||
<Content Include="Win32\System.Threading.dll" />
|
||||
<Content Include="Win32\System.Threading.Overlapped.dll" />
|
||||
<Content Include="Win32\System.Threading.Tasks.dll" />
|
||||
<Content Include="Win32\System.Threading.Tasks.Parallel.dll" />
|
||||
<Content Include="Win32\System.Threading.Thread.dll" />
|
||||
<Content Include="Win32\System.Threading.ThreadPool.dll" />
|
||||
<Content Include="Win32\System.Threading.Timer.dll" />
|
||||
<Content Include="Win32\System.ValueTuple.dll" />
|
||||
<Content Include="Win32\System.Web.Http.dll" />
|
||||
<Content Include="Win32\System.Web.Http.Owin.dll" />
|
||||
<Content Include="Win32\System.Xml.ReaderWriter.dll" />
|
||||
<Content Include="Win32\System.Xml.XDocument.dll" />
|
||||
<Content Include="Win32\System.Xml.XmlDocument.dll" />
|
||||
<Content Include="Win32\System.Xml.XmlSerializer.dll" />
|
||||
<Content Include="Win32\System.Xml.XPath.dll" />
|
||||
<Content Include="Win32\System.Xml.XPath.XDocument.dll" />
|
||||
<Content Include="Win32\Unity.Abstractions.dll" />
|
||||
<Content Include="Win32\Unity.Abstractions.pdb" />
|
||||
<Content Include="Win32\Unity.Configuration.dll" />
|
||||
@@ -230,29 +352,67 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\TerminalPage.xaml">
|
||||
<Page Include="Views\SettingsPages\ColorSettings.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\SettingsPages\FontSettings.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\SettingsPages\GeneralSettings.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\SettingsPages\KeyBindingSettings.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\SettingsPages\ShellSettings.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\TerminalView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac">
|
||||
<Version>4.6.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>6.0.6</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MvvmLightLibs">
|
||||
<Version>5.3.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json">
|
||||
<Version>10.0.3</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<SDKReference Include="WindowsDesktop, Version=10.0.16299.0">
|
||||
<Name>Windows Desktop Extensions for the UWP</Name>
|
||||
</SDKReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj">
|
||||
<Project>{5EA40453-A0AA-449E-8406-D6EA51D7E70E}</Project>
|
||||
<Name>FluentTerminal.Models</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\FluentTerminal.RuntimeComponent\FluentTerminal.RuntimeComponent.csproj">
|
||||
<Project>{BAAE6239-343F-454C-977F-F81F91715961}</Project>
|
||||
<Name>FluentTerminal.RuntimeComponent</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>mkdir "$(SolutionDir)\FluentTerminal.App\bin\$(PlatformName)\$(ConfigurationName)\AppX\Win32" 2> NULL
|
||||
xcopy /y /s "$(SolutionDir)FluentTerminal.SystemTray\bin\$(PlatformName)\$(ConfigurationName)" "$(SolutionDir)\FluentTerminal.App\bin\$(PlatformName)\$(ConfigurationName)\AppX\Win32"
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" IgnorableNamespaces="uap mp desktop rescap">
|
||||
<Identity Name="53621FSApps.FluentTerminal" Publisher="CN=BDC72197-B634-438A-B863-16DCB7D90C85" Version="0.1.3.0" />
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" IgnorableNamespaces="uap mp desktop rescap">
|
||||
<Identity Name="53621FSApps.FluentTerminal" Publisher="CN=BDC72197-B634-438A-B863-16DCB7D90C85" Version="0.1.4.0" />
|
||||
<mp:PhoneIdentity PhoneProductId="f7225014-f405-4342-ad2e-ebf6ac60ea2c" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
|
||||
<Properties>
|
||||
<DisplayName>Fluent Terminal</DisplayName>
|
||||
@@ -23,13 +23,18 @@
|
||||
<uap:ShowOn Tile="square310x310Logo" />
|
||||
</uap:ShowNameOnTiles>
|
||||
</uap:DefaultTile>
|
||||
<uap:SplashScreen Image="Assets\AppIcons\SplashScreen.png" />
|
||||
<uap:SplashScreen Image="Assets\AppIcons\SplashScreen.png" uap5:Optional="true" />
|
||||
</uap:VisualElements>
|
||||
<Extensions>
|
||||
<uap:Extension Category="windows.appService">
|
||||
<uap:AppService Name="FluentTerminalAppService" />
|
||||
</uap:Extension>
|
||||
<desktop:Extension Category="windows.fullTrustProcess" Executable="Win32\FluentTerminal.SystemTray.exe" />
|
||||
<uap5:Extension Category="windows.appExecutionAlias" Executable="FluentTerminal.App.exe" EntryPoint="FluentTerminal.App.App">
|
||||
<uap5:AppExecutionAlias>
|
||||
<uap5:ExecutionAlias Alias="flute.exe" />
|
||||
</uap5:AppExecutionAlias>
|
||||
</uap5:Extension>
|
||||
</Extensions>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using FluentTerminal.Models;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IDefaultValueProvider
|
||||
{
|
||||
ShellConfiguration GetDefaultShellConfiguration();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using FluentTerminal.Models;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface ISettingsService
|
||||
{
|
||||
ShellConfiguration GetShellConfiguration();
|
||||
void SaveShellConfiguration(ShellConfiguration spawnConfiguration);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using FluentTerminal.Models;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface ITerminalService
|
||||
{
|
||||
Task<CreateTerminalResponse> CreateTerminal(TerminalSize size, ShellConfiguration shellConfiguration);
|
||||
Task ResizeTerminal(int id, TerminalSize size);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
internal class DefaultValueProvider : IDefaultValueProvider
|
||||
{
|
||||
public ShellConfiguration GetDefaultShellConfiguration()
|
||||
{
|
||||
return new ShellConfiguration
|
||||
{
|
||||
Shell = ShellType.PowerShell,
|
||||
CustomShellLocation = string.Empty,
|
||||
WorkingDirectory = string.Empty,
|
||||
Arguments = string.Empty
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using FluentTerminal.App.Utilities;
|
||||
using FluentTerminal.Models;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
internal class SettingsService : ISettingsService
|
||||
{
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private ApplicationDataContainer _localSettings;
|
||||
private ApplicationDataContainer _roamingSettings;
|
||||
|
||||
public SettingsService(IDefaultValueProvider defaultValueProvider)
|
||||
{
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
_localSettings = ApplicationData.Current.LocalSettings;
|
||||
_roamingSettings = ApplicationData.Current.RoamingSettings;
|
||||
ApplicationData.Current.DataChanged += OnDataChanged;
|
||||
}
|
||||
|
||||
private void OnDataChanged(ApplicationData sender, object args)
|
||||
{
|
||||
//todo handle data changed for roaming settings
|
||||
}
|
||||
|
||||
public ShellConfiguration GetShellConfiguration()
|
||||
{
|
||||
return _localSettings.ReadValueFromJson(nameof(ShellConfiguration), _defaultValueProvider.GetDefaultShellConfiguration());
|
||||
}
|
||||
|
||||
public void SaveShellConfiguration(ShellConfiguration spawnConfiguration)
|
||||
{
|
||||
_localSettings.WriteValueAsJson(nameof(ShellConfiguration), spawnConfiguration);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using FluentTerminal.App.Utilities;
|
||||
using FluentTerminal.Models;
|
||||
using Windows.Web.Http;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
internal class TerminalService : ITerminalService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public TerminalService()
|
||||
{
|
||||
_httpClient = new HttpClient();
|
||||
}
|
||||
|
||||
public async Task<CreateTerminalResponse> CreateTerminal(TerminalSize size, ShellConfiguration shellConfiguration)
|
||||
{
|
||||
var request = new CreateTerminalRequest
|
||||
{
|
||||
Size = size,
|
||||
Configuration = shellConfiguration
|
||||
};
|
||||
|
||||
var response = await _httpClient.PostAsync(new Uri($"http://localhost:9000/terminals"), HttpJsonContent.From(request));
|
||||
var createTerminalResponse = await response.Content.ReadAs<CreateTerminalResponse>();
|
||||
|
||||
return createTerminalResponse;
|
||||
}
|
||||
|
||||
public Task ResizeTerminal(int id, TerminalSize size)
|
||||
{
|
||||
return _httpClient.PostAsync(new Uri($"http://localhost:9000/terminals/{id}/resize"), HttpJsonContent.From(size)).AsTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Newtonsoft.Json;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace FluentTerminal.App.Utilities
|
||||
{
|
||||
public static class ApplicationDataContainerExtensions
|
||||
{
|
||||
public static T ReadValueFromJson<T>(this ApplicationDataContainer container, string name, T fallbackValue)
|
||||
{
|
||||
if (container.Values.TryGetValue(name, out object value))
|
||||
{
|
||||
JsonConvert.PopulateObject((string)value, fallbackValue);
|
||||
}
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
public static void WriteValueAsJson<T>(this ApplicationDataContainer container, string name, T value)
|
||||
{
|
||||
var serialized = JsonConvert.SerializeObject(value);
|
||||
container.Values[name] = serialized;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Newtonsoft.Json;
|
||||
using Windows.Web.Http;
|
||||
|
||||
namespace FluentTerminal.App.Utilities
|
||||
{
|
||||
public static class HttpJsonContent
|
||||
{
|
||||
public static HttpStringContent From(object content)
|
||||
{
|
||||
var serialized = JsonConvert.SerializeObject(content);
|
||||
return new HttpStringContent(serialized, Windows.Storage.Streams.UnicodeEncoding.Utf8, "application/json");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Web.Http;
|
||||
|
||||
namespace FluentTerminal.App.Utilities
|
||||
{
|
||||
public static class IHttpContentExtensions
|
||||
{
|
||||
public static async Task<T> ReadAs<T>(this IHttpContent content)
|
||||
{
|
||||
var contentBody = await content.ReadAsStringAsync();
|
||||
return JsonConvert.DeserializeObject<T>(contentBody);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class MainViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ITerminalService _terminalService;
|
||||
private TerminalViewModel _selectedTerminal;
|
||||
private string _title;
|
||||
|
||||
public MainViewModel(ISettingsService settingsService, ITerminalService terminalService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_terminalService = terminalService;
|
||||
Title = "Fluent Terminal";
|
||||
|
||||
AddTerminalCommand = new RelayCommand(AddTerminal);
|
||||
ShowSettingsCommand = new RelayCommand(async () => await ShowSettings());
|
||||
|
||||
AddTerminal();
|
||||
}
|
||||
|
||||
public RelayCommand AddTerminalCommand { get; }
|
||||
public RelayCommand ShowSettingsCommand { get; }
|
||||
|
||||
public TerminalViewModel SelectedTerminal
|
||||
{
|
||||
get => _selectedTerminal;
|
||||
set => Set(ref _selectedTerminal, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<TerminalViewModel> Terminals { get; } = new ObservableCollection<TerminalViewModel>();
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
set => Set(ref _title, value);
|
||||
}
|
||||
|
||||
private void AddTerminal()
|
||||
{
|
||||
var terminal = new TerminalViewModel(_settingsService, _terminalService);
|
||||
terminal.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(TerminalViewModel.Title) && Terminals.Count == 1)
|
||||
{
|
||||
Title = terminal.Title;
|
||||
}
|
||||
};
|
||||
Terminals.Add(terminal);
|
||||
|
||||
SelectedTerminal = terminal;
|
||||
}
|
||||
|
||||
private Task ShowSettings()
|
||||
{
|
||||
return App.Instance.ShowSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage.Pickers;
|
||||
using Windows.UI.Popups;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class SettingsViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private ShellConfiguration _shellConfiguration;
|
||||
|
||||
public SettingsViewModel(ISettingsService settingsService, IDefaultValueProvider defaultValueProvider)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
|
||||
BrowseForCustomShellCommand = new RelayCommand(async () => await BrowseForCustomShell());
|
||||
BrowseForWorkingDirectoryCommand = new RelayCommand(async () => await BrowseForWorkingDirectory());
|
||||
RestoreDefaultsCommand = new RelayCommand<string>(async (area) => await RestoreDefaults(area));
|
||||
|
||||
_shellConfiguration = _settingsService.GetShellConfiguration();
|
||||
ShellType = _shellConfiguration.Shell;
|
||||
}
|
||||
|
||||
public RelayCommand BrowseForCustomShellCommand { get; }
|
||||
public RelayCommand BrowseForWorkingDirectoryCommand { get; }
|
||||
public RelayCommand<string> RestoreDefaultsCommand { get; }
|
||||
|
||||
public bool CMDIsSelected
|
||||
{
|
||||
get => ShellType == ShellType.CMD;
|
||||
set => ShellType = ShellType.CMD;
|
||||
}
|
||||
|
||||
public bool CustomShellIsSelected
|
||||
{
|
||||
get => ShellType == ShellType.Custom;
|
||||
set => ShellType = ShellType.Custom;
|
||||
}
|
||||
|
||||
public string CustomShellLocation
|
||||
{
|
||||
get => _shellConfiguration.CustomShellLocation;
|
||||
set
|
||||
{
|
||||
if (_shellConfiguration.CustomShellLocation != value)
|
||||
{
|
||||
_shellConfiguration.CustomShellLocation = value;
|
||||
_settingsService.SaveShellConfiguration(_shellConfiguration);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string WorkingDirectory
|
||||
{
|
||||
get => _shellConfiguration.WorkingDirectory;
|
||||
set
|
||||
{
|
||||
if (_shellConfiguration.WorkingDirectory != value)
|
||||
{
|
||||
_shellConfiguration.WorkingDirectory = value;
|
||||
_settingsService.SaveShellConfiguration(_shellConfiguration);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Arguments
|
||||
{
|
||||
get => _shellConfiguration.Arguments;
|
||||
set
|
||||
{
|
||||
if (_shellConfiguration.Arguments != value)
|
||||
{
|
||||
_shellConfiguration.Arguments = value;
|
||||
_settingsService.SaveShellConfiguration(_shellConfiguration);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool PoweShellIsSelected
|
||||
{
|
||||
get => ShellType == ShellType.PowerShell;
|
||||
set => ShellType = ShellType.PowerShell;
|
||||
}
|
||||
|
||||
public ShellType ShellType
|
||||
{
|
||||
get => _shellConfiguration.Shell;
|
||||
set
|
||||
{
|
||||
if (_shellConfiguration.Shell != value)
|
||||
{
|
||||
_shellConfiguration.Shell = value;
|
||||
_settingsService.SaveShellConfiguration(_shellConfiguration);
|
||||
RaisePropertyChanged();
|
||||
RaisePropertyChanged(nameof(CustomShellIsSelected));
|
||||
RaisePropertyChanged(nameof(PoweShellIsSelected));
|
||||
RaisePropertyChanged(nameof(CMDIsSelected));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task BrowseForCustomShell()
|
||||
{
|
||||
var picker = new FileOpenPicker();
|
||||
picker.SuggestedStartLocation = PickerLocationId.ComputerFolder;
|
||||
picker.FileTypeFilter.Add(".exe");
|
||||
|
||||
var file = await picker.PickSingleFileAsync().AsTask();
|
||||
if (file != null)
|
||||
{
|
||||
CustomShellLocation = file.Path;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task BrowseForWorkingDirectory()
|
||||
{
|
||||
var picker = new FolderPicker();
|
||||
picker.FileTypeFilter.Add(".whatever"); // else a ComException is thrown
|
||||
picker.SuggestedStartLocation = PickerLocationId.ComputerFolder;
|
||||
|
||||
var folder = await picker.PickSingleFolderAsync();
|
||||
if (folder != null)
|
||||
{
|
||||
WorkingDirectory = folder.Path;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RestoreDefaults(string area)
|
||||
{
|
||||
var dialog = new MessageDialog("Are you sure you want to restore default values for this page?", "Please confirm");
|
||||
dialog.Commands.Add(new UICommand("OK"));
|
||||
dialog.Commands.Add(new UICommand("Cancel"));
|
||||
|
||||
var result = await dialog.ShowAsync();
|
||||
|
||||
if (result.Label == "OK" && area == "shell")
|
||||
{
|
||||
var defaults = _defaultValueProvider.GetDefaultShellConfiguration();
|
||||
ShellType = defaults.Shell;
|
||||
CustomShellLocation = defaults.CustomShellLocation;
|
||||
WorkingDirectory = defaults.WorkingDirectory;
|
||||
Arguments = defaults.Arguments;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Views;
|
||||
using FluentTerminal.Models;
|
||||
using GalaSoft.MvvmLight;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.UI.Popups;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class TerminalViewModel : ViewModelBase
|
||||
{
|
||||
private ITerminalView _terminalView;
|
||||
private string _title;
|
||||
private string _resizeOverlayContent;
|
||||
private bool _showResizeOverlay;
|
||||
private DispatcherTimer _resizeOverlayTimer;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ITerminalService _terminalService;
|
||||
private int _terminalId;
|
||||
|
||||
public int Id { get; private set; }
|
||||
|
||||
public bool Initialized { get; private set; }
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
set => Set(ref _title, value);
|
||||
}
|
||||
|
||||
public bool ShowResizeOverlay
|
||||
{
|
||||
get => _showResizeOverlay;
|
||||
set
|
||||
{
|
||||
Set(ref _showResizeOverlay, value);
|
||||
if (value)
|
||||
{
|
||||
if (_resizeOverlayTimer.IsEnabled)
|
||||
{
|
||||
_resizeOverlayTimer.Stop();
|
||||
}
|
||||
_resizeOverlayTimer.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string ResizeOverlayContent
|
||||
{
|
||||
get => _resizeOverlayContent;
|
||||
set => Set(ref _resizeOverlayContent, value);
|
||||
}
|
||||
|
||||
public TerminalViewModel(ISettingsService settingsService, ITerminalService terminalService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_terminalService = terminalService;
|
||||
_resizeOverlayTimer = new DispatcherTimer();
|
||||
_resizeOverlayTimer.Interval = new TimeSpan(0, 0, 2);
|
||||
_resizeOverlayTimer.Tick += OnResizeOverlayTimerFinished;
|
||||
}
|
||||
|
||||
private void OnResizeOverlayTimerFinished(object sender, object e)
|
||||
{
|
||||
_resizeOverlayTimer.Stop();
|
||||
ShowResizeOverlay = false;
|
||||
}
|
||||
|
||||
public async Task OnViewIsReady(ITerminalView terminalView)
|
||||
{
|
||||
_terminalView = terminalView;
|
||||
|
||||
var size = await _terminalView.CreateTerminal();
|
||||
var configuration = _settingsService.GetShellConfiguration();
|
||||
|
||||
var response = await _terminalService.CreateTerminal(size, configuration);
|
||||
|
||||
if (response.Success)
|
||||
{
|
||||
_terminalId = response.Id;
|
||||
_terminalView.TerminalSizeChanged += OnTerminalSizeChanged;
|
||||
_terminalView.TerminalTitleChanged += OnTerminalTitleChanged;
|
||||
|
||||
await _terminalView.ConnectToSocket(response.WebSocketUrl);
|
||||
Initialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var dialog = new MessageDialog(response.Error, "Error");
|
||||
await dialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTerminalTitleChanged(object sender, string e)
|
||||
{
|
||||
Title = e;
|
||||
}
|
||||
|
||||
private async void OnTerminalSizeChanged(object sender, TerminalSize e)
|
||||
{
|
||||
if (!Initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ResizeOverlayContent = $"{e.Columns} x {e.Rows}";
|
||||
ShowResizeOverlay = true;
|
||||
await _terminalService.ResizeTerminal(_terminalId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using FluentTerminal.Models;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Views
|
||||
{
|
||||
public interface ITerminalView
|
||||
{
|
||||
event EventHandler<TerminalSize> TerminalSizeChanged;
|
||||
event EventHandler<string> TerminalTitleChanged;
|
||||
|
||||
Task<TerminalSize> CreateTerminal();
|
||||
Task ConnectToSocket(string url);
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,15 @@
|
||||
x:Class="FluentTerminal.App.Views.MainPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="using:FluentTerminal.App.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Background="{ThemeResource SystemControlAcrylicWindowBrush}"
|
||||
RequestedTheme="Dark"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<converters:TerminalViewModelToViewConverter x:Key="TerminalViewModelToViewConverter" />
|
||||
</Page.Resources>
|
||||
<Grid x:Name="Root">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
@@ -27,19 +31,25 @@
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Canvas.ZIndex="9"
|
||||
IsHitTestVisible="False"
|
||||
Style="{StaticResource WindowTitleTextBlockStyle}"
|
||||
Text="Fluent Terminal" />
|
||||
<!--<Button
|
||||
Text="{x:Bind ViewModel.Title, Mode=OneWay}" />
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Click="OnSettingsButtonClick"
|
||||
Command="{x:Bind ViewModel.ShowSettingsCommand}"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<Viewbox Width="16" Height="16">
|
||||
<SymbolIcon Symbol="Setting" />
|
||||
</Viewbox>
|
||||
</Button>-->
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Frame x:Name="RootFrame" Grid.Row="1" />
|
||||
<ContentControl
|
||||
x:Name="TerminalContainer"
|
||||
Grid.Row="1"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
Content="{x:Bind ViewModel.SelectedTerminal, Mode=OneWay, Converter={StaticResource TerminalViewModelToViewConverter}}" />
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,10 +1,13 @@
|
||||
using System.ComponentModel;
|
||||
using FluentTerminal.App.ViewModels;
|
||||
using System.ComponentModel;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Core.Preview;
|
||||
using Windows.UI.ViewManagement;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace FluentTerminal.App.Views
|
||||
{
|
||||
@@ -31,6 +34,8 @@ namespace FluentTerminal.App.Views
|
||||
}
|
||||
}
|
||||
|
||||
public MainViewModel ViewModel { get; private set; }
|
||||
|
||||
public MainPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -40,11 +45,25 @@ namespace FluentTerminal.App.Views
|
||||
Loaded += OnLoaded;
|
||||
Unloaded += OnUnloaded;
|
||||
Window.Current.Activated += OnWindowActivated;
|
||||
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequest;
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
if (e.Parameter is MainViewModel viewModel)
|
||||
{
|
||||
ViewModel = viewModel;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCloseRequest(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
|
||||
{
|
||||
App.Instance.TerminalWindowClosed();
|
||||
}
|
||||
|
||||
private async void OnWindowActivated(object sender, WindowActivatedEventArgs e)
|
||||
{
|
||||
if (e.WindowActivationState != CoreWindowActivationState.Deactivated && RootFrame.Content is TerminalPage terminal)
|
||||
if (e.WindowActivationState != CoreWindowActivationState.Deactivated && TerminalContainer.Content is TerminalView terminal)
|
||||
{
|
||||
await terminal.FocusWebView();
|
||||
}
|
||||
@@ -59,7 +78,6 @@ namespace FluentTerminal.App.Views
|
||||
{
|
||||
coreTitleBar.LayoutMetricsChanged += OnLayoutMetricsChanged;
|
||||
UpdateLayoutMetrics();
|
||||
RootFrame.Navigate(typeof(TerminalPage));
|
||||
}
|
||||
|
||||
private void OnLayoutMetricsChanged(CoreApplicationViewTitleBar sender, object e)
|
||||
@@ -88,10 +106,5 @@ namespace FluentTerminal.App.Views
|
||||
PropertyChanged(this, new PropertyChangedEventArgs(nameof(CoreTitleBarPadding)));
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnSettingsButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await App.Instance.ShowSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,95 @@
|
||||
x:Class="FluentTerminal.App.Views.SettingsPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="using:FluentTerminal.App.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:FluentTerminal.App.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<TextBlock Text="Settings" />
|
||||
<Page.Resources>
|
||||
<converters:TrueToVisibleConverter x:Key="TrueToVisibleConverter" />
|
||||
<converters:FalseToVisibleConverter x:Key="FalseToVisibleConverter" />
|
||||
</Page.Resources>
|
||||
<Grid x:Name="Root" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<TextBlock
|
||||
x:Name="AppTitle"
|
||||
Canvas.ZIndex="1"
|
||||
IsHitTestVisible="False"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="Settings"
|
||||
Visibility="{Binding IsPaneOpen, ElementName=NavigationView, Converter={StaticResource TrueToVisibleConverter}}" />
|
||||
<NavigationView
|
||||
x:Name="NavigationView"
|
||||
Canvas.ZIndex="0"
|
||||
CompactModeThresholdWidth="560"
|
||||
ExpandedModeThresholdWidth="560"
|
||||
IsSettingsVisible="False"
|
||||
Loaded="NavigationView_Loaded"
|
||||
OpenPaneLength="280"
|
||||
SelectionChanged="NavigationView_SelectionChanged">
|
||||
<NavigationView.MenuItems>
|
||||
<NavigationViewItem
|
||||
Content="General"
|
||||
Icon="Setting"
|
||||
Tag="general" />
|
||||
<NavigationViewItem
|
||||
Content="Key Bindings"
|
||||
Icon="Setting"
|
||||
Tag="keyBindings" />
|
||||
<NavigationViewItem
|
||||
Content="Shell"
|
||||
Icon="Setting"
|
||||
Tag="shell" />
|
||||
<NavigationViewItem
|
||||
Content="Font"
|
||||
Icon="Setting"
|
||||
Tag="font" />
|
||||
<NavigationViewItem
|
||||
Content="Colors"
|
||||
Icon="Setting"
|
||||
Tag="colors" />
|
||||
</NavigationView.MenuItems>
|
||||
<NavigationView.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Margin="24,10,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="{Binding CoreTitleBarHeight}" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Margin="0,-2,0,0"
|
||||
IsHitTestVisible="False"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="Settings"
|
||||
Visibility="{Binding IsPaneOpen, ElementName=NavigationView, Converter={StaticResource FalseToVisibleConverter}}" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="28"
|
||||
Style="{StaticResource TitleTextBlockStyle}"
|
||||
Text="{Binding SelectedItem.Content, ElementName=NavigationView}" />
|
||||
<CommandBar
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Background="{ThemeResource SystemControlBackgroundAltHighBrush}"
|
||||
DefaultLabelPosition="Right">
|
||||
<AppBarButton
|
||||
Command="{Binding ViewModel.RestoreDefaultsCommand}"
|
||||
CommandParameter="{Binding SelectedItem.Tag, ElementName=NavigationView}"
|
||||
Icon="Undo"
|
||||
Label="Restore Defaults" />
|
||||
</CommandBar>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</NavigationView.HeaderTemplate>
|
||||
<Frame x:Name="ContentFrame" Margin="24" />
|
||||
</NavigationView>
|
||||
</Grid>
|
||||
</Page>
|
||||
</Page>
|
||||
@@ -1,12 +1,87 @@
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using FluentTerminal.App.ViewModels;
|
||||
using FluentTerminal.App.Views.SettingsPages;
|
||||
using System.Linq;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Core.Preview;
|
||||
using Windows.UI.ViewManagement;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace FluentTerminal.App.Views
|
||||
{
|
||||
public sealed partial class SettingsPage : Page
|
||||
{
|
||||
public double CoreTitleBarHeight { get; }
|
||||
|
||||
public SettingsViewModel ViewModel { get; private set; }
|
||||
|
||||
public SettingsPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
InitializeComponent();
|
||||
Root.DataContext = this;
|
||||
|
||||
ApplicationViewTitleBar titleBar = ApplicationView.GetForCurrentView().TitleBar;
|
||||
titleBar.ButtonBackgroundColor = Colors.Transparent;
|
||||
titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
|
||||
titleBar.ButtonForegroundColor = (Color)this.Resources["SystemBaseHighColor"];
|
||||
|
||||
ApplicationView.GetForCurrentView().Title = "Settings";
|
||||
|
||||
CoreApplicationViewTitleBar coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
|
||||
coreTitleBar.LayoutMetricsChanged += TitleBar_LayoutMetricsChanged;
|
||||
CoreTitleBarHeight = coreTitleBar.Height;
|
||||
|
||||
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += SettingsPage_CloseRequested;
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
if (e.Parameter is SettingsViewModel viewModel)
|
||||
{
|
||||
ViewModel = viewModel;
|
||||
}
|
||||
}
|
||||
|
||||
private void SettingsPage_CloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
|
||||
{
|
||||
App.Instance.SettingsWindowClosed();
|
||||
}
|
||||
|
||||
private void TitleBar_LayoutMetricsChanged(CoreApplicationViewTitleBar sender, object args)
|
||||
{
|
||||
AppTitle.Margin = new Thickness(CoreApplication.GetCurrentView().TitleBar.SystemOverlayLeftInset + 12, 8, 0, 0);
|
||||
}
|
||||
|
||||
private void NavigationView_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
NavigationView.SelectedItem = NavigationView.MenuItems.Cast<NavigationViewItemBase>().FirstOrDefault(m => m.Tag.ToString() == "shell");
|
||||
}
|
||||
|
||||
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
|
||||
{
|
||||
if (args.SelectedItem is NavigationViewItem item)
|
||||
{
|
||||
switch (item.Tag)
|
||||
{
|
||||
case "general":
|
||||
ContentFrame.Navigate(typeof(GeneralSettings), ViewModel);
|
||||
break;
|
||||
case "shell":
|
||||
ContentFrame.Navigate(typeof(ShellSettings), ViewModel);
|
||||
break;
|
||||
case "colors":
|
||||
ContentFrame.Navigate(typeof(ColorSettings), ViewModel);
|
||||
break;
|
||||
case "font":
|
||||
ContentFrame.Navigate(typeof(FontSettings), ViewModel);
|
||||
break;
|
||||
case "keyBindings":
|
||||
ContentFrame.Navigate(typeof(KeyBindingSettings), ViewModel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<Page
|
||||
x:Class="FluentTerminal.App.Views.SettingsPages.ColorSettings"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:FluentTerminal.App.Views.SettingsPages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" />
|
||||
</Page>
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
// Die Elementvorlage "Leere Seite" wird unter https://go.microsoft.com/fwlink/?LinkId=234238 dokumentiert.
|
||||
|
||||
namespace FluentTerminal.App.Views.SettingsPages
|
||||
{
|
||||
/// <summary>
|
||||
/// Eine leere Seite, die eigenständig verwendet oder zu der innerhalb eines Rahmens navigiert werden kann.
|
||||
/// </summary>
|
||||
public sealed partial class ColorSettings : Page
|
||||
{
|
||||
public ColorSettings()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<Page
|
||||
x:Class="FluentTerminal.App.Views.SettingsPages.FontSettings"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:FluentTerminal.App.Views.SettingsPages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" />
|
||||
</Page>
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
// Die Elementvorlage "Leere Seite" wird unter https://go.microsoft.com/fwlink/?LinkId=234238 dokumentiert.
|
||||
|
||||
namespace FluentTerminal.App.Views.SettingsPages
|
||||
{
|
||||
/// <summary>
|
||||
/// Eine leere Seite, die eigenständig verwendet oder zu der innerhalb eines Rahmens navigiert werden kann.
|
||||
/// </summary>
|
||||
public sealed partial class FontSettings : Page
|
||||
{
|
||||
public FontSettings()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
<Page
|
||||
x:Class="FluentTerminal.App.Views.TerminalPage"
|
||||
x:Class="FluentTerminal.App.Views.SettingsPages.GeneralSettings"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
NavigationCacheMode="Required"
|
||||
mc:Ignorable="d">
|
||||
<Grid x:Name="WebViewContainer" Margin="12" />
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" />
|
||||
</Page>
|
||||
@@ -0,0 +1,12 @@
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace FluentTerminal.App.Views.SettingsPages
|
||||
{
|
||||
public sealed partial class GeneralSettings : Page
|
||||
{
|
||||
public GeneralSettings()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<Page
|
||||
x:Class="FluentTerminal.App.Views.SettingsPages.KeyBindingSettings"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:FluentTerminal.App.Views.SettingsPages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" />
|
||||
</Page>
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
// Die Elementvorlage "Leere Seite" wird unter https://go.microsoft.com/fwlink/?LinkId=234238 dokumentiert.
|
||||
|
||||
namespace FluentTerminal.App.Views.SettingsPages
|
||||
{
|
||||
/// <summary>
|
||||
/// Eine leere Seite, die eigenständig verwendet oder zu der innerhalb eines Rahmens navigiert werden kann.
|
||||
/// </summary>
|
||||
public sealed partial class KeyBindingSettings : Page
|
||||
{
|
||||
public KeyBindingSettings()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<Page
|
||||
x:Class="FluentTerminal.App.Views.SettingsPages.ShellSettings"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
NavigationCacheMode="Required"
|
||||
mc:Ignorable="d">
|
||||
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<TextBlock Margin="0,0,0,8" Text="Shell Type" />
|
||||
<RadioButton Content="PowerShell" IsChecked="{Binding ViewModel.PoweShellIsSelected, Mode=TwoWay}" />
|
||||
<RadioButton Content="CMD" IsChecked="{Binding ViewModel.CMDIsSelected, Mode=TwoWay}" />
|
||||
<RadioButton Content="Custom" IsChecked="{Binding ViewModel.CustomShellIsSelected, Mode=TwoWay}" />
|
||||
|
||||
<StackPanel Margin="0,24,0,0" Orientation="Horizontal">
|
||||
<TextBox
|
||||
Width="400"
|
||||
Header="Custom Shell Location"
|
||||
IsEnabled="{x:Bind ViewModel.CustomShellIsSelected, Mode=OneWay}"
|
||||
Text="{x:Bind ViewModel.CustomShellLocation, Mode=TwoWay}" />
|
||||
<Button
|
||||
Margin="6,0,0,0"
|
||||
VerticalAlignment="Bottom"
|
||||
Command="{x:Bind ViewModel.BrowseForCustomShellCommand}"
|
||||
Content="..."
|
||||
IsEnabled="{x:Bind ViewModel.CustomShellIsSelected, Mode=OneWay}"
|
||||
Style="{StaticResource ButtonRevealStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="0,24,0,0" Orientation="Horizontal">
|
||||
<TextBox
|
||||
Width="400"
|
||||
Header="Working Directory"
|
||||
Text="{x:Bind ViewModel.WorkingDirectory, Mode=TwoWay}" />
|
||||
<Button
|
||||
Margin="6,0,0,0"
|
||||
VerticalAlignment="Bottom"
|
||||
Command="{x:Bind ViewModel.BrowseForWorkingDirectoryCommand}"
|
||||
Content="..."
|
||||
Style="{StaticResource ButtonRevealStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBox
|
||||
Width="400"
|
||||
Margin="0,24,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Header="Arguments"
|
||||
Text="{x:Bind ViewModel.Arguments, Mode=TwoWay}" />
|
||||
|
||||
</StackPanel>
|
||||
</Page>
|
||||
@@ -0,0 +1,24 @@
|
||||
using FluentTerminal.App.ViewModels;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace FluentTerminal.App.Views.SettingsPages
|
||||
{
|
||||
public sealed partial class ShellSettings : Page
|
||||
{
|
||||
public SettingsViewModel ViewModel { get; private set; }
|
||||
|
||||
public ShellSettings()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
if (e.Parameter is SettingsViewModel viewModel)
|
||||
{
|
||||
ViewModel = viewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Core.Preview;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace FluentTerminal.App.Views
|
||||
{
|
||||
public sealed partial class TerminalPage : Page
|
||||
{
|
||||
private WebView _webView;
|
||||
|
||||
public TerminalPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
Loaded += OnLoaded;
|
||||
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequested;
|
||||
}
|
||||
|
||||
public async Task FocusWebView()
|
||||
{
|
||||
if (_webView != null)
|
||||
{
|
||||
_webView.Focus(FocusState.Programmatic);
|
||||
await ExecuteScriptAsync("document.focus();");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
|
||||
{
|
||||
_webView.Navigate(new Uri("about:blank"));
|
||||
_webView = null;
|
||||
}
|
||||
|
||||
public Task<string> ExecuteScriptAsync(string script)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _webView.InvokeScriptAsync("eval", new[]
|
||||
{
|
||||
"try {" + script + "} catch(error) {}"
|
||||
}).AsTask();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine($"Exception while running:\n \"{script}\"\n\n {e}");
|
||||
}
|
||||
return Task.FromResult(string.Empty);
|
||||
}
|
||||
|
||||
private async void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_webView = new WebView(WebViewExecutionMode.SeparateThread)
|
||||
{
|
||||
DefaultBackgroundColor = Colors.Transparent
|
||||
};
|
||||
WebViewContainer.Children.Add(_webView);
|
||||
|
||||
_webView.Navigate(new Uri("http://localhost:9000/Client/index.html"));
|
||||
_webView.Focus(FocusState.Programmatic);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<UserControl
|
||||
x:Class="FluentTerminal.App.Views.TerminalView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="using:FluentTerminal.App.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:FluentTerminal.App.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="400"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<converters:TrueToVisibleConverter x:Key="TrueToVisibleConverter" />
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid x:Name="WebViewContainer" Margin="12" />
|
||||
<Grid
|
||||
Margin="12"
|
||||
Padding="6"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="{ThemeResource SystemControlAccentAcrylicWindowAccentMediumHighBrush}"
|
||||
Visibility="{x:Bind ViewModel.ShowResizeOverlay, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
|
||||
<TextBlock Foreground="White" Text="{x:Bind ViewModel.ResizeOverlayContent, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,143 @@
|
||||
using FluentTerminal.App.ViewModels;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.RuntimeComponent.Interfaces;
|
||||
using FluentTerminal.RuntimeComponent.WebAllowedObjects;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Core.Preview;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace FluentTerminal.App.Views
|
||||
{
|
||||
public sealed partial class TerminalView : UserControl, ITerminalView, ITerminalEventListener
|
||||
{
|
||||
private WebView _webView;
|
||||
|
||||
private SemaphoreSlim _loaded;
|
||||
|
||||
private BlockingCollection<Action> _dispatcherJobs;
|
||||
|
||||
public event EventHandler<TerminalSize> TerminalSizeChanged;
|
||||
public event EventHandler<string> TerminalTitleChanged;
|
||||
|
||||
public TerminalViewModel ViewModel { get; }
|
||||
|
||||
public TerminalView(TerminalViewModel viewModel)
|
||||
{
|
||||
ViewModel = viewModel;
|
||||
InitializeComponent();
|
||||
Loaded += OnLoaded;
|
||||
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequested; //todo move to mainpage
|
||||
_loaded = new SemaphoreSlim(0, 1);
|
||||
|
||||
StartMediatorTask();
|
||||
}
|
||||
|
||||
public async Task FocusWebView()
|
||||
{
|
||||
if (_webView != null)
|
||||
{
|
||||
_webView.Focus(FocusState.Programmatic);
|
||||
await ExecuteScriptAsync("document.focus();");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
|
||||
{
|
||||
_webView.Navigate(new Uri("about:blank"));
|
||||
_webView = null;
|
||||
}
|
||||
|
||||
public Task<string> ExecuteScriptAsync(string script)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _webView.InvokeScriptAsync("eval", new[] { script }).AsTask();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine($"Exception while running:\n \"{script}\"\n\n {e}");
|
||||
}
|
||||
return Task.FromResult(string.Empty);
|
||||
}
|
||||
|
||||
private void StartMediatorTask()
|
||||
{
|
||||
_dispatcherJobs = new BlockingCollection<Action>();
|
||||
var dispatcher = CoreApplication.GetCurrentView().CoreWindow.Dispatcher;
|
||||
Task.Run(async () =>
|
||||
{
|
||||
foreach (var job in _dispatcherJobs.GetConsumingEnumerable())
|
||||
{
|
||||
try
|
||||
{
|
||||
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => job.Invoke());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_webView = new WebView(WebViewExecutionMode.SeparateThread)
|
||||
{
|
||||
DefaultBackgroundColor = Colors.Transparent
|
||||
};
|
||||
WebViewContainer.Children.Add(_webView);
|
||||
|
||||
_webView.NavigationCompleted += _webView_NavigationCompleted;
|
||||
_webView.NavigationStarting += _webView_NavigationStarting;
|
||||
_webView.Navigate(new Uri("http://localhost:9000/Client/index.html"));
|
||||
|
||||
await _loaded.WaitAsync();
|
||||
|
||||
_webView.Focus(FocusState.Programmatic);
|
||||
|
||||
await ViewModel.OnViewIsReady(this);
|
||||
}
|
||||
|
||||
private void _webView_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
|
||||
{
|
||||
_loaded.Release();
|
||||
}
|
||||
|
||||
private void _webView_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
|
||||
{
|
||||
var bridge = new TerminalBridge(this);
|
||||
_webView.AddWebAllowedObject("terminalBridge", bridge);
|
||||
}
|
||||
|
||||
public async Task<TerminalSize> CreateTerminal()
|
||||
{
|
||||
var size = await ExecuteScriptAsync($"createTerminal()");
|
||||
return JsonConvert.DeserializeObject<TerminalSize>(size);
|
||||
}
|
||||
|
||||
public Task ConnectToSocket(string url)
|
||||
{
|
||||
return ExecuteScriptAsync($"connectToWebSocket('{url}');");
|
||||
}
|
||||
|
||||
public void OnTerminalResized(int columns, int rows)
|
||||
{
|
||||
_dispatcherJobs.Add(() => TerminalSizeChanged?.Invoke(this, new TerminalSize { Columns = columns, Rows = rows }));
|
||||
}
|
||||
|
||||
public void OnTitleChanged(string title)
|
||||
{
|
||||
_dispatcherJobs.Add(() => TerminalTitleChanged?.Invoke(this, title));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,49 +86,49 @@ __WEBPACK_IMPORTED_MODULE_0__xterm_js_build_xterm__["applyAddon"](__WEBPACK_IMPO
|
||||
__WEBPACK_IMPORTED_MODULE_0__xterm_js_build_xterm__["applyAddon"](__WEBPACK_IMPORTED_MODULE_2__xterm_js_build_addons_fit_fit__);
|
||||
__WEBPACK_IMPORTED_MODULE_0__xterm_js_build_xterm__["applyAddon"](__WEBPACK_IMPORTED_MODULE_3__xterm_js_build_addons_winptyCompat_winptyCompat__);
|
||||
|
||||
var term, protocol, socketURL, socket;
|
||||
var term, socket;
|
||||
var terminalContainer = document.getElementById('terminal-container');
|
||||
|
||||
createTerminal();
|
||||
|
||||
function createTerminal() {
|
||||
function start() {
|
||||
while (terminalContainer.children.length) {
|
||||
terminalContainer.removeChild(terminalContainer.children[0]);
|
||||
}
|
||||
|
||||
term = new __WEBPACK_IMPORTED_MODULE_0__xterm_js_build_xterm__({
|
||||
cursorBlink: true,
|
||||
fontFamily: 'consolas',
|
||||
fontSize: 13,
|
||||
allowTransparency: true,
|
||||
theme: {
|
||||
background: "transparent"
|
||||
background: "transparent"
|
||||
}
|
||||
});
|
||||
window.term = term; // Expose `term` to window for debugging purposes
|
||||
|
||||
window.term = term;
|
||||
|
||||
term.on('resize', function (size) {
|
||||
if (!socketURL) {
|
||||
return;
|
||||
}
|
||||
var cols = size.cols,
|
||||
rows = size.rows,
|
||||
url = 'http://localhost:9000/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows;
|
||||
fetch(url, {method: 'POST'});
|
||||
terminalBridge.notifySizeChanged(term.cols, term.rows);
|
||||
});
|
||||
|
||||
term.on('title', function (title) {
|
||||
terminalBridge.notifyTitleChanged(title);
|
||||
});
|
||||
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
|
||||
|
||||
term.open(terminalContainer);
|
||||
term.winptyCompatInit();
|
||||
term.fit();
|
||||
term.focus();
|
||||
|
||||
fetch('http://localhost:9000/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) {
|
||||
console.log("got response");
|
||||
res.text().then(function (url) {
|
||||
socketURL = JSON.parse(url);
|
||||
socket = new WebSocket(socketURL);
|
||||
socket.onopen = runRealTerminal;
|
||||
});
|
||||
});
|
||||
var resizeTimeout;
|
||||
window.onresize = function () {
|
||||
clearTimeout(resizeTimeout);
|
||||
resizeTimeout = setTimeout(term.fit(), 500);
|
||||
}
|
||||
|
||||
return JSON.stringify({
|
||||
rows: term.rows,
|
||||
columns: term.cols
|
||||
});
|
||||
}
|
||||
|
||||
function runRealTerminal() {
|
||||
@@ -136,6 +136,18 @@ function runRealTerminal() {
|
||||
term._initialized = true;
|
||||
}
|
||||
|
||||
function createTerminal(configuration) {
|
||||
return start();
|
||||
}
|
||||
|
||||
function connectToWebSocket(url) {
|
||||
socket = new WebSocket(url);
|
||||
socket.onopen = runRealTerminal;
|
||||
}
|
||||
|
||||
window.createTerminal = createTerminal;
|
||||
window.connectToWebSocket = connectToWebSocket;
|
||||
|
||||
|
||||
/***/ }),
|
||||
/* 1 */
|
||||
|
||||
@@ -7,52 +7,64 @@ Terminal.applyAddon(attach);
|
||||
Terminal.applyAddon(fit);
|
||||
Terminal.applyAddon(winptyCompat);
|
||||
|
||||
var term, protocol, socketURL, socket;
|
||||
var term, socket;
|
||||
var terminalContainer = document.getElementById('terminal-container');
|
||||
|
||||
createTerminal();
|
||||
|
||||
function createTerminal() {
|
||||
function start() {
|
||||
while (terminalContainer.children.length) {
|
||||
terminalContainer.removeChild(terminalContainer.children[0]);
|
||||
}
|
||||
|
||||
term = new Terminal({
|
||||
cursorBlink: true,
|
||||
fontFamily: 'consolas',
|
||||
fontSize: 13,
|
||||
allowTransparency: true,
|
||||
theme: {
|
||||
background: "transparent"
|
||||
background: "transparent"
|
||||
}
|
||||
});
|
||||
window.term = term; // Expose `term` to window for debugging purposes
|
||||
|
||||
window.term = term;
|
||||
|
||||
term.on('resize', function (size) {
|
||||
if (!socketURL) {
|
||||
return;
|
||||
}
|
||||
var cols = size.cols,
|
||||
rows = size.rows,
|
||||
url = 'http://localhost:9000/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows;
|
||||
fetch(url, {method: 'POST'});
|
||||
terminalBridge.notifySizeChanged(term.cols, term.rows);
|
||||
});
|
||||
|
||||
term.on('title', function (title) {
|
||||
terminalBridge.notifyTitleChanged(title);
|
||||
});
|
||||
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
|
||||
|
||||
term.open(terminalContainer);
|
||||
term.winptyCompatInit();
|
||||
term.fit();
|
||||
term.focus();
|
||||
|
||||
fetch('http://localhost:9000/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) {
|
||||
console.log("got response");
|
||||
res.text().then(function (url) {
|
||||
socketURL = JSON.parse(url);
|
||||
socket = new WebSocket(socketURL);
|
||||
socket.onopen = runRealTerminal;
|
||||
});
|
||||
});
|
||||
var resizeTimeout;
|
||||
window.onresize = function () {
|
||||
clearTimeout(resizeTimeout);
|
||||
resizeTimeout = setTimeout(term.fit(), 500);
|
||||
}
|
||||
|
||||
return JSON.stringify({
|
||||
rows: term.rows,
|
||||
columns: term.cols
|
||||
});
|
||||
}
|
||||
|
||||
function runRealTerminal() {
|
||||
term.attach(socket);
|
||||
term._initialized = true;
|
||||
}
|
||||
|
||||
function createTerminal(configuration) {
|
||||
return start();
|
||||
}
|
||||
|
||||
function connectToWebSocket(url) {
|
||||
socket = new WebSocket(url);
|
||||
socket.onopen = runRealTerminal;
|
||||
}
|
||||
|
||||
window.createTerminal = createTerminal;
|
||||
window.connectToWebSocket = connectToWebSocket;
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace FluentTerminal.Models
|
||||
{
|
||||
public class CreateTerminalRequest
|
||||
{
|
||||
public TerminalSize Size { get; set; }
|
||||
public ShellConfiguration Configuration { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace FluentTerminal.Models
|
||||
{
|
||||
public class CreateTerminalResponse
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public bool Success { get; set; }
|
||||
public string WebSocketUrl { get; set; }
|
||||
public string Error { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace FluentTerminal.Models.Enums
|
||||
{
|
||||
public enum ShellType
|
||||
{
|
||||
CMD,
|
||||
PowerShell,
|
||||
Custom
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,12 @@
|
||||
using FluentTerminal.Models.Enums;
|
||||
|
||||
namespace FluentTerminal.Models
|
||||
{
|
||||
public class ShellConfiguration
|
||||
{
|
||||
public string Arguments { get; set; }
|
||||
public ShellType Shell { get; set; }
|
||||
public string CustomShellLocation { get; set; }
|
||||
public string WorkingDirectory { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace FluentTerminal.Models
|
||||
{
|
||||
public class TerminalSize
|
||||
{
|
||||
public int Columns { get; set; }
|
||||
public int Rows { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{BAAE6239-343F-454C-977F-F81F91715961}</ProjectGuid>
|
||||
<OutputType>winmdobj</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>FluentTerminal.RuntimeComponent</RootNamespace>
|
||||
<AssemblyName>FluentTerminal.RuntimeComponent</AssemblyName>
|
||||
<DefaultLanguage>de-DE</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.16299.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.16299.0</TargetPlatformMinVersion>
|
||||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<AllowCrossPlatformRetargeting>false</AllowCrossPlatformRetargeting>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\ARM\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<OutputPath>bin\ARM\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Interfaces\ITerminalEventListener.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="WebAllowedObjects\TerminalBridge.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>6.0.6</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.RuntimeComponent.Interfaces
|
||||
{
|
||||
public interface ITerminalEventListener
|
||||
{
|
||||
void OnTerminalResized(int columns, int rows);
|
||||
|
||||
void OnTitleChanged(string title);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// 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("FluentTerminal.RuntimeComponent")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("FluentTerminal.RuntimeComponent")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// 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")]
|
||||
[assembly: ComVisible(false)]
|
||||
@@ -0,0 +1,26 @@
|
||||
using FluentTerminal.RuntimeComponent.Interfaces;
|
||||
using Windows.Foundation.Metadata;
|
||||
|
||||
namespace FluentTerminal.RuntimeComponent.WebAllowedObjects
|
||||
{
|
||||
[AllowForWeb]
|
||||
public sealed class TerminalBridge
|
||||
{
|
||||
private readonly ITerminalEventListener _terminalEventListener;
|
||||
|
||||
public TerminalBridge(ITerminalEventListener terminalEventListener)
|
||||
{
|
||||
_terminalEventListener = terminalEventListener;
|
||||
}
|
||||
|
||||
public void NotifySizeChanged(int columns, int rows)
|
||||
{
|
||||
_terminalEventListener.OnTerminalResized(columns, rows);
|
||||
}
|
||||
|
||||
public void NotifyTitleChanged(string title)
|
||||
{
|
||||
_terminalEventListener.OnTitleChanged(title);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,49 +86,49 @@ __WEBPACK_IMPORTED_MODULE_0__xterm_js_build_xterm__["applyAddon"](__WEBPACK_IMPO
|
||||
__WEBPACK_IMPORTED_MODULE_0__xterm_js_build_xterm__["applyAddon"](__WEBPACK_IMPORTED_MODULE_2__xterm_js_build_addons_fit_fit__);
|
||||
__WEBPACK_IMPORTED_MODULE_0__xterm_js_build_xterm__["applyAddon"](__WEBPACK_IMPORTED_MODULE_3__xterm_js_build_addons_winptyCompat_winptyCompat__);
|
||||
|
||||
var term, protocol, socketURL, socket;
|
||||
var term, socket;
|
||||
var terminalContainer = document.getElementById('terminal-container');
|
||||
|
||||
createTerminal();
|
||||
|
||||
function createTerminal() {
|
||||
function start() {
|
||||
while (terminalContainer.children.length) {
|
||||
terminalContainer.removeChild(terminalContainer.children[0]);
|
||||
}
|
||||
|
||||
term = new __WEBPACK_IMPORTED_MODULE_0__xterm_js_build_xterm__({
|
||||
cursorBlink: true,
|
||||
fontFamily: 'consolas',
|
||||
fontSize: 13,
|
||||
allowTransparency: true,
|
||||
theme: {
|
||||
background: "transparent"
|
||||
background: "transparent"
|
||||
}
|
||||
});
|
||||
window.term = term; // Expose `term` to window for debugging purposes
|
||||
|
||||
window.term = term;
|
||||
|
||||
term.on('resize', function (size) {
|
||||
if (!socketURL) {
|
||||
return;
|
||||
}
|
||||
var cols = size.cols,
|
||||
rows = size.rows,
|
||||
url = 'http://localhost:9000/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows;
|
||||
fetch(url, {method: 'POST'});
|
||||
terminalBridge.notifySizeChanged(term.cols, term.rows);
|
||||
});
|
||||
|
||||
term.on('title', function (title) {
|
||||
terminalBridge.notifyTitleChanged(title);
|
||||
});
|
||||
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
|
||||
|
||||
term.open(terminalContainer);
|
||||
term.winptyCompatInit();
|
||||
term.fit();
|
||||
term.focus();
|
||||
|
||||
fetch('http://localhost:9000/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) {
|
||||
console.log("got response");
|
||||
res.text().then(function (url) {
|
||||
socketURL = JSON.parse(url);
|
||||
socket = new WebSocket(socketURL);
|
||||
socket.onopen = runRealTerminal;
|
||||
});
|
||||
});
|
||||
var resizeTimeout;
|
||||
window.onresize = function () {
|
||||
clearTimeout(resizeTimeout);
|
||||
resizeTimeout = setTimeout(term.fit(), 500);
|
||||
}
|
||||
|
||||
return JSON.stringify({
|
||||
rows: term.rows,
|
||||
columns: term.cols
|
||||
});
|
||||
}
|
||||
|
||||
function runRealTerminal() {
|
||||
@@ -136,6 +136,18 @@ function runRealTerminal() {
|
||||
term._initialized = true;
|
||||
}
|
||||
|
||||
function createTerminal(configuration) {
|
||||
return start();
|
||||
}
|
||||
|
||||
function connectToWebSocket(url) {
|
||||
socket = new WebSocket(url);
|
||||
socket.onopen = runRealTerminal;
|
||||
}
|
||||
|
||||
window.createTerminal = createTerminal;
|
||||
window.connectToWebSocket = connectToWebSocket;
|
||||
|
||||
|
||||
/***/ }),
|
||||
/* 1 */
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using FluentTerminal.SystemTray.DataTransferObjects;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.SystemTray.Services;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Web.Http;
|
||||
|
||||
namespace FluentTerminal.SystemTray.Controllers
|
||||
@@ -17,17 +15,17 @@ namespace FluentTerminal.SystemTray.Controllers
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("terminals")]
|
||||
public string Post()
|
||||
[Route("terminals/{id}/resize")]
|
||||
public void Resize(int id, [FromBody]TerminalSize size)
|
||||
{
|
||||
var cols = int.Parse(this.Request.GetQueryNameValuePairs().SingleOrDefault(q => q.Key == "cols").Value);
|
||||
var rows = int.Parse(this.Request.GetQueryNameValuePairs().SingleOrDefault(q => q.Key == "rows").Value);
|
||||
var options = new TerminalOptions
|
||||
{
|
||||
Columns = cols,
|
||||
Rows = rows
|
||||
};
|
||||
return _terminalsManager.CreateTerminal(options);
|
||||
_terminalsManager.ResizeTerminal(id, size);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("terminals")]
|
||||
public CreateTerminalResponse Create([FromBody]CreateTerminalRequest request)
|
||||
{
|
||||
return _terminalsManager.CreateTerminal(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace FluentTerminal.SystemTray.DataTransferObjects
|
||||
{
|
||||
public class TerminalOptions
|
||||
{
|
||||
public int Rows { get; set; }
|
||||
public int Columns { get; set; }
|
||||
public string PWD { get; set; }
|
||||
public string ShellExecutable { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -113,11 +113,10 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Controllers\TerminalsController.cs" />
|
||||
<Compile Include="DataTransferObjects\TerminalOptions.cs" />
|
||||
<Compile Include="LoopbackEnabler.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Services\Terminal.cs" />
|
||||
<Compile Include="Services\TerminalSession.cs" />
|
||||
<Compile Include="Services\TerminalsManager.cs" />
|
||||
<Compile Include="Startup.cs" />
|
||||
<Compile Include="SystemTrayApplicationContext.cs" />
|
||||
@@ -186,7 +185,12 @@
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj">
|
||||
<Project>{5EA40453-A0AA-449E-8406-D6EA51D7E70E}</Project>
|
||||
<Name>FluentTerminal.Models</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>xcopy /y "$(ProjectDir)BinaryDependencies\$(PlatformName)" "$(ProjectDir)bin\$(PlatformName)\$(ConfigurationName)"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Fleck;
|
||||
using FluentTerminal.SystemTray.DataTransferObjects;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
@@ -10,23 +11,16 @@ using static winpty.WinPty;
|
||||
|
||||
namespace FluentTerminal.SystemTray.Services
|
||||
{
|
||||
public class Terminal : IDisposable
|
||||
public class TerminalSession : IDisposable
|
||||
{
|
||||
private ManualResetEventSlim _connectedEvent;
|
||||
private bool _disposedValue;
|
||||
private IntPtr _handle;
|
||||
private Stream _stdin;
|
||||
private Stream _stdout;
|
||||
private bool _disposedValue;
|
||||
private ManualResetEventSlim _connectedEvent;
|
||||
|
||||
private IWebSocketConnection _webSocket;
|
||||
|
||||
public int Id { get; }
|
||||
|
||||
public string WebSocketUrl { get; }
|
||||
|
||||
public event EventHandler ConnectionClosed;
|
||||
|
||||
public Terminal(TerminalOptions options)
|
||||
public TerminalSession(CreateTerminalRequest request)
|
||||
{
|
||||
var configHandle = IntPtr.Zero;
|
||||
var spawnConfigHandle = IntPtr.Zero;
|
||||
@@ -35,21 +29,21 @@ namespace FluentTerminal.SystemTray.Services
|
||||
try
|
||||
{
|
||||
configHandle = winpty_config_new(WINPTY_FLAG_COLOR_ESCAPES, out errorHandle);
|
||||
winpty_config_set_initial_size(configHandle, options.Columns, options.Rows);
|
||||
winpty_config_set_initial_size(configHandle, request.Size.Columns, request.Size.Rows);
|
||||
|
||||
_handle = winpty_open(configHandle, out errorHandle);
|
||||
if (errorHandle != IntPtr.Zero)
|
||||
{
|
||||
throw new Exception(winpty_error_code(errorHandle).ToString());
|
||||
throw new Exception(winpty_error_msg(errorHandle).ToString());
|
||||
}
|
||||
|
||||
string exe = @"C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe";
|
||||
string args = "";
|
||||
string cwd = @"C:\";
|
||||
string exe = GetShellLocation(request.Configuration);
|
||||
string args = $"\"{request.Configuration.Arguments}\"";
|
||||
string cwd = GetWorkingDirectory(request.Configuration);
|
||||
spawnConfigHandle = winpty_spawn_config_new(WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, exe, args, cwd, null, out errorHandle);
|
||||
if (errorHandle != IntPtr.Zero)
|
||||
{
|
||||
throw new Exception(winpty_error_code(errorHandle).ToString());
|
||||
throw new Exception(winpty_error_msg(errorHandle).ToString());
|
||||
}
|
||||
|
||||
_stdin = CreatePipe(winpty_conin_name(_handle), PipeDirection.Out);
|
||||
@@ -57,7 +51,7 @@ namespace FluentTerminal.SystemTray.Services
|
||||
|
||||
if (!winpty_spawn(_handle, spawnConfigHandle, out IntPtr process, out IntPtr thread, out int procError, out errorHandle))
|
||||
{
|
||||
throw new Exception(winpty_error_code(errorHandle).ToString());
|
||||
throw new Exception(winpty_error_msg(errorHandle).ToString());
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -67,7 +61,6 @@ namespace FluentTerminal.SystemTray.Services
|
||||
winpty_error_free(errorHandle);
|
||||
}
|
||||
|
||||
|
||||
var port = Utilities.GetAvailablePort(49151);
|
||||
|
||||
if (port == null)
|
||||
@@ -102,6 +95,79 @@ namespace FluentTerminal.SystemTray.Services
|
||||
ListenToStdOut();
|
||||
}
|
||||
|
||||
~TerminalSession()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
private string GetShellLocation(ShellConfiguration configuration)
|
||||
{
|
||||
switch (configuration.Shell)
|
||||
{
|
||||
case ShellType.CMD:
|
||||
return @"C:\Windows\System32\cmd.exe";
|
||||
case ShellType.PowerShell:
|
||||
return @"C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe";
|
||||
case ShellType.Custom:
|
||||
return configuration.CustomShellLocation;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetWorkingDirectory(ShellConfiguration configuration)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(configuration.WorkingDirectory) || !Directory.Exists(configuration.WorkingDirectory))
|
||||
{
|
||||
return Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||
}
|
||||
return configuration.WorkingDirectory;
|
||||
}
|
||||
|
||||
public event EventHandler ConnectionClosed;
|
||||
|
||||
public int Id { get; }
|
||||
public string WebSocketUrl { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public void Resize(TerminalSize size)
|
||||
{
|
||||
var errorHandle = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
winpty_set_size(_handle, size.Columns, size.Rows, out errorHandle);
|
||||
if (errorHandle != IntPtr.Zero)
|
||||
{
|
||||
throw new Exception(winpty_error_msg(errorHandle).ToString());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
winpty_error_free(errorHandle);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_stdin?.Dispose();
|
||||
_stdout?.Dispose();
|
||||
}
|
||||
|
||||
winpty_free(_handle);
|
||||
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
private Stream CreatePipe(string pipeName, PipeDirection direction)
|
||||
{
|
||||
string serverName = ".";
|
||||
@@ -143,32 +209,5 @@ namespace FluentTerminal.SystemTray.Services
|
||||
while (!reader.EndOfStream);
|
||||
});
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_stdin?.Dispose();
|
||||
_stdout?.Dispose();
|
||||
}
|
||||
|
||||
winpty_free(_handle);
|
||||
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
~Terminal()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,57 @@
|
||||
using FluentTerminal.SystemTray.DataTransferObjects;
|
||||
using FluentTerminal.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace FluentTerminal.SystemTray.Services
|
||||
{
|
||||
public class TerminalsManager
|
||||
{
|
||||
private Dictionary<int, Terminal> _terminals = new Dictionary<int, Terminal>();
|
||||
private Dictionary<int, TerminalSession> _terminals = new Dictionary<int, TerminalSession>();
|
||||
|
||||
public TerminalsManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public string CreateTerminal(TerminalOptions options)
|
||||
public CreateTerminalResponse CreateTerminal(CreateTerminalRequest request)
|
||||
{
|
||||
var terminal = new Terminal(options);
|
||||
TerminalSession terminal;
|
||||
try
|
||||
{
|
||||
terminal = new TerminalSession(request);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return new CreateTerminalResponse { Error = e.Message };
|
||||
}
|
||||
|
||||
terminal.ConnectionClosed += OnTerminalConnectionClosed;
|
||||
_terminals.Add(terminal.Id, terminal);
|
||||
return terminal.WebSocketUrl;
|
||||
|
||||
return new CreateTerminalResponse
|
||||
{
|
||||
Success = true,
|
||||
Id = terminal.Id,
|
||||
WebSocketUrl = terminal.WebSocketUrl
|
||||
};
|
||||
}
|
||||
|
||||
public void ResizeTerminal(int id, TerminalSize size)
|
||||
{
|
||||
if (_terminals.TryGetValue(id, out TerminalSession terminal))
|
||||
{
|
||||
terminal.Resize(size);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine($"ResizeTerminal: terminal with id '{id}' was not found");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTerminalConnectionClosed(object sender, System.EventArgs e)
|
||||
{
|
||||
if (sender is Terminal terminal)
|
||||
if (sender is TerminalSession terminal)
|
||||
{
|
||||
_terminals.Remove(terminal.Id);
|
||||
terminal.Dispose();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Owin;
|
||||
using System.Web.Http;
|
||||
using Unity;
|
||||
using Unity.Lifetime;
|
||||
|
||||
namespace FluentTerminal.SystemTray
|
||||
{
|
||||
@@ -13,11 +14,10 @@ namespace FluentTerminal.SystemTray
|
||||
config.MapHttpAttributeRoutes();
|
||||
|
||||
var container = new UnityContainer();
|
||||
container.RegisterType<TerminalsManager>();
|
||||
container.RegisterType<TerminalsManager>(new ContainerControlledLifetimeManager());
|
||||
|
||||
config.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
|
||||
|
||||
//appBuilder.UseFileServer()
|
||||
appBuilder.UseStaticFiles("/Client");
|
||||
appBuilder.UseWebApi(config);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
@@ -18,7 +19,9 @@ namespace FluentTerminal.SystemTray
|
||||
public SystemTrayApplicationContext()
|
||||
|
||||
{
|
||||
var openMenuItem = new MenuItem("Open App", new EventHandler(OpenApp));
|
||||
var openMenuItem = new MenuItem("Open", new EventHandler(OpenApp));
|
||||
var newWindowItem = new MenuItem("New Window", new EventHandler(NewWindow));
|
||||
var settingsMenuItem = new MenuItem("Show Settings", new EventHandler(ShowSettings));
|
||||
var exitMenuItem = new MenuItem("Exit", new EventHandler(Exit));
|
||||
|
||||
openMenuItem.DefaultItem = true;
|
||||
@@ -27,7 +30,7 @@ namespace FluentTerminal.SystemTray
|
||||
notifyIcon.DoubleClick += OpenApp;
|
||||
notifyIcon.Text = "Fluent Terminal";
|
||||
notifyIcon.Icon = Properties.Resources.Square44x44Logo_scale_100;
|
||||
notifyIcon.ContextMenu = new ContextMenu(new MenuItem[] { openMenuItem, exitMenuItem });
|
||||
notifyIcon.ContextMenu = new ContextMenu(new MenuItem[] { openMenuItem, newWindowItem, settingsMenuItem, exitMenuItem });
|
||||
notifyIcon.Visible = true;
|
||||
}
|
||||
|
||||
@@ -37,8 +40,17 @@ namespace FluentTerminal.SystemTray
|
||||
await appListEntries.First().LaunchAsync();
|
||||
}
|
||||
|
||||
private async void Exit(object sender, EventArgs e)
|
||||
private void ShowSettings(object sender, EventArgs e)
|
||||
{
|
||||
Process.Start("flute.exe", "settings");
|
||||
}
|
||||
|
||||
private void NewWindow(object sender, EventArgs e)
|
||||
{
|
||||
Process.Start("flute.exe", "new");
|
||||
}
|
||||
|
||||
private async void Exit(object sender, EventArgs e)
|
||||
{
|
||||
var message = new ValueSet
|
||||
{
|
||||
|
||||
@@ -6,10 +6,15 @@ MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentTerminal.App", "FluentTerminal.App\FluentTerminal.App.csproj", "{41020100-3414-4E0A-9A8E-92D3C4179DF8}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{9EDDE318-0003-4553-B6F9-3FE5473563FD} = {9EDDE318-0003-4553-B6F9-3FE5473563FD}
|
||||
{BAAE6239-343F-454C-977F-F81F91715961} = {BAAE6239-343F-454C-977F-F81F91715961}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentTerminal.SystemTray", "FluentTerminal.SystemTray\FluentTerminal.SystemTray.csproj", "{9EDDE318-0003-4553-B6F9-3FE5473563FD}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentTerminal.RuntimeComponent", "FluentTerminal.RuntimeComponent\FluentTerminal.RuntimeComponent.csproj", "{BAAE6239-343F-454C-977F-F81F91715961}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentTerminal.Models", "FluentTerminal.Models\FluentTerminal.Models.csproj", "{5EA40453-A0AA-449E-8406-D6EA51D7E70E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -58,6 +63,38 @@ Global
|
||||
{9EDDE318-0003-4553-B6F9-3FE5473563FD}.Release|x64.Build.0 = Release|x64
|
||||
{9EDDE318-0003-4553-B6F9-3FE5473563FD}.Release|x86.ActiveCfg = Release|x86
|
||||
{9EDDE318-0003-4553-B6F9-3FE5473563FD}.Release|x86.Build.0 = Release|x86
|
||||
{BAAE6239-343F-454C-977F-F81F91715961}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BAAE6239-343F-454C-977F-F81F91715961}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BAAE6239-343F-454C-977F-F81F91715961}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{BAAE6239-343F-454C-977F-F81F91715961}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{BAAE6239-343F-454C-977F-F81F91715961}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{BAAE6239-343F-454C-977F-F81F91715961}.Debug|x64.Build.0 = Debug|x64
|
||||
{BAAE6239-343F-454C-977F-F81F91715961}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{BAAE6239-343F-454C-977F-F81F91715961}.Debug|x86.Build.0 = Debug|x86
|
||||
{BAAE6239-343F-454C-977F-F81F91715961}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BAAE6239-343F-454C-977F-F81F91715961}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BAAE6239-343F-454C-977F-F81F91715961}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{BAAE6239-343F-454C-977F-F81F91715961}.Release|ARM.Build.0 = Release|ARM
|
||||
{BAAE6239-343F-454C-977F-F81F91715961}.Release|x64.ActiveCfg = Release|x64
|
||||
{BAAE6239-343F-454C-977F-F81F91715961}.Release|x64.Build.0 = Release|x64
|
||||
{BAAE6239-343F-454C-977F-F81F91715961}.Release|x86.ActiveCfg = Release|x86
|
||||
{BAAE6239-343F-454C-977F-F81F91715961}.Release|x86.Build.0 = Release|x86
|
||||
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{5EA40453-A0AA-449E-8406-D6EA51D7E70E}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||