[Linux] Implemented TTY and libinput keyboard drivers

This commit is contained in:
thefiddler 2014-07-14 15:42:17 +00:00
parent 97a539258b
commit 9e73358dd7
10 changed files with 970 additions and 107 deletions

View file

@ -817,8 +817,9 @@
<Compile Include="Platform\Linux\Bindings\Poll.cs" /> <Compile Include="Platform\Linux\Bindings\Poll.cs" />
<Compile Include="Platform\Linux\LinuxKeyboardTTY.cs" /> <Compile Include="Platform\Linux\LinuxKeyboardTTY.cs" />
<Compile Include="Platform\Linux\Bindings\Udev.cs" /> <Compile Include="Platform\Linux\Bindings\Udev.cs" />
<Compile Include="Platform\Linux\LinuxKeyboardLibInput.cs" />
<Compile Include="Platform\Linux\Bindings\LibInput.cs" /> <Compile Include="Platform\Linux\Bindings\LibInput.cs" />
<Compile Include="Platform\Linux\LinuxInput.cs" />
<Compile Include="Platform\Linux\Bindings\Terminal.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>

View file

@ -27,7 +27,7 @@
// //
#endregion #endregion
#pragma warning disable 0169 #pragma warning disable 0169, 0219
using System; using System;
using System.Diagnostics; using System.Diagnostics;
@ -48,6 +48,60 @@ namespace OpenTK.Platform.Linux
[DllImport(lib, EntryPoint = "libinput_udev_create_for_seat", CallingConvention = CallingConvention.Cdecl)] [DllImport(lib, EntryPoint = "libinput_udev_create_for_seat", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr CreateContext(InputInterface @interface, public static extern IntPtr CreateContext(InputInterface @interface,
IntPtr user_data, IntPtr udev, string seat_id); IntPtr user_data, IntPtr udev, string seat_id);
[DllImport(lib, EntryPoint = "libinput_destroy", CallingConvention = CallingConvention.Cdecl)]
public static extern void DestroyContext(IntPtr libinput);
[DllImport(lib, EntryPoint = "libinput_event_destroy", CallingConvention = CallingConvention.Cdecl)]
public static extern void DestroyEvent(IntPtr @event);
[DllImport(lib, EntryPoint = "libinput_dispatch", CallingConvention = CallingConvention.Cdecl)]
public static extern int Dispatch(IntPtr libinput);
[DllImport(lib, EntryPoint = "libinput_get_event", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetEvent(IntPtr libinput);
[DllImport(lib, EntryPoint = "libinput_event_get_type", CallingConvention = CallingConvention.Cdecl)]
public static extern InputEventType GetEventType(IntPtr @event);
[DllImport(lib, EntryPoint = "libinput_get_fd", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetFD(IntPtr libinput);
[DllImport(lib, EntryPoint = "libinput_next_event_type", CallingConvention = CallingConvention.Cdecl)]
public static extern InputEventType NextEventType(IntPtr libinput);
[DllImport(lib, EntryPoint = "libinput_resume", CallingConvention = CallingConvention.Cdecl)]
public static extern void Resume(IntPtr libinput);
[DllImport(lib, EntryPoint = "libinput_suspend", CallingConvention = CallingConvention.Cdecl)]
public static extern void Suspend(IntPtr libinput);
}
enum InputEventType
{
None = 0,
DeviceAdded,
DeviceRemoved,
KeyboardKey = 300,
PointerMotion = 400,
PointerMotionAbsolute,
PointerButton,
PointerAxis,
TouchDown = 500,
TouchUP,
TouchMotion,
TouchCancel,
/// \internal
/// <summary>
/// Signals the end of a set of touchpoints at one device sample
/// time. This event has no coordinate information attached.
/// </summary>
TouchFrame
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
@ -66,27 +120,6 @@ namespace OpenTK.Platform.Linux
open = Marshal.GetFunctionPointerForDelegate(open_restricted); open = Marshal.GetFunctionPointerForDelegate(open_restricted);
close = Marshal.GetFunctionPointerForDelegate(close_restricted); close = Marshal.GetFunctionPointerForDelegate(close_restricted);
} }
#region Default implementation
static CloseRestrictedCallback CloseRestricted = CloseRestrictedHandler;
static void CloseRestrictedHandler(int fd, IntPtr data)
{
Debug.Print("[Input] Closing fd {0}", fd);
Libc.close(fd);
}
static OpenRestrictedCallback OpenRestricted = OpenRestrictedHandler;
static int OpenRestrictedHandler(IntPtr path, int flags, IntPtr data)
{
Debug.Print("[Input] Opening path '{0}'", Marshal.PtrToStringAnsi(path));
return Libc.open(path, (OpenFlags)flags);
}
public static readonly InputInterface Default = new InputInterface(
OpenRestricted, CloseRestricted);
#endregion
} }
} }

