Clipboard transfer. Unattended remote session survives Windows signout. Removed HTML-based on-screen keyboard and instead invoke native mobile keyboard. Ubuntu server install script detects version for grabbing .NET Core runtime.

This commit is contained in:
Jared Goodwin 2019-06-27 22:04:05 -07:00
parent aaab92e4d1
commit 1ea295f8cb
32 changed files with 701 additions and 653 deletions

View File

@ -304,15 +304,43 @@ namespace Remotely.Agent.Services
var rcBinaryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ScreenCast", OSUtils.ScreenCastExecutableFileName);
// Start ScreenCast.
if (OSUtils.IsWindows)
{
{
Logger.Write("Restarting screen caster.");
if (Program.IsDebug)
{
Process.Start(rcBinaryPath, $"-mode Unattended -requester {requesterID} -serviceid {serviceID} -host {Utilities.GetConnectionInfo().Host} -relaunch true -desktop default -viewers {String.Join(",", viewerIDs)}");
var proc = Process.Start(rcBinaryPath, $"-mode Unattended -requester {requesterID} -serviceid {serviceID} -host {Utilities.GetConnectionInfo().Host} -relaunch true -desktop default -viewers {String.Join(",", viewerIDs)}");
var stopwatch = Stopwatch.StartNew();
while (stopwatch.Elapsed.TotalSeconds < 10)
{
await Task.Delay(250);
if (Process.GetProcessesByName(Path.GetFileNameWithoutExtension(rcBinaryPath))?.Where(x => x.Id == proc.Id)?.Count() > 0 != true)
{
Logger.Write("Restarting screen caster after failed relaunch.");
}
}
}
else
{
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 _);
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 var procInfo);
if (result)
{
// This relaunch might have been prompted by a user logging out, which would close
// the screencaster process. In that scenario, the relaunched process can get closed again
// while the Windows sign-out process is still occurring. We'll wait a bit to make sure the
// relaunched process is still running. If not, launch again.
var stopwatch = Stopwatch.StartNew();
while (stopwatch.Elapsed.TotalSeconds < 10)
{
await Task.Delay(250);
if (Process.GetProcessesByName(Path.GetFileNameWithoutExtension(rcBinaryPath))?.Where(x=>x.Id == procInfo.dwProcessId)?.Count() > 0 != true)
{
Logger.Write("Restarting screen caster after failed relaunch.");
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 procInfo);
}
}
}
if (!result)
{
Logger.Write("Failed to relaunch screen caster.");

View File

@ -14,7 +14,6 @@ namespace Remotely.Agent.Services
{
public class Updater
{
internal static void CheckForCoreUpdates()
{
try
@ -88,8 +87,10 @@ namespace Remotely.Agent.Services
foreach (var proc in Process.GetProcesses().Where(x =>
x.ProcessName.Contains("Remotely_Agent") &&
x.Id != Process.GetCurrentProcess().Id))
(x.ProcessName.Contains("Remotely_Agent") ||
x.ProcessName.Contains("Remotely_ScreenCast") ||
x.ProcessName.Contains("Remotely_Desktop"))
&& x.Id != Process.GetCurrentProcess().Id))
{
proc.Kill();
}

View File

@ -15,7 +15,9 @@ using Remotely.Shared.Services;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -37,10 +39,10 @@ namespace Remotely.Desktop.Unix.ViewModels
Conductor.SessionIDChanged += SessionIDChanged;
Conductor.ViewerRemoved += ViewerRemoved;
Conductor.ViewerAdded += ViewerAdded;
Conductor.ClipboardTransferred += Conductor_ClipboardTransferred;
Conductor.ScreenCastRequested += ScreenCastRequested;
}
public static MainWindowViewModel Current { get; private set; }
public ICommand ChangeServerCommand => new Executor(async (param) =>
@ -70,16 +72,19 @@ namespace Remotely.Desktop.Unix.ViewModels
}
IsCopyMessageVisible = false;
});
public double CopyMessageOpacity
{
get => copyMessageOpacity;
set => this.RaiseAndSetIfChanged(ref copyMessageOpacity, value);
}
public string Host
{
get => host;
set => this.RaiseAndSetIfChanged(ref host, value);
}
public bool IsCopyMessageVisible
{
get => isCopyMessageVisible;
@ -106,6 +111,7 @@ namespace Remotely.Desktop.Unix.ViewModels
get => sessionID;
set => this.RaiseAndSetIfChanged(ref sessionID, value);
}
public ObservableCollection<Viewer> Viewers { get; } = new ObservableCollection<Viewer>();
public async Task Init()
@ -139,7 +145,7 @@ namespace Remotely.Desktop.Unix.ViewModels
{
return;
}
await Conductor.CasterSocket.SendDeviceInfo(Conductor.ServiceID, Environment.MachineName);
await Conductor.CasterSocket.GetSessionID();
}
@ -167,8 +173,29 @@ namespace Remotely.Desktop.Unix.ViewModels
}
}
private void Conductor_ClipboardTransferred(object sender, string transferredText)
{
var tempPath = Path.GetTempFileName();
File.WriteAllText(tempPath, transferredText);
try
{
var psi = new ProcessStartInfo("bash", $"-c \"cat {tempPath} | xclip -i -selection clipboard\"")
{
WindowStyle = ProcessWindowStyle.Hidden
};
var proc = Process.Start(psi);
proc.WaitForExit();
}
catch (Exception ex)
{
Logger.Write(ex);
}
finally
{
File.Delete(tempPath);
}
}
private void ScreenCastRequested(object sender, ScreenCastRequest screenCastRequest)
{
Dispatcher.UIThread.InvokeAsync(async () =>

View File

@ -40,26 +40,16 @@ namespace Remotely.Desktop.Win.ViewModels
Conductor.ViewerAdded += ViewerAdded;
Conductor.ScreenCastRequested += ScreenCastRequested;
Conductor.AudioToggled += AudioToggled;
Conductor.ClipboardTransferred += Conductor_ClipboardTransferred;
CursorIconWatcher = new CursorIconWatcher(Conductor);
CursorIconWatcher.OnChange += CursorIconWatcher_OnChange;
AudioCapturer = new AudioCapturer(Conductor);
}
private void AudioToggled(object sender, bool toggleOn)
{
if (toggleOn)
{
AudioCapturer.Start();
}
else
{
AudioCapturer.Stop();
}
}
public static MainWindowViewModel Current { get; private set; }
public AudioCapturer AudioCapturer { get; private set; }
public ICommand ChangeServerCommand
{
get
@ -73,7 +63,9 @@ namespace Remotely.Desktop.Win.ViewModels
}
public Conductor Conductor { get; }
public CursorIconWatcher CursorIconWatcher { get; private set; }
public string Host
{
get => host;
@ -85,6 +77,27 @@ namespace Remotely.Desktop.Win.ViewModels
}
public bool IsAdministrator => new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
public ICommand RemoveViewersCommand
{
get
{
return new Executor(async (param) =>
{
foreach (Viewer viewer in (param as IList<object>))
{
viewer.DisconnectRequested = true;
await Conductor.CasterSocket.SendViewerRemoved(viewer.ViewerConnectionID);
}
},
(param) =>
{
return (param as IList<object>)?.Count > 0;
});
}
}
public ICommand RestartAsAdminCommand
{
get
@ -100,7 +113,8 @@ namespace Remotely.Desktop.Win.ViewModels
}
// Exception can be thrown if UAC is dialog is cancelled.
catch { }
}, (param) => {
}, (param) =>
{
return !IsAdministrator;
});
}
@ -117,6 +131,12 @@ namespace Remotely.Desktop.Win.ViewModels
}
public ObservableCollection<Viewer> Viewers { get; } = new ObservableCollection<Viewer>();
public void CopyLink()
{
Clipboard.SetText($"{Host}/RemoteControl?sessionID={SessionID.Replace(" ", "")}");
}
public async Task Init()
{
SessionID = "Retrieving...";
@ -146,6 +166,7 @@ namespace Remotely.Desktop.Win.ViewModels
await Conductor.CasterSocket.SendDeviceInfo(Conductor.ServiceID, Environment.MachineName);
await Conductor.CasterSocket.GetSessionID();
}
public void PromptForHostName()
{
var prompt = new HostNamePrompt();
@ -169,31 +190,24 @@ namespace Remotely.Desktop.Win.ViewModels
}
}
public void CopyLink()
private void AudioToggled(object sender, bool toggleOn)
{
Clipboard.SetText($"{Host}/RemoteControl?sessionID={SessionID.Replace(" ", "")}");
}
public ICommand RemoveViewersCommand
{
get
if (toggleOn)
{
return new Executor(async (param) =>
{
foreach (Viewer viewer in (param as IList<object>))
{
viewer.DisconnectRequested = true;
await Conductor.CasterSocket.SendViewerRemoved(viewer.ViewerConnectionID);
}
},
(param) =>
{
return (param as IList<object>)?.Count > 0;
});
AudioCapturer.Start();
}
else
{
AudioCapturer.Stop();
}
}
private void Conductor_ClipboardTransferred(object sender, string transferredText)
{
Application.Current.Dispatcher.Invoke(() => {
Clipboard.SetText(transferredText);
});
}
private async void CursorIconWatcher_OnChange(object sender, CursorInfo cursor)
{
if (Conductor?.CasterSocket != null)

View File

@ -41,10 +41,10 @@ The following steps will configure your Windows 10 machine for building the Remo
* Documentation for hosting in IIS can be found here: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/index?view=aspnetcore-2.2
## Hosting a Server (Ubuntu)
* Currently, only Ubuntu 18.04 is tested. The Linux server package will likely work with other distros after some alterations to the setup script.
* Ubuntu 18.04 and 19.04 have been tested. The Linux server package might work with other distros after some alterations to the setup script.
* Run Remotely_Server_Setup.sh (with sudo), which is in the [Utilities folder in source control](https://raw.githubusercontent.com/Jay-Rad/Remotely/master/Utilities/Remotely_Server_Install.sh).
* "App root" will be the directory in which the Remotely server files are placed (typically /var/www/remotely).
* This script is only for Ubuntu 18.04.
* This script is only for Ubuntu 18.04 and 19.04.
* The script installs the .NET Core runtime, as well as other dependencies.
* Certbot is used in this script and will install an SSL certificate for your site. Your server needs to have a public domain name that is accessible from the internet for this to work.
* More information: https://letsencrypt.org/, https://certbot.eff.org/
@ -67,7 +67,7 @@ The following steps will configure your Windows 10 machine for building the Remo
* Windows: Only the latest version of Windows 10 is tested.
* Requires .NET Framework 4.7.2.
* Windows 2016/2019 should work as well, but isn't tested regularly.
* Linux: Only Lubuntu 18.10 is tested.
* Linux: Only Ubuntu 18.04+ is tested.
* Your account must be set to auto login for unattended remote control to work.
## Session Recording

View File

@ -76,7 +76,7 @@ namespace Remotely.ScreenCast.Core.Capture
// continue;
//}
while (viewer.PendingFrames > 10)
while (viewer.PendingFrames > 10 || conductor.IsSwitchingDesktops)
{
await Task.Delay(1);
}

View File

@ -19,6 +19,8 @@ namespace Remotely.ScreenCast.Core
{
public event EventHandler<bool> AudioToggled;
public event EventHandler<string> ClipboardTransferred;
public event EventHandler<ScreenCastRequest> ScreenCastInitiated;
public event EventHandler<ScreenCastRequest> ScreenCastRequested;
@ -37,6 +39,7 @@ namespace Remotely.ScreenCast.Core
public string RequesterID { get; private set; }
public string ServiceID { get; private set; }
public ConcurrentDictionary<string, Viewer> Viewers { get; } = new ConcurrentDictionary<string, Viewer>();
public bool IsSwitchingDesktops { get; set; }
public Task Connect()
{
@ -108,6 +111,12 @@ namespace Remotely.ScreenCast.Core
{
ScreenCastInitiated?.Invoke(null, viewerIdAndRequesterName);
}
internal void InvokeClipboardTransfer(string transferText)
{
ClipboardTransferred?.Invoke(null, transferText);
}
internal void InvokeScreenCastRequested(ScreenCastRequest viewerIdAndRequesterName)
{
ScreenCastRequested?.Invoke(null, viewerIdAndRequesterName);

View File

@ -26,9 +26,63 @@ namespace Remotely.ScreenCast.Core.Sockets
ApplyConnectionHandlers();
}
private HubConnection Connection { get; }
public Conductor Conductor { get; }
public IKeyboardMouseInput KeyboardMouseInput { get; }
private HubConnection Connection { get; }
public async Task GetSessionID()
{
await Connection.SendAsync("GetSessionID");
}
public async Task NotifyRequesterUnattendedReady(string requesterID)
{
await Connection.SendAsync("NotifyRequesterUnattendedReady", requesterID);
}
public async Task NotifyViewersRelaunchedScreenCasterReady(string[] viewerIDs)
{
await Connection.SendAsync("NotifyViewersRelaunchedScreenCasterReady", viewerIDs);
}
public async Task SendAudioSample(byte[] buffer, List<string> viewerIDs)
{
await Connection.SendAsync("SendAudioSample", buffer, viewerIDs);
}
public async Task SendConnectionFailedToViewers(List<string> viewerIDs)
{
await Connection.SendAsync("SendConnectionFailedToViewers", viewerIDs);
}
public async Task SendCursorChange(CursorInfo cursor, List<string> viewerIDs)
{
await Connection.SendAsync("SendCursorChange", cursor, viewerIDs);
}
public async Task SendDeviceInfo(string serviceID, string machineName)
{
await Connection.SendAsync("ReceiveDeviceInfo", serviceID, machineName);
}
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);
}
public async Task SendScreenCount(int primaryScreenIndex, int screenCount, string viewerID)
{
await Connection.SendAsync("SendScreenCountToBrowser", primaryScreenIndex, screenCount, viewerID);
}
public async Task SendScreenSize(int width, int height, string viewerID)
{
await Connection.SendAsync("SendScreenSize", width, height, viewerID);
}
public async Task SendViewerRemoved(string viewerID)
{
await Connection.SendAsync("SendViewerRemoved", viewerID);
}
private void ApplyConnectionHandlers()
{
@ -39,6 +93,18 @@ namespace Remotely.ScreenCast.Core.Sockets
return Task.CompletedTask;
};
Connection.On("ClipboardTransfer", (string transferText) =>
{
try
{
Conductor.InvokeClipboardTransfer(transferText);
}
catch (Exception ex)
{
Logger.Write(ex);
}
});
Connection.On("GetScreenCast", (string viewerID, string requesterName) =>
{
try
@ -210,7 +276,8 @@ namespace Remotely.ScreenCast.Core.Sockets
KeyboardMouseInput.SendLeftMouseUp(percentX, percentY, viewer);
}
});
Connection.On("SharedFileIDs", (List<string> fileIDs) => {
Connection.On("SharedFileIDs", (List<string> fileIDs) =>
{
fileIDs.ForEach(id =>
{
var url = $"{Conductor.Host}/API/FileSharing/{id}";
@ -244,60 +311,5 @@ namespace Remotely.ScreenCast.Core.Sockets
Conductor.InvokeSessionIDChanged(sessionID);
});
}
public async Task SendAudioSample(byte[] buffer, List<string> viewerIDs)
{
await Connection.SendAsync("SendAudioSample", buffer, viewerIDs);
}
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);
}
public 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);
}
public async Task NotifyViewersRelaunchedScreenCasterReady(string[] viewerIDs)
{
await Connection.SendAsync("NotifyViewersRelaunchedScreenCasterReady", viewerIDs);
}
public async Task SendDeviceInfo(string serviceID, string machineName)
{
await Connection.SendAsync("ReceiveDeviceInfo", serviceID, machineName);
}
public async Task SendConnectionFailedToViewers(List<string> viewerIDs)
{
await Connection.SendAsync("SendConnectionFailedToViewers", viewerIDs);
}
public async Task GetSessionID()
{
await Connection.SendAsync("GetSessionID");
}
public async Task SendViewerRemoved(string viewerID)
{
await Connection.SendAsync("SendViewerRemoved", viewerID);
}
}
}

