[WORK-AROUND - call NON-PUBLIC method] Custom game loop (not using Game class) - only partial info from Keyboard.GetState

Using MonoGame on DX template, KeyboardState ks = Keyboard.GetState() in Game.Update override SUCCESSFULLY get updates to ks.CapsLock and ks.GetPressedKeys()

Trying to do the equivalent in a custom class (not inherited from Game) in a project NOT built using a MonoGame template.
REASON: That’s the way this code was written years ago.
[I’ll change to a standard approach if I have to, but there should be some easy way to get this existing code to work.]

In my equivalent to Update, I call FrameworkDispatcher.Update().
(If I don’t, not surprisingly, KeyboardState never changes at all.)

VB code (verified is called many times per second, by incrementing a counter variable):

    FrameworkDispatcher.Update()
    Dim ks As KeyboardState = Keyboard.GetState
    Dim keysN As Keys() = ks.GetPressedKeys()
    If keysN.Length > 0 OrElse ks.CapsLock Then
        Test()
    End If

Works: updates to ks.CapsLock. <-- proving that SOME updating is occurring.

Doesn’t work: NO updates to ks.GetPressedKeys() - always returns an empty array.
If I put a breakpoint on line Test(), then while holding “W”, press “CapsLock”,
I can examine the internal array and see that the keys all have value 0.

Q: Is there something I need to do early on, to initialize the keyboard input?
Or something else I need to do in addition to FrameworkDispatcher.Update()?

