[X11] Implemented XI2 keyboard input

This commit is contained in:
thefiddler 2014-05-15 10:21:06 +02:00
parent 2eb88d9788
commit 7d1bec58cc

View file

@ -34,10 +34,12 @@ using OpenTK.Input;
namespace OpenTK.Platform.X11 namespace OpenTK.Platform.X11
{ {
sealed class XI2Mouse : IMouseDriver2, IDisposable sealed class XI2Mouse : IKeyboardDriver2, IMouseDriver2, IDisposable
{ {
const XEventName ExitEvent = XEventName.LASTEvent + 1; const XEventName ExitEvent = XEventName.LASTEvent + 1;
readonly object Sync = new object();
readonly Thread ProcessingThread; readonly Thread ProcessingThread;
readonly X11KeyMap KeyMap;
bool disposed; bool disposed;
class XIMouse class XIMouse
@ -48,6 +50,14 @@ namespace OpenTK.Platform.X11
public XIScrollClassInfo ScrollY = new XIScrollClassInfo { number = -1 }; public XIScrollClassInfo ScrollY = new XIScrollClassInfo { number = -1 };
public XIValuatorClassInfo MotionX = new XIValuatorClassInfo { number = -1 }; public XIValuatorClassInfo MotionX = new XIValuatorClassInfo { number = -1 };
public XIValuatorClassInfo MotionY = new XIValuatorClassInfo { number = -1 }; public XIValuatorClassInfo MotionY = new XIValuatorClassInfo { number = -1 };
public string Name;
}
class XIKeyboard
{
public KeyboardState State;
public XIDeviceInfo DeviceInfo;
public string Name;
} }
// Atoms // Atoms
@ -66,9 +76,12 @@ namespace OpenTK.Platform.X11
//static readonly IntPtr RelVertWheel; //static readonly IntPtr RelVertWheel;
long cursor_x, cursor_y; // For GetCursorState() long cursor_x, cursor_y; // For GetCursorState()
List<XIMouse> devices = new List<XIMouse>(); // List of connected mice List<XIMouse> devices = new List<XIMouse>(); // list of connected mice
Dictionary<int, int> rawids = new Dictionary<int, int>(); // maps hardware device ids to XIMouse ids Dictionary<int, int> rawids = new Dictionary<int, int>(); // maps hardware device ids to XIMouse ids
List<XIKeyboard> keyboards = new List<XIKeyboard>(); // list of connected keybords
Dictionary<int, int> keyboard_ids = new Dictionary<int, int>(); // maps hardware device ids to XIKeyboard ids
internal readonly X11WindowInfo window; internal readonly X11WindowInfo window;
internal static int XIOpCode { get; private set; } internal static int XIOpCode { get; private set; }
internal static int XIVersion { get; private set; } internal static int XIVersion { get; private set; }
@ -108,7 +121,6 @@ namespace OpenTK.Platform.X11
public XI2Mouse() public XI2Mouse()
{ {
Debug.WriteLine("Using XI2Mouse.");
window = new X11WindowInfo(); window = new X11WindowInfo();
window.Display = Functions.XOpenDisplay(IntPtr.Zero); window.Display = Functions.XOpenDisplay(IntPtr.Zero);
@ -117,6 +129,8 @@ namespace OpenTK.Platform.X11
window.Screen = Functions.XDefaultScreen(window.Display); window.Screen = Functions.XDefaultScreen(window.Display);
window.RootWindow = Functions.XRootWindow(window.Display, window.Screen); window.RootWindow = Functions.XRootWindow(window.Display, window.Screen);
window.Handle = window.RootWindow; window.Handle = window.RootWindow;
KeyMap = new X11KeyMap(window.Display);
} }
if (!IsSupported(window.Display)) if (!IsSupported(window.Display))
@ -124,6 +138,8 @@ namespace OpenTK.Platform.X11
using (new XLock(window.Display)) using (new XLock(window.Display))
using (XIEventMask mask = new XIEventMask(1, using (XIEventMask mask = new XIEventMask(1,
XIEventMasks.RawKeyPressMask |
XIEventMasks.RawKeyReleaseMask |
XIEventMasks.RawButtonPressMask | XIEventMasks.RawButtonPressMask |
XIEventMasks.RawButtonReleaseMask | XIEventMasks.RawButtonReleaseMask |
XIEventMasks.RawMotionMask | XIEventMasks.RawMotionMask |
@ -132,10 +148,9 @@ namespace OpenTK.Platform.X11
(XIEventMasks)(1 << (int)ExitEvent))) (XIEventMasks)(1 << (int)ExitEvent)))
{ {
XI.SelectEvents(window.Display, window.Handle, mask); XI.SelectEvents(window.Display, window.Handle, mask);
UpdateDevices();
} }
UpdateDevices();
ProcessingThread = new Thread(ProcessEvents); ProcessingThread = new Thread(ProcessEvents);
ProcessingThread.IsBackground = true; ProcessingThread.IsBackground = true;
ProcessingThread.Start(); ProcessingThread.Start();
@ -181,127 +196,95 @@ namespace OpenTK.Platform.X11
return false; return false;
} }
void UpdateDevices() #region IKeyboardDriver2 Members
KeyboardState IKeyboardDriver2.GetState()
{ {
int count; lock (Sync)
unsafe
{ {
XIDeviceInfo* list = (XIDeviceInfo*)XI.QueryDevice(window.Display, 1, out count); KeyboardState state = new KeyboardState();
foreach (XIKeyboard k in keyboards)
Debug.Print("Refreshing mouse device list");
Debug.Print("{0} mouse devices detected", count);
for (int i = 0; i < count; i++)
{ {
if (devices.Count <= i) state.MergeBits(k.State);
{
devices.Add(new XIMouse());
}
XIMouse d = devices[i];
d.DeviceInfo = *(list + i);
d.State.SetIsConnected(d.DeviceInfo.enabled);
Debug.Print("Device {0} is {1} and has:",
i, d.DeviceInfo.enabled ? "enabled" : "disabled");
// Decode the XIDeviceInfo to axes, buttons and scroll types
for (int j = 0; j < d.DeviceInfo.num_classes; j++)
{
XIAnyClassInfo* class_info = *((XIAnyClassInfo**)d.DeviceInfo.classes + j);
switch (class_info->type)
{
case XIClassType.Button:
{
XIButtonClassInfo* button = (XIButtonClassInfo*)class_info;
Debug.Print("\t{0} buttons", button->num_buttons);
}
break;
case XIClassType.Scroll:
{
XIScrollClassInfo* scroll = (XIScrollClassInfo*)class_info;
switch (scroll->scroll_type)
{
case XIScrollType.Vertical:
Debug.WriteLine("\tSmooth vertical scrolling");
d.ScrollY = *scroll;
break;
case XIScrollType.Horizontal:
Debug.WriteLine("\tSmooth horizontal scrolling");
d.ScrollX = *scroll;
break;
default:
Debug.Print("\tUnknown scrolling type {0}", scroll->scroll_type);
break;
}
}
break;
case XIClassType.Valuator:
{
XIValuatorClassInfo* valuator = (XIValuatorClassInfo*)class_info;
if (valuator->label == RelX)
{
Debug.WriteLine("\tRelative X movement");
d.MotionX = *valuator;
}
else if (valuator->label == RelY)
{
Debug.WriteLine("\tRelative Y movement");
d.MotionY = *valuator;
}
}
break;
}
}
// Map the hardware device id to the current XIMouse id
if (!rawids.ContainsKey(d.DeviceInfo.deviceid))
{
rawids.Add(d.DeviceInfo.deviceid, 0);
}
rawids[d.DeviceInfo.deviceid] = i;
} }
XI.FreeDeviceInfo((IntPtr)list); return state;
} }
} }
KeyboardState IKeyboardDriver2.GetState(int index)
{
lock (Sync)
{
if (index >= 0 && index < keyboards.Count)
{
return keyboards[index].State;
}
return new KeyboardState();
}
}
string IKeyboardDriver2.GetDeviceName(int index)
{
lock (Sync)
{
if (index >= 0 && index < keyboards.Count)
{
return keyboards[index].Name;
}
return String.Empty;
}
}
#endregion
#region IMouseDriver2 Members #region IMouseDriver2 Members
public MouseState GetState() MouseState IMouseDriver2.GetState()
{ {
MouseState master = new MouseState(); lock (Sync)
foreach (var d in devices)
{ {
master.MergeBits(d.State); MouseState master = new MouseState();
foreach (var d in devices)
{
master.MergeBits(d.State);
}
return master;
} }
return master;
} }
public MouseState GetState(int index) MouseState IMouseDriver2.GetState(int index)
{ {
if (devices.Count > index) lock (Sync)
return devices[index].State; {
else if (index >= 0 && index < devices.Count)
{
return devices[index].State;
}
return new MouseState(); return new MouseState();
}
} }
public MouseState GetCursorState() MouseState IMouseDriver2.GetCursorState()
{ {
MouseState master = GetState(); lock (Sync)
master.X = (int)Interlocked.Read(ref cursor_x); {
master.Y = (int)Interlocked.Read(ref cursor_y); MouseState master = (this as IMouseDriver2).GetState();
return master; master.X = (int)Interlocked.Read(ref cursor_x);
master.Y = (int)Interlocked.Read(ref cursor_y);
return master;
}
} }
public void SetPosition(double x, double y) void IMouseDriver2.SetPosition(double x, double y)
{ {
// Note: we cannot use window.Display here, because
// that will deadlock the input thread, which is
// blocking inside XIfEvent
using (new XLock(API.DefaultDisplay)) using (new XLock(API.DefaultDisplay))
{ {
Functions.XWarpPointer(API.DefaultDisplay, Functions.XWarpPointer(API.DefaultDisplay,
IntPtr.Zero, window.RootWindow, 0, 0, 0, 0, (int)x, (int)y); IntPtr.Zero, window.RootWindow, 0, 0, 0, 0, (int)x, (int)y);
Functions.XFlush(API.DefaultDisplay);
Interlocked.Exchange(ref cursor_x, (long)x); Interlocked.Exchange(ref cursor_x, (long)x);
Interlocked.Exchange(ref cursor_y, (long)y); Interlocked.Exchange(ref cursor_y, (long)y);
} }
@ -309,6 +292,127 @@ namespace OpenTK.Platform.X11
#endregion #endregion
#region Private Members
void UpdateDevices()
{
lock (Sync)
{
devices.Clear();
keyboards.Clear();
int count;
unsafe
{
XIDeviceInfo* list = (XIDeviceInfo*)XI.QueryDevice(window.Display,
XI.XIAllDevices, out count);
Debug.Print("Refreshing input device list");
Debug.Print("{0} input devices detected", count);
for (int i = 0; i < count; i++)
{
switch ((list + i)->use)
{
case XIDeviceType.MasterKeyboard:
//case XIDeviceType.SlaveKeyboard:
{
XIKeyboard k = new XIKeyboard();
k.DeviceInfo = *(list + i);
k.State.SetIsConnected(k.DeviceInfo.enabled);
k.Name = Marshal.PtrToStringAnsi(k.DeviceInfo.name);
int id = k.DeviceInfo.deviceid;
if (!keyboard_ids.ContainsKey(id))
{
keyboard_ids.Add(k.DeviceInfo.deviceid, 0);
}
keyboard_ids[id] = keyboards.Count;
keyboards.Add(k);
}
break;
case XIDeviceType.MasterPointer:
//case XIDeviceType.SlavePointer:
case XIDeviceType.FloatingSlave:
{
XIMouse d = new XIMouse();
d.DeviceInfo = *(list + i);
d.State.SetIsConnected(d.DeviceInfo.enabled);
d.Name = Marshal.PtrToStringAnsi(d.DeviceInfo.name);
Debug.Print("Device {0} \"{1}\" is {2} and has:",
i, d.Name, d.DeviceInfo.enabled ? "enabled" : "disabled");
// Decode the XIDeviceInfo to axes, buttons and scroll types
for (int j = 0; j < d.DeviceInfo.num_classes; j++)
{
XIAnyClassInfo* class_info = *((XIAnyClassInfo**)d.DeviceInfo.classes + j);
switch (class_info->type)
{
case XIClassType.Button:
{
XIButtonClassInfo* button = (XIButtonClassInfo*)class_info;
Debug.Print("\t{0} buttons", button->num_buttons);
}
break;
case XIClassType.Scroll:
{
XIScrollClassInfo* scroll = (XIScrollClassInfo*)class_info;
switch (scroll->scroll_type)
{
case XIScrollType.Vertical:
Debug.WriteLine("\tSmooth vertical scrolling");
d.ScrollY = *scroll;
break;
case XIScrollType.Horizontal:
Debug.WriteLine("\tSmooth horizontal scrolling");
d.ScrollX = *scroll;
break;
default:
Debug.Print("\tUnknown scrolling type {0}", scroll->scroll_type);
break;
}
}
break;
case XIClassType.Valuator:
{
XIValuatorClassInfo* valuator = (XIValuatorClassInfo*)class_info;
if (valuator->label == RelX)
{
Debug.WriteLine("\tRelative X movement");
d.MotionX = *valuator;
}
else if (valuator->label == RelY)
{
Debug.WriteLine("\tRelative Y movement");
d.MotionY = *valuator;
}
}
break;
}
}
// Map the hardware device id to the current XIMouse id
int id = d.DeviceInfo.deviceid;
if (!rawids.ContainsKey(id))
{
rawids.Add(id, 0);
}
rawids[id] = devices.Count;
devices.Add(d);
}
break;
}
}
XI.FreeDeviceInfo((IntPtr)list);
}
}
}
void ProcessEvents() void ProcessEvents()
{ {
while (!disposed) while (!disposed)
@ -341,6 +445,8 @@ namespace OpenTK.Platform.X11
// Nothing to do // Nothing to do
break; break;
case XIEventType.RawKeyPress:
case XIEventType.RawKeyRelease:
case XIEventType.RawMotion: case XIEventType.RawMotion:
case XIEventType.RawButtonPress: case XIEventType.RawButtonPress:
case XIEventType.RawButtonRelease: case XIEventType.RawButtonRelease:
@ -360,59 +466,73 @@ namespace OpenTK.Platform.X11
void ProcessRawEvent(ref XGenericEventCookie cookie) void ProcessRawEvent(ref XGenericEventCookie cookie)
{ {
unsafe lock (Sync)
{ {
XIRawEvent raw = *(XIRawEvent*)cookie.data; unsafe
if (!rawids.ContainsKey(raw.deviceid))
{ {
Debug.Print("Unknown mouse device {0} encountered, ignoring.", raw.deviceid); XIRawEvent raw = *(XIRawEvent*)cookie.data;
return; XIMouse mouse;
} XIKeyboard keyboard;
var d = devices[rawids[raw.deviceid]]; switch (raw.evtype)
{
case XIEventType.RawMotion:
if (GetMouseDevice(raw.deviceid, out mouse))
{
ProcessRawMotion(mouse, ref raw);
}
break;
switch (raw.evtype) case XIEventType.RawButtonPress:
{ case XIEventType.RawButtonRelease:
case XIEventType.RawMotion: if (GetMouseDevice(raw.deviceid, out mouse))
ProcessRawMotion(d, ref raw); {
break; float dx, dy;
MouseButton button = X11KeyMap.TranslateButton(raw.detail, out dx, out dy);
mouse.State[button] = raw.evtype == XIEventType.RawButtonPress;
}
break;
case XIEventType.RawButtonPress: case XIEventType.RawKeyPress:
switch (raw.detail) case XIEventType.RawKeyRelease:
{ if (GetKeyboardDevice(raw.deviceid, out keyboard))
case 1: d.State.EnableBit((int)MouseButton.Left); break; {
case 2: d.State.EnableBit((int)MouseButton.Middle); break; Key key;
case 3: d.State.EnableBit((int)MouseButton.Right); break; if (KeyMap.TranslateKey(raw.detail, out key))
case 8: d.State.EnableBit((int)MouseButton.Button1); break; {
case 9: d.State.EnableBit((int)MouseButton.Button2); break; keyboard.State[key] = raw.evtype == XIEventType.RawKeyPress;
case 10: d.State.EnableBit((int)MouseButton.Button3); break; }
case 11: d.State.EnableBit((int)MouseButton.Button4); break; }
case 12: d.State.EnableBit((int)MouseButton.Button5); break; break;
case 13: d.State.EnableBit((int)MouseButton.Button6); break; }
case 14: d.State.EnableBit((int)MouseButton.Button7); break;
}
break;
case XIEventType.RawButtonRelease:
switch (raw.detail)
{
case 1: d.State.DisableBit((int)MouseButton.Left); break;
case 2: d.State.DisableBit((int)MouseButton.Middle); break;
case 3: d.State.DisableBit((int)MouseButton.Right); break;
case 8: d.State.DisableBit((int)MouseButton.Button1); break;
case 9: d.State.DisableBit((int)MouseButton.Button2); break;
case 10: d.State.DisableBit((int)MouseButton.Button3); break;
case 11: d.State.DisableBit((int)MouseButton.Button4); break;
case 12: d.State.DisableBit((int)MouseButton.Button5); break;
case 13: d.State.DisableBit((int)MouseButton.Button6); break;
case 14: d.State.DisableBit((int)MouseButton.Button7); break;
}
break;
} }
} }
} }
bool GetMouseDevice(int deviceid, out XIMouse mouse)
{
if (!rawids.ContainsKey(deviceid))
{
Debug.Print("Unknown mouse device {0} encountered, ignoring.", deviceid);
mouse = null;
return false;
}
mouse = devices[rawids[deviceid]];
return true;
}
bool GetKeyboardDevice(int deviceid, out XIKeyboard keyboard)
{
if (!keyboard_ids.ContainsKey(deviceid))
{
Debug.Print("Unknown keyboard device {0} encountered, ignoring.", deviceid);
keyboard = null;
return false;
}
keyboard = keyboards[keyboard_ids[deviceid]];
return true;
}
unsafe static void ProcessRawMotion(XIMouse d, ref XIRawEvent raw) unsafe static void ProcessRawMotion(XIMouse d, ref XIRawEvent raw)
{ {
// Note: we use the raw values here, without pointer // Note: we use the raw values here, without pointer
@ -453,6 +573,8 @@ namespace OpenTK.Platform.X11
bool valid = false; bool valid = false;
if ((long)e.GenericEventCookie.extension == arg.ToInt64()) if ((long)e.GenericEventCookie.extension == arg.ToInt64())
{ {
valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawKeyPress;
valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawKeyRelease;
valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawMotion; valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawMotion;
valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawButtonPress; valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawButtonPress;
valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawButtonRelease; valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawButtonRelease;
@ -470,6 +592,8 @@ namespace OpenTK.Platform.X11
} }
} }
#endregion
#region IDisposable Members #region IDisposable Members
public void Dispose() public void Dispose()