Fix wxKeyEvent::GetKeyCode() for non-US keyboard layouts

Use the key code corresponding to the key in the US keyboard layout for
the key down/up events even when not actually using US layout, as this
is much more useful than simply returning 0 as was done before.

It also is compatible with the behaviour of the other ports.

See #17643, #23379, #23410.

(cherry picked from commit 2c0f6a2aa0c8d0650d1f856aa279ce7495925c17)
This commit is contained in:
Ivan Sorokin 2023-03-31 02:43:28 +03:00 committed by Vadim Zeitlin
parent 171369203f
commit 87ae8d5aa7
6 changed files with 256 additions and 7 deletions

View File

@ -61,7 +61,7 @@ case $(uname -s) in
extra_deps='libwebkit2gtk-4.0-dev libgspell-1-dev'
;;
2) libtoolkit_dev=libgtk2.0-dev
extra_deps='libwebkitgtk-dev'
extra_deps='libwebkitgtk-dev libxkbcommon-dev'
;;
*) echo 'Please specify wxGTK_VERSION explicitly.' >&2
exit 1

96
configure vendored
View File

@ -955,6 +955,8 @@ GSPELL_LIBS
GSPELL_CFLAGS
LIBSECRET_LIBS
LIBSECRET_CFLAGS
XKBCOMMON_LIBS
XKBCOMMON_CFLAGS
GXX_VERSION
LIBICONV
CXXFLAGS_VISIBILITY
@ -1436,6 +1438,8 @@ WAYLAND_EGL_CFLAGS
WAYLAND_EGL_LIBS
MesaGL_CFLAGS
MesaGL_LIBS
XKBCOMMON_CFLAGS
XKBCOMMON_LIBS
LIBSECRET_CFLAGS
LIBSECRET_LIBS
GSPELL_CFLAGS
@ -2478,6 +2482,10 @@ Some influential environment variables:
MesaGL_CFLAGS
C compiler flags for MesaGL, overriding pkg-config
MesaGL_LIBS linker flags for MesaGL, overriding pkg-config
XKBCOMMON_CFLAGS
C compiler flags for XKBCOMMON, overriding pkg-config
XKBCOMMON_LIBS
linker flags for XKBCOMMON, overriding pkg-config
LIBSECRET_CFLAGS
C compiler flags for LIBSECRET, overriding pkg-config
LIBSECRET_LIBS
@ -38043,6 +38051,94 @@ $as_echo "$as_me: WARNING: wxFileSystemWatcher won't be available on this platfo
fi
fi
if test "$wxUSE_GTK" = 1; then
if test "$USE_WIN32" != 1 -a "$USE_DARWIN" != 1; then
pkg_failed=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for XKBCOMMON" >&5
$as_echo_n "checking for XKBCOMMON... " >&6; }
if test -n "$PKG_CONFIG"; then
if test -n "$XKBCOMMON_CFLAGS"; then
pkg_cv_XKBCOMMON_CFLAGS="$XKBCOMMON_CFLAGS"
else
if test -n "$PKG_CONFIG" && \
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xkbcommon\""; } >&5
($PKG_CONFIG --exists --print-errors "xkbcommon") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_XKBCOMMON_CFLAGS=`$PKG_CONFIG --cflags "xkbcommon" 2>/dev/null`
else
pkg_failed=yes
fi
fi
else
pkg_failed=untried
fi
if test -n "$PKG_CONFIG"; then
if test -n "$XKBCOMMON_LIBS"; then
pkg_cv_XKBCOMMON_LIBS="$XKBCOMMON_LIBS"
else
if test -n "$PKG_CONFIG" && \
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xkbcommon\""; } >&5
($PKG_CONFIG --exists --print-errors "xkbcommon") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_XKBCOMMON_LIBS=`$PKG_CONFIG --libs "xkbcommon" 2>/dev/null`
else
pkg_failed=yes
fi
fi
else
pkg_failed=untried
fi
if test $pkg_failed = yes; then
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
XKBCOMMON_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "xkbcommon"`
else
XKBCOMMON_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "xkbcommon"`
fi
# Put the nasty error message in config.log where it belongs
echo "$XKBCOMMON_PKG_ERRORS" >&5
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libxkbcommon not found, key codes in key events may be incorrect" >&5
$as_echo "$as_me: WARNING: libxkbcommon not found, key codes in key events may be incorrect" >&2;}
elif test $pkg_failed = untried; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libxkbcommon not found, key codes in key events may be incorrect" >&5
$as_echo "$as_me: WARNING: libxkbcommon not found, key codes in key events may be incorrect" >&2;}
else
XKBCOMMON_CFLAGS=$pkg_cv_XKBCOMMON_CFLAGS
XKBCOMMON_LIBS=$pkg_cv_XKBCOMMON_LIBS
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
CFLAGS="$XKBCOMMON_CFLAGS $CFLAGS"
CXXFLAGS="$XKBCOMMON_CFLAGS $CXXFLAGS"
GUI_TK_LIBRARY="$GUI_TK_LIBRARY $XKBCOMMON_LIBS"
$as_echo "#define HAVE_XKBCOMMON 1" >>confdefs.h
fi
fi
fi
if test "$wxUSE_SECRETSTORE" = "yes"; then
if test "$WXGTK1" = "1"; then

