[Input] Implement key repeat

This commit is contained in:
thefiddler 2014-05-05 00:43:45 +02:00
parent d968281a1b
commit 8b7d5bc7e4
8 changed files with 119 additions and 15 deletions

View file

@ -46,8 +46,8 @@ namespace OpenTK.Input
#region Fields
Key key;
bool repeat;
KeyboardState state;
uint scancode;
#endregion
@ -65,7 +65,6 @@ namespace OpenTK.Input
public KeyboardKeyEventArgs(KeyboardKeyEventArgs args)
{
Key = args.Key;
ScanCode = args.ScanCode;
}
#endregion
@ -87,8 +86,7 @@ namespace OpenTK.Input
[CLSCompliant(false)]
public uint ScanCode
{
get { return scancode; }
internal set { scancode = value; }
get { return (uint)Key; }
}
/// <summary>
@ -145,6 +143,21 @@ namespace OpenTK.Input
internal set { state = value; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> indicating whether
/// this key event is a repeat.
/// </summary>
/// <value>
/// true, if this event was caused by the user holding down
/// a key; false, if this was caused by the user pressing a
/// key for the first time.
/// </value>
public bool IsRepeat
{
get { return repeat; }
internal set { repeat = value; }
}
#endregion
}
}

View file

@ -416,10 +416,7 @@ namespace OpenTK.Platform.MacOS
//GetKey(keyCode, modifierFlags, keyArgs);
Key key = MacOSKeyMap.GetKey(keyCode);
if (!isARepeat || InputDriver.Keyboard[0].KeyRepeat)
{
OnKeyDown(key);
}
OnKeyDown(key, isARepeat);
var s = Cocoa.FromNSString(Cocoa.SendIntPtr(e, selCharactersIgnoringModifiers));
foreach (var c in s)

View file

@ -121,13 +121,14 @@ namespace OpenTK.Platform
WindowStateChanged(this, e);
}
protected void OnKeyDown(Key key)
protected void OnKeyDown(Key key, bool repeat)
{
KeyboardState.SetKeyState(key, true);
var e = KeyDownArgs;
e.Keyboard = KeyboardState;
e.Key = key;
e.IsRepeat = repeat;
KeyDown(this, e);
}
@ -145,6 +146,7 @@ namespace OpenTK.Platform
var e = KeyUpArgs;
e.Keyboard = KeyboardState;
e.Key = key;
e.IsRepeat = false;
KeyUp(this, e);
}

View file

