diff --git a/Desktop.Linux/ViewModels/MainWindowViewModel.cs b/Desktop.Linux/ViewModels/MainWindowViewModel.cs index a9637642..c1125516 100644 --- a/Desktop.Linux/ViewModels/MainWindowViewModel.cs +++ b/Desktop.Linux/ViewModels/MainWindowViewModel.cs @@ -25,6 +25,8 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; namespace Remotely.Desktop.Linux.ViewModels { @@ -42,9 +44,10 @@ namespace Remotely.Desktop.Linux.ViewModels { return; } - var screenCaster = new LinuxScreenCaster(); - var casterSocket = new CasterSocket(new X11Input(), screenCaster, new LinuxAudioCapturer(), new LinuxClipboardService()); - Conductor = new Conductor(casterSocket, screenCaster); + + BuildServices(); + + Conductor = Services.GetRequiredService(); Conductor.SessionIDChanged += SessionIDChanged; Conductor.ViewerRemoved += ViewerRemoved; @@ -52,8 +55,11 @@ namespace Remotely.Desktop.Linux.ViewModels Conductor.ScreenCastRequested += ScreenCastRequested; } + public static MainWindowViewModel Current { get; private set; } + public static IServiceProvider Services => ServiceContainer.Instance; + public ICommand ChangeServerCommand => new Executor(async (param) => { await PromptForHostName(); @@ -105,6 +111,14 @@ namespace Remotely.Desktop.Linux.ViewModels (param as Window).WindowState = WindowState.Minimized; }); + public ICommand OpenOptionsMenu => new Executor((param) => + { + if (param is Button) + { + (param as Button).ContextMenu?.Open(param as Button); + } + }); + public ICommand RemoveViewerCommand => new Executor(async (param) => { var viewerList = param as AvaloniaList ?? new AvaloniaList(); @@ -149,14 +163,6 @@ namespace Remotely.Desktop.Linux.ViewModels await Conductor.CasterSocket.GetSessionID(); } - public ICommand OpenOptionsMenu => new Executor((param) => - { - if (param is Button) - { - (param as Button).ContextMenu?.Open(param as Button); - } - }); - public async Task PromptForHostName() { var prompt = new HostNamePrompt(); @@ -180,7 +186,26 @@ namespace Remotely.Desktop.Linux.ViewModels } } - + private static void BuildServices() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(builder => + { + builder.AddConsole().AddEventLog(); + }); + + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddTransient(); + + + ServiceContainer.Instance = serviceCollection.BuildServiceProvider(); + } private void ScreenCastRequested(object sender, ScreenCastRequest screenCastRequest) { Dispatcher.UIThread.InvokeAsync(async () => @@ -191,7 +216,7 @@ namespace Remotely.Desktop.Linux.ViewModels _ = Task.Run(async () => { await Conductor.CasterSocket.SendCursorChange(new CursorInfo(null, Point.Empty, "default"), new List() { screenCastRequest.ViewerID }); - _ = Conductor.ScreenCaster.BeginScreenCasting(screenCastRequest); + _ = Services.GetRequiredService().BeginScreenCasting(screenCastRequest); }); } }); diff --git a/Desktop.Win/ViewModels/MainWindowViewModel.cs b/Desktop.Win/ViewModels/MainWindowViewModel.cs index 99f98b7f..281b436b 100644 --- a/Desktop.Win/ViewModels/MainWindowViewModel.cs +++ b/Desktop.Win/ViewModels/MainWindowViewModel.cs @@ -19,6 +19,8 @@ using System.Windows.Input; using Remotely.ScreenCast.Win.Services; using Remotely.ScreenCast.Core.Interfaces; using Remotely.ScreenCast.Core.Communication; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; namespace Remotely.Desktop.Win.ViewModels { @@ -30,15 +32,12 @@ namespace Remotely.Desktop.Win.ViewModels { Current = this; - CursorIconWatcher = new CursorIconWatcher(); + BuildServices(); + + CursorIconWatcher = Services.GetRequiredService(); CursorIconWatcher.OnChange += CursorIconWatcher_OnChange; - - var screenCaster = new WinScreenCaster(CursorIconWatcher); - var clipboardService = new WinClipboardService(); - clipboardService.BeginWatching(); - var casterSocket = new CasterSocket(new WinInput(), screenCaster, new WinAudioCapturer(), clipboardService); - Conductor = new Conductor(casterSocket, screenCaster); - + Services.GetRequiredService().BeginWatching(); + Conductor = Services.GetRequiredService(); Conductor.SessionIDChanged += SessionIDChanged; Conductor.ViewerRemoved += ViewerRemoved; Conductor.ViewerAdded += ViewerAdded; @@ -46,6 +45,7 @@ namespace Remotely.Desktop.Win.ViewModels } public static MainWindowViewModel Current { get; private set; } + public static IServiceProvider Services => ServiceContainer.Instance; public ICommand ChangeServerCommand { @@ -60,7 +60,6 @@ namespace Remotely.Desktop.Win.ViewModels } public Conductor Conductor { get; } - public CursorIconWatcher CursorIconWatcher { get; private set; } public string Host @@ -128,7 +127,6 @@ namespace Remotely.Desktop.Win.ViewModels } public ObservableCollection Viewers { get; } = new ObservableCollection(); - public void CopyLink() { Clipboard.SetText($"{Host}/RemoteControl?sessionID={SessionID?.Replace(" ", "")}"); @@ -186,6 +184,36 @@ namespace Remotely.Desktop.Win.ViewModels } } + private static void BuildServices() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(builder => + { + builder.AddConsole().AddEventLog(); + }); + + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddTransient(provider => { + try + { + return new DXCapture(); + } + catch + { + return new BitBltCapture(); + } + }); + + + ServiceContainer.Instance = serviceCollection.BuildServiceProvider(); + } private async void CursorIconWatcher_OnChange(object sender, CursorInfo cursor) { if (Conductor?.CasterSocket != null && Conductor?.Viewers?.Count > 0) @@ -205,7 +233,7 @@ namespace Remotely.Desktop.Win.ViewModels Task.Run(async () => { await Conductor.CasterSocket.SendCursorChange(CursorIconWatcher.GetCurrentCursor(), new List() { screenCastRequest.ViewerID }); - _ = Conductor.ScreenCaster.BeginScreenCasting(screenCastRequest); + _ = Services.GetRequiredService().BeginScreenCasting(screenCastRequest); }); } }); diff --git a/ScreenCast.Core/Capture/ScreenCasterBase.cs b/ScreenCast.Core/Capture/ScreenCasterBase.cs index ea30e27d..9bb70c1a 100644 --- a/ScreenCast.Core/Capture/ScreenCasterBase.cs +++ b/ScreenCast.Core/Capture/ScreenCasterBase.cs @@ -16,6 +16,7 @@ using Remotely.ScreenCast.Core.Interfaces; using System.Diagnostics; using System.Threading; using System.Drawing.Imaging; +using Microsoft.Extensions.DependencyInjection; namespace Remotely.ScreenCast.Core.Capture { @@ -25,9 +26,10 @@ namespace Remotely.ScreenCast.Core.Capture string requesterName, ICapturer capturer) { - var viewers = Conductor.Current.Viewers; - var mode = Conductor.Current.Mode; - var casterSocket = Conductor.Current.CasterSocket; + var conductor = ServiceContainer.Instance.GetRequiredService(); + var viewers = conductor.Viewers; + var mode = conductor.Mode; + var casterSocket = ServiceContainer.Instance.GetRequiredService(); Logger.Write($"Starting screen cast. Requester: {requesterName}. Viewer ID: {viewerID}. Capturer: {capturer.GetType().ToString()}. App Mode: {mode}"); @@ -47,7 +49,7 @@ namespace Remotely.ScreenCast.Core.Capture if (mode == Enums.AppMode.Normal) { - Conductor.Current.InvokeViewerAdded(viewer); + conductor.InvokeViewerAdded(viewer); } if (OSUtils.IsWindows) @@ -79,7 +81,7 @@ namespace Remotely.ScreenCast.Core.Capture break; } - if (Conductor.Current.IsDebug) + if (conductor.IsDebug) { while (fpsQueue.Any() && DateTime.Now - fpsQueue.Peek() > TimeSpan.FromSeconds(1)) { diff --git a/ScreenCast.Core/Communication/CasterSocket.cs b/ScreenCast.Core/Communication/CasterSocket.cs index e5d20a6f..7a20f508 100644 --- a/ScreenCast.Core/Communication/CasterSocket.cs +++ b/ScreenCast.Core/Communication/CasterSocket.cs @@ -143,7 +143,7 @@ namespace Remotely.ScreenCast.Core.Communication } private void ApplyConnectionHandlers() { - var conductor = Conductor.Current; + var conductor = ServiceContainer.Instance.GetRequiredService(); Connection.Closed += (ex) => { Logger.Write($"Connection closed. Error: {ex?.Message}"); @@ -432,7 +432,8 @@ namespace Remotely.ScreenCast.Core.Communication private async void ClipboardService_ClipboardTextChanged(object sender, string clipboardText) { - var viewerIDs = Conductor.Current.Viewers.Keys.ToList(); + var conductor = ServiceContainer.Instance.GetRequiredService(); + var viewerIDs = conductor.Viewers.Keys.ToList(); if (viewerIDs.Any()) { await SendClipboardText(clipboardText, viewerIDs); diff --git a/ScreenCast.Core/Conductor.cs b/ScreenCast.Core/Conductor.cs index 68fc3bd5..1f349eae 100644 --- a/ScreenCast.Core/Conductor.cs +++ b/ScreenCast.Core/Conductor.cs @@ -13,15 +13,10 @@ namespace Remotely.ScreenCast.Core { public class Conductor { - public static Conductor Current { get; private set; } - public IScreenCaster ScreenCaster { get; } public bool IsDebug { get; } - public Conductor(CasterSocket casterSocket, - IScreenCaster screenCaster) + public Conductor(CasterSocket casterSocket) { - Current = this; - ScreenCaster = screenCaster; CasterSocket = casterSocket; #if DEBUG IsDebug = true; @@ -38,7 +33,6 @@ namespace Remotely.ScreenCast.Core public CasterSocket CasterSocket { get; private set; } public string DeviceID { get; private set; } public string Host { get; private set; } - public IdleTimer IdleTimer { get; set; } public AppMode Mode { get; private set; } public string RequesterID { get; private set; } public string ServiceID { get; private set; } diff --git a/ScreenCast.Core/ServiceContainer.cs b/ScreenCast.Core/ServiceContainer.cs new file mode 100644 index 00000000..ab7fef27 --- /dev/null +++ b/ScreenCast.Core/ServiceContainer.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Remotely.ScreenCast.Core +{ + public class ServiceContainer + { + private static IServiceProvider instance; + + public static IServiceProvider Instance + { + get + { + return instance; + } + set + { + if (instance != null) + { + throw new Exception("ServiceProvider can only be set once."); + } + instance = value; + } + } + } +} diff --git a/ScreenCast.Core/Services/IdleTimer.cs b/ScreenCast.Core/Services/IdleTimer.cs index 3ec11c14..a8eb7e4f 100644 --- a/ScreenCast.Core/Services/IdleTimer.cs +++ b/ScreenCast.Core/Services/IdleTimer.cs @@ -9,9 +9,9 @@ namespace Remotely.ScreenCast.Core.Services { public class IdleTimer { - public IdleTimer(ConcurrentDictionary viewerList) + public IdleTimer(Conductor conductor) { - ViewerList = viewerList; + ViewerList = conductor.Viewers; Timer.Elapsed += Timer_Elapsed; } diff --git a/ScreenCast.Linux/Program.cs b/ScreenCast.Linux/Program.cs index 260382fa..1e60a4ec 100644 --- a/ScreenCast.Linux/Program.cs +++ b/ScreenCast.Linux/Program.cs @@ -5,28 +5,33 @@ using System.Threading; using Remotely.ScreenCast.Linux.Services; using Remotely.ScreenCast.Linux.Capture; using Remotely.ScreenCast.Core.Communication; +using Remotely.ScreenCast.Core.Interfaces; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; namespace Remotely.ScreenCast.Linux { public class Program { public static Conductor Conductor { get; private set; } + public static IServiceProvider Services => ServiceContainer.Instance; + public static void Main(string[] args) { try { AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; - var screenCaster = new LinuxScreenCaster(); - var casterSocket = new CasterSocket(new X11Input(), screenCaster, new LinuxAudioCapturer(), new LinuxClipboardService()); - Conductor = new Conductor(casterSocket, screenCaster); + + BuildServices(); + + Conductor = Services.GetRequiredService(); Conductor.ProcessArgs(args); Conductor.Connect().ContinueWith(async (task) => { await Conductor.CasterSocket.SendDeviceInfo(Conductor.ServiceID, Environment.MachineName, Conductor.DeviceID); await Conductor.CasterSocket.NotifyRequesterUnattendedReady(Conductor.RequesterID); - Conductor.IdleTimer = new IdleTimer(Conductor.Viewers); - Conductor.IdleTimer.Start(); + Services.GetRequiredService().Start(); }); Thread.Sleep(Timeout.Infinite); @@ -38,6 +43,26 @@ namespace Remotely.ScreenCast.Linux } } + private static void BuildServices() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(builder => + { + builder.AddConsole().AddEventLog(); + }); + + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddTransient(); + + ServiceContainer.Instance = serviceCollection.BuildServiceProvider(); + } + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { diff --git a/ScreenCast.Linux/Services/LinuxScreenCaster.cs b/ScreenCast.Linux/Services/LinuxScreenCaster.cs index 8adfeaff..333991a6 100644 --- a/ScreenCast.Linux/Services/LinuxScreenCaster.cs +++ b/ScreenCast.Linux/Services/LinuxScreenCaster.cs @@ -1,4 +1,5 @@ -using Remotely.ScreenCast.Core; +using Microsoft.Extensions.DependencyInjection; +using Remotely.ScreenCast.Core; using Remotely.ScreenCast.Core.Capture; using Remotely.ScreenCast.Core.Interfaces; using Remotely.ScreenCast.Core.Services; @@ -18,8 +19,9 @@ namespace Remotely.ScreenCast.Linux.Services { try { - await Conductor.Current.CasterSocket.SendCursorChange(new CursorInfo(null, Point.Empty, "default"), new List() { screenCastRequest.ViewerID }); - _ = BeginScreenCasting(screenCastRequest.ViewerID, screenCastRequest.RequesterName, new X11Capture()); + var conductor = ServiceContainer.Instance.GetRequiredService(); + await conductor.CasterSocket.SendCursorChange(new CursorInfo(null, Point.Empty, "default"), new List() { screenCastRequest.ViewerID }); + _ = BeginScreenCasting(screenCastRequest.ViewerID, screenCastRequest.RequesterName, ServiceContainer.Instance.GetRequiredService()); } catch (Exception ex) { diff --git a/ScreenCast.Win/Program.cs b/ScreenCast.Win/Program.cs index f61b5a38..128b1acc 100644 --- a/ScreenCast.Win/Program.cs +++ b/ScreenCast.Win/Program.cs @@ -20,7 +20,7 @@ namespace Remotely.ScreenCast.Win { public static Conductor Conductor { get; private set; } public static CursorIconWatcher CursorIconWatcher { get; private set; } - public static ServiceProvider Services { get; private set; } + public static IServiceProvider Services => ServiceContainer.Instance; private static string CurrentDesktopName { get; set; } @@ -65,8 +65,7 @@ namespace Remotely.ScreenCast.Win Logger.Write("Failed to get initial desktop name."); } await CheckForRelaunch(); - Conductor.IdleTimer = new IdleTimer(Conductor.Viewers); - Conductor.IdleTimer.Start(); + Services.GetRequiredService().Start(); CursorIconWatcher.OnChange += CursorIconWatcher_OnChange; Services.GetRequiredService().BeginWatching(); }); @@ -88,16 +87,26 @@ namespace Remotely.ScreenCast.Win builder.AddConsole().AddEventLog(); }); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddScoped(); - serviceCollection.AddScoped(); - serviceCollection.AddScoped(); - serviceCollection.AddScoped(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddTransient(provider => { + try + { + return new DXCapture(); + } + catch + { + return new BitBltCapture(); + } + }); - Services = serviceCollection.BuildServiceProvider(); + ServiceContainer.Instance = serviceCollection.BuildServiceProvider(); } private static async Task CheckForRelaunch() diff --git a/ScreenCast.Win/Services/WinAudioCapturer.cs b/ScreenCast.Win/Services/WinAudioCapturer.cs index f6dca2da..06753f08 100644 --- a/ScreenCast.Win/Services/WinAudioCapturer.cs +++ b/ScreenCast.Win/Services/WinAudioCapturer.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Timers; +using Microsoft.Extensions.DependencyInjection; using NAudio.Wave; using Remotely.ScreenCast.Core; using Remotely.ScreenCast.Core.Interfaces; @@ -54,7 +55,8 @@ namespace Remotely.ScreenCast.Win.Services { WaveFileWriter.WriteWavFileToStream(ms3, resampler); } - await Conductor.Current.CasterSocket.SendAudioSample(ms3.ToArray(), Program.Conductor.Viewers.Keys.ToList()); + var conductor = ServiceContainer.Instance.GetRequiredService(); + await conductor.CasterSocket.SendAudioSample(ms3.ToArray(), Program.Conductor.Viewers.Keys.ToList()); } } } diff --git a/ScreenCast.Win/Services/WinScreenCaster.cs b/ScreenCast.Win/Services/WinScreenCaster.cs index 1a9eb2e4..92dceee8 100644 --- a/ScreenCast.Win/Services/WinScreenCaster.cs +++ b/ScreenCast.Win/Services/WinScreenCaster.cs @@ -12,6 +12,7 @@ using Remotely.ScreenCast.Core.Models; using Remotely.Shared.Models; using Remotely.ScreenCast.Win.Capture; using Remotely.Shared.Win32; +using Microsoft.Extensions.DependencyInjection; namespace Remotely.ScreenCast.Win.Services { @@ -35,32 +36,11 @@ namespace Remotely.ScreenCast.Win.Services { Logger.Write("Failed to get current desktop before screen casting."); } - - await Conductor.Current.CasterSocket.SendCursorChange(CursorIconWatcher.GetCurrentCursor(), new List() { screenCastRequest.ViewerID }); - _ = BeginScreenCasting(screenCastRequest.ViewerID, screenCastRequest.RequesterName, GetCapturer()); + + var conductor = ServiceContainer.Instance.GetRequiredService(); + await conductor.CasterSocket.SendCursorChange(CursorIconWatcher.GetCurrentCursor(), new List() { screenCastRequest.ViewerID }); + _ = BeginScreenCasting(screenCastRequest.ViewerID, screenCastRequest.RequesterName, ServiceContainer.Instance.GetRequiredService()); } - private static ICapturer GetCapturer() - { - 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(); - } - - return capturer; - } } } diff --git a/Server/Services/BrowserSocketHub.cs b/Server/Services/BrowserSocketHub.cs index 00e2992e..a586fbf0 100644 --- a/Server/Services/BrowserSocketHub.cs +++ b/Server/Services/BrowserSocketHub.cs @@ -120,7 +120,7 @@ namespace Remotely.Server.Services var currentUsers = RCBrowserSocketHub.OrganizationConnectionList.Count(x => x.Value.OrganizationID == RemotelyUser.OrganizationID); if (currentUsers >= AppConfig.RemoteControlSessionLimit) { - await Clients.Caller.SendAsync("DisplayMessage", $"There are already the maximum amount of active remote control sessions for your organization."); + await Clients.Caller.SendAsync("DisplayMessage", $"There are already the maximum amount of active remote control sessions for your organization.", "Max number of concurrent sessions reached."); return; } await this.Clients.Caller.SendAsync("ServiceID", targetDevice.Key);