View file

@ -46,6 +46,12 @@ namespace OpenTK.Platform.Linux
[DllImport(lib)] [DllImport(lib)]
public static extern int ioctl(int d, EvdevIoctlCode request, out EvdevInputId data); public static extern int ioctl(int d, EvdevIoctlCode request, out EvdevInputId data);
[DllImport(lib)]
public static extern int ioctl(int d, KeyboardIoctlCode request, ref IntPtr data);
[DllImport(lib)]
public static extern int ioctl(int d, KeyboardIoctlCode request, int data);
[DllImport(lib)] [DllImport(lib)]
public static extern int open([MarshalAs(UnmanagedType.LPStr)]string pathname, OpenFlags flags); public static extern int open([MarshalAs(UnmanagedType.LPStr)]string pathname, OpenFlags flags);
@ -58,6 +64,28 @@ namespace OpenTK.Platform.Linux
[DllImport(lib)] [DllImport(lib)]
unsafe public static extern IntPtr read(int fd, void* buffer, UIntPtr count); unsafe public static extern IntPtr read(int fd, void* buffer, UIntPtr count);
public static int read(int fd, out byte b)
{
unsafe
{
fixed (byte* pb = &b)
{
return read(fd, pb, (UIntPtr)1).ToInt32();
}
}
}
public static int read(int fd, out short s)
{
unsafe
{
fixed (short* ps = &s)
{
return read(fd, ps, (UIntPtr)2).ToInt32();
}
}
}
[DllImport(lib)] [DllImport(lib)]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
public static extern bool isatty(int fd); public static extern bool isatty(int fd);
@ -94,6 +122,12 @@ namespace OpenTK.Platform.Linux
Name128 = (2u << 30) | (0x6A << 8) | (0x13 << 0) | (128 << 16) //JSIOCGNAME(128), which is _IOC(_IO_READ, 'j', 0x13, len) Name128 = (2u << 30) | (0x6A << 8) | (0x13 << 0) | (128 << 16) //JSIOCGNAME(128), which is _IOC(_IO_READ, 'j', 0x13, len)
} }
enum KeyboardIoctlCode
{
GetMode = 0x4b44,
SetMode = 0x4b45,
}
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
struct Stat struct Stat
{ {

View file

@ -34,7 +34,7 @@ namespace OpenTK.Platform.Linux
{ {
partial class Libc partial class Libc
{ {
[DllImport(lib)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl)]
public static extern int poll(ref PollFD fd, IntPtr fd_count, int timeout); public static extern int poll(ref PollFD fd, IntPtr fd_count, int timeout);
public static int poll(ref PollFD fd, int fd_count, int timeout) public static int poll(ref PollFD fd, int fd_count, int timeout)

View file

@ -0,0 +1,166 @@
#region License
//
// Terminal.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-2014 Stefanos Apostolopoulos
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#endregion
using System;
using System.Runtime.InteropServices;
namespace OpenTK.Platform.Linux
{
class Terminal
{
const string lib = "libc";
[DllImport(lib, EntryPoint = "tcgetattr", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetAttributes(int fd, out TerminalState state);
[DllImport(lib, EntryPoint = "tcsetattr", CallingConvention = CallingConvention.Cdecl)]
public static extern int SetAttributes(int fd, OptionalActions actions, ref TerminalState state);
}
[Flags]
enum InputFlags
{
IGNBRK = 1 << 0,
BRKINT = 1 << 1,
IGNPAR = 1 << 2,
PARMRK = 1 << 3,
INPCK = 1 << 4,
ISTRIP = 1 << 5,
INLCR = 1 << 6,
IGNCR = 1 << 7,
ICRNL = 1 << 8,
IUCLC = 1 << 9,
IXON = 1 << 10,
IXANY = 1 << 11,
IXOFF = 1 << 12,
IMAXBEL = 1 << 13,
IUTF8 = 1 << 14,
}
[Flags]
enum OutputFlags
{
OPOST = 1 << 1,
OLCUC = 1 << 2,
ONLCR = 1 << 3,
OCRNL = 1 << 4,
ONOCR = 1 << 5,
ONLRET = 1 << 6,
OFILL = 1 << 7,
OFDEL = 1 << 8,
}
[Flags]
enum ControlFlags
{
B0 = 0, // hang up
B50,
B75,
B110,
B134,
B150,
B200,
B300,
B600,
B1200,
B1800,
B2400,
B4800,
B9600,
B19200,
B38400,
}
[Flags]
enum LocalFlags
{
ISIG = 0x01,
ICANON = 0x02,
ECHO = 0x08,
}
enum OptionalActions
{
NOW = 0,
DRAIN = 1,
FLUSH = 2
}
[StructLayout(LayoutKind.Sequential)]
struct TerminalState
{
public InputFlags InputMode;
public OutputFlags OutputMode;
public ControlFlags ControlMode;
public LocalFlags LocalMode;
public byte LineDiscipline;
public ControlCharacters ControlCharacters;
public int InputSpeed;
public int OutputSpeed;
}
[StructLayout(LayoutKind.Sequential)]
struct ControlCharacters
{
public byte VINTR;
public byte VQUIT;
public byte VERASE;
public byte VKILL;
public byte VEOF;
public byte VTIME;
public byte VMIN;
public byte VSWTC;
public byte VSTART;
public byte VSTOP;
public byte VSUSP;
public byte VEOL;
public byte VREPRINT;
public byte VDISCARD;
public byte VWERASE;
public byte VLNEXT;
public byte VEOL2;
public byte C17;
public byte C18;
public byte C19;
public byte C20;
public byte C21;
public byte C22;
public byte C23;
public byte C24;
public byte C25;
public byte C26;
public byte C27;
public byte C28;
public byte C29;
public byte C30;
public byte C31;
}
}

View file

@ -36,8 +36,11 @@ namespace OpenTK.Platform.Linux
{ {
const string lib = "libudev"; const string lib = "libudev";
[DllImport(lib, EntryPoint = "udev_new")] [DllImport(lib, EntryPoint = "udev_new", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr New(); public static extern IntPtr New();
[DllImport(lib, EntryPoint = "udev_destroy", CallingConvention = CallingConvention.Cdecl)]
public static extern void Destroy(IntPtr Udev);
} }
} }

View file

@ -217,7 +217,7 @@ namespace OpenTK.Platform.Linux
lock (this) lock (this)
{ {
KeyboardDriver = KeyboardDriver ?? KeyboardDriver = KeyboardDriver ??
(IKeyboardDriver2)new LinuxKeyboardLibInput() ?? // Todo: use LinuxInput driver if available?
(IKeyboardDriver2)new LinuxKeyboardTTY(); (IKeyboardDriver2)new LinuxKeyboardTTY();
return KeyboardDriver; return KeyboardDriver;
} }

View file

@ -0,0 +1,226 @@
#region License
//
// LinuxKeyboardLibInput.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-2014 Stefanos Apostolopoulos
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#endregion
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using OpenTK.Input;
namespace OpenTK.Platform.Linux
{
class LinuxInput : IKeyboardDriver2, IMouseDriver2, IDisposable
{
IntPtr udev;
IntPtr input_context;
InputInterface input_interface = new InputInterface(
OpenRestricted, CloseRestricted);
int fd;
Thread input_thread;
long exit;
public LinuxInput()
{
Debug.Print("[Linux] Initializing {0}", GetType().Name);
input_thread = new Thread(ProcessEvents);
input_thread.IsBackground = true;
// Todo: add static path fallback when udev is not installed.
udev = Udev.New();
if (udev == IntPtr.Zero)
{
throw new NotSupportedException("[Input] Udev.New() failed.");
}
input_context = LibInput.CreateContext(input_interface,
IntPtr.Zero, udev, "seat0");
if (input_context == IntPtr.Zero)
{
throw new NotSupportedException(
String.Format("[Input] LibInput.CreateContext({0:x}) failed.", udev));
}
fd = LibInput.GetFD(input_context);
if (fd < 0)
{
throw new NotSupportedException(
String.Format("[Input] LibInput.GetFD({0:x}) failed.", input_context));
}
input_thread.Start();
}
#region Private Members
static CloseRestrictedCallback CloseRestricted = CloseRestrictedHandler;
static void CloseRestrictedHandler(int fd, IntPtr data)
{
Debug.Print("[Input] Closing fd {0}", fd);
Libc.close(fd);
}
static OpenRestrictedCallback OpenRestricted = OpenRestrictedHandler;
static int OpenRestrictedHandler(IntPtr path, int flags, IntPtr data)
{
int fd = Libc.open(path, (OpenFlags)flags);
Debug.Print("[Input] Opening '{0}' with flags {1}. fd:{2}",
Marshal.PtrToStringAnsi(path), (OpenFlags)flags, fd);
return fd;
}
void ProcessEvents()
{
PollFD poll_fd = new PollFD();
poll_fd.fd = fd;
poll_fd.events = PollFlags.In;
while (Interlocked.Read(ref exit) == 0)
{
int ret = Libc.poll(ref poll_fd, 1, -1);
if (ret > 0 && (poll_fd.revents & PollFlags.In) != 0)
{
// Data available
ret = LibInput.Dispatch(input_context);
if (ret != 0)
{
Debug.Print("[Input] LibInput.Dispatch({0:x}) failed. Error: {1}",
input_context, ret);
continue;
}
IntPtr pevent = LibInput.GetEvent(input_context);
if (pevent == IntPtr.Zero)
{
Debug.Print("[Input] LibInput.GetEvent({0:x}) failed.",
input_context);
continue;
}
InputEventType type = LibInput.GetEventType(pevent);
Debug.Print(type.ToString());
LibInput.DestroyEvent(pevent);
}
else if (ret < 0)
{
// An error has occurred
Debug.Print("[Input] Exiting input thread {0} [ret:{1} events:{2}]",
input_thread.ManagedThreadId, ret, poll_fd.revents);
Interlocked.Increment(ref exit);
}
}
}
#endregion
#region IKeyboardDriver2 implementation
KeyboardState IKeyboardDriver2.GetState()
{
return new KeyboardState();
}
KeyboardState IKeyboardDriver2.GetState(int index)
{
return new KeyboardState();
}
string IKeyboardDriver2.GetDeviceName(int index)
{
return String.Empty;
}
#endregion
#region IMouseDriver2 implementation
MouseState IMouseDriver2.GetState()
{
throw new NotImplementedException();
}
MouseState IMouseDriver2.GetState(int index)
{
throw new NotImplementedException();
}
void IMouseDriver2.SetPosition(double x, double y)
{
throw new NotImplementedException();
}
MouseState IMouseDriver2.GetCursorState()
{
throw new NotImplementedException();
}
#endregion
#region IDisposable implementation
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
void Dispose(bool disposing)
{
if (disposing)
{
if (input_context != IntPtr.Zero)
{
LibInput.Suspend(input_context);
Interlocked.Increment(ref exit);
LibInput.DestroyContext(input_context);
input_context = IntPtr.Zero;
}
if (udev != IntPtr.Zero)
{
Udev.Destroy(udev);
udev = IntPtr.Zero;
}
input_interface = null;
}
}
~LinuxInput()
{
Dispose(false);
}
#endregion
}
}

View file

@ -1,77 +0,0 @@
#region License
//
// LinuxKeyboardLibInput.cs
//
// Author:
// thefiddler <stapostol@gmail.com>
//
// Copyright (c) 2006-2014
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#endregion
using System;
using System.Diagnostics;
using OpenTK.Input;
namespace OpenTK.Platform.Linux
{
class LinuxKeyboardLibInput : IKeyboardDriver2
{
IntPtr udev;
IntPtr input_context;
InputInterface input_interface = InputInterface.Default;
public LinuxKeyboardLibInput()
{
// Todo: add static path fallback when udev is not installed.
udev = Udev.New();
if (udev == IntPtr.Zero)
throw new NotSupportedException("[Input] Udev.New() failed.");
input_context = LibInput.CreateContext(input_interface,
IntPtr.Zero, udev, "seat0");
if (input_context == IntPtr.Zero)
throw new NotSupportedException("[Input] Udev.New() failed.");
}
#region IKeyboardDriver2 implementation
public KeyboardState GetState()
{
throw new NotImplementedException();
}
public KeyboardState GetState(int index)
{
throw new NotImplementedException();
}
public string GetDeviceName(int index)
{
throw new NotImplementedException();
}
#endregion
}
}

View file

@ -28,37 +28,514 @@
#endregion #endregion
using System; using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using OpenTK.Input; using OpenTK.Input;
namespace OpenTK.Platform.Linux namespace OpenTK.Platform.Linux
{ {
class LinuxKeyboardTTY : IKeyboardDriver2 class LinuxKeyboardTTY : IKeyboardDriver2, IDisposable
{ {
const int stdin = 0; // STDIN_FILENO
readonly object sync = new object();
Thread input_thread;
long exit;
KeyboardState state;
TerminalState original_state;
TerminalState current_state;
IntPtr original_mode = new IntPtr(-1);
public LinuxKeyboardTTY() public LinuxKeyboardTTY()
{ {
if (!SetupTTY(stdin))
{
throw new NotSupportedException();
} }
input_thread = new Thread(ProcessEvents);
input_thread.IsBackground = true;
input_thread.Start();
}
#region Private Members
bool SetupTTY(int stdin)
{
int ret = Terminal.GetAttributes(stdin, out original_state);
if (ret < 0)
{
Debug.Print("[Linux] Terminal.GetAttributes({0}) failed. Error: {1}",
stdin, ret);
return false;
}
// Retrieve current keyboard mode
ret = Libc.ioctl(stdin, KeyboardIoctlCode.GetMode, ref original_mode);
if (ret != 0)
{
Debug.Print("[Linux] Libc.ioctl({0}, KeyboardIoctlCode.GetMode) failed. Error: {1}",
stdin, ret);
return false;
}
// Update terminal state
current_state = original_state;
current_state.LocalMode &= ~(LocalFlags.ECHO | LocalFlags.ICANON | LocalFlags.ISIG);
current_state.InputMode &= ~(
InputFlags.ISTRIP | InputFlags.IGNCR | InputFlags.ICRNL |
InputFlags.INLCR | InputFlags.IXOFF | InputFlags.IXON);
current_state.ControlCharacters.VMIN = 0;
current_state.ControlCharacters.VTIME = 0;
Terminal.SetAttributes(stdin, OptionalActions.FLUSH, ref current_state);
// Request keycodes
int mode = 0x02; // K_MEDIUMRAW
ret = Libc.ioctl(stdin, KeyboardIoctlCode.SetMode, mode);
if (ret != 0)
{
Debug.Print("[Linux] Libc.ioctl({0}, KeyboardIoctlCode.SetMode, {1}) failed. Error: {2}",
stdin, mode, ret);
ExitTTY(this, EventArgs.Empty);
return false;
}
// Ensure we reset the original keyboard/terminal state on exit,
// even if we crash.
HookEvents();
return true;
}
void ExitTTY(object sender, EventArgs e)
{
if (original_mode != new IntPtr(-1))
{
Libc.ioctl(stdin, KeyboardIoctlCode.SetMode, ref original_mode);
Terminal.SetAttributes(stdin, OptionalActions.FLUSH, ref original_state);
original_mode = new IntPtr(-1);
UnhookEvents();
}
}
void HookEvents()
{
Process.GetCurrentProcess().Exited += ExitTTY;
Console.CancelKeyPress += ExitTTY;
}
void UnhookEvents()
{
Process.GetCurrentProcess().Exited -= ExitTTY;
Console.CancelKeyPress -= ExitTTY;
}
void ProcessEvents()
{
state.SetIsConnected(true);
while (Interlocked.Read(ref exit) == 0)
{
byte scancode;
short extended;
while (Libc.read(stdin, out scancode) > 0)
{
bool pressed = (scancode & 0x80) == 0;
int key = scancode & ~0x80;
KeyModifiers mods;
Debug.Print("{0}:{1} is {2}", key, (int)TranslateKey(key, out mods), pressed);
if (key == 0)
{
// This is an extended scancode, ignore
Libc.read(stdin, out extended);
}
else
{
lock (sync)
{
state[(Key)key] = pressed;
}
}
}
}
input_thread = null;
}
Key TranslateKey(int key, out KeyModifiers mods)
{
int k = MathHelper.Clamp((int)key, 0, KeyMap.Length);
Key result = KeyMap[k];
mods = 0;
mods |= (result == Key.AltLeft || result == Key.AltRight) ? KeyModifiers.Alt : 0;
mods |= (result == Key.ControlLeft || result == Key.ControlRight) ? KeyModifiers.Control : 0;
mods |= (result == Key.ShiftLeft || result == Key.ShiftRight) ? KeyModifiers.Shift : 0;
return KeyMap[k];
}
#region KeyMap
static readonly Key[] KeyMap = new Key[]
{
// 0-7
Key.Unknown,
Key.Escape,
Key.Number1,
Key.Number2,
Key.Number3,
Key.Number4,
Key.Number5,
Key.Number6,
// 8-15
Key.Number7,
Key.Number8,
Key.Number9,
Key.Number0,
Key.Minus,
Key.Plus,
Key.BackSpace,
Key.Tab,
// 16-23
Key.Q,
Key.W,
Key.E,
Key.R,
Key.T,
Key.Y,
Key.U,
Key.I,
// 24-31
Key.O,
Key.P,
Key.BracketLeft,
Key.BracketRight,
Key.Enter,
Key.ControlLeft,
Key.A,
Key.S,
// 32-39
Key.D,
Key.F,
Key.G,
Key.H,
Key.J,
Key.K,
Key.L,
Key.Semicolon,
// 40-47
Key.Quote,
Key.Tilde,
Key.ShiftLeft,
Key.BackSlash, //Key.Execute,
Key.Z,
Key.X,
Key.C,
Key.V, //Key.Help,
// 48-55
Key.B,
Key.N,
Key.M,
Key.Comma,
Key.Period,
Key.Slash,
Key.ShiftRight,
Key.KeypadMultiply,
// 56-63
Key.AltLeft,
Key.Space,
Key.CapsLock,
Key.F1,
Key.F2,
Key.F3,
Key.F4,
Key.F5,
// 64-71
Key.F6,
Key.F7,
Key.F8,
Key.F9,
Key.F10,
Key.NumLock,
Key.ScrollLock,
Key.Keypad7,
// 72-79
Key.Keypad8,
Key.Keypad9,
Key.KeypadSubtract,
Key.Keypad4,
Key.Keypad5,
Key.Keypad6,
Key.KeypadPlus,
Key.Keypad1,
// 80-87
Key.Keypad2,
Key.Keypad3,
Key.Keypad0,
Key.KeypadPeriod,
Key.Unknown,
Key.Unknown, // Zzenkakuhankaku
Key.Unknown, // 102ND
Key.F11,
// 88-95
Key.F12,
Key.Unknown, // ro
Key.Unknown, // katakana
Key.Unknown, // hiragana
Key.Unknown, // henkan
Key.Unknown, // katakanahiragana
Key.Unknown, // muhenkan
Key.Unknown, // kpjpcomma
// 96-103
Key.KeypadEnter,
Key.ControlRight,
Key.KeypadDivide,
Key.Unknown, // sysrq
Key.AltRight,
Key.Unknown, // linefeed
Key.Home,
Key.Up,
// 104-111
Key.PageUp,
Key.Left,
Key.Right,
Key.End,
Key.Down,
Key.PageDown,
Key.Insert,
Key.Delete,
// 112-119
Key.Unknown, // macro
Key.Unknown, // mute
Key.Unknown, // volumedown
Key.Unknown, // volumeup
Key.Unknown, // power
Key.Unknown, // kpequal
Key.Unknown, // kpplusminus
Key.Pause,
// 120-127
Key.Unknown, // scale
Key.Unknown, // kpcomma
Key.Unknown, // hangeul / hanguel
Key.Unknown, // hanja
Key.Unknown, // yen
Key.WinLeft,
Key.WinRight,
Key.Unknown, // compose
// 128-135
Key.Unknown, // stop
Key.Unknown, // again
Key.Unknown, // props
Key.Unknown, // undo
Key.Unknown, // front
Key.Unknown, // copy
Key.Unknown, // open
Key.Unknown, // paste
// 136-143
Key.Unknown, // find
Key.Unknown, // cut
Key.Unknown, // help
Key.Unknown, // menu
Key.Unknown, // calc
Key.Unknown, // setup
Key.Unknown, // sleep
Key.Unknown, // wakeup
// 144-151
Key.Unknown, // file
Key.Unknown, // send file
Key.Unknown, // delete file
Key.Unknown, // xfer
Key.Unknown, // prog1
Key.Unknown, // prog2
Key.Unknown, // www
Key.Unknown, // msdos
// 152-159
Key.Unknown, // coffee / screenlock
Key.Unknown, // direction
Key.Unknown, // cycle windows
Key.Unknown, // mail
Key.Unknown, // bookmarks
Key.Unknown, // computer
Key.Back,
Key.Unknown, // forward
// 160-167
Key.Unknown, // close cd
Key.Unknown, // eject cd
Key.Unknown, // eject/close cd
Key.Unknown, // next song
Key.Unknown, // play/pause
Key.Unknown, // previous song
Key.Unknown, // stop cd
Key.Unknown, // record
// 168-175
Key.Unknown, // rewind
Key.Unknown, // phone
Key.Unknown, // iso
Key.Unknown, // config
Key.Unknown, // homepage
Key.Unknown, // refresh
Key.Unknown, // exit
Key.Unknown, // move,
// 176-183
Key.Unknown, // edit,
Key.Unknown, // scroll up,
Key.Unknown, // scroll down,
Key.Unknown, // kp left paren,
Key.Unknown, // kp right paren,
Key.Unknown, // new,
Key.Unknown, // redo,
Key.F13,
// 184-191
Key.F14,
Key.F15,
Key.F16,
Key.F17,
Key.F18,
Key.F19,
Key.F20,
Key.F21,
// 192-199
Key.F22,
Key.F23,
Key.F24,
Key.Unknown,
Key.Unknown,
Key.Unknown,
Key.Unknown,
Key.Unknown,
// 200-207
Key.Unknown, // play cd
Key.Unknown, // pause cd
Key.Unknown, // prog3
Key.Unknown, // prog4
Key.Unknown, // dashboard
Key.Unknown, // suspend
Key.Unknown, // close
Key.Unknown, // play
// 208-215
Key.Unknown, // fast forward
Key.Unknown, // bass boost
Key.Unknown, // print
Key.Unknown, // hp
Key.Unknown, // camera
Key.Unknown, // sound
Key.Unknown, // question
Key.Unknown, // email
// 216-223
Key.Unknown, // chat
Key.Unknown, // search
Key.Unknown, // connect
Key.Unknown, // finance
Key.Unknown, // sport
Key.Unknown, // shop
Key.Unknown, // alt erase
Key.Unknown, // cancel
// 224-231
Key.Unknown, // brightness down
Key.Unknown, // brightness up
Key.Unknown, // media
Key.Unknown, // switch video mode
Key.Unknown, // dillum toggle
Key.Unknown, // dillum down
Key.Unknown, // dillum up
Key.Unknown, // send
// 232-239
Key.Unknown, // reply
Key.Unknown, // forward email
Key.Unknown, // save
Key.Unknown, // documents
Key.Unknown, // battery
Key.Unknown, // bluetooth
Key.Unknown, // wlan
Key.Unknown, // uwb
// 240-247
Key.Unknown,
Key.Unknown, // video next
Key.Unknown, // video prev
Key.Unknown, // brightness cycle
Key.Unknown, // brightness zero
Key.Unknown, // display off
Key.Unknown, // wwan / wimax
Key.Unknown, // rfkill
// 248-255
Key.Unknown, // mic mute
Key.Unknown,
Key.Unknown,
Key.Unknown,
Key.Unknown,
Key.Unknown,
Key.Unknown,
Key.Unknown, // reserved
};
#endregion
#endregion
#region IKeyboardDriver2 implementation #region IKeyboardDriver2 implementation
public KeyboardState GetState() public KeyboardState GetState()
{ {
throw new NotImplementedException(); lock (this)
{
return state;
}
} }
public KeyboardState GetState(int index) public KeyboardState GetState(int index)
{ {
throw new NotImplementedException(); lock (this)
{
if (index == 0)
return state;
else
return new KeyboardState();
}
} }
public string GetDeviceName(int index) public string GetDeviceName(int index)
{ {
throw new NotImplementedException(); if (index == 0)
return "Standard Input";
else
return String.Empty;
} }
#endregion #endregion
#region IDisposable Implementation
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
void Dispose(bool disposing)
{
Interlocked.Increment(ref exit);
if (disposing)
{
ExitTTY(this, EventArgs.Empty);
}
else
{
Debug.Print("{0} leaked, did you forget to call Dispose()?", typeof(LinuxKeyboardTTY).FullName);
}
}
~LinuxKeyboardTTY()
{
Dispose(false);
}
#endregion
} }
} }