Remotely/Desktop.Linux/Services/KeyboardMouseInputLinux.cs
2024-10-21 11:53:41 -07:00

260 lines
7.3 KiB
C#

using Remotely.Desktop.Shared.Abstractions;
using Remotely.Desktop.Shared.Enums;
using Remotely.Desktop.Shared.Services;
using Microsoft.Extensions.Logging;
using Remotely.Desktop.Native.Linux;
namespace Remotely.Desktop.Linux.Services;
public class KeyboardMouseInputLinux : IKeyboardMouseInput
{
private readonly ILogger<KeyboardMouseInputLinux> _logger;
private nint Display { get; set; }
public KeyboardMouseInputLinux(ILogger<KeyboardMouseInputLinux> logger)
{
_logger = logger;
}
public void Init()
{
// Nothing to do here. The Windows implementation needs to start
// a processing queue to keep all input simulation on the same
// thread. Linux doesn't.
}
public void SendKeyDown(string key)
{
try
{
InitDisplay();
key = ConvertJavaScriptKeyToX11Key(key);
var keySim = LibX11.XStringToKeysym(key);
if (keySim == nint.Zero)
{
_logger.LogError("Key not mapped: {key}", key);
return;
}
var keyCode = LibX11.XKeysymToKeycode(Display, keySim);
LibXtst.XTestFakeKeyEvent(Display, keyCode, true, 0);
LibX11.XSync(Display, false);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while sending key down.");
}
}
public void SendKeyUp(string key)
{
try
{
InitDisplay();
key = ConvertJavaScriptKeyToX11Key(key);
var keySim = LibX11.XStringToKeysym(key);
if (keySim == nint.Zero)
{
_logger.LogError("Key not mapped: {key}", key);
return;
}
var keyCode = LibX11.XKeysymToKeycode(Display, keySim);
LibXtst.XTestFakeKeyEvent(Display, keyCode, false, 0);
LibX11.XSync(Display, false);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while sending key up.");
}
}
public void SendMouseButtonAction(int button, ButtonAction buttonAction, double percentX, double percentY, IViewer viewer)
{
try
{
var isPressed = buttonAction == ButtonAction.Down;
// Browser buttons start at 0. XTest starts at 1.
var mouseButton = (uint)(button + 1);
InitDisplay();
SendMouseMove(percentX, percentY, viewer);
LibXtst.XTestFakeButtonEvent(Display, mouseButton, isPressed, 0);
LibX11.XSync(Display, false);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while sending mouse button action.");
}
}
public void SendMouseMove(double percentX, double percentY, IViewer viewer)
{
try
{
InitDisplay();
var screenBounds = viewer.Capturer.CurrentScreenBounds;
LibXtst.XTestFakeMotionEvent(Display,
LibX11.XDefaultScreen(Display),
screenBounds.X + (int)(screenBounds.Width * percentX),
screenBounds.Y + (int)(screenBounds.Height * percentY),
0);
LibX11.XSync(Display, false);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while sending mouse move.");
}
}
public void SendMouseWheel(int deltaY)
{
try
{
InitDisplay();
if (deltaY > 0)
{
LibXtst.XTestFakeButtonEvent(Display, 4, true, 0);
LibXtst.XTestFakeButtonEvent(Display, 4, false, 0);
}
else
{
LibXtst.XTestFakeButtonEvent(Display, 5, true, 0);
LibXtst.XTestFakeButtonEvent(Display, 5, false, 0);
}
LibX11.XSync(Display, false);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while sending mouse wheel.");
}
}
public void SendRightMouseDown(double percentX, double percentY, IViewer viewer)
{
try
{
InitDisplay();
SendMouseMove(percentX, percentY, viewer);
LibXtst.XTestFakeButtonEvent(Display, 3, true, 0);
LibX11.XSync(Display, false);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while sending mouse right down.");
}
}
public void SendRightMouseUp(double percentX, double percentY, IViewer viewer)
{
try
{
InitDisplay();
SendMouseMove(percentX, percentY, viewer);
LibXtst.XTestFakeButtonEvent(Display, 3, false, 0);
LibX11.XSync(Display, false);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while sending mouse right up.");
}
}
public void SendText(string transferText)
{
foreach (var key in transferText)
{
SendKeyDown(key.ToString());
SendKeyUp(key.ToString());
}
}
public void SetKeyStatesUp()
{
// Not implemented.
}
public void ToggleBlockInput(bool toggleOn)
{
// Not implemented.
}
private string ConvertJavaScriptKeyToX11Key(string key)
{
var keySym = key switch
{
"ArrowDown" => "Down",
"ArrowUp" => "Up",
"ArrowLeft" => "Left",
"ArrowRight" => "Right",
"Enter" => "Return",
"Esc" => "Escape",
"Alt" => "Alt_L",
"Control" => "Control_L",
"Shift" => "Shift_L",
"PAUSE" => "Pause",
"BREAK" => "Break",
"Backspace" => "BackSpace",
"Tab" => "Tab",
"CapsLock" => "Caps_Lock",
"Delete" => "Delete",
"PageUp" => "Page_Up",
"PageDown" => "Page_Down",
"NumLock" => "Num_Lock",
"ScrollLock" => "Scroll_Lock",
"ContextMenu" => "Menu",
" " => "space",
"!" => "exclam",
"\"" => "quotedbl",
"#" => "numbersign",
"$" => "dollar",
"%" => "percent",
"&" => "ampersand",
"'" => "apostrophe",
"(" => "parenleft",
")" => "parenright",
"*" => "asterisk",
"+" => "plus",
"," => "comma",
"-" => "minus",
"." => "period",
"/" => "slash",
":" => "colon",
";" => "semicolon",
"<" => "less",
"=" => "equal",
">" => "greater",
"?" => "question",
"@" => "at",
"[" => "bracketleft",
"\\" => "backslash",
"]" => "bracketright",
"_" => "underscore",
"`" => "grave",
"{" => "braceleft",
"|" => "bar",
"}" => "braceright",
"~" => "asciitilde",
_ => key,
};
return keySym;
}
private void InitDisplay()
{
try
{
if (Display == nint.Zero)
{
Display = LibX11.XOpenDisplay(string.Empty);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while initializing display.");
}
}
}