Try this one:

    public void SetKeyboardInput(bool enable)
    {
        var keyboardType = typeof(Microsoft.Xna.Framework.Input.Keyboard);
        var methodInfo = keyboardType.GetMethod("SetActive", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
        methodInfo.Invoke(null, new object[] { enable });
    }

@nkast - no improvement.

Testing on Microsoft Surface Pro (2017 edition).

Details (in VB) DOES NOT WORK:

    SetKeyboardInput(True)
    FrameworkDispatcher.Update()
    ...

Where:

' WORK-AROUND: Call NON-PUBLIC method.
Public Sub SetKeyboardInput(enable As Boolean)
    Dim keyboardType As Type = GetType(Microsoft.Xna.Framework.Input.Keyboard)
    Dim methodInfo As MethodInfo = keyboardType.GetMethod("SetActive", System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.[Static])
    methodInfo.Invoke(Nothing, New Object() {enable})
End Sub

Breakpoint on last line of SetKeyboardInput confirms that the method was found.

But result is still “CapsLock - changes, keys list - empty”.

UPDATE - bizarrely, after switching screens to type my previous reply, WHILE stopped at that “Test()” breakpoint, then returning to Visual Studio, “Continue F5”, the NEXT Keyboard.GetState DOES see the “W”.

This is strange, because when simply running in a loop, the W was never seen.
Somehow, switching screens and back changed something.

Focus problem maybe?

CapsLock is a persistent state, so all apps see it, but the keys themselves got intercepted?
Hmm, that doesn’t seem right, since it is their “Pressed” state that is being detected.

On XNA 3.1, I would see the keys no matter what app had focus - this is a low level input call, right?

Also curious is that the “W” was no longer being held down, so that info was held somewhere.
The second Keyboard.GetState was in later code - no intervening “FrameworkDispatcher.Update”. Logically, this should have seen same (wrong) state as the first call.


UPDATE #2 - FIXED!

Only calling Enable once - works.

VB:

Private Shared _KeyboardIsEnabled As Boolean

' WORK-AROUND: Call NON-PUBLIC method.
Public Sub SetKeyboardInput(enable As Boolean)
    If _KeyboardIsEnabled = enable Then Return

    Dim keyboardType As Type = GetType(Microsoft.Xna.Framework.Input.Keyboard)
    Dim methodInfo As MethodInfo = keyboardType.GetMethod("SetActive", System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.[Static])
    methodInfo.Invoke(Nothing, New Object() {enable})
    _KeyboardIsEnabled = enable
End Sub

I am using this on a WPF editor and enable the keyboard when the edit window gets focus and disable it whenever the edit window loses focus.

The edit window is a WindowsFormsHost and I am using the Desktop/Winforms dll version of MonoGame. I am rendering with a SwapChainRenderTarget.

I don’t know the details in your case therefore I can’t give any better alternative. I guess you have to dig into MG code yourself to see why the keys are not updated.

ahh, my edit of that previous post happened while you were editing your reply.

It is working now. Thank you!

ALTERNATIVE FIX:

Change the ORDER of the calls (Update, then SetKeyboardInput):

VB:

    FrameworkDispatcher.Update()
    SetKeyboardInput(True)

Where:

' WORK-AROUND: Call NON-PUBLIC method.
Public Sub SetKeyboardInput(enable As Boolean)
    Dim keyboardType As Type = GetType(Microsoft.Xna.Framework.Input.Keyboard)
    Dim methodInfo As MethodInfo = keyboardType.GetMethod("SetActive", System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.[Static])
    methodInfo.Invoke(Nothing, New Object() {enable})
End Sub

That’s interesting! I have no idea why.

As for XNA , yeah the keyboard works always no matter which window has focus.
Some low level DXinput I suppose. And that’s a bad thing for an editor where sometimes you want to forward the keys for editing. I tried to find a way to turn it off but seems to be impossible.

What did you do for Mouse input? I get no mouse state changes via XNA Mouse - see MouseState not changing in Custom game loop (no Game class)

I haven’t done anything about mouse at the moment.
I am using just keys for basic interactions testing with the game code.

In XNA you could set the Mouse.WndHandle property but this wont work in MG.
Probably you could use reflection to write on the private static readonly MouseState _defaultState .

OK, thanks for suggestion. For now, I wrote my own class, and changed my uses of Mouse functions to call this class instead. It also has an update method called per game tick. Then in future if Mouse is fixed, I can easily change my class methods to call through to Mouse. [this old app is Windows.Forms, so I use its Mouse features to build an XNA MouseState. Later will replace Windows.Forms with a lower-level solution.]

Here’s a modified Mouse.Windows.cs that works with WndHandle.

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace Microsoft.Xna.Framework.Input
{
    public static partial class Mouse
    {
        [DllImportAttribute("user32.dll", EntryPoint = "SetCursorPos")]
        [return: MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
        private static extern bool SetCursorPos(int X, int Y);

        private static Control _window;
        private static MouseInputWnd _mouseInputWnd = new MouseInputWnd();

        private static IntPtr PlatformGetWindowHandle()
        {
            return (_window == null) ? IntPtr.Zero : _window.Handle;
        }

        private static void PlatformSetWindowHandle(IntPtr windowHandle)
        {
            // Unregister old window
            if (_mouseInputWnd.Handle != IntPtr.Zero)
                _mouseInputWnd.ReleaseHandle();

            _window = Control.FromHandle(windowHandle);
            _mouseInputWnd.AssignHandle(windowHandle);
        }

        private static MouseState PlatformGetState(GameWindow window)
        {
            return window.MouseState;
        }
      
        private static MouseState PlatformGetState()
        {
            if (_window != null)
            {
                var pos = Control.MousePosition;
                var clientPos = _window.PointToClient(pos);
                var buttons = Control.MouseButtons;
                
                return new MouseState(
                    clientPos.X,
                    clientPos.Y,
                    _mouseInputWnd.ScrollWheelValue,
                    (buttons & MouseButtons.Left) == MouseButtons.Left ? ButtonState.Pressed : ButtonState.Released,
                    (buttons & MouseButtons.Middle) == MouseButtons.Middle ? ButtonState.Pressed : ButtonState.Released,
                    (buttons & MouseButtons.Right) == MouseButtons.Right ? ButtonState.Pressed : ButtonState.Released,                    
                    (buttons & MouseButtons.XButton1) == MouseButtons.XButton1 ? ButtonState.Pressed : ButtonState.Released,
                    (buttons & MouseButtons.XButton2) == MouseButtons.XButton2 ? ButtonState.Pressed : ButtonState.Released,
                    _mouseInputWnd.HorizontalScrollWheelValue
                    );
            }

            return _defaultState;
        }

        private static void PlatformSetPosition(int x, int y)
        {
            PrimaryWindow.MouseState.X = x;
            PrimaryWindow.MouseState.Y = y;
            
            var pt = _window.PointToScreen(new System.Drawing.Point(x, y));

            SetCursorPos(pt.X, pt.Y);
        }

        public static void PlatformSetCursor(MouseCursor cursor)
        {
            _window.Cursor = cursor.Cursor;
        }

        #region Nested class MouseInputWnd
        /// <remarks>
        /// Subclass WindowHandle to read WM_MOUSEHWHEEL messages
        /// </remarks>
        class MouseInputWnd : System.Windows.Forms.NativeWindow
        {
            const int WM_MOUSEMOVE   = 0x0200;
            const int WM_LBUTTONDOWN = 0x0201;
            const int WM_LBUTTONUP   = 0x0202;
            const int WM_RBUTTONDOWN = 0x0204;
            const int WM_RBUTTONUP   = 0x0205;
            const int WM_MBUTTONDOWN = 0x0207;
            const int WM_MBUTTONUP   = 0x0208;
            const int WM_MOUSEWHEEL  = 0x020A;
            const int WM_MOUSEHWHEEL = 0x020E;
            
            public int ScrollWheelValue = 0;
            public int HorizontalScrollWheelValue = 0;

            protected override void WndProc(ref Message m)
            {
                switch (m.Msg)
                {
                    case WM_MOUSEWHEEL:
                        var delta = (short)(((ulong)m.WParam >> 16) & 0xffff);
                        ScrollWheelValue += delta;
                        break;
                    case WM_MOUSEHWHEEL:
                        var deltaH = (short)(((ulong)m.WParam >> 16) & 0xffff);
                        HorizontalScrollWheelValue += deltaH;
                        break;
                }

                base.WndProc(ref m);
            }
        }
        #endregion Nested class MouseInputWnd
    }
}