From 8b7d5bc7e492c688ed6183e0df4408f64b31dde0 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 5 May 2014 00:43:45 +0200 Subject: [PATCH] [Input] Implement key repeat --- Source/OpenTK/Input/KeyboardKeyEventArgs.cs | 21 ++++-- .../Platform/MacOS/CocoaNativeWindow.cs | 5 +- Source/OpenTK/Platform/NativeWindowBase.cs | 4 +- .../OpenTK/Platform/SDL2/Sdl2NativeWindow.cs | 2 +- Source/OpenTK/Platform/Windows/WinGLNative.cs | 5 +- Source/OpenTK/Platform/X11/Structs.cs | 30 ++++++++- Source/OpenTK/Platform/X11/X11GLNative.cs | 64 ++++++++++++++++++- Source/OpenTK/Platform/X11/XI2Mouse.cs | 3 +- 8 files changed, 119 insertions(+), 15 deletions(-) diff --git a/Source/OpenTK/Input/KeyboardKeyEventArgs.cs b/Source/OpenTK/Input/KeyboardKeyEventArgs.cs index a22afe8e..614a8f95 100644 --- a/Source/OpenTK/Input/KeyboardKeyEventArgs.cs +++ b/Source/OpenTK/Input/KeyboardKeyEventArgs.cs @@ -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; } } /// @@ -145,6 +143,21 @@ namespace OpenTK.Input internal set { state = value; } } + /// + /// Gets a indicating whether + /// this key event is a repeat. + /// + /// + /// 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. + /// + public bool IsRepeat + { + get { return repeat; } + internal set { repeat = value; } + } + #endregion } } diff --git a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs index 3697f1bf..5372e7b2 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs @@ -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) diff --git a/Source/OpenTK/Platform/NativeWindowBase.cs b/Source/OpenTK/Platform/NativeWindowBase.cs index 6827d976..47013810 100644 --- a/Source/OpenTK/Platform/NativeWindowBase.cs +++ b/Source/OpenTK/Platform/NativeWindowBase.cs @@ -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); } diff --git a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs index dfc38e30..dc232663 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs @@ -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 { diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index 6b377cb8..f3661a19 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -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 { diff --git a/Source/OpenTK/Platform/X11/Structs.cs b/Source/OpenTK/Platform/X11/Structs.cs index 3deefa1a..68622733 100644 --- a/Source/OpenTK/Platform/X11/Structs.cs +++ b/Source/OpenTK/Platform/X11/Structs.cs @@ -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 + } } diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index 7061e21e..447e415d 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -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 diff --git a/Source/OpenTK/Platform/X11/XI2Mouse.cs b/Source/OpenTK/Platform/X11/XI2Mouse.cs index 96090e55..893783b0 100644 --- a/Source/OpenTK/Platform/X11/XI2Mouse.cs +++ b/Source/OpenTK/Platform/X11/XI2Mouse.cs @@ -40,7 +40,8 @@ namespace OpenTK.Platform.X11 List mice = new List(); Dictionary rawids = new Dictionary(); // 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);