[Input] Implement key repeat
authorthefiddler <stapostol@gmail.com>
Sun, 4 May 2014 22:43:45 +0000 (00:43 +0200)
committerthefiddler <stapostol@gmail.com>
Sun, 4 May 2014 22:43:45 +0000 (00:43 +0200)
Source/OpenTK/Input/KeyboardKeyEventArgs.cs
Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs
Source/OpenTK/Platform/NativeWindowBase.cs
Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs
Source/OpenTK/Platform/Windows/WinGLNative.cs
Source/OpenTK/Platform/X11/Structs.cs
Source/OpenTK/Platform/X11/X11GLNative.cs
Source/OpenTK/Platform/X11/XI2Mouse.cs

index a22afe8..614a8f9 100644 (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
     }
 }
index 3697f1b..5372e7b 100644 (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)
index 6827d97..4701381 100644 (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);
         }
 
index dfc38e3..dc23266 100644 (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
             {
index 6b377cb..f3661a1 100644 (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
                 {
index 3deefa1..6862273 100644 (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
+    }
 }
index 7061e21..447e415 100644 (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
index 96090e5..893783b 100644 (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);