Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable display updates on RM2 devices #5

Merged
merged 2 commits into from
Jul 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions ReMarkable.NET/Unix/Driver/Display/RM2ShimDisplayDriver.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Provides methods for interacting with the rm2fb client used on a rm2 device
/// </summary>
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);

/// <summary>
/// The device handle through which IOCTL commands can be issued
/// </summary>
private readonly SafeUnixHandle _handle;

/// <inheritdoc />
public IFramebuffer Framebuffer { get; }

/// <inheritdoc />
public int VirtualHeight { get; }

/// <inheritdoc />
public int VirtualWidth { get; }

/// <inheritdoc />
public int VisibleHeight { get; }

/// <inheritdoc />
public int VisibleWidth { get; }

/// <summary>
/// Creates a new <see cref="RM2ShimDisplayDriver" />
/// </summary>
/// <param name="devicePath">The device handle location</param>
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);
}

/// <inheritdoc />
public void Dispose()
{
_handle?.Dispose();
((HardwareFramebuffer)Framebuffer)?.Dispose();
}

/// <inheritdoc />
public void Draw(Image<Rgb24> 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);
}

/// <inheritdoc />
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();
}
}
}
50 changes: 24 additions & 26 deletions ReMarkable.NET/Unix/Driver/InputDevices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,33 +33,31 @@ public static class InputDevices
/// </summary>
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"], 1403, 1871, 32, false);
Digitizer = new HardwareDigitizerDriver(rm2DeviceMap["Wacom I2C Digitizer"], 20967, 15725);
break;
}
}
}
Expand Down
27 changes: 17 additions & 10 deletions ReMarkable.NET/Unix/Driver/OutputDevices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,25 @@ public static class OutputDevices
/// </summary>
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");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,18 @@ public sealed class HardwareTouchscreenDriver : UnixInputDriver, ITouchscreenDri
/// <inheritdoc />
public int Width { get; }

public HardwareTouchscreenDriver(string devicePath, int width, int height, int maxFingers) : base(devicePath)
/// <summary>
/// Whether the width value should be left or right biased
/// </summary>
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;
}

/// <inheritdoc />
Expand Down Expand Up @@ -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;
Expand Down
61 changes: 61 additions & 0 deletions ReMarkable.NET/Util/DeviceType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using ReMarkable.NET.Unix.Driver;
using System.Runtime.InteropServices;

namespace ReMarkable.NET.Util
{
/// <summary>
/// Possible Remarkable or emulated device types
/// </summary>
public enum Device
{
Emulator,
RM1,
RM2
}

public class DeviceType
{
private static Device ?result = null;

/// <summary>
/// Gets the device that in currently in use
/// </summary>
/// <returns>DeviceType</returns>
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;
}
}
}