Merge branch 'xkeyfix' of https://github.com/thefiddler/opentk into develop
This commit is contained in:
commit
ade8e61625
12 changed files with 1155 additions and 211 deletions
|
@ -697,7 +697,6 @@
|
|||
<Compile Include="Platform\X11\X11Keyboard.cs" />
|
||||
<Compile Include="Platform\X11\X11Mouse.cs" />
|
||||
<Compile Include="Input\ButtonState.cs" />
|
||||
<Compile Include="Platform\X11\XI2Mouse.cs" />
|
||||
<EmbeddedResource Include="OpenTK.dll.config">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
|
@ -798,7 +797,11 @@
|
|||
<Compile Include="Platform\MacOS\Quartz\CoreFoundation.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Platform\X11\Bindings\Xkb.cs" />
|
||||
<Compile Include="Input\MouseScroll.cs" />
|
||||
<Compile Include="Platform\X11\X11Input.cs" />
|
||||
<Compile Include="Platform\X11\XI2Input.cs" />
|
||||
<Compile Include="Platform\X11\XI2MouseKeyboard.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
|
|
|
@ -44,6 +44,9 @@ namespace OpenTK.Platform.X11
|
|||
{
|
||||
const string lib = "libXi";
|
||||
|
||||
internal const int XIAllDevices = 0;
|
||||
internal const int XIAllMasterDevices = 1;
|
||||
|
||||
// mouse
|
||||
internal static readonly IntPtr ButtonLeft = Functions.XInternAtom(API.DefaultDisplay, "Button Left", false);
|
||||
internal static readonly IntPtr ButtonMiddle = Functions.XInternAtom(API.DefaultDisplay, "Button Middle", false);
|
||||
|
@ -124,4 +127,13 @@ namespace OpenTK.Platform.X11
|
|||
[DllImport(lib, EntryPoint = "XIQueryVersion")]
|
||||
internal static extern Status QueryVersion(Display display, ref int major, ref int minor);
|
||||
}
|
||||
|
||||
enum XIDeviceType
|
||||
{
|
||||
MasterPointer = 1,
|
||||
MasterKeyboard = 2,
|
||||
SlavePointer = 3,
|
||||
SlaveKeyboard = 4,
|
||||
FloatingSlave = 5,
|
||||
}
|
||||
}
|
||||
|
|
302
Source/OpenTK/Platform/X11/Bindings/Xkb.cs
Normal file
302
Source/OpenTK/Platform/X11/Bindings/Xkb.cs
Normal file
|
@ -0,0 +1,302 @@
|
|||
#region License
|
||||
//
|
||||
// Xkb.cs
|
||||
//
|
||||
// Author:
|
||||
// Stefanos Apostolopoulos <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 System.Runtime.InteropServices;
|
||||
|
||||
namespace OpenTK.Platform.X11
|
||||
{
|
||||
using Atom = IntPtr;
|
||||
using KeyCode = Byte;
|
||||
using XkbControlsPtr = IntPtr;
|
||||
using XkbServerMapPtr = IntPtr;
|
||||
using XkbClientMapPtr = IntPtr;
|
||||
using XkbIndicatorPtr = IntPtr;
|
||||
using XkbCompatMapPtr = IntPtr;
|
||||
using XkbGeometryPtr = IntPtr;
|
||||
|
||||
class Xkb
|
||||
{
|
||||
const string lib = "libX11";
|
||||
|
||||
internal const int KeyNameLength = 4;
|
||||
internal const int NumModifiers = 8;
|
||||
internal const int NumVirtualMods = 16;
|
||||
internal const int NumIndicators = 32;
|
||||
internal const int NumKbdGroups = 4;
|
||||
internal const int UseCoreKeyboard = 0x0100;
|
||||
|
||||
[DllImport(lib, EntryPoint = "XkbFreeKeyboard")]
|
||||
unsafe internal extern static void FreeKeyboard(XkbDesc* descr, int which, bool free);
|
||||
|
||||
[DllImport(lib, EntryPoint = "XkbAllocKeyboard")]
|
||||
unsafe internal extern static XkbDesc* AllocKeyboard(IntPtr display);
|
||||
|
||||
[DllImport(lib, EntryPoint = "XkbGetKeyboard")]
|
||||
internal extern static IntPtr GetKeyboard(IntPtr display, XkbKeyboardMask which, int device_id);
|
||||
|
||||
[DllImport(lib, EntryPoint = "XkbGetMap")]
|
||||
internal extern static IntPtr GetMap(IntPtr display, XkbKeyboardMask which, int device_spec);
|
||||
|
||||
[DllImport(lib, EntryPoint = "XkbGetNames")]
|
||||
unsafe internal extern static IntPtr GetNames(IntPtr display, XkbNamesMask which, XkbDesc* xkb);
|
||||
|
||||
[DllImport(lib, EntryPoint = "XkbKeycodeToKeysym")]
|
||||
internal extern static XKey KeycodeToKeysym(IntPtr display, int keycode, int group, int level);
|
||||
|
||||
[DllImport(lib, EntryPoint = "XkbQueryExtension")]
|
||||
internal extern static bool QueryExtension(IntPtr display, out int opcode_rtrn, out int event_rtrn,
|
||||
out int error_rtrn, ref int major_in_out, ref int minor_in_out);
|
||||
|
||||
[DllImport(lib, EntryPoint = "XkbSetDetectableAutoRepeat")]
|
||||
internal extern static bool SetDetectableAutoRepeat(IntPtr display, bool detectable, out bool supported);
|
||||
|
||||
internal static bool IsSupported(IntPtr display)
|
||||
{
|
||||
// The XkbQueryExtension manpage says that we cannot
|
||||
// use XQueryExtension with XKB.
|
||||
int opcode, error, ev;
|
||||
int major = 1;
|
||||
int minor = 0;
|
||||
bool supported = QueryExtension(display, out opcode, out ev, out error, ref major, ref minor);
|
||||
Debug.Print("XKB extension is {0}.", supported ? "supported" : "not supported");
|
||||
if (supported)
|
||||
{
|
||||
Debug.Print("XKB version is {0}.{1}", major, minor);
|
||||
}
|
||||
return supported;
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum XkbKeyboardMask
|
||||
{
|
||||
Controls = 1 << 0,
|
||||
ServerMap = 1 << 1,
|
||||
IClientMap = 1 << 2,
|
||||
IndicatorMap = 1 << 3,
|
||||
Names = 1 << 4,
|
||||
CompatibilityMap = 1 << 5,
|
||||
Geometry = 1 << 6,
|
||||
AllComponents = 1 << 7,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum XkbNamesMask
|
||||
{
|
||||
Keycodes = 1 << 0,
|
||||
Geometry = 1 << 1,
|
||||
Symbols = 1 << 2,
|
||||
PhysSymbols = 1 << 3,
|
||||
Types = 1 << 4,
|
||||
Compat = 1 << 5,
|
||||
KeyType = 1 << 6,
|
||||
KTLevel = 1 << 7,
|
||||
Indicator = 1 << 8,
|
||||
Key = 1 << 9,
|
||||
KeyAliasesMask = 1 << 10,
|
||||
VirtualMod = 1 << 11,
|
||||
Group = 1 << 12,
|
||||
RG = 1 << 13,
|
||||
Component = 0x3f,
|
||||
All = 0x3fff
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
unsafe struct XkbDesc
|
||||
{
|
||||
internal IntPtr dpy;
|
||||
internal ushort flags;
|
||||
internal ushort device_spec;
|
||||
internal KeyCode min_key_code;
|
||||
internal KeyCode max_key_code;
|
||||
|
||||
internal XkbControlsPtr ctrls;
|
||||
internal XkbServerMapPtr server;
|
||||
internal XkbClientMapPtr map;
|
||||
internal XkbIndicatorPtr indicators;
|
||||
internal XkbNames* names;
|
||||
internal XkbCompatMapPtr compat;
|
||||
internal XkbGeometryPtr geom;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
unsafe struct XkbKeyAlias
|
||||
{
|
||||
internal fixed byte real[Xkb.KeyNameLength];
|
||||
internal fixed byte alias[Xkb.KeyNameLength];
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
unsafe struct XkbKeyName
|
||||
{
|
||||
internal fixed byte name[Xkb.KeyNameLength];
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
unsafe struct XkbNames
|
||||
{
|
||||
#region Structs
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct Groups
|
||||
{
|
||||
Atom groups0;
|
||||
Atom groups1;
|
||||
Atom groups2;
|
||||
Atom groups3;
|
||||
internal Atom this[int i]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (i < 0 || i > 3)
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (Atom* ptr = &groups0)
|
||||
{
|
||||
return *(ptr + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct Indicators
|
||||
{
|
||||
Atom indicators0;
|
||||
Atom indicators1;
|
||||
Atom indicators2;
|
||||
Atom indicators3;
|
||||
Atom indicators4;
|
||||
Atom indicators5;
|
||||
Atom indicators6;
|
||||
Atom indicators7;
|
||||
Atom indicators8;
|
||||
Atom indicators9;
|
||||
Atom indicators10;
|
||||
Atom indicators11;
|
||||
Atom indicators12;
|
||||
Atom indicators13;
|
||||
Atom indicators14;
|
||||
Atom indicators15;
|
||||
Atom indicators16;
|
||||
Atom indicators17;
|
||||
Atom indicators18;
|
||||
Atom indicators19;
|
||||
Atom indicators20;
|
||||
Atom indicators21;
|
||||
Atom indicators22;
|
||||
Atom indicators23;
|
||||
Atom indicators24;
|
||||
Atom indicators25;
|
||||
Atom indicators26;
|
||||
Atom indicators27;
|
||||
Atom indicators28;
|
||||
Atom indicators29;
|
||||
Atom indicators30;
|
||||
Atom indicators31;
|
||||
internal Atom this[int i]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (i < 0 || i > 31)
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (Atom* ptr = &indicators0)
|
||||
{
|
||||
return *(ptr + i);
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct VMods
|
||||
{
|
||||
Atom vmods0;
|
||||
Atom vmods1;
|
||||
Atom vmods2;
|
||||
Atom vmods3;
|
||||
Atom vmods4;
|
||||
Atom vmods5;
|
||||
Atom vmods6;
|
||||
Atom vmods7;
|
||||
Atom vmods8;
|
||||
Atom vmods9;
|
||||
Atom vmods10;
|
||||
Atom vmods11;
|
||||
Atom vmods12;
|
||||
Atom vmods13;
|
||||
Atom vmods14;
|
||||
Atom vmods15;
|
||||
internal Atom this[int i]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (i < 0 || i > 15)
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (Atom* ptr = &vmods0)
|
||||
{
|
||||
return *(ptr + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
internal Atom keycodes;
|
||||
internal Atom geometry;
|
||||
internal Atom symbols;
|
||||
internal Atom types;
|
||||
internal Atom compat;
|
||||
internal VMods vmods;
|
||||
internal Indicators indicators;
|
||||
internal Groups groups;
|
||||
internal XkbKeyName* keys;
|
||||
internal XkbKeyAlias* key_aliases;
|
||||
internal Atom *radio_groups;
|
||||
internal Atom phys_symbols;
|
||||
|
||||
internal byte num_keys;
|
||||
internal byte num_key_aliases;
|
||||
internal byte num_rg;
|
||||
}
|
||||
}
|
||||
|
|
@ -439,9 +439,6 @@ namespace OpenTK.Platform.X11
|
|||
[DllImport("libX11", EntryPoint = "XFilterEvent")]
|
||||
public extern static bool XFilterEvent(ref XEvent xevent, IntPtr window);
|
||||
|
||||
[DllImport("libX11")]
|
||||
public extern static bool XkbSetDetectableAutoRepeat(IntPtr display, bool detectable, out bool supported);
|
||||
|
||||
[DllImport("libX11")]
|
||||
public extern static void XPeekEvent(IntPtr display, ref XEvent xevent);
|
||||
|
||||
|
|
|
@ -945,7 +945,7 @@ namespace OpenTK.Platform.X11
|
|||
public byte pad;
|
||||
}
|
||||
|
||||
internal enum Atom
|
||||
internal enum AtomName
|
||||
{
|
||||
AnyPropertyType = 0,
|
||||
XA_PRIMARY = 1,
|
||||
|
@ -1688,7 +1688,7 @@ namespace OpenTK.Platform.X11
|
|||
{
|
||||
public int deviceid;
|
||||
public IntPtr name; // byte*
|
||||
public int use;
|
||||
public XIDeviceType use;
|
||||
public int attachment;
|
||||
public Bool enabled;
|
||||
public int num_classes;
|
||||
|
|
|
@ -34,7 +34,25 @@ namespace OpenTK.Platform.X11
|
|||
{
|
||||
class X11Factory : PlatformFactoryBase
|
||||
{
|
||||
static IMouseDriver2 MouseDriver;
|
||||
IInputDriver2 input_driver;
|
||||
IInputDriver2 InputDriver
|
||||
{
|
||||
get
|
||||
{
|
||||
if (input_driver == null)
|
||||
{
|
||||
if (XI2MouseKeyboard.IsSupported(IntPtr.Zero))
|
||||
{
|
||||
input_driver = new XI2Input();
|
||||
}
|
||||
else
|
||||
{
|
||||
input_driver = new X11Input();
|
||||
}
|
||||
}
|
||||
return input_driver;
|
||||
}
|
||||
}
|
||||
|
||||
#region Constructors
|
||||
|
||||
|
@ -83,25 +101,17 @@ namespace OpenTK.Platform.X11
|
|||
|
||||
public override IKeyboardDriver2 CreateKeyboardDriver()
|
||||
{
|
||||
return new X11Keyboard();
|
||||
return InputDriver.KeyboardDriver;
|
||||
}
|
||||
|
||||
public override IMouseDriver2 CreateMouseDriver()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
MouseDriver =
|
||||
MouseDriver ??
|
||||
(XI2Mouse.IsSupported(IntPtr.Zero) ?
|
||||
(IMouseDriver2)new XI2Mouse() : // Requires xorg 1.7 or higher.
|
||||
(IMouseDriver2)new X11Mouse()); // Always supported.
|
||||
return MouseDriver;
|
||||
}
|
||||
return InputDriver.MouseDriver;
|
||||
}
|
||||
|
||||
public override IJoystickDriver2 CreateJoystickDriver()
|
||||
{
|
||||
return new X11Joystick();
|
||||
return InputDriver.JoystickDriver;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -109,12 +119,12 @@ namespace OpenTK.Platform.X11
|
|||
protected override void Dispose(bool manual)
|
||||
{
|
||||
base.Dispose(manual);
|
||||
if (manual && MouseDriver != null)
|
||||
if (manual)
|
||||
{
|
||||
if (MouseDriver is IDisposable)
|
||||
if (input_driver is IDisposable)
|
||||
{
|
||||
(MouseDriver as IDisposable).Dispose();
|
||||
MouseDriver = null;
|
||||
(input_driver as IDisposable).Dispose();
|
||||
input_driver = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,8 @@ namespace OpenTK.Platform.X11
|
|||
|
||||
const int _min_width = 30, _min_height = 30;
|
||||
|
||||
X11WindowInfo window = new X11WindowInfo();
|
||||
readonly X11WindowInfo window = new X11WindowInfo();
|
||||
readonly X11KeyMap KeyMap;
|
||||
|
||||
// Window manager hints for fullscreen windows.
|
||||
// Not used right now (the code is written, but is not 64bit-correct), but could be useful for older WMs which
|
||||
|
@ -230,20 +231,26 @@ namespace OpenTK.Platform.X11
|
|||
Debug.WriteLine(String.Format("X11GLNative window created successfully (id: {0}).", Handle));
|
||||
Debug.Unindent();
|
||||
|
||||
// Request that auto-repeat is only set on devices that support it physically.
|
||||
// This typically means that it's turned off for keyboards (which is what we want).
|
||||
// We prefer this method over XAutoRepeatOff/On, because the latter needs to
|
||||
// be reset before the program exits.
|
||||
bool supported;
|
||||
Functions.XkbSetDetectableAutoRepeat(window.Display, true, out supported);
|
||||
using (new XLock(window.Display))
|
||||
{
|
||||
// Request that auto-repeat is only set on devices that support it physically.
|
||||
// This typically means that it's turned off for keyboards (which is what we want).
|
||||
// We prefer this method over XAutoRepeatOff/On, because the latter needs to
|
||||
// be reset before the program exits.
|
||||
if (Xkb.IsSupported(window.Display))
|
||||
{
|
||||
bool supported;
|
||||
Xkb.SetDetectableAutoRepeat(window.Display, true, out supported);
|
||||
}
|
||||
}
|
||||
|
||||
// The XInput2 extension makes keyboard and mouse handling much easier.
|
||||
// Check whether it is available.
|
||||
xi2_supported = XI2Mouse.IsSupported(window.Display);
|
||||
xi2_supported = XI2MouseKeyboard.IsSupported(window.Display);
|
||||
if (xi2_supported)
|
||||
{
|
||||
xi2_opcode = XI2Mouse.XIOpCode;
|
||||
xi2_version = XI2Mouse.XIVersion;
|
||||
xi2_opcode = XI2MouseKeyboard.XIOpCode;
|
||||
xi2_version = XI2MouseKeyboard.XIVersion;
|
||||
}
|
||||
|
||||
exists = true;
|
||||
|
@ -270,6 +277,7 @@ namespace OpenTK.Platform.X11
|
|||
{
|
||||
window.Screen = Functions.XDefaultScreen(window.Display); //API.DefaultScreen;
|
||||
window.RootWindow = Functions.XRootWindow(window.Display, window.Screen); // API.RootWindow;
|
||||
KeyMap = new X11KeyMap(window.Display);
|
||||
}
|
||||
|
||||
Debug.Print("Display: {0}, Screen {1}, Root window: {2}", window.Display, window.Screen,
|
||||
|
@ -658,7 +666,7 @@ namespace OpenTK.Platform.X11
|
|||
{
|
||||
Functions.XGetWindowProperty(window.Display, window.Handle,
|
||||
_atom_net_frame_extents, IntPtr.Zero, new IntPtr(16), false,
|
||||
(IntPtr)Atom.XA_CARDINAL, out atom, out format, out nitems, out bytes_after, ref prop);
|
||||
(IntPtr)AtomName.XA_CARDINAL, out atom, out format, out nitems, out bytes_after, ref prop);
|
||||
}
|
||||
|
||||
if ((prop != IntPtr.Zero))
|
||||
|
@ -851,7 +859,7 @@ namespace OpenTK.Platform.X11
|
|||
case XEventName.KeyRelease:
|
||||
bool pressed = e.type == XEventName.KeyPress;
|
||||
Key key;
|
||||
if (X11KeyMap.TranslateKey(ref e.KeyEvent, out key))
|
||||
if (KeyMap.TranslateKey(ref e.KeyEvent, out key))
|
||||
{
|
||||
if (pressed)
|
||||
{
|
||||
|
@ -986,6 +994,7 @@ namespace OpenTK.Platform.X11
|
|||
{
|
||||
Debug.Print("keybard mapping refreshed");
|
||||
Functions.XRefreshKeyboardMapping(ref e.MappingEvent);
|
||||
KeyMap.RefreshKeycodes(window.Display);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1719,7 +1728,6 @@ namespace OpenTK.Platform.X11
|
|||
}
|
||||
|
||||
window.Dispose();
|
||||
window = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
97
Source/OpenTK/Platform/X11/X11Input.cs
Normal file
97
Source/OpenTK/Platform/X11/X11Input.cs
Normal file
|
@ -0,0 +1,97 @@
|
|||
#region License
|
||||
//
|
||||
// X11Input.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.X11
|
||||
{
|
||||
class X11Input : IInputDriver2
|
||||
{
|
||||
readonly X11Mouse mouse = new X11Mouse();
|
||||
readonly X11Keyboard keyboard = new X11Keyboard();
|
||||
readonly X11Joystick joystick = new X11Joystick();
|
||||
readonly IGamePadDriver gamepad = new MappedGamePadDriver();
|
||||
|
||||
internal X11Input()
|
||||
{
|
||||
Debug.WriteLine("Using X11 core input driver.");
|
||||
Debug.WriteLine("[Warning] Mouse functionality will be significantly reduced.");
|
||||
Debug.WriteLine("[Warning] Copy OpenTK.dll.config to use the XI2 input driver instead.");
|
||||
}
|
||||
|
||||
#region IInputDriver2 Members
|
||||
|
||||
public IMouseDriver2 MouseDriver
|
||||
{
|
||||
get
|
||||
{
|
||||
return mouse;
|
||||
}
|
||||
}
|
||||
|
||||
public IKeyboardDriver2 KeyboardDriver
|
||||
{
|
||||
get
|
||||
{
|
||||
return keyboard;
|
||||
}
|
||||
}
|
||||
|
||||
public IGamePadDriver GamePadDriver
|
||||
{
|
||||
get
|
||||
{
|
||||
return gamepad;
|
||||
}
|
||||
}
|
||||
|
||||
public IJoystickDriver2 JoystickDriver
|
||||
{
|
||||
get
|
||||
{
|
||||
return joystick;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
joystick.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,30 @@
|
|||
#region --- License ---
|
||||
/* Licensed under the MIT/X11 license.
|
||||
* Copyright (c) 2006-2008 the OpenTK team.
|
||||
* This notice may not be removed.
|
||||
* See license.txt for licensing detailed licensing details.
|
||||
*/
|
||||
#region License
|
||||
//
|
||||
// X11KeyMap.cs
|
||||
//
|
||||
// Author:
|
||||
// Stefanos Apostolopoulos <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;
|
||||
|
@ -14,9 +35,230 @@ using OpenTK.Input;
|
|||
|
||||
namespace OpenTK.Platform.X11
|
||||
{
|
||||
static class X11KeyMap
|
||||
class X11KeyMap
|
||||
{
|
||||
public static Key GetKey(XKey key)
|
||||
// Keycode lookup table for current layout
|
||||
readonly Key[] keycodes = new Key[256];
|
||||
readonly bool xkb_supported;
|
||||
|
||||
internal X11KeyMap(IntPtr display)
|
||||
{
|
||||
xkb_supported = Xkb.IsSupported(display);
|
||||
if (xkb_supported)
|
||||
{
|
||||
RefreshKeycodes(display);
|
||||
}
|
||||
}
|
||||
|
||||
// Refreshes the keycodes lookup table based
|
||||
// on the current keyboard layout.
|
||||
internal void RefreshKeycodes(IntPtr display)
|
||||
{
|
||||
// Approach inspired by GLFW: http://www.glfw.org/
|
||||
// Huge props to the GLFW team!
|
||||
if (xkb_supported)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
// Xkb.GetKeyboard appears to be broken (multiple bug reports across distros)
|
||||
// so use Xkb.AllocKeyboard with Xkb.GetNames instead.
|
||||
XkbDesc* keyboard = Xkb.AllocKeyboard(display);
|
||||
if (keyboard != null)
|
||||
{
|
||||
Xkb.GetNames(display, XkbNamesMask.All, keyboard);
|
||||
|
||||
for (int i = 0; i < keycodes.Length; i++)
|
||||
{
|
||||
keycodes[i] = Key.Unknown;
|
||||
}
|
||||
|
||||
// Map all alphanumeric keys of this layout
|
||||
// Symbols are handled in GetKey() instead.
|
||||
for (int i = keyboard->min_key_code; i <= keyboard->max_key_code; ++i)
|
||||
{
|
||||
string name = new string((sbyte*)keyboard->names->keys[i].name, 0, Xkb.KeyNameLength);
|
||||
|
||||
Key key = Key.Unknown;
|
||||
switch (name)
|
||||
{
|
||||
case "TLDE":
|
||||
key = Key.Tilde;
|
||||
break;
|
||||
case "AE01":
|
||||
key = Key.Number1;
|
||||
break;
|
||||
case "AE02":
|
||||
key = Key.Number2;
|
||||
break;
|
||||
case "AE03":
|
||||
key = Key.Number3;
|
||||
break;
|
||||
case "AE04":
|
||||
key = Key.Number4;
|
||||
break;
|
||||
case "AE05":
|
||||
key = Key.Number5;
|
||||
break;
|
||||
case "AE06":
|
||||
key = Key.Number6;
|
||||
break;
|
||||
case "AE07":
|
||||
key = Key.Number7;
|
||||
break;
|
||||
case "AE08":
|
||||
key = Key.Number8;
|
||||
break;
|
||||
case "AE09":
|
||||
key = Key.Number9;
|
||||
break;
|
||||
case "AE10":
|
||||
key = Key.Number0;
|
||||
break;
|
||||
case "AE11":
|
||||
key = Key.Minus;
|
||||
break;
|
||||
case "AE12":
|
||||
key = Key.Plus;
|
||||
break;
|
||||
case "AD01":
|
||||
key = Key.Q;
|
||||
break;
|
||||
case "AD02":
|
||||
key = Key.W;
|
||||
break;
|
||||
case "AD03":
|
||||
key = Key.E;
|
||||
break;
|
||||
case "AD04":
|
||||
key = Key.R;
|
||||
break;
|
||||
case "AD05":
|
||||
key = Key.T;
|
||||
break;
|
||||
case "AD06":
|
||||
key = Key.Y;
|
||||
break;
|
||||
case "AD07":
|
||||
key = Key.U;
|
||||
break;
|
||||
case "AD08":
|
||||
key = Key.I;
|
||||
break;
|
||||
case "AD09":
|
||||
key = Key.O;
|
||||
break;
|
||||
case "AD10":
|
||||
key = Key.P;
|
||||
break;
|
||||
case "AD11":
|
||||
key = Key.BracketLeft;
|
||||
break;
|
||||
case "AD12":
|
||||
key = Key.BracketRight;
|
||||
break;
|
||||
case "AC01":
|
||||
key = Key.A;
|
||||
break;
|
||||
case "AC02":
|
||||
key = Key.S;
|
||||
break;
|
||||
case "AC03":
|
||||
key = Key.D;
|
||||
break;
|
||||
case "AC04":
|
||||
key = Key.F;
|
||||
break;
|
||||
case "AC05":
|
||||
key = Key.G;
|
||||
break;
|
||||
case "AC06":
|
||||
key = Key.H;
|
||||
break;
|
||||
case "AC07":
|
||||
key = Key.J;
|
||||
break;
|
||||
case "AC08":
|
||||
key = Key.K;
|
||||
break;
|
||||
case "AC09":
|
||||
key = Key.L;
|
||||
break;
|
||||
case "AC10":
|
||||
key = Key.Semicolon;
|
||||
break;
|
||||
case "AC11":
|
||||
key = Key.Quote;
|
||||
break;
|
||||
case "AB01":
|
||||
key = Key.Z;
|
||||
break;
|
||||
case "AB02":
|
||||
key = Key.X;
|
||||
break;
|
||||
case "AB03":
|
||||
key = Key.C;
|
||||
break;
|
||||
case "AB04":
|
||||
key = Key.V;
|
||||
break;
|
||||
case "AB05":
|
||||
key = Key.B;
|
||||
break;
|
||||
case "AB06":
|
||||
key = Key.N;
|
||||
break;
|
||||
case "AB07":
|
||||
key = Key.M;
|
||||
break;
|
||||
case "AB08":
|
||||
key = Key.Comma;
|
||||
break;
|
||||
case "AB09":
|
||||
key = Key.Period;
|
||||
break;
|
||||
case "AB10":
|
||||
key = Key.Slash;
|
||||
break;
|
||||
case "BKSL":
|
||||
key = Key.BackSlash;
|
||||
break;
|
||||
case "LSGT":
|
||||
key = Key.Unknown;
|
||||
break;
|
||||
default:
|
||||
key = Key.Unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
keycodes[i] = key;
|
||||
}
|
||||
|
||||
Xkb.FreeKeyboard(keyboard, 0, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Translate unknown keys (and symbols) using
|
||||
// regular layout-dependent GetKey()
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
if (keycodes[i] == Key.Unknown)
|
||||
{
|
||||
// TranslateKeyCode expects a XKeyEvent structure
|
||||
// Make one up
|
||||
XKeyEvent e = new XKeyEvent();
|
||||
e.display = display;
|
||||
e.keycode = i;
|
||||
Key key = Key.Unknown;
|
||||
if (TranslateKeyEvent(ref e, out key))
|
||||
{
|
||||
keycodes[i] = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Key TranslateXKey(XKey key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
|
@ -371,14 +613,26 @@ namespace OpenTK.Platform.X11
|
|||
}
|
||||
}
|
||||
|
||||
internal static bool TranslateKey(ref XKeyEvent e, out Key key)
|
||||
bool TranslateKeyEvent(ref XKeyEvent e, out Key key)
|
||||
{
|
||||
if (xkb_supported)
|
||||
{
|
||||
return TranslateKeyXkb(e.display, e.keycode, out key);
|
||||
}
|
||||
else
|
||||
{
|
||||
return TranslateKeyX11(ref e, out key);
|
||||
}
|
||||
}
|
||||
|
||||
bool TranslateKeyX11(ref XKeyEvent e, out Key key)
|
||||
{
|
||||
XKey keysym = (XKey)API.LookupKeysym(ref e, 0);
|
||||
XKey keysym2 = (XKey)API.LookupKeysym(ref e, 1);
|
||||
key = X11KeyMap.GetKey(keysym);
|
||||
key = X11KeyMap.TranslateXKey(keysym);
|
||||
if (key == Key.Unknown)
|
||||
{
|
||||
key = X11KeyMap.GetKey(keysym2);
|
||||
key = X11KeyMap.TranslateXKey(keysym2);
|
||||
}
|
||||
if (key == Key.Unknown)
|
||||
{
|
||||
|
@ -388,6 +642,59 @@ namespace OpenTK.Platform.X11
|
|||
return key != Key.Unknown;
|
||||
}
|
||||
|
||||
bool TranslateKeyXkb(IntPtr display, int keycode, out Key key)
|
||||
{
|
||||
if (keycode < 8 || keycode > 255)
|
||||
{
|
||||
key = Key.Unknown;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Translate the numeric keypad using the secondary group
|
||||
// (equivalent to NumLock = on)
|
||||
XKey keysym = Xkb.KeycodeToKeysym(display, keycode, 0, 1);
|
||||
switch (keysym)
|
||||
{
|
||||
case XKey.KP_0: key = Key.Keypad0; return true;
|
||||
case XKey.KP_1: key = Key.Keypad1; return true;
|
||||
case XKey.KP_2: key = Key.Keypad2; return true;
|
||||
case XKey.KP_3: key = Key.Keypad3; return true;
|
||||
case XKey.KP_4: key = Key.Keypad4; return true;
|
||||
case XKey.KP_5: key = Key.Keypad5; return true;
|
||||
case XKey.KP_6: key = Key.Keypad6; return true;
|
||||
case XKey.KP_7: key = Key.Keypad7; return true;
|
||||
case XKey.KP_8: key = Key.Keypad8; return true;
|
||||
case XKey.KP_9: key = Key.Keypad9; return true;
|
||||
case XKey.KP_Separator:
|
||||
case XKey.KP_Decimal: key = Key.KeypadDecimal; return true;
|
||||
case XKey.KP_Equal: key = Key.Unknown; return false; // Todo: fixme
|
||||
case XKey.KP_Enter: key = Key.KeypadEnter; return true;
|
||||
}
|
||||
|
||||
// Translate non-alphanumeric keys using the primary group
|
||||
keysym = Xkb.KeycodeToKeysym(display, keycode, 0, 0);
|
||||
key = TranslateXKey(keysym);
|
||||
return key != Key.Unknown;
|
||||
}
|
||||
|
||||
internal bool TranslateKey(int keycode, out Key key)
|
||||
{
|
||||
if (keycode < 0 || keycode > 255)
|
||||
{
|
||||
key = Key.Unknown;
|
||||
}
|
||||
else
|
||||
{
|
||||
key = keycodes[keycode];
|
||||
}
|
||||
return key != Key.Unknown;
|
||||
}
|
||||
|
||||
internal bool TranslateKey(ref XKeyEvent e, out Key key)
|
||||
{
|
||||
return TranslateKey(e.keycode, out key);
|
||||
}
|
||||
|
||||
internal static MouseButton TranslateButton(int button, out float wheelx, out float wheely)
|
||||
{
|
||||
wheelx = 0;
|
||||
|
|
|
@ -39,6 +39,7 @@ namespace OpenTK.Platform.X11
|
|||
readonly static string name = "Core X11 keyboard";
|
||||
readonly byte[] keys = new byte[32];
|
||||
readonly int KeysymsPerKeycode;
|
||||
readonly X11KeyMap KeyMap;
|
||||
KeyboardState state = new KeyboardState();
|
||||
|
||||
public X11Keyboard()
|
||||
|
@ -59,17 +60,15 @@ namespace OpenTK.Platform.X11
|
|||
ref KeysymsPerKeycode);
|
||||
Functions.XFree(keysym_ptr);
|
||||
|
||||
try
|
||||
if (Xkb.IsSupported(display))
|
||||
{
|
||||
// Request that auto-repeat is only set on devices that support it physically.
|
||||
// This typically means that it's turned off for keyboards what we want).
|
||||
// We prefer this method over XAutoRepeatOff/On, because the latter needs to
|
||||
// be reset before the program exits.
|
||||
bool supported;
|
||||
Functions.XkbSetDetectableAutoRepeat(display, true, out supported);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Xkb.SetDetectableAutoRepeat(display, true, out supported);
|
||||
KeyMap = new X11KeyMap(display);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -104,23 +103,13 @@ namespace OpenTK.Platform.X11
|
|||
using (new XLock(display))
|
||||
{
|
||||
Functions.XQueryKeymap(display, keys);
|
||||
for (int keycode = 8; keycode < 256; keycode++)
|
||||
for (int keycode = 0; keycode < 256; keycode++)
|
||||
{
|
||||
bool pressed = (keys[keycode >> 3] >> (keycode & 0x07) & 0x01) != 0;
|
||||
Key key;
|
||||
|
||||
for (int mod = 0; mod < KeysymsPerKeycode; mod++)
|
||||
if (KeyMap.TranslateKey(keycode, out key))
|
||||
{
|
||||
IntPtr keysym = Functions.XKeycodeToKeysym(display, (byte)keycode, mod);
|
||||
if (keysym != IntPtr.Zero)
|
||||
{
|
||||
key = X11KeyMap.GetKey((XKey)keysym);
|
||||
if (pressed)
|
||||
state.EnableBit((int)key);
|
||||
else
|
||||
state.DisableBit((int)key);
|
||||
break;
|
||||
}
|
||||
state[key] = pressed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
95
Source/OpenTK/Platform/X11/XI2Input.cs
Normal file
95
Source/OpenTK/Platform/X11/XI2Input.cs
Normal file
|
@ -0,0 +1,95 @@
|
|||
#region License
|
||||
//
|
||||
// XI2Input.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.X11
|
||||
{
|
||||
class XI2Input : IInputDriver2
|
||||
{
|
||||
readonly XI2MouseKeyboard mouse_keyboard = new XI2MouseKeyboard();
|
||||
readonly X11Joystick joystick = new X11Joystick();
|
||||
readonly IGamePadDriver gamepad = new MappedGamePadDriver();
|
||||
|
||||
internal XI2Input()
|
||||
{
|
||||
Debug.WriteLine("Using XI2 input driver.");
|
||||
}
|
||||
|
||||
#region IInputDriver2 Members
|
||||
|
||||
public IMouseDriver2 MouseDriver
|
||||
{
|
||||
get
|
||||
{
|
||||
return mouse_keyboard;
|
||||
}
|
||||
}
|
||||
|
||||
public IKeyboardDriver2 KeyboardDriver
|
||||
{
|
||||
get
|
||||
{
|
||||
return mouse_keyboard;
|
||||
}
|
||||
}
|
||||
|
||||
public IGamePadDriver GamePadDriver
|
||||
{
|
||||
get
|
||||
{
|
||||
return gamepad;
|
||||
}
|
||||
}
|
||||
|
||||
public IJoystickDriver2 JoystickDriver
|
||||
{
|
||||
get
|
||||
{
|
||||
return joystick;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
mouse_keyboard.Dispose();
|
||||
joystick.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -34,10 +34,12 @@ using OpenTK.Input;
|
|||
|
||||
namespace OpenTK.Platform.X11
|
||||
{
|
||||
sealed class XI2Mouse : IMouseDriver2, IDisposable
|
||||
sealed class XI2MouseKeyboard : IKeyboardDriver2, IMouseDriver2, IDisposable
|
||||
{
|
||||
const XEventName ExitEvent = XEventName.LASTEvent + 1;
|
||||
readonly object Sync = new object();
|
||||
readonly Thread ProcessingThread;
|
||||
readonly X11KeyMap KeyMap;
|
||||
bool disposed;
|
||||
|
||||
class XIMouse
|
||||
|
@ -48,6 +50,14 @@ namespace OpenTK.Platform.X11
|
|||
public XIScrollClassInfo ScrollY = new XIScrollClassInfo { number = -1 };
|
||||
public XIValuatorClassInfo MotionX = 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
|
||||
|
@ -66,9 +76,12 @@ namespace OpenTK.Platform.X11
|
|||
//static readonly IntPtr RelVertWheel;
|
||||
|
||||
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
|
||||
|
||||
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 static int XIOpCode { get; private set; }
|
||||
internal static int XIVersion { get; private set; }
|
||||
|
@ -76,7 +89,7 @@ namespace OpenTK.Platform.X11
|
|||
static readonly Functions.EventPredicate PredicateImpl = IsEventValid;
|
||||
readonly IntPtr Predicate = Marshal.GetFunctionPointerForDelegate(PredicateImpl);
|
||||
|
||||
static XI2Mouse()
|
||||
static XI2MouseKeyboard()
|
||||
{
|
||||
using (new XLock(API.DefaultDisplay))
|
||||
{
|
||||
|
@ -106,9 +119,8 @@ namespace OpenTK.Platform.X11
|
|||
}
|
||||
}
|
||||
|
||||
public XI2Mouse()
|
||||
public XI2MouseKeyboard()
|
||||
{
|
||||
Debug.WriteLine("Using XI2Mouse.");
|
||||
window = new X11WindowInfo();
|
||||
|
||||
window.Display = Functions.XOpenDisplay(IntPtr.Zero);
|
||||
|
@ -117,6 +129,8 @@ namespace OpenTK.Platform.X11
|
|||
window.Screen = Functions.XDefaultScreen(window.Display);
|
||||
window.RootWindow = Functions.XRootWindow(window.Display, window.Screen);
|
||||
window.Handle = window.RootWindow;
|
||||
|
||||
KeyMap = new X11KeyMap(window.Display);
|
||||
}
|
||||
|
||||
if (!IsSupported(window.Display))
|
||||
|
@ -124,6 +138,8 @@ namespace OpenTK.Platform.X11
|
|||
|
||||
using (new XLock(window.Display))
|
||||
using (XIEventMask mask = new XIEventMask(1,
|
||||
XIEventMasks.RawKeyPressMask |
|
||||
XIEventMasks.RawKeyReleaseMask |
|
||||
XIEventMasks.RawButtonPressMask |
|
||||
XIEventMasks.RawButtonReleaseMask |
|
||||
XIEventMasks.RawMotionMask |
|
||||
|
@ -132,10 +148,9 @@ namespace OpenTK.Platform.X11
|
|||
(XIEventMasks)(1 << (int)ExitEvent)))
|
||||
{
|
||||
XI.SelectEvents(window.Display, window.Handle, mask);
|
||||
UpdateDevices();
|
||||
}
|
||||
|
||||
UpdateDevices();
|
||||
|
||||
ProcessingThread = new Thread(ProcessEvents);
|
||||
ProcessingThread.IsBackground = true;
|
||||
ProcessingThread.Start();
|
||||
|
@ -181,127 +196,95 @@ namespace OpenTK.Platform.X11
|
|||
return false;
|
||||
}
|
||||
|
||||
void UpdateDevices()
|
||||
#region IKeyboardDriver2 Members
|
||||
|
||||
KeyboardState IKeyboardDriver2.GetState()
|
||||
{
|
||||
int count;
|
||||
unsafe
|
||||
lock (Sync)
|
||||
{
|
||||
XIDeviceInfo* list = (XIDeviceInfo*)XI.QueryDevice(window.Display, 1, out count);
|
||||
|
||||
Debug.Print("Refreshing mouse device list");
|
||||
Debug.Print("{0} mouse devices detected", count);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
KeyboardState state = new KeyboardState();
|
||||
foreach (XIKeyboard k in keyboards)
|
||||
{
|
||||
if (devices.Count <= i)
|
||||
{
|
||||
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;
|
||||
state.MergeBits(k.State);
|
||||
}
|
||||
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
|
||||
|
||||
public MouseState GetState()
|
||||
MouseState IMouseDriver2.GetState()
|
||||
{
|
||||
MouseState master = new MouseState();
|
||||
foreach (var d in devices)
|
||||
lock (Sync)
|
||||
{
|
||||
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)
|
||||
return devices[index].State;
|
||||
else
|
||||
lock (Sync)
|
||||
{
|
||||
if (index >= 0 && index < devices.Count)
|
||||
{
|
||||
return devices[index].State;
|
||||
}
|
||||
return new MouseState();
|
||||
}
|
||||
}
|
||||
|
||||
public MouseState GetCursorState()
|
||||
MouseState IMouseDriver2.GetCursorState()
|
||||
{
|
||||
MouseState master = GetState();
|
||||
master.X = (int)Interlocked.Read(ref cursor_x);
|
||||
master.Y = (int)Interlocked.Read(ref cursor_y);
|
||||
return master;
|
||||
lock (Sync)
|
||||
{
|
||||
MouseState master = (this as IMouseDriver2).GetState();
|
||||
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))
|
||||
{
|
||||
Functions.XWarpPointer(API.DefaultDisplay,
|
||||
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_y, (long)y);
|
||||
}
|
||||
|
@ -309,6 +292,127 @@ namespace OpenTK.Platform.X11
|
|||
|
||||
#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()
|
||||
{
|
||||
while (!disposed)
|
||||
|
@ -341,6 +445,8 @@ namespace OpenTK.Platform.X11
|
|||
// Nothing to do
|
||||
break;
|
||||
|
||||
case XIEventType.RawKeyPress:
|
||||
case XIEventType.RawKeyRelease:
|
||||
case XIEventType.RawMotion:
|
||||
case XIEventType.RawButtonPress:
|
||||
case XIEventType.RawButtonRelease:
|
||||
|
@ -360,59 +466,73 @@ namespace OpenTK.Platform.X11
|
|||
|
||||
void ProcessRawEvent(ref XGenericEventCookie cookie)
|
||||
{
|
||||
unsafe
|
||||
lock (Sync)
|
||||
{
|
||||
XIRawEvent raw = *(XIRawEvent*)cookie.data;
|
||||
|
||||
if (!rawids.ContainsKey(raw.deviceid))
|
||||
unsafe
|
||||
{
|
||||
Debug.Print("Unknown mouse device {0} encountered, ignoring.", raw.deviceid);
|
||||
return;
|
||||
}
|
||||
XIRawEvent raw = *(XIRawEvent*)cookie.data;
|
||||
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.RawMotion:
|
||||
ProcessRawMotion(d, ref raw);
|
||||
break;
|
||||
case XIEventType.RawButtonPress:
|
||||
case XIEventType.RawButtonRelease:
|
||||
if (GetMouseDevice(raw.deviceid, out mouse))
|
||||
{
|
||||
float dx, dy;
|
||||
MouseButton button = X11KeyMap.TranslateButton(raw.detail, out dx, out dy);
|
||||
mouse.State[button] = raw.evtype == XIEventType.RawButtonPress;
|
||||
}
|
||||
break;
|
||||
|
||||
case XIEventType.RawButtonPress:
|
||||
switch (raw.detail)
|
||||
{
|
||||
case 1: d.State.EnableBit((int)MouseButton.Left); break;
|
||||
case 2: d.State.EnableBit((int)MouseButton.Middle); break;
|
||||
case 3: d.State.EnableBit((int)MouseButton.Right); break;
|
||||
case 8: d.State.EnableBit((int)MouseButton.Button1); break;
|
||||
case 9: d.State.EnableBit((int)MouseButton.Button2); break;
|
||||
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;
|
||||
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;
|
||||
case XIEventType.RawKeyPress:
|
||||
case XIEventType.RawKeyRelease:
|
||||
if (GetKeyboardDevice(raw.deviceid, out keyboard))
|
||||
{
|
||||
Key key;
|
||||
if (KeyMap.TranslateKey(raw.detail, out key))
|
||||
{
|
||||
keyboard.State[key] = raw.evtype == XIEventType.RawKeyPress;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
// Note: we use the raw values here, without pointer
|
||||
|
@ -453,6 +573,8 @@ namespace OpenTK.Platform.X11
|
|||
bool valid = false;
|
||||
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.RawButtonPress;
|
||||
valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawButtonRelease;
|
||||
|
@ -470,6 +592,8 @@ namespace OpenTK.Platform.X11
|
|||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
public void Dispose()
|
||||
|
@ -496,7 +620,7 @@ namespace OpenTK.Platform.X11
|
|||
}
|
||||
}
|
||||
|
||||
~XI2Mouse()
|
||||
~XI2MouseKeyboard()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
Loading…
Reference in a new issue