2using System.Collections.Generic;
4using System.Drawing.Imaging;
6using System.Threading.Tasks;
8using System.Windows.Controls;
9using System.Windows.Input;
10using System.Windows.Media;
11using System.Windows.Media.Imaging;
25 private readonly LinkedList<(Pending, int, int)> queue =
new LinkedList<(Pending,
int,
int)>();
29 private readonly
object synchObj =
new object();
31 private Pending[,] pendingTiles =
null;
32 private WriteableBitmap desktop =
null;
33 private DateTime updateScreenTimer;
36 private bool drawing =
false;
37 private bool disposeRdpClient;
44 public Pending(
string Base64)
49 public Pending(
byte[] Bin)
59 this.rdpClient = RdpClient;
60 this.disposeRdpClient = DisposeRdpClient;
64 this.Focusable =
true;
73 if (!(this.session is
null))
75 this.session.StateChanged -= this.Session_StateChanged;
76 this.session.TileUpdated -= this.Session_TileUpdated;
77 this.session.ScanComplete -= this.Session_ScanComplete;
82 this.session.StateChanged += this.Session_StateChanged;
83 this.session.TileUpdated += this.Session_TileUpdated;
84 this.session.ScanComplete += this.Session_ScanComplete;
88 private Task Session_StateChanged(
object Sender, EventArgs e)
92 int ScreenWidth = this.session.
Width;
93 int ScreenHeight = this.session.
Height;
94 this.columns = (ScreenWidth + this.session.TileSize - 1) / this.session.
TileSize;
95 this.rows = (ScreenHeight +
this.session.TileSize - 1) / this.session.
TileSize;
99 this.pendingTiles =
new Pending[this.rows, this.columns];
101 foreach ((Pending Tile,
int X,
int Y) in this.queue)
102 this.pendingTiles[Y, X] = Tile;
108 return Task.CompletedTask;
111 private void Session_TileUpdated(
object Sender,
TileEventArgs e)
115 if (this.pendingTiles is
null)
116 this.queue.AddLast((
new Pending(e.
TileBase64), e.
X, e.
Y));
118 this.pendingTiles[e.
Y, e.
X] =
new Pending(e.
TileBase64);
120 if (this.updateScreenTimer == DateTime.MinValue)
121 this.updateScreenTimer =
MainWindow.Scheduler.Add(DateTime.Now.AddMilliseconds(250),
this.UpdateScreen,
null);
125 private Task Session_ScanComplete(
object Sender, EventArgs e)
127 this.UpdateScreen(
null);
128 return Task.CompletedTask;
131 private byte[] buffer;
132 private byte[] block;
133 private int blockState = 0;
134 private int blockLen = 0;
135 private int blockLeft = 0;
136 private int blockPos = 0;
137 private int state = 0;
138 private byte command = 0;
140 private int left = 0;
153 switch (this.blockState)
156 this.blockLen = Data[i++];
163 this.blockLen |= Data[i++];
164 this.blockLeft = this.blockLen;
165 if ((this.block?.Length ?? 0) != this.blockLen)
166 this.block =
new byte[this.blockLen];
173 int j = Math.Min(this.blockLeft, c);
174 Array.Copy(Data, i, this.block, this.blockPos, j);
180 if (this.blockLeft == 0)
182 this.BlockReceived(this.block);
189 return Task.CompletedTask;
192 private void BlockReceived(
byte[] Data)
203 this.command = Data[i++];
209 this.len = Data[i++];
215 this.len |= Data[i++] << 8;
221 this.len |= Data[i++] << 16;
223 this.left = this.len;
224 this.buffer =
new byte[this.len];
236 this.x |= Data[i++] << 8;
248 this.y |= Data[i++] << 8;
255 this.ProcessCommand();
261 j = Math.Min(this.left, c);
262 Array.Copy(Data, i, this.buffer, this.pos, j);
270 this.ProcessCommand();
282 private void ProcessCommand()
284 switch (this.command)
289 if (this.pendingTiles is
null)
290 this.queue.AddLast((
new Pending(this.buffer), this.x, this.y));
292 this.pendingTiles[this.y, this.x] =
new Pending(this.buffer);
294 if (this.updateScreenTimer == DateTime.MinValue)
295 this.updateScreenTimer =
MainWindow.Scheduler.Add(DateTime.Now.AddMilliseconds(250),
this.UpdateScreen,
null);
300 this.UpdateScreen(
null);
307 return Task.CompletedTask;
310 private void UpdateScreen(
object _)
312 this.updateScreenTimer = DateTime.MinValue;
313 MainWindow.UpdateGui(this.UpdateScreenGuiThread);
316 private Task UpdateScreenGuiThread()
318 MemoryStream ms =
null;
320 BitmapData Data =
null;
329 return Task.CompletedTask;
333 for (y = 0; y < this.rows; y++)
335 for (x = 0; x < this.columns; x++)
339 PendingTile = this.pendingTiles[y, x];
340 if (PendingTile is
null)
343 this.pendingTiles[y, x] =
null;
346 if (this.desktop is
null)
348 this.desktop =
new WriteableBitmap(this.session.
Width,
this.session.Height, 96, 96, PixelFormats.Bgra32,
null);
349 this.DesktopImage.Source = this.desktop;
354 ms =
new MemoryStream(PendingTile.Bin ?? Convert.FromBase64String(PendingTile.Base64));
356 Tile = (Bitmap)Bitmap.FromStream(ms);
357 Data = Tile.LockBits(
new Rectangle(0, 0, Tile.Width, Tile.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
365 this.desktop.WritePixels(
new Int32Rect(0, 0, Tile.Width, Tile.Height), Data.Scan0, Data.Stride * Data.Height,
366 Data.Stride, x * Size, y * Size);
368 Tile.UnlockBits(Data);
378 this.drawing =
false;
381 this.desktop.Unlock();
384 Tile.UnlockBits(Data);
390 return Task.CompletedTask;
393 public async
void Dispose()
397 this.node?.XmppAccountNode?.UnregisterView(
this);
399 if (this.updateScreenTimer > DateTime.MinValue)
401 MainWindow.Scheduler?.Remove(this.updateScreenTimer);
402 this.updateScreenTimer = DateTime.MinValue;
405 if (!(this.session is
null) &&
409 await this.rdpClient.StopSessionAsync(this.session.
RemoteJid,
this.session.SessionId);
412 if (this.disposeRdpClient)
414 this.rdpClient.Dispose();
415 this.disposeRdpClient =
false;
423 this.node?.ViewClosed();
430 private void UserControl_MouseMove(
object Sender, MouseEventArgs e)
432 if (!(this.session is
null))
434 this.GetPosition(e, out
int X, out
int Y);
440 private void GetPosition(MouseEventArgs e, out
int X, out
int Y)
442 System.Windows.Point P = e.GetPosition(this.DesktopImage);
444 X = (int)(this.session.
Width * P.X /
this.DesktopImage.ActualWidth + 0.5);
445 Y = (int)(this.session.
Height * P.Y /
this.DesktopImage.ActualHeight + 0.5);
448 private void UserControl_MouseDown(
object Sender, MouseButtonEventArgs e)
450 if (!(this.session is
null))
452 this.GetPosition(e, out
int X, out
int Y);
454 switch (e.ChangedButton)
456 case System.Windows.Input.MouseButton.Left:
457 this.session.
MouseDown(X, Y, Networking.XMPP.RDP.MouseButton.Left);
461 case System.Windows.Input.MouseButton.Middle:
462 this.session.
MouseDown(X, Y, Networking.XMPP.RDP.MouseButton.Middle);
466 case System.Windows.Input.MouseButton.Right:
467 this.session.
MouseDown(X, Y, Networking.XMPP.RDP.MouseButton.Right);
478 private void UserControl_MouseUp(
object Sender, MouseButtonEventArgs e)
480 if (!(this.session is
null))
482 this.GetPosition(e, out
int X, out
int Y);
484 switch (e.ChangedButton)
486 case System.Windows.Input.MouseButton.Left:
487 this.session.
MouseUp(X, Y, Networking.XMPP.RDP.MouseButton.Left);
491 case System.Windows.Input.MouseButton.Middle:
492 this.session.
MouseUp(X, Y, Networking.XMPP.RDP.MouseButton.Middle);
496 case System.Windows.Input.MouseButton.Right:
497 this.session.
MouseUp(X, Y, Networking.XMPP.RDP.MouseButton.Right);
508 private void UserControl_MouseWheel(
object Sender, MouseWheelEventArgs e)
510 if (!(this.session is
null))
512 this.GetPosition(e, out
int X, out
int Y);
518 private void UserControl_KeyDown(
object Sender, KeyEventArgs e)
520 if (!(this.session is
null))
522 int KeyCode = KeyInterop.VirtualKeyFromKey(e.Key);
528 private void UserControl_KeyUp(
object Sender, KeyEventArgs e)
530 if (!(this.session is
null))
532 int KeyCode = KeyInterop.VirtualKeyFromKey(e.Key);
533 this.session.
KeyUp(KeyCode);
538 private void UserControl_IsVisibleChanged(
object Sender, DependencyPropertyChangedEventArgs e)
541 Keyboard.Focus(
this);
544 public void SaveButton_Click(
object Sender, RoutedEventArgs e)
549 public void SaveAsButton_Click(
object Sender, RoutedEventArgs e)
554 public void NewButton_Click(
object Sender, RoutedEventArgs e)
559 public void OpenButton_Click(
object Sender, RoutedEventArgs e)
Interaction logic for RemoteDesktopView.xaml
void InitializeComponent()
InitializeComponent
Interaction logic for xaml
Static class managing the application event log. Applications and services log events on this static ...
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Event arguments for data reception events.
byte[] Buffer
Buffer holding received data.
int Count
Number of bytes received.
int Offset
Start index of first byte received.
Event arguments for stream callbacks.
Maintains the client-side state of a Remote Desktop Session.
RemoteDesktopSessionState State
Session state changed.
void KeyDown(int KeyCode)
Reports a key having been pressed.
int Height
Height of screen
void MouseDown(int X, int Y, MouseButton Button)
Reports the mouse having been pressed down.
void MouseUp(int X, int Y, MouseButton Button)
Reports the mouse having been released up.
void MouseMoved(int X, int Y)
Reports the mouse having moved to a given position.
string RemoteJid
Remote JID
void MouseWheel(int X, int Y, int Delta)
Reports the mouse wheel having been turned.
void KeyUp(int KeyCode)
Reports a key having been released.
Event arguments for tile events.
int X
Tile X-coordinate of remote desktop screen.
string TileBase64
PNG of tile being updated, base64-encoded.
int Y
Tile Y-coordinate of remote desktop screen.
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
Interface for tab view user controls in the client.
RemoteDesktopSessionState
State of a Remote Desktop Session