View File

@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Threading.Tasks;
using System.IO;
namespace Remotely.ScreenCast.Linux
{
@ -26,6 +27,7 @@ namespace Remotely.ScreenCast.Linux
Conductor.Connect().Wait();
Conductor.SetMessageHandlers(new X11Input());
Conductor.ScreenCastInitiated += ScreenCastInitiated;
Conductor.ClipboardTransferred += Conductor_ClipboardTransferred;
Conductor.CasterSocket.SendDeviceInfo(Conductor.ServiceID, Environment.MachineName).Wait();
Conductor.CasterSocket.NotifyRequesterUnattendedReady(Conductor.RequesterID).Wait();
Conductor.StartWaitForViewerTimer();
@ -41,6 +43,30 @@ namespace Remotely.ScreenCast.Linux
}
}
private static void Conductor_ClipboardTransferred(object sender, string transferredText)
{
var tempPath = Path.GetTempFileName();
File.WriteAllText(tempPath, transferredText);
try
{
var psi = new ProcessStartInfo("bash", $"-c \"cat {tempPath} | xclip -i -selection clipboard\"")
{
WindowStyle = ProcessWindowStyle.Hidden
};
var proc = Process.Start(psi);
proc.WaitForExit();
}
catch (Exception ex)
{
Logger.Write(ex);
}
finally
{
File.Delete(tempPath);
}
}
private static async void ScreenCastInitiated(object sender, ScreenCastRequest screenCastRequest)
{
try

View File

@ -22,14 +22,50 @@ using System.Threading.Tasks;
using System.Windows.Forms;
using Remotely.Shared.Win32;
using NAudio.Wave;
using Remotely.Shared.Services;
using System.Runtime.InteropServices;
using System.Threading;
namespace Remotely.ScreenCast.Win
{
public class Program
{
public static AudioCapturer AudioCapturer { get; private set; }
public static Conductor Conductor { get; private set; }
public static CursorIconWatcher CursorIconWatcher { get; private set; }
public static AudioCapturer AudioCapturer { get; private set; }
public static async void CursorIconWatcher_OnChange(object sender, CursorInfo cursor)
{
if (Conductor?.CasterSocket != null)
{
await Conductor.CasterSocket.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}.");
conductor.IsSwitchingDesktops = true;
// TODO: SetThreadDesktop 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.CasterSocket.SendConnectionFailedToViewers(conductor.Viewers.Keys.ToList()).Wait();
}
}
await Task.Delay(100);
}
}
public static void Main(string[] args)
{
@ -42,6 +78,7 @@ namespace Remotely.ScreenCast.Win
Conductor.SetMessageHandlers(new WinInput());
Conductor.ScreenCastInitiated += ScreenCastInitiated;
Conductor.AudioToggled += AudioToggled;
Conductor.ClipboardTransferred += Conductor_ClipboardTransferred;
AudioCapturer = new AudioCapturer(Conductor);
CursorIconWatcher = new CursorIconWatcher(Conductor);
CursorIconWatcher.OnChange += CursorIconWatcher_OnChange;
@ -86,6 +123,41 @@ namespace Remotely.ScreenCast.Win
}
}
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 Conductor_ClipboardTransferred(object sender, string transferredText)
{
var thread = new Thread(() => {
Clipboard.SetText(transferredText);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Logger.Write((Exception)e.ExceptionObject);
}
private static async void ScreenCastInitiated(object sender, ScreenCastRequest screenCastRequest)
{
ICapturer capturer;
@ -108,63 +180,5 @@ namespace Remotely.ScreenCast.Win
await Conductor.CasterSocket.SendCursorChange(CursorIconWatcher.GetCurrentCursor(), new List<string>() { screenCastRequest.ViewerID });
ScreenCaster.BeginScreenCasting(screenCastRequest.ViewerID, screenCastRequest.RequesterName, capturer, Conductor);
}
public static async void CursorIconWatcher_OnChange(object sender, CursorInfo cursor)
{
if (Conductor?.CasterSocket != null)
{
await Conductor.CasterSocket.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: SetThreadDesktop 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.CasterSocket.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);
}
}
}

