mirror of
https://github.com/immense/Remotely.git
synced 2025-10-26 11:27:15 +00:00
Re-structure projects. Introduce WPF/Avalonia chat windows. Record session from browser.
This commit is contained in:
parent
7f043d0a84
commit
bae1d1d628
2
.gitignore
vendored
2
.gitignore
vendored
@ -279,3 +279,5 @@ Server/wwwroot/Downloads/Win-x86/Remotely_Desktop.exe
|
||||
/Server/ScaffoldingReadMe.txt
|
||||
/Server/Remotely.db
|
||||
/Desktop.Win.Wrapper/Remotely_Desktop.zip
|
||||
/Server/Remotely.db-wal
|
||||
/Server/Remotely.db-shm
|
||||
|
||||
@ -25,7 +25,6 @@ namespace Remotely.Agent.Installer.Win.Services
|
||||
public event EventHandler<string> ProgressMessageChanged;
|
||||
public event EventHandler<int> ProgressValueChanged;
|
||||
|
||||
public static string CoreRuntimeVersion => "3.1.3";
|
||||
private string InstallPath => Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Program Files", "Remotely");
|
||||
private string Platform => Environment.Is64BitOperatingSystem ? "x64" : "x86";
|
||||
private JavaScriptSerializer Serializer { get; } = new JavaScriptSerializer();
|
||||
@ -44,8 +43,6 @@ namespace Remotely.Agent.Installer.Win.Services
|
||||
return false;
|
||||
}
|
||||
|
||||
//await InstallDesktpRuntimeIfNeeded();
|
||||
|
||||
StopService();
|
||||
|
||||
await StopProcesses();
|
||||
@ -120,7 +117,7 @@ namespace Remotely.Agent.Installer.Win.Services
|
||||
ClearInstallDirectory();
|
||||
ProcessEx.StartHidden("cmd.exe", $"/c timeout 5 & rd /s /q \"{InstallPath}\"");
|
||||
|
||||
ProcessEx.StartHidden("netsh", "advfirewall firewall delete rule name=\"Remotely ScreenCast\"").WaitForExit();
|
||||
ProcessEx.StartHidden("netsh", "advfirewall firewall delete rule name=\"Remotely Desktop\"").WaitForExit();
|
||||
|
||||
GetRegistryBaseKey().DeleteSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Remotely", false);
|
||||
|
||||
@ -135,9 +132,9 @@ namespace Remotely.Agent.Installer.Win.Services
|
||||
|
||||
private void AddFirewallRule()
|
||||
{
|
||||
var screenCastPath = Path.Combine(InstallPath, "ScreenCast", "Remotely_ScreenCast.exe");
|
||||
ProcessEx.StartHidden("netsh", "advfirewall firewall delete rule name=\"Remotely ScreenCast\"").WaitForExit();
|
||||
ProcessEx.StartHidden("netsh", $"advfirewall firewall add rule name=\"Remotely ScreenCast\" program=\"{screenCastPath}\" protocol=any dir=in enable=yes action=allow profile=Private,Domain description=\"The agent that allows screen sharing and remote control for Remotely.\"").WaitForExit();
|
||||
var screenCastPath = Path.Combine(InstallPath, "Desktop", "Remotely_Desktop.exe");
|
||||
ProcessEx.StartHidden("netsh", "advfirewall firewall delete rule name=\"Remotely Desktop\"").WaitForExit();
|
||||
ProcessEx.StartHidden("netsh", $"advfirewall firewall add rule name=\"Remotely Desktop\" program=\"{screenCastPath}\" protocol=any dir=in enable=yes action=allow profile=Private,Domain description=\"The agent that allows screen sharing and remote control for Remotely.\"").WaitForExit();
|
||||
}
|
||||
|
||||
private void BackupDirectory()
|
||||
@ -328,55 +325,6 @@ namespace Remotely.Agent.Installer.Win.Services
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InstallDesktpRuntimeIfNeeded()
|
||||
{
|
||||
Logger.Write("Checking for .NET Core runtime.");
|
||||
var uninstallKeys = new List<RegistryKey>();
|
||||
var runtimeInstalled = false;
|
||||
|
||||
foreach (var subkeyName in GetRegistryBaseKey().OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\", false).GetSubKeyNames())
|
||||
{
|
||||
var subkey = GetRegistryBaseKey().OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" + subkeyName, false);
|
||||
if (subkey?.GetValue("DisplayName")?.ToString()?.Contains($"Microsoft Windows Desktop Runtime - {CoreRuntimeVersion}") == true)
|
||||
{
|
||||
runtimeInstalled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!runtimeInstalled)
|
||||
{
|
||||
Logger.Write("Downloading .NET Core runtime.");
|
||||
ProgressMessageChanged.Invoke(this, "Downloading the .NET Core runtime.");
|
||||
var client = new WebClient();
|
||||
client.DownloadProgressChanged += (sender, args) =>
|
||||
{
|
||||
ProgressValueChanged?.Invoke(this, args.ProgressPercentage);
|
||||
};
|
||||
var downloadUrl = string.Empty;
|
||||
if (Environment.Is64BitOperatingSystem)
|
||||
{
|
||||
downloadUrl = "https://download.visualstudio.microsoft.com/download/pr/5954c748-86a1-4823-9e7d-d35f6039317a/169e82cbf6fdeb678c5558c5d0a83834/windowsdesktop-runtime-3.1.3-win-x64.exe";
|
||||
}
|
||||
else
|
||||
{
|
||||
downloadUrl = "https://download.visualstudio.microsoft.com/download/pr/7cd5c874-5d11-4e72-81f0-4a005d956708/0eb310169770c893407169fc3abaac4f/windowsdesktop-runtime-3.1.3-win-x86.exe";
|
||||
}
|
||||
var targetFile = Path.Combine(Path.GetTempPath(), "windowsdesktop-runtime.exe");
|
||||
await client.DownloadFileTaskAsync(downloadUrl, targetFile);
|
||||
|
||||
Logger.Write("Installing .NET Core runtime.");
|
||||
ProgressMessageChanged?.Invoke(this, "Installing the .NET Core runtime.");
|
||||
ProgressValueChanged?.Invoke(this, 0);
|
||||
|
||||
await Task.Run(() => { ProcessEx.StartHidden(targetFile, "/install /quiet /norestart").WaitForExit(); });
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Write(".NET Core runtime already installed.");
|
||||
}
|
||||
}
|
||||
|
||||
private void InstallService()
|
||||
{
|
||||
Logger.Write("Installing service.");
|
||||
@ -437,7 +385,7 @@ namespace Remotely.Agent.Installer.Win.Services
|
||||
private async Task StopProcesses()
|
||||
{
|
||||
ProgressMessageChanged?.Invoke(this, "Stopping Remotely processes.");
|
||||
var procs = Process.GetProcessesByName("Remotely_Agent").Concat(Process.GetProcessesByName("Remotely_ScreenCast"));
|
||||
var procs = Process.GetProcessesByName("Remotely_Agent").Concat(Process.GetProcessesByName("Remotely_Desktop"));
|
||||
|
||||
foreach (var proc in procs)
|
||||
{
|
||||
|
||||
@ -23,22 +23,22 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Management.Infrastructure" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.PowerShell.Commands.Diagnostics" Version="7.0.1" />
|
||||
<PackageReference Include="Microsoft.PowerShell.Commands.Management" Version="7.0.1" />
|
||||
<PackageReference Include="Microsoft.PowerShell.Commands.Utility" Version="7.0.1" />
|
||||
<PackageReference Include="Microsoft.PowerShell.CoreCLR.Eventing" Version="7.0.1" />
|
||||
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.0.1" />
|
||||
<PackageReference Include="Microsoft.PowerShell.Security" Version="7.0.1" />
|
||||
<PackageReference Include="Microsoft.WSMan.Management" Version="7.0.1" />
|
||||
<PackageReference Include="Microsoft.WSMan.Runtime" Version="7.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.PowerShell.Commands.Diagnostics" Version="7.0.3" />
|
||||
<PackageReference Include="Microsoft.PowerShell.Commands.Management" Version="7.0.3" />
|
||||
<PackageReference Include="Microsoft.PowerShell.Commands.Utility" Version="7.0.3" />
|
||||
<PackageReference Include="Microsoft.PowerShell.CoreCLR.Eventing" Version="7.0.3" />
|
||||
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.0.3" />
|
||||
<PackageReference Include="Microsoft.PowerShell.Security" Version="7.0.3" />
|
||||
<PackageReference Include="Microsoft.WSMan.Management" Version="7.0.3" />
|
||||
<PackageReference Include="Microsoft.WSMan.Runtime" Version="7.0.3" />
|
||||
<PackageReference Include="NETStandard.Library" Version="2.0.3" />
|
||||
<PackageReference Include="System.Management.Automation" Version="7.0.1" />
|
||||
<PackageReference Include="System.Management.Automation" Version="7.0.3" />
|
||||
<PackageReference Include="System.ServiceProcess.ServiceController" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -52,9 +52,7 @@
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Services\WindowsService.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Update="Services\WindowsService.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -39,7 +39,7 @@ namespace Remotely.Agent
|
||||
{
|
||||
builder.AddConsole().AddDebug();
|
||||
});
|
||||
serviceCollection.AddSingleton<DeviceSocket>();
|
||||
serviceCollection.AddSingleton<AgentSocket>();
|
||||
serviceCollection.AddScoped<ChatClientService>();
|
||||
serviceCollection.AddTransient<Bash>();
|
||||
serviceCollection.AddTransient<CMD>();
|
||||
@ -79,11 +79,11 @@ namespace Remotely.Agent
|
||||
|
||||
await Services.GetRequiredService<Updater>().BeginChecking();
|
||||
|
||||
await Services.GetRequiredService<DeviceSocket>().Connect();
|
||||
await Services.GetRequiredService<AgentSocket>().Connect();
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Services.GetRequiredService<DeviceSocket>().HandleConnection();
|
||||
await Services.GetRequiredService<AgentSocket>().HandleConnection();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -18,9 +18,9 @@ using Remotely.Shared.Enums;
|
||||
|
||||
namespace Remotely.Agent.Services
|
||||
{
|
||||
public class DeviceSocket
|
||||
public class AgentSocket
|
||||
{
|
||||
public DeviceSocket(ConfigService configService,
|
||||
public AgentSocket(ConfigService configService,
|
||||
Uninstaller uninstaller,
|
||||
CommandExecutor commandExecutor,
|
||||
ScriptRunner scriptRunner,
|
||||
@ -52,7 +52,7 @@ namespace Remotely.Agent.Services
|
||||
ConnectionInfo = ConfigService.GetConnectionInfo();
|
||||
|
||||
HubConnection = new HubConnectionBuilder()
|
||||
.WithUrl(ConnectionInfo.Host + "/DeviceHub")
|
||||
.WithUrl(ConnectionInfo.Host + "/AgentHub")
|
||||
.Build();
|
||||
|
||||
RegisterMessageHandlers();
|
||||
@ -121,7 +121,7 @@ namespace Remotely.Agent.Services
|
||||
var waitTime = new Random().Next(1000, 30000);
|
||||
Logger.Write($"Websocket closed. Reconnecting in {waitTime / 1000} seconds...");
|
||||
await Task.Delay(waitTime);
|
||||
await Program.Services.GetRequiredService<DeviceSocket>().Connect();
|
||||
await Program.Services.GetRequiredService<AgentSocket>().Connect();
|
||||
await Program.Services.GetRequiredService<Updater>().CheckForUpdates();
|
||||
}
|
||||
}
|
||||
@ -149,8 +149,8 @@ namespace Remotely.Agent.Services
|
||||
// TODO: Remove possibility for circular dependencies in the future
|
||||
// by emitting these events so other services can listen for them.
|
||||
|
||||
HubConnection.On("Chat", async (string message, string orgName, string senderConnectionID) => {
|
||||
await ChatService.SendMessage(message, orgName, senderConnectionID, HubConnection);
|
||||
HubConnection.On("Chat", async (string senderName, string message, string orgName, bool disconnected, string senderConnectionID) => {
|
||||
await ChatService.SendMessage(senderName, message, orgName, disconnected, senderConnectionID, HubConnection);
|
||||
});
|
||||
HubConnection.On("DownloadFile", async (string filePath, string senderConnectionID) =>
|
||||
{
|
||||
@ -24,14 +24,14 @@ namespace Remotely.Agent.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
var rcBinaryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ScreenCast", EnvironmentHelper.ScreenCastExecutableFileName);
|
||||
var rcBinaryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Desktop", EnvironmentHelper.DesktopExecutableFileName);
|
||||
if (!File.Exists(rcBinaryPath))
|
||||
{
|
||||
await hubConnection.SendAsync("DisplayMessage", "Chat executable not found on target device.", "Executable not found on device.", requesterID);
|
||||
}
|
||||
|
||||
|
||||
// Start ScreenCast.
|
||||
// Start Desktop app.
|
||||
await hubConnection.SendAsync("DisplayMessage", $"Starting chat service...", "Starting chat service.", requesterID);
|
||||
if (EnvironmentHelper.IsWindows)
|
||||
{
|
||||
@ -50,7 +50,7 @@ namespace Remotely.Agent.Services
|
||||
out var procInfo);
|
||||
if (!result)
|
||||
{
|
||||
await hubConnection.SendAsync("DisplayMessage", "Remote control failed to start on target device.", "Failed to start remote control.", requesterID);
|
||||
await hubConnection.SendAsync("DisplayMessage", "Chat service failed to start on target device.", "Failed to start chat service.", requesterID);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -60,14 +60,14 @@ namespace Remotely.Agent.Services
|
||||
}
|
||||
else if (EnvironmentHelper.IsLinux)
|
||||
{
|
||||
var args = $"xterm -e {rcBinaryPath} -mode Chat -requester \"{requesterID}\" -organization \"{orgName}\" & disown";
|
||||
return StartLinuxScreenCaster(args);
|
||||
var args = $"{rcBinaryPath} -mode Chat -requester \"{requesterID}\" -organization \"{orgName}\" & disown";
|
||||
return StartLinuxDesktopApp(args);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
await hubConnection.SendAsync("DisplayMessage", "Remote control failed to start on target device.", "Failed to start remote control.", requesterID);
|
||||
await hubConnection.SendAsync("DisplayMessage", "Chat service failed to start on target device.", "Failed to start chat service.", requesterID);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@ -76,7 +76,7 @@ namespace Remotely.Agent.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
var rcBinaryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ScreenCast", EnvironmentHelper.ScreenCastExecutableFileName);
|
||||
var rcBinaryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Desktop", EnvironmentHelper.DesktopExecutableFileName);
|
||||
if (!File.Exists(rcBinaryPath))
|
||||
{
|
||||
await hubConnection.SendAsync("DisplayMessage", "Remote control executable not found on target device.", "Executable not found on device.", requesterID);
|
||||
@ -84,13 +84,15 @@ namespace Remotely.Agent.Services
|
||||
}
|
||||
|
||||
|
||||
// Start ScreenCast.
|
||||
// Start Desktop app.
|
||||
await hubConnection.SendAsync("DisplayMessage", $"Starting remote control...", "Starting remote control.", requesterID);
|
||||
if (EnvironmentHelper.IsWindows)
|
||||
{
|
||||
|
||||
if (EnvironmentHelper.IsDebug)
|
||||
{
|
||||
// SignalR Connection IDs might start with a hyphen. We surround them
|
||||
// with quotes so the command line will be parsed correctly.
|
||||
Process.Start(rcBinaryPath, $"-mode Unattended -requester \"{requesterID}\" -serviceid \"{serviceID}\" -deviceid {ConnectionInfo.DeviceID} -host {ConnectionInfo.Host}");
|
||||
}
|
||||
else
|
||||
@ -110,7 +112,7 @@ namespace Remotely.Agent.Services
|
||||
else if (EnvironmentHelper.IsLinux)
|
||||
{
|
||||
var args = $"{rcBinaryPath} -mode Unattended -requester \"{requesterID}\" -serviceid \"{serviceID}\" -deviceid {ConnectionInfo.DeviceID} -host {ConnectionInfo.Host} & disown";
|
||||
StartLinuxScreenCaster(args);
|
||||
StartLinuxDesktopApp(args);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -123,13 +125,15 @@ namespace Remotely.Agent.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
var rcBinaryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ScreenCast", EnvironmentHelper.ScreenCastExecutableFileName);
|
||||
// Start ScreenCast.
|
||||
var rcBinaryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Desktop", EnvironmentHelper.DesktopExecutableFileName);
|
||||
// Start Desktop app.
|
||||
if (EnvironmentHelper.IsWindows)
|
||||
{
|
||||
Logger.Write("Restarting screen caster.");
|
||||
if (EnvironmentHelper.IsDebug)
|
||||
{
|
||||
// SignalR Connection IDs might start with a hyphen. We surround them
|
||||
// with quotes so the command line will be parsed correctly.
|
||||
Process.Start(rcBinaryPath, $"-mode Unattended -requester \"{requesterID}\" -serviceid \"{serviceID}\" -deviceid {ConnectionInfo.DeviceID} -host {ConnectionInfo.Host} -relaunch true -viewers {String.Join(",", viewerIDs)}");
|
||||
}
|
||||
else
|
||||
@ -156,7 +160,7 @@ namespace Remotely.Agent.Services
|
||||
else if (EnvironmentHelper.IsLinux)
|
||||
{
|
||||
var args = $"{rcBinaryPath} -mode Unattended -requester \"{requesterID}\" -serviceid \"{serviceID}\" -deviceid {ConnectionInfo.DeviceID} -host {ConnectionInfo.Host} -relaunch true -viewers {string.Join(",", viewerIDs)} & disown";
|
||||
StartLinuxScreenCaster(args);
|
||||
StartLinuxDesktopApp(args);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -167,7 +171,7 @@ namespace Remotely.Agent.Services
|
||||
}
|
||||
}
|
||||
|
||||
private int StartLinuxScreenCaster(string args)
|
||||
private int StartLinuxDesktopApp(string args)
|
||||
{
|
||||
var xauthority = string.Empty;
|
||||
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Remotely.Agent.Models;
|
||||
using Remotely.Shared.Models;
|
||||
using Remotely.Shared.Utilities;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
using System.Runtime.Caching;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -32,44 +34,58 @@ namespace Remotely.Agent.Services
|
||||
};
|
||||
|
||||
private MemoryCache ChatClients { get; } = new MemoryCache("ChatClients");
|
||||
public async Task SendMessage(string message, string orgName, string senderConnectionID, HubConnection hubConnection)
|
||||
public async Task SendMessage(string senderName,
|
||||
string message,
|
||||
string orgName,
|
||||
bool disconnected,
|
||||
string senderConnectionID,
|
||||
HubConnection hubConnection)
|
||||
{
|
||||
if (!await MessageLock.WaitAsync(30000))
|
||||
{
|
||||
Logger.Write("Timed out waiting for chat message lock.", Shared.Enums.EventType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (await MessageLock.WaitAsync(30000))
|
||||
ChatSession chatSession;
|
||||
if (!ChatClients.Contains(senderConnectionID))
|
||||
{
|
||||
ChatSession chatSession;
|
||||
if (!ChatClients.Contains(senderConnectionID))
|
||||
if (disconnected)
|
||||
{
|
||||
var rcBinaryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ScreenCast", EnvironmentHelper.ScreenCastExecutableFileName);
|
||||
var procID = await AppLauncher.LaunchChatService(orgName, senderConnectionID, hubConnection);
|
||||
|
||||
var clientPipe = new NamedPipeClientStream(".", "Remotely_Chat" + senderConnectionID, PipeDirection.InOut, PipeOptions.Asynchronous);
|
||||
clientPipe.Connect(15000);
|
||||
if (!clientPipe.IsConnected)
|
||||
{
|
||||
Logger.Write("Failed to connect to chat host.");
|
||||
return;
|
||||
}
|
||||
chatSession = new ChatSession() { PipeStream = clientPipe, ProcessID = procID };
|
||||
_ = Task.Run(async () => { await ReadFromStream(chatSession.PipeStream, senderConnectionID, hubConnection); });
|
||||
ChatClients.Add(senderConnectionID, chatSession, CacheItemPolicy);
|
||||
}
|
||||
|
||||
chatSession = (ChatSession)ChatClients.Get(senderConnectionID);
|
||||
|
||||
if (!chatSession.PipeStream.IsConnected)
|
||||
{
|
||||
ChatClients.Remove(senderConnectionID);
|
||||
await hubConnection.SendAsync("DisplayMessage", "Chat disconnected. Please try again.", "Chat disconnected.");
|
||||
// Don't start a new session just to show a disconnected message.
|
||||
return;
|
||||
}
|
||||
|
||||
using (var sw = new StreamWriter(chatSession.PipeStream, leaveOpen: true))
|
||||
var procID = await AppLauncher.LaunchChatService(orgName, senderConnectionID, hubConnection);
|
||||
|
||||
var clientPipe = new NamedPipeClientStream(".", "Remotely_Chat" + senderConnectionID, PipeDirection.InOut, PipeOptions.Asynchronous);
|
||||
clientPipe.Connect(15000);
|
||||
if (!clientPipe.IsConnected)
|
||||
{
|
||||
await sw.WriteLineAsync(message);
|
||||
await sw.FlushAsync();
|
||||
Logger.Write("Failed to connect to chat host.");
|
||||
return;
|
||||
}
|
||||
chatSession = new ChatSession() { PipeStream = clientPipe, ProcessID = procID };
|
||||
_ = Task.Run(async () => { await ReadFromStream(chatSession.PipeStream, senderConnectionID, hubConnection); });
|
||||
ChatClients.Add(senderConnectionID, chatSession, CacheItemPolicy);
|
||||
}
|
||||
|
||||
chatSession = (ChatSession)ChatClients.Get(senderConnectionID);
|
||||
|
||||
if (!chatSession.PipeStream.IsConnected)
|
||||
{
|
||||
ChatClients.Remove(senderConnectionID);
|
||||
await hubConnection.SendAsync("DisplayMessage", "Chat disconnected. Please try again.", "Chat disconnected.", senderConnectionID);
|
||||
return;
|
||||
}
|
||||
|
||||
using (var sw = new StreamWriter(chatSession.PipeStream, leaveOpen: true))
|
||||
{
|
||||
var chatMessage = new ChatMessage(senderName, message, disconnected);
|
||||
await sw.WriteLineAsync(JsonSerializer.Serialize(chatMessage));
|
||||
await sw.FlushAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -88,10 +104,15 @@ namespace Remotely.Agent.Services
|
||||
{
|
||||
using (var sr = new StreamReader(clientPipe, leaveOpen: true))
|
||||
{
|
||||
var message = await sr.ReadLineAsync();
|
||||
await hubConnection.SendAsync("Chat", message, senderConnectionID);
|
||||
var messageJson = await sr.ReadLineAsync();
|
||||
if (!string.IsNullOrWhiteSpace(messageJson))
|
||||
{
|
||||
var chatMessage = JsonSerializer.Deserialize<ChatMessage>(messageJson);
|
||||
await hubConnection.SendAsync("Chat", chatMessage.Message, false, senderConnectionID);
|
||||
}
|
||||
}
|
||||
}
|
||||
await hubConnection.SendAsync("Chat", string.Empty, true, senderConnectionID);
|
||||
ChatClients.Remove(senderConnectionID);
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ namespace Remotely.Agent.Services
|
||||
changeDescription.Reason == SessionChangeReason.RemoteDisconnect)
|
||||
{
|
||||
|
||||
foreach (var screenCaster in Process.GetProcessesByName("Remotely_ScreenCast"))
|
||||
foreach (var screenCaster in Process.GetProcessesByName("Remotely_Desktop"))
|
||||
{
|
||||
if (screenCaster.SessionId == changeDescription.SessionId)
|
||||
{
|
||||
|
||||
@ -1,15 +1,14 @@
|
||||
using Remotely.Shared.Models;
|
||||
using Remotely.ScreenCast.Core.Enums;
|
||||
using Remotely.ScreenCast.Core.Models;
|
||||
using Remotely.ScreenCast.Core.Communication;
|
||||
using Remotely.ScreenCast.Core.Services;
|
||||
using Remotely.Desktop.Core.Enums;
|
||||
using Remotely.Desktop.Core.Models;
|
||||
using Remotely.Desktop.Core.Services;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Remotely.Shared.Utilities;
|
||||
|
||||
namespace Remotely.ScreenCast.Core
|
||||
namespace Remotely.Desktop.Core
|
||||
{
|
||||
public class Conductor
|
||||
{
|
||||
@ -62,29 +61,14 @@ namespace Remotely.ScreenCast.Core
|
||||
{
|
||||
if (!key.Contains("-"))
|
||||
{
|
||||
Logger.Write("Command line arguments are invalid.");
|
||||
Logger.Write($"Command line arguments are invalid. Key: {key}");
|
||||
i -= 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
key = key.Trim().Replace("-", "").ToLower();
|
||||
if (i + 1 == args.Length)
|
||||
{
|
||||
ArgDict.Add(key, "true");
|
||||
continue;
|
||||
}
|
||||
var value = args[i + 1];
|
||||
if (value != null)
|
||||
{
|
||||
if (value.StartsWith("-"))
|
||||
{
|
||||
ArgDict.Add(key, "true");
|
||||
i -= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ArgDict.Add(key, args[i + 1].Trim());
|
||||
}
|
||||
}
|
||||
|
||||
ArgDict.Add(key, args[i + 1].Trim());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -94,7 +78,14 @@ namespace Remotely.ScreenCast.Core
|
||||
|
||||
}
|
||||
|
||||
Mode = (AppMode)Enum.Parse(typeof(AppMode), ArgDict["mode"], true);
|
||||
if (ArgDict.TryGetValue("mode", out var mode))
|
||||
{
|
||||
Mode = (AppMode)Enum.Parse(typeof(AppMode), mode, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mode = AppMode.Normal;
|
||||
}
|
||||
|
||||
if (ArgDict.TryGetValue("host", out var host))
|
||||
{
|
||||
@ -102,11 +93,11 @@ namespace Remotely.ScreenCast.Core
|
||||
}
|
||||
if (ArgDict.TryGetValue("requester", out var requester))
|
||||
{
|
||||
RequesterID = requester.Trim('"');
|
||||
RequesterID = requester;
|
||||
}
|
||||
if (ArgDict.TryGetValue("serviceid", out var serviceID))
|
||||
{
|
||||
ServiceID = serviceID.Trim('"');
|
||||
ServiceID = serviceID;
|
||||
}
|
||||
if (ArgDict.TryGetValue("deviceid", out var deviceID))
|
||||
{
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<RootNamespace>Remotely.ScreenCast.Core</RootNamespace>
|
||||
<AssemblyName>Remotely_ScreenCast.Core</AssemblyName>
|
||||
<RootNamespace>Remotely.Desktop.Core</RootNamespace>
|
||||
<AssemblyName>Remotely_Desktop.Core</AssemblyName>
|
||||
<Platforms>AnyCPU;x64;x86</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
@ -32,21 +32,17 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.MixedReality.WebRTC" Version="1.0.3" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="4.7.0" />
|
||||
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Shared\Shared.csproj" />
|
||||
</ItemGroup>
|
||||
@ -1,4 +1,4 @@
|
||||
namespace Remotely.ScreenCast.Core.Enums
|
||||
namespace Remotely.Desktop.Core.Enums
|
||||
{
|
||||
public enum AppMode
|
||||
{
|
||||
@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Remotely.ScreenCast.Core.Interfaces
|
||||
namespace Remotely.Desktop.Core.Interfaces
|
||||
{
|
||||
public interface IAudioCapturer
|
||||
{
|
||||
12
Desktop.Core/Interfaces/IChatHostService.cs
Normal file
12
Desktop.Core/Interfaces/IChatHostService.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely.Desktop.Core.Interfaces
|
||||
{
|
||||
public interface IChatHostService
|
||||
{
|
||||
Task StartChat(string requesterID, string organizationName);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Remotely.ScreenCast.Core.Interfaces
|
||||
namespace Remotely.Desktop.Core.Interfaces
|
||||
{
|
||||
public interface IClipboardService
|
||||
{
|
||||
@ -3,7 +3,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Remotely.ScreenCast.Core.Interfaces
|
||||
namespace Remotely.Desktop.Core.Interfaces
|
||||
{
|
||||
public interface ICursorIconWatcher
|
||||
{
|
||||
@ -1,6 +1,6 @@
|
||||
using Remotely.ScreenCast.Core.Models;
|
||||
using Remotely.Desktop.Core.Models;
|
||||
|
||||
namespace Remotely.ScreenCast.Core.Interfaces
|
||||
namespace Remotely.Desktop.Core.Interfaces
|
||||
{
|
||||
public interface IKeyboardMouseInput
|
||||
{
|
||||
@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Remotely.ScreenCast.Core.Interfaces
|
||||
namespace Remotely.Desktop.Core.Interfaces
|
||||
{
|
||||
public interface IScreenCapturer : IDisposable
|
||||
{
|
||||
@ -1,7 +1,7 @@
|
||||
using Remotely.Shared.Models;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely.ScreenCast.Core.Interfaces
|
||||
namespace Remotely.Desktop.Core.Interfaces
|
||||
{
|
||||
public interface IScreenCaster
|
||||
{
|
||||
@ -1,20 +1,23 @@
|
||||
using Remotely.ScreenCast.Core.Communication;
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.ScreenCast.Core.Services;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using Remotely.Desktop.Core.Services;
|
||||
using Remotely.Shared.Helpers;
|
||||
using Remotely.Shared.Models;
|
||||
using Remotely.Shared.Utilities;
|
||||
using Remotely.Shared.Win32;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely.ScreenCast.Core.Models
|
||||
namespace Remotely.Desktop.Core.Models
|
||||
{
|
||||
public class Viewer : IDisposable
|
||||
{
|
||||
private int imageQuality;
|
||||
private DateTimeOffset lastQualityAdjustment;
|
||||
private readonly int defaultImageQuality = 60;
|
||||
|
||||
public Viewer(CasterSocket casterSocket,
|
||||
IScreenCapturer screenCapturer,
|
||||
@ -26,7 +29,7 @@ namespace Remotely.ScreenCast.Core.Models
|
||||
CasterSocket = casterSocket;
|
||||
WebRtcSessionFactory = webRtcSessionFactory;
|
||||
EncoderParams = new EncoderParameters();
|
||||
ImageQuality = 60;
|
||||
ImageQuality = defaultImageQuality;
|
||||
ClipboardService = clipboardService;
|
||||
ClipboardService.ClipboardTextChanged += ClipboardService_ClipboardTextChanged;
|
||||
AudioCapturer = audioCapturer;
|
||||
@ -66,21 +69,13 @@ namespace Remotely.ScreenCast.Core.Models
|
||||
|
||||
public bool IsConnected => CasterSocket.IsConnected;
|
||||
public string Name { get; set; }
|
||||
public ConcurrentQueue<DateTimeOffset> PendingSentFrames { get; } = new ConcurrentQueue<DateTimeOffset>();
|
||||
public WebRtcSession RtcSession { get; set; }
|
||||
public string ViewerConnectionID { get; set; }
|
||||
public int WebSocketBuffer { get; set; }
|
||||
private IAudioCapturer AudioCapturer { get; }
|
||||
private CasterSocket CasterSocket { get; }
|
||||
private IClipboardService ClipboardService { get; }
|
||||
private int CurrentBuffer
|
||||
{
|
||||
get
|
||||
{
|
||||
return IsUsingWebRtc() ?
|
||||
(int)RtcSession.CurrentBuffer :
|
||||
WebSocketBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
private IWebRtcSessionFactory WebRtcSessionFactory { get; }
|
||||
public void Dispose()
|
||||
{
|
||||
@ -114,7 +109,7 @@ namespace Remotely.ScreenCast.Core.Models
|
||||
|
||||
public bool IsStalled()
|
||||
{
|
||||
return RtcSession?.CurrentBuffer > 1_000_000 || WebSocketBuffer > 1_000_000;
|
||||
return PendingSentFrames.TryPeek(out var result) && DateTimeOffset.Now - result > TimeSpan.FromSeconds(15);
|
||||
}
|
||||
|
||||
public bool IsUsingWebRtc()
|
||||
@ -147,14 +142,14 @@ namespace Remotely.ScreenCast.Core.Models
|
||||
|
||||
public async Task SendScreenCapture(byte[] encodedImageBytes, int left, int top, int width, int height)
|
||||
{
|
||||
PendingSentFrames.Enqueue(DateTimeOffset.Now);
|
||||
|
||||
await SendToViewer(() =>
|
||||
{
|
||||
RtcSession.SendCaptureFrame(left, top, width, height, encodedImageBytes, ImageQuality);
|
||||
WebSocketBuffer = 0;
|
||||
}, async () =>
|
||||
{
|
||||
await CasterSocket.SendScreenCapture(encodedImageBytes, ViewerConnectionID, left, top, width, height, ImageQuality);
|
||||
WebSocketBuffer += encodedImageBytes.Length;
|
||||
});
|
||||
}
|
||||
|
||||
@ -181,22 +176,25 @@ namespace Remotely.ScreenCast.Core.Models
|
||||
|
||||
public async Task ThrottleIfNeeded()
|
||||
{
|
||||
if (CurrentBuffer > 200_000)
|
||||
if (AutoAdjustQuality && DateTimeOffset.Now - lastQualityAdjustment > TimeSpan.FromSeconds(2))
|
||||
{
|
||||
if (AutoAdjustQuality)
|
||||
lastQualityAdjustment = DateTimeOffset.Now;
|
||||
if (PendingSentFrames.TryPeek(out var result) && DateTimeOffset.Now - result > TimeSpan.FromMilliseconds(200))
|
||||
{
|
||||
var imageAdjust = (double)CurrentBuffer / 200_000 * 5;
|
||||
ImageQuality = (int)Math.Max(ImageQuality - imageAdjust, 0);
|
||||
Logger.Write($"Auto-adjusting image quality. Quality: {ImageQuality}");
|
||||
var latency = (DateTimeOffset.Now - result).TotalMilliseconds;
|
||||
ImageQuality = (int)(200 / latency * defaultImageQuality);
|
||||
}
|
||||
else
|
||||
{
|
||||
ImageQuality = defaultImageQuality;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Write($"Throttling output due to buffer size. Size: {CurrentBuffer}.");
|
||||
await TaskHelper.DelayUntil(() => CurrentBuffer < 200_000, TimeSpan.FromSeconds(1));
|
||||
}
|
||||
else if (AutoAdjustQuality)
|
||||
{
|
||||
ImageQuality = Math.Min(ImageQuality + 1, 60);
|
||||
}
|
||||
await TaskHelper.DelayUntil(() => PendingSentFrames.Count < 5 &&
|
||||
(
|
||||
!PendingSentFrames.TryPeek(out var result) || DateTimeOffset.Now - result < TimeSpan.FromSeconds(1)
|
||||
),
|
||||
TimeSpan.MaxValue);
|
||||
}
|
||||
|
||||
private async void AudioCapturer_AudioSampleReady(object sender, byte[] sample)
|
||||
@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Remotely.ScreenCast.Core
|
||||
namespace Remotely.Desktop.Core
|
||||
{
|
||||
public class ServiceContainer
|
||||
{
|
||||
@ -7,12 +7,12 @@ using System.Threading.Tasks;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using Remotely.ScreenCast.Core.Services;
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Remotely.Shared.Utilities;
|
||||
using System.Threading;
|
||||
|
||||
namespace Remotely.ScreenCast.Core.Communication
|
||||
namespace Remotely.Desktop.Core.Services
|
||||
{
|
||||
public class CasterSocket
|
||||
{
|
||||
@ -47,7 +47,7 @@ namespace Remotely.ScreenCast.Core.Communication
|
||||
await Connection.DisposeAsync();
|
||||
}
|
||||
Connection = new HubConnectionBuilder()
|
||||
.WithUrl($"{host}/RCDeviceHub")
|
||||
.WithUrl($"{host}/CasterHub")
|
||||
.AddMessagePackProtocol()
|
||||
.WithAutomaticReconnect()
|
||||
.Build();
|
||||
@ -377,11 +377,17 @@ namespace Remotely.ScreenCast.Core.Communication
|
||||
conductor.InvokeViewerRemoved(viewerID);
|
||||
|
||||
});
|
||||
Connection.On("FrameReceived", (int bytesReceived, string viewerID) =>
|
||||
Connection.On("FrameReceived", (string viewerID) =>
|
||||
{
|
||||
if (conductor.Viewers.TryGetValue(viewerID, out var viewer))
|
||||
{
|
||||
viewer.WebSocketBuffer = Math.Max(0, viewer.WebSocketBuffer - bytesReceived);
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
if (viewer.PendingSentFrames.TryDequeue(out _))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Concurrent;
|
||||
using Remotely.Shared.Win32;
|
||||
|
||||
namespace Remotely.ScreenCast.Core.Services
|
||||
namespace Remotely.Desktop.Core.Services
|
||||
{
|
||||
public interface IFileTransferService
|
||||
{
|
||||
@ -1,10 +1,10 @@
|
||||
using Remotely.ScreenCast.Core.Models;
|
||||
using Remotely.Desktop.Core.Models;
|
||||
using Remotely.Shared.Utilities;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Timers;
|
||||
|
||||
namespace Remotely.ScreenCast.Core.Services
|
||||
namespace Remotely.Desktop.Core.Services
|
||||
{
|
||||
public class IdleTimer
|
||||
{
|
||||
@ -1,7 +1,6 @@
|
||||
using MessagePack;
|
||||
using Remotely.ScreenCast.Core.Communication;
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.ScreenCast.Core.Models;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using Remotely.Desktop.Core.Models;
|
||||
using Remotely.Shared.Enums;
|
||||
using Remotely.Shared.Models.RtcDtos;
|
||||
using Remotely.Shared.Utilities;
|
||||
@ -11,9 +10,10 @@ using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely.ScreenCast.Core.Services
|
||||
namespace Remotely.Desktop.Core.Services
|
||||
{
|
||||
public interface IRtcMessageHandler
|
||||
{
|
||||
@ -129,6 +129,9 @@ namespace Remotely.ScreenCast.Core.Services
|
||||
case BinaryDtoType.SetKeyStatesUp:
|
||||
SetKeyStatesUp();
|
||||
break;
|
||||
case BinaryDtoType.FrameReceived:
|
||||
HandleFrameReceived();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -167,6 +170,16 @@ namespace Remotely.ScreenCast.Core.Services
|
||||
await Viewer.SendWindowsSessions();
|
||||
}
|
||||
|
||||
private void HandleFrameReceived()
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
if (Viewer.PendingSentFrames.TryDequeue(out _))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
private void KeyDown(byte[] message)
|
||||
{
|
||||
var dto = MessagePackSerializer.Deserialize<KeyDownDto>(message);
|
||||
@ -1,21 +1,20 @@
|
||||
using Remotely.ScreenCast.Core.Models;
|
||||
using Remotely.ScreenCast.Core.Communication;
|
||||
using Remotely.Desktop.Core.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing.Imaging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Remotely.ScreenCast.Core.Utilities;
|
||||
using Remotely.Desktop.Core.Utilities;
|
||||
using Remotely.Shared.Utilities;
|
||||
using System.Collections.Concurrent;
|
||||
using Remotely.ScreenCast.Core.Enums;
|
||||
using Remotely.Desktop.Core.Enums;
|
||||
using Remotely.Shared.Models;
|
||||
using Remotely.Shared.Win32;
|
||||
|
||||
namespace Remotely.ScreenCast.Core.Services
|
||||
namespace Remotely.Desktop.Core.Services
|
||||
{
|
||||
public class ScreenCaster : IScreenCaster
|
||||
{
|
||||
@ -126,7 +125,6 @@ namespace Remotely.ScreenCast.Core.Services
|
||||
|
||||
if (encodedImageBytes?.Length > 0)
|
||||
{
|
||||
|
||||
await viewer.SendScreenCapture(encodedImageBytes, diffArea.Left, diffArea.Top, diffArea.Width, diffArea.Height);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
using MessagePack;
|
||||
using Microsoft.MixedReality.WebRTC;
|
||||
using Remotely.ScreenCast.Core.Services;
|
||||
using Remotely.Shared.Models;
|
||||
using Remotely.Shared.Models.RtcDtos;
|
||||
using Remotely.Shared.Utilities;
|
||||
@ -9,7 +8,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely.ScreenCast.Core.Communication
|
||||
namespace Remotely.Desktop.Core.Services
|
||||
{
|
||||
public class WebRtcSession : IDisposable
|
||||
{
|
||||
@ -1,11 +1,10 @@
|
||||
using Remotely.ScreenCast.Core.Communication;
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.ScreenCast.Core.Models;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using Remotely.Desktop.Core.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Remotely.ScreenCast.Core.Services
|
||||
namespace Remotely.Desktop.Core.Services
|
||||
{
|
||||
public interface IWebRtcSessionFactory
|
||||
{
|
||||
@ -5,7 +5,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Remotely.ScreenCast.Core.Utilities
|
||||
namespace Remotely.Desktop.Core.Utilities
|
||||
{
|
||||
public class ImageUtils
|
||||
{
|
||||
@ -1,6 +1,8 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Remotely.Desktop.Core;
|
||||
using Remotely.Desktop.Linux.ViewModels;
|
||||
using Remotely.Desktop.Linux.Views;
|
||||
|
||||
@ -15,14 +17,18 @@ namespace Remotely.Desktop.Linux
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
desktop.MainWindow = new MainWindow
|
||||
{
|
||||
DataContext = new MainWindowViewModel(),
|
||||
};
|
||||
}
|
||||
|
||||
//if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
//{
|
||||
// var conductor = ServiceContainer.Instance.GetRequiredService<Conductor>();
|
||||
// if (conductor.Mode == Core.Enums.AppMode.Normal)
|
||||
// {
|
||||
// desktop.MainWindow = new MainWindow
|
||||
// {
|
||||
// DataContext = new MainWindowViewModel(),
|
||||
// };
|
||||
// }
|
||||
//}
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
BIN
Desktop.Linux/Assets/Remotely_Icon.png
Normal file
BIN
Desktop.Linux/Assets/Remotely_Icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
@ -5,7 +5,10 @@
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Remotely.Desktop.Linux.Controls.HostNamePrompt"
|
||||
xmlns:ViewModels="clr-namespace:Remotely.Desktop.Linux.ViewModels;assembly=Remotely_Desktop"
|
||||
Title="Remotely Host Name" Height="150" Width="350" WindowStartupLocation="CenterScreen">
|
||||
Title="Remotely Host Name"
|
||||
Icon="/Assets/favicon.ico"
|
||||
Height="150" Width="350"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<Window.DataContext>
|
||||
<ViewModels:HostNamePromptViewModel/>
|
||||
</Window.DataContext>
|
||||
@ -18,7 +18,6 @@ namespace Remotely.Desktop.Linux.Controls
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
this.Icon = MainWindow.Current?.Icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,8 @@
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
xmlns:vm="clr-namespace:Remotely.Desktop.Linux.ViewModels;assembly=Remotely_Desktop"
|
||||
x:Class="Remotely.Desktop.Linux.Controls.MessageBox"
|
||||
Title="{Binding Caption}" SizeToContent="WidthAndHeight" MinWidth="200" MinHeight="100" WindowStartupLocation="CenterScreen">
|
||||
Icon="/Assets/favicon.ico"
|
||||
Title="{Binding Caption}" SizeToContent="WidthAndHeight" MinWidth="300" MinHeight="100" WindowStartupLocation="CenterScreen">
|
||||
<Window.DataContext>
|
||||
<vm:MessageBoxViewModel></vm:MessageBoxViewModel>
|
||||
</Window.DataContext>
|
||||
@ -12,7 +12,7 @@ namespace Remotely.Desktop.Linux.Controls
|
||||
public static async Task<MessageBoxResult> Show(string message, string caption, MessageBoxType type)
|
||||
{
|
||||
var messageBox = new MessageBox();
|
||||
var viewModel = new MessageBoxViewModel();
|
||||
var viewModel = messageBox.DataContext as MessageBoxViewModel;
|
||||
viewModel.Caption = caption;
|
||||
viewModel.Message = message;
|
||||
|
||||
@ -28,8 +28,6 @@ namespace Remotely.Desktop.Linux.Controls
|
||||
break;
|
||||
}
|
||||
|
||||
messageBox.DataContext = viewModel;
|
||||
|
||||
await messageBox.ShowDialog(MainWindow.Current);
|
||||
|
||||
return viewModel.Result;
|
||||
@ -47,7 +45,6 @@ namespace Remotely.Desktop.Linux.Controls
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
this.Icon = MainWindow.Current?.Icon;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,21 @@
|
||||
<RootNamespace>Remotely.Desktop.Linux</RootNamespace>
|
||||
<Platforms>AnyCPU;x64;x86</Platforms>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="**\*.xaml.cs">
|
||||
<DependentUpon>%(Filename)</DependentUpon>
|
||||
@ -19,29 +34,25 @@
|
||||
<Compile Remove="Models\**" />
|
||||
<EmbeddedResource Remove="Models\**" />
|
||||
<None Remove="Models\**" />
|
||||
<AvaloniaResource Remove="Controls\HostNamePrompt.xaml" />
|
||||
<AvaloniaResource Remove="Controls\MessageBox.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Controls\HostNamePrompt.xaml" />
|
||||
<None Remove="Controls\MessageBox.xaml" />
|
||||
<None Remove="Assets\Remotely_Icon.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Controls\HostNamePrompt.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Controls\MessageBox.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</EmbeddedResource>
|
||||
<PackageReference Include="Avalonia" Version="0.9.11" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.9.11" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.9.11" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.9.10" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.9.10" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.9.10" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ScreenCast.Core\ScreenCast.Core.csproj" />
|
||||
<ProjectReference Include="..\ScreenCast.Linux\ScreenCast.Linux.csproj" />
|
||||
<ProjectReference Include="..\Desktop.Core\Desktop.Core.csproj" />
|
||||
<ProjectReference Include="..\Shared\Shared.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Controls\HostNamePrompt.axaml.cs">
|
||||
<DependentUpon>HostNamePrompt.axaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Controls\MessageBox.axaml.cs">
|
||||
<DependentUpon>MessageBox.axaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@ -1,22 +1,121 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Logging.Serilog;
|
||||
using Avalonia.ReactiveUI;
|
||||
using Avalonia.Threading;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Remotely.Desktop.Core;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using Remotely.Desktop.Core.Models;
|
||||
using Remotely.Desktop.Core.Services;
|
||||
using Remotely.Desktop.Linux.Services;
|
||||
using Remotely.Desktop.Linux.Views;
|
||||
using Remotely.Shared.Utilities;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely.Desktop.Linux
|
||||
{
|
||||
class Program
|
||||
{
|
||||
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||
// yet and stuff might break.
|
||||
public static void Main(string[] args) => BuildAvaloniaApp()
|
||||
.StartWithClassicDesktopLifetime(args);
|
||||
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
=> AppBuilder.Configure<App>()
|
||||
.UsePlatformDetect()
|
||||
.LogToDebug()
|
||||
.UseReactiveUI();
|
||||
|
||||
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;
|
||||
|
||||
BuildServices();
|
||||
|
||||
Conductor = Services.GetRequiredService<Conductor>();
|
||||
|
||||
Logger.Write("Processing Args: " + string.Join(", ", Environment.GetCommandLineArgs()));
|
||||
Conductor.ProcessArgs(Environment.GetCommandLineArgs().SkipWhile(x => !x.StartsWith("-")).ToArray());
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||
});
|
||||
|
||||
while (App.Current is null)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
if (Conductor.Mode == Core.Enums.AppMode.Chat)
|
||||
{
|
||||
Services.GetRequiredService<IChatHostService>().StartChat(Conductor.RequesterID, Conductor.OrganizationName).Wait();
|
||||
}
|
||||
else if (Conductor.Mode == Core.Enums.AppMode.Unattended)
|
||||
{
|
||||
var casterSocket = Services.GetRequiredService<CasterSocket>();
|
||||
casterSocket.Connect(Conductor.Host).ContinueWith(async (task) =>
|
||||
{
|
||||
await casterSocket.SendDeviceInfo(Conductor.ServiceID, Environment.MachineName, Conductor.DeviceID);
|
||||
await casterSocket.NotifyRequesterUnattendedReady(Conductor.RequesterID);
|
||||
Services.GetRequiredService<IdleTimer>().Start();
|
||||
Services.GetRequiredService<IClipboardService>().BeginWatching();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
App.Current.RunWithMainWindow<MainWindow>();
|
||||
});
|
||||
}
|
||||
|
||||
Thread.Sleep(Timeout.Infinite);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static void BuildServices()
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddLogging(builder =>
|
||||
{
|
||||
builder.AddConsole().AddDebug();
|
||||
});
|
||||
|
||||
serviceCollection.AddSingleton<IScreenCaster, ScreenCaster>();
|
||||
serviceCollection.AddSingleton<IKeyboardMouseInput, KeyboardMouseInputLinux>();
|
||||
serviceCollection.AddSingleton<IClipboardService, ClipboardServiceLinux>();
|
||||
serviceCollection.AddSingleton<IAudioCapturer, AudioCapturerLinux>();
|
||||
serviceCollection.AddSingleton<CasterSocket>();
|
||||
serviceCollection.AddSingleton<IdleTimer>();
|
||||
serviceCollection.AddSingleton<Conductor>();
|
||||
serviceCollection.AddSingleton<IChatHostService, ChatHostServiceLinux>();
|
||||
serviceCollection.AddTransient<IScreenCapturer, ScreenCapturerLinux>();
|
||||
serviceCollection.AddTransient<Viewer>();
|
||||
serviceCollection.AddScoped<IFileTransferService, FileTransferService>();
|
||||
serviceCollection.AddScoped<IWebRtcSessionFactory, WebRtcSessionFactory>();
|
||||
serviceCollection.AddSingleton<ICursorIconWatcher, CursorIconWatcherLinux>();
|
||||
|
||||
ServiceContainer.Instance = serviceCollection.BuildServiceProvider();
|
||||
}
|
||||
|
||||
|
||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
Logger.Write((Exception)e.ExceptionObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,12 +6,12 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<PropertyGroup>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Any CPU</Platform>
|
||||
<Platform>x64</Platform>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<PublishDir>..\Agent\bin\Release\netcoreapp3.1\linux-x64\publish\ScreenCast</PublishDir>
|
||||
<PublishDir>..\Agent\bin\Release\netcoreapp3.1\linux-x64\publish\Desktop</PublishDir>
|
||||
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
|
||||
<SelfContained>True</SelfContained>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>False</PublishSingleFile>
|
||||
<PublishReadyToRun>False</PublishReadyToRun>
|
||||
<PublishTrimmed>True</PublishTrimmed>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
7
Desktop.Linux/Properties/launchSettings.json
Normal file
7
Desktop.Linux/Properties/launchSettings.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Desktop.Linux": {
|
||||
"commandName": "Project"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using System;
|
||||
|
||||
namespace Remotely.ScreenCast.Linux.Services
|
||||
namespace Remotely.Desktop.Linux.Services
|
||||
{
|
||||
public class AudioCapturerLinux : IAudioCapturer
|
||||
{
|
||||
99
Desktop.Linux/Services/ChatHostServiceLinux.cs
Normal file
99
Desktop.Linux/Services/ChatHostServiceLinux.cs
Normal file
@ -0,0 +1,99 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Threading;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using Remotely.Desktop.Linux.Controls;
|
||||
using Remotely.Desktop.Linux.ViewModels;
|
||||
using Remotely.Desktop.Linux.Views;
|
||||
using Remotely.Shared.Models;
|
||||
using Remotely.Shared.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely.Desktop.Linux.Services
|
||||
{
|
||||
public class ChatHostServiceLinux : IChatHostService
|
||||
{
|
||||
private ChatWindowViewModel ChatViewModel { get; set; }
|
||||
private NamedPipeServerStream NamedPipeStream { get; set; }
|
||||
private StreamReader Reader { get; set; }
|
||||
private StreamWriter Writer { get; set; }
|
||||
|
||||
|
||||
public async Task StartChat(string requesterID, string organizationName)
|
||||
{
|
||||
NamedPipeStream = new NamedPipeServerStream("Remotely_Chat" + requesterID, PipeDirection.InOut, 10, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
|
||||
Writer = new StreamWriter(NamedPipeStream);
|
||||
Reader = new StreamReader(NamedPipeStream);
|
||||
|
||||
var cts = new CancellationTokenSource(10000);
|
||||
try
|
||||
{
|
||||
await NamedPipeStream.WaitForConnectionAsync(cts.Token);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
Logger.Write("A chat session was attempted, but the client failed to connect in time.", Shared.Enums.EventType.Warning);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
var chatWindow = new ChatWindow();
|
||||
chatWindow.Closing += ChatWindow_Closing; ;
|
||||
ChatViewModel = chatWindow.DataContext as ChatWindowViewModel;
|
||||
ChatViewModel.PipeStreamWriter = Writer;
|
||||
ChatViewModel.OrganizationName = organizationName;
|
||||
App.Current.Run(chatWindow);
|
||||
});
|
||||
|
||||
_ = Task.Run(ReadFromStream);
|
||||
}
|
||||
|
||||
private void ChatWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
NamedPipeStream?.Disconnect();
|
||||
NamedPipeStream?.Dispose();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
|
||||
private async Task ReadFromStream()
|
||||
{
|
||||
while (NamedPipeStream.IsConnected)
|
||||
{
|
||||
try
|
||||
{
|
||||
var messageJson = await Reader.ReadLineAsync();
|
||||
if (!string.IsNullOrWhiteSpace(messageJson))
|
||||
{
|
||||
var chatMessage = JsonSerializer.Deserialize<ChatMessage>(messageJson);
|
||||
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
if (chatMessage.Disconnected)
|
||||
{
|
||||
await MessageBox.Show("The partner has disconnected.", "Partner Disconnected", MessageBoxType.OK);
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
}
|
||||
ChatViewModel.SenderName = chatMessage.SenderName;
|
||||
ChatViewModel.ChatMessages.Add(chatMessage);
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
81
Desktop.Linux/Services/ClipboardServiceLinux.cs
Normal file
81
Desktop.Linux/Services/ClipboardServiceLinux.cs
Normal file
@ -0,0 +1,81 @@
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using Remotely.Desktop.Core.Services;
|
||||
using Remotely.Shared.Utilities;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely.Desktop.Linux.Services
|
||||
{
|
||||
public class ClipboardServiceLinux : IClipboardService
|
||||
{
|
||||
private CancellationTokenSource cancelTokenSource;
|
||||
|
||||
public event EventHandler<string> ClipboardTextChanged;
|
||||
|
||||
private string ClipboardText { get; set; }
|
||||
|
||||
public void BeginWatching()
|
||||
{
|
||||
try
|
||||
{
|
||||
StopWatching();
|
||||
}
|
||||
finally
|
||||
{
|
||||
cancelTokenSource = new CancellationTokenSource();
|
||||
_ = Task.Run(() => WatchClipboard(cancelTokenSource.Token));
|
||||
}
|
||||
}
|
||||
|
||||
public void SetText(string clipboardText)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(clipboardText))
|
||||
{
|
||||
App.Current.Clipboard.ClearAsync().Wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
App.Current.Clipboard.SetTextAsync(clipboardText).Wait();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void StopWatching()
|
||||
{
|
||||
cancelTokenSource?.Cancel();
|
||||
cancelTokenSource?.Dispose();
|
||||
}
|
||||
|
||||
private void WatchClipboard(CancellationToken cancelToken)
|
||||
{
|
||||
while (!cancelToken.IsCancellationRequested &&
|
||||
!Environment.HasShutdownStarted)
|
||||
{
|
||||
try
|
||||
{
|
||||
var currentText = EnvironmentHelper.StartProcessWithResults("xclip", "-o");
|
||||
// TODO: Switch back when fixed.
|
||||
//var currentText = await App.Current.Clipboard.GetTextAsync();
|
||||
if (!string.IsNullOrEmpty(currentText) && currentText != ClipboardText)
|
||||
{
|
||||
ClipboardText = currentText;
|
||||
ClipboardTextChanged?.Invoke(this, ClipboardText);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using Remotely.Shared.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
|
||||
namespace Remotely.ScreenCast.Linux.Services
|
||||
namespace Remotely.Desktop.Linux.Services
|
||||
{
|
||||
public class CursorIconWatcherLinux : ICursorIconWatcher
|
||||
{
|
||||
@ -1,11 +1,11 @@
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.ScreenCast.Core.Services;
|
||||
using Remotely.ScreenCast.Linux.X11Interop;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using Remotely.Desktop.Core.Services;
|
||||
using Remotely.Desktop.Linux.X11Interop;
|
||||
using System;
|
||||
using Remotely.ScreenCast.Core.Models;
|
||||
using Remotely.Desktop.Core.Models;
|
||||
using Remotely.Shared.Utilities;
|
||||
|
||||
namespace Remotely.ScreenCast.Linux.Services
|
||||
namespace Remotely.Desktop.Linux.Services
|
||||
{
|
||||
public class KeyboardMouseInputLinux : IKeyboardMouseInput
|
||||
{
|
||||
@ -1,6 +1,6 @@
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.ScreenCast.Core.Services;
|
||||
using Remotely.ScreenCast.Linux.X11Interop;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using Remotely.Desktop.Core.Services;
|
||||
using Remotely.Desktop.Linux.X11Interop;
|
||||
using Remotely.Shared.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -9,7 +9,7 @@ using System.Drawing.Imaging;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Remotely.ScreenCast.Linux.Services
|
||||
namespace Remotely.Desktop.Linux.Services
|
||||
{
|
||||
public class ScreenCapturerLinux : IScreenCapturer
|
||||
{
|
||||
73
Desktop.Linux/ViewModels/ChatWindowViewModel.cs
Normal file
73
Desktop.Linux/ViewModels/ChatWindowViewModel.cs
Normal file
@ -0,0 +1,73 @@
|
||||
using Avalonia.Controls;
|
||||
using ReactiveUI;
|
||||
using Remotely.Desktop.Linux.Services;
|
||||
using Remotely.Shared.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Remotely.Desktop.Linux.ViewModels
|
||||
{
|
||||
public class ChatWindowViewModel : ViewModelBase
|
||||
{
|
||||
private string inputText;
|
||||
private string organizationName = "your IT provider";
|
||||
private string senderName = "a technician";
|
||||
public ObservableCollection<ChatMessage> ChatMessages { get; } = new ObservableCollection<ChatMessage>();
|
||||
|
||||
public string ChatSessionHeader => $"Chat session with {OrganizationName}";
|
||||
|
||||
public ICommand CloseCommand => new Executor((param) =>
|
||||
{
|
||||
(param as Window)?.Close();
|
||||
});
|
||||
public string InputText
|
||||
{
|
||||
get => inputText;
|
||||
set => this.RaiseAndSetIfChanged(ref inputText, value);
|
||||
}
|
||||
|
||||
public ICommand MinimizeCommand => new Executor((param) =>
|
||||
{
|
||||
(param as Window).WindowState = WindowState.Minimized;
|
||||
});
|
||||
|
||||
public string OrganizationName
|
||||
{
|
||||
get => organizationName;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref organizationName, value);
|
||||
this.RaisePropertyChanged(nameof(ChatSessionHeader));
|
||||
}
|
||||
}
|
||||
|
||||
public StreamWriter PipeStreamWriter { get; set; }
|
||||
|
||||
public string SenderName
|
||||
{
|
||||
get => senderName;
|
||||
set => this.RaiseAndSetIfChanged(ref senderName, value);
|
||||
}
|
||||
|
||||
public async Task SendChatMessage()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(InputText))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var chatMessage = new ChatMessage(string.Empty, InputText);
|
||||
InputText = string.Empty;
|
||||
await PipeStreamWriter.WriteLineAsync(JsonSerializer.Serialize(chatMessage));
|
||||
await PipeStreamWriter.FlushAsync();
|
||||
chatMessage.SenderName = "You";
|
||||
ChatMessages.Add(chatMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,13 +7,15 @@ namespace Remotely.Desktop.Linux.ViewModels
|
||||
{
|
||||
public class HostNamePromptViewModel : ViewModelBase
|
||||
{
|
||||
public static HostNamePromptViewModel Current { get; private set; }
|
||||
public string host;
|
||||
|
||||
public HostNamePromptViewModel()
|
||||
{
|
||||
Current = this;
|
||||
}
|
||||
|
||||
public string host;
|
||||
public static HostNamePromptViewModel Current { get; private set; }
|
||||
|
||||
public string Host
|
||||
{
|
||||
get => host;
|
||||
|
||||
@ -5,12 +5,10 @@ using ReactiveUI;
|
||||
using Remotely.Desktop.Linux.Controls;
|
||||
using Remotely.Desktop.Linux.Services;
|
||||
using Remotely.Desktop.Linux.Views;
|
||||
using Remotely.ScreenCast.Core;
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.ScreenCast.Core.Models;
|
||||
using Remotely.ScreenCast.Core.Services;
|
||||
using Remotely.ScreenCast.Core.Communication;
|
||||
using Remotely.ScreenCast.Linux.Services;
|
||||
using Remotely.Desktop.Core;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using Remotely.Desktop.Core.Models;
|
||||
using Remotely.Desktop.Core.Services;
|
||||
using Remotely.Shared.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -41,8 +39,6 @@ namespace Remotely.Desktop.Linux.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
BuildServices();
|
||||
|
||||
Conductor = Services.GetRequiredService<Conductor>();
|
||||
CasterSocket = Services.GetRequiredService<CasterSocket>();
|
||||
|
||||
@ -64,6 +60,7 @@ namespace Remotely.Desktop.Linux.ViewModels
|
||||
public ICommand CloseCommand => new Executor((param) =>
|
||||
{
|
||||
(param as Window)?.Close();
|
||||
Environment.Exit(0);
|
||||
});
|
||||
|
||||
public ICommand CopyLinkCommand => new Executor(async (param) =>
|
||||
@ -143,9 +140,10 @@ namespace Remotely.Desktop.Linux.ViewModels
|
||||
try
|
||||
{
|
||||
|
||||
SessionID = "Retrieving...";
|
||||
|
||||
await CheckDependencies();
|
||||
|
||||
SessionID = "Retrieving...";
|
||||
|
||||
Host = Config.GetConfig().Host;
|
||||
|
||||
@ -186,6 +184,7 @@ namespace Remotely.Desktop.Linux.ViewModels
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
sessionID = "Failed";
|
||||
await MessageBox.Show("Failed to connect to server.", "Connection Failed", MessageBoxType.OK);
|
||||
return;
|
||||
}
|
||||
@ -201,6 +200,12 @@ namespace Remotely.Desktop.Linux.ViewModels
|
||||
prompt.Owner = MainWindow.Current;
|
||||
await prompt.ShowDialog(MainWindow.Current);
|
||||
var result = HostNamePromptViewModel.Current.Host;
|
||||
|
||||
if (result is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.StartsWith("https://") && !result.StartsWith("http://"))
|
||||
{
|
||||
result = $"https://{result}";
|
||||
@ -214,30 +219,6 @@ namespace Remotely.Desktop.Linux.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildServices()
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddLogging(builder =>
|
||||
{
|
||||
builder.AddConsole().AddDebug();
|
||||
});
|
||||
|
||||
serviceCollection.AddSingleton<IScreenCaster, ScreenCaster>();
|
||||
serviceCollection.AddSingleton<IKeyboardMouseInput, KeyboardMouseInputLinux>();
|
||||
serviceCollection.AddSingleton<IClipboardService, ClipboardServiceLinux>();
|
||||
serviceCollection.AddSingleton<IAudioCapturer, AudioCapturerLinux>();
|
||||
serviceCollection.AddSingleton<CasterSocket>();
|
||||
serviceCollection.AddSingleton<IdleTimer>();
|
||||
serviceCollection.AddSingleton<Conductor>();
|
||||
serviceCollection.AddTransient<IScreenCapturer, ScreenCapturerLinux>();
|
||||
serviceCollection.AddTransient<Viewer>();
|
||||
serviceCollection.AddScoped<IFileTransferService, FileTransferService>();
|
||||
serviceCollection.AddScoped<IWebRtcSessionFactory, WebRtcSessionFactory>();
|
||||
serviceCollection.AddSingleton<ICursorIconWatcher, CursorIconWatcherLinux>();
|
||||
|
||||
|
||||
ServiceContainer.Instance = serviceCollection.BuildServiceProvider();
|
||||
}
|
||||
|
||||
private async Task CheckDependencies()
|
||||
{
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using Avalonia.Controls;
|
||||
using ReactiveUI;
|
||||
using Remotely.Desktop.Linux.Controls;
|
||||
using Remotely.Desktop.Linux.Services;
|
||||
using System.Windows.Input;
|
||||
@ -7,28 +8,50 @@ namespace Remotely.Desktop.Linux.ViewModels
|
||||
{
|
||||
public class MessageBoxViewModel : ViewModelBase
|
||||
{
|
||||
public string Caption { get; set; }
|
||||
public string Message { get; set; }
|
||||
private bool areYesNoButtonsVisible;
|
||||
private string caption;
|
||||
private bool isOkButtonVisible;
|
||||
private string message;
|
||||
public bool AreYesNoButtonsVisible
|
||||
{
|
||||
get => areYesNoButtonsVisible;
|
||||
set => this.RaiseAndSetIfChanged(ref areYesNoButtonsVisible, value);
|
||||
}
|
||||
|
||||
public bool IsOkButtonVisible { get; set; }
|
||||
public bool AreYesNoButtonsVisible { get; set; }
|
||||
public string Caption
|
||||
{
|
||||
get => caption;
|
||||
set => this.RaiseAndSetIfChanged(ref caption, value);
|
||||
}
|
||||
|
||||
public MessageBoxResult Result { get; set; } = MessageBoxResult.Cancel;
|
||||
public bool IsOkButtonVisible
|
||||
{
|
||||
get => isOkButtonVisible;
|
||||
set => this.RaiseAndSetIfChanged(ref isOkButtonVisible, value);
|
||||
}
|
||||
|
||||
public string Message
|
||||
{
|
||||
get => message;
|
||||
set => this.RaiseAndSetIfChanged(ref message, value);
|
||||
}
|
||||
public ICommand NoCommand => new Executor((param) =>
|
||||
{
|
||||
Result = MessageBoxResult.No;
|
||||
(param as Window).Close();
|
||||
});
|
||||
|
||||
public ICommand OKCommand => new Executor((param) =>
|
||||
{
|
||||
Result = MessageBoxResult.OK;
|
||||
(param as Window).Close();
|
||||
});
|
||||
|
||||
public MessageBoxResult Result { get; set; } = MessageBoxResult.Cancel;
|
||||
public ICommand YesCommand => new Executor((param) =>
|
||||
{
|
||||
Result = MessageBoxResult.Yes;
|
||||
(param as Window).Close();
|
||||
});
|
||||
public ICommand NoCommand => new Executor((param) =>
|
||||
{
|
||||
Result = MessageBoxResult.No;
|
||||
(param as Window).Close();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
93
Desktop.Linux/Views/ChatWindow.axaml
Normal file
93
Desktop.Linux/Views/ChatWindow.axaml
Normal file
@ -0,0 +1,93 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="clr-namespace:Remotely.Desktop.Linux.ViewModels;assembly=Remotely_Desktop"
|
||||
xmlns:Models="clr-namespace:Remotely.Shared.Models;assembly=Remotely_Shared"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Remotely.Desktop.Linux.Views.ChatWindow"
|
||||
Icon="/Assets/favicon.ico"
|
||||
HasSystemDecorations="False"
|
||||
BorderBrush="DimGray"
|
||||
BorderThickness="1"
|
||||
MinHeight="250"
|
||||
MinWidth="200"
|
||||
Title="Remotely Chat" Height="450" Width="350">
|
||||
|
||||
<Window.DataContext>
|
||||
<vm:ChatWindowViewModel />
|
||||
</Window.DataContext>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border Name="TitleBanner" Height="50" Background="#FF464646">
|
||||
<DockPanel Margin="10,4,0,0">
|
||||
|
||||
<DockPanel>
|
||||
<Image Height="50" Width="50" Source="/Assets/Remotely_Icon.png" Margin="0,0,10,0"></Image>
|
||||
<TextBlock Foreground="#FF1D90F1" FontWeight="Bold" FontSize="20" VerticalAlignment="Center">
|
||||
Remotely Chat
|
||||
</TextBlock>
|
||||
</DockPanel>
|
||||
|
||||
<Button Classes="TitlebarButton" Command="{Binding CloseCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" Content="X" />
|
||||
<Button Classes="TitlebarButton" Command="{Binding MinimizeCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" Content="_"/>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
||||
<TextBlock Grid.Row="1"
|
||||
Text="{Binding ChatSessionHeader}"
|
||||
FontWeight="Bold"
|
||||
Foreground="DimGray"
|
||||
Margin="10,10,10,0"
|
||||
TextWrapping="Wrap">
|
||||
</TextBlock>
|
||||
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="5" />
|
||||
<RowDefinition Height="50" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border BorderBrush="Gray" BorderThickness="1" Margin="5">
|
||||
<ScrollViewer x:Name="MessagesScrollViewer">
|
||||
<ItemsControl x:Name="MessagesListBox" Items="{Binding ChatMessages}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type Models:ChatMessage}">
|
||||
<!-- TODO: Replace with Run when implemented. -->
|
||||
<DockPanel Margin="5">
|
||||
<TextBlock Text="{Binding SenderName}" FontWeight="Bold" TextWrapping="Wrap" FontSize="14">
|
||||
<TextBlock.Styles>
|
||||
<Style Selector="TextBlock[Text=You]">
|
||||
<Style.Setters>
|
||||
<Setter Property="Foreground" Value="SteelBlue"></Setter>
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
</TextBlock.Styles>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock TextWrapping="Wrap" FontSize="14" Margin="0,0,5,0">: </TextBlock>
|
||||
<TextBlock Text="{Binding Message}" TextWrapping="Wrap" FontSize="14"></TextBlock>
|
||||
</DockPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
<GridSplitter Grid.Row="1" Height="5" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
|
||||
|
||||
<TextBox Grid.Row="2"
|
||||
x:Name="InputTextBox"
|
||||
FontSize="14"
|
||||
Margin="5"
|
||||
Text="{Binding InputText, Mode=TwoWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
70
Desktop.Linux/Views/ChatWindow.axaml.cs
Normal file
70
Desktop.Linux/Views/ChatWindow.axaml.cs
Normal file
@ -0,0 +1,70 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.VisualTree;
|
||||
using ReactiveUI;
|
||||
using Remotely.Desktop.Linux.ViewModels;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely.Desktop.Linux.Views
|
||||
{
|
||||
public class ChatWindow : Window
|
||||
{
|
||||
public ChatWindow()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
}
|
||||
|
||||
private ChatWindowViewModel ViewModel => DataContext as ChatWindowViewModel;
|
||||
|
||||
private void ChatWindow_Closed(object sender, System.EventArgs e)
|
||||
{
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
private async void ChatWindow_KeyUp(object sender, Avalonia.Input.KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Avalonia.Input.Key.Enter)
|
||||
{
|
||||
await ViewModel.SendChatMessage();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
|
||||
this.Closed += ChatWindow_Closed;
|
||||
|
||||
this.FindControl<Border>("TitleBanner").PointerPressed += TitleBanner_PointerPressed;
|
||||
|
||||
this.FindControl<TextBox>("InputTextBox").KeyUp += ChatWindow_KeyUp;
|
||||
|
||||
this.FindControl<ItemsControl>("MessagesListBox").ItemContainerGenerator.Materialized += ItemContainerGenerator_Materialized;
|
||||
}
|
||||
private async void ItemContainerGenerator_Materialized(object sender, Avalonia.Controls.Generators.ItemContainerEventArgs e)
|
||||
{
|
||||
// Allows listbox height to adjust to content before scrolling the scrollviewer.
|
||||
await Task.Delay(1);
|
||||
// TODO: Replace with ScrollToEnd when implemented.
|
||||
var scrollViewer = this.FindControl<ScrollViewer>("MessagesScrollViewer");
|
||||
var listBox = this.FindControl<ItemsControl>("MessagesListBox");
|
||||
scrollViewer.Offset = new Vector(0, listBox.Bounds.Height);
|
||||
}
|
||||
|
||||
|
||||
private void TitleBanner_PointerPressed(object sender, Avalonia.Input.PointerPressedEventArgs e)
|
||||
{
|
||||
if (e.GetCurrentPoint(this).Properties.PointerUpdateKind == Avalonia.Input.PointerUpdateKind.LeftButtonPressed)
|
||||
{
|
||||
this.BeginMoveDrag(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11,8 +11,9 @@ namespace Remotely.Desktop.Linux.Views
|
||||
public MainWindow()
|
||||
{
|
||||
Current = this;
|
||||
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
@ -27,7 +27,7 @@ in this Software without prior written authorization from The Open Group.
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Remotely.ScreenCast.Linux.X11Interop
|
||||
namespace Remotely.Desktop.Linux.X11Interop
|
||||
{
|
||||
public static unsafe class LibX11
|
||||
{
|
||||
@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Remotely.ScreenCast.Linux.X11Interop
|
||||
namespace Remotely.Desktop.Linux.X11Interop
|
||||
{
|
||||
public class LibXtst
|
||||
{
|
||||
@ -143,7 +143,6 @@
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<EmbeddedResource Include="Install.ps1" />
|
||||
<EmbeddedResource Include="Remotely_Desktop.zip" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 -bor [System.Net.SecurityProtocolType]::Tls13
|
||||
[System.IO.Directory]::CreateDirectory("$env:AppData\Remotely")
|
||||
Invoke-WebRequest -Uri "https://dot.net/v1/dotnet-install.ps1" -OutFile "$env:AppData\Remotely\dotnet-install.ps1" -UseBasicParsing
|
||||
&"$env:AppData\Remotely\dotnet-install.ps1" -Runtime dotnet
|
||||
&"$env:AppData\Remotely\dotnet-install.ps1" -Runtime windowsdesktop
|
||||
Start-Process -FilePath "dotnet.exe" -ArgumentList "$PSScriptRoot\Remotely_Desktop.dll" -WindowStyle Hidden
|
||||
@ -1,23 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using PathIO = System.IO.Path;
|
||||
|
||||
namespace Remotely.Desktop.Win.Wrapper
|
||||
{
|
||||
@ -26,48 +14,22 @@ namespace Remotely.Desktop.Win.Wrapper
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private static readonly string baseDir = Directory.CreateDirectory(PathIO.Combine(PathIO.GetTempPath(), "Remotely_Desktop")).FullName;
|
||||
private string tempDir;
|
||||
|
||||
private static readonly string baseDir = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "Remotely_Desktop")).FullName;
|
||||
private readonly string currentVersionDir = Path.Combine(baseDir, "Current");
|
||||
private readonly string remotelyDesktopFilename = "Remotely_Desktop.exe";
|
||||
private readonly string tempDir = Directory.CreateDirectory(Path.Combine(baseDir, Guid.NewGuid().ToString())).FullName;
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private void MinimizeButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WindowState = WindowState.Minimized;
|
||||
}
|
||||
|
||||
private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
DragMove();
|
||||
}
|
||||
|
||||
private async void Window_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
StatusText.Text = "Extracting files...";
|
||||
await Task.Run(CleanupOldFiles);
|
||||
tempDir = Directory.CreateDirectory(PathIO.Combine(baseDir, Guid.NewGuid().ToString())).FullName;
|
||||
await Task.Run(ExtractRemotely);
|
||||
await Task.Run(ExtractInstallScript);
|
||||
StatusText.Text = "Updating .NET Core runtime...";
|
||||
await Task.Run(RunInstallScript);
|
||||
Close();
|
||||
}
|
||||
|
||||
private void CleanupOldFiles()
|
||||
{
|
||||
foreach (var fse in Directory.GetFileSystemEntries(baseDir))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(fse) && fse != tempDir)
|
||||
if (Directory.Exists(fse) && fse != currentVersionDir)
|
||||
{
|
||||
Directory.Delete(fse, true);
|
||||
}
|
||||
@ -77,15 +39,22 @@ namespace Remotely.Desktop.Win.Wrapper
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private void ExtractRemotely()
|
||||
{
|
||||
try
|
||||
{
|
||||
var zipPath = PathIO.Combine(tempDir, "Remotely_Desktop.zip");
|
||||
var zipPath = Path.Combine(tempDir, "Remotely_Desktop.zip");
|
||||
var tempExePath = Path.Combine(tempDir, remotelyDesktopFilename);
|
||||
|
||||
using (var mrs = Assembly.GetExecutingAssembly()
|
||||
.GetManifestResourceStream("Remotely.Desktop.Win.Wrapper.Remotely_Desktop.zip"))
|
||||
{
|
||||
@ -94,57 +63,89 @@ namespace Remotely.Desktop.Win.Wrapper
|
||||
mrs.CopyTo(fs);
|
||||
}
|
||||
}
|
||||
ZipFile.ExtractToDirectory(zipPath, tempDir);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("An error occured while extracting files. Error: " +
|
||||
Environment.NewLine + Environment.NewLine + ex.Message, "Error",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void RunInstallScript()
|
||||
{
|
||||
try
|
||||
{
|
||||
var installPath = PathIO.Combine(tempDir, "Install.ps1");
|
||||
Process.Start(new ProcessStartInfo()
|
||||
using (var zipArchive = ZipFile.OpenRead(zipPath))
|
||||
{
|
||||
FileName = "powershell.exe",
|
||||
Arguments = $"-executionpolicy bypass -f \"{installPath}\"",
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
}).WaitForExit();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("An error occured while updating .NET Core. Error: " +
|
||||
Environment.NewLine + Environment.NewLine + ex.Message, "Error",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
zipArchive.GetEntry(remotelyDesktopFilename).ExtractToFile(tempExePath);
|
||||
var fileVersionInfo = FileVersionInfo.GetVersionInfo(tempExePath);
|
||||
|
||||
private void ExtractInstallScript()
|
||||
{
|
||||
try
|
||||
{
|
||||
var installPath = PathIO.Combine(tempDir, "Install.ps1");
|
||||
using (var mrs = Assembly.GetExecutingAssembly()
|
||||
.GetManifestResourceStream("Remotely.Desktop.Win.Wrapper.Install.ps1"))
|
||||
{
|
||||
using (var fs = new FileStream(installPath, FileMode.Create))
|
||||
var targetExePath = Path.Combine(currentVersionDir, remotelyDesktopFilename);
|
||||
if (File.Exists(targetExePath) &&
|
||||
FileVersionInfo.GetVersionInfo(targetExePath).FileVersion == fileVersionInfo.FileVersion)
|
||||
{
|
||||
mrs.CopyTo(fs);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var currentVersionDir = Path.GetDirectoryName(targetExePath);
|
||||
Directory.Delete(currentVersionDir, true);
|
||||
Directory.CreateDirectory(currentVersionDir);
|
||||
ZipFile.ExtractToDirectory(zipPath, currentVersionDir);
|
||||
}
|
||||
catch { }
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("An error occured while extracting files. Error: " +
|
||||
Environment.NewLine + Environment.NewLine + ex.Message);
|
||||
Environment.NewLine + Environment.NewLine + ex.Message, "Error",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void MinimizeButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WindowState = WindowState.Minimized;
|
||||
}
|
||||
|
||||
private void StartRemotely()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(Path.Combine(currentVersionDir, remotelyDesktopFilename));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("An error occured while starting Remotely. Error: " +
|
||||
Environment.NewLine + Environment.NewLine + ex.Message, "Error",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async void Window_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
StatusText.Text = "Extracting files...";
|
||||
await Task.Run(ExtractRemotely);
|
||||
await Task.Run(StartRemotely);
|
||||
await Task.Run(CleanupOldFiles);
|
||||
await Task.Run(AddFirewallRule);
|
||||
Close();
|
||||
}
|
||||
|
||||
private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
DragMove();
|
||||
}
|
||||
|
||||
private void AddFirewallRule()
|
||||
{
|
||||
var psi = new ProcessStartInfo()
|
||||
{
|
||||
FileName = "netsh",
|
||||
Arguments = "advfirewall firewall delete rule name=\"Remotely Desktop\"",
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
Process.Start(psi).WaitForExit();
|
||||
|
||||
psi.Arguments = $"advfirewall firewall add rule name=\"Remotely Desktop\" program=\"{Path.Combine(currentVersionDir, remotelyDesktopFilename)}\" protocol=any dir=in enable=yes action=allow profile=Private,Domain description=\"The agent that allows screen sharing and remote control for Remotely.\"";
|
||||
|
||||
Process.Start(psi).WaitForExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,12 +7,12 @@ using System.Windows;
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Desktop.Win.Wrapper")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyTitle("Remotely Desktop")]
|
||||
[assembly: AssemblyDescription("Desktop client for allowing remote control via the Remotely server.")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Desktop.Win.Wrapper")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2020")]
|
||||
[assembly: AssemblyCompany("Translucency Software")]
|
||||
[assembly: AssemblyProduct("Remotely Desktop")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2020 Translucency Software")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Remotely.Desktop.Win"
|
||||
StartupUri="MainWindow.xaml" DispatcherUnhandledException="Application_DispatcherUnhandledException" Startup="Application_Startup">
|
||||
DispatcherUnhandledException="Application_DispatcherUnhandledException" Startup="Application_Startup" Exit="Application_Exit">
|
||||
<Application.Resources>
|
||||
<Style x:Key="TitlebarButton" TargetType="Button">
|
||||
<Setter Property="Background" Value="#FF464646"></Setter>
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Remotely.ScreenCast.Core;
|
||||
using Remotely.ScreenCast.Core.Services;
|
||||
using Remotely.Desktop.Core;
|
||||
using Remotely.Shared.Utilities;
|
||||
using Remotely.Shared.Win32;
|
||||
using System;
|
||||
@ -40,5 +39,21 @@ namespace Remotely.Desktop.Win
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void Application_Exit(object sender, ExitEventArgs e)
|
||||
{
|
||||
System.Windows.Forms.Application.Exit();
|
||||
|
||||
var conductor = ServiceContainer.Instance.GetRequiredService<Conductor>();
|
||||
|
||||
foreach (var viewer in conductor.Viewers.Values)
|
||||
{
|
||||
try
|
||||
{
|
||||
viewer.Dispose();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
Desktop.Win/Assets/Remotely_Icon.png
Normal file
BIN
Desktop.Win/Assets/Remotely_Icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 103 KiB |
@ -4,6 +4,7 @@
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<AssemblyName>Remotely_Desktop</AssemblyName>
|
||||
<RootNamespace>Remotely.Desktop.Win</RootNamespace>
|
||||
<ApplicationIcon>favicon.ico</ApplicationIcon>
|
||||
@ -16,23 +17,41 @@
|
||||
<Product>Remotely Desktop</Product>
|
||||
<PackageProjectUrl>https://remotely.one</PackageProjectUrl>
|
||||
<Platforms>AnyCPU;x86;x64</Platforms>
|
||||
<StartupObject>Remotely.Desktop.Win.Program</StartupObject>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\favicon.ico" />
|
||||
<None Remove="Assets\Remotely_Icon.png" />
|
||||
<None Remove="favicon.ico" />
|
||||
<None Remove="Remotely_Icon.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.MixedReality.WebRTC" Version="1.0.3" />
|
||||
<PackageReference Include="NAudio" Version="1.10.0" />
|
||||
<PackageReference Include="SharpDX" Version="4.2.0" />
|
||||
<PackageReference Include="SharpDX.Direct3D11" Version="4.2.0" />
|
||||
<PackageReference Include="SharpDX.DXGI" Version="4.2.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ScreenCast.Win\ScreenCast.Win.csproj" />
|
||||
<ProjectReference Include="..\Desktop.Core\Desktop.Core.csproj" />
|
||||
<ProjectReference Include="..\Shared\Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Assets\favicon.ico" />
|
||||
<Resource Include="Assets\Remotely_Icon.png" />
|
||||
<Resource Include="favicon.ico" />
|
||||
<Resource Include="Remotely_Icon.png" />
|
||||
</ItemGroup>
|
||||
@ -52,7 +71,7 @@
|
||||
<Compile Update="Controls\HostNamePrompt.xaml.cs">
|
||||
<DependentUpon>HostNamePrompt.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="MainWindow.xaml.cs">
|
||||
<Compile Update="Views\MainWindow.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
@ -62,10 +81,13 @@
|
||||
<Page Update="Controls\HostNamePrompt.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Update="MainWindow.xaml">
|
||||
<Page Update="Views\MainWindow.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="if $(ConfigurationName) == Debug (
 if $(PlatformName) == Any CPU (
 md "$(SolutionDir)Agent\bin\Debug\netcoreapp3.1\Desktop\"
 xcopy "$(TargetDir)*" "$(SolutionDir)Agent\bin\Debug\netcoreapp3.1\Desktop\" /y /e /i
 )
 if $(PlatformName) == x64 (
 md "$(SolutionDir)Agent\bin\x64\Debug\netcoreapp3.1\Desktop\"
 xcopy "$(TargetDir)*" "$(SolutionDir)Agent\bin\x64\Debug\netcoreapp3.1\Desktop\" /y /e /i
 )
 if $(PlatformName) == x86 (
 md "$(SolutionDir)Agent\bin\x86\Debug\netcoreapp3.1\Desktop\"
 xcopy "$(TargetDir)*" "$(SolutionDir)Agent\bin\x86\Debug\netcoreapp3.1\Desktop\" /y /e /i
 )
)" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
@ -2,7 +2,7 @@
|
||||
using SharpDX.DXGI;
|
||||
using System;
|
||||
|
||||
namespace Remotely.ScreenCast.Win.Models
|
||||
namespace Remotely.Desktop.Win.Models
|
||||
{
|
||||
public class DirectXOutput : IDisposable
|
||||
{
|
||||
@ -1,25 +1,26 @@
|
||||
using Remotely.Shared.Models;
|
||||
using Remotely.ScreenCast.Core;
|
||||
using Remotely.ScreenCast.Core.Services;
|
||||
using Remotely.Desktop.Core;
|
||||
using Remotely.Desktop.Core.Services;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Remotely.Shared.Win32;
|
||||
using System.Threading;
|
||||
using Remotely.ScreenCast.Win.Services;
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.ScreenCast.Core.Communication;
|
||||
using Remotely.Desktop.Win.Services;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Remotely.ScreenCast.Core.Models;
|
||||
using Remotely.Desktop.Core.Models;
|
||||
using Remotely.Shared.Utilities;
|
||||
using System.Windows;
|
||||
using Remotely.Desktop.Win.Views;
|
||||
|
||||
namespace Remotely.ScreenCast.Win
|
||||
namespace Remotely.Desktop.Win
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
private static Conductor Conductor { get; set; }
|
||||
private static CasterSocket CasterSocket { get; set; }
|
||||
private static Conductor Conductor { get; set; }
|
||||
private static ICursorIconWatcher CursorIconWatcher { get; set; }
|
||||
private static IServiceProvider Services => ServiceContainer.Instance;
|
||||
public static async void CursorIconWatcher_OnChange(object sender, CursorInfo cursor)
|
||||
@ -32,7 +33,7 @@ namespace Remotely.ScreenCast.Win
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
@ -47,14 +48,27 @@ namespace Remotely.ScreenCast.Win
|
||||
|
||||
if (Conductor.Mode == Core.Enums.AppMode.Chat)
|
||||
{
|
||||
Services.GetRequiredService<ChatHostService>().StartChat(Conductor.RequesterID, Conductor.OrganizationName).Wait();
|
||||
StartUiThread(null);
|
||||
Services.GetRequiredService<IChatHostService>().StartChat(Conductor.RequesterID, Conductor.OrganizationName).Wait();
|
||||
}
|
||||
else if (Conductor.Mode == Core.Enums.AppMode.Unattended)
|
||||
{
|
||||
StartUiThread(null);
|
||||
App.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
App.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
|
||||
});
|
||||
Task.Run(StartScreenCasting);
|
||||
}
|
||||
else
|
||||
{
|
||||
Task.Run(StartScreenCasting);
|
||||
StartUiThread(() => new MainWindow());
|
||||
}
|
||||
|
||||
System.Windows.Forms.Application.Run();
|
||||
|
||||
Environment.Exit(0);
|
||||
|
||||
Thread.Sleep(Timeout.Infinite);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -79,7 +93,7 @@ namespace Remotely.ScreenCast.Win
|
||||
serviceCollection.AddSingleton<CasterSocket>();
|
||||
serviceCollection.AddSingleton<IdleTimer>();
|
||||
serviceCollection.AddSingleton<Conductor>();
|
||||
serviceCollection.AddSingleton<ChatHostService>();
|
||||
serviceCollection.AddSingleton<IChatHostService, ChatHostServiceWin>();
|
||||
serviceCollection.AddTransient<IScreenCapturer, ScreenCapturerWin>();
|
||||
serviceCollection.AddTransient<Viewer>();
|
||||
serviceCollection.AddScoped<IWebRtcSessionFactory, WebRtcSessionFactory>();
|
||||
@ -108,6 +122,7 @@ namespace Remotely.ScreenCast.Win
|
||||
await CasterSocket.NotifyRequesterUnattendedReady(Conductor.RequesterID);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task StartScreenCasting()
|
||||
{
|
||||
CursorIconWatcher = Services.GetRequiredService<ICursorIconWatcher>();
|
||||
@ -131,8 +146,29 @@ namespace Remotely.ScreenCast.Win
|
||||
Services.GetRequiredService<IdleTimer>().Start();
|
||||
CursorIconWatcher.OnChange += CursorIconWatcher_OnChange;
|
||||
Services.GetRequiredService<IClipboardService>().BeginWatching();
|
||||
}
|
||||
|
||||
Thread.Sleep(Timeout.Infinite);
|
||||
private static void StartUiThread(Func<Window> createWindowFunc)
|
||||
{
|
||||
var uiThread = new Thread(() =>
|
||||
{
|
||||
var app = new App();
|
||||
app.InitializeComponent();
|
||||
if (createWindowFunc is null)
|
||||
{
|
||||
app.Run();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.Run(createWindowFunc());
|
||||
}
|
||||
});
|
||||
uiThread.TrySetApartmentState(ApartmentState.STA);
|
||||
uiThread.Start();
|
||||
while (App.Current is null)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<PublishDir>..\Agent\bin\Release\netcoreapp3.1\win10-x64\publish\ScreenCast</PublishDir>
|
||||
<PublishDir>..\Agent\bin\Release\netcoreapp3.1\win10-x64\publish\Desktop</PublishDir>
|
||||
<SelfContained>True</SelfContained>
|
||||
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
|
||||
<PublishSingleFile>True</PublishSingleFile>
|
||||
@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<PublishDir>..\Agent\bin\Release\netcoreapp3.1\win10-x64\publish\ScreenCast</PublishDir>
|
||||
<PublishDir>..\Agent\bin\Release\netcoreapp3.1\win10-x64\publish\Desktop</PublishDir>
|
||||
<SelfContained>True</SelfContained>
|
||||
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
|
||||
<PublishSingleFile>False</PublishSingleFile>
|
||||
@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x86</Platform>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<PublishDir>..\Agent\bin\Release\netcoreapp3.1\win10-x86\publish\ScreenCast</PublishDir>
|
||||
<PublishDir>..\Agent\bin\Release\netcoreapp3.1\win10-x86\publish\Desktop</PublishDir>
|
||||
<RuntimeIdentifier>win10-x86</RuntimeIdentifier>
|
||||
<SelfContained>True</SelfContained>
|
||||
<PublishSingleFile>False</PublishSingleFile>
|
||||
@ -9,9 +9,10 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Platform>x64</Platform>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<PublishDir>bin\Release\win-x64\publish\</PublishDir>
|
||||
<SelfContained>false</SelfContained>
|
||||
<SelfContained>true</SelfContained>
|
||||
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
|
||||
<PublishSingleFile>False</PublishSingleFile>
|
||||
<PublishReadyToRun>False</PublishReadyToRun>
|
||||
<PublishTrimmed>True</PublishTrimmed>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@ -9,9 +9,10 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Platform>x86</Platform>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<PublishDir>bin\Release\win-x86\publish\</PublishDir>
|
||||
<SelfContained>false</SelfContained>
|
||||
<SelfContained>true</SelfContained>
|
||||
<RuntimeIdentifier>win10-x86</RuntimeIdentifier>
|
||||
<PublishSingleFile>False</PublishSingleFile>
|
||||
<PublishReadyToRun>False</PublishReadyToRun>
|
||||
<PublishTrimmed>True</PublishTrimmed>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@ -5,10 +5,10 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NAudio.Wave;
|
||||
using Remotely.ScreenCast.Core;
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.Desktop.Core;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
|
||||
namespace Remotely.ScreenCast.Win.Services
|
||||
namespace Remotely.Desktop.Win.Services
|
||||
{
|
||||
public class AudioCapturerWin : IAudioCapturer
|
||||
{
|
||||
95
Desktop.Win/Services/ChatHostServiceWin.cs
Normal file
95
Desktop.Win/Services/ChatHostServiceWin.cs
Normal file
@ -0,0 +1,95 @@
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using Remotely.Desktop.Win.ViewModels;
|
||||
using Remotely.Desktop.Win.Views;
|
||||
using Remotely.Shared.Models;
|
||||
using Remotely.Shared.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace Remotely.Desktop.Win.Services
|
||||
{
|
||||
public class ChatHostServiceWin : IChatHostService
|
||||
{
|
||||
private ChatWindowViewModel ChatViewModel { get; set; }
|
||||
private NamedPipeServerStream NamedPipeStream { get; set; }
|
||||
private StreamReader Reader { get; set; }
|
||||
private StreamWriter Writer { get; set; }
|
||||
|
||||
public async Task StartChat(string requesterID, string organizationName)
|
||||
{
|
||||
NamedPipeStream = new NamedPipeServerStream("Remotely_Chat" + requesterID, PipeDirection.InOut, 10, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
|
||||
Writer = new StreamWriter(NamedPipeStream);
|
||||
Reader = new StreamReader(NamedPipeStream);
|
||||
|
||||
var cts = new CancellationTokenSource(10000);
|
||||
try
|
||||
{
|
||||
await NamedPipeStream.WaitForConnectionAsync(cts.Token);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
Logger.Write("A chat session was attempted, but the client failed to connect in time.", Shared.Enums.EventType.Warning);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
App.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
var chatWindow = new ChatWindow();
|
||||
chatWindow.Closing += ChatWindow_Closing;
|
||||
ChatViewModel = chatWindow.DataContext as ChatWindowViewModel;
|
||||
ChatViewModel.PipeStreamWriter = Writer;
|
||||
ChatViewModel.OrganizationName = organizationName;
|
||||
chatWindow.Show();
|
||||
});
|
||||
|
||||
_ = Task.Run(ReadFromStream);
|
||||
}
|
||||
|
||||
private void ChatWindow_Closing(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
NamedPipeStream?.Disconnect();
|
||||
NamedPipeStream?.Dispose();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private async Task ReadFromStream()
|
||||
{
|
||||
while (NamedPipeStream.IsConnected)
|
||||
{
|
||||
try
|
||||
{
|
||||
var messageJson = await Reader.ReadLineAsync();
|
||||
if (!string.IsNullOrWhiteSpace(messageJson))
|
||||
{
|
||||
var chatMessage = JsonSerializer.Deserialize<ChatMessage>(messageJson);
|
||||
App.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
if (chatMessage.Disconnected)
|
||||
{
|
||||
MessageBox.Show("Your partner has disconnected.", "Partner Disconnected", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
App.Current.Shutdown();
|
||||
return;
|
||||
}
|
||||
ChatViewModel.SenderName = chatMessage.SenderName;
|
||||
ChatViewModel.ChatMessages.Add(chatMessage);
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
96
Desktop.Win/Services/ClipboardServiceWin.cs
Normal file
96
Desktop.Win/Services/ClipboardServiceWin.cs
Normal file
@ -0,0 +1,96 @@
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using Remotely.Desktop.Core.Services;
|
||||
using Remotely.Shared.Utilities;
|
||||
using Remotely.Shared.Win32;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Remotely.Desktop.Win.Services
|
||||
{
|
||||
public class ClipboardServiceWin : IClipboardService
|
||||
{
|
||||
|
||||
private CancellationTokenSource cancelTokenSource;
|
||||
|
||||
public event EventHandler<string> ClipboardTextChanged;
|
||||
|
||||
private string ClipboardText { get; set; }
|
||||
|
||||
public void BeginWatching()
|
||||
{
|
||||
try
|
||||
{
|
||||
StopWatching();
|
||||
}
|
||||
finally
|
||||
{
|
||||
cancelTokenSource = new CancellationTokenSource();
|
||||
_ = Task.Run(() => WatchClipboard(cancelTokenSource.Token));
|
||||
}
|
||||
}
|
||||
|
||||
public void SetText(string clipboardText)
|
||||
{
|
||||
var thread = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(clipboardText))
|
||||
{
|
||||
Clipboard.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
Clipboard.SetText(clipboardText);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
}
|
||||
});
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
|
||||
}
|
||||
|
||||
public void StopWatching()
|
||||
{
|
||||
try
|
||||
{
|
||||
cancelTokenSource?.Cancel();
|
||||
cancelTokenSource?.Dispose();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private void WatchClipboard(CancellationToken cancelToken)
|
||||
{
|
||||
while (!cancelToken.IsCancellationRequested &&
|
||||
!Environment.HasShutdownStarted)
|
||||
{
|
||||
var thread = new Thread(() =>
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
Win32Interop.SwitchToInputDesktop();
|
||||
|
||||
|
||||
if (Clipboard.ContainsText() && Clipboard.GetText() != ClipboardText)
|
||||
{
|
||||
ClipboardText = Clipboard.GetText();
|
||||
ClipboardTextChanged?.Invoke(this, ClipboardText);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Remotely.Shared.Utilities;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
|
||||
@ -37,8 +38,9 @@ namespace Remotely.Desktop.Win.Services
|
||||
Directory.CreateDirectory(ConfigFolder);
|
||||
File.WriteAllText(ConfigFile, JsonSerializer.Serialize(this));
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,9 +7,9 @@ using System.Timers;
|
||||
using System.Windows.Forms;
|
||||
using Remotely.Shared.Win32;
|
||||
using Remotely.Shared.Models;
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
|
||||
namespace Remotely.ScreenCast.Win.Services
|
||||
namespace Remotely.Desktop.Win.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// A class that can be used to watch for cursor icon changes.
|
||||
@ -1,35 +1,31 @@
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.ScreenCast.Core.Models;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using Remotely.Desktop.Core.Models;
|
||||
using System;
|
||||
using Remotely.Shared.Win32;
|
||||
using static Remotely.Shared.Win32.User32;
|
||||
using System.Windows.Forms;
|
||||
using System.Threading;
|
||||
using Remotely.ScreenCast.Core.Services;
|
||||
using Remotely.Desktop.Core.Services;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using Remotely.ScreenCast.Core;
|
||||
using Remotely.Desktop.Core;
|
||||
using System.Runtime.InteropServices;
|
||||
using Remotely.Shared.Utilities;
|
||||
|
||||
namespace Remotely.ScreenCast.Win.Services
|
||||
namespace Remotely.Desktop.Win.Services
|
||||
{
|
||||
public class KeyboardMouseInputWin : IKeyboardMouseInput
|
||||
{
|
||||
private volatile bool inputBlocked;
|
||||
|
||||
public KeyboardMouseInputWin()
|
||||
{
|
||||
Application.ApplicationExit += Application_ApplicationExit;
|
||||
StartInputActionTask();
|
||||
StartInputProcessingThread();
|
||||
}
|
||||
|
||||
private CancellationTokenSource CancelTokenSource { get; set; }
|
||||
private CancellationToken CancelToken { get; set; }
|
||||
private ConcurrentQueue<Action> InputActions { get; } = new ConcurrentQueue<Action>();
|
||||
|
||||
private Task InputActionsTask { get; set; }
|
||||
|
||||
private bool ShutdownStarted { get; set; }
|
||||
|
||||
public Tuple<double, double> GetAbsolutePercentFromRelativePercent(double percentX, double percentY, IScreenCapturer capturer)
|
||||
{
|
||||
var absoluteX = (capturer.CurrentScreenBounds.Width * percentX) + capturer.CurrentScreenBounds.Left - capturer.GetVirtualScreenBounds().Left;
|
||||
@ -54,7 +50,7 @@ namespace Remotely.ScreenCast.Win.Services
|
||||
ki = new KEYBDINPUT()
|
||||
{
|
||||
wVk = keyCode,
|
||||
wScan = 0,
|
||||
wScan = (ScanCodeShort)MapVirtualKeyEx((uint)keyCode, VkMapType.MAPVK_VK_TO_VSC, GetKeyboardLayout()),
|
||||
time = 0,
|
||||
dwExtraInfo = GetMessageExtraInfo()
|
||||
}
|
||||
@ -74,7 +70,7 @@ namespace Remotely.ScreenCast.Win.Services
|
||||
ki = new KEYBDINPUT()
|
||||
{
|
||||
wVk = keyCode,
|
||||
wScan = 0,
|
||||
wScan = (ScanCodeShort)MapVirtualKeyEx((uint)keyCode, VkMapType.MAPVK_VK_TO_VSC, GetKeyboardLayout()),
|
||||
time = 0,
|
||||
dwFlags = KEYEVENTF.KEYUP,
|
||||
dwExtraInfo = GetMessageExtraInfo()
|
||||
@ -221,27 +217,31 @@ namespace Remotely.ScreenCast.Win.Services
|
||||
{
|
||||
InputActions.Enqueue(() =>
|
||||
{
|
||||
inputBlocked = toggleOn;
|
||||
var result = BlockInput(toggleOn);
|
||||
Logger.Write($"Result of ToggleBlockInput set to {toggleOn}: {result}");
|
||||
});
|
||||
}
|
||||
|
||||
private void Application_ApplicationExit(object sender, EventArgs e)
|
||||
private void CheckQueue(CancellationToken cancelToken)
|
||||
{
|
||||
ShutdownStarted = true;
|
||||
}
|
||||
private void CheckQueue()
|
||||
{
|
||||
while (!ShutdownStarted &&
|
||||
!Environment.HasShutdownStarted &&
|
||||
!CancelToken.IsCancellationRequested)
|
||||
while (!Environment.HasShutdownStarted &&
|
||||
!cancelToken.IsCancellationRequested)
|
||||
{
|
||||
if (InputActions.TryDequeue(out var action))
|
||||
try
|
||||
{
|
||||
action();
|
||||
if (InputActions.TryDequeue(out var action))
|
||||
{
|
||||
action();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
|
||||
Logger.Write($"Stopping input processing on thread {Thread.CurrentThread.ManagedThreadId}.");
|
||||
}
|
||||
|
||||
private VirtualKey ConvertJavaScriptKeyToVirtualKey(string key)
|
||||
@ -368,27 +368,47 @@ namespace Remotely.ScreenCast.Win.Services
|
||||
}
|
||||
return keyCode;
|
||||
}
|
||||
private void StartInputActionTask()
|
||||
private void StartInputProcessingThread()
|
||||
{
|
||||
CancelTokenSource?.Cancel();
|
||||
CancelTokenSource = new CancellationTokenSource();
|
||||
CancelToken = CancelTokenSource.Token;
|
||||
InputActionsTask = Task.Run(CheckQueue, CancelTokenSource.Token);
|
||||
CancelTokenSource?.Dispose();
|
||||
|
||||
var newThread = new Thread(() =>
|
||||
{
|
||||
Logger.Write($"New input processing thread started on thread {Thread.CurrentThread.ManagedThreadId}.");
|
||||
CancelTokenSource = new CancellationTokenSource();
|
||||
if (inputBlocked)
|
||||
{
|
||||
ToggleBlockInput(true);
|
||||
}
|
||||
CheckQueue(CancelTokenSource.Token);
|
||||
});
|
||||
|
||||
newThread.SetApartmentState(ApartmentState.STA);
|
||||
newThread.Start();
|
||||
}
|
||||
|
||||
private void TryOnInputDesktop(Action inputAction)
|
||||
{
|
||||
if (InputActionsTask.Status != TaskStatus.Running)
|
||||
{
|
||||
StartInputActionTask();
|
||||
}
|
||||
|
||||
InputActions.Enqueue(() =>
|
||||
{
|
||||
if (!Win32Interop.SwitchToInputDesktop())
|
||||
try
|
||||
{
|
||||
Logger.Write("Switch failed. Last Error: " + Marshal.GetLastWin32Error().ToString());
|
||||
if (!Win32Interop.SwitchToInputDesktop())
|
||||
{
|
||||
Logger.Write("Desktop switch failed during input processing.");
|
||||
|
||||
// Thread likely has hooks in current desktop. SendKeys will create one with no way to unhook it.
|
||||
// Start a new thread for processing input.
|
||||
StartInputProcessingThread();
|
||||
return;
|
||||
}
|
||||
inputAction();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
}
|
||||
inputAction();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -22,9 +22,9 @@
|
||||
// THE SOFTWARE.
|
||||
|
||||
using Microsoft.Win32;
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.ScreenCast.Core.Services;
|
||||
using Remotely.ScreenCast.Win.Models;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using Remotely.Desktop.Core.Services;
|
||||
using Remotely.Desktop.Win.Models;
|
||||
using Remotely.Shared.Utilities;
|
||||
using Remotely.Shared.Win32;
|
||||
using SharpDX;
|
||||
@ -37,12 +37,13 @@ using System.Drawing.Imaging;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Remotely.ScreenCast.Win.Services
|
||||
namespace Remotely.Desktop.Win.Services
|
||||
{
|
||||
public class ScreenCapturerWin : IScreenCapturer
|
||||
{
|
||||
private readonly Dictionary<string, int> bitBltScreens = new Dictionary<string, int>();
|
||||
private readonly Dictionary<string, DirectXOutput> directxScreens = new Dictionary<string, DirectXOutput>();
|
||||
|
||||
public ScreenCapturerWin()
|
||||
{
|
||||
Init();
|
||||
@ -59,7 +60,6 @@ namespace Remotely.ScreenCast.Win.Services
|
||||
public Bitmap PreviousFrame { get; set; }
|
||||
public string SelectedScreen { get; private set; } = Screen.PrimaryScreen.DeviceName;
|
||||
private Graphics Graphic { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
SystemEvents.DisplaySettingsChanged -= SystemEvents_DisplaySettingsChanged;
|
||||
@ -208,12 +208,8 @@ namespace Remotely.ScreenCast.Win.Services
|
||||
catch { }
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Get dirty rects.
|
||||
//RawRectangle[] dirtyRectsBuffer = new RawRectangle[duplicateFrameInformation.TotalMetadataBufferSize];
|
||||
//duplicatedOutput.GetFrameDirtyRects(duplicateFrameInformation.TotalMetadataBufferSize, dirtyRectsBuffer, out var dirtyRectsSizeRequired);
|
||||
|
||||
// copy resource into memory that can be accessed by the CPU
|
||||
|
||||
// Copy resource into memory that can be accessed by the CPU
|
||||
using (var screenTexture2D = screenResource.QueryInterface<Texture2D>())
|
||||
{
|
||||
device.ImmediateContext.CopyResource(screenTexture2D, texture2D);
|
||||
87
Desktop.Win/ViewModels/ChatWindowViewModel.cs
Normal file
87
Desktop.Win/ViewModels/ChatWindowViewModel.cs
Normal file
@ -0,0 +1,87 @@
|
||||
using Remotely.Shared.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely.Desktop.Win.ViewModels
|
||||
{
|
||||
public class ChatWindowViewModel : ViewModelBase
|
||||
{
|
||||
private string inputText;
|
||||
private string organizationName = "your IT provider";
|
||||
private string senderName = "a technician";
|
||||
public ObservableCollection<ChatMessage> ChatMessages { get; } = new ObservableCollection<ChatMessage>();
|
||||
|
||||
public string InputText
|
||||
{
|
||||
get
|
||||
{
|
||||
return inputText;
|
||||
}
|
||||
set
|
||||
{
|
||||
inputText = value;
|
||||
FirePropertyChanged(nameof(InputText));
|
||||
}
|
||||
}
|
||||
|
||||
public string OrganizationName
|
||||
{
|
||||
get
|
||||
{
|
||||
return organizationName;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value) ||
|
||||
value == organizationName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
organizationName = value;
|
||||
FirePropertyChanged(nameof(OrganizationName));
|
||||
}
|
||||
}
|
||||
|
||||
public StreamWriter PipeStreamWriter { get; set; }
|
||||
public string SenderName
|
||||
{
|
||||
get
|
||||
{
|
||||
return senderName;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == senderName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
senderName = value;
|
||||
FirePropertyChanged(nameof(SenderName));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendChatMessage()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(InputText))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var chatMessage = new ChatMessage(string.Empty, InputText);
|
||||
InputText = string.Empty;
|
||||
await PipeStreamWriter.WriteLineAsync(JsonSerializer.Serialize(chatMessage));
|
||||
await PipeStreamWriter.FlushAsync();
|
||||
chatMessage.SenderName = "You";
|
||||
ChatMessages.Add(chatMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
using Remotely.Desktop.Win.Controls;
|
||||
using Remotely.Desktop.Win.Services;
|
||||
using Remotely.Shared.Models;
|
||||
using Remotely.ScreenCast.Core;
|
||||
using Remotely.ScreenCast.Core.Models;
|
||||
using Remotely.ScreenCast.Core.Services;
|
||||
using Remotely.Desktop.Core;
|
||||
using Remotely.Desktop.Core.Models;
|
||||
using Remotely.Desktop.Core.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@ -13,9 +13,7 @@ using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Security.Principal;
|
||||
using System.Windows.Input;
|
||||
using Remotely.ScreenCast.Win.Services;
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.ScreenCast.Core.Communication;
|
||||
using Remotely.Desktop.Core.Interfaces;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Remotely.Shared.Win32;
|
||||
@ -27,26 +25,14 @@ namespace Remotely.Desktop.Win.ViewModels
|
||||
{
|
||||
private string host;
|
||||
private string sessionID;
|
||||
|
||||
public static MainWindowViewModel Current { get; private set; }
|
||||
|
||||
public MainWindowViewModel()
|
||||
{
|
||||
Application.Current.Exit += Application_Exit;
|
||||
Current = this;
|
||||
|
||||
BuildServices();
|
||||
|
||||
CursorIconWatcher = Services.GetRequiredService<ICursorIconWatcher>();
|
||||
CursorIconWatcher.OnChange += CursorIconWatcher_OnChange;
|
||||
Services.GetRequiredService<IClipboardService>().BeginWatching();
|
||||
Conductor = Services.GetRequiredService<Conductor>();
|
||||
CasterSocket = Services.GetRequiredService<CasterSocket>();
|
||||
Conductor.SessionIDChanged += SessionIDChanged;
|
||||
Conductor.ViewerRemoved += ViewerRemoved;
|
||||
Conductor.ViewerAdded += ViewerAdded;
|
||||
Conductor.ScreenCastRequested += ScreenCastRequested;
|
||||
}
|
||||
|
||||
public static MainWindowViewModel Current { get; private set; }
|
||||
|
||||
public static IServiceProvider Services => ServiceContainer.Instance;
|
||||
|
||||
public ICommand ChangeServerCommand
|
||||
@ -61,8 +47,8 @@ namespace Remotely.Desktop.Win.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private Conductor Conductor { get; }
|
||||
private CasterSocket CasterSocket { get; }
|
||||
private Conductor Conductor { get; set; }
|
||||
private CasterSocket CasterSocket { get; set; }
|
||||
private ICursorIconWatcher CursorIconWatcher { get; set; }
|
||||
|
||||
public ICommand ElevateToAdminCommand
|
||||
@ -189,6 +175,18 @@ namespace Remotely.Desktop.Win.ViewModels
|
||||
|
||||
public async Task Init()
|
||||
{
|
||||
Application.Current.Exit += Application_Exit;
|
||||
|
||||
CursorIconWatcher = Services?.GetRequiredService<ICursorIconWatcher>();
|
||||
CursorIconWatcher.OnChange += CursorIconWatcher_OnChange;
|
||||
Services?.GetRequiredService<IClipboardService>().BeginWatching();
|
||||
Conductor = Services?.GetRequiredService<Conductor>();
|
||||
CasterSocket = Services?.GetRequiredService<CasterSocket>();
|
||||
Conductor.SessionIDChanged += SessionIDChanged;
|
||||
Conductor.ViewerRemoved += ViewerRemoved;
|
||||
Conductor.ViewerAdded += ViewerAdded;
|
||||
Conductor.ScreenCastRequested += ScreenCastRequested;
|
||||
|
||||
SessionID = "Retrieving...";
|
||||
|
||||
Host = Config.GetConfig().Host;
|
||||
@ -233,6 +231,7 @@ namespace Remotely.Desktop.Win.ViewModels
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
SessionID = "Failed";
|
||||
MessageBox.Show(Application.Current.MainWindow, "Failed to connect to server.", "Connection Failed", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
@ -272,30 +271,6 @@ namespace Remotely.Desktop.Win.ViewModels
|
||||
Viewers.Clear();
|
||||
});
|
||||
}
|
||||
private void BuildServices()
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddLogging(builder =>
|
||||
{
|
||||
builder.AddConsole().AddDebug().AddEventLog();
|
||||
});
|
||||
|
||||
serviceCollection.AddSingleton<ICursorIconWatcher, CursorIconWatcherWin>();
|
||||
serviceCollection.AddSingleton<IScreenCaster, ScreenCaster>();
|
||||
serviceCollection.AddSingleton<IKeyboardMouseInput, KeyboardMouseInputWin>();
|
||||
serviceCollection.AddSingleton<IClipboardService, ClipboardServiceWin>();
|
||||
serviceCollection.AddSingleton<IAudioCapturer, AudioCapturerWin>();
|
||||
serviceCollection.AddSingleton<CasterSocket>();
|
||||
serviceCollection.AddSingleton<IdleTimer>();
|
||||
serviceCollection.AddSingleton<Conductor>();
|
||||
serviceCollection.AddTransient<IScreenCapturer, ScreenCapturerWin>();
|
||||
serviceCollection.AddTransient<Viewer>();
|
||||
serviceCollection.AddScoped<IWebRtcSessionFactory, WebRtcSessionFactory>();
|
||||
serviceCollection.AddScoped<IFileTransferService, FileTransferService>();
|
||||
|
||||
|
||||
ServiceContainer.Instance = serviceCollection.BuildServiceProvider();
|
||||
}
|
||||
|
||||
private async void CursorIconWatcher_OnChange(object sender, CursorInfo cursor)
|
||||
{
|
||||
|
||||
98
Desktop.Win/Views/ChatWindow.xaml
Normal file
98
Desktop.Win/Views/ChatWindow.xaml
Normal file
@ -0,0 +1,98 @@
|
||||
<Window x:Class="Remotely.Desktop.Win.Views.ChatWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Remotely.Desktop.Win.Views"
|
||||
xmlns:ViewModels="clr-namespace:Remotely.Desktop.Win.ViewModels"
|
||||
xmlns:Models="clr-namespace:Remotely.Shared.Models;assembly=Remotely_Shared"
|
||||
mc:Ignorable="d"
|
||||
WindowStyle="None"
|
||||
ResizeMode="CanResizeWithGrip"
|
||||
MouseLeftButtonDown="Window_MouseLeftButtonDown"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
AllowsTransparency="True"
|
||||
BorderBrush="DimGray"
|
||||
BorderThickness="1"
|
||||
MinHeight="250"
|
||||
MinWidth="200"
|
||||
Topmost="True"
|
||||
Title="Remotely Chat" Height="450" Width="350" Icon="/Assets/favicon.ico"
|
||||
Loaded="Window_Loaded"
|
||||
ContentRendered="Window_ContentRendered">
|
||||
|
||||
<Window.DataContext>
|
||||
<ViewModels:ChatWindowViewModel />
|
||||
</Window.DataContext>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border Height="50" Background="#FF464646">
|
||||
<DockPanel Margin="10,0,0,0">
|
||||
<DockPanel>
|
||||
<Image Height="50" Width="50" Source="/Assets/Remotely_Icon.png" Margin="0,0,10,0"></Image>
|
||||
<TextBlock Foreground="#FF1D90F1" FontWeight="Bold" FontSize="20" VerticalAlignment="Center">
|
||||
Remotely Chat
|
||||
</TextBlock>
|
||||
</DockPanel>
|
||||
|
||||
<Button Style="{StaticResource TitlebarButton}" Click="CloseButton_Click" Content="X" />
|
||||
<Button Style="{StaticResource TitlebarButton}" Click="MinimizeButton_Click" Content="_"/>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
||||
<TextBlock Grid.Row="1" FontWeight="Bold" Foreground="DimGray" Margin="10,10,10,0" TextWrapping="Wrap">
|
||||
<Run>Chat session with</Run>
|
||||
<Run Text="{Binding OrganizationName}"></Run>
|
||||
</TextBlock>
|
||||
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="5" />
|
||||
<RowDefinition Height="50" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border BorderBrush="Gray" BorderThickness="1" Margin="5">
|
||||
<ScrollViewer x:Name="MessagesScrollViewer">
|
||||
<ItemsControl x:Name="MessagesItemsControl" ItemsSource="{Binding ChatMessages}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type Models:ChatMessage}">
|
||||
<TextBlock TextWrapping="Wrap" Margin="5" FontSize="14">
|
||||
<Run Text="{Binding SenderName}" FontWeight="Bold">
|
||||
<Run.Style>
|
||||
<Style TargetType="Run">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="Text" Value="You">
|
||||
<Setter Property="Foreground" Value="SteelBlue"></Setter>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Run.Style>
|
||||
</Run>
|
||||
<Run>: </Run>
|
||||
<Run Text="{Binding Message}"></Run>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
<GridSplitter Grid.Row="1" Height="5" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
|
||||
|
||||
<TextBox Grid.Row="2"
|
||||
FontSize="14"
|
||||
Margin="5"
|
||||
Text="{Binding InputText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
TextWrapping="Wrap"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
PreviewKeyUp="ChatInputBox_PreviewKeyUp" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
67
Desktop.Win/Views/ChatWindow.xaml.cs
Normal file
67
Desktop.Win/Views/ChatWindow.xaml.cs
Normal file
@ -0,0 +1,67 @@
|
||||
using Remotely.Desktop.Win.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Remotely.Desktop.Win.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ChatWindow.xaml
|
||||
/// </summary>
|
||||
public partial class ChatWindow : Window
|
||||
{
|
||||
public ChatWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
|
||||
private ChatWindowViewModel ViewModel => DataContext as ChatWindowViewModel;
|
||||
|
||||
private async void ChatInputBox_PreviewKeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Enter)
|
||||
{
|
||||
await ViewModel.SendChatMessage();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private void ItemContainerGenerator_ItemsChanged(object sender, System.Windows.Controls.Primitives.ItemsChangedEventArgs e)
|
||||
{
|
||||
MessagesScrollViewer.ScrollToEnd();
|
||||
}
|
||||
|
||||
private void MinimizeButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.WindowState = WindowState.Minimized;
|
||||
}
|
||||
|
||||
private void Window_ContentRendered(object sender, EventArgs e)
|
||||
{
|
||||
Topmost = false;
|
||||
}
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
MessagesItemsControl.ItemContainerGenerator.ItemsChanged += ItemContainerGenerator_ItemsChanged;
|
||||
}
|
||||
|
||||
private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
DragMove();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,16 +1,16 @@
|
||||
<Window x:Class="Remotely.Desktop.Win.MainWindow"
|
||||
<Window x:Class="Remotely.Desktop.Win.Views.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Remotely.Desktop.Win"
|
||||
xmlns:local="clr-namespace:Remotely.Desktop.Win.Views"
|
||||
xmlns:ViewModels="clr-namespace:Remotely.Desktop.Win.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
Title="Remotely" Height="250" Width="350"
|
||||
WindowStyle="None"
|
||||
ResizeMode="NoResize"
|
||||
MouseLeftButtonDown="Window_MouseLeftButtonDown"
|
||||
Loaded="Window_Loaded" Icon="Remotely_Icon.png">
|
||||
Loaded="Window_Loaded" Icon="/Remotely_Icon.png">
|
||||
<Window.Resources>
|
||||
<DrawingBrush x:Key="GearBrush">
|
||||
<DrawingBrush.Drawing>
|
||||
@ -24,9 +24,11 @@
|
||||
</DrawingBrush.Drawing>
|
||||
</DrawingBrush>
|
||||
</Window.Resources>
|
||||
|
||||
<Window.DataContext>
|
||||
<ViewModels:MainWindowViewModel/>
|
||||
<ViewModels:MainWindowViewModel />
|
||||
</Window.DataContext>
|
||||
|
||||
<Grid>
|
||||
<StackPanel>
|
||||
<Border Height="50" Background="#FF464646">
|
||||
@ -1,4 +1,5 @@
|
||||
using Remotely.Desktop.Win.ViewModels;
|
||||
using Remotely.Desktop.Win;
|
||||
using Remotely.Desktop.Win.ViewModels;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
@ -7,7 +8,7 @@ using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace Remotely.Desktop.Win
|
||||
namespace Remotely.Desktop.Win.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
@ -22,7 +23,7 @@ namespace Remotely.Desktop.Win
|
||||
public MainWindowViewModel ViewModel => DataContext as MainWindowViewModel;
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
App.Current.Shutdown();
|
||||
Close();
|
||||
}
|
||||
|
||||
private async void CopyLinkButton_Click(object sender, RoutedEventArgs e)
|
||||
27
README.md
27
README.md
@ -7,23 +7,6 @@ A remote control and remote scripting solution, built with .NET Core, SignalR Co
|
||||
Website: https://remotely.one
|
||||
Multi-Tenant Demo Server: https://app.remotely.one
|
||||
|
||||
> **From the Developer**:
|
||||
>
|
||||
>Remotely has become a second full-time job, one for which I don't get paid. The total donations I've received over the last 3 years, while greatly appreciated, only amount to what I earn in 3 days at a regular job.
|
||||
>
|
||||
>Every time I try to get back into it developing this project, or try to keep up with support questions and feature requests, I burn out quickly.
|
||||
>
|
||||
>I've accomplished everything I personally set out to achieve with Remotely, plus a huge amount of features requested by others. There's not enough incentive to continue working on it. I feel like the project is done, and I can set it down.
|
||||
>
|
||||
>For that reason, I will no longer be responding to emails, offering free support, or accepting feature requests. Development will be, for the most part, indefinitely suspended.
|
||||
>
|
||||
>Of course, anyone is free to fork the repo and continue development, so long as the original license is respected. I won't, however, be taking any pull requests, as I wish to maintain full ownership of the codebase.
|
||||
>
|
||||
> Thank you for understanding, and best wishes to everyone.
|
||||
>
|
||||
> \- Jared
|
||||
|
||||
|
||||
## Client Prerequisites:
|
||||
* Endpoint devices require the .NET Core runtime to be installed.
|
||||
* For Windows, the Desktop Runtime is required.
|
||||
@ -99,14 +82,6 @@ The following steps will configure your Windows 10 machine for building the Remo
|
||||
* Windows 2016/2019 should work as well, but isn't tested regularly.
|
||||
* Linux: Only Ubuntu 18.04+ is tested.
|
||||
|
||||
## Session Recording
|
||||
* You can turn on session recording in appsettings.json.
|
||||
* The following requirements must be met for it to work:
|
||||
* On Linux, libgdiplus and libc6-dev must be installed (sudo apt-get install libgdiplus libc6-dev).
|
||||
* The process running the app must have access to create and modify a folder name "Recordings" in the site's root content folder.
|
||||
* FFmpeg must be executable from the process running the Remotely server.
|
||||
* Link: https://www.ffmpeg.org/download.html
|
||||
|
||||
## Remote Control on Mobile
|
||||
Ideally, you'd be doing remote control from an actual computer or laptop. However, I've tried to make the remote control at least somewhat usable from a mobile device. Here are the controls:
|
||||
* Left-click: Single tap
|
||||
@ -128,7 +103,6 @@ Note: To retain your settings between upgrades, copy your settings to appsetting
|
||||
* DBProvider: Determines which of the three connection strings (at the top) will be used. The appropriate DB provider for the database type is automatically loaded in code.
|
||||
* MaxOrganizationCount: By default, one organization can exist on the server, which is created automatically when the first account is registered. Afterward, self-registration will be disabled.
|
||||
* Set this to -1 or increase it to a specific number to allow multi-tenancy.
|
||||
* RecordRemoteControlSessions: Whether or not to record remote control sessions.
|
||||
* RedirectToHttps: Whether ASP.NET Core will redirect all traffic from HTTP to HTTPS. This is independent of Nginx and IIS configurations that do the same.
|
||||
* UseHsts: Whether ASP.NET Core will use HTTP Strict Transport Security.
|
||||
* DataRetentionInDays: How long event logs and remote command logs will be kept.
|
||||
@ -142,7 +116,6 @@ Note: To retain your settings between upgrades, copy your settings to appsetting
|
||||
* Theme: The color theme to use for the site. Values are "Light" or "Dark". This can also be configured per-user in Account - Options.
|
||||
* UseWebRtc: Attempt to create a peer-to-peer connection via WebRTC for screen sharing.
|
||||
* Only works on Windows agents.
|
||||
* Session recording will not work if a WebRTC connection is made.
|
||||
|
||||
|
||||
## .NET Core Deployments
|
||||
|
||||
64
Remotely.sln
64
Remotely.sln
@ -27,9 +27,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Assets", "Assets", "{D96B47
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server", "Server\Server.csproj", "{3E835099-C417-4D82-8D5C-13DC09AF48AC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScreenCast.Linux", "ScreenCast.Linux\ScreenCast.Linux.csproj", "{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScreenCast.Core", "ScreenCast.Core\ScreenCast.Core.csproj", "{B04A1728-2E87-491E-BC7F-F575A1754DEF}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Desktop.Core", "Desktop.Core\Desktop.Core.csproj", "{B04A1728-2E87-491E-BC7F-F575A1754DEF}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "Shared\Shared.csproj", "{3B1B36AE-7A60-4974-A868-1E24894D4149}"
|
||||
EndProject
|
||||
@ -41,8 +39,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Folder", "Solution
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScreenCast.Win", "ScreenCast.Win\ScreenCast.Win.csproj", "{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Desktop.Win", "Desktop.Win\Desktop.Win.csproj", "{6B726FC4-A907-4813-BF38-3342E02AA8D2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Agent.Installer.Win", "Agent.Installer.Win\Agent.Installer.Win.csproj", "{A3D0368C-0850-4614-B5B5-41B9D5135AA9}"
|
||||
@ -51,7 +47,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Desktop.Win.Wrapper", "Desktop.Win.Wrapper\Desktop.Win.Wrapper.csproj", "{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.LoadTester", "Tests.LoadTester\Tests.LoadTester.csproj", "{6C25240C-613D-4A86-A04E-784BA6726094}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.LoadTester", "Tests.LoadTester\Tests.LoadTester.csproj", "{6C25240C-613D-4A86-A04E-784BA6726094}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -87,18 +83,6 @@ Global
|
||||
{3E835099-C417-4D82-8D5C-13DC09AF48AC}.Release|x64.Build.0 = Release|x64
|
||||
{3E835099-C417-4D82-8D5C-13DC09AF48AC}.Release|x86.ActiveCfg = Release|x86
|
||||
{3E835099-C417-4D82-8D5C-13DC09AF48AC}.Release|x86.Build.0 = Release|x86
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Debug|x64.Build.0 = Debug|x64
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Debug|x86.Build.0 = Debug|x86
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Release|x64.ActiveCfg = Release|x64
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Release|x64.Build.0 = Release|x64
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Release|x86.ActiveCfg = Release|x86
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Release|x86.Build.0 = Release|x86
|
||||
{B04A1728-2E87-491E-BC7F-F575A1754DEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B04A1728-2E87-491E-BC7F-F575A1754DEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B04A1728-2E87-491E-BC7F-F575A1754DEF}.Debug|x64.ActiveCfg = Debug|x64
|
||||
@ -135,18 +119,6 @@ Global
|
||||
{FF7FD66A-B3E2-4D39-A457-A00C94EDE9E6}.Release|x64.Build.0 = Release|x64
|
||||
{FF7FD66A-B3E2-4D39-A457-A00C94EDE9E6}.Release|x86.ActiveCfg = Release|x86
|
||||
{FF7FD66A-B3E2-4D39-A457-A00C94EDE9E6}.Release|x86.Build.0 = Release|x86
|
||||
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Debug|x64.Build.0 = Debug|x64
|
||||
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Debug|x86.Build.0 = Debug|x86
|
||||
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|Any CPU.Build.0 = Release|x64
|
||||
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|x64.ActiveCfg = Release|x64
|
||||
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|x64.Build.0 = Release|x64
|
||||
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|x86.ActiveCfg = Release|x86
|
||||
{8D9EA0C3-EC7F-48AC-A2ED-9C60DD5A3987}.Release|x86.Build.0 = Release|x86
|
||||
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Debug|x64.ActiveCfg = Debug|x64
|
||||
@ -161,16 +133,16 @@ Global
|
||||
{6B726FC4-A907-4813-BF38-3342E02AA8D2}.Release|x86.Build.0 = Release|x86
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Debug|x64.Build.0 = Debug|x64
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Debug|x86.Build.0 = Debug|x86
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Release|x64.ActiveCfg = Release|x64
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Release|x64.Build.0 = Release|x64
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Release|x86.ActiveCfg = Release|x86
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Release|x86.Build.0 = Release|x86
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A3D0368C-0850-4614-B5B5-41B9D5135AA9}.Release|x86.Build.0 = Release|Any CPU
|
||||
{48D9D0E6-5781-44A9-84C0-56F56C2A1193}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{48D9D0E6-5781-44A9-84C0-56F56C2A1193}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{48D9D0E6-5781-44A9-84C0-56F56C2A1193}.Debug|x64.ActiveCfg = Debug|x64
|
||||
@ -185,16 +157,16 @@ Global
|
||||
{48D9D0E6-5781-44A9-84C0-56F56C2A1193}.Release|x86.Build.0 = Release|x86
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Debug|x64.Build.0 = Debug|x64
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Debug|x86.Build.0 = Debug|x86
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Release|x64.ActiveCfg = Release|x64
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Release|x64.Build.0 = Release|x64
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Release|x86.ActiveCfg = Release|x86
|
||||
{A9967E61-B1BE-4AA5-A33A-99B0B8B5FC2A}.Release|x86.Build.0 = Release|x86
|
||||
{6C25240C-613D-4A86-A04E-784BA6726094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6C25240C-613D-4A86-A04E-784BA6726094}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6C25240C-613D-4A86-A04E-784BA6726094}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
|
||||
@ -1,153 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely.ScreenCast.Core.Services
|
||||
{
|
||||
public class ChatHostService
|
||||
{
|
||||
|
||||
private string AsciiLogo => @"
|
||||
_____ _ _
|
||||
| __ \ | | | |
|
||||
| |__) |___ _ __ ___ ___ | |_ ___| |_ _
|
||||
| _ // _ \ '_ ` _ \ / _ \| __/ _ \ | | | |
|
||||
| | \ \ __/ | | | | | (_) | || __/ | |_| |
|
||||
|_| \_\___|_| |_| |_|\___/ \__\___|_|\__, |
|
||||
__/ |
|
||||
|___/
|
||||
";
|
||||
|
||||
private int MaxCursorTop { get; set; }
|
||||
private NamedPipeServerStream NamedPipeStream { get; set; }
|
||||
private StreamReader Reader { get; set; }
|
||||
private StreamWriter Writer { get; set; }
|
||||
public async Task StartChat(string requesterID, string organizationName)
|
||||
{
|
||||
NamedPipeStream = new NamedPipeServerStream("Remotely_Chat" + requesterID, PipeDirection.InOut, 10, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
|
||||
Writer = new StreamWriter(NamedPipeStream);
|
||||
Reader = new StreamReader(NamedPipeStream);
|
||||
|
||||
Console.BackgroundColor = ConsoleColor.Black;
|
||||
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
Console.Title = "Remotely Chat";
|
||||
if (string.IsNullOrWhiteSpace(organizationName))
|
||||
{
|
||||
Console.Write(AsciiLogo);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteOrgnanizationName(organizationName);
|
||||
}
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Your IT administrator would like to chat!");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Connecting...");
|
||||
Console.WriteLine();
|
||||
|
||||
var cts = new CancellationTokenSource(10000);
|
||||
try
|
||||
{
|
||||
await NamedPipeStream.WaitForConnectionAsync(cts.Token);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
await Close();
|
||||
return;
|
||||
}
|
||||
_ = Task.Run(ReadFromStream);
|
||||
Console.WriteLine("You're now connected with a technician.");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Type your responses below and hit Enter to send.");
|
||||
Console.WriteLine("Press Ctrl + C to exit.");
|
||||
|
||||
while (NamedPipeStream.IsConnected)
|
||||
{
|
||||
SetPrompt();
|
||||
var message = Console.ReadLine();
|
||||
await Writer.WriteLineAsync(message);
|
||||
await Writer.FlushAsync();
|
||||
}
|
||||
|
||||
await Close();
|
||||
}
|
||||
|
||||
private async Task Close()
|
||||
{
|
||||
Console.WriteLine("Connection failed. Closing...");
|
||||
await Task.Delay(3000);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
private async Task ReadFromStream()
|
||||
{
|
||||
while (NamedPipeStream.IsConnected)
|
||||
{
|
||||
var message = await Reader.ReadLineAsync();
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
var split = message.Split(":", 2);
|
||||
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
Console.Write($"{split[0]}: ");
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.WriteLine(split[1]);
|
||||
SetPrompt();
|
||||
}
|
||||
}
|
||||
private void SetPrompt()
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
Console.Write("You: ");
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
}
|
||||
private void WriteOrgnanizationName(string organizationName)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
|
||||
Console.Write(" ");
|
||||
for (var i = 0; i < organizationName.Length + 10; i++)
|
||||
{
|
||||
Console.Write("/");
|
||||
}
|
||||
Console.WriteLine();
|
||||
|
||||
|
||||
Console.Write(" ///");
|
||||
for (var i = 0; i < organizationName.Length + 4; i++)
|
||||
{
|
||||
Console.Write(" ");
|
||||
}
|
||||
Console.Write("///");
|
||||
Console.WriteLine();
|
||||
|
||||
|
||||
Console.WriteLine($" /// {organizationName} ///");
|
||||
|
||||
|
||||
Console.Write(" ///");
|
||||
for (var i = 0; i < organizationName.Length + 4; i++)
|
||||
{
|
||||
Console.Write(" ");
|
||||
}
|
||||
Console.Write("///");
|
||||
Console.WriteLine();
|
||||
|
||||
|
||||
Console.Write(" ");
|
||||
for (var i = 0; i < organizationName.Length + 10; i++)
|
||||
{
|
||||
Console.Write("/");
|
||||
}
|
||||
Console.WriteLine();
|
||||
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,89 +0,0 @@
|
||||
using Remotely.ScreenCast.Core;
|
||||
using Remotely.ScreenCast.Core.Services;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Remotely.ScreenCast.Linux.Services;
|
||||
using Remotely.ScreenCast.Core.Communication;
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Linq;
|
||||
using Remotely.ScreenCast.Core.Models;
|
||||
using Remotely.Shared.Utilities;
|
||||
|
||||
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;
|
||||
|
||||
BuildServices();
|
||||
|
||||
Conductor = Services.GetRequiredService<Conductor>();
|
||||
|
||||
Logger.Write("Processing Args: " + string.Join(", ", Environment.GetCommandLineArgs()));
|
||||
Conductor.ProcessArgs(Environment.GetCommandLineArgs().SkipWhile(x => !x.StartsWith("-")).ToArray());
|
||||
|
||||
if (Conductor.Mode == Core.Enums.AppMode.Chat)
|
||||
{
|
||||
Services.GetRequiredService<ChatHostService>().StartChat(Conductor.RequesterID, Conductor.OrganizationName).Wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
var casterSocket = Services.GetRequiredService<CasterSocket>();
|
||||
casterSocket.Connect(Conductor.Host).ContinueWith(async (task) =>
|
||||
{
|
||||
await casterSocket.SendDeviceInfo(Conductor.ServiceID, Environment.MachineName, Conductor.DeviceID);
|
||||
await casterSocket.NotifyRequesterUnattendedReady(Conductor.RequesterID);
|
||||
Services.GetRequiredService<IdleTimer>().Start();
|
||||
});
|
||||
}
|
||||
|
||||
Thread.Sleep(Timeout.Infinite);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static void BuildServices()
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddLogging(builder =>
|
||||
{
|
||||
builder.AddConsole().AddDebug();
|
||||
});
|
||||
|
||||
serviceCollection.AddSingleton<IScreenCaster, ScreenCaster>();
|
||||
serviceCollection.AddSingleton<IKeyboardMouseInput, KeyboardMouseInputLinux>();
|
||||
serviceCollection.AddSingleton<IClipboardService, ClipboardServiceLinux>();
|
||||
serviceCollection.AddSingleton<IAudioCapturer, AudioCapturerLinux>();
|
||||
serviceCollection.AddSingleton<CasterSocket>();
|
||||
serviceCollection.AddSingleton<IdleTimer>();
|
||||
serviceCollection.AddSingleton<Conductor>();
|
||||
serviceCollection.AddSingleton<ChatHostService>();
|
||||
serviceCollection.AddTransient<IScreenCapturer, ScreenCapturerLinux>();
|
||||
serviceCollection.AddTransient<Viewer>();
|
||||
serviceCollection.AddScoped<IFileTransferService, FileTransferService>();
|
||||
serviceCollection.AddScoped<IWebRtcSessionFactory, WebRtcSessionFactory>();
|
||||
serviceCollection.AddSingleton<ICursorIconWatcher, CursorIconWatcherLinux>();
|
||||
|
||||
ServiceContainer.Instance = serviceCollection.BuildServiceProvider();
|
||||
}
|
||||
|
||||
|
||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
Logger.Write((Exception)e.ExceptionObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,61 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<Description>Allows unattended remote control via the Remotely server.</Description>
|
||||
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
||||
<AssemblyName>Remotely_ScreenCast.Linux</AssemblyName>
|
||||
<Platforms>AnyCPU;x64;x86</Platforms>
|
||||
<RootNamespace>Remotely.ScreenCast.Linux</RootNamespace>
|
||||
<ApplicationIcon>favicon.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Input\**" />
|
||||
<EmbeddedResource Remove="Input\**" />
|
||||
<None Remove="Input\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="favicon.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="favicon.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Drawing.Common" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ScreenCast.Core\ScreenCast.Core.csproj" />
|
||||
<ProjectReference Include="..\Shared\Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@ -1,45 +0,0 @@
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.ScreenCast.Core.Services;
|
||||
using Remotely.Shared.Utilities;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace Remotely.ScreenCast.Linux.Services
|
||||
{
|
||||
public class ClipboardServiceLinux : IClipboardService
|
||||
{
|
||||
#pragma warning disable
|
||||
public event EventHandler<string> ClipboardTextChanged;
|
||||
#pragma warning restore
|
||||
|
||||
public void BeginWatching()
|
||||
{
|
||||
// Not implemented.
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<RootNamespace>Remotely.ScreenCast.Win</RootNamespace>
|
||||
<AssemblyName>Remotely_ScreenCast</AssemblyName>
|
||||
<ApplicationIcon>favicon.ico</ApplicationIcon>
|
||||
<Authors>Jared Goodwin</Authors>
|
||||
<Company>Translucency Software</Company>
|
||||
<Product>Remotely ScreenCast</Product>
|
||||
<Description>Allows unattended remote control via the Remotely server.</Description>
|
||||
<Copyright>Copyright © 2020 Translucency Software</Copyright>
|
||||
<PackageProjectUrl>https://remotely.one</PackageProjectUrl>
|
||||
<Platforms>AnyCPU;x86;x64</Platforms>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="favicon.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="favicon.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.MixedReality.WebRTC" Version="1.0.3" />
|
||||
<PackageReference Include="Microsoft.Win32.SystemEvents" Version="4.7.0" />
|
||||
<PackageReference Include="NAudio" Version="1.10.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.1.4" />
|
||||
<PackageReference Include="SharpDX" Version="4.2.0" />
|
||||
<PackageReference Include="SharpDX.Direct3D11" Version="4.2.0" />
|
||||
<PackageReference Include="SharpDX.DXGI" Version="4.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ScreenCast.Core\ScreenCast.Core.csproj" />
|
||||
<ProjectReference Include="..\Shared\Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="if $(ConfigurationName) == Debug (
 if $(PlatformName) == Any CPU (
 md "$(SolutionDir)Agent\bin\Debug\netcoreapp3.1\ScreenCast\"
 xcopy "$(TargetDir)*" "$(SolutionDir)Agent\bin\Debug\netcoreapp3.1\ScreenCast\" /y /e /i
 )
 if $(PlatformName) == x64 (
 md "$(SolutionDir)Agent\bin\x64\Debug\netcoreapp3.1\ScreenCast\"
 xcopy "$(TargetDir)*" "$(SolutionDir)Agent\bin\x64\Debug\netcoreapp3.1\ScreenCast\" /y /e /i
 )
 if $(PlatformName) == x86 (
 md "$(SolutionDir)Agent\bin\x86\Debug\netcoreapp3.1\ScreenCast\"
 xcopy "$(TargetDir)*" "$(SolutionDir)Agent\bin\x86\Debug\netcoreapp3.1\ScreenCast\" /y /e /i
 )
)" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
@ -1,81 +0,0 @@
|
||||
using Remotely.ScreenCast.Core.Interfaces;
|
||||
using Remotely.ScreenCast.Core.Services;
|
||||
using Remotely.Shared.Utilities;
|
||||
using Remotely.Shared.Win32;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Remotely.ScreenCast.Win.Services
|
||||
{
|
||||
public class ClipboardServiceWin : IClipboardService
|
||||
{
|
||||
public event EventHandler<string> ClipboardTextChanged;
|
||||
|
||||
private string ClipboardText { get; set; }
|
||||
|
||||
private System.Timers.Timer ClipboardWatcher { get; set; }
|
||||
|
||||
public void BeginWatching()
|
||||
{
|
||||
ClipboardWatcher?.Dispose();
|
||||
ClipboardWatcher = new System.Timers.Timer(500);
|
||||
ClipboardWatcher.Elapsed += ClipboardWatcher_Elapsed;
|
||||
ClipboardWatcher.Start();
|
||||
}
|
||||
|
||||
public void SetText(string clipboardText)
|
||||
{
|
||||
try
|
||||
{
|
||||
var thread = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(clipboardText))
|
||||
{
|
||||
Clipboard.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
Clipboard.SetText(clipboardText);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void StopWatching()
|
||||
{
|
||||
ClipboardWatcher?.Stop();
|
||||
}
|
||||
|
||||
private void ClipboardWatcher_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
var thread = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Win32Interop.SwitchToInputDesktop();
|
||||
|
||||
|
||||
if (Clipboard.ContainsText() && Clipboard.GetText() != ClipboardText)
|
||||
{
|
||||
ClipboardText = Clipboard.GetText();
|
||||
ClipboardTextChanged?.Invoke(this, ClipboardText);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<!-- UAC Manifest Options
|
||||
If you want to change the Windows User Account Control level replace the
|
||||
requestedExecutionLevel node with one of the following.
|
||||
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||
|
||||
Specifying requestedExecutionLevel element will disable file and registry virtualization.
|
||||
Remove this element if your application requires this virtualization for backwards
|
||||
compatibility.
|
||||
-->
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- A list of the Windows versions that this application has been tested on
|
||||
and is designed to work with. Uncomment the appropriate elements
|
||||
and Windows will automatically select the most compatible environment. -->
|
||||
|
||||
<!-- Windows Vista -->
|
||||
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
|
||||
|
||||
<!-- Windows 7 -->
|
||||
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
|
||||
|
||||
<!-- Windows 8 -->
|
||||
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
|
||||
|
||||
<!-- Windows 8.1 -->
|
||||
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
|
||||
|
||||
<!-- Windows 10 -->
|
||||
<!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
|
||||
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
|
||||
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
|
||||
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
|
||||
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
|
||||
|
||||
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
|
||||
<!--
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
</assembly>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 103 KiB |
@ -64,10 +64,11 @@ namespace Remotely.Server.API
|
||||
}
|
||||
var waitTime = DateTimeOffset.Now - startWait;
|
||||
|
||||
downloadingAgents.Set(downloadId, string.Empty, TimeSpan.FromMinutes(10));
|
||||
|
||||
DataService.WriteEvent($"Download started after wait time of {waitTime}. " + "" +
|
||||
$"Current Downloads: {downloadingAgents.Count}. Max Allowed: {AppConfig.MaxConcurrentUpdates}", EventType.Debug, null);
|
||||
|
||||
downloadingAgents.Set(downloadId, string.Empty, TimeSpan.FromMinutes(10));
|
||||
|
||||
byte[] fileBytes;
|
||||
string filePath;
|
||||
|
||||
@ -6,6 +6,7 @@ using Remotely.Shared.Models;
|
||||
using Remotely.Server.Models;
|
||||
using Remotely.Server.Services;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Remotely.Server.Hubs;
|
||||
|
||||
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
|
||||
|
||||
@ -18,21 +19,21 @@ namespace Remotely.Server.API
|
||||
public LoginController(SignInManager<RemotelyUser> signInManager,
|
||||
DataService dataService,
|
||||
ApplicationConfig appConfig,
|
||||
IHubContext<RCDeviceHub> rcDeviceHub,
|
||||
IHubContext<CasterHub> casterHub,
|
||||
IHubContext<RCBrowserHub> rcBrowserHub)
|
||||
{
|
||||
SignInManager = signInManager;
|
||||
DataService = dataService;
|
||||
AppConfig = appConfig;
|
||||
RCDeviceHub = rcDeviceHub;
|
||||
RCBrowserHub = rcBrowserHub;
|
||||
CasterHubContext = casterHub;
|
||||
RCBrowserHubContext = rcBrowserHub;
|
||||
}
|
||||
|
||||
private SignInManager<RemotelyUser> SignInManager { get; }
|
||||
private DataService DataService { get; }
|
||||
public ApplicationConfig AppConfig { get; }
|
||||
private IHubContext<RCDeviceHub> RCDeviceHub { get; }
|
||||
private IHubContext<RCBrowserHub> RCBrowserHub { get; }
|
||||
private IHubContext<CasterHub> CasterHubContext { get; }
|
||||
private IHubContext<RCBrowserHub> RCBrowserHubContext { get; }
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Post([FromBody]ApiLogin login)
|
||||
@ -72,11 +73,11 @@ namespace Remotely.Server.API
|
||||
if (HttpContext?.User?.Identity?.IsAuthenticated == true)
|
||||
{
|
||||
orgId = DataService.GetUserByName(HttpContext.User.Identity.Name)?.OrganizationID;
|
||||
var activeSessions = Services.RCDeviceHub.SessionInfoList.Where(x => x.Value.RequesterUserName == HttpContext.User.Identity.Name);
|
||||
var activeSessions = CasterHub.SessionInfoList.Where(x => x.Value.RequesterUserName == HttpContext.User.Identity.Name);
|
||||
foreach (var session in activeSessions.ToList())
|
||||
{
|
||||
await RCDeviceHub.Clients.Client(session.Value.RCDeviceSocketID).SendAsync("Disconnect", "User logged out.");
|
||||
await RCBrowserHub.Clients.Client(session.Value.RequesterSocketID).SendAsync("ConnectionFailed");
|
||||
await CasterHubContext.Clients.Client(session.Value.CasterSocketID).SendAsync("Disconnect", "User logged out.");
|
||||
await RCBrowserHubContext.Clients.Client(session.Value.RequesterSocketID).SendAsync("ConnectionFailed");
|
||||
}
|
||||
}
|
||||
await SignInManager.SignOutAsync();
|
||||
|
||||
@ -10,6 +10,7 @@ using Remotely.Server.Models;
|
||||
using Remotely.Server.Services;
|
||||
using Remotely.Server.Attributes;
|
||||
using Remotely.Shared.Helpers;
|
||||
using Remotely.Server.Hubs;
|
||||
|
||||
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
|
||||
|
||||
@ -20,18 +21,18 @@ namespace Remotely.Server.API
|
||||
public class RemoteControlController : ControllerBase
|
||||
{
|
||||
public RemoteControlController(DataService dataService,
|
||||
IHubContext<DeviceHub> deviceHub,
|
||||
IHubContext<AgentHub> agentHub,
|
||||
ApplicationConfig appConfig,
|
||||
SignInManager<RemotelyUser> signInManager)
|
||||
{
|
||||
DataService = dataService;
|
||||
DeviceHub = deviceHub;
|
||||
AgentHubContext = agentHub;
|
||||
AppConfig = appConfig;
|
||||
SignInManager = signInManager;
|
||||
}
|
||||
|
||||
public DataService DataService { get; }
|
||||
public IHubContext<DeviceHub> DeviceHub { get; }
|
||||
public IHubContext<AgentHub> AgentHubContext { get; }
|
||||
public ApplicationConfig AppConfig { get; }
|
||||
public SignInManager<RemotelyUser> SignInManager { get; }
|
||||
|
||||
@ -76,7 +77,7 @@ namespace Remotely.Server.API
|
||||
|
||||
private async Task<IActionResult> InitiateRemoteControl(string deviceID, string orgID)
|
||||
{
|
||||
var targetDevice = Services.DeviceHub.ServiceConnections.FirstOrDefault(x =>
|
||||
var targetDevice = Hubs.AgentHub.ServiceConnections.FirstOrDefault(x =>
|
||||
x.Value.OrganizationID == orgID &&
|
||||
x.Value.ID.ToLower() == deviceID.ToLower());
|
||||
|
||||
@ -89,24 +90,24 @@ namespace Remotely.Server.API
|
||||
}
|
||||
|
||||
|
||||
var currentUsers = RCDeviceHub.SessionInfoList.Count(x => x.Value.OrganizationID == orgID);
|
||||
var currentUsers = CasterHub.SessionInfoList.Count(x => x.Value.OrganizationID == orgID);
|
||||
if (currentUsers >= AppConfig.RemoteControlSessionLimit)
|
||||
{
|
||||
return BadRequest("There are already the maximum amount of active remote control sessions for your organization.");
|
||||
}
|
||||
|
||||
var existingSessions = RCDeviceHub.SessionInfoList
|
||||
var existingSessions = CasterHub.SessionInfoList
|
||||
.Where(x => x.Value.DeviceID == targetDevice.Value.ID)
|
||||
.Select(x => x.Key)
|
||||
.ToList();
|
||||
|
||||
await DeviceHub.Clients.Client(targetDevice.Key).SendAsync("RemoteControl", Request.HttpContext.Connection.Id, targetDevice.Key);
|
||||
await AgentHubContext.Clients.Client(targetDevice.Key).SendAsync("RemoteControl", Request.HttpContext.Connection.Id, targetDevice.Key);
|
||||
|
||||
bool remoteControlStarted()
|
||||
{
|
||||
return !RCDeviceHub.SessionInfoList.Values
|
||||
return !CasterHub.SessionInfoList.Values
|
||||
.Where(x => x.DeviceID == targetDevice.Value.ID)
|
||||
.All(x => existingSessions.Contains(x.RCDeviceSocketID));
|
||||
.All(x => existingSessions.Contains(x.CasterSocketID));
|
||||
};
|
||||
|
||||
if (!await TaskHelper.DelayUntil(remoteControlStarted, TimeSpan.FromSeconds(30)))
|
||||
@ -115,9 +116,9 @@ namespace Remotely.Server.API
|
||||
}
|
||||
else
|
||||
{
|
||||
var rcSession = RCDeviceHub.SessionInfoList.Values.LastOrDefault(x => x.DeviceID == targetDevice.Value.ID && !existingSessions.Contains(x.RCDeviceSocketID));
|
||||
var rcSession = CasterHub.SessionInfoList.Values.LastOrDefault(x => x.DeviceID == targetDevice.Value.ID && !existingSessions.Contains(x.CasterSocketID));
|
||||
var otp = RemoteControlFilterAttribute.GetOtp(targetDevice.Value.ID);
|
||||
return Ok($"{HttpContext.Request.Scheme}://{Request.Host}/RemoteControl?clientID={rcSession.RCDeviceSocketID}&serviceID={targetDevice.Key}&fromApi=true&otp={Uri.EscapeDataString(otp)}");
|
||||
return Ok($"{HttpContext.Request.Scheme}://{Request.Host}/RemoteControl?clientID={rcSession.CasterSocketID}&serviceID={targetDevice.Key}&fromApi=true&otp={Uri.EscapeDataString(otp)}");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@ -10,6 +10,7 @@ using System.Threading.Tasks;
|
||||
using Remotely.Shared.Helpers;
|
||||
using System.IO;
|
||||
using Remotely.Server.Attributes;
|
||||
using Remotely.Server.Hubs;
|
||||
|
||||
namespace Remotely.Server.API
|
||||
{
|
||||
@ -19,15 +20,15 @@ namespace Remotely.Server.API
|
||||
{
|
||||
public ScriptingController(DataService dataService,
|
||||
UserManager<RemotelyUser> userManager,
|
||||
IHubContext<DeviceHub> deviceHub)
|
||||
IHubContext<AgentHub> agentHub)
|
||||
{
|
||||
DataService = dataService;
|
||||
UserManager = userManager;
|
||||
DeviceHub = deviceHub;
|
||||
AgentHubContext = agentHub;
|
||||
}
|
||||
|
||||
private DataService DataService { get; }
|
||||
private IHubContext<DeviceHub> DeviceHub { get; }
|
||||
private IHubContext<AgentHub> AgentHubContext { get; }
|
||||
private UserManager<RemotelyUser> UserManager { get; }
|
||||
|
||||
[ServiceFilter(typeof(ApiAuthorizationFilter))]
|
||||
@ -55,7 +56,7 @@ namespace Remotely.Server.API
|
||||
|
||||
Request.Headers.TryGetValue("OrganizationID", out var orgID);
|
||||
|
||||
KeyValuePair<string, Device> connection = Services.DeviceHub.ServiceConnections.FirstOrDefault(x =>
|
||||
KeyValuePair<string, Device> connection = AgentHub.ServiceConnections.FirstOrDefault(x =>
|
||||
x.Value.OrganizationID == orgID &&
|
||||
x.Value.ID == deviceID);
|
||||
|
||||
@ -75,14 +76,14 @@ namespace Remotely.Server.API
|
||||
};
|
||||
DataService.AddOrUpdateCommandResult(commandResult);
|
||||
var requestID = Guid.NewGuid().ToString();
|
||||
await DeviceHub.Clients.Client(connection.Key).SendAsync("ExecuteCommandFromApi", mode, requestID, command, commandResult.ID, Guid.NewGuid().ToString());
|
||||
var success = await TaskHelper.DelayUntil(() => Services.DeviceHub.ApiScriptResults.TryGetValue(requestID, out _), TimeSpan.FromSeconds(30));
|
||||
await AgentHubContext.Clients.Client(connection.Key).SendAsync("ExecuteCommandFromApi", mode, requestID, command, commandResult.ID, Guid.NewGuid().ToString());
|
||||
var success = await TaskHelper.DelayUntil(() => AgentHub.ApiScriptResults.TryGetValue(requestID, out _), TimeSpan.FromSeconds(30));
|
||||
if (!success)
|
||||
{
|
||||
return commandResult;
|
||||
}
|
||||
Services.DeviceHub.ApiScriptResults.TryGetValue(requestID, out var commandID);
|
||||
Services.DeviceHub.ApiScriptResults.Remove(requestID);
|
||||
AgentHub.ApiScriptResults.TryGetValue(requestID, out var commandID);
|
||||
AgentHub.ApiScriptResults.Remove(requestID);
|
||||
DataService.DetachEntity(commandResult);
|
||||
var result = DataService.GetCommandResult(commandID.ToString(), orgID);
|
||||
return result;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user