Added bi-directional clipboard sharing.

This commit is contained in:
Jared Goodwin 2019-12-16 15:07:46 -08:00
parent e5b86ebd18
commit cf14ad8781
19 changed files with 297 additions and 157 deletions

View File

@ -9,6 +9,7 @@ using Remotely.ScreenCast.Core.Capture;
using Remotely.ScreenCast.Core.Interfaces;
using Remotely.ScreenCast.Core.Models;
using Remotely.ScreenCast.Core.Services;
using Remotely.ScreenCast.Core.Sockets;
using Remotely.ScreenCast.Linux.Capture;
using Remotely.ScreenCast.Linux.Services;
using Remotely.Shared.Models;
@ -36,11 +37,9 @@ namespace Remotely.Desktop.Unix.ViewModels
public MainWindowViewModel()
{
Current = this;
Conductor = new Conductor(
new X11Input(),
new LinuxAudioCapturer(),
new LinuxClipboardService(),
new LinuxScreenCaster());
var screenCaster = new LinuxScreenCaster();
var casterSocket = new CasterSocket(new X11Input(), screenCaster, new LinuxAudioCapturer(), new LinuxClipboardService());
Conductor = new Conductor(casterSocket, screenCaster);
Conductor.SessionIDChanged += SessionIDChanged;
Conductor.ViewerRemoved += ViewerRemoved;

View File

@ -18,6 +18,7 @@ using System.Security.Principal;
using System.Windows.Input;
using Remotely.ScreenCast.Win.Services;
using Remotely.ScreenCast.Core.Interfaces;
using Remotely.ScreenCast.Core.Sockets;
namespace Remotely.Desktop.Win.ViewModels
{
@ -32,11 +33,11 @@ namespace Remotely.Desktop.Win.ViewModels
CursorIconWatcher = new CursorIconWatcher(Conductor);
CursorIconWatcher.OnChange += CursorIconWatcher_OnChange;
Conductor = new Conductor(
new WinInput(),
new WinAudioCapturer(),
new WinClipboardService(),
new WinScreenCaster(CursorIconWatcher));
var screenCaster = new WinScreenCaster(CursorIconWatcher);
var clipboardService = new WinClipboardService();
clipboardService.BeginWatching();
var casterSocket = new CasterSocket(new WinInput(), screenCaster, new WinAudioCapturer(), clipboardService);
Conductor = new Conductor(casterSocket, screenCaster);
Conductor.SessionIDChanged += SessionIDChanged;
Conductor.ViewerRemoved += ViewerRemoved;

View File

@ -21,14 +21,12 @@ namespace Remotely.ScreenCast.Core
public static Conductor Current { get; private set; }
public IScreenCaster ScreenCaster { get; }
public Conductor(IKeyboardMouseInput keyboardMouse,
IAudioCapturer audioService,
IClipboardService clipboardService,
public Conductor(CasterSocket casterSocket,
IScreenCaster screenCaster)
{
Current = this;
ScreenCaster = screenCaster;
CasterSocket = new CasterSocket(this, keyboardMouse, screenCaster, audioService, clipboardService);
CasterSocket = casterSocket;
}
public event EventHandler<ScreenCastRequest> ScreenCastRequested;

View File

@ -17,5 +17,6 @@ namespace Remotely.ScreenCast.Core.Input
uint SendRightMouseDown(double percentX, double percentY, Viewer viewer);
uint SendRightMouseUp(double percentX, double percentY, Viewer viewer);
uint SendMouseWheel(int deltaY, Viewer viewer);
void SendText(string transferText, Viewer viewer);
}
}

View File

@ -6,6 +6,10 @@ namespace Remotely.ScreenCast.Core.Interfaces
{
public interface IClipboardService
{
event EventHandler<string> ClipboardTextChanged;
void BeginWatching();
void SetText(string clipboardText);
}
}

View File

@ -46,6 +46,7 @@ namespace Remotely.ScreenCast.Core.Services
}
}
File.AppendAllText(path, JsonSerializer.Serialize(jsoninfo) + Environment.NewLine);
Console.WriteLine(message);
}
}
catch { }
@ -90,6 +91,7 @@ namespace Remotely.ScreenCast.Core.Services
}
}
File.AppendAllText(path, JsonSerializer.Serialize(jsonError) + Environment.NewLine);
Console.WriteLine(exception.Message);
exception = exception.InnerException;
}
}

