From d28929e29749c44ffabea940d74fb7736618c19d Mon Sep 17 00:00:00 2001 From: i-am-shodan <6901273+i-am-shodan@users.noreply.github.com> Date: Fri, 30 Jul 2021 22:25:05 +0100 Subject: [PATCH 1/2] This enables remarkable.net to correctly use rm2fb via direct invocation. It also abstracts version information into a new enum --- .../Driver/Display/RM2ShimDisplayDriver.cs | 118 ++++++++++++++++++ ReMarkable.NET/Unix/Driver/InputDevices.cs | 50 ++++---- ReMarkable.NET/Unix/Driver/OutputDevices.cs | 27 ++-- ReMarkable.NET/Util/DeviceType.cs | 61 +++++++++ 4 files changed, 220 insertions(+), 36 deletions(-) create mode 100644 ReMarkable.NET/Unix/Driver/Display/RM2ShimDisplayDriver.cs create mode 100644 ReMarkable.NET/Util/DeviceType.cs diff --git a/ReMarkable.NET/Unix/Driver/Display/RM2ShimDisplayDriver.cs b/ReMarkable.NET/Unix/Driver/Display/RM2ShimDisplayDriver.cs new file mode 100644 index 0000000..acd1f5c --- /dev/null +++ b/ReMarkable.NET/Unix/Driver/Display/RM2ShimDisplayDriver.cs @@ -0,0 +1,118 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; +using ReMarkable.NET.Unix.Driver.Display.EinkController; +using ReMarkable.NET.Unix.Driver.Display.Framebuffer; +using ReMarkable.NET.Unix.Stream; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; + +namespace ReMarkable.NET.Unix.Driver.Display +{ + /// + /// Provides methods for interacting with the rm2fb client used on a rm2 device + /// + public sealed class RM2ShimDisplayDriver : IDisposable, IDisplayDriver + { + // Because of the way dotnet works we need to manually invoke functions normally hooked by rm2fb + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + [DllImport("librm2fb_client.so.1.0.1", EntryPoint = "close", SetLastError = true)] + private static extern int Close(IntPtr handle); + + [DllImport("librm2fb_client.so.1.0.1", EntryPoint = "open", SetLastError = false)] + [SuppressMessage("Globalization", "CA2101:Specify marshaling for P/Invoke string arguments", Justification = + "Specifying a marshaling breaks rM compatability")] + private static extern SafeUnixHandle Open(string path, uint flags, UnixFileMode mode); + + [DllImport("librm2fb_client.so.1.0.1", EntryPoint = "ioctl", SetLastError = false)] + public static extern int Ioctl(SafeHandle handle, IoctlDisplayCommand code, ref FbUpdateData data); + + /// + /// The device handle through which IOCTL commands can be issued + /// + private readonly SafeUnixHandle _handle; + + /// + public IFramebuffer Framebuffer { get; } + + /// + public int VirtualHeight { get; } + + /// + public int VirtualWidth { get; } + + /// + public int VisibleHeight { get; } + + /// + public int VisibleWidth { get; } + + /// + /// Creates a new + /// + /// The device handle location + public RM2ShimDisplayDriver() + { + var devicePath = "/dev/fb0"; + + _handle = Open(devicePath, 0, UnixFileMode.WriteOnly); + + VisibleWidth = 1404; + VisibleHeight = 1872; + VirtualWidth = 1404; + VirtualHeight = 1872; + Framebuffer = new HardwareFramebuffer(devicePath, VisibleWidth, VisibleHeight, VirtualWidth, VirtualHeight); + } + + /// + public void Dispose() + { + _handle?.Dispose(); + ((HardwareFramebuffer)Framebuffer)?.Dispose(); + } + + /// + public void Draw(Image image, Rectangle srcArea, Point destPoint, Rectangle refreshArea = default, + WaveformMode waveformMode = WaveformMode.Auto, DisplayTemp displayTemp = DisplayTemp.Papyrus, UpdateMode updateMode = UpdateMode.Partial) + { + Framebuffer.Write(image, srcArea, destPoint); + + if (refreshArea == default) + { + refreshArea.Location = destPoint; + refreshArea.Size = srcArea.Size; + } + + Refresh(refreshArea, waveformMode, displayTemp, updateMode); + } + + /// + public void Refresh(Rectangle rectangle, WaveformMode mode, DisplayTemp displayTemp, UpdateMode updateMode) + { + Framebuffer.ConstrainRectangle(ref rectangle); + var data = new FbUpdateData + { + UpdateRegion = new FbRect + { + X = (uint)rectangle.X, + Y = (uint)rectangle.Y, + Width = (uint)rectangle.Width, + Height = (uint)rectangle.Height + }, + WaveformMode = mode, + DisplayTemp = displayTemp, + UpdateMode = updateMode, + UpdateMarker = 0, + DitherMode = 0, + QuantBit = 0, + Flags = 0 + }; + + var retCode = Ioctl(_handle, IoctlDisplayCommand.SendUpdate, ref data); + if (retCode == -1) + throw new UnixException(); + } + } +} \ No newline at end of file diff --git a/ReMarkable.NET/Unix/Driver/InputDevices.cs b/ReMarkable.NET/Unix/Driver/InputDevices.cs index 3089e1c..7c76400 100644 --- a/ReMarkable.NET/Unix/Driver/InputDevices.cs +++ b/ReMarkable.NET/Unix/Driver/InputDevices.cs @@ -33,33 +33,31 @@ public static class InputDevices /// static InputDevices() { -#if DEBUG - // Load emulated input devices - var deviceContainer = Type.GetType("RmEmulator.EmulatedDevices, RmEmulator"); - if (deviceContainer != null) + switch (DeviceType.GetDevice()) { - deviceContainer.ReadStaticField("PhysicalButtons", out PhysicalButtons); - deviceContainer.ReadStaticField("Touchscreen", out Touchscreen); - deviceContainer.ReadStaticField("Digitizer", out Digitizer); - - return; - } -#endif - // Load hardware input devices - var deviceMap = DeviceUtils.GetInputDeviceEventHandlers(); - - if (deviceMap.ContainsKey("30370000.snvs:snvs-powerkey")) - { - // rM2 - PhysicalButtons = new HardwarePhysicalButtonDriver(deviceMap["30370000.snvs:snvs-powerkey"]); - Touchscreen = new HardwareTouchscreenDriver(deviceMap["cyttsp5_mt"], 767, 1023, 32); - Digitizer = new HardwareDigitizerDriver(deviceMap["Wacom I2C Digitizer"], 20967, 15725); - } - else - { - PhysicalButtons = new HardwarePhysicalButtonDriver(deviceMap["gpio-keys"]); - Touchscreen = new HardwareTouchscreenDriver(deviceMap["cyttsp5_mt"], 767, 1023, 32); - Digitizer = new HardwareDigitizerDriver(deviceMap["Wacom I2C Digitizer"], 20967, 15725); + case Device.Emulator: + // Load emulated input devices + var deviceContainer = Type.GetType("RmEmulator.EmulatedDevices, RmEmulator"); + if (deviceContainer != null) + { + deviceContainer.ReadStaticField("PhysicalButtons", out PhysicalButtons); + deviceContainer.ReadStaticField("Touchscreen", out Touchscreen); + deviceContainer.ReadStaticField("Digitizer", out Digitizer); + } + break; + case Device.RM1: + var rm1DeviceMap = DeviceUtils.GetInputDeviceEventHandlers(); + PhysicalButtons = new HardwarePhysicalButtonDriver(rm1DeviceMap["gpio-keys"]); + Touchscreen = new HardwareTouchscreenDriver(rm1DeviceMap["cyttsp5_mt"], 767, 1023, 32); + Digitizer = new HardwareDigitizerDriver(rm1DeviceMap["Wacom I2C Digitizer"], 20967, 15725); + break; + case Device.RM2: + // rM2 + var rm2DeviceMap = DeviceUtils.GetInputDeviceEventHandlers(); + PhysicalButtons = new HardwarePhysicalButtonDriver(rm2DeviceMap["30370000.snvs:snvs-powerkey"]); + Touchscreen = new HardwareTouchscreenDriver(rm2DeviceMap["pt_mt"], 767, 1023, 32); + Digitizer = new HardwareDigitizerDriver(rm2DeviceMap["Wacom I2C Digitizer"], 20967, 15725); + break; } } } diff --git a/ReMarkable.NET/Unix/Driver/OutputDevices.cs b/ReMarkable.NET/Unix/Driver/OutputDevices.cs index a74acd0..848afaf 100644 --- a/ReMarkable.NET/Unix/Driver/OutputDevices.cs +++ b/ReMarkable.NET/Unix/Driver/OutputDevices.cs @@ -21,18 +21,25 @@ public static class OutputDevices /// static OutputDevices() { -#if DEBUG - // Load emulated input devices - var deviceContainer = Type.GetType("RmEmulator.EmulatedDevices, RmEmulator"); - if (deviceContainer != null) + switch (DeviceType.GetDevice()) { - deviceContainer.ReadStaticField("Display", out Display); - - return; + case Device.Emulator: + // Load emulated input devices + var deviceContainer = Type.GetType("RmEmulator.EmulatedDevices, RmEmulator"); + if (deviceContainer != null) + { + deviceContainer.ReadStaticField("Display", out Display); + } + break; + case Device.RM1: + // Load hardware output devices + Display = new HardwareDisplayDriver("/dev/fb0"); + break; + case Device.RM2: + // Load hardware output devices + Display = new RM2ShimDisplayDriver(); + break; } -#endif - // Load hardware output devices - Display = new HardwareDisplayDriver("/dev/fb0"); } } } \ No newline at end of file diff --git a/ReMarkable.NET/Util/DeviceType.cs b/ReMarkable.NET/Util/DeviceType.cs new file mode 100644 index 0000000..cc28bad --- /dev/null +++ b/ReMarkable.NET/Util/DeviceType.cs @@ -0,0 +1,61 @@ +using ReMarkable.NET.Unix.Driver; +using System.Runtime.InteropServices; + +namespace ReMarkable.NET.Util +{ + /// + /// Possible Remarkable or emulated device types + /// + public enum Device + { + Emulator, + RM1, + RM2 + } + + public class DeviceType + { + private static Device ?result = null; + + /// + /// Gets the device that in currently in use + /// + /// DeviceType + public static Device GetDevice() + { + if (result != null) + { + return result.Value; + } + + var ret = Device.Emulator; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + ret = Device.Emulator; + } + else + { + try + { + var deviceMap = DeviceUtils.GetInputDeviceEventHandlers(); + + if (deviceMap.ContainsKey("30370000.snvs:snvs-powerkey")) // rm2 has a power button + { + ret = Device.RM2; + } + else if (deviceMap.ContainsKey("cyttsp5_mt")) // rm1 touch screen driver + { + ret = Device.RM1; + } + } + catch + { + } + } + + result = ret; + return ret; + } + } +} From 91f0856ec4703295b597d71b7c6bcc172b04184e Mon Sep 17 00:00:00 2001 From: i-am-shodan <6901273+i-am-shodan@users.noreply.github.com> Date: Fri, 30 Jul 2021 23:52:03 +0100 Subject: [PATCH 2/2] This fixes touchscreen input on rm2 somewhat. There look to be some issues but for the most part events now pass through correctly. --- ReMarkable.NET/Unix/Driver/InputDevices.cs | 2 +- .../Driver/Touchscreen/HardwareTouchscreenDriver.cs | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ReMarkable.NET/Unix/Driver/InputDevices.cs b/ReMarkable.NET/Unix/Driver/InputDevices.cs index 7c76400..fca3bfa 100644 --- a/ReMarkable.NET/Unix/Driver/InputDevices.cs +++ b/ReMarkable.NET/Unix/Driver/InputDevices.cs @@ -55,7 +55,7 @@ static InputDevices() // rM2 var rm2DeviceMap = DeviceUtils.GetInputDeviceEventHandlers(); PhysicalButtons = new HardwarePhysicalButtonDriver(rm2DeviceMap["30370000.snvs:snvs-powerkey"]); - Touchscreen = new HardwareTouchscreenDriver(rm2DeviceMap["pt_mt"], 767, 1023, 32); + Touchscreen = new HardwareTouchscreenDriver(rm2DeviceMap["pt_mt"], 1403, 1871, 32, false); Digitizer = new HardwareDigitizerDriver(rm2DeviceMap["Wacom I2C Digitizer"], 20967, 15725); break; } diff --git a/ReMarkable.NET/Unix/Driver/Touchscreen/HardwareTouchscreenDriver.cs b/ReMarkable.NET/Unix/Driver/Touchscreen/HardwareTouchscreenDriver.cs index 2fb9954..03589b3 100644 --- a/ReMarkable.NET/Unix/Driver/Touchscreen/HardwareTouchscreenDriver.cs +++ b/ReMarkable.NET/Unix/Driver/Touchscreen/HardwareTouchscreenDriver.cs @@ -42,12 +42,18 @@ public sealed class HardwareTouchscreenDriver : UnixInputDriver, ITouchscreenDri /// public int Width { get; } - public HardwareTouchscreenDriver(string devicePath, int width, int height, int maxFingers) : base(devicePath) + /// + /// Whether the width value should be left or right biased + /// + private readonly bool invertWidth; + + public HardwareTouchscreenDriver(string devicePath, int width, int height, int maxFingers, bool shouldInvertWidth = true) : base(devicePath) { Width = width; Height = height; MaxFingers = maxFingers; Fingers = new FingerState[maxFingers]; + invertWidth = shouldInvertWidth; } /// @@ -121,7 +127,7 @@ private void ProcessAbsoluteTouch(TouchscreenEventAbsCode code, int value) Fingers[_slot].PreviousDevicePosition.X = Fingers[_slot].DevicePosition.X; Fingers[_slot].PreviousRawPosition.X = Fingers[_slot].RawPosition.X; - float pos = Width - 1 - value; + float pos = invertWidth ? Width - 1 - value : value; _position.X = (int)(pos / Width * OutputDevices.Display.VisibleWidth); Fingers[_slot].DevicePosition.X = _position.X;