[Linux] Fixed poll() in libinput event loop

This commit is contained in:
thefiddler 2014-07-15 17:28:31 +02:00
parent 67727d2e9b
commit 468a8518cb
2 changed files with 140 additions and 61 deletions

View file

@ -51,7 +51,7 @@ namespace OpenTK.Platform.Linux
Out = 0x04, Out = 0x04,
Error = 0x08, Error = 0x08,
Hup = 0x10, Hup = 0x10,
Nval = 0x20, Invalid = 0x20,
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]

View file

@ -90,7 +90,9 @@ namespace OpenTK.Platform.Linux
public MouseState State; public MouseState State;
} }
static readonly object Sync = new object();
static readonly Key[] KeyMap = Evdev.KeyMap; static readonly Key[] KeyMap = Evdev.KeyMap;
static long DeviceFDCount;
DeviceCollection<KeyboardDevice> Keyboards = new DeviceCollection<KeyboardDevice>(); DeviceCollection<KeyboardDevice> Keyboards = new DeviceCollection<KeyboardDevice>();
DeviceCollection<MouseDevice> Mice = new DeviceCollection<MouseDevice>(); DeviceCollection<MouseDevice> Mice = new DeviceCollection<MouseDevice>();
@ -105,35 +107,33 @@ namespace OpenTK.Platform.Linux
public LinuxInput() public LinuxInput()
{ {
Debug.Print("[Linux] Initializing {0}", GetType().Name); Debug.Print("[Linux] Initializing {0}", GetType().Name);
Debug.Indent();
try
{
Semaphore ready = new Semaphore(0, 1);
input_thread = new Thread(InputThreadLoop); input_thread = new Thread(InputThreadLoop);
input_thread.IsBackground = true; input_thread.IsBackground = true;
input_thread.Start(ready);
// Todo: add static path fallback when udev is not installed. // Wait until the input thread is ready.
udev = Udev.New(); // Note: it would be nicer if we could avoid this.
if (udev == IntPtr.Zero) // however we need to marshal errors back to the caller
// as exceptions.
// Todo: in a future version, we should add an "Application" object
// to handle all communication with the OS (including event processing.)
// Once we do that, we can remove all separate input threads.
ready.WaitOne();
if (exit != 0)
{ {
throw new NotSupportedException("[Input] Udev.New() failed."); throw new NotSupportedException();
} }
}
input_context = LibInput.CreateContext(input_interface, finally
IntPtr.Zero, udev, "seat0");
if (input_context == IntPtr.Zero)
{ {
throw new NotSupportedException( Debug.Print("Initialization {0}", exit == 0 ?
String.Format("[Input] LibInput.CreateContext({0:x}) failed.", udev)); "complete" : "failed");
Debug.Unindent();
} }
fd = LibInput.GetFD(input_context);
if (fd < 0)
{
throw new NotSupportedException(
String.Format("[Input] LibInput.GetFD({0:x}) failed.", input_context));
}
ProcessEvents(input_context);
LibInput.Resume(input_context);
//input_thread.Start();
} }
#region Private Members #region Private Members
@ -142,7 +142,16 @@ namespace OpenTK.Platform.Linux
static void CloseRestrictedHandler(int fd, IntPtr data) static void CloseRestrictedHandler(int fd, IntPtr data)
{ {
Debug.Print("[Input] Closing fd {0}", fd); Debug.Print("[Input] Closing fd {0}", fd);
Libc.close(fd); int ret = Libc.close(fd);
if (ret < 0)
{
Debug.Print("[Input] Failed to close fd {0}. Error: {1}", fd, ret);
}
else
{
Interlocked.Decrement(ref DeviceFDCount);
}
} }
static OpenRestrictedCallback OpenRestricted = OpenRestrictedHandler; static OpenRestrictedCallback OpenRestricted = OpenRestrictedHandler;
@ -152,30 +161,89 @@ namespace OpenTK.Platform.Linux
Debug.Print("[Input] Opening '{0}' with flags {1}. fd:{2}", Debug.Print("[Input] Opening '{0}' with flags {1}. fd:{2}",
Marshal.PtrToStringAnsi(path), (OpenFlags)flags, fd); Marshal.PtrToStringAnsi(path), (OpenFlags)flags, fd);
if (fd >= 0)
{
Interlocked.Increment(ref DeviceFDCount);
}
return fd; return fd;
} }
void InputThreadLoop() void InputThreadLoop(object semaphore)
{ {
Debug.Print("[Input] Running on thread {0}", Thread.CurrentThread.ManagedThreadId);
Setup();
// Inform the parent thread that initialization has completed successfully
(semaphore as Semaphore).Release();
Debug.Print("[Input] Released main thread.", input_context);
// Use a blocking poll for input messages, in order to reduce CPU usage
PollFD poll_fd = new PollFD(); PollFD poll_fd = new PollFD();
poll_fd.fd = fd; poll_fd.fd = fd;
poll_fd.events = PollFlags.In; poll_fd.events = PollFlags.In;
Debug.Print("[Input] Created PollFD({0}, {1})", poll_fd.fd, poll_fd.events);
Debug.Print("[Input] Entering input loop.", poll_fd.fd, poll_fd.events);
while (Interlocked.Read(ref exit) == 0) while (Interlocked.Read(ref exit) == 0)
{ {
int ret = Libc.poll(ref poll_fd, 1, -1); int ret = Libc.poll(ref poll_fd, 1, -1);
if (ret > 0 && (poll_fd.revents & PollFlags.In) != 0) if (ret > 0 && (poll_fd.revents & (PollFlags.In | PollFlags.Pri)) != 0)
{ {
ProcessEvents(input_context); ProcessEvents(input_context);
} }
else if (ret < 0)
if ((poll_fd.revents & (PollFlags.Hup | PollFlags.Error | PollFlags.Invalid)) != 0)
{ {
// An error has occurred // An error has occurred
Debug.Print("[Input] Exiting input thread {0} [ret:{1} events:{2}]", Debug.Print("[Input] Exiting input thread {0} due to error [ret:{1} events:{2}]",
input_thread.ManagedThreadId, ret, poll_fd.revents); input_thread.ManagedThreadId, ret, poll_fd.revents);
Interlocked.Increment(ref exit); Interlocked.Increment(ref exit);
} }
} }
Debug.Print("[Input] Exited input loop.", poll_fd.fd, poll_fd.events);
}
void Setup()
{
// Todo: add static path fallback when udev is not installed.
udev = Udev.New();
if (udev == IntPtr.Zero)
{
Debug.Print("[Input] Udev.New() failed.");
Interlocked.Increment(ref exit);
return;
}
Debug.Print("[Input] Udev.New() = {0:x}", udev);
input_context = LibInput.CreateContext(input_interface, IntPtr.Zero, udev, "seat0");
if (input_context == IntPtr.Zero)
{
Debug.Print("[Input] LibInput.CreateContext({0:x}) failed.", udev);
Interlocked.Increment(ref exit);
return;
}
Debug.Print("[Input] LibInput.CreateContext({0:x}) = {1:x}", udev, input_context);
fd = LibInput.GetFD(input_context);
if (fd < 0)
{
Debug.Print("[Input] LibInput.GetFD({0:x}) failed.", input_context);
Interlocked.Increment(ref exit);
return;
}
Debug.Print("[Input] LibInput.GetFD({0:x}) = {1}.", input_context, fd);
ProcessEvents(input_context);
LibInput.Resume(input_context);
Debug.Print("[Input] LibInput.Resume({0:x})", input_context);
if (Interlocked.Read(ref DeviceFDCount) <= 0)
{
Debug.Print("[Error] Failed to open any input devices.");
Debug.Print("[Error] Ensure that you have access to '/dev/input/event*'.");
Interlocked.Increment(ref exit);
}
} }
void ProcessEvents(IntPtr input_context) void ProcessEvents(IntPtr input_context)
@ -201,6 +269,9 @@ namespace OpenTK.Platform.Linux
IntPtr device = LibInput.GetDevice(pevent); IntPtr device = LibInput.GetDevice(pevent);
InputEventType type = LibInput.GetEventType(pevent); InputEventType type = LibInput.GetEventType(pevent);
Debug.Print(type.ToString()); Debug.Print(type.ToString());
lock (Sync)
{
switch (type) switch (type)
{ {
case InputEventType.DeviceAdded: case InputEventType.DeviceAdded:
@ -215,6 +286,7 @@ namespace OpenTK.Platform.Linux
HandleKeyboard(input_context, device, LibInput.GetKeyboardEvent(pevent)); HandleKeyboard(input_context, device, LibInput.GetKeyboardEvent(pevent));
break; break;
} }
}
LibInput.DestroyEvent(pevent); LibInput.DestroyEvent(pevent);
} }
@ -281,7 +353,8 @@ namespace OpenTK.Platform.Linux
KeyboardState IKeyboardDriver2.GetState() KeyboardState IKeyboardDriver2.GetState()
{ {
ProcessEvents(input_context); lock (Sync)
{
KeyboardState state = new KeyboardState(); KeyboardState state = new KeyboardState();
foreach (KeyboardDevice keyboard in Keyboards) foreach (KeyboardDevice keyboard in Keyboards)
{ {
@ -289,10 +362,12 @@ namespace OpenTK.Platform.Linux
} }
return state; return state;
} }
}
KeyboardState IKeyboardDriver2.GetState(int index) KeyboardState IKeyboardDriver2.GetState(int index)
{ {
ProcessEvents(input_context); lock (Sync)
{
KeyboardDevice device = Keyboards.FromIndex(index); KeyboardDevice device = Keyboards.FromIndex(index);
if (device != null) if (device != null)
{ {
@ -303,8 +378,11 @@ namespace OpenTK.Platform.Linux
return new KeyboardState(); return new KeyboardState();
} }
} }
}
string IKeyboardDriver2.GetDeviceName(int index) string IKeyboardDriver2.GetDeviceName(int index)
{
lock (Sync)
{ {
KeyboardDevice device = Keyboards.FromIndex(index); KeyboardDevice device = Keyboards.FromIndex(index);
if (device != null) if (device != null)
@ -316,6 +394,7 @@ namespace OpenTK.Platform.Linux
return String.Empty; return String.Empty;
} }
} }
}
#endregion #endregion