View File

@ -22,25 +22,29 @@ namespace Remotely.ScreenCast.Core.Sockets
public class CasterSocket
{
public CasterSocket(
Conductor conductor,
IKeyboardMouseInput keyboardMouseInput,
IScreenCaster screenCastService,
IAudioCapturer audioCapturer,
IClipboardService clipboardService)
{
Conductor = conductor;
KeyboardMouseInput = keyboardMouseInput;
ClipboardService = clipboardService;
AudioCapturer = audioCapturer;
ScreenCaster = screenCastService;
ClipboardService.ClipboardTextChanged += ClipboardService_ClipboardTextChanged;
}
public IScreenCaster ScreenCaster { get; }
private IAudioCapturer AudioCapturer { get; }
private IClipboardService ClipboardService { get; }
private Conductor Conductor { get; }
private HubConnection Connection { get; set; }
private IKeyboardMouseInput KeyboardMouseInput { get; }
public async Task Connect(string host)
{
Connection = new HubConnectionBuilder()
@ -73,11 +77,17 @@ namespace Remotely.ScreenCast.Core.Sockets
{
await Connection.SendAsync("NotifyViewersRelaunchedScreenCasterReady", viewerIDs);
}
public async Task SendAudioSample(byte[] buffer, List<string> viewerIDs)
{
await Connection.SendAsync("SendAudioSample", buffer, viewerIDs);
}
public async Task SendClipboardText(string clipboardText, List<string> viewerIDs)
{
await Connection.SendAsync("SendClipboardText", clipboardText, viewerIDs);
}
public async Task SendConnectionFailedToViewers(List<string> viewerIDs)
{
await Connection.SendAsync("SendConnectionFailedToViewers", viewerIDs);
@ -97,6 +107,7 @@ namespace Remotely.ScreenCast.Core.Sockets
{
await Connection.SendAsync("SendMachineName", machineName, 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);
@ -119,6 +130,7 @@ namespace Remotely.ScreenCast.Core.Sockets
private void ApplyConnectionHandlers()
{
var conductor = Conductor.Current;
Connection.Closed += (ex) =>
{
Logger.Write($"Connection closed. Error: {ex?.Message}");
@ -126,13 +138,20 @@ namespace Remotely.ScreenCast.Core.Sockets
return Task.CompletedTask;
};
Connection.On("ClipboardTransfer", (string transferText, string viewerID) =>
Connection.On("ClipboardTransfer", (string transferText, bool typeText, string viewerID) =>
{
try
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
{
ClipboardService.SetText(transferText);
if (typeText)
{
KeyboardMouseInput.SendText(transferText, viewer);
}
else
{
ClipboardService.SetText(transferText);
}
}
}
catch (Exception ex)
@ -143,7 +162,7 @@ namespace Remotely.ScreenCast.Core.Sockets
Connection.On("CtrlAltDel", async (string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
{
await Connection.InvokeAsync("CtrlAltDel");
}
@ -163,12 +182,12 @@ namespace Remotely.ScreenCast.Core.Sockets
Connection.On("RequestScreenCast", (string viewerID, string requesterName) =>
{
Conductor.InvokeScreenCastRequested(new ScreenCastRequest() { ViewerID = viewerID, RequesterName = requesterName });
conductor.InvokeScreenCastRequested(new ScreenCastRequest() { ViewerID = viewerID, RequesterName = requesterName });
});
Connection.On("KeyDown", (string key, string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
{
KeyboardMouseInput.SendKeyDown(key, viewer);
}
@ -176,7 +195,7 @@ namespace Remotely.ScreenCast.Core.Sockets
Connection.On("KeyUp", (string key, string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
{
KeyboardMouseInput.SendKeyUp(key, viewer);
}
@ -184,7 +203,7 @@ namespace Remotely.ScreenCast.Core.Sockets
Connection.On("KeyPress", async (string key, string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
{
KeyboardMouseInput.SendKeyDown(key, viewer);
await Task.Delay(1);
@ -194,7 +213,7 @@ namespace Remotely.ScreenCast.Core.Sockets
Connection.On("MouseMove", (double percentX, double percentY, string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
{
KeyboardMouseInput.SendMouseMove(percentX, percentY, viewer);
}
@ -202,7 +221,7 @@ namespace Remotely.ScreenCast.Core.Sockets
Connection.On("MouseDown", (int button, double percentX, double percentY, string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
{
if (button == 0)
{
@ -217,7 +236,7 @@ namespace Remotely.ScreenCast.Core.Sockets
Connection.On("MouseUp", (int button, double percentX, double percentY, string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
{
if (button == 0)
{
@ -232,7 +251,7 @@ namespace Remotely.ScreenCast.Core.Sockets
Connection.On("MouseWheel", (double deltaX, double deltaY, string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
{
KeyboardMouseInput.SendMouseWheel(-(int)deltaY, viewer);
}
@ -240,17 +259,17 @@ namespace Remotely.ScreenCast.Core.Sockets
Connection.On("ViewerDisconnected", async (string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer))
if (conductor.Viewers.TryGetValue(viewerID, out var viewer))
{
viewer.DisconnectRequested = true;
}
await Connection.InvokeAsync("ViewerDisconnected", viewerID);
Conductor.InvokeViewerRemoved(viewerID);
conductor.InvokeViewerRemoved(viewerID);
});
Connection.On("LatencyUpdate", (double latency, string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer))
if (conductor.Viewers.TryGetValue(viewerID, out var viewer))
{
viewer.PendingFrames--;
viewer.Latency = latency;
@ -259,7 +278,7 @@ namespace Remotely.ScreenCast.Core.Sockets
Connection.On("SelectScreen", (int screenIndex, string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer))
if (conductor.Viewers.TryGetValue(viewerID, out var viewer))
{
viewer.Capturer.SetSelectedScreen(screenIndex);
}
@ -267,7 +286,7 @@ namespace Remotely.ScreenCast.Core.Sockets
Connection.On("QualityChange", (int qualityLevel, string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer))
if (conductor.Viewers.TryGetValue(viewerID, out var viewer))
{
viewer.ImageQuality = qualityLevel;
}
@ -275,7 +294,7 @@ namespace Remotely.ScreenCast.Core.Sockets
Connection.On("ToggleAudio", (bool toggleOn, string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
{
AudioCapturer.ToggleAudio(toggleOn);
}
@ -284,7 +303,7 @@ namespace Remotely.ScreenCast.Core.Sockets
Connection.On("TouchDown", (string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
{
//User32.GetCursorPos(out var point);
//Win32Interop.SendLeftMouseDown(point.X, point.Y);
@ -292,7 +311,7 @@ namespace Remotely.ScreenCast.Core.Sockets
});
Connection.On("LongPress", (string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
{
//User32.GetCursorPos(out var point);
//Win32Interop.SendRightMouseDown(point.X, point.Y);
@ -301,7 +320,7 @@ namespace Remotely.ScreenCast.Core.Sockets
});
Connection.On("TouchMove", (double moveX, double moveY, string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
{
//User32.GetCursorPos(out var point);
//Win32Interop.SendMouseMove(point.X + moveX, point.Y + moveY);
@ -309,7 +328,7 @@ namespace Remotely.ScreenCast.Core.Sockets
});
Connection.On("TouchUp", (string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
{
//User32.GetCursorPos(out var point);
//Win32Interop.SendLeftMouseUp(point.X, point.Y);
@ -317,7 +336,7 @@ namespace Remotely.ScreenCast.Core.Sockets
});
Connection.On("Tap", (double percentX, double percentY, string viewerID) =>
{
if (Conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
if (conductor.Viewers.TryGetValue(viewerID, out var viewer) && viewer.HasControl)
{
KeyboardMouseInput.SendLeftMouseDown(percentX, percentY, viewer);
KeyboardMouseInput.SendLeftMouseUp(percentX, percentY, viewer);
@ -327,7 +346,7 @@ namespace Remotely.ScreenCast.Core.Sockets
{
fileIDs.ForEach(id =>
{
var url = $"{Conductor.Host}/API/FileSharing/{id}";
var url = $"{conductor.Host}/API/FileSharing/{id}";
var webRequest = WebRequest.CreateHttp(url);
var response = webRequest.GetResponse();
var contentDisp = response.Headers["Content-Disposition"];
@ -355,8 +374,17 @@ namespace Remotely.ScreenCast.Core.Sockets
Connection.On("SessionID", (string sessionID) =>
{
Conductor.InvokeSessionIDChanged(sessionID);
conductor.InvokeSessionIDChanged(sessionID);
});
}
private async void ClipboardService_ClipboardTextChanged(object sender, string clipboardText)
{
var viewerIDs = Conductor.Current.Viewers.Keys.ToList();
if (viewerIDs.Any())
{
await SendClipboardText(clipboardText, viewerIDs);
}
}
}
}

View File

@ -4,6 +4,7 @@ using System;
using System.Threading;
using Remotely.ScreenCast.Linux.Services;
using Remotely.ScreenCast.Linux.Capture;
using Remotely.ScreenCast.Core.Sockets;
namespace Remotely.ScreenCast.Linux
{
@ -15,11 +16,9 @@ namespace Remotely.ScreenCast.Linux
try
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
Conductor = new Conductor(
new X11Input(),
new LinuxAudioCapturer(),
new LinuxClipboardService(),
new LinuxScreenCaster());
var screenCaster = new LinuxScreenCaster();
var casterSocket = new CasterSocket(new X11Input(), screenCaster, new LinuxAudioCapturer(), new LinuxClipboardService());
Conductor = new Conductor(casterSocket, screenCaster);
Conductor.ProcessArgs(args);
Conductor.Connect().ContinueWith(async (task) =>

View File

@ -10,6 +10,13 @@ namespace Remotely.ScreenCast.Linux.Services
{
public class LinuxClipboardService : IClipboardService
{
public event EventHandler<string> ClipboardTextChanged;
public void BeginWatching()
{
// Not implemented.
}
public void SetText(string clipboardText)
{
var tempPath = Path.GetTempFileName();

View File

@ -171,6 +171,15 @@ namespace Remotely.ScreenCast.Linux.Services
}
}
public void SendText(string transferText, Viewer viewer)
{
foreach (var key in transferText)
{
SendKeyDown(key.ToString(), viewer);
SendKeyUp(key.ToString(), viewer);
}
}
private string ConvertJavaScriptKeyToX11Key(string key)
{
string keySym;

View File

@ -10,6 +10,7 @@ using System.Threading;
using Remotely.ScreenCast.Win.Services;
using Remotely.ScreenCast.Core.Interfaces;
using Remotely.ScreenCast.Win.Capture;
using Remotely.ScreenCast.Core.Sockets;
namespace Remotely.ScreenCast.Win
{
@ -31,11 +32,11 @@ namespace Remotely.ScreenCast.Win
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
CursorIconWatcher = new CursorIconWatcher(Conductor);
Conductor = new Conductor(
new WinInput(),
new WinAudioCapturer(),
new WinClipboardService(),
new WinScreenCaster(CursorIconWatcher));
var screenCaster = new WinScreenCaster(CursorIconWatcher);
var clipboardService = new WinClipboardService();
clipboardService.BeginWatching();
var casterSocket = new CasterSocket(new WinInput(), screenCaster, new WinAudioCapturer(), clipboardService);
Conductor = new Conductor(casterSocket, screenCaster);
Conductor.ProcessArgs(args);
Conductor.Connect().ContinueWith(async (task) =>

View File

@ -1,4 +1,6 @@
using Remotely.ScreenCast.Core.Interfaces;
using Remotely.ScreenCast.Core.Services;
using Remotely.ScreenCast.Core.Sockets;
using System;
using System.Collections.Generic;
using System.Linq;
@ -11,14 +13,74 @@ namespace Remotely.ScreenCast.Win.Services
{
public class WinClipboardService : IClipboardService
{
public event EventHandler<string> ClipboardTextChanged;
private string ClipboardText { get; set; }
private System.Timers.Timer ClipboardWatcher { get; set; }
public void BeginWatching()
{
try
{
if (ClipboardWatcher?.Enabled == true)
{
ClipboardWatcher.Stop();
}
if (Clipboard.ContainsText())
{
ClipboardText = Clipboard.GetText();
ClipboardTextChanged.Invoke(this, ClipboardText);
}
ClipboardWatcher = new System.Timers.Timer(500);
}
catch
{
return;
}
ClipboardWatcher.Elapsed += (sender, args) =>
{
var thread = new Thread(() =>
{
try
{
if (Clipboard.ContainsText() && Clipboard.GetText() != ClipboardText)
{
ClipboardText = Clipboard.GetText();
ClipboardTextChanged.Invoke(this, ClipboardText);
}
}
catch (Exception ex)
{
Logger.Write(ex);
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
};
ClipboardWatcher.Start();
}
public void SetText(string clipboardText)
{
var thread = new Thread(() =>
try
{
Clipboard.SetText(clipboardText);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
var thread = new Thread(() =>
{
Clipboard.SetText(clipboardText);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
catch (Exception ex)
{
Logger.Write(ex);
}
}
public void StopWatching()
{
ClipboardWatcher?.Stop();
}
}
}

View File

@ -10,6 +10,57 @@ namespace Remotely.ScreenCast.Win.Services
{
public class WinInput : IKeyboardMouseInput
{
public Tuple<double, double> GetAbsolutePercentFromRelativePercent(double percentX, double percentY, ICapturer capturer)
{
var absoluteX = (capturer.CurrentScreenBounds.Width * percentX) + capturer.CurrentScreenBounds.Left - capturer.GetVirtualScreenBounds().Left;
var absoluteY = (capturer.CurrentScreenBounds.Height * percentY) + capturer.CurrentScreenBounds.Top - capturer.GetVirtualScreenBounds().Top;
return new Tuple<double, double>(absoluteX / capturer.GetVirtualScreenBounds().Width, absoluteY / capturer.GetVirtualScreenBounds().Height);
}
public Tuple<double, double> GetAbsolutePointFromRelativePercent(double percentX, double percentY, ICapturer capturer)
{
var absoluteX = (capturer.CurrentScreenBounds.Width * percentX) + capturer.CurrentScreenBounds.Left;
var absoluteY = (capturer.CurrentScreenBounds.Height * percentY) + capturer.CurrentScreenBounds.Top;
return new Tuple<double, double>(absoluteX, absoluteY);
}
public void SendKeyDown(string key, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
var keyCode = ConvertJavaScriptKeyToVirtualKey(key);
var union = new InputUnion()
{
ki = new KEYBDINPUT()
{
wVk = keyCode,
wScan = 0,
time = 0,
dwExtraInfo = GetMessageExtraInfo()
}
};
var input = new INPUT() { type = InputType.KEYBOARD, U = union };
SendInput(1, new INPUT[] { input }, INPUT.Size);
}
public void SendKeyUp(string key, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
var keyCode = ConvertJavaScriptKeyToVirtualKey(key);
var union = new InputUnion()
{
ki = new KEYBDINPUT()
{
wVk = keyCode,
wScan = 0,
time = 0,
dwFlags = KEYEVENTF.KEYUP,
dwExtraInfo = GetMessageExtraInfo()
}
};
var input = new INPUT() { type = InputType.KEYBOARD, U = union };
SendInput(1, new INPUT[] { input }, INPUT.Size);
}
public uint SendLeftMouseDown(double percentX, double percentY, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
@ -32,6 +83,34 @@ namespace Remotely.ScreenCast.Win.Services
var input = new INPUT() { type = InputType.MOUSE, U = union };
return SendInput(1, new INPUT[] { input }, INPUT.Size);
}
public uint SendMouseMove(double percentX, double percentY, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
var xyPercent = GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
// Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.
var normalizedX = xyPercent.Item1 * 65535D;
var normalizedY = xyPercent.Item2 * 65535D;
var union = new InputUnion() { mi = new MOUSEINPUT() { dwFlags = MOUSEEVENTF.ABSOLUTE | MOUSEEVENTF.MOVE | MOUSEEVENTF.VIRTUALDESK, dx = (int)normalizedX, dy = (int)normalizedY, time = 0, mouseData = 0, dwExtraInfo = GetMessageExtraInfo() } };
var input = new INPUT() { type = InputType.MOUSE, U = union };
return SendInput(1, new INPUT[] { input }, INPUT.Size);
}
public uint SendMouseWheel(int deltaY, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
if (deltaY < 0)
{
deltaY = -120;
}
else if (deltaY > 0)
{
deltaY = 120;
}
var union = new User32.InputUnion() { mi = new User32.MOUSEINPUT() { dwFlags = MOUSEEVENTF.WHEEL, dx = 0, dy = 0, time = 0, mouseData = deltaY, dwExtraInfo = GetMessageExtraInfo() } };
var input = new User32.INPUT() { type = InputType.MOUSE, U = union };
return SendInput(1, new User32.INPUT[] { input }, INPUT.Size);
}
public uint SendRightMouseDown(double percentX, double percentY, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
@ -54,66 +133,13 @@ namespace Remotely.ScreenCast.Win.Services
var input = new INPUT() { type = InputType.MOUSE, U = union };
return SendInput(1, new INPUT[] { input }, INPUT.Size);
}
public uint SendMouseMove(double percentX, double percentY, Viewer viewer)
public void SendText(string transferText, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
var xyPercent = GetAbsolutePercentFromRelativePercent(percentX, percentY, viewer.Capturer);
// Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.
var normalizedX = xyPercent.Item1 * 65535D;
var normalizedY = xyPercent.Item2 * 65535D;
var union = new InputUnion() { mi = new MOUSEINPUT() { dwFlags = MOUSEEVENTF.ABSOLUTE | MOUSEEVENTF.MOVE | MOUSEEVENTF.VIRTUALDESK, dx = (int)normalizedX, dy = (int)normalizedY, time = 0, mouseData = 0, dwExtraInfo = GetMessageExtraInfo() } };
var input = new INPUT() { type = InputType.MOUSE, U = union };
return SendInput(1, new INPUT[] { input }, INPUT.Size);
}
public uint SendMouseWheel(int deltaY, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
if (deltaY < 0)
foreach (var key in transferText)
{
deltaY = -120;
SendKeyDown(key.ToString(), viewer);
SendKeyUp(key.ToString(), viewer);
}
else if (deltaY > 0)
{
deltaY = 120;
}
var union = new User32.InputUnion() { mi = new User32.MOUSEINPUT() { dwFlags = MOUSEEVENTF.WHEEL, dx = 0, dy = 0, time = 0, mouseData = deltaY, dwExtraInfo = GetMessageExtraInfo() } };
var input = new User32.INPUT() { type = InputType.MOUSE, U = union };
return SendInput(1, new User32.INPUT[] { input }, INPUT.Size);
}
public void SendKeyDown(string key, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
var keyCode = ConvertJavaScriptKeyToVirtualKey(key);
var union = new InputUnion()
{
ki = new KEYBDINPUT()
{
wVk = keyCode,
wScan = 0,
time = 0,
dwExtraInfo = GetMessageExtraInfo()
}
};
var input = new INPUT() { type = InputType.KEYBOARD, U = union };
SendInput(1, new INPUT[] { input }, INPUT.Size);
}
public void SendKeyUp(string key, Viewer viewer)
{
Win32Interop.SwitchToInputDesktop();
var keyCode = ConvertJavaScriptKeyToVirtualKey(key);
var union = new InputUnion()
{
ki = new KEYBDINPUT()
{
wVk = keyCode,
wScan = 0,
time = 0,
dwFlags = KEYEVENTF.KEYUP,
dwExtraInfo = GetMessageExtraInfo()
}
};
var input = new INPUT() { type = InputType.KEYBOARD, U = union };
SendInput(1, new INPUT[] { input }, INPUT.Size);
}
private VirtualKey ConvertJavaScriptKeyToVirtualKey(string key)
@ -234,18 +260,5 @@ namespace Remotely.ScreenCast.Win.Services
}
return keyCode;
}
public Tuple<double, double> GetAbsolutePercentFromRelativePercent(double percentX, double percentY, ICapturer capturer)
{
var absoluteX = (capturer.CurrentScreenBounds.Width * percentX) + capturer.CurrentScreenBounds.Left - capturer.GetVirtualScreenBounds().Left;
var absoluteY = (capturer.CurrentScreenBounds.Height * percentY) + capturer.CurrentScreenBounds.Top - capturer.GetVirtualScreenBounds().Top;
return new Tuple<double, double>(absoluteX / capturer.GetVirtualScreenBounds().Width, absoluteY / capturer.GetVirtualScreenBounds().Height);
}
public Tuple<double, double> GetAbsolutePointFromRelativePercent(double percentX, double percentY, ICapturer capturer)
{
var absoluteX = (capturer.CurrentScreenBounds.Width * percentX) + capturer.CurrentScreenBounds.Left;
var absoluteY = (capturer.CurrentScreenBounds.Height * percentY) + capturer.CurrentScreenBounds.Top;
return new Tuple<double, double>(absoluteX, absoluteY);
}
}
}

View File

@ -95,9 +95,13 @@
<div id="clipboardTransferBar" class="horizontal-button-bar">
<div style="color:white; font-size:12px">
Paste to Transfer
Shared Clipboard
</div>
<textarea id="clipboardTransferTextArea"></textarea>
<div style="color:white; font-size:12px; text-align: left">
<input id="clipboardTransferTypeCheckbox" type="checkbox" />
<label>Type Input</label>
</div>
</div>
<div id="connectBox">

View File

@ -145,9 +145,9 @@ namespace Remotely.Server.Services
await RCDeviceHub.Clients.Client(ScreenCasterID).SendAsync("SelectScreen", screenIndex, Context.ConnectionId);
}
public async Task SendClipboardTransfer(string transferText)
public async Task SendClipboardTransfer(string transferText, bool typeText)
{
await RCDeviceHub.Clients.Client(ScreenCasterID).SendAsync("ClipboardTransfer", transferText, Context.ConnectionId);
await RCDeviceHub.Clients.Client(ScreenCasterID).SendAsync("ClipboardTransfer", transferText, typeText, Context.ConnectionId);
}
public async Task SendLatencyUpdate(double latency)

View File

@ -52,6 +52,12 @@ namespace Remotely.Server.Services
Context.Items["CurrentScreenSize"] = value;
}
}
private DataService DataService { get; }
private IHubContext<DeviceSocketHub> DeviceHub { get; }
private IHubContext<RCBrowserSocketHub> RCBrowserHub { get; }
private RCSessionInfo SessionInfo
{
get
@ -70,13 +76,6 @@ namespace Remotely.Server.Services
Context.Items["SessionInfo"] = value;
}
}
private DataService DataService { get; }
private IHubContext<DeviceSocketHub> DeviceHub { get; }
private IHubContext<RCBrowserSocketHub> RCBrowserHub { get; }
private List<string> ViewerList
{
get
@ -155,15 +154,15 @@ namespace Remotely.Server.Services
SessionInfo.MachineName = machineName;
SessionInfo.DeviceID = deviceID;
}
public async Task SendMachineName(string machineName, string viewerID)
{
await RCBrowserHub.Clients.Client(viewerID).SendAsync("ReceiveMachineName", machineName);
}
public async Task SendAudioSample(byte[] buffer, List<string> viewerIDs)
{
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("AudioSample", buffer);
}
public async Task SendClipboardText(string clipboardText, List<string> viewerIDs)
{
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("ClipboardTextChanged", clipboardText);
}
public async Task SendConnectionFailedToViewers(List<string> viewerIDs)
{
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("ConnectionFailed");
@ -174,6 +173,10 @@ namespace Remotely.Server.Services
await RCBrowserHub.Clients.Clients(viewerIDs).SendAsync("CursorChange", cursor);
}
public async Task SendMachineName(string machineName, string viewerID)
{
await RCBrowserHub.Clients.Client(viewerID).SendAsync("ReceiveMachineName", machineName);
}
public Task SendScreenCapture(byte[] captureBytes, string rcBrowserHubConnectionID, int left, int top, int width, int height, DateTime captureTime)
{
if (AppConfig.RecordRemoteControlSessions)

View File

@ -3,6 +3,7 @@ import * as UI from "./UI.js";
import { RemoteControl } from "./Main.js";
import { CursorInfo } from "../Models/CursorInfo.js";
import { Sound } from "../Sound.js";
import { PopupMessage } from "../UI.js";
var signalR = window["signalR"];
@ -103,10 +104,15 @@ export class RCBrowserSockets {
SendToggleAudio(toggleOn: boolean) {
this.Connection.invoke("SendToggleAudio", toggleOn);
};
SendClipboardTransfer(text: string) {
this.Connection.invoke("SendClipboardTransfer", text);
SendClipboardTransfer(text: string, typeText: boolean) {
this.Connection.invoke("SendClipboardTransfer", text, typeText);
}
private ApplyMessageHandlers(hubConnection) {
hubConnection.on("ClipboardTextChanged", (clipboardText: string) => {
Utilities.SetClipboardText(clipboardText);
UI.ClipboardTransferTextArea.innerHTML = clipboardText;
PopupMessage("Clipboard updated.");
});
hubConnection.on("ScreenCount", (primaryScreenIndex: number, screenCount: number) => {
document.querySelector("#screenSelectBar").innerHTML = "";
for (let i = 0; i < screenCount; i++) {

View File

@ -1,5 +1,5 @@
import { RCBrowserSockets } from "./RCBrowserSockets.js";
import { GetDistanceBetween } from "../Utilities.js";
import { GetDistanceBetween, SetClipboardText } from "../Utilities.js";
import { RemoteControl } from "./Main.js";
import { PopupMessage } from "../UI.js";
import { RemoteControlMode } from "../Enums/RemoteControlMode.js";
@ -35,6 +35,7 @@ export var TouchKeyboardTextArea = document.getElementById("touchKeyboardTextAre
export var ClipboardTransferBar = document.getElementById("clipboardTransferBar") as HTMLDivElement;
export var ClipboardTransferTextArea = document.getElementById("clipboardTransferTextArea") as HTMLTextAreaElement;
export var ClipboardTransferButton = document.getElementById("clipboardTransferButton") as HTMLButtonElement;
export var ClipboardTransferTypeCheckbox = document.getElementById("clipboardTransferTypeCheckbox") as HTMLInputElement;
var lastPointerMove = Date.now();
var isDragging: boolean;
@ -66,10 +67,8 @@ export function ApplyInputHandlers(sockets: RCBrowserSockets) {
if (ClipboardTransferTextArea.value.length == 0) {
return;
}
sockets.SendClipboardTransfer(ClipboardTransferTextArea.value);
ClipboardTransferTextArea.value = "";
sockets.SendClipboardTransfer(ClipboardTransferTextArea.value, ClipboardTransferTypeCheckbox.checked);
ClipboardTransferTextArea.blur();
ClipboardTransferBar.classList.remove("open");
PopupMessage("Clipboard sent!");
});
ConnectButton.addEventListener("click", (ev) => {
@ -120,15 +119,7 @@ export function ApplyInputHandlers(sockets: RCBrowserSockets) {
else {
url = `${location.origin}${location.pathname}?clientID=${RemoteControl.ClientID}&serviceID=${RemoteControl.ServiceID}`;
}
var input = document.createElement("input");
input.style.position = "fixed";
input.style.top = "-1000px";
input.type = "text";
document.body.appendChild(input);
input.value = url;
input.select();
document.execCommand("copy", false, location.href);
input.remove();
SetClipboardText(url);
PopupMessage("Link copied to clipboard.");
});

View File

@ -84,4 +84,16 @@ export function RemoveFromArray(array: Array<any>, item: any) {
if (index > -1) {
array.splice(index, 1);
}
};
};
export function SetClipboardText(text: string) {
var input = document.createElement("input");
input.style.position = "fixed";
input.style.top = "-1000px";
input.type = "text";
document.body.appendChild(input);
input.value = text;
input.select();
document.execCommand("copy", false);
input.remove();
}