mirror of
https://github.com/immense/Remotely.git
synced 2025-10-26 11:27:15 +00:00
Added bi-directional clipboard sharing.
This commit is contained in:
parent
e5b86ebd18
commit
cf14ad8781
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,10 @@ namespace Remotely.ScreenCast.Core.Interfaces
|
||||
{
|
||||
public interface IClipboardService
|
||||
{
|
||||
event EventHandler<string> ClipboardTextChanged;
|
||||
|
||||
void BeginWatching();
|
||||
|
||||
void SetText(string clipboardText);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) =>
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) =>
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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++) {
|
||||
|
||||
@ -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.");
|
||||
|
||||
});
|
||||
|
||||
@ -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();
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user