mirror of
https://github.com/immense/Remotely.git
synced 2025-10-26 11:27:15 +00:00
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:
parent
aaab92e4d1
commit
1ea295f8cb
@ -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.");
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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 () =>
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -76,7 +76,7 @@ namespace Remotely.ScreenCast.Core.Capture
|
||||
// continue;
|
||||
//}
|
||||
|
||||
while (viewer.PendingFrames > 10)
|
||||
while (viewer.PendingFrames > 10 || conductor.IsSwitchingDesktops)
|
||||
{
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
2019.06.23.1129
|
||||
2019.06.27.2150
|
||||
|
||||
@ -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>© @DateTime.Now.Year - Translucency Software</p>
|
||||
|
||||
@ -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="&">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=""">'</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>
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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/
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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%;
|
||||
}
|
||||
|
||||
@ -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
@ -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.");
|
||||
}
|
||||
});
|
||||
|
||||
@ -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
|
||||
@ -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"}
|
||||
@ -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");
|
||||
})
|
||||
});
|
||||
@ -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
@ -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) => {
|
||||
|
||||
@ -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
@ -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`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user