0

I have run into a wxGetKeyState() issue with Wayland. Let me explain: In some of my apps, I add a test for the Shift key being pressed in the ctor of my app’s top wxFrame window. If the Shift key is down during launch, I run diagnostic code relevant to my app. This has always worked just fine until I switched to Ubuntu 22.04 with the Wayland display server. If I run my app in Ubuntu 22.04 with the X.org display sever, everything runs as expected. By the way, I’m using wxWidgets 3.2.0.

To test this possible bug just add these few lines of code to the end of the top wxFrame ctor.

MyFrame::MyFrame()
{
    ...
    if (wxGetKeyState(WXK_SHIFT))
    {
        wxMessageBox("Hello there");
    }
}

Does anyone have run into this issue? Is there a known work-around?

Regards, Bob

EDIT: When I run the minimal app (shown below) I see these results for X and Wayland.

Output when launching the app while holding the shift key down in X.

18:40:25: Debug: from CTOR: 1
18:40:25: Debug: from idle: 1
18:40:25: Debug: from idle: 1
18:40:25: Debug: from idle: 1
18:40:25: Debug: from idle: 1
18:40:25: Debug: from idle: 1
18:40:25: Debug: from idle: 1
18:40:25: Debug: from idle: 1
18:40:25: Debug: from idle: 1
18:40:25: Debug: from idle: 1
18:40:25: Debug: from idle: 1
18:40:25: Debug: from idle: 1
18:40:25: Debug: from idle: 1
18:40:25: Debug: from idle: 1
18:40:25: Debug: from idle: 1
18:40:25: Debug: from idle: 1

Under X, wxGetKeyState() behaves as expected. It goes thru 16 idle cycles before stopping while showing the correct value all along.

Now, this is the output when launching the app while holding the shift key down in Waylan.

18:32:43: Debug: from CTOR: 0
18:32:43: Debug: from idle: 0
18:32:43: Debug: from idle: 0
18:32:43: Debug: from idle: 0
18:32:43: Debug: from idle: 1

Under Wayland, the test at the ctor fails and it takes 3 idle cycles before reporting the correct value. I hope this test helps to identify and solve this issue.

Minimal test program:

#include <wx/wx.h>

class MyApp: public wxApp
{
public:
    virtual bool OnInit();
};

class MyFrame: public wxFrame
{
public:
            MyFrame();
    void    on_idle(wxIdleEvent& event);
};

wxIMPLEMENT_APP(MyApp);

bool MyApp::OnInit()
{
    MyFrame* wnd = new MyFrame();
    wnd->Show();
    return true;
}

MyFrame::MyFrame() : wxFrame(NULL, wxID_ANY, "minimal")
{
    Bind(wxEVT_IDLE, &MyFrame::on_idle, this);
    wxLogDebug("from CTOR: %d", wxGetKeyState(WXK_SHIFT));
}

void MyFrame::on_idle(wxIdleEvent& event)
{
    wxLogDebug("from idle: %d", wxGetKeyState(WXK_SHIFT));
}
genpfault
  • 51,148
  • 11
  • 85
  • 139
