From 107ff34fcdb015bb52c6e9d33706aec37511404d Mon Sep 17 00:00:00 2001 From: Jared Goodwin Date: Fri, 13 Dec 2019 19:44:08 -0800 Subject: [PATCH] Created interfaces for services. Reworked dependencies. --- .../ViewModels/MainWindowViewModel.cs | 32 +++---- Desktop.Win/ViewModels/MainWindowViewModel.cs | 51 +++-------- ScreenCast.Core/Capture/ScreenCaster.cs | 9 +- ScreenCast.Core/Conductor.cs | 50 ++++------- ScreenCast.Core/Interfaces/IAudioCapturer.cs | 11 +++ .../{Capture => Interfaces}/ICapturer.cs | 2 +- .../Interfaces/IClipboardService.cs | 11 +++ ScreenCast.Core/Interfaces/IScreenCaster.cs | 13 +++ ScreenCast.Core/Models/Viewer.cs | 1 + ScreenCast.Core/Services/Logger.cs | 3 +- ScreenCast.Core/Sockets/CasterSocket.cs | 48 +++++++--- ScreenCast.Linux/Capture/X11Capture.cs | 1 + ScreenCast.Linux/Program.cs | 60 ++----------- ScreenCast.Linux/ScreenCast.Linux.csproj | 4 + .../Services/LinuxAudioCapturer.cs | 15 ++++ .../Services/LinuxClipboardService.cs | 37 ++++++++ .../Services/LinuxScreenCaster.cs | 31 +++++++ .../{Input => Services}/X11Input.cs | 2 +- ScreenCast.Win/Capture/BitBltCapture.cs | 5 +- ScreenCast.Win/Capture/DXCapture.cs | 2 +- ScreenCast.Win/Program.cs | 83 +++-------------- ScreenCast.Win/ScreenCast.Win.csproj | 8 +- .../CursorIconWatcher.cs | 2 +- .../WinAudioCapturer.cs} | 89 ++++++++++--------- .../Services/WinClipboardService.cs | 24 +++++ .../{Input => Services}/WinInput.cs | 3 +- ScreenCast.Win/Services/WinScreenCaster.cs | 49 ++++++++++ 27 files changed, 364 insertions(+), 282 deletions(-) create mode 100644 ScreenCast.Core/Interfaces/IAudioCapturer.cs rename ScreenCast.Core/{Capture => Interfaces}/ICapturer.cs (93%) create mode 100644 ScreenCast.Core/Interfaces/IClipboardService.cs create mode 100644 ScreenCast.Core/Interfaces/IScreenCaster.cs create mode 100644 ScreenCast.Linux/Services/LinuxAudioCapturer.cs create mode 100644 ScreenCast.Linux/Services/LinuxClipboardService.cs create mode 100644 ScreenCast.Linux/Services/LinuxScreenCaster.cs rename ScreenCast.Linux/{Input => Services}/X11Input.cs (99%) rename ScreenCast.Win/{Capture => Services}/CursorIconWatcher.cs (99%) rename ScreenCast.Win/{Capture/AudioCapturer.cs => Services/WinAudioCapturer.cs} (85%) create mode 100644 ScreenCast.Win/Services/WinClipboardService.cs rename ScreenCast.Win/{Input => Services}/WinInput.cs (99%) create mode 100644 ScreenCast.Win/Services/WinScreenCaster.cs diff --git a/Desktop.Unix/ViewModels/MainWindowViewModel.cs b/Desktop.Unix/ViewModels/MainWindowViewModel.cs index 14b2f531..5e6dd684 100644 --- a/Desktop.Unix/ViewModels/MainWindowViewModel.cs +++ b/Desktop.Unix/ViewModels/MainWindowViewModel.cs @@ -6,10 +6,11 @@ using Remotely.Desktop.Unix.Controls; using Remotely.Desktop.Unix.Services; using Remotely.ScreenCast.Core; using Remotely.ScreenCast.Core.Capture; +using Remotely.ScreenCast.Core.Interfaces; using Remotely.ScreenCast.Core.Models; using Remotely.ScreenCast.Core.Services; using Remotely.ScreenCast.Linux.Capture; -using Remotely.ScreenCast.Linux.Input; +using Remotely.ScreenCast.Linux.Services; using Remotely.Shared.Models; using Remotely.Shared.Services; using System; @@ -35,11 +36,15 @@ namespace Remotely.Desktop.Unix.ViewModels public MainWindowViewModel() { Current = this; - Conductor = new Conductor(); + Conductor = new Conductor( + new X11Input(), + new LinuxAudioCapturer(), + new LinuxClipboardService(), + new LinuxScreenCaster()); + Conductor.SessionIDChanged += SessionIDChanged; Conductor.ViewerRemoved += ViewerRemoved; Conductor.ViewerAdded += ViewerAdded; - Conductor.ClipboardTransferred += Conductor_ClipboardTransferred; Conductor.ScreenCastRequested += ScreenCastRequested; } @@ -136,11 +141,6 @@ namespace Remotely.Desktop.Unix.ViewModels return; } - if (OSUtils.IsLinux) - { - Conductor.SetMessageHandlers(new X11Input()); - } - if (OSUtils.IsWindows) { return; @@ -201,15 +201,15 @@ namespace Remotely.Desktop.Unix.ViewModels { _ = Task.Run(async () => { - ICapturer capturer = null; + ICapturer capturer = null; - if (OSUtils.IsLinux) - { - capturer = new X11Capture(); - } - - await Conductor.CasterSocket.SendCursorChange(new CursorInfo(null, Point.Empty, "default"), new List() { screenCastRequest.ViewerID }); - ScreenCaster.BeginScreenCasting(screenCastRequest.ViewerID, screenCastRequest.RequesterName, capturer, Conductor); + if (OSUtils.IsLinux) + { + capturer = new X11Capture(); + } + + await Conductor.CasterSocket.SendCursorChange(new CursorInfo(null, Point.Empty, "default"), new List() { screenCastRequest.ViewerID }); + _ = ScreenCaster.BeginScreenCasting(screenCastRequest.ViewerID, screenCastRequest.RequesterName, capturer); }); } }); diff --git a/Desktop.Win/ViewModels/MainWindowViewModel.cs b/Desktop.Win/ViewModels/MainWindowViewModel.cs index 145068f3..9080f7a5 100644 --- a/Desktop.Win/ViewModels/MainWindowViewModel.cs +++ b/Desktop.Win/ViewModels/MainWindowViewModel.cs @@ -5,25 +5,19 @@ using Remotely.ScreenCast.Core; using Remotely.ScreenCast.Core.Capture; using Remotely.ScreenCast.Core.Models; using Remotely.ScreenCast.Core.Services; -using Remotely.ScreenCast.Win; using Remotely.ScreenCast.Win.Capture; -using Remotely.ScreenCast.Win.Input; using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Configuration; using System.Diagnostics; -using System.IO; using System.Linq; using System.Reflection; -using System.Text; using System.Threading.Tasks; using System.Windows; using System.Security.Principal; -using System.Security.Claims; using System.Windows.Input; -using System.Windows.Controls; +using Remotely.ScreenCast.Win.Services; +using Remotely.ScreenCast.Core.Interfaces; namespace Remotely.Desktop.Win.ViewModels { @@ -34,22 +28,24 @@ namespace Remotely.Desktop.Win.ViewModels public MainWindowViewModel() { Current = this; - Conductor = new Conductor(); + + CursorIconWatcher = new CursorIconWatcher(Conductor); + CursorIconWatcher.OnChange += CursorIconWatcher_OnChange; + + Conductor = new Conductor( + new WinInput(), + new WinAudioCapturer(), + new WinClipboardService(), + new WinScreenCaster(CursorIconWatcher)); + Conductor.SessionIDChanged += SessionIDChanged; Conductor.ViewerRemoved += ViewerRemoved; Conductor.ViewerAdded += ViewerAdded; Conductor.ScreenCastRequested += ScreenCastRequested; - Conductor.AudioToggled += AudioToggled; - Conductor.ClipboardTransferred += Conductor_ClipboardTransferred; - CursorIconWatcher = new CursorIconWatcher(Conductor); - CursorIconWatcher.OnChange += CursorIconWatcher_OnChange; - AudioCapturer = new AudioCapturer(Conductor); } public static MainWindowViewModel Current { get; private set; } - public AudioCapturer AudioCapturer { get; private set; } - public ICommand ChangeServerCommand { get @@ -162,7 +158,6 @@ namespace Remotely.Desktop.Win.ViewModels return; } - Conductor.SetMessageHandlers(new WinInput()); await Conductor.CasterSocket.SendDeviceInfo(Conductor.ServiceID, Environment.MachineName, Conductor.DeviceID); await Conductor.CasterSocket.GetSessionID(); } @@ -186,27 +181,9 @@ namespace Remotely.Desktop.Win.ViewModels } } - private void AudioToggled(object sender, bool toggleOn) - { - if (toggleOn) - { - AudioCapturer.Start(); - } - else - { - AudioCapturer.Stop(); - } - } - - private void Conductor_ClipboardTransferred(object sender, string transferredText) - { - Application.Current.Dispatcher.Invoke(() => { - Clipboard.SetText(transferredText); - }); - } private async void CursorIconWatcher_OnChange(object sender, CursorInfo cursor) { - if (Conductor?.CasterSocket != null) + if (Conductor?.CasterSocket != null && Conductor?.Viewers?.Count > 0) { await Conductor?.CasterSocket?.SendCursorChange(cursor, Conductor.Viewers.Keys.ToList()); } @@ -238,7 +215,7 @@ namespace Remotely.Desktop.Win.ViewModels capturer = new BitBltCapture(); } await Conductor.CasterSocket.SendCursorChange(CursorIconWatcher.GetCurrentCursor(), new List() { screenCastRequest.ViewerID }); - ScreenCaster.BeginScreenCasting(screenCastRequest.ViewerID, screenCastRequest.RequesterName, capturer, Conductor); + _ = ScreenCaster.BeginScreenCasting(screenCastRequest.ViewerID, screenCastRequest.RequesterName, capturer); }); } }); diff --git a/ScreenCast.Core/Capture/ScreenCaster.cs b/ScreenCast.Core/Capture/ScreenCaster.cs index 994dbf87..685138a0 100644 --- a/ScreenCast.Core/Capture/ScreenCaster.cs +++ b/ScreenCast.Core/Capture/ScreenCaster.cs @@ -12,16 +12,17 @@ using System.Text; using System.Threading.Tasks; using Remotely.Shared.Services; using Remotely.Shared.Win32; +using Remotely.ScreenCast.Core.Interfaces; namespace Remotely.ScreenCast.Core.Capture { public class ScreenCaster { - public static async void BeginScreenCasting(string viewerID, + public static async Task BeginScreenCasting(string viewerID, string requesterName, - ICapturer capturer, - Conductor conductor) + ICapturer capturer) { + var conductor = Conductor.Current; Viewer viewer; byte[] encodedImageBytes; @@ -129,9 +130,9 @@ namespace Remotely.ScreenCast.Core.Capture // Close if no one is viewing. if (conductor.Viewers.Count == 0 && conductor.Mode == Enums.AppMode.Unattended) { + await conductor.CasterSocket.Disconnect(); Environment.Exit(0); } - } } } diff --git a/ScreenCast.Core/Conductor.cs b/ScreenCast.Core/Conductor.cs index 53e4179a..2585b98c 100644 --- a/ScreenCast.Core/Conductor.cs +++ b/ScreenCast.Core/Conductor.cs @@ -12,18 +12,22 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Remotely.ScreenCast.Core.Interfaces; namespace Remotely.ScreenCast.Core { public class Conductor { + public static Conductor Current { get; private set; } - public event EventHandler AudioToggled; - - public event EventHandler ClipboardTransferred; - - public event EventHandler ScreenCastInitiated; - + public Conductor(IKeyboardMouseInput keyboardMouse, + IAudioCapturer audioService, + IClipboardService clipboardService, + IScreenCaster screenCastService) + { + Current = this; + CasterSocket = new CasterSocket(this, keyboardMouse, screenCastService, audioService, clipboardService); + } public event EventHandler ScreenCastRequested; public event EventHandler SessionIDChanged; @@ -44,12 +48,7 @@ namespace Remotely.ScreenCast.Core public ConcurrentDictionary Viewers { get; } = new ConcurrentDictionary(); public async Task Connect() { - Connection = new HubConnectionBuilder() - .WithUrl($"{Host}/RCDeviceHub") - .AddMessagePackProtocol() - .Build(); - - await Connection.StartAsync(); + await CasterSocket.Connect(Host); } public void ProcessArgs(string[] args) @@ -83,39 +82,20 @@ namespace Remotely.ScreenCast.Core } } - public void SetMessageHandlers(IKeyboardMouseInput keyboardMouse) - { - CasterSocket = new CasterSocket(Connection, this, keyboardMouse); - } - - internal void InvokeAudioToggled(bool toggleOn) - { - AudioToggled?.Invoke(null, toggleOn); - } - - internal void InvokeClipboardTransfer(string transferText) - { - ClipboardTransferred?.Invoke(null, transferText); - } - - internal void InvokeScreenCastInitiated(ScreenCastRequest viewerIdAndRequesterName) - { - ScreenCastInitiated?.Invoke(null, viewerIdAndRequesterName); - } - internal void InvokeScreenCastRequested(ScreenCastRequest viewerIdAndRequesterName) + public void InvokeScreenCastRequested(ScreenCastRequest viewerIdAndRequesterName) { ScreenCastRequested?.Invoke(null, viewerIdAndRequesterName); } - internal void InvokeSessionIDChanged(string sessionID) + public void InvokeSessionIDChanged(string sessionID) { SessionIDChanged?.Invoke(null, sessionID); } - internal void InvokeViewerAdded(Viewer viewer) + public void InvokeViewerAdded(Viewer viewer) { ViewerAdded?.Invoke(null, viewer); } - internal void InvokeViewerRemoved(string viewerID) + public void InvokeViewerRemoved(string viewerID) { ViewerRemoved?.Invoke(null, viewerID); } diff --git a/ScreenCast.Core/Interfaces/IAudioCapturer.cs b/ScreenCast.Core/Interfaces/IAudioCapturer.cs new file mode 100644 index 00000000..397882b4 --- /dev/null +++ b/ScreenCast.Core/Interfaces/IAudioCapturer.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Remotely.ScreenCast.Core.Interfaces +{ + public interface IAudioCapturer + { + void ToggleAudio(bool toggleOn); + } +} diff --git a/ScreenCast.Core/Capture/ICapturer.cs b/ScreenCast.Core/Interfaces/ICapturer.cs similarity index 93% rename from ScreenCast.Core/Capture/ICapturer.cs rename to ScreenCast.Core/Interfaces/ICapturer.cs index 9b791d0c..728ec44a 100644 --- a/ScreenCast.Core/Capture/ICapturer.cs +++ b/ScreenCast.Core/Interfaces/ICapturer.cs @@ -5,7 +5,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Remotely.ScreenCast.Core.Capture +namespace Remotely.ScreenCast.Core.Interfaces { public interface ICapturer : IDisposable { diff --git a/ScreenCast.Core/Interfaces/IClipboardService.cs b/ScreenCast.Core/Interfaces/IClipboardService.cs new file mode 100644 index 00000000..3ee52627 --- /dev/null +++ b/ScreenCast.Core/Interfaces/IClipboardService.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Remotely.ScreenCast.Core.Interfaces +{ + public interface IClipboardService + { + void SetText(string clipboardText); + } +} diff --git a/ScreenCast.Core/Interfaces/IScreenCaster.cs b/ScreenCast.Core/Interfaces/IScreenCaster.cs new file mode 100644 index 00000000..dbb1b191 --- /dev/null +++ b/ScreenCast.Core/Interfaces/IScreenCaster.cs @@ -0,0 +1,13 @@ +using Remotely.Shared.Models; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Remotely.ScreenCast.Core.Interfaces +{ + public interface IScreenCaster + { + Task BeginScreenCasting(ScreenCastRequest screenCastRequest); + } +} diff --git a/ScreenCast.Core/Models/Viewer.cs b/ScreenCast.Core/Models/Viewer.cs index 3914b7d5..54104bc0 100644 --- a/ScreenCast.Core/Models/Viewer.cs +++ b/ScreenCast.Core/Models/Viewer.cs @@ -1,4 +1,5 @@ using Remotely.ScreenCast.Core.Capture; +using Remotely.ScreenCast.Core.Interfaces; using System; using System.Collections.Generic; using System.Drawing; diff --git a/ScreenCast.Core/Services/Logger.cs b/ScreenCast.Core/Services/Logger.cs index f288e108..2b8e6738 100644 --- a/ScreenCast.Core/Services/Logger.cs +++ b/ScreenCast.Core/Services/Logger.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.Extensions.Logging; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; diff --git a/ScreenCast.Core/Sockets/CasterSocket.cs b/ScreenCast.Core/Sockets/CasterSocket.cs index 4bd8feb4..2ffb186d 100644 --- a/ScreenCast.Core/Sockets/CasterSocket.cs +++ b/ScreenCast.Core/Sockets/CasterSocket.cs @@ -14,22 +14,51 @@ using System.Net; using Remotely.ScreenCast.Core.Services; using Remotely.ScreenCast.Core.Input; using Remotely.Shared.Win32; +using Remotely.ScreenCast.Core.Interfaces; +using Microsoft.Extensions.DependencyInjection; namespace Remotely.ScreenCast.Core.Sockets { public class CasterSocket { - public CasterSocket(HubConnection hubConnection, Conductor conductor, IKeyboardMouseInput keyboardMouseInput) + public CasterSocket( + Conductor conductor, + IKeyboardMouseInput keyboardMouseInput, + IScreenCaster screenCastService, + IAudioCapturer audioService, + IClipboardService clipboardService) { - Connection = hubConnection; Conductor = conductor; KeyboardMouseInput = keyboardMouseInput; - ApplyConnectionHandlers(); + ClipboardService = clipboardService; + AudioService = audioService; + ScreenCastService = screenCastService; + } + + public IScreenCaster ScreenCastService { get; } + private IAudioCapturer AudioService { get; } + private IClipboardService ClipboardService { get; } + private Conductor Conductor { get; } + private HubConnection Connection { get; set; } + private IKeyboardMouseInput KeyboardMouseInput { get; } + public async Task Connect(string host) + { + Connection = new HubConnectionBuilder() + .WithUrl($"{host}/RCDeviceHub") + .AddMessagePackProtocol() + .Build(); + + ApplyConnectionHandlers(); + + await Connection.StartAsync(); + } + + public async Task Disconnect() + { + await Connection.StopAsync(); + await Connection.DisposeAsync(); } - public Conductor Conductor { get; } - public IKeyboardMouseInput KeyboardMouseInput { get; } - private HubConnection Connection { get; } public async Task GetSessionID() { await Connection.SendAsync("GetSessionID"); @@ -44,7 +73,6 @@ namespace Remotely.ScreenCast.Core.Sockets { await Connection.SendAsync("NotifyViewersRelaunchedScreenCasterReady", viewerIDs); } - public async Task SendAudioSample(byte[] buffer, List viewerIDs) { await Connection.SendAsync("SendAudioSample", buffer, viewerIDs); @@ -104,7 +132,7 @@ namespace Remotely.ScreenCast.Core.Sockets { if (Conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl) { - Conductor.InvokeClipboardTransfer(transferText); + ClipboardService.SetText(transferText); } } catch (Exception ex) @@ -125,7 +153,7 @@ namespace Remotely.ScreenCast.Core.Sockets { try { - Conductor.InvokeScreenCastInitiated(new ScreenCastRequest() { ViewerID = viewerID, RequesterName = requesterName }); + ScreenCastService.BeginScreenCasting(new ScreenCastRequest() { ViewerID = viewerID, RequesterName = requesterName }); } catch (Exception ex) { @@ -249,7 +277,7 @@ namespace Remotely.ScreenCast.Core.Sockets { if (Conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl) { - Conductor.InvokeAudioToggled(toggleOn); + AudioService.ToggleAudio(toggleOn); } }); diff --git a/ScreenCast.Linux/Capture/X11Capture.cs b/ScreenCast.Linux/Capture/X11Capture.cs index ebb90a36..78d62f77 100644 --- a/ScreenCast.Linux/Capture/X11Capture.cs +++ b/ScreenCast.Linux/Capture/X11Capture.cs @@ -1,4 +1,5 @@ using Remotely.ScreenCast.Core.Capture; +using Remotely.ScreenCast.Core.Interfaces; using Remotely.ScreenCast.Core.Services; using Remotely.ScreenCast.Linux.X11Interop; using System; diff --git a/ScreenCast.Linux/Program.cs b/ScreenCast.Linux/Program.cs index df5f08b8..eea33e04 100644 --- a/ScreenCast.Linux/Program.cs +++ b/ScreenCast.Linux/Program.cs @@ -1,17 +1,8 @@ -using Remotely.Shared.Models; -using Remotely.ScreenCast.Core; -using Remotely.ScreenCast.Core.Capture; +using Remotely.ScreenCast.Core; using Remotely.ScreenCast.Core.Services; -using Remotely.ScreenCast.Linux.Capture; -using Remotely.ScreenCast.Linux.Input; -using Remotely.ScreenCast.Linux.X11Interop; using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Drawing; -using System.Threading.Tasks; -using System.IO; using System.Threading; +using Remotely.ScreenCast.Linux.Services; namespace Remotely.ScreenCast.Linux { @@ -23,13 +14,15 @@ namespace Remotely.ScreenCast.Linux try { AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; - Conductor = new Conductor(); + Conductor = new Conductor( + new X11Input(), + new LinuxAudioCapturer(), + new LinuxClipboardService(), + new LinuxScreenCaster()); + Conductor.ProcessArgs(args); Conductor.Connect().ContinueWith(async (task) => { - Conductor.SetMessageHandlers(new X11Input()); - Conductor.ScreenCastInitiated += ScreenCastInitiated; - Conductor.ClipboardTransferred += Conductor_ClipboardTransferred; await Conductor.CasterSocket.SendDeviceInfo(Conductor.ServiceID, Environment.MachineName, Conductor.DeviceID); await Conductor.CasterSocket.NotifyRequesterUnattendedReady(Conductor.RequesterID); Conductor.IdleTimer = new IdleTimer(Conductor.Viewers); @@ -45,43 +38,6 @@ namespace Remotely.ScreenCast.Linux } } - private static void Conductor_ClipboardTransferred(object sender, string transferredText) - { - var tempPath = Path.GetTempFileName(); - File.WriteAllText(tempPath, transferredText); - try - { - var psi = new ProcessStartInfo("bash", $"-c \"cat {tempPath} | xclip -i -selection clipboard\"") - { - WindowStyle = ProcessWindowStyle.Hidden - }; - var proc = Process.Start(psi); - proc.WaitForExit(); - - } - catch (Exception ex) - { - Logger.Write(ex); - } - finally - { - File.Delete(tempPath); - } - } - - private static async void ScreenCastInitiated(object sender, ScreenCastRequest screenCastRequest) - { - try - { - var capturer = new X11Capture(); - await Conductor.CasterSocket.SendCursorChange(new CursorInfo(null, Point.Empty, "default"), new List() { screenCastRequest.ViewerID }); - ScreenCaster.BeginScreenCasting(screenCastRequest.ViewerID, screenCastRequest.RequesterName, capturer, Conductor); - } - catch (Exception ex) - { - Logger.Write(ex); - } - } private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { diff --git a/ScreenCast.Linux/ScreenCast.Linux.csproj b/ScreenCast.Linux/ScreenCast.Linux.csproj index ab6a422f..c9bf2e0b 100644 --- a/ScreenCast.Linux/ScreenCast.Linux.csproj +++ b/ScreenCast.Linux/ScreenCast.Linux.csproj @@ -43,4 +43,8 @@ + + + + diff --git a/ScreenCast.Linux/Services/LinuxAudioCapturer.cs b/ScreenCast.Linux/Services/LinuxAudioCapturer.cs new file mode 100644 index 00000000..4db6ac7e --- /dev/null +++ b/ScreenCast.Linux/Services/LinuxAudioCapturer.cs @@ -0,0 +1,15 @@ +using Remotely.ScreenCast.Core.Interfaces; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Remotely.ScreenCast.Linux.Services +{ + public class LinuxAudioCapturer : IAudioCapturer + { + public void ToggleAudio(bool toggleOn) + { + // Not implemented. + } + } +} diff --git a/ScreenCast.Linux/Services/LinuxClipboardService.cs b/ScreenCast.Linux/Services/LinuxClipboardService.cs new file mode 100644 index 00000000..78900b8a --- /dev/null +++ b/ScreenCast.Linux/Services/LinuxClipboardService.cs @@ -0,0 +1,37 @@ +using Remotely.ScreenCast.Core.Interfaces; +using Remotely.ScreenCast.Core.Services; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; + +namespace Remotely.ScreenCast.Linux.Services +{ + public class LinuxClipboardService : IClipboardService + { + public void SetText(string clipboardText) + { + var tempPath = Path.GetTempFileName(); + File.WriteAllText(tempPath, clipboardText); + try + { + var psi = new ProcessStartInfo("bash", $"-c \"cat {tempPath} | xclip -i -selection clipboard\"") + { + WindowStyle = ProcessWindowStyle.Hidden + }; + var proc = Process.Start(psi); + proc.WaitForExit(); + + } + catch (Exception ex) + { + Logger.Write(ex); + } + finally + { + File.Delete(tempPath); + } + } + } +} diff --git a/ScreenCast.Linux/Services/LinuxScreenCaster.cs b/ScreenCast.Linux/Services/LinuxScreenCaster.cs new file mode 100644 index 00000000..5dea5f3e --- /dev/null +++ b/ScreenCast.Linux/Services/LinuxScreenCaster.cs @@ -0,0 +1,31 @@ +using Remotely.ScreenCast.Core; +using Remotely.ScreenCast.Core.Capture; +using Remotely.ScreenCast.Core.Interfaces; +using Remotely.ScreenCast.Core.Services; +using Remotely.ScreenCast.Linux.Capture; +using Remotely.Shared.Models; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Text; +using System.Threading.Tasks; + +namespace Remotely.ScreenCast.Linux.Services +{ + public class LinuxScreenCaster : IScreenCaster + { + public async Task BeginScreenCasting(ScreenCastRequest screenCastRequest) + { + try + { + var capturer = new X11Capture(); + await Conductor.Current.CasterSocket.SendCursorChange(new CursorInfo(null, Point.Empty, "default"), new List() { screenCastRequest.ViewerID }); + _ = ScreenCaster.BeginScreenCasting(screenCastRequest.ViewerID, screenCastRequest.RequesterName, capturer); + } + catch (Exception ex) + { + Logger.Write(ex); + } + } + } +} diff --git a/ScreenCast.Linux/Input/X11Input.cs b/ScreenCast.Linux/Services/X11Input.cs similarity index 99% rename from ScreenCast.Linux/Input/X11Input.cs rename to ScreenCast.Linux/Services/X11Input.cs index d99d4f43..1e663ab1 100644 --- a/ScreenCast.Linux/Input/X11Input.cs +++ b/ScreenCast.Linux/Services/X11Input.cs @@ -9,7 +9,7 @@ using System.Linq; using Remotely.ScreenCast.Linux.Capture; using Remotely.ScreenCast.Core.Models; -namespace Remotely.ScreenCast.Linux.Input +namespace Remotely.ScreenCast.Linux.Services { public class X11Input : IKeyboardMouseInput { diff --git a/ScreenCast.Win/Capture/BitBltCapture.cs b/ScreenCast.Win/Capture/BitBltCapture.cs index d308a442..adf6b593 100644 --- a/ScreenCast.Win/Capture/BitBltCapture.cs +++ b/ScreenCast.Win/Capture/BitBltCapture.cs @@ -8,12 +8,9 @@ using System.Windows.Forms; using System.Linq; using System.Threading.Tasks; using System.Collections.Generic; -using System.Drawing.Drawing2D; -using System.Diagnostics; -using System.Runtime.Serialization.Formatters.Binary; using Remotely.ScreenCast.Core.Services; using System.Threading; -using Remotely.ScreenCast.Core.Capture; +using Remotely.ScreenCast.Core.Interfaces; namespace Remotely.ScreenCast.Win.Capture { diff --git a/ScreenCast.Win/Capture/DXCapture.cs b/ScreenCast.Win/Capture/DXCapture.cs index b46a9155..78d6af62 100644 --- a/ScreenCast.Win/Capture/DXCapture.cs +++ b/ScreenCast.Win/Capture/DXCapture.cs @@ -1,4 +1,4 @@ -using Remotely.ScreenCast.Core.Capture; +using Remotely.ScreenCast.Core.Interfaces; using Remotely.ScreenCast.Core.Services; using SharpDX; using SharpDX.Direct3D11; diff --git a/ScreenCast.Win/Program.cs b/ScreenCast.Win/Program.cs index cf169c34..a3a87c3e 100644 --- a/ScreenCast.Win/Program.cs +++ b/ScreenCast.Win/Program.cs @@ -1,37 +1,18 @@ -using Microsoft.AspNetCore.SignalR.Client; -using Microsoft.Extensions.DependencyInjection; -using Remotely.Shared.Models; +using Remotely.Shared.Models; using Remotely.ScreenCast.Core; -using Remotely.ScreenCast.Core.Capture; -using Remotely.ScreenCast.Core.Enums; -using Remotely.ScreenCast.Core.Models; -using Remotely.ScreenCast.Core.Sockets; using Remotely.ScreenCast.Core.Services; -using Remotely.ScreenCast.Win.Capture; -using Remotely.ScreenCast.Win.Input; using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Drawing; -using System.IO; using System.Linq; using System.Reflection; -using System.Text; using System.Threading.Tasks; -using System.Windows.Forms; using Remotely.Shared.Win32; -using NAudio.Wave; -using Remotely.Shared.Services; -using System.Runtime.InteropServices; using System.Threading; -using Microsoft.Win32; +using Remotely.ScreenCast.Win.Services; namespace Remotely.ScreenCast.Win { - public class Program + public class Program { - public static AudioCapturer AudioCapturer { get; private set; } public static Conductor Conductor { get; private set; } public static CursorIconWatcher CursorIconWatcher { get; private set; } public static async void CursorIconWatcher_OnChange(object sender, CursorInfo cursor) @@ -47,18 +28,16 @@ namespace Remotely.ScreenCast.Win try { AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; - Conductor = new Conductor(); + CursorIconWatcher = new CursorIconWatcher(Conductor); + Conductor = new Conductor( + new WinInput(), + new WinAudioCapturer(), + new WinClipboardService(), + new WinScreenCaster(CursorIconWatcher)); Conductor.ProcessArgs(args); - Conductor.ScreenCastInitiated += ScreenCastInitiated; - Conductor.AudioToggled += AudioToggled; - Conductor.ClipboardTransferred += Conductor_ClipboardTransferred; - Conductor.Connect().ContinueWith(async (task) => { - Conductor.SetMessageHandlers(new WinInput()); - AudioCapturer = new AudioCapturer(Conductor); - CursorIconWatcher = new CursorIconWatcher(Conductor); CursorIconWatcher.OnChange += CursorIconWatcher_OnChange; await Conductor.CasterSocket.SendDeviceInfo(Conductor.ServiceID, Environment.MachineName, Conductor.DeviceID); CheckInitialDesktop(); @@ -78,17 +57,7 @@ namespace Remotely.ScreenCast.Win } } - private static void AudioToggled(object sender, bool toggledOn) - { - if (toggledOn) - { - AudioCapturer.Start(); - } - else - { - AudioCapturer.Stop(); - } - } + private static async Task CheckForRelaunch() { @@ -128,16 +97,6 @@ namespace Remotely.ScreenCast.Win } } - private static void Conductor_ClipboardTransferred(object sender, string transferredText) - { - var thread = new Thread(() => - { - Clipboard.SetText(transferredText); - }); - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - } - private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { Logger.Write((Exception)e.ExceptionObject); @@ -157,27 +116,5 @@ namespace Remotely.ScreenCast.Win await Task.Delay(1000); } } - private static async void ScreenCastInitiated(object sender, ScreenCastRequest screenCastRequest) - { - ICapturer capturer; - try - { - if (Conductor.Viewers.Count == 0) - { - capturer = new DXCapture(); - } - else - { - capturer = new BitBltCapture(); - } - } - catch (Exception ex) - { - Logger.Write(ex); - capturer = new BitBltCapture(); - } - await Conductor.CasterSocket.SendCursorChange(CursorIconWatcher.GetCurrentCursor(), new List() { screenCastRequest.ViewerID }); - ScreenCaster.BeginScreenCasting(screenCastRequest.ViewerID, screenCastRequest.RequesterName, capturer, Conductor); - } } } diff --git a/ScreenCast.Win/ScreenCast.Win.csproj b/ScreenCast.Win/ScreenCast.Win.csproj index 5ad7e31f..be467bae 100644 --- a/ScreenCast.Win/ScreenCast.Win.csproj +++ b/ScreenCast.Win/ScreenCast.Win.csproj @@ -234,11 +234,13 @@ - + - + + - + + diff --git a/ScreenCast.Win/Capture/CursorIconWatcher.cs b/ScreenCast.Win/Services/CursorIconWatcher.cs similarity index 99% rename from ScreenCast.Win/Capture/CursorIconWatcher.cs rename to ScreenCast.Win/Services/CursorIconWatcher.cs index 5831fb50..43d2709d 100644 --- a/ScreenCast.Win/Capture/CursorIconWatcher.cs +++ b/ScreenCast.Win/Services/CursorIconWatcher.cs @@ -14,7 +14,7 @@ using System.Windows.Forms; using Remotely.Shared.Win32; using Remotely.Shared.Models; -namespace Remotely.ScreenCast.Win.Capture +namespace Remotely.ScreenCast.Win.Services { /// /// A class that can be used to watch for cursor icon changes. diff --git a/ScreenCast.Win/Capture/AudioCapturer.cs b/ScreenCast.Win/Services/WinAudioCapturer.cs similarity index 85% rename from ScreenCast.Win/Capture/AudioCapturer.cs rename to ScreenCast.Win/Services/WinAudioCapturer.cs index 4a5f3816..f6dca2da 100644 --- a/ScreenCast.Win/Capture/AudioCapturer.cs +++ b/ScreenCast.Win/Services/WinAudioCapturer.cs @@ -8,23 +8,58 @@ using System.Threading.Tasks; using System.Timers; using NAudio.Wave; using Remotely.ScreenCast.Core; +using Remotely.ScreenCast.Core.Interfaces; -namespace Remotely.ScreenCast.Win.Capture +namespace Remotely.ScreenCast.Win.Services { - public class AudioCapturer + public class WinAudioCapturer : IAudioCapturer { - public AudioCapturer(Conductor conductor) - { - Conductor = conductor; - } private WasapiLoopbackCapture Capturer { get; set; } - private WaveFormat TargetFormat { get; set; } - private Conductor Conductor { get; set; } - - private List TempBuffer { get; set; } = new List(); private Stopwatch SendTimer { get; set; } + private WaveFormat TargetFormat { get; set; } + private List TempBuffer { get; set; } = new List(); + public void ToggleAudio(bool toggleOn) + { + if (toggleOn) + { + Start(); + } + else + { + Stop(); + } + } - public void Start() + private async void SendTempBuffer() + { + if (TempBuffer.Count == 0) + { + return; + } + + using (var ms1 = new MemoryStream()) + { + using (var wfw = new WaveFileWriter(ms1, Capturer.WaveFormat)) + { + wfw.Write(TempBuffer.ToArray(), 0, TempBuffer.Count); + } + TempBuffer.Clear(); + + // Resample to 16-bit so Firefox will play it. + using (var ms2 = new MemoryStream(ms1.ToArray())) + using (var wfr = new WaveFileReader(ms2)) + using (var ms3 = new MemoryStream()) + { + using (var resampler = new MediaFoundationResampler(wfr, TargetFormat)) + { + WaveFileWriter.WriteWavFileToStream(ms3, resampler); + } + await Conductor.Current.CasterSocket.SendAudioSample(ms3.ToArray(), Program.Conductor.Viewers.Keys.ToList()); + } + } + } + + private void Start() { try { @@ -63,37 +98,7 @@ namespace Remotely.ScreenCast.Win.Capture } catch { } } - - private async void SendTempBuffer() - { - if (TempBuffer.Count == 0) - { - return; - } - - using (var ms1 = new MemoryStream()) - { - using (var wfw = new WaveFileWriter(ms1, Capturer.WaveFormat)) - { - wfw.Write(TempBuffer.ToArray(), 0, TempBuffer.Count); - } - TempBuffer.Clear(); - - // Resample to 16-bit so Firefox will play it. - using (var ms2 = new MemoryStream(ms1.ToArray())) - using (var wfr = new WaveFileReader(ms2)) - using (var ms3 = new MemoryStream()) - { - using (var resampler = new MediaFoundationResampler(wfr, TargetFormat)) - { - WaveFileWriter.WriteWavFileToStream(ms3, resampler); - } - await Conductor.CasterSocket.SendAudioSample(ms3.ToArray(), Conductor.Viewers.Keys.ToList()); - } - } - } - - public void Stop() + private void Stop() { Capturer.StopRecording(); SendTimer.Reset(); diff --git a/ScreenCast.Win/Services/WinClipboardService.cs b/ScreenCast.Win/Services/WinClipboardService.cs new file mode 100644 index 00000000..6dc343e5 --- /dev/null +++ b/ScreenCast.Win/Services/WinClipboardService.cs @@ -0,0 +1,24 @@ +using Remotely.ScreenCast.Core.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Remotely.ScreenCast.Win.Services +{ + public class WinClipboardService : IClipboardService + { + public void SetText(string clipboardText) + { + var thread = new Thread(() => + { + Clipboard.SetText(clipboardText); + }); + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + } + } +} diff --git a/ScreenCast.Win/Input/WinInput.cs b/ScreenCast.Win/Services/WinInput.cs similarity index 99% rename from ScreenCast.Win/Input/WinInput.cs rename to ScreenCast.Win/Services/WinInput.cs index e072d100..2c5d47d0 100644 --- a/ScreenCast.Win/Input/WinInput.cs +++ b/ScreenCast.Win/Services/WinInput.cs @@ -4,8 +4,9 @@ using System; using Remotely.Shared.Win32; using static Remotely.Shared.Win32.User32; using Remotely.ScreenCast.Core.Capture; +using Remotely.ScreenCast.Core.Interfaces; -namespace Remotely.ScreenCast.Win.Input +namespace Remotely.ScreenCast.Win.Services { public class WinInput : IKeyboardMouseInput { diff --git a/ScreenCast.Win/Services/WinScreenCaster.cs b/ScreenCast.Win/Services/WinScreenCaster.cs new file mode 100644 index 00000000..3bae027d --- /dev/null +++ b/ScreenCast.Win/Services/WinScreenCaster.cs @@ -0,0 +1,49 @@ +using Remotely.ScreenCast.Core.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Remotely.ScreenCast.Core.Enums; +using Remotely.ScreenCast.Core.Services; +using Remotely.ScreenCast.Core.Capture; +using Remotely.ScreenCast.Core; +using Remotely.ScreenCast.Core.Models; +using Remotely.Shared.Models; +using Remotely.ScreenCast.Win.Capture; + +namespace Remotely.ScreenCast.Win.Services +{ + public class WinScreenCaster : IScreenCaster + { + public WinScreenCaster(CursorIconWatcher cursorIconWatcher) + { + CursorIconWatcher = cursorIconWatcher; + } + + public CursorIconWatcher CursorIconWatcher { get; } + + public async Task BeginScreenCasting(ScreenCastRequest screenCastRequest) + { + ICapturer capturer; + try + { + if (Conductor.Current.Viewers.Count == 0) + { + capturer = new DXCapture(); + } + else + { + capturer = new BitBltCapture(); + } + } + catch (Exception ex) + { + Logger.Write(ex); + capturer = new BitBltCapture(); + } + await Conductor.Current.CasterSocket.SendCursorChange(CursorIconWatcher.GetCurrentCursor(), new List() { screenCastRequest.ViewerID }); + _ = ScreenCaster.BeginScreenCasting(screenCastRequest.ViewerID, screenCastRequest.RequesterName, capturer); + } + } +}