mirror of
https://github.com/immense/Remotely.git
synced 2025-10-26 11:27:15 +00:00
Beginning work on Linux remote control client.
This commit is contained in:
parent
aa05a9e049
commit
a43e7cba27
42
Remotely.sln
42
Remotely.sln
@ -29,11 +29,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Remotely_Server", "Remotely
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Remotely_Library", "Remotely_Library\Remotely_Library.csproj", "{A9E1BA7A-6080-4DAC-9B29-6DC8437150EA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Remotely_ScreenCast", "Remotely_ScreenCast\Remotely_ScreenCast.csproj", "{2DCEA1F5-9B64-4EDB-9CD0-4D6675D96709}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Remotely_ScreenCast.Win", "Remotely_ScreenCast.Win\Remotely_ScreenCast.Win.csproj", "{2DCEA1F5-9B64-4EDB-9CD0-4D6675D96709}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Remotely_Desktop", "Remotely_Desktop\Remotely_Desktop.csproj", "{486A238C-387B-49C5-A361-B86ACDB2572A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Remotely_ScreenCast_Linux", "Remotely_ScreenCast_Linux\Remotely_ScreenCast_Linux.csproj", "{9DB9F02E-9952-4A61-860A-CE2C6CE2A7E1}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Remotely_ScreenCast.Linux", "Remotely_ScreenCast.Linux\Remotely_ScreenCast.Linux.csproj", "{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Remotely_ScreenCast.Core", "Remotely_ScreenCast.Core\Remotely_ScreenCast.Core.csproj", "{B04A1728-2E87-491E-BC7F-F575A1754DEF}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -105,18 +107,30 @@ Global
|
||||
{486A238C-387B-49C5-A361-B86ACDB2572A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{486A238C-387B-49C5-A361-B86ACDB2572A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{486A238C-387B-49C5-A361-B86ACDB2572A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{9DB9F02E-9952-4A61-860A-CE2C6CE2A7E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9DB9F02E-9952-4A61-860A-CE2C6CE2A7E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9DB9F02E-9952-4A61-860A-CE2C6CE2A7E1}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{9DB9F02E-9952-4A61-860A-CE2C6CE2A7E1}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{9DB9F02E-9952-4A61-860A-CE2C6CE2A7E1}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9DB9F02E-9952-4A61-860A-CE2C6CE2A7E1}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{9DB9F02E-9952-4A61-860A-CE2C6CE2A7E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9DB9F02E-9952-4A61-860A-CE2C6CE2A7E1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9DB9F02E-9952-4A61-860A-CE2C6CE2A7E1}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{9DB9F02E-9952-4A61-860A-CE2C6CE2A7E1}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9DB9F02E-9952-4A61-860A-CE2C6CE2A7E1}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9DB9F02E-9952-4A61-860A-CE2C6CE2A7E1}.Release|x86.Build.0 = Release|Any CPU
|
||||
{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|Any CPU
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{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|Any CPU
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E46F11D0-3C88-4D43-8CCC-EE7182CAD5C1}.Release|x86.Build.0 = Release|Any CPU
|
||||
{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|Any CPU
|
||||
{B04A1728-2E87-491E-BC7F-F575A1754DEF}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{B04A1728-2E87-491E-BC7F-F575A1754DEF}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{B04A1728-2E87-491E-BC7F-F575A1754DEF}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{B04A1728-2E87-491E-BC7F-F575A1754DEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B04A1728-2E87-491E-BC7F-F575A1754DEF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B04A1728-2E87-491E-BC7F-F575A1754DEF}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{B04A1728-2E87-491E-BC7F-F575A1754DEF}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B04A1728-2E87-491E-BC7F-F575A1754DEF}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{B04A1728-2E87-491E-BC7F-F575A1754DEF}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@ -258,13 +258,8 @@ namespace Remotely_Agent.Services
|
||||
}
|
||||
try
|
||||
{
|
||||
if (!OSUtils.IsWindows)
|
||||
{
|
||||
await hubConnection.InvokeAsync("DisplayConsoleMessage", $"Remote control is only supported on Windows at this time.", requesterID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ScreenCast", OSUtils.ScreenCastExecutableFileName)))
|
||||
var rcBinaryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ScreenCast", OSUtils.ScreenCastExecutableFileName);
|
||||
if (!File.Exists(rcBinaryPath))
|
||||
{
|
||||
await hubConnection.InvokeAsync("DisplayConsoleMessage", "Remote control executable not found on target device.", requesterID);
|
||||
return;
|
||||
@ -278,24 +273,24 @@ namespace Remotely_Agent.Services
|
||||
|
||||
if (Program.IsDebug)
|
||||
{
|
||||
Process.Start(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ScreenCast", OSUtils.ScreenCastExecutableFileName), $"-mode Unattended -requester {requesterID} -serviceid {serviceID} -host {Utilities.GetConnectionInfo().Host} -desktop default");
|
||||
Process.Start(rcBinaryPath, $"-mode Unattended -requester {requesterID} -serviceid {serviceID} -host {Utilities.GetConnectionInfo().Host} -desktop default");
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = Win32Interop.OpenInteractiveProcess(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ScreenCast", OSUtils.ScreenCastExecutableFileName) + $" -mode Unattended -requester {requesterID} -serviceid {serviceID} -host {Utilities.GetConnectionInfo().Host} -desktop default", "default", true, out _);
|
||||
var result = Win32Interop.OpenInteractiveProcess(rcBinaryPath + $" -mode Unattended -requester {requesterID} -serviceid {serviceID} -host {Utilities.GetConnectionInfo().Host} -desktop default", "default", true, out _);
|
||||
if (!result)
|
||||
{
|
||||
await hubConnection.InvokeAsync("DisplayConsoleMessage", "Remote control failed to start on target device.", requesterID);
|
||||
}
|
||||
}
|
||||
}
|
||||
//else if (OSUtils.IsLinux)
|
||||
//{
|
||||
// var users = OSUtils.StartProcessWithResults("users", "");
|
||||
// var username = users?.Split()?.FirstOrDefault()?.Trim();
|
||||
else if (OSUtils.IsLinux)
|
||||
{
|
||||
var users = OSUtils.StartProcessWithResults("users", "");
|
||||
var username = users?.Split()?.FirstOrDefault()?.Trim();
|
||||
|
||||
// Process.Start("sudo", $"-u {username} {rcBinaryPath} -mode Unattended -requester {requesterID} -serviceid {serviceID} -desktop default -hostname {Utilities.GetConnectionInfo().Host.Split("//").Last()}");
|
||||
//}
|
||||
Process.Start("sudo", $"-u {username} mono {rcBinaryPath} -mode Unattended -requester {requesterID} -serviceid {serviceID} -host {Utilities.GetConnectionInfo().Host} -desktop default");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -314,17 +309,18 @@ namespace Remotely_Agent.Services
|
||||
}
|
||||
try
|
||||
{
|
||||
var rcBinaryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ScreenCast", OSUtils.ScreenCastExecutableFileName);
|
||||
// Start ScreenCast.
|
||||
if (OSUtils.IsWindows)
|
||||
{
|
||||
|
||||
if (Program.IsDebug)
|
||||
{
|
||||
Process.Start(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ScreenCast", OSUtils.ScreenCastExecutableFileName), $"-mode Unattended -requester {requesterID} -serviceid {serviceID} -host {Utilities.GetConnectionInfo().Host} -relaunch true -desktop default -viewers {String.Join(",", viewerIDs)}");
|
||||
Process.Start(rcBinaryPath, $"-mode Unattended -requester {requesterID} -serviceid {serviceID} -host {Utilities.GetConnectionInfo().Host} -relaunch true -desktop default -viewers {String.Join(",", viewerIDs)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = Win32Interop.OpenInteractiveProcess(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ScreenCast", OSUtils.ScreenCastExecutableFileName) + $" -mode Unattended -requester {requesterID} -serviceid {serviceID} -host {Utilities.GetConnectionInfo().Host} -relaunch true -desktop default -viewers {String.Join(",", viewerIDs)}", "default", true, out _);
|
||||
var result = Win32Interop.OpenInteractiveProcess(rcBinaryPath + $" -mode Unattended -requester {requesterID} -serviceid {serviceID} -host {Utilities.GetConnectionInfo().Host} -relaunch true -desktop default -viewers {String.Join(",", viewerIDs)}", "default", true, out _);
|
||||
if (!result)
|
||||
{
|
||||
Logger.Write("Failed to relaunch screen caster.");
|
||||
@ -333,13 +329,13 @@ namespace Remotely_Agent.Services
|
||||
}
|
||||
}
|
||||
}
|
||||
//else if (OSUtils.IsLinux)
|
||||
//{
|
||||
// var users = OSUtils.StartProcessWithResults("users", "");
|
||||
// var username = users?.Split()?.FirstOrDefault()?.Trim();
|
||||
else if (OSUtils.IsLinux)
|
||||
{
|
||||
var users = OSUtils.StartProcessWithResults("users", "");
|
||||
var username = users?.Split()?.FirstOrDefault()?.Trim();
|
||||
|
||||
// Process.Start("sudo", $"-u {username} {rcBinaryPath} -mode Unattended -requester {requesterID} -serviceid {serviceID} -desktop default -hostname {Utilities.GetConnectionInfo().Host.Split("//").Last()}");
|
||||
//}
|
||||
Process.Start("sudo", $"-u {username} mono {rcBinaryPath} -mode Unattended -requester {requesterID} -serviceid {serviceID} -hostname {Utilities.GetConnectionInfo().Host} -desktop default");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
using Remotely_Desktop.ViewModels;
|
||||
using Remotely_ScreenCast.Models;
|
||||
using Remotely_ScreenCast.Core.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\Costura.Fody.3.3.2\build\Costura.Fody.props" Condition="Exists('..\packages\Costura.Fody.3.3.2\build\Costura.Fody.props')" />
|
||||
<Import Project="..\packages\Costura.Fody.3.3.3\build\Costura.Fody.props" Condition="Exists('..\packages\Costura.Fody.3.3.3\build\Costura.Fody.props')" />
|
||||
<Import Project="..\packages\PropertyChanged.Fody.2.6.0\build\PropertyChanged.Fody.props" Condition="Exists('..\packages\PropertyChanged.Fody.2.6.0\build\PropertyChanged.Fody.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
@ -45,8 +45,8 @@
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Costura, Version=3.3.2.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Costura.Fody.3.3.2\lib\net40\Costura.dll</HintPath>
|
||||
<Reference Include="Costura, Version=3.3.3.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Costura.Fody.3.3.3\lib\net40\Costura.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
@ -135,20 +135,23 @@
|
||||
<Resource Include="favicon.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Remotely_ScreenCast\Remotely_ScreenCast.csproj">
|
||||
<ProjectReference Include="..\Remotely_ScreenCast.Core\Remotely_ScreenCast.Core.csproj">
|
||||
<Project>{b04a1728-2e87-491e-bc7f-f575a1754def}</Project>
|
||||
<Name>Remotely_ScreenCast.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Remotely_ScreenCast.Win\Remotely_ScreenCast.Win.csproj">
|
||||
<Project>{2dcea1f5-9b64-4edb-9cd0-4d6675d96709}</Project>
|
||||
<Name>Remotely_ScreenCast</Name>
|
||||
<Name>Remotely_ScreenCast.Win</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\PropertyChanged.Fody.2.6.0\build\PropertyChanged.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\PropertyChanged.Fody.2.6.0\build\PropertyChanged.Fody.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Costura.Fody.3.3.2\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.3.3.2\build\Costura.Fody.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Fody.4.0.2\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.4.0.2\build\Fody.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Fody.4.2.1\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.4.2.1\build\Fody.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Costura.Fody.3.3.3\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.3.3.3\build\Costura.Fody.props'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\Fody.4.0.2\build\Fody.targets" Condition="Exists('..\packages\Fody.4.0.2\build\Fody.targets')" />
|
||||
<Import Project="..\packages\Fody.4.2.1\build\Fody.targets" Condition="Exists('..\packages\Fody.4.2.1\build\Fody.targets')" />
|
||||
</Project>
|
||||
@ -1,8 +1,11 @@
|
||||
using Remotely_Desktop.Controls;
|
||||
using Remotely_Desktop.Services;
|
||||
using Remotely_ScreenCast;
|
||||
using Remotely_ScreenCast.Capture;
|
||||
using Remotely_ScreenCast.Models;
|
||||
using Remotely_ScreenCast.Core;
|
||||
using Remotely_ScreenCast.Core.Capture;
|
||||
using Remotely_ScreenCast.Core.Models;
|
||||
using Remotely_ScreenCast.Win;
|
||||
using Remotely_ScreenCast.Win.Capture;
|
||||
using Remotely_ScreenCast.Win.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@ -23,16 +26,24 @@ namespace Remotely_Desktop.ViewModels
|
||||
public MainWindowViewModel()
|
||||
{
|
||||
Current = this;
|
||||
Conductor = new Conductor();
|
||||
Conductor.SessionIDChanged += SessionIDChanged;
|
||||
Conductor.ViewerRemoved += ViewerRemoved;
|
||||
Conductor.ViewerAdded += ViewerAdded;
|
||||
Conductor.ScreenCastRequested += ScreenCastRequested;
|
||||
CursorIconWatcher = new CursorIconWatcher(Conductor);
|
||||
CursorIconWatcher.OnChange += CursorIconWatcher_OnChange;
|
||||
}
|
||||
|
||||
Program.SessionIDChanged += SessionIDChanged;
|
||||
Program.ViewerRemoved += ViewerRemoved;
|
||||
Program.ViewerAdded += ViewerAdded;
|
||||
Program.ScreenCastRequested += ScreenCastRequested;
|
||||
private async void CursorIconWatcher_OnChange(object sender, CursorInfo cursor)
|
||||
{
|
||||
await Conductor.OutgoingMessages.SendCursorChange(cursor, Conductor.Viewers.Keys.ToList());
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public static MainWindowViewModel Current { get; private set; }
|
||||
public Conductor Conductor { get; }
|
||||
public Config Config { get; private set; }
|
||||
public string ForceHost { get; }
|
||||
public bool AllowHostChange
|
||||
@ -52,6 +63,8 @@ namespace Remotely_Desktop.ViewModels
|
||||
|
||||
public string SessionID { get; set; }
|
||||
public ObservableCollection<Viewer> Viewers { get; } = new ObservableCollection<Viewer>();
|
||||
public CursorIconWatcher CursorIconWatcher { get; }
|
||||
|
||||
public void FirePropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
@ -75,10 +88,10 @@ namespace Remotely_Desktop.ViewModels
|
||||
}
|
||||
|
||||
|
||||
Program.ProcessArgs(new string[] { "-mode", "Normal", "-host", Config.Host });
|
||||
Conductor.ProcessArgs(new string[] { "-mode", "Normal", "-host", Config.Host });
|
||||
try
|
||||
{
|
||||
await Program.Connect();
|
||||
await Conductor.Connect();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -86,24 +99,43 @@ namespace Remotely_Desktop.ViewModels
|
||||
MessageBox.Show("Failed to connect to server.", "Connection Failed", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
Program.SetEventHandlers();
|
||||
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
await Program.HandleConnection();
|
||||
});
|
||||
|
||||
Conductor.SetMessageHandlers(new WinInput());
|
||||
await Conductor.OutgoingMessages.SendDeviceInfo(Conductor.ServiceID, Environment.MachineName);
|
||||
await Conductor.OutgoingMessages.GetSessionID();
|
||||
}
|
||||
|
||||
private void ScreenCastRequested(object sender, Tuple<string, string> args)
|
||||
|
||||
|
||||
private void ScreenCastRequested(object sender, Tuple<string, string> viewerAndRequester)
|
||||
{
|
||||
App.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
var result = MessageBox.Show($"You've received a connection request from {args.Item2}. Accept?", "Connection Request", MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||||
var result = MessageBox.Show($"You've received a connection request from {viewerAndRequester.Item2}. Accept?", "Connection Request", MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||||
if (result == MessageBoxResult.Yes)
|
||||
{
|
||||
Task.Run(() =>
|
||||
Task.Run(async() =>
|
||||
{
|
||||
ScreenCaster.BeginScreenCasting(args.Item1, args.Item2, Program.OutgoingMessages);
|
||||
ICapturer capturer;
|
||||
try
|
||||
{
|
||||
if (Conductor.Viewers.Count == 0)
|
||||
{
|
||||
capturer = new DXCapture();
|
||||
capturer.Init();
|
||||
}
|
||||
else
|
||||
{
|
||||
capturer = new BitBltCapture();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
capturer = new BitBltCapture();
|
||||
}
|
||||
await Conductor.OutgoingMessages.SendCursorChange(CursorIconWatcher.GetCurrentCursor(), new List<string>() { viewerAndRequester.Item1 });
|
||||
ScreenCaster.BeginScreenCasting(viewerAndRequester.Item1, viewerAndRequester.Item2, Conductor.OutgoingMessages, capturer, Conductor);
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -114,7 +146,7 @@ namespace Remotely_Desktop.ViewModels
|
||||
foreach (Viewer viewer in viewerList)
|
||||
{
|
||||
viewer.DisconnectRequested = true;
|
||||
await Program.OutgoingMessages.SendViewerRemoved(viewer.ViewerConnectionID);
|
||||
await Conductor.OutgoingMessages.SendViewerRemoved(viewer.ViewerConnectionID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Costura.Fody" version="3.3.2" targetFramework="net472" />
|
||||
<package id="Fody" version="4.0.2" targetFramework="net472" developmentDependency="true" />
|
||||
<package id="Costura.Fody" version="3.3.3" targetFramework="net472" />
|
||||
<package id="Fody" version="4.2.1" targetFramework="net472" developmentDependency="true" />
|
||||
<package id="Newtonsoft.Json" version="12.0.1" targetFramework="net472" />
|
||||
<package id="PropertyChanged.Fody" version="2.6.0" targetFramework="net472" />
|
||||
</packages>
|
||||
@ -47,7 +47,7 @@ namespace Remotely_Library.Services
|
||||
}
|
||||
else if (IsLinux)
|
||||
{
|
||||
return "Remotely_ScreenCast";
|
||||
return "Remotely_ScreenCast.Mono.exe";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -5,7 +5,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast.Capture
|
||||
namespace Remotely_ScreenCast.Core.Capture
|
||||
{
|
||||
public interface ICapturer : IDisposable
|
||||
{
|
||||
@ -15,6 +15,10 @@ namespace Remotely_ScreenCast.Capture
|
||||
Bitmap PreviousFrame { get; set; }
|
||||
EventHandler<Rectangle> ScreenChanged { get; set; }
|
||||
int SelectedScreen { get; set; }
|
||||
int ScreenCount { get; set; }
|
||||
double VirtualScreenHeight { get; set; }
|
||||
double VirtualScreenWidth { get; set; }
|
||||
|
||||
void Capture();
|
||||
void Init();
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
using Remotely_ScreenCast.Models;
|
||||
using Remotely_ScreenCast.Core.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
@ -10,7 +10,7 @@ using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast.Capture
|
||||
namespace Remotely_ScreenCast.Core.Capture
|
||||
{
|
||||
public class ImageUtils
|
||||
{
|
||||
@ -1,7 +1,7 @@
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Remotely_ScreenCast.Models;
|
||||
using Remotely_ScreenCast.Sockets;
|
||||
using Remotely_ScreenCast.Utilities;
|
||||
using Remotely_ScreenCast.Core.Models;
|
||||
using Remotely_ScreenCast.Core.Sockets;
|
||||
using Remotely_ScreenCast.Core.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
@ -9,44 +9,23 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Remotely_ScreenCast.Capture
|
||||
namespace Remotely_ScreenCast.Core.Capture
|
||||
{
|
||||
public class ScreenCaster
|
||||
{
|
||||
public static async void BeginScreenCasting(string viewerID,
|
||||
string requesterName,
|
||||
OutgoingMessages outgoingMessages)
|
||||
OutgoingMessages outgoingMessages,
|
||||
ICapturer capturer,
|
||||
Conductor conductor)
|
||||
{
|
||||
ICapturer capturer;
|
||||
CaptureMode captureMode;
|
||||
Viewer viewer;
|
||||
byte[] encodedImageBytes;
|
||||
var success = false;
|
||||
|
||||
try
|
||||
{
|
||||
if (Program.Viewers.Count == 0)
|
||||
{
|
||||
capturer = new DXCapture();
|
||||
captureMode = CaptureMode.DirectX;
|
||||
capturer.Init();
|
||||
}
|
||||
else
|
||||
{
|
||||
capturer = new BitBltCapture();
|
||||
captureMode = CaptureMode.BitBtl;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
capturer = new BitBltCapture();
|
||||
captureMode = CaptureMode.BitBtl;
|
||||
}
|
||||
|
||||
|
||||
Logger.Write($"Starting screen cast. Requester: {requesterName}. Viewer ID: {viewerID}. Capture Mode: {captureMode.ToString()}. App Mode: {Program.Mode} Desktop: {Program.CurrentDesktopName}");
|
||||
Logger.Write($"Starting screen cast. Requester: {requesterName}. Viewer ID: {viewerID}. Capturer: {capturer.GetType().ToString()}. App Mode: {conductor.Mode} Desktop: {conductor.CurrentDesktopName}");
|
||||
|
||||
viewer = new Viewer()
|
||||
{
|
||||
@ -59,17 +38,17 @@ namespace Remotely_ScreenCast.Capture
|
||||
|
||||
while (!success)
|
||||
{
|
||||
success = Program.Viewers.TryAdd(viewerID, viewer);
|
||||
success = conductor.Viewers.TryAdd(viewerID, viewer);
|
||||
}
|
||||
|
||||
if (Program.Mode == Enums.AppMode.Normal)
|
||||
if (conductor.Mode == Enums.AppMode.Normal)
|
||||
{
|
||||
Program.ViewerAdded?.Invoke(null, viewer);
|
||||
conductor.ViewerAdded?.Invoke(null, viewer);
|
||||
}
|
||||
|
||||
await outgoingMessages.SendScreenCount(
|
||||
capturer.SelectedScreen,
|
||||
Screen.AllScreens.Length,
|
||||
capturer.ScreenCount,
|
||||
viewerID);
|
||||
|
||||
await outgoingMessages.SendScreenSize(capturer.CurrentScreenBounds.Width, capturer.CurrentScreenBounds.Height, viewerID);
|
||||
@ -79,8 +58,6 @@ namespace Remotely_ScreenCast.Capture
|
||||
await outgoingMessages.SendScreenSize(bounds.Width, bounds.Height, viewerID);
|
||||
};
|
||||
|
||||
await outgoingMessages.SendCursorChange(CursorIconWatcher.Current.GetCurrentCursor(), new List<string>() { viewerID });
|
||||
|
||||
// TODO: SetThradDesktop causes issues with input after switching.
|
||||
//var desktopName = Win32Interop.GetCurrentDesktop();
|
||||
while (!viewer.DisconnectRequested)
|
||||
@ -164,13 +141,13 @@ namespace Remotely_ScreenCast.Capture
|
||||
success = false;
|
||||
while (!success)
|
||||
{
|
||||
success = Program.Viewers.TryRemove(viewerID, out _);
|
||||
success = conductor.Viewers.TryRemove(viewerID, out _);
|
||||
}
|
||||
|
||||
capturer.Dispose();
|
||||
|
||||
// Close if no one is viewing.
|
||||
if (Program.Viewers.Count == 0 && Program.Mode == Enums.AppMode.Unattended)
|
||||
if (conductor.Viewers.Count == 0 && conductor.Mode == Enums.AppMode.Unattended)
|
||||
{
|
||||
Environment.Exit(0);
|
||||
}
|
||||
@ -180,7 +157,7 @@ namespace Remotely_ScreenCast.Capture
|
||||
{
|
||||
var absoluteX = (capturer.CurrentScreenBounds.Width * percentX) + capturer.CurrentScreenBounds.Left;
|
||||
var absoluteY = (capturer.CurrentScreenBounds.Height * percentY) + capturer.CurrentScreenBounds.Top;
|
||||
return new Tuple<double, double>(absoluteX / SystemInformation.VirtualScreen.Width, absoluteY / SystemInformation.VirtualScreen.Height);
|
||||
return new Tuple<double, double>(absoluteX / capturer.VirtualScreenWidth, absoluteY / capturer.VirtualScreenHeight);
|
||||
}
|
||||
public static Tuple<double, double> GetAbsolutePointFromRelativePercent(double percentX, double percentY, ICapturer capturer)
|
||||
{
|
||||
98
Remotely_ScreenCast.Core/Conductor.cs
Normal file
98
Remotely_ScreenCast.Core/Conductor.cs
Normal file
@ -0,0 +1,98 @@
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Remotely_ScreenCast.Core.Enums;
|
||||
using Remotely_ScreenCast.Core.Input;
|
||||
using Remotely_ScreenCast.Core.Models;
|
||||
using Remotely_ScreenCast.Core.Sockets;
|
||||
using Remotely_ScreenCast.Core.Utilities;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast.Core
|
||||
{
|
||||
public class Conductor
|
||||
{
|
||||
public AppMode Mode { get; private set; }
|
||||
public string RequesterID { get; private set; }
|
||||
public string ServiceID { get; private set; }
|
||||
public string Host { get; private set; }
|
||||
public HubConnection Connection { get; private set; }
|
||||
public OutgoingMessages OutgoingMessages { get; private set; }
|
||||
public string CurrentDesktopName { get; set; }
|
||||
public ConcurrentDictionary<string, Viewer> Viewers { get; } = new ConcurrentDictionary<string, Viewer>();
|
||||
public Dictionary<string, string> ArgDict { get; set; }
|
||||
|
||||
public void SetMessageHandlers(IKeyboardMouseInput keyboardMouse)
|
||||
{
|
||||
OutgoingMessages = new OutgoingMessages(Connection);
|
||||
|
||||
MessageHandlers.ApplyConnectionHandlers(Connection, this, keyboardMouse);
|
||||
}
|
||||
|
||||
public Task Connect()
|
||||
{
|
||||
Connection = new HubConnectionBuilder()
|
||||
.WithUrl($"{Host}/RCDeviceHub")
|
||||
.AddMessagePackProtocol()
|
||||
.Build();
|
||||
|
||||
return Connection.StartAsync();
|
||||
}
|
||||
|
||||
public void StartWaitForViewerTimer()
|
||||
{
|
||||
var timer = new System.Timers.Timer(10000);
|
||||
timer.AutoReset = false;
|
||||
timer.Elapsed += (sender, arg) =>
|
||||
{
|
||||
// Shut down if no viewers have connected within 10 seconds.
|
||||
if (Viewers.Count == 0)
|
||||
{
|
||||
Logger.Write("No viewers connected after 10 seconds. Shutting down.");
|
||||
Environment.Exit(0);
|
||||
}
|
||||
};
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
public void ProcessArgs(string[] args)
|
||||
{
|
||||
ArgDict = new Dictionary<string, string>();
|
||||
|
||||
for (var i = 0; i < args.Length; i += 2)
|
||||
{
|
||||
var key = args?[i];
|
||||
if (key != null)
|
||||
{
|
||||
key = key.Trim().Replace("-", "").ToLower();
|
||||
var value = args?[i + 1];
|
||||
if (value != null)
|
||||
{
|
||||
ArgDict[key] = args[i + 1].Trim();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Mode = (AppMode)Enum.Parse(typeof(AppMode), ArgDict["mode"]);
|
||||
Host = ArgDict["host"];
|
||||
|
||||
if (Mode == AppMode.Unattended)
|
||||
{
|
||||
RequesterID = ArgDict["requester"];
|
||||
CurrentDesktopName = ArgDict["desktop"];
|
||||
ServiceID = ArgDict["serviceid"];
|
||||
}
|
||||
|
||||
}
|
||||
public EventHandler<string> SessionIDChanged { get; set; }
|
||||
public EventHandler<string> ViewerRemoved { get; set; }
|
||||
public EventHandler<Viewer> ViewerAdded { get; set; }
|
||||
public EventHandler<Tuple<string, string>> ScreenCastRequested { get; set; }
|
||||
public EventHandler<Tuple<string, string>> ScreenCastInitiated { get; set; }
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast.Enums
|
||||
namespace Remotely_ScreenCast.Core.Enums
|
||||
{
|
||||
public enum AppMode
|
||||
{
|
||||
20
Remotely_ScreenCast.Core/Input/IKeyboardMouseInput.cs
Normal file
20
Remotely_ScreenCast.Core/Input/IKeyboardMouseInput.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast.Core.Input
|
||||
{
|
||||
public interface IKeyboardMouseInput
|
||||
{
|
||||
void SendKeyDown(string key);
|
||||
void SendKeyUp(string key);
|
||||
uint SendMouseMove(double percentX, double percentY);
|
||||
uint SendLeftMouseDown(double percentX, double percentY);
|
||||
uint SendLeftMouseUp(double percentX, double percentY);
|
||||
uint SendRightMouseDown(double percentX, double percentY);
|
||||
uint SendRightMouseUp(double percentX, double percentY);
|
||||
uint SendMouseWheel(int deltaY);
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast.Models
|
||||
namespace Remotely_ScreenCast.Core.Models
|
||||
{
|
||||
public class CursorInfo
|
||||
{
|
||||
@ -1,4 +1,4 @@
|
||||
using Remotely_ScreenCast.Capture;
|
||||
using Remotely_ScreenCast.Core.Capture;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
@ -6,7 +6,7 @@ using System.Drawing.Imaging;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast.Models
|
||||
namespace Remotely_ScreenCast.Core.Models
|
||||
{
|
||||
public class Viewer
|
||||
{
|
||||
26
Remotely_ScreenCast.Core/Remotely_ScreenCast.Core.csproj
Normal file
26
Remotely_ScreenCast.Core/Remotely_ScreenCast.Core.csproj
Normal file
@ -0,0 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client.Core" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="1.1.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="4.5.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@ -1,7 +1,7 @@
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Remotely_ScreenCast_Linux.Capture;
|
||||
using Remotely_ScreenCast_Linux.Utilities;
|
||||
using Remotely_ScreenCast_Linux;
|
||||
using Remotely_ScreenCast.Core.Capture;
|
||||
using Remotely_ScreenCast.Core.Utilities;
|
||||
using Remotely_ScreenCast.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -10,17 +10,18 @@ using System.Threading.Tasks;
|
||||
using System.Net;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using Remotely_ScreenCast_Linux.Models;
|
||||
using Remotely_ScreenCast.Core.Models;
|
||||
using Remotely_ScreenCast.Core.Input;
|
||||
|
||||
namespace Remotely_ScreenCast_Linux.Sockets
|
||||
namespace Remotely_ScreenCast.Core.Sockets
|
||||
{
|
||||
public class MessageHandlers
|
||||
{
|
||||
public static void ApplyConnectionHandlers(HubConnection hubConnection, OutgoingMessages outgoingMessages)
|
||||
public static void ApplyConnectionHandlers(HubConnection hubConnection, Conductor conductor, IKeyboardMouseInput keyboardMouse)
|
||||
{
|
||||
hubConnection.Closed += (ex) =>
|
||||
{
|
||||
Logger.Write($"Error: {ex.Message}");
|
||||
Logger.Write($"Connection closed. Error: {ex.Message}");
|
||||
Environment.Exit(1);
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
@ -29,7 +30,7 @@ namespace Remotely_ScreenCast_Linux.Sockets
|
||||
{
|
||||
try
|
||||
{
|
||||
ScreenCaster.BeginScreenCasting(viewerID, requesterName, outgoingMessages);
|
||||
conductor.ScreenCastInitiated?.Invoke(null, new Tuple<string, string>(viewerID, requesterName));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -39,100 +40,97 @@ namespace Remotely_ScreenCast_Linux.Sockets
|
||||
|
||||
hubConnection.On("RequestScreenCast", (string viewerID, string requesterName) =>
|
||||
{
|
||||
Program.ScreenCastRequested?.Invoke(null, new Tuple<string, string>(viewerID, requesterName));
|
||||
conductor.ScreenCastRequested?.Invoke(null, new Tuple<string, string>(viewerID, requesterName));
|
||||
});
|
||||
|
||||
hubConnection.On("KeyDown", (string key, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
//var keyCode = Win32Interop.ConvertJavaScriptKeyToVirtualKey(key);
|
||||
//Win32Interop.SendKeyDown((User32.VirtualKey)keyCode);
|
||||
keyboardMouse.SendKeyDown(key);
|
||||
}
|
||||
});
|
||||
|
||||
hubConnection.On("KeyUp", (string key, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
//var keyCode = Win32Interop.ConvertJavaScriptKeyToVirtualKey(key);
|
||||
//Win32Interop.SendKeyUp((User32.VirtualKey)keyCode);
|
||||
keyboardMouse.SendKeyUp(key);
|
||||
}
|
||||
});
|
||||
|
||||
hubConnection.On("KeyPress", async (string key, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
//var keyCode = Win32Interop.ConvertJavaScriptKeyToVirtualKey(key);
|
||||
//Win32Interop.SendKeyDown((User32.VirtualKey)keyCode);
|
||||
//await Task.Delay(1);
|
||||
//Win32Interop.SendKeyUp((User32.VirtualKey)keyCode);
|
||||
keyboardMouse.SendKeyDown(key);
|
||||
await Task.Delay(1);
|
||||
keyboardMouse.SendKeyUp(key);
|
||||
}
|
||||
});
|
||||
|
||||
hubConnection.On("MouseMove", (double percentX, double percentY, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
var mousePoint = ScreenCaster.GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
|
||||
//Win32Interop.SendMouseMove(mousePoint.Item1, mousePoint.Item2);
|
||||
var xyPercents = ScreenCaster.GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
|
||||
keyboardMouse.SendMouseMove(xyPercents.Item1, xyPercents.Item2);
|
||||
}
|
||||
});
|
||||
|
||||
hubConnection.On("MouseDown", (int button, double percentX, double percentY, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
var mousePoint = ScreenCaster.GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
|
||||
var xyPercents = ScreenCaster.GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
|
||||
if (button == 0)
|
||||
{
|
||||
//Win32Interop.SendLeftMouseDown((int)mousePoint.Item1, (int)mousePoint.Item2);
|
||||
keyboardMouse.SendLeftMouseDown(xyPercents.Item1, xyPercents.Item2);
|
||||
}
|
||||
else if (button == 2)
|
||||
{
|
||||
//Win32Interop.SendRightMouseDown((int)mousePoint.Item1, (int)mousePoint.Item2);
|
||||
keyboardMouse.SendRightMouseDown(xyPercents.Item1, xyPercents.Item2);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
hubConnection.On("MouseUp", (int button, double percentX, double percentY, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
var mousePoint = ScreenCaster.GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
|
||||
var xyPercents = ScreenCaster.GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
|
||||
if (button == 0)
|
||||
{
|
||||
//Win32Interop.SendLeftMouseUp((int)mousePoint.Item1, (int)mousePoint.Item2);
|
||||
keyboardMouse.SendLeftMouseUp(xyPercents.Item1, xyPercents.Item2);
|
||||
}
|
||||
else if (button == 2)
|
||||
{
|
||||
//Win32Interop.SendRightMouseUp((int)mousePoint.Item1, (int)mousePoint.Item2);
|
||||
keyboardMouse.SendRightMouseUp(xyPercents.Item1, xyPercents.Item2);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
hubConnection.On("MouseWheel", (double deltaX, double deltaY, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
//Win32Interop.SendMouseWheel(-(int)deltaY);
|
||||
keyboardMouse.SendMouseWheel(-(int)deltaY);
|
||||
}
|
||||
});
|
||||
|
||||
hubConnection.On("ViewerDisconnected", async (string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer))
|
||||
if (conductor.Viewers.TryGetValue(viewerID, out var viewer))
|
||||
{
|
||||
viewer.DisconnectRequested = true;
|
||||
}
|
||||
await hubConnection.InvokeAsync("ViewerDisconnected", viewerID);
|
||||
Program.ViewerRemoved?.Invoke(null, viewerID);
|
||||
conductor.ViewerRemoved?.Invoke(null, viewerID);
|
||||
|
||||
});
|
||||
hubConnection.On("LatencyUpdate", (double latency, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer))
|
||||
if (conductor.Viewers.TryGetValue(viewerID, out var viewer))
|
||||
{
|
||||
viewer.PendingFrames--;
|
||||
viewer.Latency = latency;
|
||||
@ -141,7 +139,7 @@ namespace Remotely_ScreenCast_Linux.Sockets
|
||||
|
||||
hubConnection.On("SelectScreen", (int screenIndex, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer))
|
||||
if (conductor.Viewers.TryGetValue(viewerID, out var viewer))
|
||||
{
|
||||
viewer.Capturer.SelectedScreen = screenIndex;
|
||||
}
|
||||
@ -149,7 +147,7 @@ namespace Remotely_ScreenCast_Linux.Sockets
|
||||
|
||||
hubConnection.On("TouchDown", (string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
//User32.GetCursorPos(out var point);
|
||||
//Win32Interop.SendLeftMouseDown(point.X, point.Y);
|
||||
@ -157,7 +155,7 @@ namespace Remotely_ScreenCast_Linux.Sockets
|
||||
});
|
||||
hubConnection.On("LongPress", (string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
//User32.GetCursorPos(out var point);
|
||||
//Win32Interop.SendRightMouseDown(point.X, point.Y);
|
||||
@ -166,7 +164,7 @@ namespace Remotely_ScreenCast_Linux.Sockets
|
||||
});
|
||||
hubConnection.On("TouchMove", (double moveX, double moveY, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
//User32.GetCursorPos(out var point);
|
||||
//Win32Interop.SendMouseMove(point.X + moveX, point.Y + moveY);
|
||||
@ -174,7 +172,7 @@ namespace Remotely_ScreenCast_Linux.Sockets
|
||||
});
|
||||
hubConnection.On("TouchUp", (string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
//User32.GetCursorPos(out var point);
|
||||
//Win32Interop.SendLeftMouseUp(point.X, point.Y);
|
||||
@ -182,17 +180,17 @@ namespace Remotely_ScreenCast_Linux.Sockets
|
||||
});
|
||||
hubConnection.On("Tap", (double percentX, double percentY, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
var mousePoint = ScreenCaster.GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
|
||||
//Win32Interop.SendLeftMouseDown((int)mousePoint.Item1, (int)mousePoint.Item2);
|
||||
//Win32Interop.SendLeftMouseUp((int)mousePoint.Item1, (int)mousePoint.Item2);
|
||||
var xyPercents = ScreenCaster.GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
|
||||
keyboardMouse.SendLeftMouseDown(xyPercents.Item1, xyPercents.Item2);
|
||||
keyboardMouse.SendLeftMouseUp(xyPercents.Item1, xyPercents.Item2);
|
||||
}
|
||||
});
|
||||
hubConnection.On("SharedFileIDs", (List<string> fileIDs) => {
|
||||
fileIDs.ForEach(id =>
|
||||
{
|
||||
var url = $"{Program.Host}/API/FileSharing/{id}";
|
||||
var url = $"{conductor.Host}/API/FileSharing/{id}";
|
||||
var webRequest = WebRequest.CreateHttp(url);
|
||||
var response = webRequest.GetResponse();
|
||||
var contentDisp = response.Headers["Content-Disposition"];
|
||||
@ -220,7 +218,7 @@ namespace Remotely_ScreenCast_Linux.Sockets
|
||||
|
||||
hubConnection.On("SessionID", (string sessionID) =>
|
||||
{
|
||||
Program.SessionIDChanged?.Invoke(null, sessionID);
|
||||
conductor.SessionIDChanged?.Invoke(null, sessionID);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Remotely_ScreenCast.Models;
|
||||
using Remotely_ScreenCast.Core.Models;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
@ -7,7 +7,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast.Sockets
|
||||
namespace Remotely_ScreenCast.Core.Sockets
|
||||
{
|
||||
public class OutgoingMessages
|
||||
{
|
||||
@ -27,7 +27,7 @@ namespace Remotely_ScreenCast.Sockets
|
||||
await Connection.SendAsync("SendScreenCapture", captureBytes, viewerID, left, top, width, height, captureTime);
|
||||
}
|
||||
|
||||
internal async Task SendScreenCount(int primaryScreenIndex, int screenCount, string viewerID)
|
||||
public async Task SendScreenCount(int primaryScreenIndex, int screenCount, string viewerID)
|
||||
{
|
||||
await Connection.SendAsync("SendScreenCountToBrowser", primaryScreenIndex, screenCount, viewerID);
|
||||
}
|
||||
@ -42,22 +42,22 @@ namespace Remotely_ScreenCast.Sockets
|
||||
await Connection.SendAsync("SendCursorChange", cursor, viewerIDs);
|
||||
}
|
||||
|
||||
internal async Task NotifyViewersRelaunchedScreenCasterReady(string[] viewerIDs)
|
||||
public async Task NotifyViewersRelaunchedScreenCasterReady(string[] viewerIDs)
|
||||
{
|
||||
await Connection.SendAsync("NotifyViewersRelaunchedScreenCasterReady", viewerIDs);
|
||||
}
|
||||
|
||||
internal async Task SendDeviceInfo(string serviceID, string machineName)
|
||||
public async Task SendDeviceInfo(string serviceID, string machineName)
|
||||
{
|
||||
await Connection.SendAsync("ReceiveDeviceInfo", serviceID, machineName);
|
||||
}
|
||||
|
||||
internal async Task SendConnectionFailedToViewers(List<string> viewerIDs)
|
||||
public async Task SendConnectionFailedToViewers(List<string> viewerIDs)
|
||||
{
|
||||
await Connection.SendAsync("SendConnectionFailedToViewers", viewerIDs);
|
||||
}
|
||||
|
||||
internal async Task GetSessionID()
|
||||
public async Task GetSessionID()
|
||||
{
|
||||
await Connection.SendAsync("GetSessionID");
|
||||
}
|
||||
@ -6,7 +6,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast.Utilities
|
||||
namespace Remotely_ScreenCast.Core.Utilities
|
||||
{
|
||||
public static class Logger
|
||||
{
|
||||
38
Remotely_ScreenCast.Linux/Capture/X11Capture.cs
Normal file
38
Remotely_ScreenCast.Linux/Capture/X11Capture.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using Remotely_ScreenCast.Core.Capture;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
|
||||
namespace Remotely_ScreenCast.Linux.Capture
|
||||
{
|
||||
public class X11Capture : ICapturer
|
||||
{
|
||||
public bool CaptureFullscreen { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
public System.Drawing.Bitmap CurrentFrame { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
|
||||
public Rectangle CurrentScreenBounds => throw new NotImplementedException();
|
||||
|
||||
public System.Drawing.Bitmap PreviousFrame { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
public EventHandler<Rectangle> ScreenChanged { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
public int SelectedScreen { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
public int ScreenCount { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
public double VirtualScreenHeight { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
public double VirtualScreenWidth { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
|
||||
public void Capture()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
50
Remotely_ScreenCast.Linux/Input/X11Input.cs
Normal file
50
Remotely_ScreenCast.Linux/Input/X11Input.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using Remotely_ScreenCast.Core.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Remotely_ScreenCast.Linux.Input
|
||||
{
|
||||
public class X11Input : IKeyboardMouseInput
|
||||
{
|
||||
public void SendKeyDown(string key)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SendKeyUp(string key)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public uint SendLeftMouseDown(double percentX, double percentY)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public uint SendLeftMouseUp(double percentX, double percentY)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public uint SendMouseMove(double percentX, double percentY)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public uint SendMouseWheel(int deltaY)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public uint SendRightMouseDown(double percentX, double percentY)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public uint SendRightMouseUp(double percentX, double percentY)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
88
Remotely_ScreenCast.Linux/Program.cs
Normal file
88
Remotely_ScreenCast.Linux/Program.cs
Normal file
@ -0,0 +1,88 @@
|
||||
using Remotely_ScreenCast.Core;
|
||||
using Remotely_ScreenCast.Core.Capture;
|
||||
using Remotely_ScreenCast.Core.Utilities;
|
||||
using Remotely_ScreenCast.Linux.Capture;
|
||||
using Remotely_ScreenCast.Linux.Input;
|
||||
using Remotely_ScreenCast.Linux.X11Interop;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast.Linux
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static Conductor Conductor { get; private set; }
|
||||
//public static CursorIconWatcher CursorIconWatcher { get; private set; }
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
//var display = Xlib.XOpenDisplay(null);
|
||||
//Console.WriteLine($"Display is {display.ToString()}");
|
||||
//var count = Xlib.XScreenCount(display);
|
||||
//Console.WriteLine($"Count is {count}");
|
||||
//using (var bitmap = new System.Drawing.Bitmap(800, 600))
|
||||
//{
|
||||
// using (var graphic = System.Drawing.Graphics.FromImage(bitmap))
|
||||
// {
|
||||
// graphic.CopyFromScreen(0, 0, 0, 0, new System.Drawing.Size(800, 600));
|
||||
// }
|
||||
// bitmap.Save("Test.jpg");
|
||||
//}
|
||||
//var width = Xlib.XDisplayWidth(display, 0);
|
||||
//var height = Xlib.XDisplayHeight(display, 0);
|
||||
//Console.WriteLine($"Width: {width}, Height: {height}");
|
||||
//Console.ReadLine();
|
||||
Conductor = new Conductor();
|
||||
Conductor.ProcessArgs(args);
|
||||
Conductor.Connect().Wait();
|
||||
Conductor.SetMessageHandlers(new X11Input());
|
||||
Conductor.ScreenCastInitiated += ScreenCastInitiated;
|
||||
//CursorIconWatcher = new CursorIconWatcher(Conductor);
|
||||
//CursorIconWatcher.OnChange += CursorIconWatcher_OnChange;
|
||||
Conductor.OutgoingMessages.SendDeviceInfo(Conductor.ServiceID, Environment.MachineName).Wait();
|
||||
Conductor.StartWaitForViewerTimer();
|
||||
HandleConnection(Conductor).Wait();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ScreenCastInitiated(object sender, Tuple<string, string> viewerAndRequester)
|
||||
{
|
||||
ICapturer capturer;
|
||||
try
|
||||
{
|
||||
capturer = new X11Capture();
|
||||
//await Conductor.OutgoingMessages.SendCursorChange(CursorIconWatcher.GetCurrentCursor(), new List<string>() { viewerAndRequester.Item1 });
|
||||
ScreenCaster.BeginScreenCasting(viewerAndRequester.Item1, viewerAndRequester.Item2, Conductor.OutgoingMessages, capturer, Conductor);
|
||||
Conductor.OutgoingMessages.SendConnectionFailedToViewers(new List<string>() { viewerAndRequester.Item1 }).Wait();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
}
|
||||
}
|
||||
|
||||
//public static async void CursorIconWatcher_OnChange(object sender, CursorInfo cursor)
|
||||
//{
|
||||
// await Conductor.OutgoingMessages.SendCursorChange(cursor, Conductor.Viewers.Keys.ToList());
|
||||
//}
|
||||
|
||||
public static async Task HandleConnection(Conductor conductor)
|
||||
{
|
||||
await Task.Delay(100);
|
||||
|
||||
}
|
||||
|
||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
Logger.Write((Exception)e.ExceptionObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
<AssemblyName>Remotely_ScreenCast</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
@ -15,25 +14,12 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="1.1.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="4.5.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Models\CursorInfo.cs">
|
||||
<LastGenOutput>CursorInfo.d.ts</LastGenOutput>
|
||||
<Generator>DtsGenerator</Generator>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Models\CursorInfo.d.ts">
|
||||
<DependentUpon>CursorInfo.cs</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
</None>
|
||||
<ProjectReference Include="..\Remotely_ScreenCast.Core\Remotely_ScreenCast.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
154
Remotely_ScreenCast.Linux/X11Interop/Xlib.cs
Normal file
154
Remotely_ScreenCast.Linux/X11Interop/Xlib.cs
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
|
||||
Copyright 1985, 1986, 1987, 1991, 1998 The Open Group
|
||||
|
||||
Permission to use, copy, modify, distribute, and sell this software and its
|
||||
documentation for any purpose is hereby granted without fee, provided that
|
||||
the above copyright notice appear in all copies and that both that
|
||||
copyright notice and this permission notice appear in supporting
|
||||
documentation.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of The Open Group shall not be
|
||||
used in advertising or otherwise to promote the sale, use or other dealings
|
||||
in this Software without prior written authorization from The Open Group.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
using System.Text;
|
||||
|
||||
namespace Remotely_ScreenCast.Linux.X11Interop
|
||||
{
|
||||
public static unsafe class Xlib
|
||||
{
|
||||
|
||||
#region Structs
|
||||
public struct XImage
|
||||
{
|
||||
public int width;
|
||||
public int height; /* size of image */
|
||||
public int xoffset; /* number of pixels offset in X direction */
|
||||
public int format; /* XYBitmap, XYPixmap, ZPixmap */
|
||||
public char* data; /* pointer to image data */
|
||||
public int byte_order; /* data byte order, LSBFirst, MSBFirst */
|
||||
public int bitmap_unit; /* quant. of scanline 8, 16, 32 */
|
||||
public int bitmap_bit_order; /* LSBFirst, MSBFirst */
|
||||
public int bitmap_pad; /* 8, 16, 32 either XY or ZPixmap */
|
||||
public int depth; /* depth of image */
|
||||
public int bytes_per_line; /* accelerator to next scanline */
|
||||
public int bits_per_pixel; /* bits per pixel (ZPixmap) */
|
||||
public ulong red_mask; /* bits in z arrangement */
|
||||
public ulong green_mask;
|
||||
public ulong blue_mask;
|
||||
public IntPtr obdata; /* hook for the object routines to hang on */
|
||||
|
||||
public XImage create_image()
|
||||
{
|
||||
return new XImage();
|
||||
}
|
||||
|
||||
public int destroy_image()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public ulong get_pixel()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
public XImage sub_image()
|
||||
{
|
||||
return new XImage();
|
||||
}
|
||||
|
||||
public int add_pixel()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
public struct Screen {
|
||||
|
||||
public IntPtr ext_data; /* hook for extension to hang data */
|
||||
public IntPtr *display;/* back pointer to display structure */ // TODO: XDisplay struct?
|
||||
public IntPtr root; /* Root window id. */
|
||||
public int width;
|
||||
public int height; /* width and height of screen */
|
||||
public int mwidth;
|
||||
public int mheight; /* width and height of in millimeters */
|
||||
public int ndepths; /* number of depths possible */
|
||||
public uint depths; /* list of allowable depths on the screen */ //TODO: Depth[]
|
||||
public int root_depth; /* bits per pixel */
|
||||
public IntPtr root_visual; /* root visual */
|
||||
public IntPtr default_gc; /* GC for the root root visual */
|
||||
public IntPtr cmap; /* default color map */ // TODO: Colormap struct?
|
||||
public ulong white_pixel;
|
||||
public ulong black_pixel; /* White and Black pixel values */
|
||||
public int max_maps, min_maps; /* max and min color maps */
|
||||
public int backing_store; /* Never, WhenMapped, Always */
|
||||
public bool save_unders;
|
||||
public long root_input_mask; /* initial root input mask */
|
||||
}
|
||||
public struct Depth {
|
||||
|
||||
public int depth; /* this depth (Z) of the depth */
|
||||
public int nvisuals; /* number of Visual types at this depth */
|
||||
public Visual[] visuals; /* list of visuals possible at this depth */
|
||||
}
|
||||
public struct Visual {
|
||||
|
||||
public IntPtr ext_data; /* hook for extension to hang data */
|
||||
public int visualid; /* visual id of this visual */
|
||||
public ulong red_mask, green_mask, blue_mask; /* mask values */
|
||||
public int bits_per_rgb; /* log base 2 of distinct color values */
|
||||
public int map_entries; /* color map entries */
|
||||
}
|
||||
|
||||
public class XExtData
|
||||
{
|
||||
public int number; /* number returned by XRegisterExtension */
|
||||
public XExtData next; /* next item on list of data for structure */
|
||||
public IntPtr private_data; /* data private to this extension. */
|
||||
}
|
||||
#endregion Structs
|
||||
|
||||
#region Imports
|
||||
[DllImport("libX11")]
|
||||
public static extern XImage XGetImage(IntPtr display, IntPtr drawable, int x, int y, uint width, uint height, ulong plane_mask, int format);
|
||||
[DllImport("libX11")]
|
||||
public static extern int XScreenCount(IntPtr display);
|
||||
[DllImport("libX11")]
|
||||
public static extern int XDefaultScreen(IntPtr display);
|
||||
[DllImport("libX11")]
|
||||
public static extern IntPtr XOpenDisplay(string display_name);
|
||||
[DllImport("libX11")]
|
||||
public static extern IntPtr XRootWindow(IntPtr display, int screen_number);
|
||||
|
||||
[DllImport("libX11")]
|
||||
public static extern XImage* XGetSubImage(IntPtr display, IntPtr drawable, int x, int y, uint width, uint height, ulong plane_mask, int format, XImage dest_image, int dest_x, int dest_y);
|
||||
[DllImport("libX11")]
|
||||
public static extern IntPtr XScreenOfDisplay(IntPtr display, int screen_number);
|
||||
[DllImport("libX11")]
|
||||
public static extern int XDisplayWidth(IntPtr display, int screen_number);
|
||||
[DllImport("libX11")]
|
||||
public static extern int XDisplayHeight(IntPtr display, int screen_number);
|
||||
[DllImport("libX11")]
|
||||
public static extern int XWidthOfScreen(IntPtr screen);
|
||||
[DllImport("libX11")]
|
||||
public static extern int XHeightOfScreen(IntPtr screen);
|
||||
#endregion Imports
|
||||
}
|
||||
}
|
||||
@ -12,10 +12,11 @@ using System.Collections.Generic;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using Remotely_ScreenCast.Utilities;
|
||||
using Remotely_ScreenCast.Core.Utilities;
|
||||
using System.Threading;
|
||||
using Remotely_ScreenCast.Core.Capture;
|
||||
|
||||
namespace Remotely_ScreenCast.Capture
|
||||
namespace Remotely_ScreenCast.Win.Capture
|
||||
{
|
||||
public class BitBltCapture : ICapturer
|
||||
{
|
||||
@ -56,6 +57,10 @@ namespace Remotely_ScreenCast.Capture
|
||||
}
|
||||
}
|
||||
public Rectangle CurrentScreenBounds { get; set; } = Screen.PrimaryScreen.Bounds;
|
||||
public int ScreenCount { get; set; } = Screen.AllScreens.Length;
|
||||
public double VirtualScreenHeight { get; set; } = SystemInformation.VirtualScreen.Width;
|
||||
public double VirtualScreenWidth { get; set; } = SystemInformation.VirtualScreen.Height;
|
||||
|
||||
private int selectedScreen = Screen.AllScreens.ToList().IndexOf(Screen.PrimaryScreen);
|
||||
private Graphics graphic;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using Remotely_ScreenCast.Models;
|
||||
using Remotely_ScreenCast.Core;
|
||||
using Remotely_ScreenCast.Core.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
@ -12,17 +13,22 @@ using System.Timers;
|
||||
using System.Windows.Forms;
|
||||
using Win32;
|
||||
|
||||
namespace Remotely_ScreenCast.Capture
|
||||
namespace Remotely_ScreenCast.Win.Capture
|
||||
{
|
||||
/// <summary>
|
||||
/// A class that can be used to watch for cursor icon changes.
|
||||
/// </summary>
|
||||
public class CursorIconWatcher
|
||||
{
|
||||
public static CursorIconWatcher Current { get; } = new CursorIconWatcher();
|
||||
public CursorIconWatcher(Conductor conductor)
|
||||
{
|
||||
Conductor = conductor;
|
||||
}
|
||||
public event EventHandler<CursorInfo> OnChange;
|
||||
private System.Timers.Timer ChangeTimer { get; set; }
|
||||
private string PreviousCursorHandle { get; set; }
|
||||
public Conductor Conductor { get; }
|
||||
|
||||
private User32.CursorInfo cursorInfo;
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using Remotely_ScreenCast.Utilities;
|
||||
using Remotely_ScreenCast.Core.Capture;
|
||||
using Remotely_ScreenCast.Core.Utilities;
|
||||
using SharpDX;
|
||||
using SharpDX.Direct3D11;
|
||||
using SharpDX.DXGI;
|
||||
@ -8,9 +9,10 @@ using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using Win32;
|
||||
|
||||
namespace Remotely_ScreenCast.Capture
|
||||
namespace Remotely_ScreenCast.Win.Capture
|
||||
{
|
||||
public class DXCapture : ICapturer
|
||||
{
|
||||
@ -63,6 +65,10 @@ namespace Remotely_ScreenCast.Capture
|
||||
}
|
||||
}
|
||||
|
||||
public int ScreenCount { get; set; } = Screen.AllScreens.Length;
|
||||
public double VirtualScreenHeight { get; set; } = SystemInformation.VirtualScreen.Width;
|
||||
public double VirtualScreenWidth { get; set; } = SystemInformation.VirtualScreen.Height;
|
||||
|
||||
public void Capture()
|
||||
{
|
||||
try
|
||||
224
Remotely_ScreenCast.Win/Input/WinInput.cs
Normal file
224
Remotely_ScreenCast.Win/Input/WinInput.cs
Normal file
@ -0,0 +1,224 @@
|
||||
using Remotely_ScreenCast.Core.Input;
|
||||
using System;
|
||||
using Win32;
|
||||
using static Win32.User32;
|
||||
|
||||
namespace Remotely_ScreenCast.Win.Input
|
||||
{
|
||||
public class WinInput : IKeyboardMouseInput
|
||||
{
|
||||
public uint SendLeftMouseDown(double percentX, double percentY)
|
||||
{
|
||||
// Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.
|
||||
var normalizedX = percentX * 65535D;
|
||||
var normalizedY = percentY * 65535D;
|
||||
var union = new InputUnion() { mi = new MOUSEINPUT() { dwFlags = MOUSEEVENTF.ABSOLUTE | MOUSEEVENTF.LEFTDOWN | MOUSEEVENTF.VIRTUALDESK, dx = (int)normalizedX, dy = (int)normalizedY, time = 0, mouseData = 0, dwExtraInfo = GetMessageExtraInfo() } };
|
||||
var input = new INPUT() { type = InputType.MOUSE, U = union };
|
||||
return SendInput(1, new INPUT[] { input }, INPUT.Size);
|
||||
}
|
||||
public uint SendLeftMouseUp(double percentX, double percentY)
|
||||
{
|
||||
// Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.
|
||||
var normalizedX = percentX * 65535D;
|
||||
var normalizedY = percentY * 65535D;
|
||||
var union = new InputUnion() { mi = new MOUSEINPUT() { dwFlags = MOUSEEVENTF.ABSOLUTE | MOUSEEVENTF.LEFTUP | MOUSEEVENTF.VIRTUALDESK, dx = (int)normalizedX, dy = (int)normalizedY, time = 0, mouseData = 0, dwExtraInfo = GetMessageExtraInfo() } };
|
||||
var input = new INPUT() { type = InputType.MOUSE, U = union };
|
||||
return SendInput(1, new INPUT[] { input }, INPUT.Size);
|
||||
}
|
||||
public uint SendRightMouseDown(double percentX, double percentY)
|
||||
{
|
||||
// Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.
|
||||
var normalizedX = percentX * 65535D;
|
||||
var normalizedY = percentY * 65535D;
|
||||
var union = new InputUnion() { mi = new MOUSEINPUT() { dwFlags = MOUSEEVENTF.ABSOLUTE | MOUSEEVENTF.RIGHTDOWN | MOUSEEVENTF.VIRTUALDESK, dx = (int)normalizedX, dy = (int)normalizedY, time = 0, mouseData = 0, dwExtraInfo = GetMessageExtraInfo() } };
|
||||
var input = new INPUT() { type = InputType.MOUSE, U = union };
|
||||
return SendInput(1, new INPUT[] { input }, INPUT.Size);
|
||||
}
|
||||
public uint SendRightMouseUp(double percentX, double percentY)
|
||||
{
|
||||
// Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.
|
||||
var normalizedX = percentX * 65535D;
|
||||
var normalizedY = percentY * 65535D;
|
||||
var union = new InputUnion() { mi = new MOUSEINPUT() { dwFlags = MOUSEEVENTF.ABSOLUTE | MOUSEEVENTF.RIGHTUP | MOUSEEVENTF.VIRTUALDESK, dx = (int)normalizedX, dy = (int)normalizedY, time = 0, mouseData = 0, dwExtraInfo = GetMessageExtraInfo() } };
|
||||
var input = new INPUT() { type = InputType.MOUSE, U = union };
|
||||
return SendInput(1, new INPUT[] { input }, INPUT.Size);
|
||||
}
|
||||
public uint SendMouseMove(double percentX, double percentY)
|
||||
{
|
||||
// Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.
|
||||
var normalizedX = percentX * 65535D;
|
||||
var normalizedY = percentY * 65535D;
|
||||
var union = new InputUnion() { mi = new MOUSEINPUT() { dwFlags = MOUSEEVENTF.ABSOLUTE | MOUSEEVENTF.MOVE | MOUSEEVENTF.VIRTUALDESK, dx = (int)normalizedX, dy = (int)normalizedY, time = 0, mouseData = 0, dwExtraInfo = GetMessageExtraInfo() } };
|
||||
var input = new INPUT() { type = InputType.MOUSE, U = union };
|
||||
return SendInput(1, new INPUT[] { input }, INPUT.Size);
|
||||
}
|
||||
public uint SendMouseWheel(int deltaY)
|
||||
{
|
||||
if (deltaY < 0)
|
||||
{
|
||||
deltaY = -120;
|
||||
}
|
||||
else if (deltaY > 0)
|
||||
{
|
||||
deltaY = 120;
|
||||
}
|
||||
var union = new User32.InputUnion() { mi = new User32.MOUSEINPUT() { dwFlags = MOUSEEVENTF.WHEEL, dx = 0, dy = 0, time = 0, mouseData = deltaY, dwExtraInfo = GetMessageExtraInfo() } };
|
||||
var input = new User32.INPUT() { type = InputType.MOUSE, U = union };
|
||||
return SendInput(1, new User32.INPUT[] { input }, INPUT.Size);
|
||||
}
|
||||
public void SendKeyDown(string key)
|
||||
{
|
||||
var keyCode = ConvertJavaScriptKeyToVirtualKey(key);
|
||||
var union = new InputUnion()
|
||||
{
|
||||
ki = new KEYBDINPUT()
|
||||
{
|
||||
wVk = keyCode,
|
||||
wScan = 0,
|
||||
time = 0,
|
||||
dwExtraInfo = GetMessageExtraInfo()
|
||||
}
|
||||
};
|
||||
var input = new INPUT() { type = InputType.KEYBOARD, U = union };
|
||||
SendInput(1, new INPUT[] { input }, INPUT.Size);
|
||||
}
|
||||
public void SendKeyUp(string key)
|
||||
{
|
||||
var keyCode = ConvertJavaScriptKeyToVirtualKey(key);
|
||||
var union = new InputUnion()
|
||||
{
|
||||
ki = new KEYBDINPUT()
|
||||
{
|
||||
wVk = keyCode,
|
||||
wScan = 0,
|
||||
time = 0,
|
||||
dwFlags = KEYEVENTF.KEYUP,
|
||||
dwExtraInfo = GetMessageExtraInfo()
|
||||
}
|
||||
};
|
||||
var input = new INPUT() { type = InputType.KEYBOARD, U = union };
|
||||
SendInput(1, new INPUT[] { input }, INPUT.Size);
|
||||
}
|
||||
|
||||
private VirtualKey ConvertJavaScriptKeyToVirtualKey(string key)
|
||||
{
|
||||
VirtualKey keyCode;
|
||||
switch (key)
|
||||
{
|
||||
case "Down":
|
||||
case "ArrowDown":
|
||||
keyCode = VirtualKey.DOWN;
|
||||
break;
|
||||
case "Up":
|
||||
case "ArrowUp":
|
||||
keyCode = VirtualKey.UP;
|
||||
break;
|
||||
case "Left":
|
||||
case "ArrowLeft":
|
||||
keyCode = VirtualKey.LEFT;
|
||||
break;
|
||||
case "Right":
|
||||
case "ArrowRight":
|
||||
keyCode = VirtualKey.RIGHT;
|
||||
break;
|
||||
case "Enter":
|
||||
keyCode = VirtualKey.RETURN;
|
||||
break;
|
||||
case "Esc":
|
||||
case "Escape":
|
||||
keyCode = VirtualKey.ESCAPE;
|
||||
break;
|
||||
case "Alt":
|
||||
keyCode = VirtualKey.MENU;
|
||||
break;
|
||||
case "Control":
|
||||
keyCode = VirtualKey.CONTROL;
|
||||
break;
|
||||
case "Shift":
|
||||
keyCode = VirtualKey.SHIFT;
|
||||
break;
|
||||
case "PAUSE":
|
||||
keyCode = VirtualKey.PAUSE;
|
||||
break;
|
||||
case "BREAK":
|
||||
keyCode = VirtualKey.PAUSE;
|
||||
break;
|
||||
case "Backspace":
|
||||
keyCode = VirtualKey.BACK;
|
||||
break;
|
||||
case "Tab":
|
||||
keyCode = VirtualKey.TAB;
|
||||
break;
|
||||
case "CapsLock":
|
||||
keyCode = VirtualKey.CAPITAL;
|
||||
break;
|
||||
case "Delete":
|
||||
keyCode = VirtualKey.DELETE;
|
||||
break;
|
||||
case "Home":
|
||||
keyCode = VirtualKey.HOME;
|
||||
break;
|
||||
case "End":
|
||||
keyCode = VirtualKey.END;
|
||||
break;
|
||||
case "PageUp":
|
||||
keyCode = VirtualKey.PRIOR;
|
||||
break;
|
||||
case "PageDown":
|
||||
keyCode = VirtualKey.NEXT;
|
||||
break;
|
||||
case "NumLock":
|
||||
keyCode = VirtualKey.NUMLOCK;
|
||||
break;
|
||||
case "Insert":
|
||||
keyCode = VirtualKey.INSERT;
|
||||
break;
|
||||
case "ScrollLock":
|
||||
keyCode = VirtualKey.SCROLL;
|
||||
break;
|
||||
case "F1":
|
||||
keyCode = VirtualKey.F1;
|
||||
break;
|
||||
case "F2":
|
||||
keyCode = VirtualKey.F2;
|
||||
break;
|
||||
case "F3":
|
||||
keyCode = VirtualKey.F3;
|
||||
break;
|
||||
case "F4":
|
||||
keyCode = VirtualKey.F4;
|
||||
break;
|
||||
case "F5":
|
||||
keyCode = VirtualKey.F5;
|
||||
break;
|
||||
case "F6":
|
||||
keyCode = VirtualKey.F6;
|
||||
break;
|
||||
case "F7":
|
||||
keyCode = VirtualKey.F7;
|
||||
break;
|
||||
case "F8":
|
||||
keyCode = VirtualKey.F8;
|
||||
break;
|
||||
case "F9":
|
||||
keyCode = VirtualKey.F9;
|
||||
break;
|
||||
case "F10":
|
||||
keyCode = VirtualKey.F10;
|
||||
break;
|
||||
case "F11":
|
||||
keyCode = VirtualKey.F11;
|
||||
break;
|
||||
case "F12":
|
||||
keyCode = VirtualKey.F12;
|
||||
break;
|
||||
default:
|
||||
keyCode = (VirtualKey)VkKeyScan(Convert.ToChar(key));
|
||||
break;
|
||||
}
|
||||
return keyCode;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
150
Remotely_ScreenCast.Win/Program.cs
Normal file
150
Remotely_ScreenCast.Win/Program.cs
Normal file
@ -0,0 +1,150 @@
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Remotely_ScreenCast.Core;
|
||||
using Remotely_ScreenCast.Core.Capture;
|
||||
using Remotely_ScreenCast.Core.Enums;
|
||||
using Remotely_ScreenCast.Core.Models;
|
||||
using Remotely_ScreenCast.Core.Sockets;
|
||||
using Remotely_ScreenCast.Core.Utilities;
|
||||
using Remotely_ScreenCast.Win.Capture;
|
||||
using Remotely_ScreenCast.Win.Input;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Win32;
|
||||
|
||||
namespace Remotely_ScreenCast.Win
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static Conductor Conductor { get; private set; }
|
||||
public static CursorIconWatcher CursorIconWatcher { get; private set; }
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
Conductor = new Conductor();
|
||||
Conductor.ProcessArgs(args);
|
||||
Conductor.Connect().Wait();
|
||||
Conductor.SetMessageHandlers(new WinInput());
|
||||
Conductor.ScreenCastInitiated += ScreenCastInitiated;
|
||||
CursorIconWatcher = new CursorIconWatcher(Conductor);
|
||||
CursorIconWatcher.OnChange += CursorIconWatcher_OnChange;
|
||||
Conductor.OutgoingMessages.SendDeviceInfo(Conductor.ServiceID, Environment.MachineName).Wait();
|
||||
CheckInitialDesktop();
|
||||
CheckForRelaunch();
|
||||
Conductor.StartWaitForViewerTimer();
|
||||
HandleConnection(Conductor).Wait();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckForRelaunch()
|
||||
{
|
||||
|
||||
if (Conductor.ArgDict.ContainsKey("relaunch"))
|
||||
{
|
||||
Logger.Write($"Resuming after relaunch in desktop {Conductor.CurrentDesktopName}.");
|
||||
var viewersString = Conductor.ArgDict["viewers"];
|
||||
var viewerIDs = viewersString.Split(",".ToCharArray());
|
||||
Conductor.OutgoingMessages.NotifyViewersRelaunchedScreenCasterReady(viewerIDs).Wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
Conductor.OutgoingMessages.NotifyRequesterUnattendedReady(Conductor.RequesterID).Wait();
|
||||
}
|
||||
}
|
||||
|
||||
private static async void ScreenCastInitiated(object sender, Tuple<string, string> viewerAndRequester)
|
||||
{
|
||||
ICapturer capturer;
|
||||
try
|
||||
{
|
||||
if (Conductor.Viewers.Count == 0)
|
||||
{
|
||||
capturer = new DXCapture();
|
||||
capturer.Init();
|
||||
}
|
||||
else
|
||||
{
|
||||
capturer = new BitBltCapture();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
capturer = new BitBltCapture();
|
||||
}
|
||||
await Conductor.OutgoingMessages.SendCursorChange(CursorIconWatcher.GetCurrentCursor(), new List<string>() { viewerAndRequester.Item1 });
|
||||
ScreenCaster.BeginScreenCasting(viewerAndRequester.Item1, viewerAndRequester.Item2, Conductor.OutgoingMessages, capturer, Conductor);
|
||||
}
|
||||
|
||||
public static async void CursorIconWatcher_OnChange(object sender, CursorInfo cursor)
|
||||
{
|
||||
await Conductor.OutgoingMessages.SendCursorChange(cursor, Conductor.Viewers.Keys.ToList());
|
||||
}
|
||||
|
||||
public static async Task HandleConnection(Conductor conductor)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var desktopName = Win32Interop.GetCurrentDesktop();
|
||||
if (desktopName.ToLower() != conductor.CurrentDesktopName.ToLower() && conductor.Viewers.Count > 0)
|
||||
{
|
||||
conductor.CurrentDesktopName = desktopName;
|
||||
Logger.Write($"Switching desktops to {desktopName}.");
|
||||
// TODO: SetThradDesktop causes issues with input after switching.
|
||||
//var inputDesktop = Win32Interop.OpenInputDesktop();
|
||||
//User32.SetThreadDesktop(inputDesktop);
|
||||
//User32.CloseDesktop(inputDesktop);
|
||||
conductor.Connection.InvokeAsync("SwitchingDesktops", conductor.Viewers.Keys.ToList()).Wait();
|
||||
var result = Win32Interop.OpenInteractiveProcess(Assembly.GetExecutingAssembly().Location + $" -mode {conductor.Mode.ToString()} -requester {conductor.RequesterID} -serviceid {conductor.ServiceID} -host {conductor.Host} -relaunch true -desktop {desktopName} -viewers {String.Join(",", conductor.Viewers.Keys.ToList())}", desktopName, true, out _);
|
||||
if (!result)
|
||||
{
|
||||
Logger.Write($"Desktop switch to {desktopName} failed.");
|
||||
conductor.OutgoingMessages.SendConnectionFailedToViewers(conductor.Viewers.Keys.ToList()).Wait();
|
||||
}
|
||||
}
|
||||
await Task.Delay(100);
|
||||
}
|
||||
}
|
||||
private static void CheckInitialDesktop()
|
||||
{
|
||||
var desktopName = Win32Interop.GetCurrentDesktop();
|
||||
if (desktopName.ToLower() != Conductor.CurrentDesktopName.ToLower())
|
||||
{
|
||||
Conductor.CurrentDesktopName = desktopName;
|
||||
Logger.Write($"Setting initial desktop to {desktopName}.");
|
||||
Conductor.ArgDict["desktop"] = desktopName;
|
||||
var openProcessString = Assembly.GetExecutingAssembly().Location;
|
||||
foreach (var arg in Conductor.ArgDict)
|
||||
{
|
||||
openProcessString += $" -{arg.Key} {arg.Value}";
|
||||
}
|
||||
var result = Win32Interop.OpenInteractiveProcess(openProcessString, desktopName, true, out _);
|
||||
if (!result)
|
||||
{
|
||||
Logger.Write($"Desktop relaunch to {desktopName} failed.");
|
||||
}
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
Logger.Write((Exception)e.ExceptionObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
200
Remotely_ScreenCast.Win/Remotely_ScreenCast.Win.csproj
Normal file
200
Remotely_ScreenCast.Win/Remotely_ScreenCast.Win.csproj
Normal file
@ -0,0 +1,200 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\Costura.Fody.3.3.3\build\Costura.Fody.props" Condition="Exists('..\packages\Costura.Fody.3.3.3\build\Costura.Fody.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{2DCEA1F5-9B64-4EDB-9CD0-4D6675D96709}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>Remotely_ScreenCast.Win</RootNamespace>
|
||||
<AssemblyName>Remotely_ScreenCast</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Costura, Version=3.3.3.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Costura.Fody.3.3.3\lib\net40\Costura.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MessagePack, Version=1.7.3.4, Culture=neutral, PublicKeyToken=b4a0369545f0a1be, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MessagePack.1.7.3.4\lib\net47\MessagePack.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNetCore.Connections.Abstractions, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNetCore.Connections.Abstractions.2.2.0\lib\netstandard2.0\Microsoft.AspNetCore.Connections.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNetCore.Http.Connections.Client, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNetCore.Http.Connections.Client.1.1.0\lib\netstandard2.0\Microsoft.AspNetCore.Http.Connections.Client.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNetCore.Http.Connections.Common, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNetCore.Http.Connections.Common.1.1.0\lib\netstandard2.0\Microsoft.AspNetCore.Http.Connections.Common.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNetCore.Http.Features, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNetCore.Http.Features.2.2.0\lib\netstandard2.0\Microsoft.AspNetCore.Http.Features.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNetCore.SignalR.Client, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNetCore.SignalR.Client.1.1.0\lib\netstandard2.0\Microsoft.AspNetCore.SignalR.Client.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNetCore.SignalR.Client.Core, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNetCore.SignalR.Client.Core.1.1.0\lib\netstandard2.0\Microsoft.AspNetCore.SignalR.Client.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNetCore.SignalR.Common, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNetCore.SignalR.Common.1.1.0\lib\netstandard2.0\Microsoft.AspNetCore.SignalR.Common.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNetCore.SignalR.Protocols.Json, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNetCore.SignalR.Protocols.Json.1.1.0\lib\netstandard2.0\Microsoft.AspNetCore.SignalR.Protocols.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNetCore.SignalR.Protocols.MessagePack.1.1.0\lib\netstandard2.0\Microsoft.AspNetCore.SignalR.Protocols.MessagePack.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Configuration, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Configuration.2.2.0\lib\netstandard2.0\Microsoft.Extensions.Configuration.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Configuration.Abstractions, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Configuration.Abstractions.2.2.0\lib\netstandard2.0\Microsoft.Extensions.Configuration.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Configuration.Binder, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Configuration.Binder.2.2.0\lib\netstandard2.0\Microsoft.Extensions.Configuration.Binder.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.DependencyInjection, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.2.2.0\lib\net461\Microsoft.Extensions.DependencyInjection.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.2.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Logging, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Logging.2.2.0\lib\netstandard2.0\Microsoft.Extensions.Logging.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.2.2.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Options, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Options.2.2.0\lib\netstandard2.0\Microsoft.Extensions.Options.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Primitives, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Primitives.2.2.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SharpDX, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\SharpDX.4.2.0\lib\net45\SharpDX.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SharpDX.Direct3D11, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\SharpDX.Direct3D11.4.2.0\lib\net45\SharpDX.Direct3D11.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SharpDX.DXGI, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\SharpDX.DXGI.4.2.0\lib\net45\SharpDX.DXGI.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.5.0\lib\netstandard2.0\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ComponentModel.Annotations, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.ComponentModel.Annotations.4.5.0\lib\net461\System.ComponentModel.Annotations.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Drawing.Common, Version=4.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Drawing.Common.4.5.1\lib\net461\System.Drawing.Common.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.IO.Pipelines, Version=4.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.IO.Pipelines.4.5.3\lib\netstandard2.0\System.IO.Pipelines.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Memory, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.5.2\lib\netstandard2.0\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Threading.Channels, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Channels.4.5.0\lib\netstandard2.0\System.Threading.Channels.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Capture\BitBltCapture.cs" />
|
||||
<Compile Include="Capture\CursorIconWatcher.cs" />
|
||||
<Compile Include="Capture\DXCapture.cs" />
|
||||
<Compile Include="Input\WinInput.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Win32\ADVAPI32.cs" />
|
||||
<Compile Include="Win32\GDI32.cs" />
|
||||
<Compile Include="Win32\Kernel32.cs" />
|
||||
<Compile Include="Win32\SECUR32.cs" />
|
||||
<Compile Include="Win32\User32.cs" />
|
||||
<Compile Include="Win32\Win32Interop.cs" />
|
||||
<Compile Include="Win32\WTSAPI32.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="FodyWeavers.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Remotely_ScreenCast.Core\Remotely_ScreenCast.Core.csproj">
|
||||
<Project>{b04a1728-2e87-491e-bc7f-f575a1754def}</Project>
|
||||
<Name>Remotely_ScreenCast.Core</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>if $(ConfigurationName) == Debug (
|
||||
md "$(SolutionDir)Remotely_Agent\bin\Debug\netcoreapp2.2\ScreenCast\"
|
||||
copy /y "$(TargetPath)" "$(SolutionDir)Remotely_Agent\bin\Debug\netcoreapp2.2\ScreenCast\"
|
||||
)</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\packages\Fody.4.2.1\build\Fody.targets" Condition="Exists('..\packages\Fody.4.2.1\build\Fody.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Fody.4.2.1\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.4.2.1\build\Fody.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Costura.Fody.3.3.3\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.3.3.3\build\Costura.Fody.props'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
140
Remotely_ScreenCast.Win/Win32/Win32Interop.cs
Normal file
140
Remotely_ScreenCast.Win/Win32/Win32Interop.cs
Normal file
@ -0,0 +1,140 @@
|
||||
using Win32;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using static Win32.ADVAPI32;
|
||||
using static Win32.User32;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Win32
|
||||
{
|
||||
public class Win32Interop
|
||||
{
|
||||
public static bool OpenInteractiveProcess(string applicationName, string desktopName, bool hiddenWindow, out PROCESS_INFORMATION procInfo)
|
||||
{
|
||||
uint winlogonPid = 0;
|
||||
IntPtr hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero;
|
||||
procInfo = new PROCESS_INFORMATION();
|
||||
|
||||
// Obtain session ID for active session.
|
||||
uint dwSessionId = Kernel32.WTSGetActiveConsoleSessionId();
|
||||
|
||||
// Check for RDP session. If active, use that session ID instead.
|
||||
var rdpSessionID = GetRDPSession();
|
||||
if (rdpSessionID > 0)
|
||||
{
|
||||
dwSessionId = rdpSessionID;
|
||||
}
|
||||
|
||||
// Obtain the process ID of the winlogon process that is running within the currently active session.
|
||||
Process[] processes = Process.GetProcessesByName("winlogon");
|
||||
foreach (Process p in processes)
|
||||
{
|
||||
if ((uint)p.SessionId == dwSessionId)
|
||||
{
|
||||
winlogonPid = (uint)p.Id;
|
||||
}
|
||||
}
|
||||
|
||||
// Obtain a handle to the winlogon process.
|
||||
hProcess = Kernel32.OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);
|
||||
|
||||
// Obtain a handle to the access token of the winlogon process.
|
||||
if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken))
|
||||
{
|
||||
Kernel32.CloseHandle(hProcess);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser.
|
||||
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
|
||||
sa.Length = Marshal.SizeOf(sa);
|
||||
|
||||
// Copy the access token of the winlogon process; the newly created token will be a primary token.
|
||||
if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, TOKEN_TYPE.TokenPrimary, out hUserTokenDup))
|
||||
{
|
||||
Kernel32.CloseHandle(hProcess);
|
||||
Kernel32.CloseHandle(hPToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
// By default, CreateProcessAsUser creates a process on a non-interactive window station, meaning
|
||||
// the window station has a desktop that is invisible and the process is incapable of receiving
|
||||
// user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user
|
||||
// interaction with the new process.
|
||||
STARTUPINFO si = new STARTUPINFO();
|
||||
si.cb = Marshal.SizeOf(si);
|
||||
si.lpDesktop = @"winsta0\" + desktopName;
|
||||
|
||||
// Flags that specify the priority and creation method of the process.
|
||||
uint dwCreationFlags;
|
||||
if (hiddenWindow)
|
||||
{
|
||||
dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | DETACHED_PROCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
|
||||
}
|
||||
|
||||
// Create a new process in the current user's logon session.
|
||||
bool result = CreateProcessAsUser(hUserTokenDup, null, applicationName, ref sa, ref sa, false, dwCreationFlags, IntPtr.Zero, null, ref si, out procInfo);
|
||||
|
||||
// Invalidate the handles.
|
||||
Kernel32.CloseHandle(hProcess);
|
||||
Kernel32.CloseHandle(hPToken);
|
||||
Kernel32.CloseHandle(hUserTokenDup);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static uint GetRDPSession()
|
||||
{
|
||||
IntPtr ppSessionInfo = IntPtr.Zero;
|
||||
Int32 count = 0;
|
||||
Int32 retval = WTSAPI32.WTSEnumerateSessions(WTSAPI32.WTS_CURRENT_SERVER_HANDLE, 0, 1, ref ppSessionInfo, ref count);
|
||||
Int32 dataSize = Marshal.SizeOf(typeof(WTSAPI32.WTS_SESSION_INFO));
|
||||
var sessList = new List<WTSAPI32.WTS_SESSION_INFO>();
|
||||
Int64 current = (Int64)ppSessionInfo;
|
||||
|
||||
if (retval != 0)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
WTSAPI32.WTS_SESSION_INFO sessInf = (WTSAPI32.WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTSAPI32.WTS_SESSION_INFO));
|
||||
current += dataSize;
|
||||
sessList.Add(sessInf);
|
||||
}
|
||||
}
|
||||
uint retVal = 0;
|
||||
var rdpSession = sessList.Find(ses => ses.pWinStationName.ToLower().Contains("rdp") && ses.State == 0);
|
||||
if (sessList.Exists(ses => ses.pWinStationName.ToLower().Contains("rdp") && ses.State == 0))
|
||||
{
|
||||
retVal = (uint)rdpSession.SessionID;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
public static IntPtr OpenInputDesktop()
|
||||
{
|
||||
return User32.OpenInputDesktop(0, false, ACCESS_MASK.GENERIC_ALL);
|
||||
}
|
||||
public static string GetCurrentDesktop()
|
||||
{
|
||||
var inputDesktop = OpenInputDesktop();
|
||||
byte[] deskBytes = new byte[256];
|
||||
uint lenNeeded;
|
||||
var success = GetUserObjectInformationW(inputDesktop, UOI_NAME, deskBytes, 256, out lenNeeded);
|
||||
if (!success)
|
||||
{
|
||||
CloseDesktop(inputDesktop);
|
||||
return "Default";
|
||||
}
|
||||
var desktopName = Encoding.Unicode.GetString(deskBytes.Take((int)lenNeeded).ToArray()).Replace("\0", "");
|
||||
CloseDesktop(inputDesktop);
|
||||
return desktopName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Costura.Fody" version="3.3.2" targetFramework="net472" />
|
||||
<package id="Fody" version="4.0.2" targetFramework="net472" developmentDependency="true" />
|
||||
<package id="Costura.Fody" version="3.3.3" targetFramework="net472" />
|
||||
<package id="Fody" version="4.2.1" targetFramework="net472" developmentDependency="true" />
|
||||
<package id="MessagePack" version="1.7.3.4" targetFramework="net472" />
|
||||
<package id="Microsoft.AspNetCore.Connections.Abstractions" version="2.2.0" targetFramework="net472" />
|
||||
<package id="Microsoft.AspNetCore.Http.Connections.Client" version="1.1.0" targetFramework="net472" />
|
||||
@ -27,6 +27,7 @@
|
||||
<package id="SharpDX.DXGI" version="4.2.0" targetFramework="net472" />
|
||||
<package id="System.Buffers" version="4.5.0" targetFramework="net472" />
|
||||
<package id="System.ComponentModel.Annotations" version="4.5.0" targetFramework="net472" />
|
||||
<package id="System.Drawing.Common" version="4.5.1" targetFramework="net472" />
|
||||
<package id="System.IO.Pipelines" version="4.5.3" targetFramework="net472" />
|
||||
<package id="System.Memory" version="4.5.2" targetFramework="net472" />
|
||||
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net472" />
|
||||
@ -1,14 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast.Capture
|
||||
{
|
||||
public enum CaptureMode
|
||||
{
|
||||
BitBtl,
|
||||
DirectX
|
||||
}
|
||||
}
|
||||
@ -1,206 +0,0 @@
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Remotely_ScreenCast;
|
||||
using Remotely_ScreenCast.Capture;
|
||||
using Remotely_ScreenCast.Enums;
|
||||
using Remotely_ScreenCast.Models;
|
||||
using Remotely_ScreenCast.Sockets;
|
||||
using Remotely_ScreenCast.Utilities;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Win32;
|
||||
|
||||
namespace Remotely_ScreenCast
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static AppMode Mode { get; private set; }
|
||||
public static string RequesterID { get; private set; }
|
||||
public static string ServiceID { get; private set; }
|
||||
public static string Host { get; private set; }
|
||||
public static HubConnection Connection { get; private set; }
|
||||
public static OutgoingMessages OutgoingMessages { get; private set; }
|
||||
public static string CurrentDesktopName { get; set; }
|
||||
public static ConcurrentDictionary<string, Viewer> Viewers { get; } = new ConcurrentDictionary<string, Viewer>();
|
||||
public static Dictionary<string,string> ArgDict { get; set; }
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
ProcessArgs(args);
|
||||
Connect().Wait();
|
||||
SetEventHandlers();
|
||||
HandleConnection().Wait();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task HandleConnection()
|
||||
{
|
||||
OutgoingMessages.SendDeviceInfo(ServiceID, Environment.MachineName).Wait();
|
||||
|
||||
|
||||
if (Mode == AppMode.Unattended)
|
||||
{
|
||||
var desktopName = Win32Interop.GetCurrentDesktop();
|
||||
if (desktopName.ToLower() != CurrentDesktopName.ToLower())
|
||||
{
|
||||
CurrentDesktopName = desktopName;
|
||||
Logger.Write($"Setting initial desktop to {desktopName}.");
|
||||
ArgDict["desktop"] = desktopName;
|
||||
var openProcessString = Assembly.GetExecutingAssembly().Location;
|
||||
foreach (var arg in ArgDict)
|
||||
{
|
||||
openProcessString += $" -{arg.Key} {arg.Value}";
|
||||
}
|
||||
var result = Win32Interop.OpenInteractiveProcess(openProcessString, desktopName, true, out _);
|
||||
if (!result)
|
||||
{
|
||||
Logger.Write($"Desktop relaunch to {desktopName} failed.");
|
||||
}
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
if (ArgDict.ContainsKey("relaunch"))
|
||||
{
|
||||
Logger.Write($"Resuming after relaunch in desktop {CurrentDesktopName}.");
|
||||
var viewersString = ArgDict["viewers"];
|
||||
var viewerIDs = viewersString.Split(",".ToCharArray());
|
||||
OutgoingMessages.NotifyViewersRelaunchedScreenCasterReady(viewerIDs).Wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
OutgoingMessages.NotifyRequesterUnattendedReady(RequesterID).Wait();
|
||||
}
|
||||
|
||||
StartWaitForViewerTimer();
|
||||
}
|
||||
else if (Mode == AppMode.Normal)
|
||||
{
|
||||
OutgoingMessages.GetSessionID().Wait();
|
||||
}
|
||||
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (Mode == AppMode.Unattended)
|
||||
{
|
||||
var desktopName = Win32Interop.GetCurrentDesktop();
|
||||
if (desktopName.ToLower() != CurrentDesktopName.ToLower() && Viewers.Count > 0)
|
||||
{
|
||||
CurrentDesktopName = desktopName;
|
||||
Logger.Write($"Switching desktops to {desktopName}.");
|
||||
// TODO: SetThradDesktop causes issues with input after switching.
|
||||
//var inputDesktop = Win32Interop.OpenInputDesktop();
|
||||
//User32.SetThreadDesktop(inputDesktop);
|
||||
//User32.CloseDesktop(inputDesktop);
|
||||
Connection.InvokeAsync("SwitchingDesktops", Viewers.Keys.ToList()).Wait();
|
||||
var result = Win32Interop.OpenInteractiveProcess(Assembly.GetExecutingAssembly().Location + $" -mode {Mode.ToString()} -requester {RequesterID} -serviceid {ServiceID} -host {Host} -relaunch true -desktop {desktopName} -viewers {String.Join(",", Viewers.Keys.ToList())}", desktopName, true, out _);
|
||||
if (!result)
|
||||
{
|
||||
Logger.Write($"Desktop switch to {desktopName} failed.");
|
||||
OutgoingMessages.SendConnectionFailedToViewers(Viewers.Keys.ToList()).Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
await Task.Delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetEventHandlers()
|
||||
{
|
||||
OutgoingMessages = new OutgoingMessages(Connection);
|
||||
|
||||
MessageHandlers.ApplyConnectionHandlers(Connection, OutgoingMessages);
|
||||
|
||||
CursorIconWatcher.Current.OnChange += CursorIconWatcher_OnChange;
|
||||
}
|
||||
|
||||
public static Task Connect()
|
||||
{
|
||||
Connection = new HubConnectionBuilder()
|
||||
.WithUrl($"{Host}/RCDeviceHub")
|
||||
.AddMessagePackProtocol()
|
||||
.Build();
|
||||
|
||||
return Connection.StartAsync();
|
||||
}
|
||||
|
||||
|
||||
private static async void CursorIconWatcher_OnChange(object sender, CursorInfo cursor)
|
||||
{
|
||||
await OutgoingMessages.SendCursorChange(cursor, Viewers.Keys.ToList());
|
||||
}
|
||||
|
||||
private static void StartWaitForViewerTimer()
|
||||
{
|
||||
var timer = new System.Timers.Timer(10000);
|
||||
timer.AutoReset = false;
|
||||
timer.Elapsed += (sender, arg) =>
|
||||
{
|
||||
// Shut down if no viewers have connected within 10 seconds.
|
||||
if (Viewers.Count == 0)
|
||||
{
|
||||
Logger.Write("No viewers connected after 10 seconds. Shutting down.");
|
||||
Environment.Exit(0);
|
||||
}
|
||||
};
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
Logger.Write((Exception)e.ExceptionObject);
|
||||
}
|
||||
|
||||
public static void ProcessArgs(string[] args)
|
||||
{
|
||||
ArgDict = new Dictionary<string, string>();
|
||||
|
||||
for (var i = 0; i < args.Length; i += 2)
|
||||
{
|
||||
var key = args?[i];
|
||||
if (key != null)
|
||||
{
|
||||
key = key.Trim().Replace("-", "").ToLower();
|
||||
var value = args?[i + 1];
|
||||
if (value != null)
|
||||
{
|
||||
ArgDict[key] = args[i + 1].Trim();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Mode = (AppMode)Enum.Parse(typeof(AppMode), Program.ArgDict["mode"]);
|
||||
Host = Program.ArgDict["host"];
|
||||
|
||||
if (Mode == AppMode.Unattended)
|
||||
{
|
||||
RequesterID = Program.ArgDict["requester"];
|
||||
CurrentDesktopName = Program.ArgDict["desktop"];
|
||||
ServiceID = Program.ArgDict["serviceid"];
|
||||
}
|
||||
|
||||
}
|
||||
public static EventHandler<string> SessionIDChanged { get; set; }
|
||||
public static EventHandler<string> ViewerRemoved { get; set; }
|
||||
public static EventHandler<Viewer> ViewerAdded { get; set; }
|
||||
public static EventHandler<Tuple<string, string>> ScreenCastRequested { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,229 +0,0 @@
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Remotely_ScreenCast.Capture;
|
||||
using Remotely_ScreenCast.Utilities;
|
||||
using Remotely_ScreenCast;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Win32;
|
||||
using System.Net;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using Remotely_ScreenCast.Models;
|
||||
|
||||
namespace Remotely_ScreenCast.Sockets
|
||||
{
|
||||
public class MessageHandlers
|
||||
{
|
||||
public static void ApplyConnectionHandlers(HubConnection hubConnection, OutgoingMessages outgoingMessages)
|
||||
{
|
||||
hubConnection.Closed += (ex) =>
|
||||
{
|
||||
Logger.Write($"Error: {ex.Message}");
|
||||
Environment.Exit(1);
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
hubConnection.On("GetScreenCast", (string viewerID, string requesterName) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
ScreenCaster.BeginScreenCasting(viewerID, requesterName, outgoingMessages);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
}
|
||||
});
|
||||
|
||||
hubConnection.On("RequestScreenCast", (string viewerID, string requesterName) =>
|
||||
{
|
||||
Program.ScreenCastRequested?.Invoke(null, new Tuple<string, string>(viewerID, requesterName));
|
||||
});
|
||||
|
||||
hubConnection.On("KeyDown", (string key, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
var keyCode = Win32Interop.ConvertJavaScriptKeyToVirtualKey(key);
|
||||
Win32Interop.SendKeyDown((User32.VirtualKey)keyCode);
|
||||
}
|
||||
});
|
||||
|
||||
hubConnection.On("KeyUp", (string key, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
var keyCode = Win32Interop.ConvertJavaScriptKeyToVirtualKey(key);
|
||||
Win32Interop.SendKeyUp((User32.VirtualKey)keyCode);
|
||||
}
|
||||
});
|
||||
|
||||
hubConnection.On("KeyPress", async (string key, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
var keyCode = Win32Interop.ConvertJavaScriptKeyToVirtualKey(key);
|
||||
Win32Interop.SendKeyDown((User32.VirtualKey)keyCode);
|
||||
await Task.Delay(1);
|
||||
Win32Interop.SendKeyUp((User32.VirtualKey)keyCode);
|
||||
}
|
||||
});
|
||||
|
||||
hubConnection.On("MouseMove", (double percentX, double percentY, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
var mousePoint = ScreenCaster.GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
|
||||
Win32Interop.SendMouseMove(mousePoint.Item1, mousePoint.Item2);
|
||||
}
|
||||
});
|
||||
|
||||
hubConnection.On("MouseDown", (int button, double percentX, double percentY, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
var mousePoint = ScreenCaster.GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
|
||||
if (button == 0)
|
||||
{
|
||||
Win32Interop.SendLeftMouseDown((int)mousePoint.Item1, (int)mousePoint.Item2);
|
||||
}
|
||||
else if (button == 2)
|
||||
{
|
||||
Win32Interop.SendRightMouseDown((int)mousePoint.Item1, (int)mousePoint.Item2);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
hubConnection.On("MouseUp", (int button, double percentX, double percentY, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
var mousePoint = ScreenCaster.GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
|
||||
if (button == 0)
|
||||
{
|
||||
Win32Interop.SendLeftMouseUp((int)mousePoint.Item1, (int)mousePoint.Item2);
|
||||
}
|
||||
else if (button == 2)
|
||||
{
|
||||
Win32Interop.SendRightMouseUp((int)mousePoint.Item1, (int)mousePoint.Item2);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
hubConnection.On("MouseWheel", (double deltaX, double deltaY, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
Win32Interop.SendMouseWheel(-(int)deltaY);
|
||||
}
|
||||
});
|
||||
|
||||
hubConnection.On("ViewerDisconnected", async (string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer))
|
||||
{
|
||||
viewer.DisconnectRequested = true;
|
||||
}
|
||||
await hubConnection.InvokeAsync("ViewerDisconnected", viewerID);
|
||||
Program.ViewerRemoved?.Invoke(null, viewerID);
|
||||
|
||||
});
|
||||
hubConnection.On("LatencyUpdate", (double latency, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer))
|
||||
{
|
||||
viewer.PendingFrames--;
|
||||
viewer.Latency = latency;
|
||||
}
|
||||
});
|
||||
|
||||
hubConnection.On("SelectScreen", (int screenIndex, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer))
|
||||
{
|
||||
viewer.Capturer.SelectedScreen = screenIndex;
|
||||
}
|
||||
});
|
||||
|
||||
hubConnection.On("TouchDown", (string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
User32.GetCursorPos(out var point);
|
||||
Win32Interop.SendLeftMouseDown(point.X, point.Y);
|
||||
}
|
||||
});
|
||||
hubConnection.On("LongPress", (string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
User32.GetCursorPos(out var point);
|
||||
Win32Interop.SendRightMouseDown(point.X, point.Y);
|
||||
Win32Interop.SendRightMouseUp(point.X, point.Y);
|
||||
}
|
||||
});
|
||||
hubConnection.On("TouchMove", (double moveX, double moveY, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
User32.GetCursorPos(out var point);
|
||||
Win32Interop.SendMouseMove(point.X + moveX, point.Y + moveY);
|
||||
}
|
||||
});
|
||||
hubConnection.On("TouchUp", (string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
User32.GetCursorPos(out var point);
|
||||
Win32Interop.SendLeftMouseUp(point.X, point.Y);
|
||||
}
|
||||
});
|
||||
hubConnection.On("Tap", (double percentX, double percentY, string viewerID) =>
|
||||
{
|
||||
if (Program.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
|
||||
{
|
||||
var mousePoint = ScreenCaster.GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
|
||||
Win32Interop.SendLeftMouseDown((int)mousePoint.Item1, (int)mousePoint.Item2);
|
||||
Win32Interop.SendLeftMouseUp((int)mousePoint.Item1, (int)mousePoint.Item2);
|
||||
}
|
||||
});
|
||||
hubConnection.On("SharedFileIDs", (List<string> fileIDs) => {
|
||||
fileIDs.ForEach(id =>
|
||||
{
|
||||
var url = $"{Program.Host}/API/FileSharing/{id}";
|
||||
var webRequest = WebRequest.CreateHttp(url);
|
||||
var response = webRequest.GetResponse();
|
||||
var contentDisp = response.Headers["Content-Disposition"];
|
||||
var fileName = contentDisp
|
||||
.Split(";".ToCharArray())
|
||||
.FirstOrDefault(x => x.Trim().StartsWith("filename"))
|
||||
.Split("=".ToCharArray())[1];
|
||||
|
||||
var legalChars = fileName.ToCharArray().Where(x => !Path.GetInvalidFileNameChars().Any(y => x == y));
|
||||
|
||||
fileName = new string(legalChars.ToArray());
|
||||
|
||||
var dirPath = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "RemotelySharedFiles")).FullName;
|
||||
var filePath = Path.Combine(dirPath, fileName);
|
||||
using (var fs = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
using (var rs = response.GetResponseStream())
|
||||
{
|
||||
rs.CopyTo(fs);
|
||||
}
|
||||
}
|
||||
Process.Start("explorer.exe", dirPath);
|
||||
});
|
||||
});
|
||||
|
||||
hubConnection.On("SessionID", (string sessionID) =>
|
||||
{
|
||||
Program.SessionIDChanged?.Invoke(null, sessionID);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,354 +0,0 @@
|
||||
using Win32;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using static Win32.ADVAPI32;
|
||||
using static Win32.User32;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Win32
|
||||
{
|
||||
public class Win32Interop
|
||||
{
|
||||
public static bool OpenInteractiveProcess(string applicationName, string desktopName, bool hiddenWindow, out PROCESS_INFORMATION procInfo)
|
||||
{
|
||||
uint winlogonPid = 0;
|
||||
IntPtr hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero;
|
||||
procInfo = new PROCESS_INFORMATION();
|
||||
|
||||
// Obtain session ID for active session.
|
||||
uint dwSessionId = Kernel32.WTSGetActiveConsoleSessionId();
|
||||
|
||||
// Check for RDP session. If active, use that session ID instead.
|
||||
var rdpSessionID = GetRDPSession();
|
||||
if (rdpSessionID > 0)
|
||||
{
|
||||
dwSessionId = rdpSessionID;
|
||||
}
|
||||
|
||||
// Obtain the process ID of the winlogon process that is running within the currently active session.
|
||||
Process[] processes = Process.GetProcessesByName("winlogon");
|
||||
foreach (Process p in processes)
|
||||
{
|
||||
if ((uint)p.SessionId == dwSessionId)
|
||||
{
|
||||
winlogonPid = (uint)p.Id;
|
||||
}
|
||||
}
|
||||
|
||||
// Obtain a handle to the winlogon process.
|
||||
hProcess = Kernel32.OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);
|
||||
|
||||
// Obtain a handle to the access token of the winlogon process.
|
||||
if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken))
|
||||
{
|
||||
Kernel32.CloseHandle(hProcess);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser.
|
||||
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
|
||||
sa.Length = Marshal.SizeOf(sa);
|
||||
|
||||
// Copy the access token of the winlogon process; the newly created token will be a primary token.
|
||||
if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, TOKEN_TYPE.TokenPrimary, out hUserTokenDup))
|
||||
{
|
||||
Kernel32.CloseHandle(hProcess);
|
||||
Kernel32.CloseHandle(hPToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
// By default, CreateProcessAsUser creates a process on a non-interactive window station, meaning
|
||||
// the window station has a desktop that is invisible and the process is incapable of receiving
|
||||
// user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user
|
||||
// interaction with the new process.
|
||||
STARTUPINFO si = new STARTUPINFO();
|
||||
si.cb = (int)Marshal.SizeOf(si);
|
||||
si.lpDesktop = @"winsta0\" + desktopName;
|
||||
|
||||
// Flags that specify the priority and creation method of the process.
|
||||
uint dwCreationFlags;
|
||||
if (hiddenWindow)
|
||||
{
|
||||
dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | DETACHED_PROCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
|
||||
}
|
||||
|
||||
// Create a new process in the current user's logon session.
|
||||
bool result = CreateProcessAsUser(hUserTokenDup, null, applicationName, ref sa, ref sa, false, dwCreationFlags, IntPtr.Zero, null, ref si, out procInfo);
|
||||
|
||||
// Invalidate the handles.
|
||||
Kernel32.CloseHandle(hProcess);
|
||||
Kernel32.CloseHandle(hPToken);
|
||||
Kernel32.CloseHandle(hUserTokenDup);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static uint GetRDPSession()
|
||||
{
|
||||
IntPtr ppSessionInfo = IntPtr.Zero;
|
||||
Int32 count = 0;
|
||||
Int32 retval = WTSAPI32.WTSEnumerateSessions(WTSAPI32.WTS_CURRENT_SERVER_HANDLE, 0, 1, ref ppSessionInfo, ref count);
|
||||
Int32 dataSize = Marshal.SizeOf(typeof(WTSAPI32.WTS_SESSION_INFO));
|
||||
var sessList = new List<WTSAPI32.WTS_SESSION_INFO>();
|
||||
Int64 current = (Int64)ppSessionInfo;
|
||||
|
||||
if (retval != 0)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
WTSAPI32.WTS_SESSION_INFO sessInf = (WTSAPI32.WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTSAPI32.WTS_SESSION_INFO));
|
||||
current += dataSize;
|
||||
sessList.Add(sessInf);
|
||||
}
|
||||
}
|
||||
uint retVal = 0;
|
||||
var rdpSession = sessList.Find(ses => ses.pWinStationName.ToLower().Contains("rdp") && ses.State == 0);
|
||||
if (sessList.Exists(ses => ses.pWinStationName.ToLower().Contains("rdp") && ses.State == 0))
|
||||
{
|
||||
retVal = (uint)rdpSession.SessionID;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
public static IntPtr OpenInputDesktop()
|
||||
{
|
||||
return User32.OpenInputDesktop(0, false, ACCESS_MASK.GENERIC_ALL);
|
||||
}
|
||||
public static uint SendLeftMouseDown(int x, int y)
|
||||
{
|
||||
// Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.
|
||||
var normalizedX = x * (double)65535;
|
||||
var normalizedY = y * (double)65535;
|
||||
var union = new InputUnion() { mi = new MOUSEINPUT() { dwFlags = MOUSEEVENTF.ABSOLUTE | MOUSEEVENTF.LEFTDOWN | MOUSEEVENTF.VIRTUALDESK, dx = (int)normalizedX, dy = (int)normalizedY, time = 0, mouseData = 0, dwExtraInfo = (UIntPtr)GetMessageExtraInfo() } };
|
||||
var input = new INPUT() { type = InputType.MOUSE, U = union };
|
||||
return SendInput(1, new INPUT[] { input }, INPUT.Size);
|
||||
}
|
||||
public static uint SendLeftMouseUp(int x, int y)
|
||||
{
|
||||
// Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.
|
||||
var normalizedX = x * (double)65535;
|
||||
var normalizedY = y * (double)65535;
|
||||
var union = new InputUnion() { mi = new MOUSEINPUT() { dwFlags = MOUSEEVENTF.ABSOLUTE | MOUSEEVENTF.LEFTUP | MOUSEEVENTF.VIRTUALDESK, dx = (int)normalizedX, dy = (int)normalizedY, time = 0, mouseData = 0, dwExtraInfo = (UIntPtr)GetMessageExtraInfo() } };
|
||||
var input = new INPUT() { type = InputType.MOUSE, U = union };
|
||||
return SendInput(1, new INPUT[] { input }, INPUT.Size);
|
||||
}
|
||||
public static uint SendRightMouseDown(int x, int y)
|
||||
{
|
||||
// Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.
|
||||
var normalizedX = x * (double)65535;
|
||||
var normalizedY = y * (double)65535;
|
||||
var union = new InputUnion() { mi = new MOUSEINPUT() { dwFlags = MOUSEEVENTF.ABSOLUTE | MOUSEEVENTF.RIGHTDOWN | MOUSEEVENTF.VIRTUALDESK, dx = (int)normalizedX, dy = (int)normalizedY, time = 0, mouseData = 0, dwExtraInfo = (UIntPtr)GetMessageExtraInfo() } };
|
||||
var input = new INPUT() { type = InputType.MOUSE, U = union };
|
||||
return SendInput(1, new INPUT[] { input }, INPUT.Size);
|
||||
}
|
||||
public static uint SendRightMouseUp(int x, int y)
|
||||
{
|
||||
// Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.
|
||||
var normalizedX = x * (double)65535;
|
||||
var normalizedY = y * (double)65535;
|
||||
var union = new InputUnion() { mi = new MOUSEINPUT() { dwFlags = MOUSEEVENTF.ABSOLUTE | MOUSEEVENTF.RIGHTUP | MOUSEEVENTF.VIRTUALDESK, dx = (int)normalizedX, dy = (int)normalizedY, time = 0, mouseData = 0, dwExtraInfo = (UIntPtr)GetMessageExtraInfo() } };
|
||||
var input = new INPUT() { type = InputType.MOUSE, U = union };
|
||||
return SendInput(1, new INPUT[] { input }, INPUT.Size);
|
||||
}
|
||||
|
||||
// Offsets are used in case there's a multi-monitor setup where the left-most or top-most edge of the virtual screen
|
||||
// is not 0.
|
||||
public static uint SendMouseMove(double x, double y)
|
||||
{
|
||||
// Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.
|
||||
var normalizedX = x * (double)65535;
|
||||
var normalizedY = y * (double)65535;
|
||||
var union = new InputUnion() { mi = new MOUSEINPUT() { dwFlags = MOUSEEVENTF.ABSOLUTE | MOUSEEVENTF.MOVE | MOUSEEVENTF.VIRTUALDESK, dx = (int)normalizedX, dy = (int)normalizedY, time = 0, mouseData = 0, dwExtraInfo = (UIntPtr)GetMessageExtraInfo() } };
|
||||
var input = new INPUT() { type = InputType.MOUSE, U = union };
|
||||
return SendInput(1, new INPUT[] { input }, INPUT.Size);
|
||||
}
|
||||
|
||||
public static uint SendMouseWheel(int deltaY)
|
||||
{
|
||||
if (deltaY < 0)
|
||||
{
|
||||
deltaY = -120;
|
||||
}
|
||||
else if (deltaY > 0)
|
||||
{
|
||||
deltaY = 120;
|
||||
}
|
||||
var union = new User32.InputUnion() { mi = new User32.MOUSEINPUT() { dwFlags = MOUSEEVENTF.WHEEL, dx = 0, dy = 0, time = 0, mouseData = deltaY, dwExtraInfo = GetMessageExtraInfo() } };
|
||||
var input = new User32.INPUT() { type = InputType.MOUSE, U = union };
|
||||
return SendInput(1, new User32.INPUT[] { input }, INPUT.Size);
|
||||
}
|
||||
|
||||
public static void SendKeyDown(VirtualKey key)
|
||||
{
|
||||
var union = new InputUnion()
|
||||
{
|
||||
ki = new KEYBDINPUT()
|
||||
{
|
||||
wVk = key,
|
||||
wScan = 0,
|
||||
time = 0,
|
||||
dwExtraInfo = GetMessageExtraInfo()
|
||||
}
|
||||
};
|
||||
var input = new INPUT() { type = InputType.KEYBOARD, U = union };
|
||||
SendInput(1, new INPUT[] { input }, INPUT.Size);
|
||||
}
|
||||
public static void SendKeyUp(VirtualKey key)
|
||||
{
|
||||
var union = new InputUnion()
|
||||
{
|
||||
ki = new KEYBDINPUT()
|
||||
{
|
||||
wVk = key,
|
||||
wScan = 0,
|
||||
time = 0,
|
||||
dwFlags = KEYEVENTF.KEYUP,
|
||||
dwExtraInfo = GetMessageExtraInfo()
|
||||
}
|
||||
};
|
||||
var input = new INPUT() { type = InputType.KEYBOARD, U = union };
|
||||
SendInput(1, new INPUT[] { input }, INPUT.Size);
|
||||
}
|
||||
public static string GetCurrentDesktop()
|
||||
{
|
||||
var inputDesktop = OpenInputDesktop();
|
||||
byte[] deskBytes = new byte[256];
|
||||
uint lenNeeded;
|
||||
var success = GetUserObjectInformationW(inputDesktop, UOI_NAME, deskBytes, 256, out lenNeeded);
|
||||
if (!success)
|
||||
{
|
||||
CloseDesktop(inputDesktop);
|
||||
return "Default";
|
||||
}
|
||||
var desktopName = Encoding.Unicode.GetString(deskBytes.Take((int)lenNeeded).ToArray()).Replace("\0", "");
|
||||
CloseDesktop(inputDesktop);
|
||||
return desktopName;
|
||||
}
|
||||
|
||||
public static short ConvertJavaScriptKeyToVirtualKey(string key)
|
||||
{
|
||||
short keyCode;
|
||||
switch (key)
|
||||
{
|
||||
case "Down":
|
||||
case "ArrowDown":
|
||||
keyCode = (short)VirtualKey.DOWN;
|
||||
break;
|
||||
case "Up":
|
||||
case "ArrowUp":
|
||||
keyCode = (short)VirtualKey.UP;
|
||||
break;
|
||||
case "Left":
|
||||
case "ArrowLeft":
|
||||
keyCode = (short)VirtualKey.LEFT;
|
||||
break;
|
||||
case "Right":
|
||||
case "ArrowRight":
|
||||
keyCode = (short)VirtualKey.RIGHT;
|
||||
break;
|
||||
case "Enter":
|
||||
keyCode = (short)VirtualKey.RETURN;
|
||||
break;
|
||||
case "Esc":
|
||||
case "Escape":
|
||||
keyCode = (short)VirtualKey.ESCAPE;
|
||||
break;
|
||||
case "Alt":
|
||||
keyCode = (short)VirtualKey.MENU;
|
||||
break;
|
||||
case "Control":
|
||||
keyCode = (short)VirtualKey.CONTROL;
|
||||
break;
|
||||
case "Shift":
|
||||
keyCode = (short)VirtualKey.SHIFT;
|
||||
break;
|
||||
case "PAUSE":
|
||||
keyCode = (short)VirtualKey.PAUSE;
|
||||
break;
|
||||
case "BREAK":
|
||||
keyCode = (short)VirtualKey.PAUSE;
|
||||
break;
|
||||
case "Backspace":
|
||||
keyCode = (short)VirtualKey.BACK;
|
||||
break;
|
||||
case "Tab":
|
||||
keyCode = (short)VirtualKey.TAB;
|
||||
break;
|
||||
case "CapsLock":
|
||||
keyCode = (short)VirtualKey.CAPITAL;
|
||||
break;
|
||||
case "Delete":
|
||||
keyCode = (short)VirtualKey.DELETE;
|
||||
break;
|
||||
case "Home":
|
||||
keyCode = (short)VirtualKey.HOME;
|
||||
break;
|
||||
case "End":
|
||||
keyCode = (short)VirtualKey.END;
|
||||
break;
|
||||
case "PageUp":
|
||||
keyCode = (short)VirtualKey.PRIOR;
|
||||
break;
|
||||
case "PageDown":
|
||||
keyCode = (short)VirtualKey.NEXT;
|
||||
break;
|
||||
case "NumLock":
|
||||
keyCode = (short)VirtualKey.NUMLOCK;
|
||||
break;
|
||||
case "Insert":
|
||||
keyCode = (short)VirtualKey.INSERT;
|
||||
break;
|
||||
case "ScrollLock":
|
||||
keyCode = (short)VirtualKey.SCROLL;
|
||||
break;
|
||||
case "F1":
|
||||
keyCode = (short)VirtualKey.F1;
|
||||
break;
|
||||
case "F2":
|
||||
keyCode = (short)VirtualKey.F2;
|
||||
break;
|
||||
case "F3":
|
||||
keyCode = (short)VirtualKey.F3;
|
||||
break;
|
||||
case "F4":
|
||||
keyCode = (short)VirtualKey.F4;
|
||||
break;
|
||||
case "F5":
|
||||
keyCode = (short)VirtualKey.F5;
|
||||
break;
|
||||
case "F6":
|
||||
keyCode = (short)VirtualKey.F6;
|
||||
break;
|
||||
case "F7":
|
||||
keyCode = (short)VirtualKey.F7;
|
||||
break;
|
||||
case "F8":
|
||||
keyCode = (short)VirtualKey.F8;
|
||||
break;
|
||||
case "F9":
|
||||
keyCode = (short)VirtualKey.F9;
|
||||
break;
|
||||
case "F10":
|
||||
keyCode = (short)VirtualKey.F10;
|
||||
break;
|
||||
case "F11":
|
||||
keyCode = (short)VirtualKey.F11;
|
||||
break;
|
||||
case "F12":
|
||||
keyCode = (short)VirtualKey.F12;
|
||||
break;
|
||||
default:
|
||||
keyCode = User32.VkKeyScan(Convert.ToChar(key));
|
||||
break;
|
||||
}
|
||||
return keyCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast_Linux.Capture
|
||||
{
|
||||
public interface ICapturer : IDisposable
|
||||
{
|
||||
bool CaptureFullscreen { get; set; }
|
||||
Bitmap CurrentFrame { get; set; }
|
||||
Rectangle CurrentScreenBounds { get; }
|
||||
Bitmap PreviousFrame { get; set; }
|
||||
EventHandler<Rectangle> ScreenChanged { get; set; }
|
||||
int SelectedScreen { get; set; }
|
||||
void Capture();
|
||||
void Init();
|
||||
}
|
||||
}
|
||||
@ -1,198 +0,0 @@
|
||||
using Remotely_ScreenCast_Linux.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast_Linux.Capture
|
||||
{
|
||||
public class ImageUtils
|
||||
{
|
||||
public static byte[] EncodeBitmap(Bitmap bitmap)
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
bitmap.Save(ms, ImageFormat.Jpeg);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static Rectangle GetDiffArea(Bitmap currentFrame, Bitmap previousFrame, bool captureFullscreen)
|
||||
{
|
||||
if (captureFullscreen)
|
||||
{
|
||||
return new Rectangle(new Point(0, 0), currentFrame.Size);
|
||||
}
|
||||
if (currentFrame.Height != previousFrame.Height || currentFrame.Width != previousFrame.Width)
|
||||
{
|
||||
throw new Exception("Bitmaps are not of equal dimensions.");
|
||||
}
|
||||
if (!Bitmap.IsAlphaPixelFormat(currentFrame.PixelFormat) || !Bitmap.IsAlphaPixelFormat(previousFrame.PixelFormat) ||
|
||||
!Bitmap.IsCanonicalPixelFormat(currentFrame.PixelFormat) || !Bitmap.IsCanonicalPixelFormat(previousFrame.PixelFormat))
|
||||
{
|
||||
throw new Exception("Bitmaps must be 32 bits per pixel and contain alpha channel.");
|
||||
}
|
||||
var width = currentFrame.Width;
|
||||
var height = currentFrame.Height;
|
||||
int left = int.MaxValue;
|
||||
int top = int.MaxValue;
|
||||
int right = int.MinValue;
|
||||
int bottom = int.MinValue;
|
||||
|
||||
BitmapData bd1 = null;
|
||||
BitmapData bd2 = null;
|
||||
|
||||
try
|
||||
{
|
||||
bd1 = previousFrame.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, currentFrame.PixelFormat);
|
||||
bd2 = currentFrame.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, previousFrame.PixelFormat);
|
||||
|
||||
var bytesPerPixel = Bitmap.GetPixelFormatSize(currentFrame.PixelFormat) / 8;
|
||||
var totalSize = bd1.Height * bd1.Width * bytesPerPixel;
|
||||
|
||||
unsafe
|
||||
{
|
||||
byte* scan1 = (byte*)bd1.Scan0.ToPointer();
|
||||
byte* scan2 = (byte*)bd2.Scan0.ToPointer();
|
||||
|
||||
for (int counter = 0; counter < totalSize - bytesPerPixel; counter += bytesPerPixel)
|
||||
{
|
||||
byte* data1 = scan1 + counter;
|
||||
byte* data2 = scan2 + counter;
|
||||
|
||||
if (data1[0] != data2[0] ||
|
||||
data1[1] != data2[1] ||
|
||||
data1[2] != data2[2] ||
|
||||
data1[3] != data2[3])
|
||||
{
|
||||
// Change was found.
|
||||
var pixel = counter / 4;
|
||||
var row = (int)Math.Floor((double)pixel / bd1.Width);
|
||||
var column = pixel % bd1.Width;
|
||||
if (row < top)
|
||||
{
|
||||
top = row;
|
||||
}
|
||||
if (row > bottom)
|
||||
{
|
||||
bottom = row;
|
||||
}
|
||||
if (column < left)
|
||||
{
|
||||
left = column;
|
||||
}
|
||||
if (column > right)
|
||||
{
|
||||
right = column;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (left < right && top < bottom)
|
||||
{
|
||||
// Bounding box is valid.
|
||||
|
||||
left = Math.Max(left - 20, 0);
|
||||
top = Math.Max(top - 20, 0);
|
||||
right = Math.Min(right + 20, width);
|
||||
bottom = Math.Min(bottom + 20, height);
|
||||
|
||||
return new Rectangle(left, top, right - left, bottom - top);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Rectangle.Empty;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Rectangle.Empty;
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
currentFrame.UnlockBits(bd1);
|
||||
previousFrame.UnlockBits(bd2);
|
||||
bd1 = null;
|
||||
bd2 = null;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
public static Bitmap GetImageDiff(Bitmap currentFrame, Bitmap previousFrame, bool captureFullscreen)
|
||||
{
|
||||
if (captureFullscreen)
|
||||
{
|
||||
return (Bitmap)currentFrame.Clone();
|
||||
}
|
||||
|
||||
if (currentFrame.Height != previousFrame.Height || currentFrame.Width != previousFrame.Width)
|
||||
{
|
||||
throw new Exception("Bitmaps are not of equal dimensions.");
|
||||
}
|
||||
if (!Bitmap.IsAlphaPixelFormat(currentFrame.PixelFormat) || !Bitmap.IsAlphaPixelFormat(previousFrame.PixelFormat) ||
|
||||
!Bitmap.IsCanonicalPixelFormat(currentFrame.PixelFormat) || !Bitmap.IsCanonicalPixelFormat(previousFrame.PixelFormat))
|
||||
{
|
||||
throw new Exception("Bitmaps must be 32 bits per pixel and contain alpha channel.");
|
||||
}
|
||||
var width = currentFrame.Width;
|
||||
var height = currentFrame.Height;
|
||||
|
||||
var mergedFrame = new Bitmap(width, height);
|
||||
|
||||
var bd1 = previousFrame.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, currentFrame.PixelFormat);
|
||||
var bd2 = currentFrame.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, previousFrame.PixelFormat);
|
||||
var bd3 = mergedFrame.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, currentFrame.PixelFormat);
|
||||
|
||||
|
||||
// Get the address of the first line.
|
||||
IntPtr ptr1 = bd1.Scan0;
|
||||
IntPtr ptr2 = bd2.Scan0;
|
||||
IntPtr ptr3 = bd3.Scan0;
|
||||
|
||||
// Declare an array to hold the bytes of the bitmap.
|
||||
int arraySize = Math.Abs(bd1.Stride) * currentFrame.Height;
|
||||
var rgbValues1 = new byte[arraySize];
|
||||
var rgbValues2 = new byte[arraySize];
|
||||
var rgbValues3 = new byte[arraySize];
|
||||
|
||||
// Copy the RGBA values into the array.
|
||||
Marshal.Copy(ptr1, rgbValues1, 0, arraySize);
|
||||
Marshal.Copy(ptr2, rgbValues2, 0, arraySize);
|
||||
|
||||
// Check RGBA value for each pixel.
|
||||
for (int counter = 0; counter < rgbValues2.Length - 4; counter += 4)
|
||||
{
|
||||
if (rgbValues1[counter] != rgbValues2[counter] ||
|
||||
rgbValues1[counter + 1] != rgbValues2[counter + 1] ||
|
||||
rgbValues1[counter + 2] != rgbValues2[counter + 2] ||
|
||||
rgbValues1[counter + 3] != rgbValues2[counter + 3])
|
||||
{
|
||||
// Change was found.
|
||||
rgbValues3[counter] = rgbValues2[counter];
|
||||
rgbValues3[counter + 1] = rgbValues2[counter + 1];
|
||||
rgbValues3[counter + 2] = rgbValues2[counter + 2];
|
||||
rgbValues3[counter + 3] = rgbValues2[counter + 3];
|
||||
}
|
||||
}
|
||||
|
||||
// Copy merged frame to bitmap.
|
||||
Marshal.Copy(rgbValues3, 0, ptr3, rgbValues3.Length);
|
||||
|
||||
previousFrame.UnlockBits(bd1);
|
||||
currentFrame.UnlockBits(bd2);
|
||||
mergedFrame.UnlockBits(bd3);
|
||||
|
||||
return mergedFrame;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,137 +0,0 @@
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Remotely_ScreenCast_Linux;
|
||||
using Remotely_ScreenCast_Linux.Capture;
|
||||
using Remotely_ScreenCast_Linux.Models;
|
||||
using Remotely_ScreenCast_Linux.Sockets;
|
||||
using Remotely_ScreenCast_Linux.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast_Linux.Capture
|
||||
{
|
||||
public class ScreenCaster
|
||||
{
|
||||
public static async void BeginScreenCasting(string viewerID,
|
||||
string requesterName,
|
||||
OutgoingMessages outgoingMessages)
|
||||
{
|
||||
ICapturer capturer;
|
||||
capturer = new X11Capture();
|
||||
capturer.Init();
|
||||
Viewer viewer;
|
||||
byte[] encodedImageBytes;
|
||||
var success = false;
|
||||
|
||||
Logger.Write($"Starting screen cast. Requester: {requesterName}. Viewer ID: {viewerID}. App Mode: {Program.Mode}");
|
||||
|
||||
viewer = new Viewer()
|
||||
{
|
||||
Capturer = capturer,
|
||||
DisconnectRequested = false,
|
||||
Name = requesterName,
|
||||
ViewerConnectionID = viewerID,
|
||||
HasControl = true,
|
||||
ImageQuality = 1
|
||||
};
|
||||
|
||||
while (!success)
|
||||
{
|
||||
success = Program.Viewers.TryAdd(viewerID, viewer);
|
||||
}
|
||||
|
||||
if (Program.Mode == Enums.AppMode.Normal)
|
||||
{
|
||||
Program.ViewerAdded?.Invoke(null, viewer);
|
||||
}
|
||||
|
||||
await outgoingMessages.SendScreenCount(
|
||||
capturer.SelectedScreen,
|
||||
//Screen.AllScreens.Length,
|
||||
1,
|
||||
viewerID);
|
||||
|
||||
await outgoingMessages.SendScreenSize(capturer.CurrentScreenBounds.Width, capturer.CurrentScreenBounds.Height, viewerID);
|
||||
|
||||
capturer.ScreenChanged += async (sender, bounds) =>
|
||||
{
|
||||
await outgoingMessages.SendScreenSize(bounds.Width, bounds.Height, viewerID);
|
||||
};
|
||||
|
||||
//await outgoingMessages.SendCursorChange(CursorIconWatcher.Current.GetCurrentCursor(), new List<string>() { viewerID });
|
||||
|
||||
while (!viewer.DisconnectRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (viewer.PendingFrames > 10)
|
||||
{
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
capturer.Capture();
|
||||
|
||||
var diffArea = ImageUtils.GetDiffArea(capturer.CurrentFrame, capturer.PreviousFrame, capturer.CaptureFullscreen);
|
||||
|
||||
if (diffArea.IsEmpty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
using (var newImage = capturer.CurrentFrame.Clone(diffArea, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
|
||||
{
|
||||
if (capturer.CaptureFullscreen)
|
||||
{
|
||||
capturer.CaptureFullscreen = false;
|
||||
}
|
||||
|
||||
encodedImageBytes = ImageUtils.EncodeBitmap(newImage);
|
||||
|
||||
if (encodedImageBytes?.Length > 0)
|
||||
{
|
||||
await outgoingMessages.SendScreenCapture(encodedImageBytes, viewerID, diffArea.Left, diffArea.Top, diffArea.Width, diffArea.Height, DateTime.UtcNow);
|
||||
viewer.PendingFrames++;
|
||||
}
|
||||
GC.Collect();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
}
|
||||
}
|
||||
Logger.Write($"Ended screen cast. Requester: {requesterName}. Viewer ID: {viewerID}.");
|
||||
success = false;
|
||||
while (!success)
|
||||
{
|
||||
success = Program.Viewers.TryRemove(viewerID, out _);
|
||||
}
|
||||
|
||||
capturer.Dispose();
|
||||
|
||||
// Close if no one is viewing.
|
||||
if (Program.Viewers.Count == 0 && Program.Mode == Enums.AppMode.Unattended)
|
||||
{
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
}
|
||||
public static Tuple<double, double> GetAbsolutePercentFromRelativePercent(double percentX, double percentY, ICapturer capturer)
|
||||
{
|
||||
var absoluteX = (capturer.CurrentScreenBounds.Width * percentX) + capturer.CurrentScreenBounds.Left;
|
||||
var absoluteY = (capturer.CurrentScreenBounds.Height * percentY) + capturer.CurrentScreenBounds.Top;
|
||||
return new Tuple<double, double>(800, 600);
|
||||
//return new Tuple<double, double>(absoluteX / SystemInformation.VirtualScreen.Width, absoluteY / SystemInformation.VirtualScreen.Height);
|
||||
}
|
||||
public static Tuple<double, double> GetAbsolutePointFromRelativePercent(double percentX, double percentY, ICapturer capturer)
|
||||
{
|
||||
var absoluteX = (capturer.CurrentScreenBounds.Width * percentX) + capturer.CurrentScreenBounds.Left;
|
||||
var absoluteY = (capturer.CurrentScreenBounds.Height * percentY) + capturer.CurrentScreenBounds.Top;
|
||||
return new Tuple<double, double>(absoluteX, absoluteY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,96 +0,0 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using Remotely_ScreenCast_Linux.Utilities;
|
||||
using System.Threading;
|
||||
|
||||
namespace Remotely_ScreenCast_Linux.Capture
|
||||
{
|
||||
public class X11Capture : ICapturer
|
||||
{
|
||||
public Bitmap CurrentFrame { get; set; }
|
||||
public Bitmap PreviousFrame { get; set; }
|
||||
public bool IsCapturing { get; set; }
|
||||
public bool CaptureFullscreen { get; set; } = true;
|
||||
public int PauseForMilliseconds { get; set; }
|
||||
public EventHandler<Rectangle> ScreenChanged { get; set; }
|
||||
private object ScreenLock { get; } = new object();
|
||||
public int SelectedScreen
|
||||
{
|
||||
get
|
||||
{
|
||||
return selectedScreen;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == selectedScreen)
|
||||
{
|
||||
return;
|
||||
}
|
||||
lock (ScreenLock)
|
||||
{
|
||||
//if (Screen.AllScreens.Length >= value + 1)
|
||||
//{
|
||||
// selectedScreen = value;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// selectedScreen = 0;
|
||||
//}
|
||||
//CurrentScreenBounds = Screen.AllScreens[selectedScreen].Bounds;
|
||||
CaptureFullscreen = true;
|
||||
Init();
|
||||
ScreenChanged?.Invoke(this, CurrentScreenBounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
//public Rectangle CurrentScreenBounds { get; set; } = Screen.PrimaryScreen.Bounds;
|
||||
//private int selectedScreen = Screen.AllScreens.ToList().IndexOf(Screen.PrimaryScreen);
|
||||
public Rectangle CurrentScreenBounds { get; set; }
|
||||
private int selectedScreen;
|
||||
private Graphics graphic;
|
||||
|
||||
|
||||
public X11Capture()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
CurrentFrame = new Bitmap(CurrentScreenBounds.Width, CurrentScreenBounds.Height, PixelFormat.Format32bppArgb);
|
||||
PreviousFrame = new Bitmap(CurrentScreenBounds.Width, CurrentScreenBounds.Height, PixelFormat.Format32bppArgb);
|
||||
graphic = Graphics.FromImage(CurrentFrame);
|
||||
}
|
||||
|
||||
public void Capture()
|
||||
{
|
||||
try
|
||||
{
|
||||
PreviousFrame = (Bitmap)CurrentFrame.Clone();
|
||||
graphic.CopyFromScreen(CurrentScreenBounds.Left, CurrentScreenBounds.Top, 0, 0, new Size(CurrentScreenBounds.Width, CurrentScreenBounds.Height));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
Init();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
graphic.Dispose();
|
||||
CurrentFrame.Dispose();
|
||||
PreviousFrame.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast_Linux.Enums
|
||||
{
|
||||
public enum AppMode
|
||||
{
|
||||
Unattended,
|
||||
Normal
|
||||
}
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast_Linux.Models
|
||||
{
|
||||
public class CursorInfo
|
||||
{
|
||||
public CursorInfo(byte[] imageBytes, Point hotspot, string cssOverride = null)
|
||||
{
|
||||
ImageBytes = imageBytes;
|
||||
HotSpot = hotspot;
|
||||
CssOverride = cssOverride;
|
||||
}
|
||||
|
||||
public byte[] ImageBytes { get; set; }
|
||||
public Point HotSpot { get; set; }
|
||||
|
||||
public string CssOverride { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
interface CursorInfo {
|
||||
ImageBytes: any[];
|
||||
HotSpot: any;
|
||||
CssOverride: string;
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
using Remotely_ScreenCast_Linux.Capture;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast_Linux.Models
|
||||
{
|
||||
public class Viewer
|
||||
{
|
||||
public string ViewerConnectionID { get; set; }
|
||||
public string Name { get; set; }
|
||||
public ICapturer Capturer { get; set; }
|
||||
public bool DisconnectRequested { get; set; }
|
||||
public bool HasControl { get; set; }
|
||||
public double Latency { get; set; } = 1;
|
||||
public int PendingFrames { get; set; }
|
||||
|
||||
private double imageQuality = 1;
|
||||
public double ImageQuality
|
||||
{
|
||||
get
|
||||
{
|
||||
return imageQuality;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (imageQuality > 1 || imageQuality < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
imageQuality = value;
|
||||
}
|
||||
}
|
||||
public bool FullScreenRefreshNeeded { get; internal set; }
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,151 +0,0 @@
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Remotely_ScreenCast_Linux;
|
||||
using Remotely_ScreenCast_Linux.Capture;
|
||||
using Remotely_ScreenCast_Linux.Enums;
|
||||
using Remotely_ScreenCast_Linux.Models;
|
||||
using Remotely_ScreenCast_Linux.Sockets;
|
||||
using Remotely_ScreenCast_Linux.Utilities;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast_Linux
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static AppMode Mode { get; private set; }
|
||||
public static string RequesterID { get; private set; }
|
||||
public static string ServiceID { get; private set; }
|
||||
public static string Host { get; private set; }
|
||||
public static HubConnection Connection { get; private set; }
|
||||
public static OutgoingMessages OutgoingMessages { get; private set; }
|
||||
public static ConcurrentDictionary<string, Viewer> Viewers { get; } = new ConcurrentDictionary<string, Viewer>();
|
||||
public static Dictionary<string, string> ArgDict { get; set; }
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
ProcessArgs(args);
|
||||
Connect().Wait();
|
||||
SetEventHandlers();
|
||||
HandleConnection().Wait();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task HandleConnection()
|
||||
{
|
||||
OutgoingMessages.SendDeviceInfo(ServiceID, Environment.MachineName).Wait();
|
||||
|
||||
|
||||
if (Mode == AppMode.Unattended)
|
||||
{
|
||||
StartWaitForViewerTimer();
|
||||
}
|
||||
else if (Mode == AppMode.Normal)
|
||||
{
|
||||
OutgoingMessages.GetSessionID().Wait();
|
||||
}
|
||||
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
await Task.Delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetEventHandlers()
|
||||
{
|
||||
OutgoingMessages = new OutgoingMessages(Connection);
|
||||
|
||||
MessageHandlers.ApplyConnectionHandlers(Connection, OutgoingMessages);
|
||||
|
||||
//CursorIconWatcher.Current.OnChange += CursorIconWatcher_OnChange;
|
||||
}
|
||||
|
||||
public static Task Connect()
|
||||
{
|
||||
Connection = new HubConnectionBuilder()
|
||||
.WithUrl($"{Host}/RCDeviceHub")
|
||||
.AddMessagePackProtocol()
|
||||
.Build();
|
||||
|
||||
return Connection.StartAsync();
|
||||
}
|
||||
|
||||
|
||||
private static async void CursorIconWatcher_OnChange(object sender, CursorInfo cursor)
|
||||
{
|
||||
await OutgoingMessages.SendCursorChange(cursor, Viewers.Keys.ToList());
|
||||
}
|
||||
|
||||
private static void StartWaitForViewerTimer()
|
||||
{
|
||||
var timer = new System.Timers.Timer(10000);
|
||||
timer.AutoReset = false;
|
||||
timer.Elapsed += (sender, arg) =>
|
||||
{
|
||||
// Shut down if no viewers have connected within 10 seconds.
|
||||
if (Viewers.Count == 0)
|
||||
{
|
||||
Logger.Write("No viewers connected after 10 seconds. Shutting down.");
|
||||
Environment.Exit(0);
|
||||
}
|
||||
};
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
Logger.Write((Exception)e.ExceptionObject);
|
||||
}
|
||||
|
||||
public static void ProcessArgs(string[] args)
|
||||
{
|
||||
ArgDict = new Dictionary<string, string>();
|
||||
|
||||
for (var i = 0; i < args.Length; i += 2)
|
||||
{
|
||||
var key = args?[i];
|
||||
if (key != null)
|
||||
{
|
||||
key = key.Trim().Replace("-", "").ToLower();
|
||||
var value = args?[i + 1];
|
||||
if (value != null)
|
||||
{
|
||||
ArgDict[key] = args[i + 1].Trim();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Mode = (AppMode)Enum.Parse(typeof(AppMode), ArgDict["mode"]);
|
||||
Host = ArgDict["host"];
|
||||
|
||||
if (Mode == AppMode.Unattended)
|
||||
{
|
||||
RequesterID = ArgDict["requester"];
|
||||
ServiceID = ArgDict["serviceid"];
|
||||
}
|
||||
|
||||
}
|
||||
public static EventHandler<string> SessionIDChanged { get; set; }
|
||||
public static EventHandler<string> ViewerRemoved { get; set; }
|
||||
public static EventHandler<Viewer> ViewerAdded { get; set; }
|
||||
public static EventHandler<Tuple<string, string>> ScreenCastRequested { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,70 +0,0 @@
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Remotely_ScreenCast_Linux.Models;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast_Linux.Sockets
|
||||
{
|
||||
public class OutgoingMessages
|
||||
{
|
||||
public OutgoingMessages(HubConnection hubConnection)
|
||||
{
|
||||
Connection = hubConnection;
|
||||
}
|
||||
|
||||
private HubConnection Connection { get; }
|
||||
public async Task SendScreenSize(int width, int height, string viewerID)
|
||||
{
|
||||
await Connection.SendAsync("SendScreenSize", width, height, viewerID);
|
||||
}
|
||||
|
||||
public async Task SendScreenCapture(byte[] captureBytes, string viewerID, int left, int top, int width, int height, DateTime captureTime)
|
||||
{
|
||||
await Connection.SendAsync("SendScreenCapture", captureBytes, viewerID, left, top, width, height, captureTime);
|
||||
}
|
||||
|
||||
internal async Task SendScreenCount(int primaryScreenIndex, int screenCount, string viewerID)
|
||||
{
|
||||
await Connection.SendAsync("SendScreenCountToBrowser", primaryScreenIndex, screenCount, viewerID);
|
||||
}
|
||||
|
||||
public async Task NotifyRequesterUnattendedReady(string requesterID)
|
||||
{
|
||||
await Connection.SendAsync("NotifyRequesterUnattendedReady", requesterID);
|
||||
}
|
||||
|
||||
public async Task SendCursorChange(CursorInfo cursor, List<string> viewerIDs)
|
||||
{
|
||||
await Connection.SendAsync("SendCursorChange", cursor, viewerIDs);
|
||||
}
|
||||
|
||||
internal async Task NotifyViewersRelaunchedScreenCasterReady(string[] viewerIDs)
|
||||
{
|
||||
await Connection.SendAsync("NotifyViewersRelaunchedScreenCasterReady", viewerIDs);
|
||||
}
|
||||
|
||||
internal async Task SendDeviceInfo(string serviceID, string machineName)
|
||||
{
|
||||
await Connection.SendAsync("ReceiveDeviceInfo", serviceID, machineName);
|
||||
}
|
||||
|
||||
internal async Task SendConnectionFailedToViewers(List<string> viewerIDs)
|
||||
{
|
||||
await Connection.SendAsync("SendConnectionFailedToViewers", viewerIDs);
|
||||
}
|
||||
|
||||
internal async Task GetSessionID()
|
||||
{
|
||||
await Connection.SendAsync("GetSessionID");
|
||||
}
|
||||
|
||||
public async Task SendViewerRemoved(string viewerID)
|
||||
{
|
||||
await Connection.SendAsync("SendViewerRemoved", viewerID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,76 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_ScreenCast_Linux.Utilities
|
||||
{
|
||||
public static class Logger
|
||||
{
|
||||
public static void Write(string message)
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), "Remotely_Logs.txt");
|
||||
|
||||
var jsoninfo = new
|
||||
{
|
||||
Type = "Info",
|
||||
Timestamp = DateTime.Now.ToString(),
|
||||
Message = message
|
||||
};
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var fi = new FileInfo(path);
|
||||
while (fi.Length > 1000000)
|
||||
{
|
||||
var content = File.ReadAllLines(path);
|
||||
File.WriteAllLines(path, content.Skip(10));
|
||||
fi = new FileInfo(path);
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
File.AppendAllText(path, JsonConvert.SerializeObject(jsoninfo) + Environment.NewLine);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Task.Delay(1000).ContinueWith((Task task) =>
|
||||
{
|
||||
Write(message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void Write(Exception ex)
|
||||
{
|
||||
var exception = ex;
|
||||
var path = Path.Combine(Path.GetTempPath(), "Remotely_Logs.txt");
|
||||
|
||||
while (exception != null)
|
||||
{
|
||||
var jsonError = new
|
||||
{
|
||||
Type = "Error",
|
||||
Timestamp = DateTime.Now.ToString(),
|
||||
Message = exception?.Message,
|
||||
Source = exception?.Source,
|
||||
StackTrace = exception?.StackTrace,
|
||||
};
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var fi = new FileInfo(path);
|
||||
while (fi.Length > 1000000)
|
||||
{
|
||||
var content = File.ReadAllLines(path);
|
||||
File.WriteAllLines(path, content.Skip(10));
|
||||
fi = new FileInfo(path);
|
||||
}
|
||||
}
|
||||
File.AppendAllText(path, JsonConvert.SerializeObject(jsonError) + Environment.NewLine);
|
||||
exception = exception.InnerException;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Remotely_ScreenCast_Linux.X11
|
||||
{
|
||||
public class X11Interop
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -1 +1 @@
|
||||
2019.03.25.1255
|
||||
2019.03.26.2309
|
||||
|
||||
@ -57,8 +57,8 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="2.10.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.2" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.2" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.3" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.3" PrivateAssets="All" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.2.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="4.5.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -11,7 +11,8 @@ if [ "$1" = "--uninstall" ]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
apt-get install unzip
|
||||
apt-get -y install unzip
|
||||
apt-get -y install libgdiplus
|
||||
|
||||
mkdir -p /usr/local/bin/Remotely/
|
||||
cd /usr/local/bin/Remotely/
|
||||
@ -48,9 +49,11 @@ WorkingDirectory=/usr/local/bin/Remotely/
|
||||
ExecStart=/usr/local/bin/Remotely/Remotely_Agent
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment=DISPLAY=:0
|
||||
Environment="XAUTHORITY=/home/$USER/.Xauthority"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
WantedBy=graphical.target
|
||||
EOL
|
||||
|
||||
systemctl enable remotely-agent
|
||||
|
||||
@ -106,10 +106,12 @@ export function ApplyInputHandlers(sockets) {
|
||||
ConnectionBar.classList.remove("open");
|
||||
RemoteControl.RCBrowserSockets.SendCtrlAltDel();
|
||||
});
|
||||
document.querySelector("#sessionIDInput, #nameInput").addEventListener("keypress", (ev) => {
|
||||
if (ev.key.toLowerCase() == "enter") {
|
||||
ConnectToClient();
|
||||
}
|
||||
document.querySelectorAll("#sessionIDInput, #nameInput").forEach(x => {
|
||||
x.addEventListener("keypress", (ev) => {
|
||||
if (ev.key.toLowerCase() == "enter") {
|
||||
ConnectToClient();
|
||||
}
|
||||
});
|
||||
});
|
||||
document.querySelector("#connectButton").addEventListener("click", (ev) => {
|
||||
ConnectToClient();
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -114,10 +114,12 @@ export function ApplyInputHandlers(sockets: RCBrowserSockets) {
|
||||
ConnectionBar.classList.remove("open");
|
||||
RemoteControl.RCBrowserSockets.SendCtrlAltDel();
|
||||
});
|
||||
document.querySelector("#sessionIDInput, #nameInput").addEventListener("keypress", (ev: KeyboardEvent) => {
|
||||
if (ev.key.toLowerCase() == "enter") {
|
||||
ConnectToClient();
|
||||
}
|
||||
document.querySelectorAll("#sessionIDInput, #nameInput").forEach(x => {
|
||||
x.addEventListener("keypress", (ev: KeyboardEvent) => {
|
||||
if (ev.key.toLowerCase() == "enter") {
|
||||
ConnectToClient();
|
||||
}
|
||||
})
|
||||
});
|
||||
document.querySelector("#connectButton").addEventListener("click", (ev) => {
|
||||
ConnectToClient();
|
||||
|
||||
@ -69,7 +69,7 @@ if ($ArgList.Contains("c")) {
|
||||
if ((Test-Path -Path ".\Remotely_Agent\bin\Release\netcoreapp2.2\linux-x64\publish") -eq $true) {
|
||||
Get-ChildItem -Path ".\Remotely_Agent\bin\Release\netcoreapp2.2\linux-x64\publish" | Remove-Item -Force -Recurse
|
||||
}
|
||||
|
||||
|
||||
Push-Location -Path ".\Remotely_Agent"
|
||||
|
||||
# Publish Core clients.
|
||||
@ -81,15 +81,24 @@ if ($ArgList.Contains("c")) {
|
||||
|
||||
New-Item -Path ".\Remotely_Agent\bin\Release\netcoreapp2.2\win10-x64\publish\ScreenCast\" -ItemType Directory -Force
|
||||
New-Item -Path ".\Remotely_Agent\bin\Release\netcoreapp2.2\win10-x86\publish\ScreenCast\" -ItemType Directory -Force
|
||||
New-Item -Path ".\Remotely_Agent\bin\Release\netcoreapp2.2\linux-x64\publish\ScreenCast\" -ItemType Directory -Force
|
||||
|
||||
# Copy .NET Framework ScreenCaster to Agent output.
|
||||
if ((Test-Path -Path ".\Remotely_ScreenCast\bin\Release\Remotely_ScreenCast.exe") -eq $true) {
|
||||
Copy-Item -Path ".\Remotely_ScreenCast\bin\Release\Remotely_ScreenCast.exe" -Destination ".\Remotely_Agent\bin\Release\netcoreapp2.2\win10-x64\publish\ScreenCast\Remotely_ScreenCast.exe" -Force
|
||||
Copy-Item -Path ".\Remotely_ScreenCast\bin\Release\Remotely_ScreenCast.exe" -Destination ".\Remotely_Agent\bin\Release\netcoreapp2.2\win10-x86\publish\ScreenCast\Remotely_ScreenCast.exe" -Force
|
||||
# Copy .NET Framework ScreenCaster to Agent output folder.
|
||||
if ((Test-Path -Path ".\Remotely_ScreenCast.Win\bin\Release\Remotely_ScreenCast.exe") -eq $true) {
|
||||
Copy-Item -Path ".\Remotely_ScreenCast.Win\bin\Release\Remotely_ScreenCast.exe" -Destination ".\Remotely_Agent\bin\Release\netcoreapp2.2\win10-x64\publish\ScreenCast\Remotely_ScreenCast.exe" -Force
|
||||
Copy-Item -Path ".\Remotely_ScreenCast.Win\bin\Release\Remotely_ScreenCast.exe" -Destination ".\Remotely_Agent\bin\Release\netcoreapp2.2\win10-x86\publish\ScreenCast\Remotely_ScreenCast.exe" -Force
|
||||
}
|
||||
elseif ((Test-Path -Path ".\Remotely_ScreenCast\bin\Debug\Remotely_ScreenCast.exe") -eq $true) {
|
||||
Copy-Item -Path ".\Remotely_ScreenCast\bin\Debug\Remotely_ScreenCast.exe" -Destination ".\Remotely_Agent\bin\Release\netcoreapp2.2\win10-x64\publish\ScreenCast\Remotely_ScreenCast.exe" -Force
|
||||
Copy-Item -Path ".\Remotely_ScreenCast\bin\Debug\Remotely_ScreenCast.exe" -Destination ".\Remotely_Agent\bin\Release\netcoreapp2.2\win10-x86\publish\ScreenCast\Remotely_ScreenCast.exe" -Force
|
||||
elseif ((Test-Path -Path ".\Remotely_ScreenCast.Win\bin\Debug\Remotely_ScreenCast.exe") -eq $true) {
|
||||
Copy-Item -Path ".\Remotely_ScreenCast.Win\bin\Debug\Remotely_ScreenCast.exe" -Destination ".\Remotely_Agent\bin\Release\netcoreapp2.2\win10-x64\publish\ScreenCast\Remotely_ScreenCast.exe" -Force
|
||||
Copy-Item -Path ".\Remotely_ScreenCast.Win\bin\Debug\Remotely_ScreenCast.exe" -Destination ".\Remotely_Agent\bin\Release\netcoreapp2.2\win10-x86\publish\ScreenCast\Remotely_ScreenCast.exe" -Force
|
||||
}
|
||||
|
||||
# Copy Mono ScreenCaster to Agent output folder.
|
||||
if ((Test-Path -Path ".\Remotely_ScreenCast.Mono\bin\Release\Remotely_ScreenCast.Mono.exe") -eq $true) {
|
||||
Copy-Item -Path ".\Remotely_ScreenCast.Mono\bin\Release\Remotely_ScreenCast.Mono.exe" -Destination ".\Remotely_Agent\bin\Release\netcoreapp2.2\linux-x64\publish\ScreenCast\Remotely_ScreenCast.Mono.exe" -Force
|
||||
}
|
||||
elseif ((Test-Path -Path ".\Remotely_ScreenCast.Mono\bin\Debug\Remotely_ScreenCast.Mono.exe") -eq $true) {
|
||||
Copy-Item -Path ".\Remotely_ScreenCast.Mono\bin\Debug\Remotely_ScreenCast.Mono.exe" -Destination ".\Remotely_Agent\bin\Release\netcoreapp2.2\linux-x64\publish\ScreenCast\Remotely_ScreenCast.Mono.exe" -Force
|
||||
}
|
||||
|
||||
# Compress Core clients.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user