Bob Enohp
  • 1,302
  • 12
  • 11
  • @BobEnotip, can you build and run the keyboard sample and check its output? Also, is it by chance left vs right keys on the VM? – Igor Aug 05 '22 at 23:56
  • @Igor, I built the keyboard sample app and ran it under Wayland and X.org. The results are identical for both. I get Hook, KeyDown, and KeyUp entries when I press/release the left Shift and right Shift keys. No difference! I also tried the left vs right shift keys with my defective program and showed no difference either. – Bob Enohp Aug 06 '22 at 03:42
  • where are you trying to place this check? The keyboard sample use EVT__KEYDOWN for it? Also try to add the reproducible code in the sample, i.e. add the code you posted into the sample MyFrame constructor. Does it still works? – Igor Aug 06 '22 at 03:52
  • I added my `if (wxGetKeyState(WXK_SHIFT))...` test at the end of the MyFrame ctor of the keyboard sample program (line 288) and rebuilt the app. When I run the sample program the results are just like my defective app, i.e. the sample program also exhibits the problem under Wayland only. – Bob Enohp Aug 06 '22 at 04:17
  • @BobEnotip, I a not sure what do you expect to happen. Basically this check will be executed once and thats it. You should put it inside the EVT_KEYDOWN somewhere. What exactly are you trying to achieve? Do you want to check if the `Shift` is pressed during the application start-up? – Igor Aug 06 '22 at 04:20
  • As I explained in my original post, I'm trying to catch if the shift key is down when the app is launched. I have done this successfully by adding the said code in the constructor of the frame window while running under X.org; however, when I use Wayland the function `wxGetKeyState` fails to detect the shift key being pressed. This is definitely unexpected behavior. In the meantime I'm reverting to using X.org. – Bob Enohp Aug 06 '22 at 04:28
  • you can try to create an issue at github.wxwidgets.org and hopefully someone will come up with the fix... – Igor Aug 06 '22 at 04:47
  • Thanks Igor. I appreciate the effort. – Bob Enohp Aug 06 '22 at 15:05

2 Answers2

1

I implemented a temporary solution to my problem with the Wayland/wxGetKeyState() issue. The function get_key_state_hack(), below, offers the same functionally of wxGetKeyState() with the following caveats:

  1. The use of this function only makes sense when targeting Linux/Wayland. It does not provide any advantage in any other context. The day that wxGetKeyState() starts working correctly with Wayland, you won’t need this function anymore. As suggested by VZ, the problem seems to be with GDK.

  2. This function is only useful during the start-up of the program since after a few idle event cycles wxGetKeyState() works correctly.

  3. This function will block for a few idle event cycles. This is not a problem in my case since I use it as a one-time test to detect if the Shift key was held down during program launch and as soon as the user moves the mouse or touches the keyboard there will be an avalanche of idle events.

#include <wx/evtloop.h>

bool get_key_state_hack(wxKeyCode key)
{
    int   count = 5;
    bool  pressed = false;
    wxEventLoop loop;

    auto on_idle = [&](wxIdleEvent& event)
    {
        pressed = wxGetKeyState(key);

        if (pressed || (--count < 1))
        {
            loop.Exit();
        }
    };

    wxTheApp->Bind(wxEVT_IDLE, on_idle);
    loop.Run();
    wxTheApp->Unbind(wxEVT_IDLE, on_idle);

    return pressed;
}

By the way, I decided to use a lambda function as event handler to keep all the code nicely packed in a single C++ function.

I hope this function can be useful to anyone who faces this problem.

Bob Enohp
  • 1,302
  • 12
  • 11
0

This is supposed to work and I've just added a demonstration of this function to the keyboard sample and it behaves as expected both with X and Wayland for me, including showing "Shift" in the status bar when the corresponding key is pressed while launching the sample.

Looking at the code, Wayland support requires GTK 3.4 or later, but you definitely should have this (and actually a much more recent version) under Ubuntu 22.04, so I have no idea why it doesn't work. If you also see the problem in the keyboard sample, please try debugging this yourself, i.e. check what happens inside wxGetKeyStateGTK() function.

VZ.
  • 21,740
  • 3
  • 39
  • 42
  • Vadim, I added extra information to my original posting that could provide some insight into this issue. --Thanks – Bob Enohp Aug 07 '22 at 02:20
  • I added `wxLogDebug("%s", state);` to your newly added function `MyFrame::OnIdle()` (line 594) in keyboard.cpp. When I launch the app holding the shift key down in Wayland, it takes 3 incorrect values before displaying the correct result, i.e. the correct result shows up on the 4th idle cycle. – Bob Enohp Aug 07 '22 at 05:00
  • This looks like a problem in `gdk_keymap_get_modifier_state()` itself and I have no idea what can we do about it :-( Moreover, this function has been completely removed from the GDK public API in GTK 4, so it looks like we're going to be unable to do anything like this at all with GTK 4 and would only be able to retrieve modifiers for the key events. – VZ. Aug 07 '22 at 13:21