View File

@ -5688,6 +5688,25 @@ if test "$wxUSE_FSWATCHER" = "yes"; then
fi
fi
dnl ---------------------------------------------------------------------------
dnl xkbcommon library for key code translations in wxGTK
dnl ---------------------------------------------------------------------------
if test "$wxUSE_GTK" = 1; then
if test "$USE_WIN32" != 1 -a "$USE_DARWIN" != 1; then
PKG_CHECK_MODULES(XKBCOMMON, [xkbcommon],
[
CFLAGS="$XKBCOMMON_CFLAGS $CFLAGS"
CXXFLAGS="$XKBCOMMON_CFLAGS $CXXFLAGS"
GUI_TK_LIBRARY="$GUI_TK_LIBRARY $XKBCOMMON_LIBS"
AC_DEFINE(HAVE_XKBCOMMON)
],
[
AC_MSG_WARN([libxkbcommon not found, key codes in key events may be incorrect])
]
)
fi
fi
dnl ---------------------------------------------------------------------------
dnl Secret storage
dnl ---------------------------------------------------------------------------

View File

@ -253,6 +253,7 @@ All (GUI):
wxGTK:
- Fix key codes with Shift/on non-US keyboards (Ivan Sorokin, #17643, #23379).
- Dramatically optimize adding many items to wxChoice (Ian McInerney, #23443).
- Improve document wxGLCanvas::CreateSurface() (Dan Gudmundsson, #23366).
- Fix loading WebKit2 extension when using wxWebView (Scott Talbert, #23497).

View File

@ -1246,6 +1246,9 @@
/* Define if setpriority() is available. */
#undef HAVE_SETPRIORITY
/* Define if xkbcommon is available */
#undef HAVE_XKBCOMMON
/* Define if xlocale.h header file exists. */
#undef HAVE_XLOCALE_H

View File

@ -24,6 +24,7 @@
#include "wx/settings.h"
#include "wx/msgdlg.h"
#include "wx/math.h"
#include "wx/module.h"
#endif
#include "wx/display.h"
@ -54,6 +55,12 @@ using namespace wxGTKImpl;
typedef guint KeySym;
#endif
// Use libxkbcommon for key code translation if we need and have it.
#if (defined (GDK_WINDOWING_X11) || defined (GDK_WINDOWING_WAYLAND)) && defined (HAVE_XKBCOMMON)
#define wxHAS_XKB
#include <xkbcommon/xkbcommon.h>
#endif
#ifdef __WXGTK4__
#define wxGTK_HAS_COMPOSITING_SUPPORT 0
#else
@ -225,6 +232,85 @@ static wxWindowGTK *gs_deferredFocusOut = NULL;
GdkEvent *g_lastMouseEvent = NULL;
int g_lastButtonNumber = 0;
#ifdef wxHAS_XKB
namespace
{
// Global data used for raw key codes translation.
class XkbData
{
public:
XkbData()
{
m_ctx = NULL;
m_keymap = NULL;
m_state = NULL;
}
// Get the state pointer allocating it on demand if necessary.
xkb_state* GetState()
{
if ( !m_state )
{
m_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
struct xkb_rule_names names = {0};
names.layout = "us";
m_keymap = xkb_keymap_new_from_names(m_ctx, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
m_state = xkb_state_new(m_keymap);
}
return m_state;
}
// Called by wxXKBModule::OnExit() to free all our data.
void Free()
{
if ( m_state )
{
xkb_state_unref(m_state);
m_state = NULL;
}
if ( m_keymap )
{
xkb_keymap_unref(m_keymap);
m_keymap = NULL;
}
if ( m_ctx )
{
xkb_context_unref(m_ctx);
m_ctx = NULL;
}
}
private:
xkb_context *m_ctx;
xkb_keymap *m_keymap;
xkb_state *m_state;
wxDECLARE_NO_COPY_CLASS(XkbData);
};
XkbData gs_xkbData;
} // anonymous namespace
// wxXKBModule: used for freeing global xkb data
class wxXKBModule : public wxModule
{
public:
bool OnInit() wxOVERRIDE { return true; }
void OnExit() wxOVERRIDE { gs_xkbData.Free(); }
private:
wxDECLARE_DYNAMIC_CLASS(wxXKBModule);
};
wxIMPLEMENT_DYNAMIC_CLASS(wxXKBModule, wxModule);
#endif // wxHAS_XKB
#ifdef __WXGTK3__
static GList* gs_sizeRevalidateList;
static GSList* gs_setSizeRequestList;
@ -1028,7 +1114,38 @@ wxTranslateGTKKeyEventToWx(wxKeyEvent& event,
: wxT("press"),
static_cast<unsigned long>(keysym));
long key_code = wxTranslateKeySymToWXKey(keysym, false /* !isChar */);
long key_code = 0;
#ifdef wxHAS_XKB
if ( gdk_keyval_to_unicode(gdk_event->keyval) )
{
// If the event has the corresponding Unicode char, let us set key_code
// to character that is generated by that key in the US keyboard layout
// (as wxMSW does and because it's useful to be able to identify the
// key independently of the current keyboard layout).
char key_code_str[64];
xkb_state_key_get_utf8(gs_xkbData.GetState(),
gdk_event->hardware_keycode,
key_code_str,
sizeof(key_code_str));
if ( strlen(key_code_str) == 1 )
{
key_code = key_code_str[0];
// If key_code is a Latin char, it should be in upper register to
// match the other ports.
if (islower(key_code))
{
key_code = toupper(key_code);
}
}
}
#endif // wxHAS_XKB
if ( !key_code )
{
key_code = wxTranslateKeySymToWXKey(keysym, false /* !isChar */);
}
if ( !key_code )
{
@ -1308,16 +1425,29 @@ gtk_window_key_press_callback( GtkWidget *WXUNUSED(widget),
}
}
if ( key_code )
const guint32 uniChar = gdk_keyval_to_unicode(keysym);
if ( key_code || uniChar )
{
wxKeyEvent eventChar(wxEVT_CHAR, event);
wxLogTrace(TRACE_KEYS, wxT("Char event: %ld"), key_code);
eventChar.m_keyCode = key_code;
if ( event.ControlDown() && isalpha(event.m_keyCode) )
{
// Ctrl+letter is handled specially by AdjustCharEventKeyCodes().
eventChar.m_keyCode = event.m_keyCode;
#if wxUSE_UNICODE
eventChar.m_uniChar = gdk_keyval_to_unicode(key_code);
eventChar.m_uniChar = event.m_uniChar;
#endif // wxUSE_UNICODE
}
else
{
// use Unicode values
eventChar.m_keyCode = key_code;
#if wxUSE_UNICODE
eventChar.m_uniChar = uniChar;
#endif // wxUSE_UNICODE
}
wxLogTrace(TRACE_KEYS, wxT("Char event: %ld"), eventChar.m_keyCode);
AdjustCharEventKeyCodes(eventChar);