[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 #region Fields
Key key; Key key;
bool repeat;
KeyboardState state; KeyboardState state;
uint scancode;
#endregion #endregion
@ -65,7 +65,6 @@ namespace OpenTK.Input
public KeyboardKeyEventArgs(KeyboardKeyEventArgs args) public KeyboardKeyEventArgs(KeyboardKeyEventArgs args)
{ {
Key = args.Key; Key = args.Key;
ScanCode = args.ScanCode;
} }
#endregion #endregion
@ -87,8 +86,7 @@ namespace OpenTK.Input
[CLSCompliant(false)] [CLSCompliant(false)]
public uint ScanCode public uint ScanCode
{ {
get { return scancode; } get { return (uint)Key; }
internal set { scancode = value; }
} }
/// <summary> /// <summary>
@ -145,6 +143,21 @@ namespace OpenTK.Input
internal set { state = value; } 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 #endregion
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -1707,7 +1707,7 @@ namespace OpenTK.Platform.X11
public double root_y; public double root_y;
public double event_x; public double event_x;
public double event_y; public double event_y;
public int flags; public XIEventFlags flags;
public XIButtonState buttons; public XIButtonState buttons;
public XIValuatorState valuators; public XIValuatorState valuators;
public XIModifierState mods; public XIModifierState mods;
@ -1828,4 +1828,32 @@ namespace OpenTK.Platform.X11
RawButtonReleaseMask = (1 << (int)XIEventType.RawButtonRelease), RawButtonReleaseMask = (1 << (int)XIEventType.RawButtonRelease),
RawMotionMask = (1 << (int)XIEventType.RawMotion), 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; public static bool MouseWarpActive = false;
readonly bool xi2_supported;
readonly int xi2_opcode;
#endregion #endregion
#region Constructors #region Constructors
@ -228,6 +231,14 @@ namespace OpenTK.Platform.X11
bool supported; bool supported;
Functions.XkbSetDetectableAutoRepeat(window.Display, true, out 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; exists = true;
} }
@ -848,8 +859,41 @@ namespace OpenTK.Platform.X11
{ {
if (pressed) 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 // Raise KeyDown event
OnKeyDown(key); OnKeyDown(key, is_repeat);
} }
else 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 #endregion
#region Bounds #region Bounds

View file

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