View File

@ -1 +1 @@
2019.06.23.1129
2019.06.27.2150

View File

@ -52,6 +52,7 @@
</div>
<div>
<button id="clipboardTransferButton" class="option-button">Clipboard <i class="fas fa-clipboard"></i></button>
<button id="inviteButton" class="option-button">Invite Others <i class="fas fa-user-plus"></i></button>
<button id="audioButton" class="option-button">Audio <i class="fas fa-volume-up"></i></button>
<button id="fileTransferButton" class="option-button">File Transfer <i class="fas fa-file-upload"></i></button>
@ -78,12 +79,19 @@
</div>
<div id="qualityBar" class="horizontal-button-bar">
<div class="center-aligned" style="color:white; font-size:12px">
<div style="color:white; font-size:12px">
Image Quality
</div>
<input id="qualityRangeInput" value="50" max="100" class="form-control-range" type="range" />
</div>
<div id="clipboardTransferBar" class="horizontal-button-bar">
<div style="color:white; font-size:12px">
Paste to Transfer
</div>
<textarea id="clipboardTransferTextArea"></textarea>
</div>
<div id="connectBox">
<h3>Connect to a client:</h3>
<div class="form-block">
@ -108,10 +116,9 @@
<div class="center-aligned">
<canvas id="screenViewer" hidden="hidden"></canvas>
<input id="fileTransferInput" hidden="hidden" type="file" />
<input id="touchKeyboardInput" type="text" />
<textarea id="touchKeyboardTextArea" value=" #"> #</textarea>
</div>
<partial name="_OSK.cshtml" />
<footer>
<div class="footer-wrapper">
<p>&copy; @DateTime.Now.Year - Translucency Software</p>

View File