@ -232,7 +232,7 @@ namespace OpenTK.Platform.SDL2
Key key = TranslateKey(ev.Key.Keysym.Scancode);
if (key_pressed)
{
window.OnKeyDown(key);
window.OnKeyDown(key, ev.Key.Repeat > 0);
}
else
{

View file

@ -581,7 +581,8 @@ namespace OpenTK.Platform.Windows
// In this case, both keys will be reported as pressed.
bool extended = (lParam.ToInt64() & ExtendedBit) != 0;
short scancode = (short)((lParam.ToInt64() >> 16) & 0xFF);
short scancode = (short)((lParam.ToInt64() >> 16) & 0xff);
ushort repeat_count = unchecked((ushort)((ulong)lParam.ToInt64() & 0xffffu));
VirtualKeys vkey = (VirtualKeys)wParam;
bool is_valid;
Key key = WinKeyMap.TranslateKey(scancode, vkey, extended, false, out is_valid);
@ -590,7 +591,7 @@ namespace OpenTK.Platform.Windows
{
if (pressed)
{
OnKeyDown(key);
OnKeyDown(key, repeat_count > 0);
}
else
{

View file

@ -1707,7 +1707,7 @@ namespace OpenTK.Platform.X11
public double root_y;
public double event_x;
public double event_y;
public int flags;
public XIEventFlags flags;
public XIButtonState buttons;
public XIValuatorState valuators;
public XIModifierState mods;
@ -1828,4 +1828,32 @@ namespace OpenTK.Platform.X11
RawButtonReleaseMask = (1 << (int)XIEventType.RawButtonRelease),
RawMotionMask = (1 << (int)XIEventType.RawMotion),
}
[Flags]
enum XIKeyEventFlags
{
Repeat = (1 << 16),
}
[Flags]
enum XIPointerEventFlags
{
Emulated = (1 << 16),
}
[Flags]
enum XITouchEventFlags
{
PendingEnd = (1 << 16),
EmulatingPointer = (1 << 17),
}
[Flags]
enum XIEventFlags
{
KeyRepeat = XIKeyEventFlags.Repeat,
PointerEmulated = XIPointerEventFlags.Emulated,
TouchPendingEnd = XITouchEventFlags.PendingEnd,
TouchEmulatingPointer = XITouchEventFlags.EmulatingPointer
}
}

View file

@ -124,6 +124,9 @@ namespace OpenTK.Platform.X11
public static bool MouseWarpActive = false;
readonly bool xi2_supported;
readonly int xi2_opcode;
#endregion
#region Constructors
@ -228,6 +231,14 @@ namespace OpenTK.Platform.X11
bool supported;
Functions.XkbSetDetectableAutoRepeat(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);
if (xi2_supported)
{
xi2_opcode = XI2Mouse.XIOpCode;
}
exists = true;
}
@ -848,8 +859,41 @@ namespace OpenTK.Platform.X11
{
if (pressed)
{
// Check if this is a key repeat event.
// X11 does not provide this information,
// so we rely on the XInput2 extension for that.
// Todo: hack this when XInput2 is not available
// by checking if another KeyPress event is enqueued.
bool is_repeat = false;
if (xi2_supported && e.GenericEventCookie.extension == xi2_opcode)
{
if (e.GenericEventCookie.evtype == (int)XIEventType.KeyPress)
{
unsafe
{
XIDeviceEvent* xi = (XIDeviceEvent*)e.GenericEventCookie.data;
is_repeat = (xi->flags & XIEventFlags.KeyRepeat) != 0;
}
}
}
else
{
if (API.Pending(window.Display) > 0)
{
unsafe
{
XEvent dummy = new XEvent();
KeyRepeatTestData arg = new KeyRepeatTestData();
arg.Event = e;
API.CheckIfEvent(window.Display, ref dummy, IsKeyRepeatPredicate,
new IntPtr(&arg));
is_repeat = arg.IsRepeat;
}
}
}
// Raise KeyDown event
OnKeyDown(key);
OnKeyDown(key, is_repeat);
}
else
{
@ -1010,6 +1054,24 @@ namespace OpenTK.Platform.X11
}
}
struct KeyRepeatTestData
{
public XEvent Event;
public bool IsRepeat;
}
unsafe static bool IsKeyRepeatPredicate(IntPtr display, ref XEvent e, IntPtr arg)
{
// IsRepeat is true when the event queue contains an identical
// KeyPress event at later time no greater than 2.
KeyRepeatTestData* data = (KeyRepeatTestData*)arg;
data->IsRepeat =
e.type == XEventName.KeyPress &&
e.KeyEvent.keycode == data->Event.KeyEvent.keycode &&
e.KeyEvent.time.ToInt64() - data->Event.KeyEvent.time.ToInt64() < 2;
return false; // keep the event in the queue
}
#endregion
#region Bounds

View file

@ -40,7 +40,8 @@ namespace OpenTK.Platform.X11
List<MouseState> mice = new List<MouseState>();
Dictionary<int, int> rawids = new Dictionary<int, int>(); // maps raw ids to mouse ids
internal readonly X11WindowInfo window;
static int XIOpCode;
internal static int XIOpCode { get; private set; }
static bool supported;
static readonly Functions.EventPredicate PredicateImpl = IsEventValid;
readonly IntPtr Predicate = Marshal.GetFunctionPointerForDelegate(PredicateImpl);