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;