diff --git a/App/App.csproj b/App/App.csproj
new file mode 100644
index 0000000..7a9ec8f
--- /dev/null
+++ b/App/App.csproj
@@ -0,0 +1,48 @@
+
+
+ WinExe
+ net8.0-windows10.0.19041.0
+ 10.0.17763.0
+ Coder.Desktop.App
+ app.manifest
+ x86;x64;ARM64
+ win-x86;win-x64;win-arm64
+ true
+ enable
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+ MSBuild:Compile
+
+
+
+
+
+ MSBuild:Compile
+
+
+
+
+
+ False
+ True
+ False
+ True
+
+
diff --git a/App/App.xaml b/App/App.xaml
new file mode 100644
index 0000000..a5b6d8b
--- /dev/null
+++ b/App/App.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/App/App.xaml.cs b/App/App.xaml.cs
new file mode 100644
index 0000000..4fbde75
--- /dev/null
+++ b/App/App.xaml.cs
@@ -0,0 +1,29 @@
+using Microsoft.UI.Xaml;
+
+namespace Coder.Desktop.App;
+
+public partial class App : Application
+{
+ private TrayWindow? TrayWindow;
+
+ public App()
+ {
+ InitializeComponent();
+ }
+
+ private bool HandleClosedEvents { get; } = true;
+
+ protected override void OnLaunched(LaunchActivatedEventArgs args)
+ {
+ TrayWindow = new TrayWindow();
+ TrayWindow.Closed += (sender, args) =>
+ {
+ // TODO: wire up HandleClosedEvents properly
+ if (HandleClosedEvents)
+ {
+ args.Handled = true;
+ TrayWindow.AppWindow.Hide();
+ }
+ };
+ }
+}
diff --git a/App/Assets/coder_icon_32_dark.ico b/App/Assets/coder_icon_32_dark.ico
new file mode 100644
index 0000000..4eaa1bb
Binary files /dev/null and b/App/Assets/coder_icon_32_dark.ico differ
diff --git a/App/Assets/coder_icon_32_light.ico b/App/Assets/coder_icon_32_light.ico
new file mode 100644
index 0000000..1fc307f
Binary files /dev/null and b/App/Assets/coder_icon_32_light.ico differ
diff --git a/App/HorizontalRule.xaml b/App/HorizontalRule.xaml
new file mode 100644
index 0000000..706e9b7
--- /dev/null
+++ b/App/HorizontalRule.xaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/App/HorizontalRule.xaml.cs b/App/HorizontalRule.xaml.cs
new file mode 100644
index 0000000..ffb86dc
--- /dev/null
+++ b/App/HorizontalRule.xaml.cs
@@ -0,0 +1,11 @@
+using Microsoft.UI.Xaml.Controls;
+
+namespace Coder.Desktop.App;
+
+public sealed partial class HorizontalRule : UserControl
+{
+ public HorizontalRule()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/App/TrayIcon.xaml b/App/TrayIcon.xaml
new file mode 100644
index 0000000..7f5dede
--- /dev/null
+++ b/App/TrayIcon.xaml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/App/TrayIcon.xaml.cs b/App/TrayIcon.xaml.cs
new file mode 100644
index 0000000..db62e79
--- /dev/null
+++ b/App/TrayIcon.xaml.cs
@@ -0,0 +1,45 @@
+using System.Diagnostics;
+using System.Windows.Input;
+using Windows.UI.ViewManagement;
+using DependencyPropertyGenerator;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Media.Imaging;
+
+namespace Coder.Desktop.App;
+
+[DependencyProperty("OpenCommand")]
+[DependencyProperty("ExitCommand")]
+public sealed partial class TrayIcon : UserControl
+{
+ private readonly UISettings _uiSettings = new();
+
+ public TrayIcon()
+ {
+ InitializeComponent();
+ _uiSettings.ColorValuesChanged += OnColorValuesChanged;
+ UpdateTrayIconBasedOnTheme();
+ }
+
+ private void OnColorValuesChanged(UISettings sender, object args)
+ {
+ DispatcherQueue.TryEnqueue(UpdateTrayIconBasedOnTheme);
+ }
+
+ private void UpdateTrayIconBasedOnTheme()
+ {
+ var currentTheme = Application.Current.RequestedTheme;
+ Debug.WriteLine("Theme update requested, found theme: " + currentTheme);
+
+ switch (currentTheme)
+ {
+ case ApplicationTheme.Dark:
+ TaskbarIcon.IconSource = (BitmapImage)Resources["IconDarkTheme"];
+ break;
+ case ApplicationTheme.Light:
+ default:
+ TaskbarIcon.IconSource = (BitmapImage)Resources["IconLightTheme"];
+ break;
+ }
+ }
+}
diff --git a/App/TrayWindow.xaml b/App/TrayWindow.xaml
new file mode 100644
index 0000000..a779bb5
--- /dev/null
+++ b/App/TrayWindow.xaml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/App/TrayWindow.xaml.cs b/App/TrayWindow.xaml.cs
new file mode 100644
index 0000000..9e172b4
--- /dev/null
+++ b/App/TrayWindow.xaml.cs
@@ -0,0 +1,323 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using Windows.ApplicationModel.DataTransfer;
+using Windows.Foundation;
+using Windows.Graphics;
+using Windows.System;
+using Windows.UI;
+using Windows.UI.Core;
+using CommunityToolkit.Mvvm.Input;
+using Microsoft.UI;
+using Microsoft.UI.Input;
+using Microsoft.UI.Windowing;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Controls.Primitives;
+using Microsoft.UI.Xaml.Documents;
+using Microsoft.UI.Xaml.Media;
+using WinRT.Interop;
+using WindowActivatedEventArgs = Microsoft.UI.Xaml.WindowActivatedEventArgs;
+
+namespace Coder.Desktop.App;
+
+public enum AgentStatus
+{
+ Green,
+ Red,
+ Gray,
+}
+
+public partial class Agent
+{
+ public required string Hostname { get; set; } // without suffix
+ public required string Suffix { get; set; }
+ public AgentStatus Status { get; set; }
+
+ public Brush StatusColor => Status switch
+ {
+ AgentStatus.Green => new SolidColorBrush(Color.FromArgb(255, 52, 199, 89)),
+ AgentStatus.Red => new SolidColorBrush(Color.FromArgb(255, 255, 59, 48)),
+ _ => new SolidColorBrush(Color.FromArgb(255, 142, 142, 147)),
+ };
+
+ [RelayCommand]
+ private void AgentHostnameButton_Click()
+ {
+ try
+ {
+ Process.Start(new ProcessStartInfo
+ {
+ // TODO: this should probably be more robust instead of just joining strings
+ FileName = "http://" + Hostname + Suffix,
+ UseShellExecute = true,
+ });
+ }
+ catch
+ {
+ // TODO: log (notify?)
+ }
+ }
+
+ [RelayCommand]
+ private void AgentHostnameCopyButton_Click(object parameter)
+ {
+ var dataPackage = new DataPackage
+ {
+ RequestedOperation = DataPackageOperation.Copy,
+ };
+ dataPackage.SetText(Hostname + Suffix);
+ Clipboard.SetContent(dataPackage);
+
+ if (parameter is not FrameworkElement frameworkElement) return;
+
+ var flyout = new Flyout
+ {
+ Content = new TextBlock
+ {
+ Text = "DNS Copied",
+ Margin = new Thickness(4),
+ },
+ };
+ FlyoutBase.SetAttachedFlyout(frameworkElement, flyout);
+ FlyoutBase.ShowAttachedFlyout(frameworkElement);
+ }
+
+ public void AgentHostnameText_OnLoaded(object sender, RoutedEventArgs e)
+ {
+ if (sender is not TextBlock textBlock) return;
+ textBlock.Inlines.Clear();
+ textBlock.Inlines.Add(new Run
+ {
+ Text = Hostname,
+ Foreground =
+ (SolidColorBrush)Application.Current.Resources.ThemeDictionaries[
+ "DefaultTextForegroundThemeBrush"],
+ });
+ textBlock.Inlines.Add(new Run
+ {
+ Text = Suffix,
+ Foreground =
+ (SolidColorBrush)Application.Current.Resources.ThemeDictionaries[
+ "SystemControlForegroundBaseMediumBrush"],
+ });
+ }
+}
+
+public sealed partial class TrayWindow : Window
+{
+ private const int WIDTH = 300;
+
+ private NativeApi.POINT? _lastActivatePosition;
+
+ public ObservableCollection Agents =
+ [
+ new()
+ {
+ Hostname = "coder2",
+ Suffix = ".coder",
+ Status = AgentStatus.Green,
+ },
+ new()
+ {
+ Hostname = "coder3",
+ Suffix = ".coder",
+ Status = AgentStatus.Red,
+ },
+ new()
+ {
+ Hostname = "coder4",
+ Suffix = ".coder",
+ Status = AgentStatus.Gray,
+ },
+ new()
+ {
+ Hostname = "superlongworkspacenamewhyisitsolong",
+ Suffix = ".coder",
+ Status = AgentStatus.Gray,
+ },
+ ];
+
+ public TrayWindow()
+ {
+ InitializeComponent();
+ AppWindow.Hide();
+ SystemBackdrop = new DesktopAcrylicBackdrop();
+ Activated += Window_Activated;
+
+ // Setting OpenCommand and ExitCommand directly in the .xaml doesn't seem to work for whatever reason.
+ TrayIcon.OpenCommand = Tray_OpenCommand;
+ TrayIcon.ExitCommand = Tray_ExitCommand;
+
+ if (Content is FrameworkElement frameworkElement)
+ frameworkElement.SizeChanged += Content_SizeChanged;
+ else
+ throw new Exception("Failed to get Content as FrameworkElement for window");
+
+ // Hide the title bar and buttons. WinUi 3 provides a method to do this with
+ // `ExtendsContentIntoTitleBar = true;`, but it automatically adds emulated title bar buttons that cannot be
+ // removed.
+ if (AppWindow.Presenter is not OverlappedPresenter presenter)
+ throw new Exception("Failed to get OverlappedPresenter for window");
+ presenter.IsMaximizable = false;
+ presenter.IsMinimizable = false;
+ presenter.IsResizable = false;
+ presenter.IsAlwaysOnTop = true;
+ presenter.SetBorderAndTitleBar(true, false);
+ AppWindow.IsShownInSwitchers = false;
+
+ // Ensure the corner is rounded.
+ var windowHandle = Win32Interop.GetWindowFromWindowId(AppWindow.Id);
+ var value = 2;
+ var result = NativeApi.DwmSetWindowAttribute(windowHandle, 33, ref value, Marshal.SizeOf());
+ if (result != 0) throw new Exception("Failed to set window corner preference");
+ }
+
+ private void Content_SizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ ResizeWindow();
+ MoveWindow();
+ }
+
+ private void ResizeWindow()
+ {
+ if (Content is not FrameworkElement content)
+ throw new Exception("Failed to get Content as FrameworkElement for window");
+
+ // Measure the desired size of the content
+ content.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
+ var desiredSize = content.DesiredSize;
+
+ // Adjust the AppWindow size
+ var scale = GetDisplayScale();
+ var height = (int)(desiredSize.Height * scale);
+ AppWindow.Resize(new SizeInt32(WIDTH, height));
+ }
+
+ private double GetDisplayScale()
+ {
+ var hwnd = WindowNative.GetWindowHandle(this);
+ var dpi = NativeApi.GetDpiForWindow(hwnd);
+ if (dpi == 0) return 1; // assume scale of 1
+ return dpi / 96.0; // 96 DPI == 1
+ }
+
+ public void MoveResizeAndActivate()
+ {
+ SaveCursorPos();
+ ResizeWindow();
+ MoveWindow();
+ AppWindow.Show();
+ NativeApi.SetForegroundWindow(WindowNative.GetWindowHandle(this));
+ }
+
+ private void SaveCursorPos()
+ {
+ var res = NativeApi.GetCursorPos(out var cursorPosition);
+ if (res)
+ _lastActivatePosition = cursorPosition;
+ else
+ // When the cursor position is null, we will spawn the window in
+ // the bottom right corner of the primary display.
+ // TODO: log(?) an error when this happens
+ _lastActivatePosition = null;
+ }
+
+ private void MoveWindow()
+ {
+ AppWindow.Move(GetWindowPosition());
+ }
+
+ private PointInt32 GetWindowPosition()
+ {
+ var height = AppWindow.Size.Height;
+ var cursorPosition = _lastActivatePosition;
+ if (cursorPosition is null)
+ {
+ var primaryWorkArea = DisplayArea.Primary.WorkArea;
+ return new PointInt32(
+ primaryWorkArea.Width - WIDTH,
+ primaryWorkArea.Height - height
+ );
+ }
+
+ // Spawn the window to the top right of the cursor.
+ var x = cursorPosition.Value.X + 10;
+ var y = cursorPosition.Value.Y - 10 - height;
+
+ var workArea = DisplayArea.GetFromPoint(
+ new PointInt32(cursorPosition.Value.X, cursorPosition.Value.Y),
+ DisplayAreaFallback.Primary
+ ).WorkArea;
+
+ // Adjust if the window goes off the right edge of the display.
+ if (x + WIDTH > workArea.X + workArea.Width) x = workArea.X + workArea.Width - WIDTH;
+
+ // Adjust if the window goes off the bottom edge of the display.
+ if (y + height > workArea.Y + workArea.Height) y = workArea.Y + workArea.Height - height;
+
+ // Adjust if the window goes off the left edge of the display (somehow).
+ if (x < workArea.X) x = workArea.X;
+
+ // Adjust if the window goes off the top edge of the display (somehow).
+ if (y < workArea.Y) y = workArea.Y;
+
+ return new PointInt32(x, y);
+ }
+
+ private void Window_Activated(object sender, WindowActivatedEventArgs e)
+ {
+ // Close the window as soon as it loses focus.
+ if (e.WindowActivationState == WindowActivationState.Deactivated
+#if DEBUG
+ // In DEBUG, holding SHIFT is required to have the window close when it loses focus.
+ && InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down)
+#endif
+ )
+ AppWindow.Hide();
+ }
+
+ private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
+ {
+ Agents.Add(new Agent
+ {
+ Hostname = "cool",
+ Suffix = ".coder",
+ Status = AgentStatus.Gray,
+ });
+ }
+
+ [RelayCommand]
+ private void Tray_Open()
+ {
+ MoveResizeAndActivate();
+ }
+
+ [RelayCommand]
+ private void Tray_Exit()
+ {
+ // TODO: implement exit
+ }
+
+ public class NativeApi
+ {
+ [DllImport("dwmapi.dll")]
+ public static extern int DwmSetWindowAttribute(IntPtr hwnd, int attribute, ref int value, int size);
+
+ [DllImport("user32.dll")]
+ public static extern bool GetCursorPos(out POINT lpPoint);
+
+ [DllImport("user32.dll")]
+ public static extern bool SetForegroundWindow(IntPtr hwnd);
+
+ [DllImport("user32.dll")]
+ public static extern int GetDpiForWindow(IntPtr hwnd);
+
+ public struct POINT
+ {
+ public int X;
+ public int Y;
+ }
+ }
+}
diff --git a/App/app.manifest b/App/app.manifest
new file mode 100644
index 0000000..28bdcff
--- /dev/null
+++ b/App/app.manifest
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PerMonitorV2
+
+
+
diff --git a/Coder.Desktop.sln b/Coder.Desktop.sln
index c5fc598..e25c8fa 100644
--- a/Coder.Desktop.sln
+++ b/Coder.Desktop.sln
@@ -1,53 +1,192 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-#
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coder.Desktop.Vpn", "Vpn\Vpn.csproj", "{B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}"
+# Visual Studio Version 17
+VisualStudioVersion = 17.12.35707.178 d17.12
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vpn", "Vpn\Vpn.csproj", "{B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coder.Desktop.Vpn.Proto", "Vpn.Proto\Vpn.Proto.csproj", "{318E78BB-E6AD-410F-8F3F-B680F6880293}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vpn.Proto", "Vpn.Proto\Vpn.Proto.csproj", "{318E78BB-E6AD-410F-8F3F-B680F6880293}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coder.Desktop.Vpn.Service", "Vpn.Service\Vpn.Service.csproj", "{51B91794-0A2A-4F84-9935-8E17DD2AB260}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vpn.Service", "Vpn.Service\Vpn.Service.csproj", "{51B91794-0A2A-4F84-9935-8E17DD2AB260}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coder.Desktop.Tests.Vpn", "Tests.Vpn\Tests.Vpn.csproj", "{D247B2E7-38A0-4A69-A710-7E8FAA7B807E}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Vpn", "Tests.Vpn\Tests.Vpn.csproj", "{D247B2E7-38A0-4A69-A710-7E8FAA7B807E}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coder.Desktop.Tests.Vpn.Proto", "Tests.Vpn.Proto\Tests.Vpn.Proto.csproj", "{AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Vpn.Proto", "Tests.Vpn.Proto\Tests.Vpn.Proto.csproj", "{AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coder.Desktop.Tests.Vpn.Service", "Tests.Vpn.Service\Tests.Vpn.Service.csproj", "{D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Vpn.Service", "Tests.Vpn.Service\Tests.Vpn.Service.csproj", "{D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coder.Desktop.CoderSdk", "CoderSdk\CoderSdk.csproj", "{A3D2B2B3-A051-46BD-A190-5487A9F24C28}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoderSdk", "CoderSdk\CoderSdk.csproj", "{A3D2B2B3-A051-46BD-A190-5487A9F24C28}"
+EndProject
+Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "Package", "Package\Package.wapproj", "{C184988D-56E0-451F-B6A1-E5FE0405C80B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "App\App.csproj", "{800C7E2D-0D86-4554-9903-B879DA6FA2CE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
+ Debug|ARM64 = Debug|ARM64
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
+ Release|ARM64 = Release|ARM64
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}.Debug|x64.Build.0 = Debug|Any CPU
+ {B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}.Debug|x86.Build.0 = Debug|Any CPU
{B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}.Release|ARM64.Build.0 = Release|Any CPU
+ {B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}.Release|x64.ActiveCfg = Release|Any CPU
+ {B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}.Release|x64.Build.0 = Release|Any CPU
+ {B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}.Release|x86.ActiveCfg = Release|Any CPU
+ {B342F896-C721-4AA5-A0F6-0BFA8EBAFACB}.Release|x86.Build.0 = Release|Any CPU
{318E78BB-E6AD-410F-8F3F-B680F6880293}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{318E78BB-E6AD-410F-8F3F-B680F6880293}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {318E78BB-E6AD-410F-8F3F-B680F6880293}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {318E78BB-E6AD-410F-8F3F-B680F6880293}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {318E78BB-E6AD-410F-8F3F-B680F6880293}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {318E78BB-E6AD-410F-8F3F-B680F6880293}.Debug|x64.Build.0 = Debug|Any CPU
+ {318E78BB-E6AD-410F-8F3F-B680F6880293}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {318E78BB-E6AD-410F-8F3F-B680F6880293}.Debug|x86.Build.0 = Debug|Any CPU
{318E78BB-E6AD-410F-8F3F-B680F6880293}.Release|Any CPU.ActiveCfg = Release|Any CPU
{318E78BB-E6AD-410F-8F3F-B680F6880293}.Release|Any CPU.Build.0 = Release|Any CPU
+ {318E78BB-E6AD-410F-8F3F-B680F6880293}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {318E78BB-E6AD-410F-8F3F-B680F6880293}.Release|ARM64.Build.0 = Release|Any CPU
+ {318E78BB-E6AD-410F-8F3F-B680F6880293}.Release|x64.ActiveCfg = Release|Any CPU
+ {318E78BB-E6AD-410F-8F3F-B680F6880293}.Release|x64.Build.0 = Release|Any CPU
+ {318E78BB-E6AD-410F-8F3F-B680F6880293}.Release|x86.ActiveCfg = Release|Any CPU
+ {318E78BB-E6AD-410F-8F3F-B680F6880293}.Release|x86.Build.0 = Release|Any CPU
{51B91794-0A2A-4F84-9935-8E17DD2AB260}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{51B91794-0A2A-4F84-9935-8E17DD2AB260}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {51B91794-0A2A-4F84-9935-8E17DD2AB260}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {51B91794-0A2A-4F84-9935-8E17DD2AB260}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {51B91794-0A2A-4F84-9935-8E17DD2AB260}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {51B91794-0A2A-4F84-9935-8E17DD2AB260}.Debug|x64.Build.0 = Debug|Any CPU
+ {51B91794-0A2A-4F84-9935-8E17DD2AB260}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {51B91794-0A2A-4F84-9935-8E17DD2AB260}.Debug|x86.Build.0 = Debug|Any CPU
{51B91794-0A2A-4F84-9935-8E17DD2AB260}.Release|Any CPU.ActiveCfg = Release|Any CPU
{51B91794-0A2A-4F84-9935-8E17DD2AB260}.Release|Any CPU.Build.0 = Release|Any CPU
+ {51B91794-0A2A-4F84-9935-8E17DD2AB260}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {51B91794-0A2A-4F84-9935-8E17DD2AB260}.Release|ARM64.Build.0 = Release|Any CPU
+ {51B91794-0A2A-4F84-9935-8E17DD2AB260}.Release|x64.ActiveCfg = Release|Any CPU
+ {51B91794-0A2A-4F84-9935-8E17DD2AB260}.Release|x64.Build.0 = Release|Any CPU
+ {51B91794-0A2A-4F84-9935-8E17DD2AB260}.Release|x86.ActiveCfg = Release|Any CPU
+ {51B91794-0A2A-4F84-9935-8E17DD2AB260}.Release|x86.Build.0 = Release|Any CPU
{D247B2E7-38A0-4A69-A710-7E8FAA7B807E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D247B2E7-38A0-4A69-A710-7E8FAA7B807E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D247B2E7-38A0-4A69-A710-7E8FAA7B807E}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {D247B2E7-38A0-4A69-A710-7E8FAA7B807E}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {D247B2E7-38A0-4A69-A710-7E8FAA7B807E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D247B2E7-38A0-4A69-A710-7E8FAA7B807E}.Debug|x64.Build.0 = Debug|Any CPU
+ {D247B2E7-38A0-4A69-A710-7E8FAA7B807E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D247B2E7-38A0-4A69-A710-7E8FAA7B807E}.Debug|x86.Build.0 = Debug|Any CPU
{D247B2E7-38A0-4A69-A710-7E8FAA7B807E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D247B2E7-38A0-4A69-A710-7E8FAA7B807E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D247B2E7-38A0-4A69-A710-7E8FAA7B807E}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {D247B2E7-38A0-4A69-A710-7E8FAA7B807E}.Release|ARM64.Build.0 = Release|Any CPU
+ {D247B2E7-38A0-4A69-A710-7E8FAA7B807E}.Release|x64.ActiveCfg = Release|Any CPU
+ {D247B2E7-38A0-4A69-A710-7E8FAA7B807E}.Release|x64.Build.0 = Release|Any CPU
+ {D247B2E7-38A0-4A69-A710-7E8FAA7B807E}.Release|x86.ActiveCfg = Release|Any CPU
+ {D247B2E7-38A0-4A69-A710-7E8FAA7B807E}.Release|x86.Build.0 = Release|Any CPU
{AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}.Debug|x64.Build.0 = Debug|Any CPU
+ {AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}.Debug|x86.Build.0 = Debug|Any CPU
{AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}.Release|ARM64.Build.0 = Release|Any CPU
+ {AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}.Release|x64.ActiveCfg = Release|Any CPU
+ {AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}.Release|x64.Build.0 = Release|Any CPU
+ {AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}.Release|x86.ActiveCfg = Release|Any CPU
+ {AA3EEFF4-414B-4A83-8ACF-188C3C61CCE1}.Release|x86.Build.0 = Release|Any CPU
{D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}.Debug|x64.Build.0 = Debug|Any CPU
+ {D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}.Debug|x86.Build.0 = Debug|Any CPU
{D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}.Release|ARM64.Build.0 = Release|Any CPU
+ {D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}.Release|x64.ActiveCfg = Release|Any CPU
+ {D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}.Release|x64.Build.0 = Release|Any CPU
+ {D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}.Release|x86.ActiveCfg = Release|Any CPU
+ {D32E5FE1-C251-4A08-8EBE-B8D4F18A36F1}.Release|x86.Build.0 = Release|Any CPU
{A3D2B2B3-A051-46BD-A190-5487A9F24C28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A3D2B2B3-A051-46BD-A190-5487A9F24C28}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A3D2B2B3-A051-46BD-A190-5487A9F24C28}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {A3D2B2B3-A051-46BD-A190-5487A9F24C28}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {A3D2B2B3-A051-46BD-A190-5487A9F24C28}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A3D2B2B3-A051-46BD-A190-5487A9F24C28}.Debug|x64.Build.0 = Debug|Any CPU
+ {A3D2B2B3-A051-46BD-A190-5487A9F24C28}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A3D2B2B3-A051-46BD-A190-5487A9F24C28}.Debug|x86.Build.0 = Debug|Any CPU
{A3D2B2B3-A051-46BD-A190-5487A9F24C28}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A3D2B2B3-A051-46BD-A190-5487A9F24C28}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A3D2B2B3-A051-46BD-A190-5487A9F24C28}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {A3D2B2B3-A051-46BD-A190-5487A9F24C28}.Release|ARM64.Build.0 = Release|Any CPU
+ {A3D2B2B3-A051-46BD-A190-5487A9F24C28}.Release|x64.ActiveCfg = Release|Any CPU
+ {A3D2B2B3-A051-46BD-A190-5487A9F24C28}.Release|x64.Build.0 = Release|Any CPU
+ {A3D2B2B3-A051-46BD-A190-5487A9F24C28}.Release|x86.ActiveCfg = Release|Any CPU
+ {A3D2B2B3-A051-46BD-A190-5487A9F24C28}.Release|x86.Build.0 = Release|Any CPU
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Debug|Any CPU.Build.0 = Debug|x64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Debug|Any CPU.Deploy.0 = Debug|x64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Debug|ARM64.Build.0 = Debug|ARM64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Debug|x64.ActiveCfg = Debug|x64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Debug|x64.Build.0 = Debug|x64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Debug|x64.Deploy.0 = Debug|x64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Debug|x86.ActiveCfg = Debug|x86
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Debug|x86.Build.0 = Debug|x86
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Debug|x86.Deploy.0 = Debug|x86
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Release|Any CPU.ActiveCfg = Release|x64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Release|Any CPU.Build.0 = Release|x64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Release|Any CPU.Deploy.0 = Release|x64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Release|ARM64.ActiveCfg = Release|ARM64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Release|ARM64.Build.0 = Release|ARM64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Release|ARM64.Deploy.0 = Release|ARM64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Release|x64.ActiveCfg = Release|x64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Release|x64.Build.0 = Release|x64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Release|x64.Deploy.0 = Release|x64
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Release|x86.ActiveCfg = Release|x86
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Release|x86.Build.0 = Release|x86
+ {C184988D-56E0-451F-B6A1-E5FE0405C80B}.Release|x86.Deploy.0 = Release|x86
+ {800C7E2D-0D86-4554-9903-B879DA6FA2CE}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {800C7E2D-0D86-4554-9903-B879DA6FA2CE}.Debug|Any CPU.Build.0 = Debug|x64
+ {800C7E2D-0D86-4554-9903-B879DA6FA2CE}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {800C7E2D-0D86-4554-9903-B879DA6FA2CE}.Debug|ARM64.Build.0 = Debug|ARM64
+ {800C7E2D-0D86-4554-9903-B879DA6FA2CE}.Debug|x64.ActiveCfg = Debug|x64
+ {800C7E2D-0D86-4554-9903-B879DA6FA2CE}.Debug|x64.Build.0 = Debug|x64
+ {800C7E2D-0D86-4554-9903-B879DA6FA2CE}.Debug|x86.ActiveCfg = Debug|x86
+ {800C7E2D-0D86-4554-9903-B879DA6FA2CE}.Debug|x86.Build.0 = Debug|x86
+ {800C7E2D-0D86-4554-9903-B879DA6FA2CE}.Release|Any CPU.ActiveCfg = Release|x64
+ {800C7E2D-0D86-4554-9903-B879DA6FA2CE}.Release|Any CPU.Build.0 = Release|x64
+ {800C7E2D-0D86-4554-9903-B879DA6FA2CE}.Release|ARM64.ActiveCfg = Release|ARM64
+ {800C7E2D-0D86-4554-9903-B879DA6FA2CE}.Release|ARM64.Build.0 = Release|ARM64
+ {800C7E2D-0D86-4554-9903-B879DA6FA2CE}.Release|x64.ActiveCfg = Release|x64
+ {800C7E2D-0D86-4554-9903-B879DA6FA2CE}.Release|x64.Build.0 = Release|x64
+ {800C7E2D-0D86-4554-9903-B879DA6FA2CE}.Release|x86.ActiveCfg = Release|x86
+ {800C7E2D-0D86-4554-9903-B879DA6FA2CE}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
diff --git a/CoderSdk/CoderSdk.csproj b/CoderSdk/CoderSdk.csproj
index 3a63532..1ca7d3c 100644
--- a/CoderSdk/CoderSdk.csproj
+++ b/CoderSdk/CoderSdk.csproj
@@ -1,6 +1,7 @@
-
+
+ Coder.Desktop.CoderSdk
net8.0
enable
enable
diff --git a/Package/Images/SplashScreen.scale-200.png b/Package/Images/SplashScreen.scale-200.png
new file mode 100644
index 0000000..32f486a
Binary files /dev/null and b/Package/Images/SplashScreen.scale-200.png differ
diff --git a/Package/Images/Square150x150Logo.scale-200.png b/Package/Images/Square150x150Logo.scale-200.png
new file mode 100644
index 0000000..53ee377
Binary files /dev/null and b/Package/Images/Square150x150Logo.scale-200.png differ
diff --git a/Package/Images/Square44x44Logo.scale-200.png b/Package/Images/Square44x44Logo.scale-200.png
new file mode 100644
index 0000000..f713bba
Binary files /dev/null and b/Package/Images/Square44x44Logo.scale-200.png differ
diff --git a/Package/Package.appxmanifest b/Package/Package.appxmanifest
new file mode 100644
index 0000000..679c072
--- /dev/null
+++ b/Package/Package.appxmanifest
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+ App (Package)
+ dean
+ Images\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Package/Package.wapproj b/Package/Package.wapproj
new file mode 100644
index 0000000..10fb751
--- /dev/null
+++ b/Package/Package.wapproj
@@ -0,0 +1,67 @@
+
+
+
+ 15.0
+
+
+
+ Debug
+ x86
+
+
+ Release
+ x86
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+ Debug
+ ARM64
+
+
+ Release
+ ARM64
+
+
+
+ $(MSBuildExtensionsPath)\Microsoft\DesktopBridge\
+ App\
+
+
+
+ c184988d-56e0-451f-b6a1-e5fe0405c80b
+ 10.0.22621.0
+ 10.0.17763.0
+ net8.0-windows$(TargetPlatformVersion);$(AssetTargetFallback)
+ en-US
+ false
+ ..\App\App.csproj
+
+
+
+ Designer
+
+
+
+
+
+
+
+
+
+ True
+ Properties\PublishProfiles\win-$(Platform).pubxml
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Tests.Vpn.Service/TestHttpServer.cs b/Tests.Vpn.Service/TestHttpServer.cs
index d33697f..4129b0d 100644
--- a/Tests.Vpn.Service/TestHttpServer.cs
+++ b/Tests.Vpn.Service/TestHttpServer.cs
@@ -15,8 +15,6 @@ public class TestHttpServer : IDisposable
private readonly HttpListener _listener;
private readonly Thread _listenerThread;
- public string BaseUrl { get; private set; }
-
public TestHttpServer(Action handler) : this(ctx =>
{
handler(ctx);
@@ -77,6 +75,8 @@ public TestHttpServer(Func handler)
_listenerThread.Start();
}
+ public string BaseUrl { get; private set; }
+
public void Dispose()
{
_cts.Cancel();
diff --git a/Tests.Vpn/SpeakerTest.cs b/Tests.Vpn/SpeakerTest.cs
index 51950f7..0b1552b 100644
--- a/Tests.Vpn/SpeakerTest.cs
+++ b/Tests.Vpn/SpeakerTest.cs
@@ -16,6 +16,13 @@ internal class FailableStream : Stream
private readonly TaskCompletionSource _writeTcs = new();
+ public FailableStream(Stream inner, Exception? writeException, Exception? readException)
+ {
+ _inner = inner;
+ if (writeException != null) _writeTcs.SetException(writeException);
+ if (readException != null) _readTcs.SetException(readException);
+ }
+
public override bool CanRead => _inner.CanRead;
public override bool CanSeek => _inner.CanSeek;
public override bool CanWrite => _inner.CanWrite;
@@ -27,13 +34,6 @@ public override long Position
set => _inner.Position = value;
}
- public FailableStream(Stream inner, Exception? writeException, Exception? readException)
- {
- _inner = inner;
- if (writeException != null) _writeTcs.SetException(writeException);
- if (readException != null) _readTcs.SetException(readException);
- }
-
public void SetWriteException(Exception ex)
{
_writeTcs.SetException(ex);
diff --git a/Vpn.Proto/RpcHeader.cs b/Vpn.Proto/RpcHeader.cs
index cf7ffcc..c81eb1d 100644
--- a/Vpn.Proto/RpcHeader.cs
+++ b/Vpn.Proto/RpcHeader.cs
@@ -9,9 +9,6 @@ public class RpcHeader
{
private const string Preamble = "codervpn";
- public string Role { get; }
- public RpcVersionList VersionList { get; }
-
/// Role of the peer
/// Version of the peer
public RpcHeader(string role, RpcVersionList versionList)
@@ -20,6 +17,9 @@ public RpcHeader(string role, RpcVersionList versionList)
VersionList = versionList;
}
+ public string Role { get; }
+ public RpcVersionList VersionList { get; }
+
///
/// Parse a header string into a SpeakerHeader.
///
diff --git a/Vpn.Proto/RpcMessage.cs b/Vpn.Proto/RpcMessage.cs
index bfe4d82..fc1af11 100644
--- a/Vpn.Proto/RpcMessage.cs
+++ b/Vpn.Proto/RpcMessage.cs
@@ -6,12 +6,12 @@ namespace Coder.Desktop.Vpn.Proto;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class RpcRoleAttribute : Attribute
{
- public string Role { get; }
-
public RpcRoleAttribute(string role)
{
Role = role;
}
+
+ public string Role { get; }
}
///
diff --git a/Vpn.Proto/RpcVersion.cs b/Vpn.Proto/RpcVersion.cs
index 574768d..8eb0de0 100644
--- a/Vpn.Proto/RpcVersion.cs
+++ b/Vpn.Proto/RpcVersion.cs
@@ -7,9 +7,6 @@ public class RpcVersion
{
public static readonly RpcVersion Current = new(1, 0);
- public ulong Major { get; }
- public ulong Minor { get; }
-
/// The major version of the peer
/// The minor version of the peer
public RpcVersion(ulong major, ulong minor)
@@ -18,6 +15,9 @@ public RpcVersion(ulong major, ulong minor)
Minor = minor;
}
+ public ulong Major { get; }
+ public ulong Minor { get; }
+
///
/// Parse a string in the format "major.minor" into an ApiVersion.
///
diff --git a/Vpn.Service/Downloader.cs b/Vpn.Service/Downloader.cs
index 83eda24..ea4c587 100644
--- a/Vpn.Service/Downloader.cs
+++ b/Vpn.Service/Downloader.cs
@@ -42,13 +42,13 @@ public class AuthenticodeDownloadValidator : IDownloadValidator
{
private readonly string _expectedName;
- public static AuthenticodeDownloadValidator Coder => new("Coder Technologies Inc.");
-
public AuthenticodeDownloadValidator(string expectedName)
{
_expectedName = expectedName;
}
+ public static AuthenticodeDownloadValidator Coder => new("Coder Technologies Inc.");
+
public async Task ValidateAsync(string path, CancellationToken ct = default)
{
FileSignatureInfo fileSigInfo;
@@ -187,13 +187,6 @@ public class DownloadTask
public readonly HttpRequestMessage Request;
public readonly string TempDestinationPath;
- public ulong? TotalBytes { get; private set; }
- public ulong BytesRead { get; private set; }
- public Task Task { get; private set; } = null!; // Set in EnsureStartedAsync
-
- public double? Progress => TotalBytes == null ? null : (double)BytesRead / TotalBytes.Value;
- public bool IsCompleted => Task.IsCompleted;
-
internal DownloadTask(ILogger logger, HttpRequestMessage req, string destinationPath, IDownloadValidator validator)
{
_logger = logger;
@@ -216,6 +209,13 @@ internal DownloadTask(ILogger logger, HttpRequestMessage req, string destination
".download-" + Path.GetRandomFileName());
}
+ public ulong? TotalBytes { get; private set; }
+ public ulong BytesRead { get; private set; }
+ public Task Task { get; private set; } = null!; // Set in EnsureStartedAsync
+
+ public double? Progress => TotalBytes == null ? null : (double)BytesRead / TotalBytes.Value;
+ public bool IsCompleted => Task.IsCompleted;
+
internal async Task EnsureStartedAsync(CancellationToken ct = default)
{
using var _ = await _semaphore.LockAsync(ct);
diff --git a/Vpn/Speaker.cs b/Vpn/Speaker.cs
index 4c6ef3c..899c346 100644
--- a/Vpn/Speaker.cs
+++ b/Vpn/Speaker.cs
@@ -27,14 +27,6 @@ public class ReplyableRpcMessage : RpcMessage
private readonly TR _message;
private readonly Speaker _speaker;
- public override RPC? RpcField
- {
- get => _message.RpcField;
- set => _message.RpcField = value;
- }
-
- public override TR Message => _message;
-
/// Speaker to use for sending reply
/// Original received message
public ReplyableRpcMessage(Speaker speaker, TR message)
@@ -43,6 +35,14 @@ public ReplyableRpcMessage(Speaker speaker, TR message)
_message = message;
}
+ public override RPC? RpcField
+ {
+ get => _message.RpcField;
+ set => _message.RpcField = value;
+ }
+
+ public override TR Message => _message;
+
public override void Validate()
{
_message.Validate();
diff --git a/Vpn/Utilities/BidirectionalPipe.cs b/Vpn/Utilities/BidirectionalPipe.cs
index 72e633b..0792ce8 100644
--- a/Vpn/Utilities/BidirectionalPipe.cs
+++ b/Vpn/Utilities/BidirectionalPipe.cs
@@ -10,6 +10,14 @@ public class BidirectionalPipe : Stream
private readonly Stream _reader;
private readonly Stream _writer;
+ /// The stream to perform reads from
+ /// The stream to write data to
+ public BidirectionalPipe(Stream reader, Stream writer)
+ {
+ _reader = reader;
+ _writer = writer;
+ }
+
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => true;
@@ -21,14 +29,6 @@ public override long Position
set => throw new NotImplementedException("BidirectionalPipe does not support setting position");
}
- /// The stream to perform reads from
- /// The stream to write data to
- public BidirectionalPipe(Stream reader, Stream writer)
- {
- _reader = reader;
- _writer = writer;
- }
-
///
/// Creates a new pair of BidirectionalPipes that are connected to each other using buffered in-memory pipes.
///