@ -1,74 +0,0 @@
<link rel="stylesheet" href="~/css/osk.css" asp-append-version="true" />
<div id="osk" class="osk-wrapper">
<div class="osk-row right-aligned">
<button id="closeOSKButton">X</button>
</div>
<div class="osk-row">
<div class="osk-key" alt-key="~">`</div>
<div class="osk-key" alt-key="!">1</div>
<div class="osk-key" alt-key="@@">2</div>
<div class="osk-key" alt-key="#">3</div>
<div class="osk-key" alt-key="$">4</div>
<div class="osk-key" alt-key="%">5</div>
<div class="osk-key" alt-key="^">6</div>
<div class="osk-key" alt-key="&amp;">7</div>
<div class="osk-key" alt-key="*">8</div>
<div class="osk-key" alt-key="(">9</div>
<div class="osk-key" alt-key=")">0</div>
<div class="osk-key" alt-key="_">-</div>
<div class="osk-key" alt-key="+">=</div>
</div>
<div class="osk-row">
<div class="osk-key" alt-key="Q">q</div>
<div class="osk-key" alt-key="W">w</div>
<div class="osk-key" alt-key="E">e</div>
<div class="osk-key" alt-key="R">r</div>
<div class="osk-key" alt-key="T">t</div>
<div class="osk-key" alt-key="Y">y</div>
<div class="osk-key" alt-key="U">u</div>
<div class="osk-key" alt-key="I">i</div>
<div class="osk-key" alt-key="O">o</div>
<div class="osk-key" alt-key="P">p</div>
<div class="osk-key" alt-key="{">[</div>
<div class="osk-key" alt-key="}">]</div>
<div class="osk-key" alt-key="|">\</div>
</div>
<div class="osk-row">
<div class="osk-key" alt-key="A">a</div>
<div class="osk-key" alt-key="S">s</div>
<div class="osk-key" alt-key="D">d</div>
<div class="osk-key" alt-key="F">f</div>
<div class="osk-key" alt-key="G">g</div>
<div class="osk-key" alt-key="H">h</div>
<div class="osk-key" alt-key="J">j</div>
<div class="osk-key" alt-key="K">k</div>
<div class="osk-key" alt-key="L">l</div>
<div class="osk-key" alt-key=":">;</div>
<div class="osk-key" alt-key="&quot;">'</div>
<div class="osk-key wide-key-sm">Enter</div>
</div>
<div class="osk-row">
<div id="shiftKey" class="osk-key wide-key-sm">Shift</div>
<div class="osk-key" alt-key="Z">z</div>
<div class="osk-key" alt-key="X">x</div>
<div class="osk-key" alt-key="C">c</div>
<div class="osk-key" alt-key="V">v</div>
<div class="osk-key" alt-key="B">b</div>
<div class="osk-key" alt-key="N">n</div>
<div class="osk-key" alt-key="M">m</div>
<div class="osk-key" alt-key="<">,</div>
<div class="osk-key" alt-key=">">.</div>
<div class="osk-key" alt-key="?">/</div>
</div>
<div class="osk-row">
<div id="ctrlKey" class="osk-key">Ctrl</div>
<div id="altKey" class="osk-key">Alt</div>
<div class="osk-key wide-key-lg">Space</div>
<div class="osk-key">Left</div>
<div class="osk-key">Up</div>
<div class="osk-key">Down</div>
<div class="osk-key">Right</div>
<div class="osk-key">Backspace</div>
</div>
</div>
<script src="~/scripts/RemoteControl/OSK.js" type="module"></script>

View File

@ -22,8 +22,8 @@ namespace Remotely.Server.Services
}
public static ConcurrentDictionary<string, Device> ServiceConnections { get; set; } = new ConcurrentDictionary<string, Device>();
private IHubContext<BrowserSocketHub> BrowserHub { get; }
public IHubContext<RCBrowserSocketHub> RCBrowserHub { get; }
private IHubContext<BrowserSocketHub> BrowserHub { get; }
private DataService DataService { get; }
private Device Device
{
@ -58,13 +58,8 @@ namespace Remotely.Server.Services
await BrowserHub.Clients.Client(commandContext.SenderConnectionID).SendAsync("CommandResult", result);
}
public async Task DisplayConsoleMessage(string message, string requesterID)
{
await BrowserHub.Clients.Client(requesterID).SendAsync("DisplayConsoleMessage", message);
}
public async Task DeviceCameOnline(Device device)
{
public async Task DeviceCameOnline(Device device)
{
try
{
if (ServiceConnections.Any(x => x.Value.ID == device.ID))
@ -107,18 +102,22 @@ namespace Remotely.Server.Services
{
DataService.WriteEvent(ex);
throw;
}
}
}
}
public async Task DeviceHeartbeat(Device device)
{
device.IsOnline = true;
device.LastOnline = DateTime.Now;
Device = device;
DataService.AddOrUpdateDevice(device);
await BrowserHub.Clients.Group(Device.OrganizationID).SendAsync("DeviceHeartbeat", Device);
}
public async Task DeviceHeartbeat(Device device)
{
device.IsOnline = true;
device.LastOnline = DateTime.Now;
Device = device;
DataService.AddOrUpdateDevice(device);
await BrowserHub.Clients.Group(Device.OrganizationID).SendAsync("DeviceHeartbeat", Device);
}
public async Task DisplayConsoleMessage(string message, string requesterID)
{
await BrowserHub.Clients.Client(requesterID).SendAsync("DisplayConsoleMessage", message);
}
public override Task OnConnectedAsync()
{
return base.OnConnectedAsync();
@ -153,8 +152,13 @@ namespace Remotely.Server.Services
await BrowserHub.Clients.Client(commandContext.SenderConnectionID).SendAsync("PSCoreResultViaAjax", commandID, Device.ID);
}
public async Task SendServerVerificationToken()
{
public async Task SendConnectionFailedToViewers(List<string> viewerIDs)
{
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("ConnectionFailed");
}
public async Task SendServerVerificationToken()
{
await Clients.Caller.SendAsync("ServerVerificationToken", Device.ServerVerificationToken);
}
public void SetServerVerificationToken(string verificationToken)
@ -172,9 +176,5 @@ namespace Remotely.Server.Services
var commandContext = DataService.GetCommandContext(commandID);
await BrowserHub.Clients.Client(commandContext.SenderConnectionID).SendAsync("WinPSResultViaAjax", commandID, Device.ID);
}
public async Task SendConnectionFailedToViewers(List<string> viewerIDs)
{
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("ConnectionFailed");
}
}
}

View File

@ -199,6 +199,10 @@ namespace Remotely.Server.Services
{
await RCDeviceHub.Clients.Client(ScreenCasterID).SendAsync("ToggleAudio", toggleOn);
}
public async Task SendClipboardTransfer(string transferText)
{
await RCDeviceHub.Clients.Client(ScreenCasterID).SendAsync("ClipboardTransfer", transferText);
}
public async Task Tap(double percentX, double percentY)
{
await RCDeviceHub.Clients.Client(ScreenCasterID).SendAsync("Tap", percentX, percentY, Context.ConnectionId);

View File

@ -14,13 +14,9 @@ namespace Remotely.Server.Services
{
public class RCDeviceSocketHub : Hub
{
public static ConcurrentDictionary<string, string> AttendedSessionList { get; set; } = new ConcurrentDictionary<string, string>();
public static ConcurrentDictionary<string, string> MachineNameToSessionIDLookup { get; set; } = new ConcurrentDictionary<string, string>();
public RCDeviceSocketHub(DataService dataService,
IHubContext<BrowserSocketHub> browserHub,
IHubContext<RCBrowserSocketHub> rcBrowserHub,
public RCDeviceSocketHub(DataService dataService,
IHubContext<BrowserSocketHub> browserHub,
IHubContext<RCBrowserSocketHub> rcBrowserHub,
IHubContext<DeviceSocketHub> deviceSocketHub,
RemoteControlSessionRecorder rcSessionRecorder,
ApplicationConfig appConfig)
@ -32,48 +28,13 @@ namespace Remotely.Server.Services
RCSessionRecorder = rcSessionRecorder;
AppConfig = appConfig;
}
private IHubContext<DeviceSocketHub> DeviceHub { get; }
public RemoteControlSessionRecorder RCSessionRecorder { get; }
public static ConcurrentDictionary<string, string> AttendedSessionList { get; set; } = new ConcurrentDictionary<string, string>();
public static ConcurrentDictionary<string, string> MachineNameToSessionIDLookup { get; set; } = new ConcurrentDictionary<string, string>();
public ApplicationConfig AppConfig { get; }
private DataService DataService { get; }
public RemoteControlSessionRecorder RCSessionRecorder { get; }
private IHubContext<BrowserSocketHub> BrowserHub { get; }
private IHubContext<RCBrowserSocketHub> RCBrowserHub { get; }
private string ServiceID
{
get
{
if (Context.Items.ContainsKey("ServiceID"))
{
return Context.Items["ServiceID"] as string;
}
else
{
return null;
}
}
set
{
Context.Items["ServiceID"] = value;
}
}
private string MachineName
{
get
{
if (Context.Items.ContainsKey("MachineName"))
{
return Context.Items["MachineName"] as string;
}
else
{
return null;
}
}
set
{
Context.Items["MachineName"] = value;
}
}
private Size CurrentScreenSize
{
get
@ -93,6 +54,46 @@ namespace Remotely.Server.Services
}
}
private DataService DataService { get; }
private IHubContext<DeviceSocketHub> DeviceHub { get; }
private string MachineName
{
get
{
if (Context.Items.ContainsKey("MachineName"))
{
return Context.Items["MachineName"] as string;
}
else
{
return null;
}
}
set
{
Context.Items["MachineName"] = value;
}
}
private IHubContext<RCBrowserSocketHub> RCBrowserHub { get; }
private string ServiceID
{
get
{
if (Context.Items.ContainsKey("ServiceID"))
{
return Context.Items["ServiceID"] as string;
}
else
{
return null;
}
}
set
{
Context.Items["ServiceID"] = value;
}
}
private DateTime StartTime
{
get
@ -124,6 +125,34 @@ namespace Remotely.Server.Services
}
}
public async Task GetSessionID()
{
var random = new Random();
var sessionID = "";
for (var i = 0; i < 3; i++)
{
sessionID += random.Next(0, 999).ToString().PadLeft(3, '0');
}
Context.Items["SessionID"] = sessionID;
while (!AttendedSessionList.TryAdd(sessionID, Context.ConnectionId))
{
await Task.Delay(1000);
}
await Clients.Caller.SendAsync("SessionID", sessionID);
}
public async Task NotifyRequesterUnattendedReady(string browserHubConnectionID)
{
await BrowserHub.Clients.Client(browserHubConnectionID).SendAsync("UnattendedSessionReady", Context.ConnectionId);
}
public async Task NotifyViewersRelaunchedScreenCasterReady(string[] viewerIDs)
{
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("RelaunchedScreenCasterReady", Context.ConnectionId);
}
public override Task OnConnectedAsync()
{
StartTime = DateTime.Now;
@ -163,31 +192,19 @@ namespace Remotely.Server.Services
MachineName = machineName;
MachineNameToSessionIDLookup[MachineName] = Context.ConnectionId;
}
public void ViewerDisconnected(string viewerID)
{
lock (ViewerList)
{
ViewerList.Remove(viewerID);
}
}
public async Task SendScreenCountToBrowser(int primaryScreenIndex, int screenCount, string rcBrowserHubConnectionID)
{
lock (ViewerList)
{
ViewerList.Add(rcBrowserHubConnectionID);
}
await RCBrowserHub.Clients.Client(rcBrowserHubConnectionID).SendAsync("ScreenCount", primaryScreenIndex, screenCount);
}
public async Task SendAudioSample(byte[] buffer, List<string> viewerIDs)
{
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("AudioSample", buffer);
}
public async Task SendScreenSize(int width, int height, string rcBrowserHubConnectionID)
public async Task SendConnectionFailedToViewers(List<string> viewerIDs)
{
CurrentScreenSize = new Size(width, height);
await RCBrowserHub.Clients.Client(rcBrowserHubConnectionID).SendAsync("ScreenSize", width, height);
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("ConnectionFailed");
}
public async Task SendCursorChange(CursorInfo cursor, List<string> viewerIDs)
{
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("CursorChange", cursor);
}
public Task SendScreenCapture(byte[] captureBytes, string rcBrowserHubConnectionID, int left, int top, int width, int height, DateTime captureTime)
@ -200,28 +217,19 @@ namespace Remotely.Server.Services
return RCBrowserHub.Clients.Client(rcBrowserHubConnectionID).SendAsync("ScreenCapture", captureBytes, left, top, width, height, captureTime);
}
public async Task NotifyRequesterUnattendedReady(string browserHubConnectionID)
public async Task SendScreenCountToBrowser(int primaryScreenIndex, int screenCount, string rcBrowserHubConnectionID)
{
await BrowserHub.Clients.Client(browserHubConnectionID).SendAsync("UnattendedSessionReady", Context.ConnectionId);
}
public async Task NotifyViewersRelaunchedScreenCasterReady(string[] viewerIDs)
{
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("RelaunchedScreenCasterReady", Context.ConnectionId);
}
public async Task SendConnectionFailedToViewers(List<string> viewerIDs)
{
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("ConnectionFailed");
lock (ViewerList)
{
ViewerList.Add(rcBrowserHubConnectionID);
}
await RCBrowserHub.Clients.Client(rcBrowserHubConnectionID).SendAsync("ScreenCount", primaryScreenIndex, screenCount);
}
public async Task SendCursorChange(CursorInfo cursor, List<string> viewerIDs)
public async Task SendScreenSize(int width, int height, string rcBrowserHubConnectionID)
{
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("CursorChange", cursor);
}
public async Task SwitchingDesktops(string[] viewerIDs)
{
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("SwitchingDesktops");
CurrentScreenSize = new Size(width, height);
await RCBrowserHub.Clients.Client(rcBrowserHubConnectionID).SendAsync("ScreenSize", width, height);
}
public async Task SendViewerRemoved(string viewerID)
@ -229,22 +237,17 @@ namespace Remotely.Server.Services
await RCBrowserHub.Clients.Clients(viewerID).SendAsync("ViewerRemoved");
}
public async Task GetSessionID()
public async Task SwitchingDesktops(string[] viewerIDs)
{
var random = new Random();
var sessionID = "";
for (var i = 0; i < 3; i++)
{
sessionID += random.Next(0, 999).ToString().PadLeft(3, '0');
}
Context.Items["SessionID"] = sessionID;
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("SwitchingDesktops");
}
while (!AttendedSessionList.TryAdd(sessionID, Context.ConnectionId))
public void ViewerDisconnected(string viewerID)
{
lock (ViewerList)
{
await Task.Delay(1000);
ViewerList.Remove(viewerID);
}
await Clients.Caller.SendAsync("SessionID", sessionID);
}
}
}

View File

@ -16,6 +16,7 @@ apt-get -y install unzip
apt-get -y install libc6-dev
apt-get -y install libgdiplus
apt-get -y install libxtst-dev
apt-get -y install xclip
mkdir -p /usr/local/bin/Remotely/
cd /usr/local/bin/Remotely/

View File

@ -1,58 +0,0 @@
.osk-wrapper {
position: fixed;
top: calc(100% + 250px);
left: 50%;
transform: translate(-50%, -100%);
background-color: rgb(30, 30, 30);
padding: 5px 20px 20px 20px;
border-radius: 30px;
transition: .5s top;
white-space: nowrap;
z-index: 1;
-ms-touch-action: manipulation;
touch-action: manipulation;
}
.osk-wrapper.open {
top: 100%;
}
.osk-key {
display: inline-block;
padding: 5px;
min-height: 15px;
min-width: 20px;
margin: 2px;
background-color: rgb(60, 60, 60);
color: white;
border: 1px solid #e3e3e3;
border-radius: 3px;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
text-align: center;
cursor: pointer;
transition: 50ms background-color;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
font-size: .8em;
}
.osk-key.toggled {
background-color: rgb(120, 120, 120);
}
.osk-key.wide-key-lg {
width: 125px;
}
.osk-key.wide-key-sm {
width: 75px;
}
.osk-key:hover {
background-color: rgb(20, 20, 20);
}
#closeOSKButton {
margin-bottom:10px;
}

View File

@ -125,10 +125,12 @@ button[disabled] {
border-radius: 5px;
overflow: hidden;
white-space: nowrap;
text-align: center;
}
.horizontal-button-bar.open {
height: 40px;
height: auto;
min-height: 40px;
padding: 10px;
transition: all 250ms;
}
@ -287,7 +289,7 @@ footer a {
margin-left: 10px;
}
#touchKeyboardInput {
#touchKeyboardTextArea {
position: fixed;
top: -100%;
}

View File

@ -23,7 +23,7 @@ export function Connect() {
this.Connection.closedCallbacks.push((ev) => {
Connected = false;
if (!Store.IsDisconnectExpected) {
UI.ShowModal("Connection Failure", "Your connection was lost. Refresh the page or enter a command to reconnect.");
UI.ShowModal("Connection Failure", "Your connection was lost. Click Reconnect to start a new session.", `<button type="button" class="btn btn-secondary" onclick="location.reload()">Reconnect</button>`);
UI.AddConsoleOutput("Connection lost.");
}
});

File diff suppressed because one or more lines are too long

View File

@ -33,7 +33,9 @@ export function Connect() {
this.Connection.closedCallbacks.push((ev) => {
Connected = false;
if (!Store.IsDisconnectExpected) {
UI.ShowModal("Connection Failure", "Your connection was lost. Refresh the page or enter a command to reconnect.");
UI.ShowModal("Connection Failure",
"Your connection was lost. Click Reconnect to start a new session.",
`<button type="button" class="btn btn-secondary" onclick="location.reload()">Reconnect</button>`);
UI.AddConsoleOutput("Connection lost.");
}
});

View File

@ -1,35 +0,0 @@
import { RemoteControl } from "./RemoteControl.js";
import * as UI from "./UI.js";
window.addEventListener("load", (ev) => {
document.querySelectorAll(".osk-key").forEach(key => {
key.addEventListener("click", (ev) => {
if (key.id == "shiftKey") {
document.querySelectorAll('[alt-key]').forEach(dualKey => {
var newVal = dualKey.getAttribute("alt-key");
var oldVal = dualKey.innerHTML;
dualKey.innerHTML = newVal;
dualKey.setAttribute("alt-key", oldVal);
});
}
if (key.id == "shiftKey" || key.id == "ctrlKey" || key.id == "altKey") {
key.classList.toggle("toggled");
if (key.classList.contains("toggled")) {
RemoteControl.RCBrowserSockets.SendKeyDown(key.innerHTML);
}
else {
RemoteControl.RCBrowserSockets.SendKeyUp(key.innerHTML);
}
}
else if (key.innerHTML == "Space") {
RemoteControl.RCBrowserSockets.SendKeyPress(" ");
}
else {
RemoteControl.RCBrowserSockets.SendKeyPress(key.innerHTML);
}
});
});
document.getElementById("closeOSKButton").addEventListener("click", () => {
UI.OnScreenKeyboard.classList.toggle("open");
});
});
//# sourceMappingURL=OSK.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"OSK.js","sourceRoot":"","sources":["OSK.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,EAAE,EAAC,EAAE;IAClC,QAAQ,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,GAAG,CAAA,EAAE;QAC/C,GAAG,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,EAAE,EAAC,EAAE;YAChC,IAAI,GAAG,CAAC,EAAE,IAAI,UAAU,EAAE;gBACtB,QAAQ,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,OAAO,CAAA,EAAE;oBACpD,IAAI,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;oBAC7C,IAAI,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;oBAC/B,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC;oBAC3B,OAAO,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;gBAC5C,CAAC,CAAC,CAAA;aACL;YACD,IAAI,GAAG,CAAC,EAAE,IAAI,UAAU,IAAI,GAAG,CAAC,EAAE,IAAI,SAAS,IAAI,GAAG,CAAC,EAAE,IAAI,QAAQ,EAAC;gBAClE,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAChC,IAAI,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE;oBACnC,aAAa,CAAC,gBAAgB,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;iBAC7D;qBACI;oBACD,aAAa,CAAC,gBAAgB,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;iBAC3D;aACJ;iBACI,IAAI,GAAG,CAAC,SAAS,IAAI,OAAO,EAAE;gBAC/B,aAAa,CAAC,gBAAgB,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;aACpD;iBACI;gBACD,aAAa,CAAC,gBAAgB,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;aAC9D;QACL,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAA;IACF,QAAQ,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;QACrE,EAAE,CAAC,gBAAgB,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACjD,CAAC,CAAC,CAAA;AACN,CAAC,CAAC,CAAC"}

View File

@ -1,35 +0,0 @@
import { RemoteControl } from "./RemoteControl.js";
import * as UI from "./UI.js";
window.addEventListener("load", (ev)=>{
document.querySelectorAll(".osk-key").forEach(key=>{
key.addEventListener("click", (ev)=>{
if (key.id == "shiftKey") {
document.querySelectorAll('[alt-key]').forEach(dualKey=>{
var newVal = dualKey.getAttribute("alt-key");
var oldVal = dualKey.innerHTML;
dualKey.innerHTML = newVal;
dualKey.setAttribute("alt-key", oldVal);
})
}
if (key.id == "shiftKey" || key.id == "ctrlKey" || key.id == "altKey"){
key.classList.toggle("toggled");
if (key.classList.contains("toggled")) {
RemoteControl.RCBrowserSockets.SendKeyDown(key.innerHTML);
}
else {
RemoteControl.RCBrowserSockets.SendKeyUp(key.innerHTML);
}
}
else if (key.innerHTML == "Space") {
RemoteControl.RCBrowserSockets.SendKeyPress(" ");
}
else {
RemoteControl.RCBrowserSockets.SendKeyPress(key.innerHTML);
}
});
})
document.getElementById("closeOSKButton").addEventListener("click", () => {
UI.OnScreenKeyboard.classList.toggle("open");
})
});

View File

@ -85,6 +85,10 @@ export class RCBrowserSockets {
SendToggleAudio(toggleOn) {
this.Connection.invoke("SendToggleAudio", toggleOn);
}
;
SendClipboardTransfer(text) {
this.Connection.invoke("SendClipboardTransfer", text);
}
ApplyMessageHandlers(hubConnection) {
hubConnection.on("ScreenCount", (primaryScreenIndex, screenCount) => {
document.querySelector("#screenSelectBar").innerHTML = "";

File diff suppressed because one or more lines are too long

View File

@ -92,6 +92,9 @@ export class RCBrowserSockets {
}
SendToggleAudio(toggleOn: boolean) {
this.Connection.invoke("SendToggleAudio", toggleOn);
};
SendClipboardTransfer(text: string) {
this.Connection.invoke("SendClipboardTransfer", text);
}
private ApplyMessageHandlers(hubConnection) {
hubConnection.on("ScreenCount", (primaryScreenIndex: number, screenCount: number) => {

View File

@ -21,52 +21,77 @@ export var ChangeScreenButton = document.getElementById("changeScreenButton");
export var QualityButton = document.getElementById("qualityButton");
export var FitToScreenButton = document.getElementById("fitToScreenButton");
export var DisconnectButton = document.getElementById("disconnectButton");
export var OnScreenKeyboard = document.getElementById("osk");
export var FileTransferInput = document.getElementById("fileTransferInput");
export var FileTransferProgress = document.getElementById("fileTransferProgress");
export var KeyboardButton = document.getElementById("keyboardButton");
export var InviteButton = document.getElementById("inviteButton");
export var FileTransferButton = document.getElementById("fileTransferButton");
export var CtrlAltDelButton = document.getElementById("ctrlAltDelButton");
export var TouchKeyboardTextArea = document.getElementById("touchKeyboardTextArea");
export var ClipboardTransferBar = document.getElementById("clipboardTransferBar");
export var ClipboardTransferTextArea = document.getElementById("clipboardTransferTextArea");
export var ClipboardTransferButton = document.getElementById("clipboardTransferButton");
var lastPointerMove = Date.now();
var isDragging;
var currentPointerDevice;
var currentTouchCount;
var cancelNextClick;
var cancelNextViewerClick;
var isPinchZooming;
var startPinchPoint1;
var startPinchPoint2;
var isMenuButtonDragging;
var startMenuDraggingY;
var ignoreNextMenuClick;
export function ApplyInputHandlers(sockets) {
MenuButton.addEventListener("click", (ev) => {
if (ignoreNextMenuClick) {
ignoreNextMenuClick = false;
return;
}
MenuFrame.classList.toggle("open");
MenuButton.classList.toggle("open");
closeAllHorizontalBars(null);
});
MenuButton.addEventListener("mousedown", (ev) => {
isMenuButtonDragging = true;
ignoreNextMenuClick = false;
startMenuDraggingY = ev.clientY;
window.addEventListener("mousemove", moveMenuButton);
window.addEventListener("mouseup", removeMouseButtonWindowListeners);
window.addEventListener("mouseleave", removeMouseButtonWindowListeners);
});
MenuButton.addEventListener("touchmove", (ev) => {
MenuButton.style.top = `${ev.touches[0].clientY}px`;
AudioButton.addEventListener("click", (ev) => {
AudioButton.classList.toggle("toggled");
var toggleOn = AudioButton.classList.contains("toggled");
sockets.SendToggleAudio(toggleOn);
});
ChangeScreenButton.addEventListener("click", (ev) => {
closeAllHorizontalBars("screenSelectBar");
ScreenSelectBar.classList.toggle("open");
});
QualityButton.addEventListener("click", (ev) => {
closeAllHorizontalBars("qualityBar");
QualityBar.classList.toggle("open");
ClipboardTransferButton.addEventListener("click", (ev) => {
closeAllHorizontalBars("clipboardTransferBar");
ClipboardTransferBar.classList.toggle("open");
});
ClipboardTransferTextArea.addEventListener("input", (ev) => {
if (ClipboardTransferTextArea.value.length == 0) {
return;
}
sockets.SendClipboardTransfer(ClipboardTransferTextArea.value);
ClipboardTransferTextArea.value = "";
ClipboardTransferTextArea.blur();
ClipboardTransferBar.classList.remove("open");
FloatMessage("Clipboard sent!");
});
ConnectButton.addEventListener("click", (ev) => {
ConnectToClient();
});
CtrlAltDelButton.addEventListener("click", (ev) => {
if (!RemoteControl.ServiceID) {
ShowMessage("Not available for this session.");
return;
}
closeAllHorizontalBars(null);
RemoteControl.RCBrowserSockets.SendCtrlAltDel();
});
DisconnectButton.addEventListener("click", (ev) => {
ConnectButton.removeAttribute("disabled");
RemoteControl.RCBrowserSockets.Connection.stop();
});
document.querySelectorAll("#sessionIDInput, #nameInput").forEach(x => {
x.addEventListener("keypress", (ev) => {
if (ev.key.toLowerCase() == "enter") {
ConnectToClient();
}
});
});
FileTransferButton.addEventListener("click", (ev) => {
FileTransferInput.click();
});
FileTransferInput.addEventListener("change", (ev) => {
uploadFiles(FileTransferInput.files);
});
FitToScreenButton.addEventListener("click", (ev) => {
var button = ev.currentTarget;
@ -80,19 +105,6 @@ export function ApplyInputHandlers(sockets) {
ScreenViewer.style.maxHeight = "unset";
}
});
AudioButton.addEventListener("click", (ev) => {
AudioButton.classList.toggle("toggled");
var toggleOn = AudioButton.classList.contains("toggled");
sockets.SendToggleAudio(toggleOn);
});
DisconnectButton.addEventListener("click", (ev) => {
ConnectButton.removeAttribute("disabled");
RemoteControl.RCBrowserSockets.Connection.stop();
});
KeyboardButton.addEventListener("click", (ev) => {
closeAllHorizontalBars(null);
OnScreenKeyboard.classList.toggle("open");
});
InviteButton.addEventListener("click", (ev) => {
var url = "";
if (RemoteControl.Mode == RemoteControlMode.Normal) {
@ -112,29 +124,35 @@ export function ApplyInputHandlers(sockets) {
input.remove();
FloatMessage("Link copied to clipboard.");
});
FileTransferButton.addEventListener("click", (ev) => {
FileTransferInput.click();
KeyboardButton.addEventListener("click", (ev) => {
closeAllHorizontalBars(null);
TouchKeyboardTextArea.focus();
TouchKeyboardTextArea.setSelectionRange(TouchKeyboardTextArea.value.length, TouchKeyboardTextArea.value.length);
MenuFrame.classList.remove("open");
MenuButton.classList.remove("open");
});
FileTransferInput.addEventListener("change", (ev) => {
uploadFiles(FileTransferInput.files);
});
CtrlAltDelButton.addEventListener("click", (ev) => {
if (!RemoteControl.ServiceID) {
ShowMessage("Not available for this session.");
MenuButton.addEventListener("click", (ev) => {
if (isMenuButtonDragging) {
isMenuButtonDragging = false;
return;
}
MenuFrame.classList.toggle("open");
MenuButton.classList.toggle("open");
closeAllHorizontalBars(null);
RemoteControl.RCBrowserSockets.SendCtrlAltDel();
});
document.querySelectorAll("#sessionIDInput, #nameInput").forEach(x => {
x.addEventListener("keypress", (ev) => {
if (ev.key.toLowerCase() == "enter") {
ConnectToClient();
}
});
MenuButton.addEventListener("mousedown", (ev) => {
isMenuButtonDragging = false;
startMenuDraggingY = ev.clientY;
window.addEventListener("mousemove", moveMenuButton);
window.addEventListener("mouseup", removeMouseButtonWindowListeners);
window.addEventListener("mouseleave", removeMouseButtonWindowListeners);
});
ConnectButton.addEventListener("click", (ev) => {
ConnectToClient();
MenuButton.addEventListener("touchmove", (ev) => {
MenuButton.style.top = `${ev.touches[0].clientY}px`;
});
QualityButton.addEventListener("click", (ev) => {
closeAllHorizontalBars("qualityBar");
QualityBar.classList.toggle("open");
});
QualitySlider.addEventListener("change", (ev) => {
sockets.SendQualityChange(Number(QualitySlider.value));
@ -183,8 +201,8 @@ export function ApplyInputHandlers(sockets) {
sockets.SendMouseUp(e.button, percentX, percentY);
});
ScreenViewer.addEventListener("click", function (e) {
if (cancelNextClick) {
cancelNextClick = false;
if (cancelNextViewerClick) {
cancelNextViewerClick = false;
return;
}
if (currentPointerDevice == "mouse") {
@ -208,7 +226,7 @@ export function ApplyInputHandlers(sockets) {
});
ScreenViewer.addEventListener("touchstart", function (e) {
if (e.touches.length > 1) {
cancelNextClick = true;
cancelNextViewerClick = true;
}
if (e.touches.length == 2) {
startPinchPoint1 = { X: e.touches[0].pageX, Y: e.touches[0].pageY, IsEmpty: false };
@ -251,7 +269,7 @@ export function ApplyInputHandlers(sockets) {
return;
}
if (currentTouchCount == 0) {
cancelNextClick = false;
cancelNextViewerClick = false;
isPinchZooming = false;
startPinchPoint1 = null;
startPinchPoint2 = null;
@ -270,15 +288,44 @@ export function ApplyInputHandlers(sockets) {
e.preventDefault();
sockets.SendMouseWheel(e.deltaX, e.deltaY);
});
TouchKeyboardTextArea.addEventListener("input", (ev) => {
if (TouchKeyboardTextArea.value.length == 1) {
sockets.SendKeyPress("Backspace");
}
else if (TouchKeyboardTextArea.value.endsWith("\n")) {
sockets.SendKeyPress("Enter");
}
else if (TouchKeyboardTextArea.value.endsWith(" ")) {
sockets.SendKeyPress(" ");
}
else {
var input = TouchKeyboardTextArea.value.trim().substr(1);
for (var i = 0; i < input.length; i++) {
var character = input.charAt(i);
var sendShift = character.match(/[A-Z~!@#$%^&*()_+{}|<>?]/);
if (sendShift) {
sockets.SendKeyDown("Shift");
}
sockets.SendKeyPress(character);
if (sendShift) {
sockets.SendKeyUp("Shift");
}
}
}
window.setTimeout(() => {
TouchKeyboardTextArea.value = " #";
TouchKeyboardTextArea.setSelectionRange(TouchKeyboardTextArea.value.length, TouchKeyboardTextArea.value.length);
});
});
window.addEventListener("keydown", function (e) {
if (document.querySelector("input:focus")) {
if (document.querySelector("input:focus") || document.querySelector("textarea:focus")) {
return;
}
e.preventDefault();
sockets.SendKeyDown(e.key);
});
window.addEventListener("keyup", function (e) {
if (document.querySelector("input:focus")) {
if (document.querySelector("input:focus") || document.querySelector("textarea:focus")) {
return;
}
e.preventDefault();
@ -296,15 +343,6 @@ export function ApplyInputHandlers(sockets) {
uploadFiles(e.dataTransfer.files);
};
}
export function ShowMessage(message) {
var messageDiv = document.createElement("div");
messageDiv.classList.add("float-message");
messageDiv.innerHTML = message;
document.body.appendChild(messageDiv);
window.setTimeout(() => {
messageDiv.remove();
}, 5000);
}
export function Prompt(promptMessage) {
return new Promise((resolve, reject) => {
var modalDiv = document.createElement("div");
@ -334,6 +372,15 @@ export function Prompt(promptMessage) {
};
});
}
export function ShowMessage(message) {
var messageDiv = document.createElement("div");
messageDiv.classList.add("float-message");
messageDiv.innerHTML = message;
document.body.appendChild(messageDiv);
window.setTimeout(() => {
messageDiv.remove();
}, 5000);
}
function uploadFiles(fileList) {
ShowMessage("File upload started...");
FileTransferProgress.value = 0;
@ -376,7 +423,7 @@ function moveMenuButton(ev) {
if (ev.clientY < 0 || ev.clientY > window.innerHeight) {
return;
}
ignoreNextMenuClick = true;
isMenuButtonDragging = true;
MenuButton.style.top = `${ev.clientY}px`;
}
}

File diff suppressed because one or more lines are too long

View File

@ -25,59 +25,80 @@ export var ChangeScreenButton = document.getElementById("changeScreenButton") as
export var QualityButton = document.getElementById("qualityButton") as HTMLButtonElement;
export var FitToScreenButton = document.getElementById("fitToScreenButton") as HTMLButtonElement;
export var DisconnectButton = document.getElementById("disconnectButton") as HTMLButtonElement;
export var OnScreenKeyboard = document.getElementById("osk") as HTMLDivElement;
export var FileTransferInput = document.getElementById("fileTransferInput") as HTMLInputElement;
export var FileTransferProgress = document.getElementById("fileTransferProgress") as HTMLProgressElement;
export var KeyboardButton = document.getElementById("keyboardButton") as HTMLButtonElement;
export var InviteButton = document.getElementById("inviteButton") as HTMLButtonElement;
export var FileTransferButton = document.getElementById("fileTransferButton") as HTMLButtonElement;
export var CtrlAltDelButton = document.getElementById("ctrlAltDelButton") as HTMLButtonElement;
export var TouchKeyboardTextArea = document.getElementById("touchKeyboardTextArea") as HTMLTextAreaElement;
export var ClipboardTransferBar = document.getElementById("clipboardTransferBar") as HTMLDivElement;
export var ClipboardTransferTextArea = document.getElementById("clipboardTransferTextArea") as HTMLTextAreaElement;
export var ClipboardTransferButton = document.getElementById("clipboardTransferButton") as HTMLButtonElement;
var lastPointerMove = Date.now();
var isDragging: boolean;
var currentPointerDevice: string;
var currentTouchCount: number;
var cancelNextClick: boolean;
var cancelNextViewerClick: boolean;
var isPinchZooming: boolean;
var startPinchPoint1: Point;
var startPinchPoint2: Point;
var isMenuButtonDragging: boolean;
var startMenuDraggingY: number;
var ignoreNextMenuClick: boolean;
export function ApplyInputHandlers(sockets: RCBrowserSockets) {
MenuButton.addEventListener("click", (ev) => {
if (ignoreNextMenuClick) {
ignoreNextMenuClick = false;
return;
}
MenuFrame.classList.toggle("open");
MenuButton.classList.toggle("open");
closeAllHorizontalBars(null);
AudioButton.addEventListener("click", (ev) => {
AudioButton.classList.toggle("toggled");
var toggleOn = AudioButton.classList.contains("toggled");
sockets.SendToggleAudio(toggleOn);
});
MenuButton.addEventListener("mousedown", (ev) => {
isMenuButtonDragging = true;
ignoreNextMenuClick = false;
startMenuDraggingY = ev.clientY;
window.addEventListener("mousemove", moveMenuButton);
window.addEventListener("mouseup", removeMouseButtonWindowListeners);
window.addEventListener("mouseleave", removeMouseButtonWindowListeners);
});
MenuButton.addEventListener("touchmove", (ev) => {
MenuButton.style.top = `${ev.touches[0].clientY}px`;
});
ChangeScreenButton.addEventListener("click", (ev) => {
closeAllHorizontalBars("screenSelectBar");
ScreenSelectBar.classList.toggle("open");
})
QualityButton.addEventListener("click", (ev) => {
closeAllHorizontalBars("qualityBar");
QualityBar.classList.toggle("open");
})
});
ClipboardTransferButton.addEventListener("click", (ev) => {
closeAllHorizontalBars("clipboardTransferBar");
ClipboardTransferBar.classList.toggle("open");
});
ClipboardTransferTextArea.addEventListener("input", (ev) => {
if (ClipboardTransferTextArea.value.length == 0) {
return;
}
sockets.SendClipboardTransfer(ClipboardTransferTextArea.value);
ClipboardTransferTextArea.value = "";
ClipboardTransferTextArea.blur();
ClipboardTransferBar.classList.remove("open");
FloatMessage("Clipboard sent!");
});
ConnectButton.addEventListener("click", (ev) => {
ConnectToClient();
});
CtrlAltDelButton.addEventListener("click", (ev) => {
if (!RemoteControl.ServiceID) {
ShowMessage("Not available for this session.");
return;
}
closeAllHorizontalBars(null);
RemoteControl.RCBrowserSockets.SendCtrlAltDel();
});
DisconnectButton.addEventListener("click", (ev) => {
ConnectButton.removeAttribute("disabled");
RemoteControl.RCBrowserSockets.Connection.stop();
});
document.querySelectorAll("#sessionIDInput, #nameInput").forEach(x => {
x.addEventListener("keypress", (ev: KeyboardEvent) => {
if (ev.key.toLowerCase() == "enter") {
ConnectToClient();
}
})
});
FileTransferButton.addEventListener("click", (ev) => {
FileTransferInput.click();
});
FileTransferInput.addEventListener("change", (ev) => {
uploadFiles(FileTransferInput.files);
});
FitToScreenButton.addEventListener("click", (ev) => {
var button = ev.currentTarget as HTMLButtonElement;
button.classList.toggle("toggled");
@ -90,22 +111,9 @@ export function ApplyInputHandlers(sockets: RCBrowserSockets) {
ScreenViewer.style.maxHeight = "unset";
}
})
AudioButton.addEventListener("click", (ev) => {
AudioButton.classList.toggle("toggled");
var toggleOn = AudioButton.classList.contains("toggled");
sockets.SendToggleAudio(toggleOn);
})
DisconnectButton.addEventListener("click", (ev) => {
ConnectButton.removeAttribute("disabled");
RemoteControl.RCBrowserSockets.Connection.stop();
});
KeyboardButton.addEventListener("click", (ev) => {
closeAllHorizontalBars(null);
OnScreenKeyboard.classList.toggle("open");
});
InviteButton.addEventListener("click", (ev) => {
var url = "";
if (RemoteControl.Mode == RemoteControlMode.Normal) {
if (RemoteControl.Mode == RemoteControlMode.Normal) {
url = `${location.origin}${location.pathname}?sessionID=${RemoteControl.ClientID}`;
}
else {
@ -121,33 +129,38 @@ export function ApplyInputHandlers(sockets: RCBrowserSockets) {
document.execCommand("copy", false, location.href);
input.remove();
FloatMessage("Link copied to clipboard.");
})
FileTransferButton.addEventListener("click", (ev) => {
FileTransferInput.click();
});
FileTransferInput.addEventListener("change", (ev) => {
uploadFiles(FileTransferInput.files);
KeyboardButton.addEventListener("click", (ev) => {
closeAllHorizontalBars(null);
TouchKeyboardTextArea.focus();
TouchKeyboardTextArea.setSelectionRange(TouchKeyboardTextArea.value.length, TouchKeyboardTextArea.value.length);
MenuFrame.classList.remove("open");
MenuButton.classList.remove("open");
});
CtrlAltDelButton.addEventListener("click", (ev) => {
if (!RemoteControl.ServiceID) {
ShowMessage("Not available for this session.");
MenuButton.addEventListener("click", (ev) => {
if (isMenuButtonDragging) {
isMenuButtonDragging = false;
return;
}
MenuFrame.classList.toggle("open");
MenuButton.classList.toggle("open");
closeAllHorizontalBars(null);
RemoteControl.RCBrowserSockets.SendCtrlAltDel();
});
document.querySelectorAll("#sessionIDInput, #nameInput").forEach(x => {
x.addEventListener("keypress", (ev: KeyboardEvent) => {
if (ev.key.toLowerCase() == "enter") {
ConnectToClient();
}
})
MenuButton.addEventListener("mousedown", (ev) => {
isMenuButtonDragging = false;
startMenuDraggingY = ev.clientY;
window.addEventListener("mousemove", moveMenuButton);
window.addEventListener("mouseup", removeMouseButtonWindowListeners);
window.addEventListener("mouseleave", removeMouseButtonWindowListeners);
});
ConnectButton.addEventListener("click", (ev) => {
ConnectToClient();
MenuButton.addEventListener("touchmove", (ev) => {
MenuButton.style.top = `${ev.touches[0].clientY}px`;
});
QualityButton.addEventListener("click", (ev) => {
closeAllHorizontalBars("qualityBar");
QualityBar.classList.toggle("open");
})
QualitySlider.addEventListener("change", (ev) => {
sockets.SendQualityChange(Number(QualitySlider.value));
})
@ -196,8 +209,8 @@ export function ApplyInputHandlers(sockets: RCBrowserSockets) {
});
ScreenViewer.addEventListener("click", function (e) {
if (cancelNextClick) {
cancelNextClick = false;
if (cancelNextViewerClick) {
cancelNextViewerClick = false;
return;
}
if (currentPointerDevice == "mouse") {
@ -222,7 +235,7 @@ export function ApplyInputHandlers(sockets: RCBrowserSockets) {
ScreenViewer.addEventListener("touchstart", function (e) {
if (e.touches.length > 1) {
cancelNextClick = true;
cancelNextViewerClick = true;
}
if (e.touches.length == 2) {
startPinchPoint1 = { X: e.touches[0].pageX, Y: e.touches[0].pageY, IsEmpty: false };
@ -269,7 +282,7 @@ export function ApplyInputHandlers(sockets: RCBrowserSockets) {
}
if (currentTouchCount == 0) {
cancelNextClick = false;
cancelNextViewerClick = false;
isPinchZooming = false;
startPinchPoint1 = null;
startPinchPoint2 = null;
@ -289,16 +302,48 @@ export function ApplyInputHandlers(sockets: RCBrowserSockets) {
ScreenViewer.addEventListener("wheel", function (e) {
e.preventDefault();
sockets.SendMouseWheel(e.deltaX, e.deltaY);
})
});
TouchKeyboardTextArea.addEventListener("input", (ev) => {
if (TouchKeyboardTextArea.value.length == 1) {
sockets.SendKeyPress("Backspace");
}
else if (TouchKeyboardTextArea.value.endsWith("\n")) {
sockets.SendKeyPress("Enter");
}
else if (TouchKeyboardTextArea.value.endsWith(" ")) {
sockets.SendKeyPress(" ");
}
else {
var input = TouchKeyboardTextArea.value.trim().substr(1);
for (var i = 0; i < input.length; i++) {
var character = input.charAt(i);
var sendShift = character.match(/[A-Z~!@#$%^&*()_+{}|<>?]/);
if (sendShift) {
sockets.SendKeyDown("Shift");
}
sockets.SendKeyPress(character);
if (sendShift) {
sockets.SendKeyUp("Shift");
}
}
}
window.setTimeout(() => {
TouchKeyboardTextArea.value = " #";
TouchKeyboardTextArea.setSelectionRange(TouchKeyboardTextArea.value.length, TouchKeyboardTextArea.value.length);
});
});
window.addEventListener("keydown", function (e) {
if (document.querySelector("input:focus")) {
if (document.querySelector("input:focus") || document.querySelector("textarea:focus")) {
return;
}
e.preventDefault();
sockets.SendKeyDown(e.key);
});
window.addEventListener("keyup", function (e) {
if (document.querySelector("input:focus")) {
if (document.querySelector("input:focus") || document.querySelector("textarea:focus")) {
return;
}
e.preventDefault();
@ -318,16 +363,6 @@ export function ApplyInputHandlers(sockets: RCBrowserSockets) {
};
}
export function ShowMessage(message: string) {
var messageDiv = document.createElement("div");
messageDiv.classList.add("float-message");
messageDiv.innerHTML = message;
document.body.appendChild(messageDiv);
window.setTimeout(() => {
messageDiv.remove();
}, 5000);
}
export function Prompt(promptMessage: string): Promise<string> {
return new Promise((resolve, reject) => {
var modalDiv = document.createElement("div");
@ -367,6 +402,16 @@ export function Prompt(promptMessage: string): Promise<string> {
});
}
export function ShowMessage(message: string) {
var messageDiv = document.createElement("div");
messageDiv.classList.add("float-message");
messageDiv.innerHTML = message;
document.body.appendChild(messageDiv);
window.setTimeout(() => {
messageDiv.remove();
}, 5000);
}
function uploadFiles(fileList: FileList) {
ShowMessage("File upload started...");
FileTransferProgress.value = 0;
@ -413,7 +458,7 @@ function moveMenuButton(ev: MouseEvent) {
if (ev.clientY < 0 || ev.clientY > window.innerHeight) {
return;
}
ignoreNextMenuClick = true;
isMenuButtonDragging = true;
MenuButton.style.top = `${ev.clientY}px`;
}
}

View File

@ -1,13 +1,15 @@
#!/bin/bash
echo "Thanks for trying remotely! If you have any questions, feel free to email me at Translucency_Software@outlook.com."
echo
read -p "Enter app root path (typically /var/www/remotely): " appRoot
read -p "Enter server host (e.g. example.com): " serverHost
read -p "Enter path where the Remotely server will be installed (typically /var/www/remotely): " appRoot
read -p "Enter server host (e.g. remotely.yourdomainname.com): " serverHost
UbuntuVersion=$(lsb_release -r -s)
apt-get update
# Install .NET Core Runtime.
wget -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb
wget -q https://packages.microsoft.com/config/ubuntu/$UbuntuVersion/packages-microsoft-prod.deb
dpkg -i packages-microsoft-prod.deb
add-apt-repository universe
apt-get install -y apt-transport-https