Merge remote-tracking branch 'origin/5.13' into dev

Change-Id: Ic0037eac1d85a0e60e7b1a590d49b5ee6205bfc8
This commit is contained in:
Qt Forward Merge Bot 2019-03-03 01:00:24 +01:00
commit 84e15d6f48
40 changed files with 1329 additions and 2040 deletions

View File

@ -200,7 +200,9 @@ CMAKE_QT5_MODULE_DEPS = $$join(lib_deps, ";")
CMAKE_INTERFACE_MODULE_DEPS = $$join(aux_mod_deps, ";") CMAKE_INTERFACE_MODULE_DEPS = $$join(aux_mod_deps, ";")
CMAKE_INTERFACE_QT5_MODULE_DEPS = $$join(aux_lib_deps, ";") CMAKE_INTERFACE_QT5_MODULE_DEPS = $$join(aux_lib_deps, ";")
CMAKE_QT_STEM = Qt$$QT_MAJOR_VERSION$${CMAKE_MODULE_NAME}$${QT_LIBINFIX} # TARGET here is the one changed at the end of qt_module.prf,
# which already contains the Qt5 prefix and QT_LIBINFIX suffix
CMAKE_QT_STEM = $${TARGET}
mac { mac {
!isEmpty(CMAKE_STATIC_TYPE) { !isEmpty(CMAKE_STATIC_TYPE) {

View File

@ -338,7 +338,7 @@ defineTest(qtConfParseCommandLine) {
qtConfAddWarning("Command line option -skip is only effective in top-level builds.") qtConfAddWarning("Command line option -skip is only effective in top-level builds.")
skipOptionWarningAdded = 1 skipOptionWarningAdded = 1
} }
$$qtConfGetNextCommandlineArg() val = $$qtConfGetNextCommandlineArg()
next() next()
} }

View File

@ -532,7 +532,7 @@ MakefileGenerator::init()
QStack<int> state; QStack<int> state;
enum { IN_CONDITION, MET_CONDITION, PENDING_CONDITION }; enum { IN_CONDITION, MET_CONDITION, PENDING_CONDITION };
for (int count = 1; !in.atEnd(); ++count) { for (int count = 1; !in.atEnd(); ++count) {
QString line = QString::fromUtf8(in.readLine()); QString line = QString::fromLatin1(in.readLine());
if (line.startsWith("!!IF ")) { if (line.startsWith("!!IF ")) {
if (state.isEmpty() || state.top() == IN_CONDITION) { if (state.isEmpty() || state.top() == IN_CONDITION) {
QString test = line.mid(5, line.length()-(5+1)); QString test = line.mid(5, line.length()-(5+1));
@ -578,7 +578,7 @@ MakefileGenerator::init()
contents += project->expand(line, in.fileName(), count); contents += project->expand(line, in.fileName(), count);
} }
} }
contentBytes = contents.toUtf8(); contentBytes = contents.toLatin1();
} }
QFile out(outn); QFile out(outn);
QFileInfo outfi(out); QFileInfo outfi(out);

View File

@ -344,7 +344,9 @@ public class QtActivityDelegate
} }
} else if ((inputHints & ImhHiddenText) != 0) { } else if ((inputHints & ImhHiddenText) != 0) {
inputType |= android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD; inputType |= android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD;
} else if ((inputHints & ImhSensitiveData) != 0 || (inputHints & ImhNoPredictiveText) != 0) { } else if ((inputHints & ImhSensitiveData) != 0 ||
((inputHints & ImhNoPredictiveText) != 0 &&
System.getenv("QT_ANDROID_ENABLE_WORKAROUND_TO_DISABLE_PREDICTIVE_TEXT") != null)) {
inputType |= android.text.InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; inputType |= android.text.InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
} }

View File

@ -0,0 +1,35 @@
TARGET = QtInputSupport
MODULE = input_support
QT = core-private gui-private devicediscovery_support-private
CONFIG += static internal_module
DEFINES += QT_NO_CAST_FROM_ASCII
PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h
qtConfig(evdev) {
include($$PWD/evdevmouse/evdevmouse.pri)
include($$PWD/evdevkeyboard/evdevkeyboard.pri)
include($$PWD/evdevtouch/evdevtouch.pri)
qtConfig(tabletevent) {
include($$PWD/evdevtablet/evdevtablet.pri)
}
}
qtConfig(tslib) {
include($$PWD/tslib/tslib.pri)
}
qtConfig(libinput) {
include($$PWD/libinput/libinput.pri)
}
qtConfig(evdev)|qtConfig(libinput) {
include($$PWD/shared/shared.pri)
}
qtConfig(integrityhid) {
include($$PWD/integrityhid/integrityhid.pri)
}
load(qt_module)

View File

@ -1,35 +1,8 @@
TARGET = QtInputSupport TEMPLATE = subdirs
MODULE = input_support QT_FOR_CONFIG += gui-private
QT = core-private gui-private devicediscovery_support-private qtConfig(xkbcommon): SUBDIRS += xkbcommon
CONFIG += static internal_module
DEFINES += QT_NO_CAST_FROM_ASCII SUBDIRS += input-support.pro ### FIXME - QTBUG-52657
PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h
qtConfig(evdev) { CONFIG += ordered
include($$PWD/evdevmouse/evdevmouse.pri)
include($$PWD/evdevkeyboard/evdevkeyboard.pri)
include($$PWD/evdevtouch/evdevtouch.pri)
qtConfig(tabletevent) {
include($$PWD/evdevtablet/evdevtablet.pri)
}
}
qtConfig(tslib) {
include($$PWD/tslib/tslib.pri)
}
qtConfig(libinput) {
include($$PWD/libinput/libinput.pri)
}
qtConfig(evdev)|qtConfig(libinput) {
include($$PWD/shared/shared.pri)
}
qtConfig(integrityhid) {
include($$PWD/integrityhid/integrityhid.pri)
}
load(qt_module)

View File

@ -14,4 +14,7 @@ QMAKE_USE_PRIVATE += libudev libinput
INCLUDEPATH += $$PWD/../shared INCLUDEPATH += $$PWD/../shared
qtConfig(xkbcommon): QMAKE_USE_PRIVATE += xkbcommon qtConfig(xkbcommon): {
QMAKE_USE_PRIVATE += xkbcommon
QT += xkbcommon_support-private
}

View File

@ -47,6 +47,7 @@
#if QT_CONFIG(xkbcommon) #if QT_CONFIG(xkbcommon)
#include <xkbcommon/xkbcommon-keysyms.h> #include <xkbcommon/xkbcommon-keysyms.h>
#include <xkbcommon/xkbcommon-names.h> #include <xkbcommon/xkbcommon-names.h>
#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
#endif #endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -56,88 +57,7 @@ Q_DECLARE_LOGGING_CATEGORY(qLcLibInput)
const int REPEAT_DELAY = 500; const int REPEAT_DELAY = 500;
const int REPEAT_RATE = 100; const int REPEAT_RATE = 100;
#if QT_CONFIG(xkbcommon)
struct KeyTabEntry {
int xkbkey;
int qtkey;
};
static inline bool operator==(const KeyTabEntry &a, const KeyTabEntry &b)
{
return a.xkbkey == b.xkbkey;
}
static const KeyTabEntry keyTab[] = {
{ XKB_KEY_Escape, Qt::Key_Escape },
{ XKB_KEY_Tab, Qt::Key_Tab },
{ XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab },
{ XKB_KEY_BackSpace, Qt::Key_Backspace },
{ XKB_KEY_Return, Qt::Key_Return },
{ XKB_KEY_Insert, Qt::Key_Insert },
{ XKB_KEY_Delete, Qt::Key_Delete },
{ XKB_KEY_Clear, Qt::Key_Delete },
{ XKB_KEY_Pause, Qt::Key_Pause },
{ XKB_KEY_Print, Qt::Key_Print },
{ XKB_KEY_Home, Qt::Key_Home },
{ XKB_KEY_End, Qt::Key_End },
{ XKB_KEY_Left, Qt::Key_Left },
{ XKB_KEY_Up, Qt::Key_Up },
{ XKB_KEY_Right, Qt::Key_Right },
{ XKB_KEY_Down, Qt::Key_Down },
{ XKB_KEY_Prior, Qt::Key_PageUp },
{ XKB_KEY_Next, Qt::Key_PageDown },
{ XKB_KEY_Shift_L, Qt::Key_Shift },
{ XKB_KEY_Shift_R, Qt::Key_Shift },
{ XKB_KEY_Shift_Lock, Qt::Key_Shift },
{ XKB_KEY_Control_L, Qt::Key_Control },
{ XKB_KEY_Control_R, Qt::Key_Control },
{ XKB_KEY_Meta_L, Qt::Key_Meta },
{ XKB_KEY_Meta_R, Qt::Key_Meta },
{ XKB_KEY_Alt_L, Qt::Key_Alt },
{ XKB_KEY_Alt_R, Qt::Key_Alt },
{ XKB_KEY_Caps_Lock, Qt::Key_CapsLock },
{ XKB_KEY_Num_Lock, Qt::Key_NumLock },
{ XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock },
{ XKB_KEY_Super_L, Qt::Key_Super_L },
{ XKB_KEY_Super_R, Qt::Key_Super_R },
{ XKB_KEY_Menu, Qt::Key_Menu },
{ XKB_KEY_Hyper_L, Qt::Key_Hyper_L },
{ XKB_KEY_Hyper_R, Qt::Key_Hyper_R },
{ XKB_KEY_Help, Qt::Key_Help },
{ XKB_KEY_KP_Space, Qt::Key_Space },
{ XKB_KEY_KP_Tab, Qt::Key_Tab },
{ XKB_KEY_KP_Enter, Qt::Key_Enter },
{ XKB_KEY_KP_Home, Qt::Key_Home },
{ XKB_KEY_KP_Left, Qt::Key_Left },
{ XKB_KEY_KP_Up, Qt::Key_Up },
{ XKB_KEY_KP_Right, Qt::Key_Right },
{ XKB_KEY_KP_Down, Qt::Key_Down },
{ XKB_KEY_KP_Prior, Qt::Key_PageUp },
{ XKB_KEY_KP_Next, Qt::Key_PageDown },
{ XKB_KEY_KP_End, Qt::Key_End },
{ XKB_KEY_KP_Begin, Qt::Key_Clear },
{ XKB_KEY_KP_Insert, Qt::Key_Insert },
{ XKB_KEY_KP_Delete, Qt::Key_Delete },
{ XKB_KEY_KP_Equal, Qt::Key_Equal },
{ XKB_KEY_KP_Multiply, Qt::Key_Asterisk },
{ XKB_KEY_KP_Add, Qt::Key_Plus },
{ XKB_KEY_KP_Separator, Qt::Key_Comma },
{ XKB_KEY_KP_Subtract, Qt::Key_Minus },
{ XKB_KEY_KP_Decimal, Qt::Key_Period },
{ XKB_KEY_KP_Divide, Qt::Key_Slash },
};
#endif
QLibInputKeyboard::QLibInputKeyboard() QLibInputKeyboard::QLibInputKeyboard()
#if QT_CONFIG(xkbcommon)
: m_ctx(0),
m_keymap(0),
m_state(0),
m_mods(Qt::NoModifier)
#endif
{ {
#if QT_CONFIG(xkbcommon) #if QT_CONFIG(xkbcommon)
qCDebug(qLcLibInput) << "Using xkbcommon for key mapping"; qCDebug(qLcLibInput) << "Using xkbcommon for key mapping";
@ -148,18 +68,14 @@ QLibInputKeyboard::QLibInputKeyboard()
} }
m_keymap = xkb_keymap_new_from_names(m_ctx, nullptr, XKB_KEYMAP_COMPILE_NO_FLAGS); m_keymap = xkb_keymap_new_from_names(m_ctx, nullptr, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!m_keymap) { if (!m_keymap) {
qWarning("Failed to compile keymap"); qCWarning(qLcLibInput, "Failed to compile keymap");
return; return;
} }
m_state = xkb_state_new(m_keymap); m_state = xkb_state_new(m_keymap);
if (!m_state) { if (!m_state) {
qWarning("Failed to create xkb state"); qCWarning(qLcLibInput, "Failed to create xkb state");
return; return;
} }
m_modindex[0] = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_CTRL);
m_modindex[1] = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_ALT);
m_modindex[2] = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_SHIFT);
m_modindex[3] = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_LOGO);
m_repeatTimer.setSingleShot(true); m_repeatTimer.setSingleShot(true);
connect(&m_repeatTimer, &QTimer::timeout, this, &QLibInputKeyboard::handleRepeat); connect(&m_repeatTimer, &QTimer::timeout, this, &QLibInputKeyboard::handleRepeat);
@ -186,52 +102,33 @@ void QLibInputKeyboard::processKey(libinput_event_keyboard *e)
if (!m_ctx || !m_keymap || !m_state) if (!m_ctx || !m_keymap || !m_state)
return; return;
const uint32_t k = libinput_event_keyboard_get_key(e) + 8; const uint32_t keycode = libinput_event_keyboard_get_key(e) + 8;
const xkb_keysym_t sym = xkb_state_key_get_one_sym(m_state, keycode);
const bool pressed = libinput_event_keyboard_get_key_state(e) == LIBINPUT_KEY_STATE_PRESSED; const bool pressed = libinput_event_keyboard_get_key_state(e) == LIBINPUT_KEY_STATE_PRESSED;
QVarLengthArray<char, 32> chars(32); // Modifiers here is the modifier state before the event, i.e. not
const int size = xkb_state_key_get_utf8(m_state, k, chars.data(), chars.size()); // including the current key in case it is a modifier. See the XOR
if (Q_UNLIKELY(size + 1 > chars.size())) { // +1 for NUL // logic in QKeyEvent::modifiers(). ### QTBUG-73826
chars.resize(size + 1); Qt::KeyboardModifiers modifiers = QXkbCommon::modifiers(m_state);
xkb_state_key_get_utf8(m_state, k, chars.data(), chars.size());
}
const QString text = QString::fromUtf8(chars.constData(), size);
const xkb_keysym_t sym = xkb_state_key_get_one_sym(m_state, k); const QString text = QXkbCommon::lookupString(m_state, keycode);
const int qtkey = QXkbCommon::keysymToQtKey(sym, modifiers, m_state, keycode);
// mods here is the modifier state before the event, i.e. not xkb_state_update_key(m_state, keycode, pressed ? XKB_KEY_DOWN : XKB_KEY_UP);
// including the current key in case it is a modifier.
Qt::KeyboardModifiers mods = Qt::NoModifier;
const int qtkey = keysymToQtKey(sym, &mods, text);
if (qtkey == Qt::Key_Control) Qt::KeyboardModifiers modifiersAfterStateChange = QXkbCommon::modifiers(m_state);
mods |= Qt::ControlModifier; QGuiApplicationPrivate::inputDeviceManager()->setKeyboardModifiers(modifiersAfterStateChange);
if (qtkey == Qt::Key_Alt)
mods |= Qt::AltModifier;
if (qtkey == Qt::Key_Shift)
mods |= Qt::ShiftModifier;
if (qtkey == Qt::Key_Meta)
mods |= Qt::MetaModifier;
xkb_state_update_key(m_state, k, pressed ? XKB_KEY_DOWN : XKB_KEY_UP);
if (mods != Qt::NoModifier) {
if (pressed)
m_mods |= mods;
else
m_mods &= ~mods;
QGuiApplicationPrivate::inputDeviceManager()->setKeyboardModifiers(m_mods);
}
QWindowSystemInterface::handleExtendedKeyEvent(nullptr, QWindowSystemInterface::handleExtendedKeyEvent(nullptr,
pressed ? QEvent::KeyPress : QEvent::KeyRelease, pressed ? QEvent::KeyPress : QEvent::KeyRelease,
qtkey, m_mods, k, sym, m_mods, text); qtkey, modifiers, keycode, sym, modifiers, text);
if (pressed && xkb_keymap_key_repeats(m_keymap, k)) { if (pressed && xkb_keymap_key_repeats(m_keymap, keycode)) {
m_repeatData.qtkey = qtkey; m_repeatData.qtkey = qtkey;
m_repeatData.mods = mods; m_repeatData.mods = modifiers;
m_repeatData.nativeScanCode = k; m_repeatData.nativeScanCode = keycode;
m_repeatData.virtualKey = sym; m_repeatData.virtualKey = sym;
m_repeatData.nativeMods = mods; m_repeatData.nativeMods = modifiers;
m_repeatData.unicodeText = text; m_repeatData.unicodeText = text;
m_repeatData.repeatCount = 1; m_repeatData.repeatCount = 1;
m_repeatTimer.setInterval(REPEAT_DELAY); m_repeatTimer.setInterval(REPEAT_DELAY);
@ -256,50 +153,6 @@ void QLibInputKeyboard::handleRepeat()
m_repeatTimer.setInterval(REPEAT_RATE); m_repeatTimer.setInterval(REPEAT_RATE);
m_repeatTimer.start(); m_repeatTimer.start();
} }
int QLibInputKeyboard::keysymToQtKey(xkb_keysym_t key) const
{
const size_t elemCount = sizeof(keyTab) / sizeof(KeyTabEntry);
KeyTabEntry e;
e.xkbkey = key;
const KeyTabEntry *result = std::find(keyTab, keyTab + elemCount, e);
return result != keyTab + elemCount ? result->qtkey : 0;
}
int QLibInputKeyboard::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers *modifiers, const QString &text) const
{
int code = 0;
#if QT_CONFIG(textcodec)
QTextCodec *systemCodec = QTextCodec::codecForLocale();
#endif
if (keysym < 128 || (keysym < 256
#if QT_CONFIG(textcodec)
&& systemCodec->mibEnum() == 4
#endif
)) {
// upper-case key, if known
code = isprint((int)keysym) ? toupper((int)keysym) : 0;
} else if (keysym >= XKB_KEY_F1 && keysym <= XKB_KEY_F35) {
// function keys
code = Qt::Key_F1 + ((int)keysym - XKB_KEY_F1);
} else if (keysym >= XKB_KEY_KP_Space && keysym <= XKB_KEY_KP_9) {
if (keysym >= XKB_KEY_KP_0) {
// numeric keypad keys
code = Qt::Key_0 + ((int)keysym - XKB_KEY_KP_0);
} else {
code = keysymToQtKey(keysym);
}
*modifiers |= Qt::KeypadModifier;
} else if (text.length() == 1 && text.unicode()->unicode() > 0x1f
&& text.unicode()->unicode() != 0x7f
&& !(keysym >= XKB_KEY_dead_grave && keysym <= XKB_KEY_dead_longsolidusoverlay)) {
code = text.unicode()->toUpper().unicode();
} else {
// any other keys
code = keysymToQtKey(keysym);
}
return code;
}
#endif #endif
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -79,10 +79,9 @@ private:
int keysymToQtKey(xkb_keysym_t key) const; int keysymToQtKey(xkb_keysym_t key) const;
int keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers *modifiers, const QString &text) const; int keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers *modifiers, const QString &text) const;
xkb_context *m_ctx; xkb_context *m_ctx = nullptr;
xkb_keymap *m_keymap; xkb_keymap *m_keymap = nullptr;
xkb_state *m_state; xkb_state *m_state = nullptr;
xkb_mod_index_t m_modindex[4];
QTimer m_repeatTimer; QTimer m_repeatTimer;
@ -95,7 +94,6 @@ private:
QString unicodeText; QString unicodeText;
int repeatCount; int repeatCount;
} m_repeatData; } m_repeatData;
Qt::KeyboardModifiers m_mods;
#endif #endif
}; };

View File

@ -0,0 +1,828 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qxkbcommon_p.h"
#include <private/qmakearray_p.h>
#include <QtCore/QMetaMethod>
#include <QtGui/QKeyEvent>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatforminputcontext.h>
#include <qpa/qplatformintegration.h>
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcXkbcommon, "qt.xkbcommon")
static int keysymToQtKey_internal(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers,
xkb_state *state, xkb_keycode_t code,
bool superAsMeta, bool hyperAsMeta);
typedef struct xkb2qt
{
unsigned int xkb;
unsigned int qt;
constexpr bool operator <=(const xkb2qt &that) const noexcept
{
return xkb <= that.xkb;
}
constexpr bool operator <(const xkb2qt &that) const noexcept
{
return xkb < that.xkb;
}
} xkb2qt_t;
template<std::size_t Xkb, std::size_t Qt>
struct Xkb2Qt
{
using Type = xkb2qt_t;
static constexpr Type data() noexcept { return Type{Xkb, Qt}; }
};
static constexpr const auto KeyTbl = qMakeArray(
QSortedData<
// misc keys
Xkb2Qt<XKB_KEY_Escape, Qt::Key_Escape>,
Xkb2Qt<XKB_KEY_Tab, Qt::Key_Tab>,
Xkb2Qt<XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab>,
Xkb2Qt<XKB_KEY_BackSpace, Qt::Key_Backspace>,
Xkb2Qt<XKB_KEY_Return, Qt::Key_Return>,
Xkb2Qt<XKB_KEY_Insert, Qt::Key_Insert>,
Xkb2Qt<XKB_KEY_Delete, Qt::Key_Delete>,
Xkb2Qt<XKB_KEY_Clear, Qt::Key_Delete>,
Xkb2Qt<XKB_KEY_Pause, Qt::Key_Pause>,
Xkb2Qt<XKB_KEY_Print, Qt::Key_Print>,
Xkb2Qt<0x1005FF60, Qt::Key_SysReq>, // hardcoded Sun SysReq
Xkb2Qt<0x1007ff00, Qt::Key_SysReq>, // hardcoded X386 SysReq
// cursor movement
Xkb2Qt<XKB_KEY_Home, Qt::Key_Home>,
Xkb2Qt<XKB_KEY_End, Qt::Key_End>,
Xkb2Qt<XKB_KEY_Left, Qt::Key_Left>,
Xkb2Qt<XKB_KEY_Up, Qt::Key_Up>,
Xkb2Qt<XKB_KEY_Right, Qt::Key_Right>,
Xkb2Qt<XKB_KEY_Down, Qt::Key_Down>,
Xkb2Qt<XKB_KEY_Prior, Qt::Key_PageUp>,
Xkb2Qt<XKB_KEY_Next, Qt::Key_PageDown>,
// modifiers
Xkb2Qt<XKB_KEY_Shift_L, Qt::Key_Shift>,
Xkb2Qt<XKB_KEY_Shift_R, Qt::Key_Shift>,
Xkb2Qt<XKB_KEY_Shift_Lock, Qt::Key_Shift>,
Xkb2Qt<XKB_KEY_Control_L, Qt::Key_Control>,
Xkb2Qt<XKB_KEY_Control_R, Qt::Key_Control>,
Xkb2Qt<XKB_KEY_Meta_L, Qt::Key_Meta>,
Xkb2Qt<XKB_KEY_Meta_R, Qt::Key_Meta>,
Xkb2Qt<XKB_KEY_Alt_L, Qt::Key_Alt>,
Xkb2Qt<XKB_KEY_Alt_R, Qt::Key_Alt>,
Xkb2Qt<XKB_KEY_Caps_Lock, Qt::Key_CapsLock>,
Xkb2Qt<XKB_KEY_Num_Lock, Qt::Key_NumLock>,
Xkb2Qt<XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock>,
Xkb2Qt<XKB_KEY_Super_L, Qt::Key_Super_L>,
Xkb2Qt<XKB_KEY_Super_R, Qt::Key_Super_R>,
Xkb2Qt<XKB_KEY_Menu, Qt::Key_Menu>,
Xkb2Qt<XKB_KEY_Hyper_L, Qt::Key_Hyper_L>,
Xkb2Qt<XKB_KEY_Hyper_R, Qt::Key_Hyper_R>,
Xkb2Qt<XKB_KEY_Help, Qt::Key_Help>,
Xkb2Qt<0x1000FF74, Qt::Key_Backtab>, // hardcoded HP backtab
Xkb2Qt<0x1005FF10, Qt::Key_F11>, // hardcoded Sun F36 (labeled F11)
Xkb2Qt<0x1005FF11, Qt::Key_F12>, // hardcoded Sun F37 (labeled F12)
// numeric and function keypad keys
Xkb2Qt<XKB_KEY_KP_Space, Qt::Key_Space>,
Xkb2Qt<XKB_KEY_KP_Tab, Qt::Key_Tab>,
Xkb2Qt<XKB_KEY_KP_Enter, Qt::Key_Enter>,
Xkb2Qt<XKB_KEY_KP_Home, Qt::Key_Home>,
Xkb2Qt<XKB_KEY_KP_Left, Qt::Key_Left>,
Xkb2Qt<XKB_KEY_KP_Up, Qt::Key_Up>,
Xkb2Qt<XKB_KEY_KP_Right, Qt::Key_Right>,
Xkb2Qt<XKB_KEY_KP_Down, Qt::Key_Down>,
Xkb2Qt<XKB_KEY_KP_Prior, Qt::Key_PageUp>,
Xkb2Qt<XKB_KEY_KP_Next, Qt::Key_PageDown>,
Xkb2Qt<XKB_KEY_KP_End, Qt::Key_End>,
Xkb2Qt<XKB_KEY_KP_Begin, Qt::Key_Clear>,
Xkb2Qt<XKB_KEY_KP_Insert, Qt::Key_Insert>,
Xkb2Qt<XKB_KEY_KP_Delete, Qt::Key_Delete>,
Xkb2Qt<XKB_KEY_KP_Equal, Qt::Key_Equal>,
Xkb2Qt<XKB_KEY_KP_Multiply, Qt::Key_Asterisk>,
Xkb2Qt<XKB_KEY_KP_Add, Qt::Key_Plus>,
Xkb2Qt<XKB_KEY_KP_Separator, Qt::Key_Comma>,
Xkb2Qt<XKB_KEY_KP_Subtract, Qt::Key_Minus>,
Xkb2Qt<XKB_KEY_KP_Decimal, Qt::Key_Period>,
Xkb2Qt<XKB_KEY_KP_Divide, Qt::Key_Slash>,
// special non-XF86 function keys
Xkb2Qt<XKB_KEY_Undo, Qt::Key_Undo>,
Xkb2Qt<XKB_KEY_Redo, Qt::Key_Redo>,
Xkb2Qt<XKB_KEY_Find, Qt::Key_Find>,
Xkb2Qt<XKB_KEY_Cancel, Qt::Key_Cancel>,
// International input method support keys
// International & multi-key character composition
Xkb2Qt<XKB_KEY_ISO_Level3_Shift, Qt::Key_AltGr>,
Xkb2Qt<XKB_KEY_Multi_key, Qt::Key_Multi_key>,
Xkb2Qt<XKB_KEY_Codeinput, Qt::Key_Codeinput>,
Xkb2Qt<XKB_KEY_SingleCandidate, Qt::Key_SingleCandidate>,
Xkb2Qt<XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate>,
Xkb2Qt<XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate>,
// Misc Functions
Xkb2Qt<XKB_KEY_Mode_switch, Qt::Key_Mode_switch>,
Xkb2Qt<XKB_KEY_script_switch, Qt::Key_Mode_switch>,
// Japanese keyboard support
Xkb2Qt<XKB_KEY_Kanji, Qt::Key_Kanji>,
Xkb2Qt<XKB_KEY_Muhenkan, Qt::Key_Muhenkan>,
//Xkb2Qt<XKB_KEY_Henkan_Mode, Qt::Key_Henkan_Mode>,
Xkb2Qt<XKB_KEY_Henkan_Mode, Qt::Key_Henkan>,
Xkb2Qt<XKB_KEY_Henkan, Qt::Key_Henkan>,
Xkb2Qt<XKB_KEY_Romaji, Qt::Key_Romaji>,
Xkb2Qt<XKB_KEY_Hiragana, Qt::Key_Hiragana>,
Xkb2Qt<XKB_KEY_Katakana, Qt::Key_Katakana>,
Xkb2Qt<XKB_KEY_Hiragana_Katakana, Qt::Key_Hiragana_Katakana>,
Xkb2Qt<XKB_KEY_Zenkaku, Qt::Key_Zenkaku>,
Xkb2Qt<XKB_KEY_Hankaku, Qt::Key_Hankaku>,
Xkb2Qt<XKB_KEY_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku>,
Xkb2Qt<XKB_KEY_Touroku, Qt::Key_Touroku>,
Xkb2Qt<XKB_KEY_Massyo, Qt::Key_Massyo>,
Xkb2Qt<XKB_KEY_Kana_Lock, Qt::Key_Kana_Lock>,
Xkb2Qt<XKB_KEY_Kana_Shift, Qt::Key_Kana_Shift>,
Xkb2Qt<XKB_KEY_Eisu_Shift, Qt::Key_Eisu_Shift>,
Xkb2Qt<XKB_KEY_Eisu_toggle, Qt::Key_Eisu_toggle>,
//Xkb2Qt<XKB_KEY_Kanji_Bangou, Qt::Key_Kanji_Bangou>,
//Xkb2Qt<XKB_KEY_Zen_Koho, Qt::Key_Zen_Koho>,
//Xkb2Qt<XKB_KEY_Mae_Koho, Qt::Key_Mae_Koho>,
Xkb2Qt<XKB_KEY_Kanji_Bangou, Qt::Key_Codeinput>,
Xkb2Qt<XKB_KEY_Zen_Koho, Qt::Key_MultipleCandidate>,
Xkb2Qt<XKB_KEY_Mae_Koho, Qt::Key_PreviousCandidate>,
// Korean keyboard support
Xkb2Qt<XKB_KEY_Hangul, Qt::Key_Hangul>,
Xkb2Qt<XKB_KEY_Hangul_Start, Qt::Key_Hangul_Start>,
Xkb2Qt<XKB_KEY_Hangul_End, Qt::Key_Hangul_End>,
Xkb2Qt<XKB_KEY_Hangul_Hanja, Qt::Key_Hangul_Hanja>,
Xkb2Qt<XKB_KEY_Hangul_Jamo, Qt::Key_Hangul_Jamo>,
Xkb2Qt<XKB_KEY_Hangul_Romaja, Qt::Key_Hangul_Romaja>,
//Xkb2Qt<XKB_KEY_Hangul_Codeinput, Qt::Key_Hangul_Codeinput>,
Xkb2Qt<XKB_KEY_Hangul_Codeinput, Qt::Key_Codeinput>,
Xkb2Qt<XKB_KEY_Hangul_Jeonja, Qt::Key_Hangul_Jeonja>,
Xkb2Qt<XKB_KEY_Hangul_Banja, Qt::Key_Hangul_Banja>,
Xkb2Qt<XKB_KEY_Hangul_PreHanja, Qt::Key_Hangul_PreHanja>,
Xkb2Qt<XKB_KEY_Hangul_PostHanja, Qt::Key_Hangul_PostHanja>,
//Xkb2Qt<XKB_KEY_Hangul_SingleCandidate,Qt::Key_Hangul_SingleCandidate>,
//Xkb2Qt<XKB_KEY_Hangul_MultipleCandidate,Qt::Key_Hangul_MultipleCandidate>,
//Xkb2Qt<XKB_KEY_Hangul_PreviousCandidate,Qt::Key_Hangul_PreviousCandidate>,
Xkb2Qt<XKB_KEY_Hangul_SingleCandidate, Qt::Key_SingleCandidate>,
Xkb2Qt<XKB_KEY_Hangul_MultipleCandidate,Qt::Key_MultipleCandidate>,
Xkb2Qt<XKB_KEY_Hangul_PreviousCandidate,Qt::Key_PreviousCandidate>,
Xkb2Qt<XKB_KEY_Hangul_Special, Qt::Key_Hangul_Special>,
//Xkb2Qt<XKB_KEY_Hangul_switch, Qt::Key_Hangul_switch>,
Xkb2Qt<XKB_KEY_Hangul_switch, Qt::Key_Mode_switch>,
// dead keys
Xkb2Qt<XKB_KEY_dead_grave, Qt::Key_Dead_Grave>,
Xkb2Qt<XKB_KEY_dead_acute, Qt::Key_Dead_Acute>,
Xkb2Qt<XKB_KEY_dead_circumflex, Qt::Key_Dead_Circumflex>,
Xkb2Qt<XKB_KEY_dead_tilde, Qt::Key_Dead_Tilde>,
Xkb2Qt<XKB_KEY_dead_macron, Qt::Key_Dead_Macron>,
Xkb2Qt<XKB_KEY_dead_breve, Qt::Key_Dead_Breve>,
Xkb2Qt<XKB_KEY_dead_abovedot, Qt::Key_Dead_Abovedot>,
Xkb2Qt<XKB_KEY_dead_diaeresis, Qt::Key_Dead_Diaeresis>,
Xkb2Qt<XKB_KEY_dead_abovering, Qt::Key_Dead_Abovering>,
Xkb2Qt<XKB_KEY_dead_doubleacute, Qt::Key_Dead_Doubleacute>,
Xkb2Qt<XKB_KEY_dead_caron, Qt::Key_Dead_Caron>,
Xkb2Qt<XKB_KEY_dead_cedilla, Qt::Key_Dead_Cedilla>,
Xkb2Qt<XKB_KEY_dead_ogonek, Qt::Key_Dead_Ogonek>,
Xkb2Qt<XKB_KEY_dead_iota, Qt::Key_Dead_Iota>,
Xkb2Qt<XKB_KEY_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound>,
Xkb2Qt<XKB_KEY_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound>,
Xkb2Qt<XKB_KEY_dead_belowdot, Qt::Key_Dead_Belowdot>,
Xkb2Qt<XKB_KEY_dead_hook, Qt::Key_Dead_Hook>,
Xkb2Qt<XKB_KEY_dead_horn, Qt::Key_Dead_Horn>,
Xkb2Qt<XKB_KEY_dead_stroke, Qt::Key_Dead_Stroke>,
Xkb2Qt<XKB_KEY_dead_abovecomma, Qt::Key_Dead_Abovecomma>,
Xkb2Qt<XKB_KEY_dead_abovereversedcomma, Qt::Key_Dead_Abovereversedcomma>,
Xkb2Qt<XKB_KEY_dead_doublegrave, Qt::Key_Dead_Doublegrave>,
Xkb2Qt<XKB_KEY_dead_belowring, Qt::Key_Dead_Belowring>,
Xkb2Qt<XKB_KEY_dead_belowmacron, Qt::Key_Dead_Belowmacron>,
Xkb2Qt<XKB_KEY_dead_belowcircumflex, Qt::Key_Dead_Belowcircumflex>,
Xkb2Qt<XKB_KEY_dead_belowtilde, Qt::Key_Dead_Belowtilde>,
Xkb2Qt<XKB_KEY_dead_belowbreve, Qt::Key_Dead_Belowbreve>,
Xkb2Qt<XKB_KEY_dead_belowdiaeresis, Qt::Key_Dead_Belowdiaeresis>,
Xkb2Qt<XKB_KEY_dead_invertedbreve, Qt::Key_Dead_Invertedbreve>,
Xkb2Qt<XKB_KEY_dead_belowcomma, Qt::Key_Dead_Belowcomma>,
Xkb2Qt<XKB_KEY_dead_currency, Qt::Key_Dead_Currency>,
Xkb2Qt<XKB_KEY_dead_a, Qt::Key_Dead_a>,
Xkb2Qt<XKB_KEY_dead_A, Qt::Key_Dead_A>,
Xkb2Qt<XKB_KEY_dead_e, Qt::Key_Dead_e>,
Xkb2Qt<XKB_KEY_dead_E, Qt::Key_Dead_E>,
Xkb2Qt<XKB_KEY_dead_i, Qt::Key_Dead_i>,
Xkb2Qt<XKB_KEY_dead_I, Qt::Key_Dead_I>,
Xkb2Qt<XKB_KEY_dead_o, Qt::Key_Dead_o>,
Xkb2Qt<XKB_KEY_dead_O, Qt::Key_Dead_O>,
Xkb2Qt<XKB_KEY_dead_u, Qt::Key_Dead_u>,
Xkb2Qt<XKB_KEY_dead_U, Qt::Key_Dead_U>,
Xkb2Qt<XKB_KEY_dead_small_schwa, Qt::Key_Dead_Small_Schwa>,
Xkb2Qt<XKB_KEY_dead_capital_schwa, Qt::Key_Dead_Capital_Schwa>,
Xkb2Qt<XKB_KEY_dead_greek, Qt::Key_Dead_Greek>,
Xkb2Qt<XKB_KEY_dead_lowline, Qt::Key_Dead_Lowline>,
Xkb2Qt<XKB_KEY_dead_aboveverticalline, Qt::Key_Dead_Aboveverticalline>,
Xkb2Qt<XKB_KEY_dead_belowverticalline, Qt::Key_Dead_Belowverticalline>,
Xkb2Qt<XKB_KEY_dead_longsolidusoverlay, Qt::Key_Dead_Longsolidusoverlay>,
// Special keys from X.org - This include multimedia keys,
// wireless/bluetooth/uwb keys, special launcher keys, etc.
Xkb2Qt<XKB_KEY_XF86Back, Qt::Key_Back>,
Xkb2Qt<XKB_KEY_XF86Forward, Qt::Key_Forward>,
Xkb2Qt<XKB_KEY_XF86Stop, Qt::Key_Stop>,
Xkb2Qt<XKB_KEY_XF86Refresh, Qt::Key_Refresh>,
Xkb2Qt<XKB_KEY_XF86Favorites, Qt::Key_Favorites>,
Xkb2Qt<XKB_KEY_XF86AudioMedia, Qt::Key_LaunchMedia>,
Xkb2Qt<XKB_KEY_XF86OpenURL, Qt::Key_OpenUrl>,
Xkb2Qt<XKB_KEY_XF86HomePage, Qt::Key_HomePage>,
Xkb2Qt<XKB_KEY_XF86Search, Qt::Key_Search>,
Xkb2Qt<XKB_KEY_XF86AudioLowerVolume, Qt::Key_VolumeDown>,
Xkb2Qt<XKB_KEY_XF86AudioMute, Qt::Key_VolumeMute>,
Xkb2Qt<XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp>,
Xkb2Qt<XKB_KEY_XF86AudioPlay, Qt::Key_MediaPlay>,
Xkb2Qt<XKB_KEY_XF86AudioStop, Qt::Key_MediaStop>,
Xkb2Qt<XKB_KEY_XF86AudioPrev, Qt::Key_MediaPrevious>,
Xkb2Qt<XKB_KEY_XF86AudioNext, Qt::Key_MediaNext>,
Xkb2Qt<XKB_KEY_XF86AudioRecord, Qt::Key_MediaRecord>,
Xkb2Qt<XKB_KEY_XF86AudioPause, Qt::Key_MediaPause>,
Xkb2Qt<XKB_KEY_XF86Mail, Qt::Key_LaunchMail>,
Xkb2Qt<XKB_KEY_XF86MyComputer, Qt::Key_Launch0>, // ### Qt 6: remap properly
Xkb2Qt<XKB_KEY_XF86Calculator, Qt::Key_Launch1>,
Xkb2Qt<XKB_KEY_XF86Memo, Qt::Key_Memo>,
Xkb2Qt<XKB_KEY_XF86ToDoList, Qt::Key_ToDoList>,
Xkb2Qt<XKB_KEY_XF86Calendar, Qt::Key_Calendar>,
Xkb2Qt<XKB_KEY_XF86PowerDown, Qt::Key_PowerDown>,
Xkb2Qt<XKB_KEY_XF86ContrastAdjust, Qt::Key_ContrastAdjust>,
Xkb2Qt<XKB_KEY_XF86Standby, Qt::Key_Standby>,
Xkb2Qt<XKB_KEY_XF86MonBrightnessUp, Qt::Key_MonBrightnessUp>,
Xkb2Qt<XKB_KEY_XF86MonBrightnessDown, Qt::Key_MonBrightnessDown>,
Xkb2Qt<XKB_KEY_XF86KbdLightOnOff, Qt::Key_KeyboardLightOnOff>,
Xkb2Qt<XKB_KEY_XF86KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp>,
Xkb2Qt<XKB_KEY_XF86KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown>,
Xkb2Qt<XKB_KEY_XF86PowerOff, Qt::Key_PowerOff>,
Xkb2Qt<XKB_KEY_XF86WakeUp, Qt::Key_WakeUp>,
Xkb2Qt<XKB_KEY_XF86Eject, Qt::Key_Eject>,
Xkb2Qt<XKB_KEY_XF86ScreenSaver, Qt::Key_ScreenSaver>,
Xkb2Qt<XKB_KEY_XF86WWW, Qt::Key_WWW>,
Xkb2Qt<XKB_KEY_XF86Sleep, Qt::Key_Sleep>,
Xkb2Qt<XKB_KEY_XF86LightBulb, Qt::Key_LightBulb>,
Xkb2Qt<XKB_KEY_XF86Shop, Qt::Key_Shop>,
Xkb2Qt<XKB_KEY_XF86History, Qt::Key_History>,
Xkb2Qt<XKB_KEY_XF86AddFavorite, Qt::Key_AddFavorite>,
Xkb2Qt<XKB_KEY_XF86HotLinks, Qt::Key_HotLinks>,
Xkb2Qt<XKB_KEY_XF86BrightnessAdjust, Qt::Key_BrightnessAdjust>,
Xkb2Qt<XKB_KEY_XF86Finance, Qt::Key_Finance>,
Xkb2Qt<XKB_KEY_XF86Community, Qt::Key_Community>,
Xkb2Qt<XKB_KEY_XF86AudioRewind, Qt::Key_AudioRewind>,
Xkb2Qt<XKB_KEY_XF86BackForward, Qt::Key_BackForward>,
Xkb2Qt<XKB_KEY_XF86ApplicationLeft, Qt::Key_ApplicationLeft>,
Xkb2Qt<XKB_KEY_XF86ApplicationRight, Qt::Key_ApplicationRight>,
Xkb2Qt<XKB_KEY_XF86Book, Qt::Key_Book>,
Xkb2Qt<XKB_KEY_XF86CD, Qt::Key_CD>,
Xkb2Qt<XKB_KEY_XF86Calculater, Qt::Key_Calculator>,
Xkb2Qt<XKB_KEY_XF86Clear, Qt::Key_Clear>,
Xkb2Qt<XKB_KEY_XF86ClearGrab, Qt::Key_ClearGrab>,
Xkb2Qt<XKB_KEY_XF86Close, Qt::Key_Close>,
Xkb2Qt<XKB_KEY_XF86Copy, Qt::Key_Copy>,
Xkb2Qt<XKB_KEY_XF86Cut, Qt::Key_Cut>,
Xkb2Qt<XKB_KEY_XF86Display, Qt::Key_Display>,
Xkb2Qt<XKB_KEY_XF86DOS, Qt::Key_DOS>,
Xkb2Qt<XKB_KEY_XF86Documents, Qt::Key_Documents>,
Xkb2Qt<XKB_KEY_XF86Excel, Qt::Key_Excel>,
Xkb2Qt<XKB_KEY_XF86Explorer, Qt::Key_Explorer>,
Xkb2Qt<XKB_KEY_XF86Game, Qt::Key_Game>,
Xkb2Qt<XKB_KEY_XF86Go, Qt::Key_Go>,
Xkb2Qt<XKB_KEY_XF86iTouch, Qt::Key_iTouch>,
Xkb2Qt<XKB_KEY_XF86LogOff, Qt::Key_LogOff>,
Xkb2Qt<XKB_KEY_XF86Market, Qt::Key_Market>,
Xkb2Qt<XKB_KEY_XF86Meeting, Qt::Key_Meeting>,
Xkb2Qt<XKB_KEY_XF86MenuKB, Qt::Key_MenuKB>,
Xkb2Qt<XKB_KEY_XF86MenuPB, Qt::Key_MenuPB>,
Xkb2Qt<XKB_KEY_XF86MySites, Qt::Key_MySites>,
Xkb2Qt<XKB_KEY_XF86New, Qt::Key_New>,
Xkb2Qt<XKB_KEY_XF86News, Qt::Key_News>,
Xkb2Qt<XKB_KEY_XF86OfficeHome, Qt::Key_OfficeHome>,
Xkb2Qt<XKB_KEY_XF86Open, Qt::Key_Open>,
Xkb2Qt<XKB_KEY_XF86Option, Qt::Key_Option>,
Xkb2Qt<XKB_KEY_XF86Paste, Qt::Key_Paste>,
Xkb2Qt<XKB_KEY_XF86Phone, Qt::Key_Phone>,
Xkb2Qt<XKB_KEY_XF86Reply, Qt::Key_Reply>,
Xkb2Qt<XKB_KEY_XF86Reload, Qt::Key_Reload>,
Xkb2Qt<XKB_KEY_XF86RotateWindows, Qt::Key_RotateWindows>,
Xkb2Qt<XKB_KEY_XF86RotationPB, Qt::Key_RotationPB>,
Xkb2Qt<XKB_KEY_XF86RotationKB, Qt::Key_RotationKB>,
Xkb2Qt<XKB_KEY_XF86Save, Qt::Key_Save>,
Xkb2Qt<XKB_KEY_XF86Send, Qt::Key_Send>,
Xkb2Qt<XKB_KEY_XF86Spell, Qt::Key_Spell>,
Xkb2Qt<XKB_KEY_XF86SplitScreen, Qt::Key_SplitScreen>,
Xkb2Qt<XKB_KEY_XF86Support, Qt::Key_Support>,
Xkb2Qt<XKB_KEY_XF86TaskPane, Qt::Key_TaskPane>,
Xkb2Qt<XKB_KEY_XF86Terminal, Qt::Key_Terminal>,
Xkb2Qt<XKB_KEY_XF86Tools, Qt::Key_Tools>,
Xkb2Qt<XKB_KEY_XF86Travel, Qt::Key_Travel>,
Xkb2Qt<XKB_KEY_XF86Video, Qt::Key_Video>,
Xkb2Qt<XKB_KEY_XF86Word, Qt::Key_Word>,
Xkb2Qt<XKB_KEY_XF86Xfer, Qt::Key_Xfer>,
Xkb2Qt<XKB_KEY_XF86ZoomIn, Qt::Key_ZoomIn>,
Xkb2Qt<XKB_KEY_XF86ZoomOut, Qt::Key_ZoomOut>,
Xkb2Qt<XKB_KEY_XF86Away, Qt::Key_Away>,
Xkb2Qt<XKB_KEY_XF86Messenger, Qt::Key_Messenger>,
Xkb2Qt<XKB_KEY_XF86WebCam, Qt::Key_WebCam>,
Xkb2Qt<XKB_KEY_XF86MailForward, Qt::Key_MailForward>,
Xkb2Qt<XKB_KEY_XF86Pictures, Qt::Key_Pictures>,
Xkb2Qt<XKB_KEY_XF86Music, Qt::Key_Music>,
Xkb2Qt<XKB_KEY_XF86Battery, Qt::Key_Battery>,
Xkb2Qt<XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth>,
Xkb2Qt<XKB_KEY_XF86WLAN, Qt::Key_WLAN>,
Xkb2Qt<XKB_KEY_XF86UWB, Qt::Key_UWB>,
Xkb2Qt<XKB_KEY_XF86AudioForward, Qt::Key_AudioForward>,
Xkb2Qt<XKB_KEY_XF86AudioRepeat, Qt::Key_AudioRepeat>,
Xkb2Qt<XKB_KEY_XF86AudioRandomPlay, Qt::Key_AudioRandomPlay>,
Xkb2Qt<XKB_KEY_XF86Subtitle, Qt::Key_Subtitle>,
Xkb2Qt<XKB_KEY_XF86AudioCycleTrack, Qt::Key_AudioCycleTrack>,
Xkb2Qt<XKB_KEY_XF86Time, Qt::Key_Time>,
Xkb2Qt<XKB_KEY_XF86Select, Qt::Key_Select>,
Xkb2Qt<XKB_KEY_XF86View, Qt::Key_View>,
Xkb2Qt<XKB_KEY_XF86TopMenu, Qt::Key_TopMenu>,
Xkb2Qt<XKB_KEY_XF86Red, Qt::Key_Red>,
Xkb2Qt<XKB_KEY_XF86Green, Qt::Key_Green>,
Xkb2Qt<XKB_KEY_XF86Yellow, Qt::Key_Yellow>,
Xkb2Qt<XKB_KEY_XF86Blue, Qt::Key_Blue>,
Xkb2Qt<XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth>,
Xkb2Qt<XKB_KEY_XF86Suspend, Qt::Key_Suspend>,
Xkb2Qt<XKB_KEY_XF86Hibernate, Qt::Key_Hibernate>,
Xkb2Qt<XKB_KEY_XF86TouchpadToggle, Qt::Key_TouchpadToggle>,
Xkb2Qt<XKB_KEY_XF86TouchpadOn, Qt::Key_TouchpadOn>,
Xkb2Qt<XKB_KEY_XF86TouchpadOff, Qt::Key_TouchpadOff>,
Xkb2Qt<XKB_KEY_XF86AudioMicMute, Qt::Key_MicMute>,
Xkb2Qt<XKB_KEY_XF86Launch0, Qt::Key_Launch2>, // ### Qt 6: remap properly
Xkb2Qt<XKB_KEY_XF86Launch1, Qt::Key_Launch3>,
Xkb2Qt<XKB_KEY_XF86Launch2, Qt::Key_Launch4>,
Xkb2Qt<XKB_KEY_XF86Launch3, Qt::Key_Launch5>,
Xkb2Qt<XKB_KEY_XF86Launch4, Qt::Key_Launch6>,
Xkb2Qt<XKB_KEY_XF86Launch5, Qt::Key_Launch7>,
Xkb2Qt<XKB_KEY_XF86Launch6, Qt::Key_Launch8>,
Xkb2Qt<XKB_KEY_XF86Launch7, Qt::Key_Launch9>,
Xkb2Qt<XKB_KEY_XF86Launch8, Qt::Key_LaunchA>,
Xkb2Qt<XKB_KEY_XF86Launch9, Qt::Key_LaunchB>,
Xkb2Qt<XKB_KEY_XF86LaunchA, Qt::Key_LaunchC>,
Xkb2Qt<XKB_KEY_XF86LaunchB, Qt::Key_LaunchD>,
Xkb2Qt<XKB_KEY_XF86LaunchC, Qt::Key_LaunchE>,
Xkb2Qt<XKB_KEY_XF86LaunchD, Qt::Key_LaunchF>,
Xkb2Qt<XKB_KEY_XF86LaunchE, Qt::Key_LaunchG>,
Xkb2Qt<XKB_KEY_XF86LaunchF, Qt::Key_LaunchH>
>::Data{}
);
xkb_keysym_t QXkbCommon::qxkbcommon_xkb_keysym_to_upper(xkb_keysym_t ks)
{
xkb_keysym_t lower, upper;
xkbcommon_XConvertCase(ks, &lower, &upper);
return upper;
}
QString QXkbCommon::lookupString(struct xkb_state *state, xkb_keycode_t code)
{
QVarLengthArray<char, 32> chars(32);
const int size = xkb_state_key_get_utf8(state, code, chars.data(), chars.size());
if (Q_UNLIKELY(size + 1 > chars.size())) { // +1 for NUL
chars.resize(size + 1);
xkb_state_key_get_utf8(state, code, chars.data(), chars.size());
}
return QString::fromUtf8(chars.constData(), size);
}
QString QXkbCommon::lookupStringNoKeysymTransformations(xkb_keysym_t keysym)
{
QVarLengthArray<char, 32> chars(32);
const int size = xkb_keysym_to_utf8(keysym, chars.data(), chars.size());
if (size == 0)
return QString(); // the keysym does not have a Unicode representation
if (Q_UNLIKELY(size > chars.size())) {
chars.resize(size);
xkb_keysym_to_utf8(keysym, chars.data(), chars.size());
}
return QString::fromUtf8(chars.constData(), size - 1);
}
QVector<xkb_keysym_t> QXkbCommon::toKeysym(QKeyEvent *event)
{
QVector<xkb_keysym_t> keysyms;
int qtKey = event->key();
if (qtKey >= Qt::Key_F1 && qtKey <= Qt::Key_F35) {
keysyms.append(XKB_KEY_F1 + (qtKey - Qt::Key_F1));
} else if (event->modifiers() & Qt::KeypadModifier) {
if (qtKey >= Qt::Key_0 && qtKey <= Qt::Key_9)
keysyms.append(XKB_KEY_KP_0 + (qtKey - Qt::Key_0));
} else if (isLatin(qtKey) && event->text().isUpper()) {
keysyms.append(qtKey);
}
if (!keysyms.isEmpty())
return keysyms;
// check if we have a direct mapping
auto it = std::find_if(KeyTbl.cbegin(), KeyTbl.cend(), [&qtKey](xkb2qt_t elem) {
return elem.qt == static_cast<uint>(qtKey);
});
if (it != KeyTbl.end()) {
keysyms.append(it->xkb);
return keysyms;
}
QVector<uint> ucs4;
if (event->text().isEmpty())
ucs4.append(qtKey);
else
ucs4 = event->text().toUcs4();
// From libxkbcommon keysym-utf.c:
// "We allow to represent any UCS character in the range U-00000000 to
// U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff."
for (uint utf32 : qAsConst(ucs4))
keysyms.append(utf32 | 0x01000000);
return keysyms;
}
int QXkbCommon::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers)
{
return keysymToQtKey(keysym, modifiers, nullptr, 0);
}
int QXkbCommon::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers,
xkb_state *state, xkb_keycode_t code,
bool superAsMeta, bool hyperAsMeta)
{
// Note 1: All standard key sequences on linux (as defined in platform theme)
// that use a latin character also contain a control modifier, which is why
// checking for Qt::ControlModifier is sufficient here. It is possible to
// override QPlatformTheme::keyBindings() and provide custom sequences for
// QKeySequence::StandardKey. Custom sequences probably should respect this
// convention (alternatively, we could test against other modifiers here).
// Note 2: The possibleKeys() shorcut mechanism is not affected by this value
// adjustment and does its own thing.
if (modifiers & Qt::ControlModifier) {
// With standard shortcuts we should prefer a latin character, this is
// for checks like "some qkeyevent == QKeySequence::Copy" to work even
// when using for example 'russian' keyboard layout.
if (!QXkbCommon::isLatin(keysym)) {
xkb_keysym_t latinKeysym = QXkbCommon::lookupLatinKeysym(state, code);
if (latinKeysym != XKB_KEY_NoSymbol)
keysym = latinKeysym;
}
}
return keysymToQtKey_internal(keysym, modifiers, state, code, superAsMeta, hyperAsMeta);
}
static int keysymToQtKey_internal(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers,
xkb_state *state, xkb_keycode_t code,
bool superAsMeta, bool hyperAsMeta)
{
int qtKey = 0;
// lookup from direct mapping
if (keysym >= XKB_KEY_F1 && keysym <= XKB_KEY_F35) {
// function keys
qtKey = Qt::Key_F1 + (keysym - XKB_KEY_F1);
} else if (keysym >= XKB_KEY_KP_0 && keysym <= XKB_KEY_KP_9) {
// numeric keypad keys
qtKey = Qt::Key_0 + (keysym - XKB_KEY_KP_0);
} else if (QXkbCommon::isLatin(keysym)) {
qtKey = QXkbCommon::qxkbcommon_xkb_keysym_to_upper(keysym);
} else {
// check if we have a direct mapping
xkb2qt_t searchKey{keysym, 0};
auto it = std::lower_bound(KeyTbl.cbegin(), KeyTbl.cend(), searchKey);
if (it != KeyTbl.end() && !(searchKey < *it))
qtKey = it->qt;
}
if (qtKey)
return qtKey;
// lookup from unicode
QString text;
if (!state || modifiers & Qt::ControlModifier) {
// Control modifier changes the text to ASCII control character, therefore we
// can't use this text to map keysym to a qt key. We can use the same keysym
// (it is not affectd by transformation) to obtain untransformed text. For details
// see "Appendix A. Default Symbol Transformations" in the XKB specification.
text = QXkbCommon::lookupStringNoKeysymTransformations(keysym);
} else {
text = QXkbCommon::lookupString(state, code);
}
if (!text.isEmpty()) {
if (text.unicode()->isDigit()) {
// Ensures that also non-latin digits are mapped to corresponding qt keys,
// e.g CTRL + ۲ (arabic two), is mapped to CTRL + Qt::Key_2.
qtKey = Qt::Key_0 + text.unicode()->digitValue();
} else {
qtKey = text.unicode()->toUpper().unicode();
}
}
// translate Super/Hyper keys to Meta if we're using them as the MetaModifier
if (superAsMeta && (qtKey == Qt::Key_Super_L || qtKey == Qt::Key_Super_R))
qtKey = Qt::Key_Meta;
if (hyperAsMeta && (qtKey == Qt::Key_Hyper_L || qtKey == Qt::Key_Hyper_R))
qtKey = Qt::Key_Meta;
return qtKey;
}
Qt::KeyboardModifiers QXkbCommon::modifiers(struct xkb_state *state)
{
Qt::KeyboardModifiers modifiers = Qt::NoModifier;
if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0)
modifiers |= Qt::ControlModifier;
if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) > 0)
modifiers |= Qt::AltModifier;
if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) > 0)
modifiers |= Qt::ShiftModifier;
if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) > 0)
modifiers |= Qt::MetaModifier;
return modifiers;
}
// Possible modifier states.
static const Qt::KeyboardModifiers ModsTbl[] = {
Qt::NoModifier, // 0
Qt::ShiftModifier, // 1
Qt::ControlModifier, // 2
Qt::ControlModifier | Qt::ShiftModifier, // 3
Qt::AltModifier, // 4
Qt::AltModifier | Qt::ShiftModifier, // 5
Qt::AltModifier | Qt::ControlModifier, // 6
Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7
Qt::NoModifier // Fall-back to raw Key_*, for non-latin1 kb layouts
};
QList<int> QXkbCommon::possibleKeys(xkb_state *state, const QKeyEvent *event,
bool superAsMeta, bool hyperAsMeta)
{
QList<int> result;
quint32 keycode = event->nativeScanCode();
Qt::KeyboardModifiers modifiers = event->modifiers();
xkb_keymap *keymap = xkb_state_get_keymap(state);
// turn off the modifier bits which doesn't participate in shortcuts
Qt::KeyboardModifiers notNeeded = Qt::KeypadModifier | Qt::GroupSwitchModifier;
modifiers &= ~notNeeded;
// create a fresh kb state and test against the relevant modifier combinations
ScopedXKBState scopedXkbQueryState(xkb_state_new(keymap));
xkb_state *queryState = scopedXkbQueryState.get();
if (!queryState) {
qCWarning(lcXkbcommon) << Q_FUNC_INFO << "failed to compile xkb keymap";
return result;
}
// get kb state from the master state and update the temporary state
xkb_layout_index_t lockedLayout = xkb_state_serialize_layout(state, XKB_STATE_LAYOUT_LOCKED);
xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LATCHED);
xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LOCKED);
xkb_mod_mask_t depressedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_DEPRESSED);
xkb_state_update_mask(queryState, depressedMods, latchedMods, lockedMods, 0, 0, lockedLayout);
// handle shortcuts for level three and above
xkb_layout_index_t layoutIndex = xkb_state_key_get_layout(queryState, keycode);
xkb_level_index_t levelIndex = 0;
if (layoutIndex != XKB_LAYOUT_INVALID) {
levelIndex = xkb_state_key_get_level(queryState, keycode, layoutIndex);
if (levelIndex == XKB_LEVEL_INVALID)
levelIndex = 0;
}
if (levelIndex <= 1)
xkb_state_update_mask(queryState, 0, latchedMods, lockedMods, 0, 0, lockedLayout);
xkb_keysym_t sym = xkb_state_key_get_one_sym(queryState, keycode);
if (sym == XKB_KEY_NoSymbol)
return result;
int baseQtKey = keysymToQtKey_internal(sym, modifiers, queryState, keycode, superAsMeta, hyperAsMeta);
if (baseQtKey)
result += (baseQtKey + modifiers);
xkb_mod_index_t shiftMod = xkb_keymap_mod_get_index(keymap, "Shift");
xkb_mod_index_t altMod = xkb_keymap_mod_get_index(keymap, "Alt");
xkb_mod_index_t controlMod = xkb_keymap_mod_get_index(keymap, "Control");
xkb_mod_index_t metaMod = xkb_keymap_mod_get_index(keymap, "Meta");
Q_ASSERT(shiftMod < 32);
Q_ASSERT(altMod < 32);
Q_ASSERT(controlMod < 32);
xkb_mod_mask_t depressed;
int qtKey = 0;
// obtain a list of possible shortcuts for the given key event
for (uint i = 1; i < sizeof(ModsTbl) / sizeof(*ModsTbl) ; ++i) {
Qt::KeyboardModifiers neededMods = ModsTbl[i];
if ((modifiers & neededMods) == neededMods) {
if (i == 8) {
if (isLatin(baseQtKey))
continue;
// add a latin key as a fall back key
sym = lookupLatinKeysym(state, keycode);
} else {
depressed = 0;
if (neededMods & Qt::AltModifier)
depressed |= (1 << altMod);
if (neededMods & Qt::ShiftModifier)
depressed |= (1 << shiftMod);
if (neededMods & Qt::ControlModifier)
depressed |= (1 << controlMod);
if (metaMod < 32 && neededMods & Qt::MetaModifier)
depressed |= (1 << metaMod);
xkb_state_update_mask(queryState, depressed, latchedMods, lockedMods, 0, 0, lockedLayout);
sym = xkb_state_key_get_one_sym(queryState, keycode);
}
if (sym == XKB_KEY_NoSymbol)
continue;
Qt::KeyboardModifiers mods = modifiers & ~neededMods;
qtKey = keysymToQtKey_internal(sym, mods, queryState, keycode, superAsMeta, hyperAsMeta);
if (!qtKey || qtKey == baseQtKey)
continue;
// catch only more specific shortcuts, i.e. Ctrl+Shift+= also generates Ctrl++ and +,
// but Ctrl++ is more specific than +, so we should skip the last one
bool ambiguous = false;
for (int shortcut : qAsConst(result)) {
if (int(shortcut & ~Qt::KeyboardModifierMask) == qtKey && (shortcut & mods) == mods) {
ambiguous = true;
break;
}
}
if (ambiguous)
continue;
result += (qtKey + mods);
}
}
return result;
}
void QXkbCommon::verifyHasLatinLayout(xkb_keymap *keymap)
{
const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts(keymap);
const xkb_keycode_t minKeycode = xkb_keymap_min_keycode(keymap);
const xkb_keycode_t maxKeycode = xkb_keymap_max_keycode(keymap);
const xkb_keysym_t *keysyms = nullptr;
int nrLatinKeys = 0;
for (xkb_layout_index_t layout = 0; layout < layoutCount; ++layout) {
for (xkb_keycode_t code = minKeycode; code < maxKeycode; ++code) {
xkb_keymap_key_get_syms_by_level(keymap, code, layout, 0, &keysyms);
if (keysyms && isLatin(keysyms[0]))
nrLatinKeys++;
if (nrLatinKeys > 10) // arbitrarily chosen threshold
return;
}
}
// This means that lookupLatinKeysym() will not find anything and latin
// key shortcuts might not work. This is a bug in the affected desktop
// environment. Usually can be solved via system settings by adding e.g. 'us'
// layout to the list of seleced layouts, or by using command line, "setxkbmap
// -layout rus,en". The position of latin key based layout in the list of the
// selected layouts is irrelevant. Properly functioning desktop environments
// handle this behind the scenes, even if no latin key based layout has been
// explicitly listed in the selected layouts.
qCDebug(lcXkbcommon, "no keyboard layouts with latin keys present");
}
xkb_keysym_t QXkbCommon::lookupLatinKeysym(xkb_state *state, xkb_keycode_t keycode)
{
xkb_layout_index_t layout;
xkb_keysym_t sym = XKB_KEY_NoSymbol;
xkb_keymap *keymap = xkb_state_get_keymap(state);
const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts_for_key(keymap, keycode);
const xkb_layout_index_t currentLayout = xkb_state_key_get_layout(state, keycode);
// Look at user layouts in the order in which they are defined in system
// settings to find a latin keysym.
for (layout = 0; layout < layoutCount; ++layout) {
if (layout == currentLayout)
continue;
const xkb_keysym_t *syms = nullptr;
xkb_level_index_t level = xkb_state_key_get_level(state, keycode, layout);
if (xkb_keymap_key_get_syms_by_level(keymap, keycode, layout, level, &syms) != 1)
continue;
if (isLatin(syms[0])) {
sym = syms[0];
break;
}
}
if (sym == XKB_KEY_NoSymbol)
return sym;
xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LATCHED);
xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LOCKED);
// Check for uniqueness, consider the following setup:
// setxkbmap -layout us,ru,us -variant dvorak,, -option 'grp:ctrl_alt_toggle' (set 'ru' as active).
// In this setup, the user would expect to trigger a ctrl+q shortcut by pressing ctrl+<physical x key>,
// because "US dvorak" is higher up in the layout settings list. This check verifies that an obtained
// 'sym' can not be acquired by any other layout higher up in the user's layout list. If it can be acquired
// then the obtained key is not unique. This prevents ctrl+<physical q key> from generating a ctrl+q
// shortcut in the above described setup. We don't want ctrl+<physical x key> and ctrl+<physical q key> to
// generate the same shortcut event in this case.
const xkb_keycode_t minKeycode = xkb_keymap_min_keycode(keymap);
const xkb_keycode_t maxKeycode = xkb_keymap_max_keycode(keymap);
ScopedXKBState queryState(xkb_state_new(keymap));
for (xkb_layout_index_t prevLayout = 0; prevLayout < layout; ++prevLayout) {
xkb_state_update_mask(queryState.get(), 0, latchedMods, lockedMods, 0, 0, prevLayout);
for (xkb_keycode_t code = minKeycode; code < maxKeycode; ++code) {
xkb_keysym_t prevSym = xkb_state_key_get_one_sym(queryState.get(), code);
if (prevSym == sym) {
sym = XKB_KEY_NoSymbol;
break;
}
}
}
return sym;
}
void QXkbCommon::setXkbContext(QPlatformInputContext *inputContext, struct xkb_context *context)
{
if (!inputContext || !context)
return;
const char *const inputContextClassName = "QComposeInputContext";
const char *const normalizedSignature = "setXkbContext(xkb_context*)";
if (inputContext->objectName() != QLatin1String(inputContextClassName))
return;
static const QMetaMethod setXkbContext = [&]() {
int methodIndex = inputContext->metaObject()->indexOfMethod(normalizedSignature);
QMetaMethod method = inputContext->metaObject()->method(methodIndex);
Q_ASSERT(method.isValid());
if (!method.isValid())
qCWarning(lcXkbcommon) << normalizedSignature << "not found on" << inputContextClassName;
return method;
}();
if (!setXkbContext.isValid())
return;
setXkbContext.invoke(inputContext, Qt::DirectConnection, Q_ARG(struct xkb_context*, context));
}
QT_END_NAMESPACE

View File

@ -1,9 +1,9 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2018 The Qt Company Ltd. ** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the plugins of the Qt Toolkit. ** This file is part of the QtGui module of the Qt Toolkit.
** **
** $QT_BEGIN_LICENSE:LGPL$ ** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage ** Commercial License Usage
@ -37,10 +37,7 @@
** **
****************************************************************************/ ****************************************************************************/
/* XConvertCase was copied from src/3rdparty/xkbcommon/src/keysym.c, /* Copyright 1985, 1987, 1990, 1998 The Open Group
which contains the following license information:
Copyright 1985, 1987, 1990, 1998 The Open Group
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),
@ -89,6 +86,7 @@
*/ */
/* /*
XConvertCase was copied from src/3rdparty/xkbcommon/src/keysym.c
The following code modifications were applied: The following code modifications were applied:
XConvertCase() was renamed to xkbcommon_XConvertCase(), to not confuse it XConvertCase() was renamed to xkbcommon_XConvertCase(), to not confuse it
@ -99,10 +97,9 @@
results instead of using the less complete version from keysym.c results instead of using the less complete version from keysym.c
*/ */
#include <xkbcommon/xkbcommon.h> #include "qxkbcommon_p.h"
#include <QtCore/QChar>
QT_BEGIN_NAMESPACE #include <QtCore/QChar>
static void qt_UCSConvertCase(uint32_t code, xkb_keysym_t *lower, xkb_keysym_t *upper) static void qt_UCSConvertCase(uint32_t code, xkb_keysym_t *lower, xkb_keysym_t *upper)
{ {
@ -110,7 +107,7 @@ static void qt_UCSConvertCase(uint32_t code, xkb_keysym_t *lower, xkb_keysym_t *
*upper = QChar::toUpper(code); *upper = QChar::toUpper(code);
} }
void xkbcommon_XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t *upper) void QXkbCommon::xkbcommon_XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t *upper)
{ {
/* Latin 1 keysym */ /* Latin 1 keysym */
if (sym < 0x100) { if (sym < 0x100) {
@ -220,14 +217,3 @@ void xkbcommon_XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t
break; break;
} }
} }
xkb_keysym_t xkbcommon_xkb_keysym_to_upper(xkb_keysym_t ks)
{
xkb_keysym_t lower, upper;
xkbcommon_XConvertCase(ks, &lower, &upper);
return upper;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,122 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QXKBCOMMON_P_H
#define QXKBCOMMON_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/QString>
#include <QtCore/QVector>
#include <QtCore/QLoggingCategory>
#include <QtCore/QList>
#include <xkbcommon/xkbcommon.h>
#include <memory>
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcXkbcommon)
class QEvent;
class QKeyEvent;
class QPlatformInputContext;
class QXkbCommon
{
public:
static QString lookupString(struct xkb_state *state, xkb_keycode_t code);
static QString lookupStringNoKeysymTransformations(xkb_keysym_t keysym);
static QVector<xkb_keysym_t> toKeysym(QKeyEvent *event);
static int keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers);
static int keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers,
xkb_state *state, xkb_keycode_t code,
bool superAsMeta = false, bool hyperAsMeta = false);
// xkbcommon_* API is part of libxkbcommon internals, with modifications as
// desribed in the header of the implementation file.
static void xkbcommon_XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t *upper);
static xkb_keysym_t qxkbcommon_xkb_keysym_to_upper(xkb_keysym_t ks);
static Qt::KeyboardModifiers modifiers(struct xkb_state *state);
static QList<int> possibleKeys(xkb_state *state, const QKeyEvent *event,
bool superAsMeta = false, bool hyperAsMeta = false);
static void verifyHasLatinLayout(xkb_keymap *keymap);
static xkb_keysym_t lookupLatinKeysym(xkb_state *state, xkb_keycode_t keycode);
static bool isLatin(xkb_keysym_t sym) {
return ((sym >= 'a' && sym <= 'z') || (sym >= 'A' && sym <= 'Z'));
}
static bool isKeypad(xkb_keysym_t sym) {
return sym >= XKB_KEY_KP_Space && sym <= XKB_KEY_KP_9;
}
static void setXkbContext(QPlatformInputContext *inputContext, struct xkb_context *context);
struct XKBStateDeleter {
void operator()(struct xkb_state *state) const { return xkb_state_unref(state); }
};
struct XKBKeymapDeleter {
void operator()(struct xkb_keymap *keymap) const { return xkb_keymap_unref(keymap); }
};
struct XKBContextDeleter {
void operator()(struct xkb_context *context) const { return xkb_context_unref(context); }
};
using ScopedXKBState = std::unique_ptr<struct xkb_state, XKBStateDeleter>;
using ScopedXKBKeymap = std::unique_ptr<struct xkb_keymap, XKBKeymapDeleter>;
using ScopedXKBContext = std::unique_ptr<struct xkb_context, XKBContextDeleter>;
};
QT_END_NAMESPACE
#endif // QXKBCOMMON_P_H

View File

@ -0,0 +1,23 @@
TARGET = QtXkbCommonSupport
MODULE = xkbcommon_support
QT = core-private gui-private
CONFIG += static internal_module
DEFINES += QT_NO_CAST_FROM_ASCII
PRECOMPILED_HEADER = ../../../corelib/global/qt_pch.h
QMAKE_USE_PRIVATE += xkbcommon
HEADERS += \
qxkbcommon_p.h
SOURCES += \
qxkbcommon.cpp \
qxkbcommon_3rdparty.cpp
# qxkbcommon.cpp::KeyTbl has more than 256 levels of expansion and older
# Clang uses that as a limit (it's 1024 in current versions).
clang:!intel_icc: QMAKE_CXXFLAGS += -ftemplate-depth=1024
load(qt_module)

View File

@ -3,18 +3,14 @@ TARGET = composeplatforminputcontextplugin
QT += core-private gui-private QT += core-private gui-private
SOURCES += $$PWD/qcomposeplatforminputcontextmain.cpp \ SOURCES += $$PWD/qcomposeplatforminputcontextmain.cpp \
$$PWD/qcomposeplatforminputcontext.cpp \ $$PWD/qcomposeplatforminputcontext.cpp
$$PWD/generator/qtablegenerator.cpp \
HEADERS += $$PWD/qcomposeplatforminputcontext.h \ HEADERS += $$PWD/qcomposeplatforminputcontext.h
$$PWD/generator/qtablegenerator.h \
QMAKE_USE_PRIVATE += xkbcommon QMAKE_USE_PRIVATE += xkbcommon
include($$OUT_PWD/../../../gui/qtgui-config.pri) include($$OUT_PWD/../../../gui/qtgui-config.pri)
DEFINES += X11_PREFIX='\\"$$QMAKE_X11_PREFIX\\"'
OTHER_FILES += $$PWD/compose.json OTHER_FILES += $$PWD/compose.json
PLUGIN_TYPE = platforminputcontexts PLUGIN_TYPE = platforminputcontexts

View File

@ -1,658 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qtablegenerator.h"
#include <QtCore/QByteArray>
#include <QtCore/QTextCodec>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QStringList>
#include <QtCore/QString>
#include <QtCore/QSaveFile>
#include <QtCore/QStandardPaths>
#include <private/qcore_unix_p.h>
#include <algorithm>
#include <xkbcommon/xkbcommon.h>
#include <locale.h> // LC_CTYPE
#include <string.h> // strchr, strncmp, etc.
#include <strings.h> // strncasecmp
#include <clocale> // LC_CTYPE
static const quint32 SupportedCacheVersion = 1;
/*
In short on how and why the "Compose" file is cached:
The "Compose" file is large, for en_US it's likely located at:
/usr/share/X11/locale/en_US.UTF-8/Compose
and it has about 6000 string lines.
Q(Gui)Applications parse this file each time they're created. On modern CPUs
it incurs a 4-10 ms startup penalty of each Qt gui app, on older CPUs -
tens of ms or more.
Since the "Compose" file (almost) never changes using a pre-parsed
cache file instead of the "Compose" file is a good idea to improve Qt5
application startup time by about 5+ ms (or tens of ms on older CPUs).
The cache file contains the contents of the QComposeCacheFileHeader struct at the
beginning followed by the pre-parsed contents of the "Compose" file.
struct QComposeCacheFileHeader stores
(a) The cache version - in the unlikely event that some day one might need
to break compatibility.
(b) The (cache) file size.
(c) The lastModified field tracks if anything changed since the last time
the cache file was saved.
If anything did change then we read the compose file and save (cache) it
in binary/pre-parsed format, which should happen extremely rarely if at all.
*/
struct QComposeCacheFileHeader
{
quint32 cacheVersion;
// The compiler will add 4 padding bytes anyway.
// Reserve them explicitly to possibly use in the future.
quint32 reserved;
quint64 fileSize;
qint64 lastModified;
};
// localHostName() copied from qtbase/src/corelib/io/qlockfile_unix.cpp
static QByteArray localHostName()
{
QByteArray hostName(512, Qt::Uninitialized);
if (gethostname(hostName.data(), hostName.size()) == -1)
return QByteArray();
hostName.truncate(strlen(hostName.data()));
return hostName;
}
/*
Reads metadata about the Compose file. Later used to determine if the
compose cache should be updated. The fileSize field will be zero on failure.
*/
static QComposeCacheFileHeader readFileMetadata(const QString &path)
{
quint64 fileSize = 0;
qint64 lastModified = 0;
const QByteArray pathBytes = QFile::encodeName(path);
QT_STATBUF st;
if (QT_STAT(pathBytes.data(), &st) == 0) {
lastModified = st.st_mtime;
fileSize = st.st_size;
}
QComposeCacheFileHeader info = { 0, 0, fileSize, lastModified };
return info;
}
static const QString getCacheFilePath()
{
QFile machineIdFile("/var/lib/dbus/machine-id");
QString machineId;
if (machineIdFile.exists()) {
if (machineIdFile.open(QIODevice::ReadOnly))
machineId = QString::fromLatin1(machineIdFile.readAll().trimmed());
}
if (machineId.isEmpty())
machineId = localHostName();
const QString dirPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation);
if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
return dirPath + QLatin1String("/qt_compose_cache_big_endian_") + machineId;
return dirPath + QLatin1String("/qt_compose_cache_little_endian_") + machineId;
}
// Returns empty vector on failure
static QVector<QComposeTableElement> loadCache(const QComposeCacheFileHeader &composeInfo)
{
QVector<QComposeTableElement> vec;
const QString cacheFilePath = getCacheFilePath();
QFile inputFile(cacheFilePath);
if (!inputFile.open(QIODevice::ReadOnly))
return vec;
QComposeCacheFileHeader cacheInfo;
// use a "buffer" variable to make the line after this one more readable.
char *buffer = reinterpret_cast<char*>(&cacheInfo);
if (inputFile.read(buffer, sizeof cacheInfo) != sizeof cacheInfo)
return vec;
if (cacheInfo.fileSize == 0)
return vec;
// using "!=" just in case someone replaced with a backup that existed before
if (cacheInfo.lastModified != composeInfo.lastModified)
return vec;
if (cacheInfo.cacheVersion != SupportedCacheVersion)
return vec;
const QByteArray pathBytes = QFile::encodeName(cacheFilePath);
QT_STATBUF st;
if (QT_STAT(pathBytes.data(), &st) != 0)
return vec;
const off_t fileSize = st.st_size;
if (fileSize > 1024 * 1024 * 5) {
// The cache file size is usually about 150KB, so if its size is over
// say 5MB then somebody inflated the file, abort.
return vec;
}
const off_t bufferSize = fileSize - (sizeof cacheInfo);
const size_t elemSize = sizeof (struct QComposeTableElement);
const int elemCount = bufferSize / elemSize;
const QByteArray ba = inputFile.read(bufferSize);
const char *data = ba.data();
// Since we know the number of the (many) elements and their size in
// advance calling vector.reserve(..) seems reasonable.
vec.reserve(elemCount);
for (int i = 0; i < elemCount; i++) {
const QComposeTableElement *elem =
reinterpret_cast<const QComposeTableElement*>(data + (i * elemSize));
vec.push_back(*elem);
}
return vec;
}
// Returns true on success, false otherwise.
static bool saveCache(const QComposeCacheFileHeader &info, const QVector<QComposeTableElement> &vec)
{
const QString filePath = getCacheFilePath();
#if QT_CONFIG(temporaryfile)
QSaveFile outputFile(filePath);
#else
QFile outputFile(filePath);
#endif
if (!outputFile.open(QIODevice::WriteOnly))
return false;
const char *data = reinterpret_cast<const char*>(&info);
if (outputFile.write(data, sizeof info) != sizeof info)
return false;
data = reinterpret_cast<const char*>(vec.constData());
const qint64 size = vec.size() * (sizeof (struct QComposeTableElement));
if (outputFile.write(data, size) != size)
return false;
#if QT_CONFIG(temporaryfile)
return outputFile.commit();
#else
return true;
#endif
}
TableGenerator::TableGenerator() : m_state(NoErrors),
m_systemComposeDir(QString())
{
initPossibleLocations();
QString composeFilePath = findComposeFile();
#ifdef DEBUG_GENERATOR
// don't use cache when in debug mode.
if (!composeFilePath.isEmpty())
qDebug() << "Using Compose file from: " << composeFilePath;
#else
QComposeCacheFileHeader fileInfo = readFileMetadata(composeFilePath);
if (fileInfo.fileSize != 0)
m_composeTable = loadCache(fileInfo);
#endif
if (m_composeTable.isEmpty() && cleanState()) {
if (composeFilePath.isEmpty()) {
m_state = MissingComposeFile;
} else {
QFile composeFile(composeFilePath);
composeFile.open(QIODevice::ReadOnly);
parseComposeFile(&composeFile);
orderComposeTable();
if (m_composeTable.isEmpty()) {
m_state = EmptyTable;
#ifndef DEBUG_GENERATOR
// don't save cache when in debug mode
} else {
fileInfo.cacheVersion = SupportedCacheVersion;
saveCache(fileInfo, m_composeTable);
#endif
}
}
}
#ifdef DEBUG_GENERATOR
printComposeTable();
#endif
}
void TableGenerator::initPossibleLocations()
{
// Compose files come as a part of Xlib library. Xlib doesn't provide
// a mechanism how to retrieve the location of these files reliably, since it was
// never meant for external software to parse compose tables directly. Best we
// can do is to hardcode search paths. To add an extra system path use
// the QTCOMPOSE environment variable
m_possibleLocations.reserve(7);
if (qEnvironmentVariableIsSet("QTCOMPOSE"))
m_possibleLocations.append(QString::fromLocal8Bit(qgetenv("QTCOMPOSE")));
m_possibleLocations.append(QStringLiteral("/usr/share/X11/locale"));
m_possibleLocations.append(QStringLiteral("/usr/local/share/X11/locale"));
m_possibleLocations.append(QStringLiteral("/usr/lib/X11/locale"));
m_possibleLocations.append(QStringLiteral("/usr/local/lib/X11/locale"));
m_possibleLocations.append(QStringLiteral(X11_PREFIX "/share/X11/locale"));
m_possibleLocations.append(QStringLiteral(X11_PREFIX "/lib/X11/locale"));
}
QString TableGenerator::findComposeFile()
{
// check if XCOMPOSEFILE points to a Compose file
if (qEnvironmentVariableIsSet("XCOMPOSEFILE")) {
const QString path = QFile::decodeName(qgetenv("XCOMPOSEFILE"));
if (QFile::exists(path))
return path;
else
qWarning("$XCOMPOSEFILE doesn't point to an existing file");
}
// check if users home directory has a file named .XCompose
if (cleanState()) {
QString path = qgetenv("HOME") + QLatin1String("/.XCompose");
if (QFile::exists(path))
return path;
}
// check for the system provided compose files
if (cleanState()) {
QString table = composeTableForLocale();
if (cleanState()) {
if (table.isEmpty())
// no table mappings for the system's locale in the compose.dir
m_state = UnsupportedLocale;
else {
QString path = QDir(systemComposeDir()).filePath(table);
if (QFile::exists(path))
return path;
}
}
}
return QString();
}
QString TableGenerator::composeTableForLocale()
{
QByteArray loc = locale().toUpper().toUtf8();
QString table = readLocaleMappings(loc);
if (table.isEmpty())
table = readLocaleMappings(readLocaleAliases(loc));
return table;
}
bool TableGenerator::findSystemComposeDir()
{
bool found = false;
for (int i = 0; i < m_possibleLocations.size(); ++i) {
QString path = m_possibleLocations.at(i);
if (QFile::exists(path + QLatin1String("/compose.dir"))) {
m_systemComposeDir = path;
found = true;
break;
}
}
if (!found) {
// should we ask to report this in the qt bug tracker?
m_state = UnknownSystemComposeDir;
qWarning("Qt Warning: Could not find a location of the system's Compose files. "
"Consider setting the QTCOMPOSE environment variable.");
}
return found;
}
QString TableGenerator::systemComposeDir()
{
if (m_systemComposeDir.isNull()
&& !findSystemComposeDir()) {
return QLatin1String("$QTCOMPOSE");
}
return m_systemComposeDir;
}
QString TableGenerator::locale() const
{
char *name = setlocale(LC_CTYPE, (char *)0);
return QLatin1String(name);
}
QString TableGenerator::readLocaleMappings(const QByteArray &locale)
{
QString file;
if (locale.isEmpty())
return file;
QFile mappings(systemComposeDir() + QLatin1String("/compose.dir"));
if (mappings.open(QIODevice::ReadOnly)) {
const int localeNameLength = locale.size();
const char * const localeData = locale.constData();
char l[1024];
// formating of compose.dir has some inconsistencies
while (!mappings.atEnd()) {
int read = mappings.readLine(l, sizeof(l));
if (read <= 0)
break;
char *line = l;
if (*line >= 'a' && *line <= 'z') {
// file name
while (*line && *line != ':' && *line != ' ' && *line != '\t')
++line;
if (!*line)
continue;
const char * const composeFileNameEnd = line;
*line = '\0';
++line;
// locale name
while (*line && (*line == ' ' || *line == '\t'))
++line;
const char * const lc = line;
while (*line && *line != ' ' && *line != '\t' && *line != '\n')
++line;
*line = '\0';
if (localeNameLength == (line - lc) && !strncasecmp(lc, localeData, line - lc)) {
file = QString::fromLocal8Bit(l, composeFileNameEnd - l);
break;
}
}
}
mappings.close();
}
return file;
}
QByteArray TableGenerator::readLocaleAliases(const QByteArray &locale)
{
QFile aliases(systemComposeDir() + QLatin1String("/locale.alias"));
QByteArray fullLocaleName;
if (aliases.open(QIODevice::ReadOnly)) {
while (!aliases.atEnd()) {
char l[1024];
int read = aliases.readLine(l, sizeof(l));
char *line = l;
if (read && ((*line >= 'a' && *line <= 'z') ||
(*line >= 'A' && *line <= 'Z'))) {
const char *alias = line;
while (*line && *line != ':' && *line != ' ' && *line != '\t')
++line;
if (!*line)
continue;
*line = 0;
if (locale.size() == (line - alias)
&& !strncasecmp(alias, locale.constData(), line - alias)) {
// found a match for alias, read the real locale name
++line;
while (*line && (*line == ' ' || *line == '\t'))
++line;
const char *fullName = line;
while (*line && *line != ' ' && *line != '\t' && *line != '\n')
++line;
*line = 0;
fullLocaleName = fullName;
#ifdef DEBUG_GENERATOR
qDebug() << "Alias for: " << alias << "is: " << fullLocaleName;
break;
#endif
}
}
}
aliases.close();
}
return fullLocaleName;
}
bool TableGenerator::processFile(const QString &composeFileName)
{
QFile composeFile(composeFileName);
if (composeFile.open(QIODevice::ReadOnly)) {
parseComposeFile(&composeFile);
return true;
}
qWarning() << QString(QLatin1String("Qt Warning: Compose file: \"%1\" can't be found"))
.arg(composeFile.fileName());
return false;
}
TableGenerator::~TableGenerator()
{
}
QVector<QComposeTableElement> TableGenerator::composeTable() const
{
return m_composeTable;
}
void TableGenerator::parseComposeFile(QFile *composeFile)
{
#ifdef DEBUG_GENERATOR
qDebug() << "TableGenerator::parseComposeFile: " << composeFile->fileName();
#endif
char line[1024];
while (!composeFile->atEnd()) {
composeFile->readLine(line, sizeof(line));
if (*line == '<')
parseKeySequence(line);
else if (!strncmp(line, "include", 7))
parseIncludeInstruction(QString::fromLocal8Bit(line));
}
composeFile->close();
}
void TableGenerator::parseIncludeInstruction(QString line)
{
// Parse something that looks like:
// include "/usr/share/X11/locale/en_US.UTF-8/Compose"
QString quote = QStringLiteral("\"");
line.remove(0, line.indexOf(quote) + 1);
line.chop(line.length() - line.indexOf(quote));
// expand substitutions if present
line.replace(QLatin1String("%H"), QString(qgetenv("HOME")));
line.replace(QLatin1String("%L"), systemComposeDir() + QLatin1Char('/') + composeTableForLocale());
line.replace(QLatin1String("%S"), systemComposeDir());
processFile(line);
}
ushort TableGenerator::keysymToUtf8(quint32 sym)
{
QByteArray chars;
int bytes;
chars.resize(8);
bytes = xkb_keysym_to_utf8(sym, chars.data(), chars.size());
if (bytes == -1)
qWarning("TableGenerator::keysymToUtf8 - buffer too small");
chars.resize(bytes-1);
#ifdef DEBUG_GENERATOR
QTextCodec *codec = QTextCodec::codecForLocale();
qDebug() << QString("keysym - 0x%1 : utf8 - %2").arg(QString::number(sym, 16))
.arg(codec->toUnicode(chars));
#endif
return QString::fromUtf8(chars).at(0).unicode();
}
static inline int fromBase8(const char *s, const char *end)
{
int result = 0;
while (*s && s != end) {
if (*s < '0' || *s > '7')
return 0;
result *= 8;
result += *s - '0';
++s;
}
return result;
}
static inline int fromBase16(const char *s, const char *end)
{
int result = 0;
while (*s && s != end) {
result *= 16;
if (*s >= '0' && *s <= '9')
result += *s - '0';
else if (*s >= 'a' && *s <= 'f')
result += *s - 'a' + 10;
else if (*s >= 'A' && *s <= 'F')
result += *s - 'A' + 10;
else
return 0;
++s;
}
return result;
}
void TableGenerator::parseKeySequence(char *line)
{
// we are interested in the lines with the following format:
// <Multi_key> <numbersign> <S> : "♬" U266c # BEAMED SIXTEENTH NOTE
char *keysEnd = strchr(line, ':');
if (!keysEnd)
return;
QComposeTableElement elem;
// find the composed value - strings may be direct text encoded in the locale
// for which the compose file is to be used, or an escaped octal or hexadecimal
// character code. Octal codes are specified as "\123" and hexadecimal codes as "\0x123a".
char *composeValue = strchr(keysEnd, '"');
if (!composeValue)
return;
++composeValue;
char *composeValueEnd = strchr(composeValue, '"');
if (!composeValueEnd)
return;
// if composed value is a quotation mark adjust the end pointer
if (composeValueEnd[1] == '"')
++composeValueEnd;
if (*composeValue == '\\' && composeValue[1] >= '0' && composeValue[1] <= '9') {
// handle octal and hex code values
char detectBase = composeValue[2];
if (detectBase == 'x') {
// hexadecimal character code
elem.value = keysymToUtf8(fromBase16(composeValue + 3, composeValueEnd));
} else {
// octal character code
elem.value = keysymToUtf8(fromBase8(composeValue + 1, composeValueEnd));
}
} else {
// handle direct text encoded in the locale
if (*composeValue == '\\')
++composeValue;
elem.value = QString::fromLocal8Bit(composeValue, composeValueEnd - composeValue).at(0).unicode();
++composeValue;
}
#ifdef DEBUG_GENERATOR
// find the comment
elem.comment = QString::fromLocal8Bit(composeValueEnd + 1).trimmed();
#endif
// find the key sequence and convert to X11 keysym
char *k = line;
const char *kend = keysEnd;
for (int i = 0; i < QT_KEYSEQUENCE_MAX_LEN; i++) {
// find the next pair of angle brackets and get the contents within
while (k < kend && *k != '<')
++k;
char *sym = ++k;
while (k < kend && *k != '>')
++k;
*k = '\0';
if (k < kend) {
elem.keys[i] = xkb_keysym_from_name(sym, (xkb_keysym_flags)0);
if (elem.keys[i] == XKB_KEY_NoSymbol) {
if (!strcmp(sym, "dead_inverted_breve"))
elem.keys[i] = XKB_KEY_dead_invertedbreve;
else if (!strcmp(sym, "dead_double_grave"))
elem.keys[i] = XKB_KEY_dead_doublegrave;
#ifdef DEBUG_GENERATOR
else
qWarning() << QString("Qt Warning - invalid keysym: %1").arg(sym);
#endif
}
} else {
elem.keys[i] = 0;
}
}
m_composeTable.append(elem);
}
void TableGenerator::printComposeTable() const
{
#ifdef DEBUG_GENERATOR
# ifndef QT_NO_DEBUG_STREAM
if (m_composeTable.isEmpty())
return;
QDebug ds = qDebug() << "output:\n";
ds.nospace();
const int tableSize = m_composeTable.size();
for (int i = 0; i < tableSize; ++i) {
const QComposeTableElement &elem = m_composeTable.at(i);
ds << "{ {";
for (int j = 0; j < QT_KEYSEQUENCE_MAX_LEN; j++) {
ds << hex << showbase << elem.keys[j] << ", ";
}
ds << "}, " << hex << showbase << elem.value << ", \"\" }, // " << elem.comment << " \n";
}
# endif
#endif
}
void TableGenerator::orderComposeTable()
{
// Stable-sorting to ensure that the item that appeared before the other in the
// original container will still appear first after the sort. This property is
// needed to handle the cases when user re-defines already defined key sequence
std::stable_sort(m_composeTable.begin(), m_composeTable.end(), ByKeys());
}

View File

@ -1,145 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QTABLEGENERATOR_H
#define QTABLEGENERATOR_H
#include <QtCore/QVector>
#include <QtCore/QFile>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <algorithm>
static Q_CONSTEXPR int QT_KEYSEQUENCE_MAX_LEN = 6;
//#define DEBUG_GENERATOR
/* Whenever QComposeTableElement gets modified supportedCacheVersion
from qtablegenerator.cpp must be bumped. */
struct QComposeTableElement {
uint keys[QT_KEYSEQUENCE_MAX_LEN];
uint value;
#ifdef DEBUG_GENERATOR
QString comment;
#endif
};
#ifndef DEBUG_GENERATOR
QT_BEGIN_NAMESPACE
Q_DECLARE_TYPEINFO(QComposeTableElement, Q_PRIMITIVE_TYPE);
QT_END_NAMESPACE
#endif
struct ByKeys
{
using uint_array = uint[QT_KEYSEQUENCE_MAX_LEN];
using result_type = bool;
bool operator()(const uint_array &lhs, const uint_array &rhs) const Q_DECL_NOTHROW
{
return std::lexicographical_compare(lhs, lhs + QT_KEYSEQUENCE_MAX_LEN,
rhs, rhs + QT_KEYSEQUENCE_MAX_LEN);
}
bool operator()(const uint_array &lhs, const QComposeTableElement &rhs) const Q_DECL_NOTHROW
{
return operator()(lhs, rhs.keys);
}
bool operator()(const QComposeTableElement &lhs, const uint_array &rhs) const Q_DECL_NOTHROW
{
return operator()(lhs.keys, rhs);
}
bool operator()(const QComposeTableElement &lhs, const QComposeTableElement &rhs) const Q_DECL_NOTHROW
{
return operator()(lhs.keys, rhs.keys);
}
};
class TableGenerator
{
public:
enum TableState
{
UnsupportedLocale,
EmptyTable,
UnknownSystemComposeDir,
MissingComposeFile,
NoErrors
};
TableGenerator();
~TableGenerator();
void parseComposeFile(QFile *composeFile);
void printComposeTable() const;
void orderComposeTable();
QVector<QComposeTableElement> composeTable() const;
TableState tableState() const { return m_state; }
protected:
bool processFile(const QString &composeFileName);
void parseKeySequence(char *line);
void parseIncludeInstruction(QString line);
QString findComposeFile();
bool findSystemComposeDir();
QString systemComposeDir();
QString composeTableForLocale();
ushort keysymToUtf8(quint32 sym);
QString readLocaleMappings(const QByteArray &locale);
QByteArray readLocaleAliases(const QByteArray &locale);
void initPossibleLocations();
bool cleanState() const { return m_state == NoErrors; }
QString locale() const;
private:
QVector<QComposeTableElement> m_composeTable;
TableState m_state;
QString m_systemComposeDir;
QList<QString> m_possibleLocations;
};
#endif // QTABLEGENERATOR_H

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the plugins of the Qt Toolkit. ** This file is part of the plugins of the Qt Toolkit.
@ -36,131 +36,100 @@
** $QT_END_LICENSE$ ** $QT_END_LICENSE$
** **
****************************************************************************/ ****************************************************************************/
#include "qcomposeplatforminputcontext.h" #include "qcomposeplatforminputcontext.h"
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtGui/QKeyEvent> #include <QtGui/QKeyEvent>
#include <QtCore/QDebug>
#include <algorithm> #include <locale.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
//#define DEBUG_COMPOSING Q_LOGGING_CATEGORY(lcXkbCompose, "qt.xkb.compose")
static const int ignoreKeys[] = {
Qt::Key_Shift,
Qt::Key_Control,
Qt::Key_Meta,
Qt::Key_Alt,
Qt::Key_CapsLock,
Qt::Key_Super_L,
Qt::Key_Super_R,
Qt::Key_Hyper_L,
Qt::Key_Hyper_R,
Qt::Key_Mode_switch
};
static const int composingKeys[] = {
Qt::Key_Multi_key,
Qt::Key_Dead_Grave,
Qt::Key_Dead_Acute,
Qt::Key_Dead_Circumflex,
Qt::Key_Dead_Tilde,
Qt::Key_Dead_Macron,
Qt::Key_Dead_Breve,
Qt::Key_Dead_Abovedot,
Qt::Key_Dead_Diaeresis,
Qt::Key_Dead_Abovering,
Qt::Key_Dead_Doubleacute,
Qt::Key_Dead_Caron,
Qt::Key_Dead_Cedilla,
Qt::Key_Dead_Ogonek,
Qt::Key_Dead_Iota,
Qt::Key_Dead_Voiced_Sound,
Qt::Key_Dead_Semivoiced_Sound,
Qt::Key_Dead_Belowdot,
Qt::Key_Dead_Hook,
Qt::Key_Dead_Horn,
Qt::Key_Dead_Stroke,
Qt::Key_Dead_Abovecomma,
Qt::Key_Dead_Abovereversedcomma,
Qt::Key_Dead_Doublegrave,
Qt::Key_Dead_Belowring,
Qt::Key_Dead_Belowmacron,
Qt::Key_Dead_Belowcircumflex,
Qt::Key_Dead_Belowtilde,
Qt::Key_Dead_Belowbreve,
Qt::Key_Dead_Belowdiaeresis,
Qt::Key_Dead_Invertedbreve,
Qt::Key_Dead_Belowcomma,
Qt::Key_Dead_Currency,
Qt::Key_Dead_a,
Qt::Key_Dead_A,
Qt::Key_Dead_e,
Qt::Key_Dead_E,
Qt::Key_Dead_i,
Qt::Key_Dead_I,
Qt::Key_Dead_o,
Qt::Key_Dead_O,
Qt::Key_Dead_u,
Qt::Key_Dead_U,
Qt::Key_Dead_Small_Schwa,
Qt::Key_Dead_Capital_Schwa,
Qt::Key_Dead_Greek,
Qt::Key_Dead_Lowline,
Qt::Key_Dead_Aboveverticalline,
Qt::Key_Dead_Belowverticalline,
Qt::Key_Dead_Longsolidusoverlay
};
QComposeInputContext::QComposeInputContext() QComposeInputContext::QComposeInputContext()
: m_tableState(TableGenerator::EmptyTable)
, m_compositionTableInitialized(false)
{ {
clearComposeBuffer(); setObjectName(QStringLiteral("QComposeInputContext"));
qCDebug(lcXkbCompose, "using xkb compose input context");
}
QComposeInputContext::~QComposeInputContext()
{
xkb_compose_state_unref(m_composeState);
xkb_compose_table_unref(m_composeTable);
}
void QComposeInputContext::ensureInitialized()
{
if (m_initialized)
return;
if (!m_XkbContext) {
qCWarning(lcXkbCompose) << "error: xkb context has not been set on" << metaObject()->className();
return;
}
m_initialized = true;
const char *const locale = setlocale(LC_CTYPE, "");
qCDebug(lcXkbCompose) << "detected locale (LC_CTYPE):" << locale;
m_composeTable = xkb_compose_table_new_from_locale(m_XkbContext, locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
if (m_composeTable)
m_composeState = xkb_compose_state_new(m_composeTable, XKB_COMPOSE_STATE_NO_FLAGS);
if (!m_composeTable) {
qCWarning(lcXkbCompose, "failed to create compose table");
return;
}
if (!m_composeState) {
qCWarning(lcXkbCompose, "failed to create compose state");
return;
}
} }
bool QComposeInputContext::filterEvent(const QEvent *event) bool QComposeInputContext::filterEvent(const QEvent *event)
{ {
const QKeyEvent *keyEvent = (const QKeyEvent *)event; auto keyEvent = static_cast<const QKeyEvent *>(event);
// should pass only the key presses if (keyEvent->type() != QEvent::KeyPress)
if (keyEvent->type() != QEvent::KeyPress) {
return false;
}
// if there were errors when generating the compose table input
// context should not try to filter anything, simply return false
if (m_compositionTableInitialized && (m_tableState & TableGenerator::NoErrors) != TableGenerator::NoErrors)
return false; return false;
int keyval = keyEvent->key(); if (!inputMethodAccepted())
int keysym = 0;
if (ignoreKey(keyval))
return false; return false;
if (!composeKey(keyval) && keyEvent->text().isEmpty()) // lazy initialization - we don't want to do this on an app startup
ensureInitialized();
if (!m_composeTable || !m_composeState)
return false; return false;
keysym = keyEvent->nativeVirtualKey(); xkb_compose_state_feed(m_composeState, keyEvent->nativeVirtualKey());
int nCompose = 0; switch (xkb_compose_state_get_status(m_composeState)) {
while (nCompose < QT_KEYSEQUENCE_MAX_LEN && m_composeBuffer[nCompose] != 0) case XKB_COMPOSE_COMPOSING:
nCompose++;
if (nCompose == QT_KEYSEQUENCE_MAX_LEN) {
reset();
nCompose = 0;
}
m_composeBuffer[nCompose] = keysym;
// check sequence
if (checkComposeTable())
return true; return true;
case XKB_COMPOSE_CANCELLED:
reset();
return false;
case XKB_COMPOSE_COMPOSED:
{
const int size = xkb_compose_state_get_utf8(m_composeState, nullptr, 0);
QVarLengthArray<char, 32> buffer(size + 1);
xkb_compose_state_get_utf8(m_composeState, buffer.data(), buffer.size());
QString composedText = QString::fromUtf8(buffer.constData());
return false; QInputMethodEvent event;
event.setCommitString(composedText);
QCoreApplication::sendEvent(m_focusObject, &event);
reset();
return true;
}
case XKB_COMPOSE_NOTHING:
return false;
default:
Q_UNREACHABLE();
return false;
}
} }
bool QComposeInputContext::isValid() const bool QComposeInputContext::isValid() const
@ -175,7 +144,8 @@ void QComposeInputContext::setFocusObject(QObject *object)
void QComposeInputContext::reset() void QComposeInputContext::reset()
{ {
clearComposeBuffer(); if (m_composeState)
xkb_compose_state_reset(m_composeState);
} }
void QComposeInputContext::update(Qt::InputMethodQueries q) void QComposeInputContext::update(Qt::InputMethodQueries q)
@ -183,125 +153,4 @@ void QComposeInputContext::update(Qt::InputMethodQueries q)
QPlatformInputContext::update(q); QPlatformInputContext::update(q);
} }
static bool isDuplicate(const QComposeTableElement &lhs, const QComposeTableElement &rhs)
{
return std::equal(lhs.keys, lhs.keys + QT_KEYSEQUENCE_MAX_LEN,
QT_MAKE_CHECKED_ARRAY_ITERATOR(rhs.keys, QT_KEYSEQUENCE_MAX_LEN));
}
bool QComposeInputContext::checkComposeTable()
{
if (!m_compositionTableInitialized) {
TableGenerator reader;
m_tableState = reader.tableState();
m_compositionTableInitialized = true;
if ((m_tableState & TableGenerator::NoErrors) == TableGenerator::NoErrors) {
m_composeTable = reader.composeTable();
} else {
#ifdef DEBUG_COMPOSING
qDebug( "### FAILED_PARSING ###" );
#endif
// if we have errors, don' try to look things up anyways.
reset();
return false;
}
}
Q_ASSERT(!m_composeTable.isEmpty());
QVector<QComposeTableElement>::const_iterator it =
std::lower_bound(m_composeTable.constBegin(), m_composeTable.constEnd(), m_composeBuffer, ByKeys());
// prevent dereferencing an 'end' iterator, which would result in a crash
if (it == m_composeTable.constEnd())
it -= 1;
QComposeTableElement elem = *it;
// would be nicer if qLowerBound had API that tells if the item was actually found
if (m_composeBuffer[0] != elem.keys[0]) {
#ifdef DEBUG_COMPOSING
qDebug( "### no match ###" );
#endif
reset();
return false;
}
// check if compose buffer is matched
for (int i=0; i < QT_KEYSEQUENCE_MAX_LEN; i++) {
// check if partial match
if (m_composeBuffer[i] == 0 && elem.keys[i]) {
#ifdef DEBUG_COMPOSING
qDebug("### partial match ###");
#endif
return true;
}
if (m_composeBuffer[i] != elem.keys[i]) {
#ifdef DEBUG_COMPOSING
qDebug("### different entry ###");
#endif
reset();
return i != 0;
}
}
#ifdef DEBUG_COMPOSING
qDebug("### match exactly ###");
#endif
// check if the key sequence is overwriten - see the comment in
// TableGenerator::orderComposeTable()
int next = 1;
do {
// if we are at the end of the table, then we have nothing to do here
if (it + next != m_composeTable.constEnd()) {
QComposeTableElement nextElem = *(it + next);
if (isDuplicate(elem, nextElem)) {
elem = nextElem;
next++;
continue;
} else {
break;
}
}
break;
} while (true);
commitText(elem.value);
reset();
return true;
}
void QComposeInputContext::commitText(uint character) const
{
QInputMethodEvent event;
event.setCommitString(QChar(character));
QCoreApplication::sendEvent(m_focusObject, &event);
}
bool QComposeInputContext::ignoreKey(int keyval) const
{
for (uint i = 0; i < (sizeof(ignoreKeys) / sizeof(ignoreKeys[0])); i++)
if (keyval == ignoreKeys[i])
return true;
return false;
}
bool QComposeInputContext::composeKey(int keyval) const
{
for (uint i = 0; i < (sizeof(composingKeys) / sizeof(composingKeys[0])); i++)
if (keyval == composingKeys[i])
return true;
return false;
}
void QComposeInputContext::clearComposeBuffer()
{
for (uint i=0; i < (sizeof(m_composeBuffer) / sizeof(int)); i++)
m_composeBuffer[i] = 0;
}
QComposeInputContext::~QComposeInputContext() {}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the plugins of the Qt Toolkit. ** This file is part of the plugins of the Qt Toolkit.
@ -36,24 +36,24 @@
** $QT_END_LICENSE$ ** $QT_END_LICENSE$
** **
****************************************************************************/ ****************************************************************************/
#ifndef QCOMPOSEPLATFORMINPUTCONTEXT_H #ifndef QCOMPOSEPLATFORMINPUTCONTEXT_H
#define QCOMPOSEPLATFORMINPUTCONTEXT_H #define QCOMPOSEPLATFORMINPUTCONTEXT_H
#include <QtCore/QLoggingCategory>
#include <qpa/qplatforminputcontext.h> #include <qpa/qplatforminputcontext.h>
#include <QtCore/QList> #include <xkbcommon/xkbcommon-compose.h>
#include "generator/qtablegenerator.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcXkbCompose)
class QEvent; class QEvent;
class QComposeInputContext : public QPlatformInputContext class QComposeInputContext : public QPlatformInputContext
{ {
Q_OBJECT Q_OBJECT
public: public:
QComposeInputContext(); QComposeInputContext();
~QComposeInputContext(); ~QComposeInputContext();
@ -62,21 +62,22 @@ public:
void setFocusObject(QObject *object) override; void setFocusObject(QObject *object) override;
void reset() override; void reset() override;
void update(Qt::InputMethodQueries) override; void update(Qt::InputMethodQueries) override;
bool filterEvent(const QEvent *event) override; bool filterEvent(const QEvent *event) override;
// This invokable is called from QXkbCommon::setXkbContext().
Q_INVOKABLE void setXkbContext(struct xkb_context *context) { m_XkbContext = context; }
protected: protected:
void clearComposeBuffer(); void ensureInitialized();
bool ignoreKey(int keyval) const;
bool composeKey(int keyval) const;
bool checkComposeTable();
void commitText(uint character) const;
private: private:
QObject *m_focusObject; bool m_initialized = false;
QVector<QComposeTableElement> m_composeTable; xkb_context *m_context = nullptr;
uint m_composeBuffer[QT_KEYSEQUENCE_MAX_LEN]; xkb_compose_table *m_composeTable = nullptr;
TableGenerator::TableState m_tableState; xkb_compose_state *m_composeState = nullptr;
bool m_compositionTableInitialized; QObject *m_focusObject = nullptr;
struct xkb_context *m_XkbContext = nullptr;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the plugins of the Qt Toolkit. ** This file is part of the plugins of the Qt Toolkit.
@ -61,7 +61,7 @@ QComposeInputContext *QComposePlatformInputContextPlugin::create(const QString &
if (system.compare(system, QLatin1String("compose"), Qt::CaseInsensitive) == 0 if (system.compare(system, QLatin1String("compose"), Qt::CaseInsensitive) == 0
|| system.compare(system, QLatin1String("xim"), Qt::CaseInsensitive) == 0) || system.compare(system, QLatin1String("xim"), Qt::CaseInsensitive) == 0)
return new QComposeInputContext; return new QComposeInputContext;
return 0; return nullptr;
} }
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -565,10 +565,15 @@ static void quitQtAndroidPlugin(JNIEnv *env, jclass /*clazz*/)
static void terminateQt(JNIEnv *env, jclass /*clazz*/) static void terminateQt(JNIEnv *env, jclass /*clazz*/)
{ {
// QAndroidEventDispatcherStopper is stopped when the user uses the task manager to kill the application // QAndroidEventDispatcherStopper is stopped when the user uses the task manager to kill the application
if (!QAndroidEventDispatcherStopper::instance()->stopped()) { if (QAndroidEventDispatcherStopper::instance()->stopped()) {
sem_wait(&m_terminateSemaphore); QAndroidEventDispatcherStopper::instance()->startAll();
sem_destroy(&m_terminateSemaphore); QCoreApplication::quit();
QAndroidEventDispatcherStopper::instance()->goingToStop(false);
} }
sem_wait(&m_terminateSemaphore);
sem_destroy(&m_terminateSemaphore);
env->DeleteGlobalRef(m_applicationClass); env->DeleteGlobalRef(m_applicationClass);
env->DeleteGlobalRef(m_classLoaderObject); env->DeleteGlobalRef(m_classLoaderObject);
if (m_resourcesObj) if (m_resourcesObj)
@ -588,10 +593,7 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/)
m_androidPlatformIntegration = nullptr; m_androidPlatformIntegration = nullptr;
delete m_androidAssetsFileEngineHandler; delete m_androidAssetsFileEngineHandler;
m_androidAssetsFileEngineHandler = nullptr; m_androidAssetsFileEngineHandler = nullptr;
sem_post(&m_exitSemaphore);
if (!QAndroidEventDispatcherStopper::instance()->stopped()) {
sem_post(&m_exitSemaphore);
}
} }
static void setSurface(JNIEnv *env, jobject /*thiz*/, jint id, jobject jSurface, jint w, jint h) static void setSurface(JNIEnv *env, jobject /*thiz*/, jint id, jobject jSurface, jint w, jint h)

View File

@ -47,6 +47,7 @@
#include <QSurfaceFormat> #include <QSurfaceFormat>
#include <QtGui/private/qwindow_p.h> #include <QtGui/private/qwindow_p.h>
#include <QtGui/qguiapplication.h>
#include <qpa/qwindowsysteminterface.h> #include <qpa/qwindowsysteminterface.h>
#include <qpa/qplatformscreen.h> #include <qpa/qplatformscreen.h>
@ -121,7 +122,7 @@ void QAndroidPlatformOpenGLWindow::setGeometry(const QRect &rect)
EGLSurface QAndroidPlatformOpenGLWindow::eglSurface(EGLConfig config) EGLSurface QAndroidPlatformOpenGLWindow::eglSurface(EGLConfig config)
{ {
if (QAndroidEventDispatcherStopper::stopped()) if (QAndroidEventDispatcherStopper::stopped() || QGuiApplication::applicationState() == Qt::ApplicationSuspended)
return m_eglSurface; return m_eglSurface;
QMutexLocker lock(&m_surfaceMutex); QMutexLocker lock(&m_surfaceMutex);

View File

@ -357,6 +357,8 @@ void QXcbIntegration::initialize()
m_inputContext.reset(QPlatformInputContextFactory::create(icStr)); m_inputContext.reset(QPlatformInputContextFactory::create(icStr));
if (!m_inputContext && icStr != defaultInputContext && icStr != QLatin1String("none")) if (!m_inputContext && icStr != defaultInputContext && icStr != QLatin1String("none"))
m_inputContext.reset(QPlatformInputContextFactory::create(defaultInputContext)); m_inputContext.reset(QPlatformInputContextFactory::create(defaultInputContext));
defaultConnection()->keyboard()->initialize();
} }
void QXcbIntegration::moveToScreen(QWindow *window, int screen) void QXcbIntegration::moveToScreen(QWindow *window, int screen)

View File

@ -39,7 +39,6 @@
#include "qxcbkeyboard.h" #include "qxcbkeyboard.h"
#include "qxcbwindow.h" #include "qxcbwindow.h"
#include "qxcbscreen.h" #include "qxcbscreen.h"
#include "qxcbxkbcommon.h"
#include <qpa/qwindowsysteminterface.h> #include <qpa/qwindowsysteminterface.h>
#include <qpa/qplatforminputcontext.h> #include <qpa/qplatforminputcontext.h>
@ -49,404 +48,20 @@
#include <QtCore/QMetaEnum> #include <QtCore/QMetaEnum>
#include <private/qguiapplication_p.h> #include <private/qguiapplication_p.h>
#include <private/qmakearray_p.h>
#include <xkbcommon/xkbcommon-keysyms.h> #if QT_CONFIG(xkb)
#include <xkbcommon/xkbcommon-x11.h>
#endif
#if QT_CONFIG(xcb_xinput) #if QT_CONFIG(xcb_xinput)
#include <xcb/xinput.h> #include <xcb/xinput.h>
#endif #endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
typedef struct xkb2qt
{
unsigned int xkb;
unsigned int qt;
constexpr bool operator <=(const xkb2qt &that) const noexcept
{
return xkb <= that.xkb;
}
constexpr bool operator <(const xkb2qt &that) const noexcept
{
return xkb < that.xkb;
}
} xkb2qt_t;
template<std::size_t Xkb, std::size_t Qt>
struct Xkb2Qt
{
using Type = xkb2qt_t;
static constexpr Type data() noexcept { return Type{Xkb, Qt}; }
};
static constexpr const auto KeyTbl = qMakeArray(
QSortedData<
// misc keys
Xkb2Qt<XKB_KEY_Escape, Qt::Key_Escape>,
Xkb2Qt<XKB_KEY_Tab, Qt::Key_Tab>,
Xkb2Qt<XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab>,
Xkb2Qt<XKB_KEY_BackSpace, Qt::Key_Backspace>,
Xkb2Qt<XKB_KEY_Return, Qt::Key_Return>,
Xkb2Qt<XKB_KEY_Insert, Qt::Key_Insert>,
Xkb2Qt<XKB_KEY_Delete, Qt::Key_Delete>,
Xkb2Qt<XKB_KEY_Clear, Qt::Key_Delete>,
Xkb2Qt<XKB_KEY_Pause, Qt::Key_Pause>,
Xkb2Qt<XKB_KEY_Print, Qt::Key_Print>,
Xkb2Qt<0x1005FF60, Qt::Key_SysReq>, // hardcoded Sun SysReq
Xkb2Qt<0x1007ff00, Qt::Key_SysReq>, // hardcoded X386 SysReq
// cursor movement
Xkb2Qt<XKB_KEY_Home, Qt::Key_Home>,
Xkb2Qt<XKB_KEY_End, Qt::Key_End>,
Xkb2Qt<XKB_KEY_Left, Qt::Key_Left>,
Xkb2Qt<XKB_KEY_Up, Qt::Key_Up>,
Xkb2Qt<XKB_KEY_Right, Qt::Key_Right>,
Xkb2Qt<XKB_KEY_Down, Qt::Key_Down>,
Xkb2Qt<XKB_KEY_Prior, Qt::Key_PageUp>,
Xkb2Qt<XKB_KEY_Next, Qt::Key_PageDown>,
// modifiers
Xkb2Qt<XKB_KEY_Shift_L, Qt::Key_Shift>,
Xkb2Qt<XKB_KEY_Shift_R, Qt::Key_Shift>,
Xkb2Qt<XKB_KEY_Shift_Lock, Qt::Key_Shift>,
Xkb2Qt<XKB_KEY_Control_L, Qt::Key_Control>,
Xkb2Qt<XKB_KEY_Control_R, Qt::Key_Control>,
Xkb2Qt<XKB_KEY_Meta_L, Qt::Key_Meta>,
Xkb2Qt<XKB_KEY_Meta_R, Qt::Key_Meta>,
Xkb2Qt<XKB_KEY_Alt_L, Qt::Key_Alt>,
Xkb2Qt<XKB_KEY_Alt_R, Qt::Key_Alt>,
Xkb2Qt<XKB_KEY_Caps_Lock, Qt::Key_CapsLock>,
Xkb2Qt<XKB_KEY_Num_Lock, Qt::Key_NumLock>,
Xkb2Qt<XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock>,
Xkb2Qt<XKB_KEY_Super_L, Qt::Key_Super_L>,
Xkb2Qt<XKB_KEY_Super_R, Qt::Key_Super_R>,
Xkb2Qt<XKB_KEY_Menu, Qt::Key_Menu>,
Xkb2Qt<XKB_KEY_Hyper_L, Qt::Key_Hyper_L>,
Xkb2Qt<XKB_KEY_Hyper_R, Qt::Key_Hyper_R>,
Xkb2Qt<XKB_KEY_Help, Qt::Key_Help>,
Xkb2Qt<0x1000FF74, Qt::Key_Backtab>, // hardcoded HP backtab
Xkb2Qt<0x1005FF10, Qt::Key_F11>, // hardcoded Sun F36 (labeled F11)
Xkb2Qt<0x1005FF11, Qt::Key_F12>, // hardcoded Sun F37 (labeled F12)
// numeric and function keypad keys
Xkb2Qt<XKB_KEY_KP_Space, Qt::Key_Space>,
Xkb2Qt<XKB_KEY_KP_Tab, Qt::Key_Tab>,
Xkb2Qt<XKB_KEY_KP_Enter, Qt::Key_Enter>,
//Xkb2Qt<XKB_KEY_KP_F1, Qt::Key_F1>,
//Xkb2Qt<XKB_KEY_KP_F2, Qt::Key_F2>,
//Xkb2Qt<XKB_KEY_KP_F3, Qt::Key_F3>,
//Xkb2Qt<XKB_KEY_KP_F4, Qt::Key_F4>,
Xkb2Qt<XKB_KEY_KP_Home, Qt::Key_Home>,
Xkb2Qt<XKB_KEY_KP_Left, Qt::Key_Left>,
Xkb2Qt<XKB_KEY_KP_Up, Qt::Key_Up>,
Xkb2Qt<XKB_KEY_KP_Right, Qt::Key_Right>,
Xkb2Qt<XKB_KEY_KP_Down, Qt::Key_Down>,
Xkb2Qt<XKB_KEY_KP_Prior, Qt::Key_PageUp>,
Xkb2Qt<XKB_KEY_KP_Next, Qt::Key_PageDown>,
Xkb2Qt<XKB_KEY_KP_End, Qt::Key_End>,
Xkb2Qt<XKB_KEY_KP_Begin, Qt::Key_Clear>,
Xkb2Qt<XKB_KEY_KP_Insert, Qt::Key_Insert>,
Xkb2Qt<XKB_KEY_KP_Delete, Qt::Key_Delete>,
Xkb2Qt<XKB_KEY_KP_Equal, Qt::Key_Equal>,
Xkb2Qt<XKB_KEY_KP_Multiply, Qt::Key_Asterisk>,
Xkb2Qt<XKB_KEY_KP_Add, Qt::Key_Plus>,
Xkb2Qt<XKB_KEY_KP_Separator, Qt::Key_Comma>,
Xkb2Qt<XKB_KEY_KP_Subtract, Qt::Key_Minus>,
Xkb2Qt<XKB_KEY_KP_Decimal, Qt::Key_Period>,
Xkb2Qt<XKB_KEY_KP_Divide, Qt::Key_Slash>,
// special non-XF86 function keys
Xkb2Qt<XKB_KEY_Undo, Qt::Key_Undo>,
Xkb2Qt<XKB_KEY_Redo, Qt::Key_Redo>,
Xkb2Qt<XKB_KEY_Find, Qt::Key_Find>,
Xkb2Qt<XKB_KEY_Cancel, Qt::Key_Cancel>,
// International input method support keys
// International & multi-key character composition
Xkb2Qt<XKB_KEY_ISO_Level3_Shift, Qt::Key_AltGr>,
Xkb2Qt<XKB_KEY_Multi_key, Qt::Key_Multi_key>,
Xkb2Qt<XKB_KEY_Codeinput, Qt::Key_Codeinput>,
Xkb2Qt<XKB_KEY_SingleCandidate, Qt::Key_SingleCandidate>,
Xkb2Qt<XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate>,
Xkb2Qt<XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate>,
// Misc Functions
Xkb2Qt<XKB_KEY_Mode_switch, Qt::Key_Mode_switch>,
Xkb2Qt<XKB_KEY_script_switch, Qt::Key_Mode_switch>,
// Japanese keyboard support
Xkb2Qt<XKB_KEY_Kanji, Qt::Key_Kanji>,
Xkb2Qt<XKB_KEY_Muhenkan, Qt::Key_Muhenkan>,
//Xkb2Qt<XKB_KEY_Henkan_Mode, Qt::Key_Henkan_Mode>,
Xkb2Qt<XKB_KEY_Henkan_Mode, Qt::Key_Henkan>,
Xkb2Qt<XKB_KEY_Henkan, Qt::Key_Henkan>,
Xkb2Qt<XKB_KEY_Romaji, Qt::Key_Romaji>,
Xkb2Qt<XKB_KEY_Hiragana, Qt::Key_Hiragana>,
Xkb2Qt<XKB_KEY_Katakana, Qt::Key_Katakana>,
Xkb2Qt<XKB_KEY_Hiragana_Katakana, Qt::Key_Hiragana_Katakana>,
Xkb2Qt<XKB_KEY_Zenkaku, Qt::Key_Zenkaku>,
Xkb2Qt<XKB_KEY_Hankaku, Qt::Key_Hankaku>,
Xkb2Qt<XKB_KEY_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku>,
Xkb2Qt<XKB_KEY_Touroku, Qt::Key_Touroku>,
Xkb2Qt<XKB_KEY_Massyo, Qt::Key_Massyo>,
Xkb2Qt<XKB_KEY_Kana_Lock, Qt::Key_Kana_Lock>,
Xkb2Qt<XKB_KEY_Kana_Shift, Qt::Key_Kana_Shift>,
Xkb2Qt<XKB_KEY_Eisu_Shift, Qt::Key_Eisu_Shift>,
Xkb2Qt<XKB_KEY_Eisu_toggle, Qt::Key_Eisu_toggle>,
//Xkb2Qt<XKB_KEY_Kanji_Bangou, Qt::Key_Kanji_Bangou>,
//Xkb2Qt<XKB_KEY_Zen_Koho, Qt::Key_Zen_Koho>,
//Xkb2Qt<XKB_KEY_Mae_Koho, Qt::Key_Mae_Koho>,
Xkb2Qt<XKB_KEY_Kanji_Bangou, Qt::Key_Codeinput>,
Xkb2Qt<XKB_KEY_Zen_Koho, Qt::Key_MultipleCandidate>,
Xkb2Qt<XKB_KEY_Mae_Koho, Qt::Key_PreviousCandidate>,
// Korean keyboard support
Xkb2Qt<XKB_KEY_Hangul, Qt::Key_Hangul>,
Xkb2Qt<XKB_KEY_Hangul_Start, Qt::Key_Hangul_Start>,
Xkb2Qt<XKB_KEY_Hangul_End, Qt::Key_Hangul_End>,
Xkb2Qt<XKB_KEY_Hangul_Hanja, Qt::Key_Hangul_Hanja>,
Xkb2Qt<XKB_KEY_Hangul_Jamo, Qt::Key_Hangul_Jamo>,
Xkb2Qt<XKB_KEY_Hangul_Romaja, Qt::Key_Hangul_Romaja>,
//Xkb2Qt<XKB_KEY_Hangul_Codeinput, Qt::Key_Hangul_Codeinput>,
Xkb2Qt<XKB_KEY_Hangul_Codeinput, Qt::Key_Codeinput>,
Xkb2Qt<XKB_KEY_Hangul_Jeonja, Qt::Key_Hangul_Jeonja>,
Xkb2Qt<XKB_KEY_Hangul_Banja, Qt::Key_Hangul_Banja>,
Xkb2Qt<XKB_KEY_Hangul_PreHanja, Qt::Key_Hangul_PreHanja>,
Xkb2Qt<XKB_KEY_Hangul_PostHanja, Qt::Key_Hangul_PostHanja>,
//Xkb2Qt<XKB_KEY_Hangul_SingleCandidate,Qt::Key_Hangul_SingleCandidate>,
//Xkb2Qt<XKB_KEY_Hangul_MultipleCandidate,Qt::Key_Hangul_MultipleCandidate>,
//Xkb2Qt<XKB_KEY_Hangul_PreviousCandidate,Qt::Key_Hangul_PreviousCandidate>,
Xkb2Qt<XKB_KEY_Hangul_SingleCandidate, Qt::Key_SingleCandidate>,
Xkb2Qt<XKB_KEY_Hangul_MultipleCandidate,Qt::Key_MultipleCandidate>,
Xkb2Qt<XKB_KEY_Hangul_PreviousCandidate,Qt::Key_PreviousCandidate>,
Xkb2Qt<XKB_KEY_Hangul_Special, Qt::Key_Hangul_Special>,
//Xkb2Qt<XKB_KEY_Hangul_switch, Qt::Key_Hangul_switch>,
Xkb2Qt<XKB_KEY_Hangul_switch, Qt::Key_Mode_switch>,
// dead keys
Xkb2Qt<XKB_KEY_dead_grave, Qt::Key_Dead_Grave>,
Xkb2Qt<XKB_KEY_dead_acute, Qt::Key_Dead_Acute>,
Xkb2Qt<XKB_KEY_dead_circumflex, Qt::Key_Dead_Circumflex>,
Xkb2Qt<XKB_KEY_dead_tilde, Qt::Key_Dead_Tilde>,
Xkb2Qt<XKB_KEY_dead_macron, Qt::Key_Dead_Macron>,
Xkb2Qt<XKB_KEY_dead_breve, Qt::Key_Dead_Breve>,
Xkb2Qt<XKB_KEY_dead_abovedot, Qt::Key_Dead_Abovedot>,
Xkb2Qt<XKB_KEY_dead_diaeresis, Qt::Key_Dead_Diaeresis>,
Xkb2Qt<XKB_KEY_dead_abovering, Qt::Key_Dead_Abovering>,
Xkb2Qt<XKB_KEY_dead_doubleacute, Qt::Key_Dead_Doubleacute>,
Xkb2Qt<XKB_KEY_dead_caron, Qt::Key_Dead_Caron>,
Xkb2Qt<XKB_KEY_dead_cedilla, Qt::Key_Dead_Cedilla>,
Xkb2Qt<XKB_KEY_dead_ogonek, Qt::Key_Dead_Ogonek>,
Xkb2Qt<XKB_KEY_dead_iota, Qt::Key_Dead_Iota>,
Xkb2Qt<XKB_KEY_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound>,
Xkb2Qt<XKB_KEY_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound>,
Xkb2Qt<XKB_KEY_dead_belowdot, Qt::Key_Dead_Belowdot>,
Xkb2Qt<XKB_KEY_dead_hook, Qt::Key_Dead_Hook>,
Xkb2Qt<XKB_KEY_dead_horn, Qt::Key_Dead_Horn>,
Xkb2Qt<XKB_KEY_dead_stroke, Qt::Key_Dead_Stroke>,
Xkb2Qt<XKB_KEY_dead_abovecomma, Qt::Key_Dead_Abovecomma>,
Xkb2Qt<XKB_KEY_dead_abovereversedcomma, Qt::Key_Dead_Abovereversedcomma>,
Xkb2Qt<XKB_KEY_dead_doublegrave, Qt::Key_Dead_Doublegrave>,
Xkb2Qt<XKB_KEY_dead_belowring, Qt::Key_Dead_Belowring>,
Xkb2Qt<XKB_KEY_dead_belowmacron, Qt::Key_Dead_Belowmacron>,
Xkb2Qt<XKB_KEY_dead_belowcircumflex, Qt::Key_Dead_Belowcircumflex>,
Xkb2Qt<XKB_KEY_dead_belowtilde, Qt::Key_Dead_Belowtilde>,
Xkb2Qt<XKB_KEY_dead_belowbreve, Qt::Key_Dead_Belowbreve>,
Xkb2Qt<XKB_KEY_dead_belowdiaeresis, Qt::Key_Dead_Belowdiaeresis>,
Xkb2Qt<XKB_KEY_dead_invertedbreve, Qt::Key_Dead_Invertedbreve>,
Xkb2Qt<XKB_KEY_dead_belowcomma, Qt::Key_Dead_Belowcomma>,
Xkb2Qt<XKB_KEY_dead_currency, Qt::Key_Dead_Currency>,
Xkb2Qt<XKB_KEY_dead_a, Qt::Key_Dead_a>,
Xkb2Qt<XKB_KEY_dead_A, Qt::Key_Dead_A>,
Xkb2Qt<XKB_KEY_dead_e, Qt::Key_Dead_e>,
Xkb2Qt<XKB_KEY_dead_E, Qt::Key_Dead_E>,
Xkb2Qt<XKB_KEY_dead_i, Qt::Key_Dead_i>,
Xkb2Qt<XKB_KEY_dead_I, Qt::Key_Dead_I>,
Xkb2Qt<XKB_KEY_dead_o, Qt::Key_Dead_o>,
Xkb2Qt<XKB_KEY_dead_O, Qt::Key_Dead_O>,
Xkb2Qt<XKB_KEY_dead_u, Qt::Key_Dead_u>,
Xkb2Qt<XKB_KEY_dead_U, Qt::Key_Dead_U>,
Xkb2Qt<XKB_KEY_dead_small_schwa, Qt::Key_Dead_Small_Schwa>,
Xkb2Qt<XKB_KEY_dead_capital_schwa, Qt::Key_Dead_Capital_Schwa>,
Xkb2Qt<XKB_KEY_dead_greek, Qt::Key_Dead_Greek>,
Xkb2Qt<XKB_KEY_dead_lowline, Qt::Key_Dead_Lowline>,
Xkb2Qt<XKB_KEY_dead_aboveverticalline, Qt::Key_Dead_Aboveverticalline>,
Xkb2Qt<XKB_KEY_dead_belowverticalline, Qt::Key_Dead_Belowverticalline>,
Xkb2Qt<XKB_KEY_dead_longsolidusoverlay, Qt::Key_Dead_Longsolidusoverlay>,
// Special keys from X.org - This include multimedia keys,
// wireless/bluetooth/uwb keys, special launcher keys, etc.
Xkb2Qt<XKB_KEY_XF86Back, Qt::Key_Back>,
Xkb2Qt<XKB_KEY_XF86Forward, Qt::Key_Forward>,
Xkb2Qt<XKB_KEY_XF86Stop, Qt::Key_Stop>,
Xkb2Qt<XKB_KEY_XF86Refresh, Qt::Key_Refresh>,
Xkb2Qt<XKB_KEY_XF86Favorites, Qt::Key_Favorites>,
Xkb2Qt<XKB_KEY_XF86AudioMedia, Qt::Key_LaunchMedia>,
Xkb2Qt<XKB_KEY_XF86OpenURL, Qt::Key_OpenUrl>,
Xkb2Qt<XKB_KEY_XF86HomePage, Qt::Key_HomePage>,
Xkb2Qt<XKB_KEY_XF86Search, Qt::Key_Search>,
Xkb2Qt<XKB_KEY_XF86AudioLowerVolume, Qt::Key_VolumeDown>,
Xkb2Qt<XKB_KEY_XF86AudioMute, Qt::Key_VolumeMute>,
Xkb2Qt<XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp>,
Xkb2Qt<XKB_KEY_XF86AudioPlay, Qt::Key_MediaPlay>,
Xkb2Qt<XKB_KEY_XF86AudioStop, Qt::Key_MediaStop>,
Xkb2Qt<XKB_KEY_XF86AudioPrev, Qt::Key_MediaPrevious>,
Xkb2Qt<XKB_KEY_XF86AudioNext, Qt::Key_MediaNext>,
Xkb2Qt<XKB_KEY_XF86AudioRecord, Qt::Key_MediaRecord>,
Xkb2Qt<XKB_KEY_XF86AudioPause, Qt::Key_MediaPause>,
Xkb2Qt<XKB_KEY_XF86Mail, Qt::Key_LaunchMail>,
Xkb2Qt<XKB_KEY_XF86MyComputer, Qt::Key_Launch0>, // ### Qt 6: remap properly
Xkb2Qt<XKB_KEY_XF86Calculator, Qt::Key_Launch1>,
Xkb2Qt<XKB_KEY_XF86Memo, Qt::Key_Memo>,
Xkb2Qt<XKB_KEY_XF86ToDoList, Qt::Key_ToDoList>,
Xkb2Qt<XKB_KEY_XF86Calendar, Qt::Key_Calendar>,
Xkb2Qt<XKB_KEY_XF86PowerDown, Qt::Key_PowerDown>,
Xkb2Qt<XKB_KEY_XF86ContrastAdjust, Qt::Key_ContrastAdjust>,
Xkb2Qt<XKB_KEY_XF86Standby, Qt::Key_Standby>,
Xkb2Qt<XKB_KEY_XF86MonBrightnessUp, Qt::Key_MonBrightnessUp>,
Xkb2Qt<XKB_KEY_XF86MonBrightnessDown, Qt::Key_MonBrightnessDown>,
Xkb2Qt<XKB_KEY_XF86KbdLightOnOff, Qt::Key_KeyboardLightOnOff>,
Xkb2Qt<XKB_KEY_XF86KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp>,
Xkb2Qt<XKB_KEY_XF86KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown>,
Xkb2Qt<XKB_KEY_XF86PowerOff, Qt::Key_PowerOff>,
Xkb2Qt<XKB_KEY_XF86WakeUp, Qt::Key_WakeUp>,
Xkb2Qt<XKB_KEY_XF86Eject, Qt::Key_Eject>,
Xkb2Qt<XKB_KEY_XF86ScreenSaver, Qt::Key_ScreenSaver>,
Xkb2Qt<XKB_KEY_XF86WWW, Qt::Key_WWW>,
Xkb2Qt<XKB_KEY_XF86Sleep, Qt::Key_Sleep>,
Xkb2Qt<XKB_KEY_XF86LightBulb, Qt::Key_LightBulb>,
Xkb2Qt<XKB_KEY_XF86Shop, Qt::Key_Shop>,
Xkb2Qt<XKB_KEY_XF86History, Qt::Key_History>,
Xkb2Qt<XKB_KEY_XF86AddFavorite, Qt::Key_AddFavorite>,
Xkb2Qt<XKB_KEY_XF86HotLinks, Qt::Key_HotLinks>,
Xkb2Qt<XKB_KEY_XF86BrightnessAdjust, Qt::Key_BrightnessAdjust>,
Xkb2Qt<XKB_KEY_XF86Finance, Qt::Key_Finance>,
Xkb2Qt<XKB_KEY_XF86Community, Qt::Key_Community>,
Xkb2Qt<XKB_KEY_XF86AudioRewind, Qt::Key_AudioRewind>,
Xkb2Qt<XKB_KEY_XF86BackForward, Qt::Key_BackForward>,
Xkb2Qt<XKB_KEY_XF86ApplicationLeft, Qt::Key_ApplicationLeft>,
Xkb2Qt<XKB_KEY_XF86ApplicationRight, Qt::Key_ApplicationRight>,
Xkb2Qt<XKB_KEY_XF86Book, Qt::Key_Book>,
Xkb2Qt<XKB_KEY_XF86CD, Qt::Key_CD>,
Xkb2Qt<XKB_KEY_XF86Calculater, Qt::Key_Calculator>,
Xkb2Qt<XKB_KEY_XF86Clear, Qt::Key_Clear>,
Xkb2Qt<XKB_KEY_XF86ClearGrab, Qt::Key_ClearGrab>,
Xkb2Qt<XKB_KEY_XF86Close, Qt::Key_Close>,
Xkb2Qt<XKB_KEY_XF86Copy, Qt::Key_Copy>,
Xkb2Qt<XKB_KEY_XF86Cut, Qt::Key_Cut>,
Xkb2Qt<XKB_KEY_XF86Display, Qt::Key_Display>,
Xkb2Qt<XKB_KEY_XF86DOS, Qt::Key_DOS>,
Xkb2Qt<XKB_KEY_XF86Documents, Qt::Key_Documents>,
Xkb2Qt<XKB_KEY_XF86Excel, Qt::Key_Excel>,
Xkb2Qt<XKB_KEY_XF86Explorer, Qt::Key_Explorer>,
Xkb2Qt<XKB_KEY_XF86Game, Qt::Key_Game>,
Xkb2Qt<XKB_KEY_XF86Go, Qt::Key_Go>,
Xkb2Qt<XKB_KEY_XF86iTouch, Qt::Key_iTouch>,
Xkb2Qt<XKB_KEY_XF86LogOff, Qt::Key_LogOff>,
Xkb2Qt<XKB_KEY_XF86Market, Qt::Key_Market>,
Xkb2Qt<XKB_KEY_XF86Meeting, Qt::Key_Meeting>,
Xkb2Qt<XKB_KEY_XF86MenuKB, Qt::Key_MenuKB>,
Xkb2Qt<XKB_KEY_XF86MenuPB, Qt::Key_MenuPB>,
Xkb2Qt<XKB_KEY_XF86MySites, Qt::Key_MySites>,
Xkb2Qt<XKB_KEY_XF86New, Qt::Key_New>,
Xkb2Qt<XKB_KEY_XF86News, Qt::Key_News>,
Xkb2Qt<XKB_KEY_XF86OfficeHome, Qt::Key_OfficeHome>,
Xkb2Qt<XKB_KEY_XF86Open, Qt::Key_Open>,
Xkb2Qt<XKB_KEY_XF86Option, Qt::Key_Option>,
Xkb2Qt<XKB_KEY_XF86Paste, Qt::Key_Paste>,
Xkb2Qt<XKB_KEY_XF86Phone, Qt::Key_Phone>,
Xkb2Qt<XKB_KEY_XF86Reply, Qt::Key_Reply>,
Xkb2Qt<XKB_KEY_XF86Reload, Qt::Key_Reload>,
Xkb2Qt<XKB_KEY_XF86RotateWindows, Qt::Key_RotateWindows>,
Xkb2Qt<XKB_KEY_XF86RotationPB, Qt::Key_RotationPB>,
Xkb2Qt<XKB_KEY_XF86RotationKB, Qt::Key_RotationKB>,
Xkb2Qt<XKB_KEY_XF86Save, Qt::Key_Save>,
Xkb2Qt<XKB_KEY_XF86Send, Qt::Key_Send>,
Xkb2Qt<XKB_KEY_XF86Spell, Qt::Key_Spell>,
Xkb2Qt<XKB_KEY_XF86SplitScreen, Qt::Key_SplitScreen>,
Xkb2Qt<XKB_KEY_XF86Support, Qt::Key_Support>,
Xkb2Qt<XKB_KEY_XF86TaskPane, Qt::Key_TaskPane>,
Xkb2Qt<XKB_KEY_XF86Terminal, Qt::Key_Terminal>,
Xkb2Qt<XKB_KEY_XF86Tools, Qt::Key_Tools>,
Xkb2Qt<XKB_KEY_XF86Travel, Qt::Key_Travel>,
Xkb2Qt<XKB_KEY_XF86Video, Qt::Key_Video>,
Xkb2Qt<XKB_KEY_XF86Word, Qt::Key_Word>,
Xkb2Qt<XKB_KEY_XF86Xfer, Qt::Key_Xfer>,
Xkb2Qt<XKB_KEY_XF86ZoomIn, Qt::Key_ZoomIn>,
Xkb2Qt<XKB_KEY_XF86ZoomOut, Qt::Key_ZoomOut>,
Xkb2Qt<XKB_KEY_XF86Away, Qt::Key_Away>,
Xkb2Qt<XKB_KEY_XF86Messenger, Qt::Key_Messenger>,
Xkb2Qt<XKB_KEY_XF86WebCam, Qt::Key_WebCam>,
Xkb2Qt<XKB_KEY_XF86MailForward, Qt::Key_MailForward>,
Xkb2Qt<XKB_KEY_XF86Pictures, Qt::Key_Pictures>,
Xkb2Qt<XKB_KEY_XF86Music, Qt::Key_Music>,
Xkb2Qt<XKB_KEY_XF86Battery, Qt::Key_Battery>,
Xkb2Qt<XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth>,
Xkb2Qt<XKB_KEY_XF86WLAN, Qt::Key_WLAN>,
Xkb2Qt<XKB_KEY_XF86UWB, Qt::Key_UWB>,
Xkb2Qt<XKB_KEY_XF86AudioForward, Qt::Key_AudioForward>,
Xkb2Qt<XKB_KEY_XF86AudioRepeat, Qt::Key_AudioRepeat>,
Xkb2Qt<XKB_KEY_XF86AudioRandomPlay, Qt::Key_AudioRandomPlay>,
Xkb2Qt<XKB_KEY_XF86Subtitle, Qt::Key_Subtitle>,
Xkb2Qt<XKB_KEY_XF86AudioCycleTrack, Qt::Key_AudioCycleTrack>,
Xkb2Qt<XKB_KEY_XF86Time, Qt::Key_Time>,
Xkb2Qt<XKB_KEY_XF86Select, Qt::Key_Select>,
Xkb2Qt<XKB_KEY_XF86View, Qt::Key_View>,
Xkb2Qt<XKB_KEY_XF86TopMenu, Qt::Key_TopMenu>,
Xkb2Qt<XKB_KEY_XF86Red, Qt::Key_Red>,
Xkb2Qt<XKB_KEY_XF86Green, Qt::Key_Green>,
Xkb2Qt<XKB_KEY_XF86Yellow, Qt::Key_Yellow>,
Xkb2Qt<XKB_KEY_XF86Blue, Qt::Key_Blue>,
Xkb2Qt<XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth>,
Xkb2Qt<XKB_KEY_XF86Suspend, Qt::Key_Suspend>,
Xkb2Qt<XKB_KEY_XF86Hibernate, Qt::Key_Hibernate>,
Xkb2Qt<XKB_KEY_XF86TouchpadToggle, Qt::Key_TouchpadToggle>,
Xkb2Qt<XKB_KEY_XF86TouchpadOn, Qt::Key_TouchpadOn>,
Xkb2Qt<XKB_KEY_XF86TouchpadOff, Qt::Key_TouchpadOff>,
Xkb2Qt<XKB_KEY_XF86AudioMicMute, Qt::Key_MicMute>,
Xkb2Qt<XKB_KEY_XF86Launch0, Qt::Key_Launch2>, // ### Qt 6: remap properly
Xkb2Qt<XKB_KEY_XF86Launch1, Qt::Key_Launch3>,
Xkb2Qt<XKB_KEY_XF86Launch2, Qt::Key_Launch4>,
Xkb2Qt<XKB_KEY_XF86Launch3, Qt::Key_Launch5>,
Xkb2Qt<XKB_KEY_XF86Launch4, Qt::Key_Launch6>,
Xkb2Qt<XKB_KEY_XF86Launch5, Qt::Key_Launch7>,
Xkb2Qt<XKB_KEY_XF86Launch6, Qt::Key_Launch8>,
Xkb2Qt<XKB_KEY_XF86Launch7, Qt::Key_Launch9>,
Xkb2Qt<XKB_KEY_XF86Launch8, Qt::Key_LaunchA>,
Xkb2Qt<XKB_KEY_XF86Launch9, Qt::Key_LaunchB>,
Xkb2Qt<XKB_KEY_XF86LaunchA, Qt::Key_LaunchC>,
Xkb2Qt<XKB_KEY_XF86LaunchB, Qt::Key_LaunchD>,
Xkb2Qt<XKB_KEY_XF86LaunchC, Qt::Key_LaunchE>,
Xkb2Qt<XKB_KEY_XF86LaunchD, Qt::Key_LaunchF>,
Xkb2Qt<XKB_KEY_XF86LaunchE, Qt::Key_LaunchG>,
Xkb2Qt<XKB_KEY_XF86LaunchF, Qt::Key_LaunchH>
>::Data{}
);
// Possible modifier states.
static const Qt::KeyboardModifiers ModsTbl[] = {
Qt::NoModifier, // 0
Qt::ShiftModifier, // 1
Qt::ControlModifier, // 2
Qt::ControlModifier | Qt::ShiftModifier, // 3
Qt::AltModifier, // 4
Qt::AltModifier | Qt::ShiftModifier, // 5
Qt::AltModifier | Qt::ControlModifier, // 6
Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7
Qt::NoModifier // Fall-back to raw Key_*, for non-latin1 kb layouts
};
Qt::KeyboardModifiers QXcbKeyboard::translateModifiers(int s) const Qt::KeyboardModifiers QXcbKeyboard::translateModifiers(int s) const
{ {
Qt::KeyboardModifiers ret = 0; Qt::KeyboardModifiers ret = Qt::NoModifier;
if (s & XCB_MOD_MASK_SHIFT) if (s & XCB_MOD_MASK_SHIFT)
ret |= Qt::ShiftModifier; ret |= Qt::ShiftModifier;
if (s & XCB_MOD_MASK_CONTROL) if (s & XCB_MOD_MASK_CONTROL)
@ -473,7 +88,7 @@ static xcb_keysym_t getUnshiftedXKey(xcb_keysym_t unshifted, xcb_keysym_t shifte
xcb_keysym_t xlower; xcb_keysym_t xlower;
xcb_keysym_t xupper; xcb_keysym_t xupper;
xkbcommon_XConvertCase(unshifted, &xlower, &xupper); QXkbCommon::xkbcommon_XConvertCase(unshifted, &xlower, &xupper);
if (xlower != xupper // Check if symbol is cased if (xlower != xupper // Check if symbol is cased
&& unshifted == xupper) { // Unshifted must be upper case && unshifted == xupper) { // Unshifted must be upper case
@ -805,7 +420,12 @@ void QXcbKeyboard::updateKeymap()
updateXKBMods(); updateXKBMods();
checkForLatinLayout(); QXkbCommon::verifyHasLatinLayout(m_xkbKeymap.get());
}
QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const
{
return QXkbCommon::possibleKeys(m_xkbState.get(), event, m_superAsMeta, m_hyperAsMeta);
} }
#if QT_CONFIG(xkb) #if QT_CONFIG(xkb)
@ -918,272 +538,6 @@ void QXcbKeyboard::updateXKBMods()
xkb_mods.mod5 = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Mod5"); xkb_mods.mod5 = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Mod5");
} }
static bool isLatin(xkb_keysym_t sym)
{
return ((sym >= 'a' && sym <= 'z') || (sym >= 'A' && sym <= 'Z'));
}
void QXcbKeyboard::checkForLatinLayout() const
{
const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts(m_xkbKeymap.get());
const xcb_keycode_t minKeycode = xkb_keymap_min_keycode(m_xkbKeymap.get());
const xcb_keycode_t maxKeycode = xkb_keymap_max_keycode(m_xkbKeymap.get());
const xkb_keysym_t *keysyms = nullptr;
int nrLatinKeys = 0;
for (xkb_layout_index_t layout = 0; layout < layoutCount; ++layout) {
for (xcb_keycode_t code = minKeycode; code < maxKeycode; ++code) {
xkb_keymap_key_get_syms_by_level(m_xkbKeymap.get(), code, layout, 0, &keysyms);
if (keysyms && isLatin(keysyms[0]))
nrLatinKeys++;
if (nrLatinKeys > 10) // arbitrarily chosen threshold
return;
}
}
// This means that lookupLatinKeysym() will not find anything and latin
// key shortcuts might not work. This is a bug in the affected desktop
// environment. Usually can be solved via system settings by adding e.g. 'us'
// layout to the list of seleced layouts, or by using command line, "setxkbmap
// -layout rus,en". The position of latin key based layout in the list of the
// selected layouts is irrelevant. Properly functioning desktop environments
// handle this behind the scenes, even if no latin key based layout has been
// explicitly listed in the selected layouts.
qCWarning(lcQpaKeyboard, "no keyboard layouts with latin keys present");
}
xkb_keysym_t QXcbKeyboard::lookupLatinKeysym(xkb_keycode_t keycode) const
{
xkb_layout_index_t layout;
xkb_keysym_t sym = XKB_KEY_NoSymbol;
const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts_for_key(m_xkbKeymap.get(), keycode);
const xkb_layout_index_t currentLayout = xkb_state_key_get_layout(m_xkbState.get(), keycode);
// Look at user layouts in the order in which they are defined in system
// settings to find a latin keysym.
for (layout = 0; layout < layoutCount; ++layout) {
if (layout == currentLayout)
continue;
const xkb_keysym_t *syms;
xkb_level_index_t level = xkb_state_key_get_level(m_xkbState.get(), keycode, layout);
if (xkb_keymap_key_get_syms_by_level(m_xkbKeymap.get(), keycode, layout, level, &syms) != 1)
continue;
if (isLatin(syms[0])) {
sym = syms[0];
break;
}
}
if (sym == XKB_KEY_NoSymbol)
return sym;
xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_LATCHED);
xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_LOCKED);
// Check for uniqueness, consider the following setup:
// setxkbmap -layout us,ru,us -variant dvorak,, -option 'grp:ctrl_alt_toggle' (set 'ru' as active).
// In this setup, the user would expect to trigger a ctrl+q shortcut by pressing ctrl+<physical x key>,
// because "US dvorak" is higher up in the layout settings list. This check verifies that an obtained
// 'sym' can not be acquired by any other layout higher up in the user's layout list. If it can be acquired
// then the obtained key is not unique. This prevents ctrl+<physical q key> from generating a ctrl+q
// shortcut in the above described setup. We don't want ctrl+<physical x key> and ctrl+<physical q key> to
// generate the same shortcut event in this case.
const xcb_keycode_t minKeycode = xkb_keymap_min_keycode(m_xkbKeymap.get());
const xcb_keycode_t maxKeycode = xkb_keymap_max_keycode(m_xkbKeymap.get());
ScopedXKBState state(xkb_state_new(m_xkbKeymap.get()));
for (xkb_layout_index_t prevLayout = 0; prevLayout < layout; ++prevLayout) {
xkb_state_update_mask(state.get(), 0, latchedMods, lockedMods, 0, 0, prevLayout);
for (xcb_keycode_t code = minKeycode; code < maxKeycode; ++code) {
xkb_keysym_t prevSym = xkb_state_key_get_one_sym(state.get(), code);
if (prevSym == sym) {
sym = XKB_KEY_NoSymbol;
break;
}
}
}
return sym;
}
static const char *qtKeyName(int qtKey)
{
int keyEnumIndex = qt_getQtMetaObject()->indexOfEnumerator("Key");
QMetaEnum keyEnum = qt_getQtMetaObject()->enumerator(keyEnumIndex);
return keyEnum.valueToKey(qtKey);
}
QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const
{
// turn off the modifier bits which doesn't participate in shortcuts
Qt::KeyboardModifiers notNeeded = Qt::KeypadModifier | Qt::GroupSwitchModifier;
Qt::KeyboardModifiers modifiers = event->modifiers() &= ~notNeeded;
// create a fresh kb state and test against the relevant modifier combinations
struct xkb_state *kb_state = xkb_state_new(m_xkbKeymap.get());
if (!kb_state) {
qWarning("QXcbKeyboard: failed to compile xkb keymap!");
return QList<int>();
}
// get kb state from the master xkb_state and update the temporary kb_state
xkb_layout_index_t lockedLayout = xkb_state_serialize_layout(m_xkbState.get(), XKB_STATE_LAYOUT_LOCKED);
xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_LATCHED);
xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_LOCKED);
xkb_mod_mask_t depressedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_DEPRESSED);
xkb_state_update_mask(kb_state, depressedMods, latchedMods, lockedMods, 0, 0, lockedLayout);
quint32 keycode = event->nativeScanCode();
// handle shortcuts for level three and above
xkb_layout_index_t layoutIndex = xkb_state_key_get_layout(kb_state, keycode);
xkb_level_index_t levelIndex = 0;
if (layoutIndex != XKB_LAYOUT_INVALID) {
levelIndex = xkb_state_key_get_level(kb_state, keycode, layoutIndex);
if (levelIndex == XKB_LEVEL_INVALID)
levelIndex = 0;
}
if (levelIndex <= 1)
xkb_state_update_mask(kb_state, 0, latchedMods, lockedMods, 0, 0, lockedLayout);
xkb_keysym_t sym = xkb_state_key_get_one_sym(kb_state, keycode);
if (sym == XKB_KEY_NoSymbol) {
xkb_state_unref(kb_state);
return QList<int>();
}
QList<int> result;
int baseQtKey = keysymToQtKey(sym, modifiers, kb_state, keycode);
if (baseQtKey)
result += (baseQtKey + modifiers);
xkb_mod_index_t shiftMod = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Shift");
xkb_mod_index_t altMod = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Alt");
xkb_mod_index_t controlMod = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Control");
xkb_mod_index_t metaMod = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Meta");
Q_ASSERT(shiftMod < 32);
Q_ASSERT(altMod < 32);
Q_ASSERT(controlMod < 32);
xkb_mod_mask_t depressed;
int qtKey = 0;
// obtain a list of possible shortcuts for the given key event
for (uint i = 1; i < sizeof(ModsTbl) / sizeof(*ModsTbl) ; ++i) {
Qt::KeyboardModifiers neededMods = ModsTbl[i];
if ((modifiers & neededMods) == neededMods) {
if (i == 8) {
if (isLatin(baseQtKey))
continue;
// add a latin key as a fall back key
sym = lookupLatinKeysym(keycode);
} else {
depressed = 0;
if (neededMods & Qt::AltModifier)
depressed |= (1 << altMod);
if (neededMods & Qt::ShiftModifier)
depressed |= (1 << shiftMod);
if (neededMods & Qt::ControlModifier)
depressed |= (1 << controlMod);
if (metaMod < 32 && neededMods & Qt::MetaModifier)
depressed |= (1 << metaMod);
xkb_state_update_mask(kb_state, depressed, latchedMods, lockedMods, 0, 0, lockedLayout);
sym = xkb_state_key_get_one_sym(kb_state, keycode);
}
if (sym == XKB_KEY_NoSymbol)
continue;
Qt::KeyboardModifiers mods = modifiers & ~neededMods;
qtKey = keysymToQtKey(sym, mods, kb_state, keycode);
if (!qtKey || qtKey == baseQtKey)
continue;
// catch only more specific shortcuts, i.e. Ctrl+Shift+= also generates Ctrl++ and +,
// but Ctrl++ is more specific than +, so we should skip the last one
bool ambiguous = false;
for (int shortcut : qAsConst(result)) {
if (int(shortcut & ~Qt::KeyboardModifierMask) == qtKey && (shortcut & mods) == mods) {
ambiguous = true;
break;
}
}
if (ambiguous)
continue;
result += (qtKey + mods);
}
}
xkb_state_unref(kb_state);
return result;
}
int QXcbKeyboard::keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers modifiers,
struct xkb_state *state, xcb_keycode_t code) const
{
int qtKey = 0;
// lookup from direct mapping
if (keysym >= XKB_KEY_F1 && keysym <= XKB_KEY_F35) {
// function keys
qtKey = Qt::Key_F1 + (keysym - XKB_KEY_F1);
} else if (keysym >= XKB_KEY_KP_0 && keysym <= XKB_KEY_KP_9) {
// numeric keypad keys
qtKey = Qt::Key_0 + (keysym - XKB_KEY_KP_0);
} else if (isLatin(keysym)) {
qtKey = xkbcommon_xkb_keysym_to_upper(keysym);
} else {
// check if we have a direct mapping
xkb2qt_t searchKey{keysym, 0};
auto it = std::lower_bound(KeyTbl.cbegin(), KeyTbl.cend(), searchKey);
if (it != KeyTbl.end() && !(searchKey < *it))
qtKey = it->qt;
}
QString text;
bool fromUnicode = qtKey == 0;
if (fromUnicode) { // lookup from unicode
if (modifiers & Qt::ControlModifier) {
// Control modifier changes the text to ASCII control character, therefore we
// can't use this text to map keysym to a qt key. We can use the same keysym
// (it is not affectd by transformation) to obtain untransformed text. For details
// see "Appendix A. Default Symbol Transformations" in the XKB specification.
text = lookupStringNoKeysymTransformations(keysym);
} else {
text = lookupString(state, code);
}
if (!text.isEmpty()) {
if (text.unicode()->isDigit()) {
// Ensures that also non-latin digits are mapped to corresponding qt keys,
// e.g CTRL + ۲ (arabic two), is mapped to CTRL + Qt::Key_2.
qtKey = Qt::Key_0 + text.unicode()->digitValue();
} else {
qtKey = text.unicode()->toUpper().unicode();
}
}
}
if (rmod_masks.meta) {
// translate Super/Hyper keys to Meta if we're using them as the MetaModifier
if (rmod_masks.meta == rmod_masks.super && (qtKey == Qt::Key_Super_L
|| qtKey == Qt::Key_Super_R)) {
qtKey = Qt::Key_Meta;
} else if (rmod_masks.meta == rmod_masks.hyper && (qtKey == Qt::Key_Hyper_L
|| qtKey == Qt::Key_Hyper_R)) {
qtKey = Qt::Key_Meta;
}
}
if (Q_UNLIKELY(lcQpaKeyboard().isDebugEnabled())) {
char keysymName[64];
xkb_keysym_get_name(keysym, keysymName, sizeof(keysymName));
QString keysymInHex = QString(QStringLiteral("0x%1")).arg(keysym, 0, 16);
if (qtKeyName(qtKey)) {
qCDebug(lcQpaKeyboard).nospace() << "keysym: " << keysymName << "("
<< keysymInHex << ") mapped to Qt::" << qtKeyName(qtKey) << " | text: " << text
<< " | qt key: " << qtKey << " mapped from unicode number: " << fromUnicode;
} else {
qCDebug(lcQpaKeyboard).nospace() << "no Qt::Key for keysym: " << keysymName
<< "(" << keysymInHex << ") | text: " << text << " | qt key: " << qtKey;
}
}
return qtKey;
}
QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection) QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection)
: QXcbObject(connection) : QXcbObject(connection)
{ {
@ -1211,6 +565,12 @@ QXcbKeyboard::~QXcbKeyboard()
xcb_key_symbols_free(m_key_symbols); xcb_key_symbols_free(m_key_symbols);
} }
void QXcbKeyboard::initialize()
{
auto inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
QXkbCommon::setXkbContext(inputContext, m_xkbContext.get());
}
void QXcbKeyboard::selectEvents() void QXcbKeyboard::selectEvents()
{ {
#if QT_CONFIG(xkb) #if QT_CONFIG(xkb)
@ -1514,6 +874,12 @@ void QXcbKeyboard::resolveMaskConflicts()
rmod_masks.meta = rmod_masks.hyper; rmod_masks.meta = rmod_masks.hyper;
} }
} }
// translate Super/Hyper keys to Meta if we're using them as the MetaModifier
if (rmod_masks.meta && rmod_masks.meta == rmod_masks.super)
m_superAsMeta = true;
if (rmod_masks.meta && rmod_masks.meta == rmod_masks.hyper)
m_hyperAsMeta = true;
} }
void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type, xcb_keycode_t code, void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type, xcb_keycode_t code,
@ -1529,7 +895,7 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
if (type == QEvent::KeyPress) if (type == QEvent::KeyPress)
targetWindow->updateNetWmUserTime(time); targetWindow->updateNetWmUserTime(time);
ScopedXKBState sendEventState; QXkbCommon::ScopedXKBState sendEventState;
if (fromSendEvent) { if (fromSendEvent) {
// Have a temporary keyboard state filled in from state // Have a temporary keyboard state filled in from state
// this way we allow for synthetic events to have different state // this way we allow for synthetic events to have different state
@ -1546,30 +912,13 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
struct xkb_state *xkbState = fromSendEvent ? sendEventState.get() : m_xkbState.get(); struct xkb_state *xkbState = fromSendEvent ? sendEventState.get() : m_xkbState.get();
xcb_keysym_t sym = xkb_state_key_get_one_sym(xkbState, code); xcb_keysym_t sym = xkb_state_key_get_one_sym(xkbState, code);
QString text = lookupString(xkbState, code); QString text = QXkbCommon::lookupString(xkbState, code);
Qt::KeyboardModifiers modifiers = translateModifiers(state); Qt::KeyboardModifiers modifiers = translateModifiers(state);
if (sym >= XKB_KEY_KP_Space && sym <= XKB_KEY_KP_9) if (QXkbCommon::isKeypad(sym))
modifiers |= Qt::KeypadModifier; modifiers |= Qt::KeypadModifier;
// Note 1: All standard key sequences on linux (as defined in platform theme) int qtcode = QXkbCommon::keysymToQtKey(sym, modifiers, xkbState, code, m_superAsMeta, m_hyperAsMeta);
// that use a latin character also contain a control modifier, which is why
// checking for Qt::ControlModifier is sufficient here. It is possible to
// override QPlatformTheme::keyBindings() and provide custom sequences for
// QKeySequence::StandardKey. Custom sequences probably should respect this
// convention (alternatively, we could test against other modifiers here).
// Note 2: The possibleKeys() shorcut mechanism is not affected by this value
// adjustment and does its own thing.
xcb_keysym_t latinKeysym = XKB_KEY_NoSymbol;
if (modifiers & Qt::ControlModifier) {
// With standard shortcuts we should prefer a latin character, this is
// in checks like "event == QKeySequence::Copy".
if (!isLatin(sym))
latinKeysym = lookupLatinKeysym(code);
}
int qtcode = keysymToQtKey(latinKeysym != XKB_KEY_NoSymbol ? latinKeysym : sym,
modifiers, xkbState, code);
if (type == QEvent::KeyPress) { if (type == QEvent::KeyPress) {
if (m_isAutoRepeat && m_autoRepeatCode != code) if (m_isAutoRepeat && m_autoRepeatCode != code)
@ -1611,28 +960,6 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
} }
} }
QString QXcbKeyboard::lookupString(struct xkb_state *state, xcb_keycode_t code) const
{
QVarLengthArray<char, 32> chars(32);
const int size = xkb_state_key_get_utf8(state, code, chars.data(), chars.size());
if (Q_UNLIKELY(size + 1 > chars.size())) { // +1 for NUL
chars.resize(size + 1);
xkb_state_key_get_utf8(state, code, chars.data(), chars.size());
}
return QString::fromUtf8(chars.constData(), size);
}
QString QXcbKeyboard::lookupStringNoKeysymTransformations(xkb_keysym_t keysym) const
{
QVarLengthArray<char, 32> chars(32);
const int size = xkb_keysym_to_utf8(keysym, chars.data(), chars.size());
if (Q_UNLIKELY(size > chars.size())) {
chars.resize(size);
xkb_keysym_to_utf8(keysym, chars.data(), chars.size());
}
return QString::fromUtf8(chars.constData(), size);
}
static bool fromSendEvent(const void *event) static bool fromSendEvent(const void *event)
{ {
// From X11 protocol: Every event contains an 8-bit type code. The most // From X11 protocol: Every event contains an 8-bit type code. The most

View File

@ -50,16 +50,12 @@
#endif #endif
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#if QT_CONFIG(xkb) #include <QtXkbCommonSupport/private/qxkbcommon_p.h>
#include <xkbcommon/xkbcommon-x11.h>
#endif
#include <QEvent> #include <QEvent>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QWindow;
class QXcbKeyboard : public QXcbObject class QXcbKeyboard : public QXcbObject
{ {
public: public:
@ -67,6 +63,7 @@ public:
~QXcbKeyboard(); ~QXcbKeyboard();
void initialize();
void selectEvents(); void selectEvents();
void handleKeyPressEvent(const xcb_key_press_event_t *event); void handleKeyPressEvent(const xcb_key_press_event_t *event);
@ -75,7 +72,7 @@ public:
Qt::KeyboardModifiers translateModifiers(int s) const; Qt::KeyboardModifiers translateModifiers(int s) const;
void updateKeymap(xcb_mapping_notify_event_t *event); void updateKeymap(xcb_mapping_notify_event_t *event);
void updateKeymap(); void updateKeymap();
QList<int> possibleKeys(const QKeyEvent *e) const; QList<int> possibleKeys(const QKeyEvent *event) const;
// when XKEYBOARD not present on the X server // when XKEYBOARD not present on the X server
void updateXKBMods(); void updateXKBMods();
@ -96,10 +93,6 @@ protected:
quint16 state, xcb_timestamp_t time, bool fromSendEvent); quint16 state, xcb_timestamp_t time, bool fromSendEvent);
void resolveMaskConflicts(); void resolveMaskConflicts();
QString lookupString(struct xkb_state *state, xcb_keycode_t code) const;
QString lookupStringNoKeysymTransformations(xkb_keysym_t keysym) const;
int keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers modifiers,
struct xkb_state *state, xcb_keycode_t code) const;
typedef QMap<xcb_keysym_t, int> KeysymModifierMap; typedef QMap<xcb_keysym_t, int> KeysymModifierMap;
struct xkb_keymap *keymapFromCore(const KeysymModifierMap &keysymMods); struct xkb_keymap *keymapFromCore(const KeysymModifierMap &keysymMods);
@ -111,9 +104,6 @@ protected:
void updateVModMapping(); void updateVModMapping();
void updateVModToRModMapping(); void updateVModToRModMapping();
xkb_keysym_t lookupLatinKeysym(xkb_keycode_t keycode) const;
void checkForLatinLayout() const;
private: private:
bool m_config = false; bool m_config = false;
bool m_isAutoRepeat = false; bool m_isAutoRepeat = false;
@ -148,22 +138,12 @@ private:
int core_device_id; int core_device_id;
#endif #endif
struct XKBStateDeleter { QXkbCommon::ScopedXKBState m_xkbState;
void operator()(struct xkb_state *state) const { return xkb_state_unref(state); } QXkbCommon::ScopedXKBKeymap m_xkbKeymap;
}; QXkbCommon::ScopedXKBContext m_xkbContext;
struct XKBKeymapDeleter {
void operator()(struct xkb_keymap *keymap) const { return xkb_keymap_unref(keymap); }
};
struct XKBContextDeleter {
void operator()(struct xkb_context *context) const { return xkb_context_unref(context); }
};
using ScopedXKBState = std::unique_ptr<struct xkb_state, XKBStateDeleter>;
using ScopedXKBKeymap = std::unique_ptr<struct xkb_keymap, XKBKeymapDeleter>;
using ScopedXKBContext = std::unique_ptr<struct xkb_context, XKBContextDeleter>;
ScopedXKBState m_xkbState; bool m_superAsMeta = false;
ScopedXKBKeymap m_xkbKeymap; bool m_hyperAsMeta = false;
ScopedXKBContext m_xkbContext;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -6,7 +6,8 @@ QT += \
core-private gui-private \ core-private gui-private \
service_support-private theme_support-private \ service_support-private theme_support-private \
fontdatabase_support-private \ fontdatabase_support-private \
edid_support-private edid_support-private \
xkbcommon_support-private
qtHaveModule(linuxaccessibility_support-private): \ qtHaveModule(linuxaccessibility_support-private): \
QT += linuxaccessibility_support-private QT += linuxaccessibility_support-private
@ -52,7 +53,6 @@ HEADERS = \
qxcbimage.h \ qxcbimage.h \
qxcbxsettings.h \ qxcbxsettings.h \
qxcbsystemtraytracker.h \ qxcbsystemtraytracker.h \
qxcbxkbcommon.h \
qxcbeventqueue.h \ qxcbeventqueue.h \
qxcbeventdispatcher.h \ qxcbeventdispatcher.h \
qxcbconnection_basic.h \ qxcbconnection_basic.h \

View File

@ -780,7 +780,10 @@ void QDialog::setVisible(bool visible)
QWidget::setVisible(visible); QWidget::setVisible(visible);
#if QT_DEPRECATED_SINCE(5, 13) #if QT_DEPRECATED_SINCE(5, 13)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
showExtension(d->doShowExtension); showExtension(d->doShowExtension);
QT_WARNING_POP
#endif #endif
QWidget *fw = window()->focusWidget(); QWidget *fw = window()->focusWidget();
if (!fw) if (!fw)

View File

@ -587,10 +587,13 @@ void QFileDialogPrivate::retranslateWindowTitle()
return; return;
if (q->acceptMode() == QFileDialog::AcceptOpen) { if (q->acceptMode() == QFileDialog::AcceptOpen) {
const QFileDialog::FileMode fileMode = q->fileMode(); const QFileDialog::FileMode fileMode = q->fileMode();
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
if (fileMode == QFileDialog::DirectoryOnly || fileMode == QFileDialog::Directory) if (fileMode == QFileDialog::DirectoryOnly || fileMode == QFileDialog::Directory)
q->setWindowTitle(QFileDialog::tr("Find Directory")); q->setWindowTitle(QFileDialog::tr("Find Directory"));
else else
q->setWindowTitle(QFileDialog::tr("Open")); q->setWindowTitle(QFileDialog::tr("Open"));
QT_WARNING_POP
} else } else
q->setWindowTitle(QFileDialog::tr("Save As")); q->setWindowTitle(QFileDialog::tr("Save As"));
@ -614,7 +617,10 @@ void QFileDialogPrivate::updateFileNameLabel()
setLabelTextControl(QFileDialog::FileName, options->labelText(QFileDialogOptions::FileName)); setLabelTextControl(QFileDialog::FileName, options->labelText(QFileDialogOptions::FileName));
} else { } else {
switch (q_func()->fileMode()) { switch (q_func()->fileMode()) {
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
case QFileDialog::DirectoryOnly: case QFileDialog::DirectoryOnly:
QT_WARNING_POP
case QFileDialog::Directory: case QFileDialog::Directory:
setLabelTextControl(QFileDialog::FileName, QFileDialog::tr("Directory:")); setLabelTextControl(QFileDialog::FileName, QFileDialog::tr("Directory:"));
break; break;
@ -642,7 +648,10 @@ void QFileDialogPrivate::updateOkButtonText(bool saveAsOnFolder)
return; return;
} else { } else {
switch (q->fileMode()) { switch (q->fileMode()) {
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
case QFileDialog::DirectoryOnly: case QFileDialog::DirectoryOnly:
QT_WARNING_POP
case QFileDialog::Directory: case QFileDialog::Directory:
setLabelTextControl(QFileDialog::Accept, QFileDialog::tr("&Choose")); setLabelTextControl(QFileDialog::Accept, QFileDialog::tr("&Choose"));
break; break;
@ -1709,7 +1718,10 @@ void QFileDialog::setFileMode(QFileDialog::FileMode mode)
d->options->setFileMode(static_cast<QFileDialogOptions::FileMode>(mode)); d->options->setFileMode(static_cast<QFileDialogOptions::FileMode>(mode));
// keep ShowDirsOnly option in sync with fileMode (BTW, DirectoryOnly is obsolete) // keep ShowDirsOnly option in sync with fileMode (BTW, DirectoryOnly is obsolete)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
setOption(ShowDirsOnly, mode == DirectoryOnly); setOption(ShowDirsOnly, mode == DirectoryOnly);
QT_WARNING_POP
if (!d->usingWidgets()) if (!d->usingWidgets())
return; return;
@ -1727,11 +1739,14 @@ void QFileDialog::setFileMode(QFileDialog::FileMode mode)
// set filter // set filter
d->model->setFilter(d->filterForMode(filter())); d->model->setFilter(d->filterForMode(filter()));
// setup file type for directory // setup file type for directory
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
if (mode == DirectoryOnly || mode == Directory) { if (mode == DirectoryOnly || mode == Directory) {
d->qFileDialogUi->fileTypeCombo->clear(); d->qFileDialogUi->fileTypeCombo->clear();
d->qFileDialogUi->fileTypeCombo->addItem(tr("Directories")); d->qFileDialogUi->fileTypeCombo->addItem(tr("Directories"));
d->qFileDialogUi->fileTypeCombo->setEnabled(false); d->qFileDialogUi->fileTypeCombo->setEnabled(false);
} }
QT_WARNING_POP
d->updateFileNameLabel(); d->updateFileNameLabel();
d->updateOkButtonText(); d->updateOkButtonText();
d->qFileDialogUi->fileTypeCombo->setEnabled(!testOption(ShowDirsOnly)); d->qFileDialogUi->fileTypeCombo->setEnabled(!testOption(ShowDirsOnly));
@ -2634,7 +2649,10 @@ QUrl QFileDialog::getExistingDirectoryUrl(QWidget *parent,
args.parent = parent; args.parent = parent;
args.caption = caption; args.caption = caption;
args.directory = QFileDialogPrivate::workingDirectory(dir); args.directory = QFileDialogPrivate::workingDirectory(dir);
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
args.mode = (options & ShowDirsOnly ? DirectoryOnly : Directory); args.mode = (options & ShowDirsOnly ? DirectoryOnly : Directory);
QT_WARNING_POP
args.options = options; args.options = options;
QFileDialog dialog(args); QFileDialog dialog(args);
@ -2747,7 +2765,10 @@ void QFileDialog::accept()
} }
switch (fileMode()) { switch (fileMode()) {
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
case DirectoryOnly: case DirectoryOnly:
QT_WARNING_POP
case Directory: { case Directory: {
QString fn = files.first(); QString fn = files.first();
QFileInfo info(fn); QFileInfo info(fn);
@ -2783,7 +2804,7 @@ void QFileDialog::accept()
} }
// check if we have to ask for permission to overwrite the file // check if we have to ask for permission to overwrite the file
if (!info.exists() || !confirmOverwrite() || acceptMode() == AcceptOpen) { if (!info.exists() || testOption(DontConfirmOverwrite) || acceptMode() == AcceptOpen) {
d->emitFilesSelected(QStringList(fn)); d->emitFilesSelected(QStringList(fn));
QDialog::accept(); QDialog::accept();
#if QT_CONFIG(messagebox) #if QT_CONFIG(messagebox)
@ -3651,7 +3672,10 @@ void QFileDialogPrivate::_q_updateOkButton()
isOpenDirectory = true; isOpenDirectory = true;
} else { } else {
switch (fileMode) { switch (fileMode) {
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
case QFileDialog::DirectoryOnly: case QFileDialog::DirectoryOnly:
QT_WARNING_POP
case QFileDialog::Directory: { case QFileDialog::Directory: {
QString fn = files.first(); QString fn = files.first();
QModelIndex idx = model->index(fn); QModelIndex idx = model->index(fn);
@ -3743,12 +3767,15 @@ void QFileDialogPrivate::_q_enterDirectory(const QModelIndex &index)
const QFileDialog::FileMode fileMode = q->fileMode(); const QFileDialog::FileMode fileMode = q->fileMode();
q->setDirectory(path); q->setDirectory(path);
emit q->directoryEntered(path); emit q->directoryEntered(path);
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
if (fileMode == QFileDialog::Directory if (fileMode == QFileDialog::Directory
|| fileMode == QFileDialog::DirectoryOnly) { || fileMode == QFileDialog::DirectoryOnly) {
// ### find out why you have to do both of these. // ### find out why you have to do both of these.
lineEdit()->setText(QString()); lineEdit()->setText(QString());
lineEdit()->clear(); lineEdit()->clear();
} }
QT_WARNING_POP
} else { } else {
// Do not accept when shift-clicking to multi-select a file in environments with single-click-activation (KDE) // Do not accept when shift-clicking to multi-select a file in environments with single-click-activation (KDE)
if (!q->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, qFileDialogUi->treeView) if (!q->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, qFileDialogUi->treeView)
@ -3840,7 +3867,10 @@ void QFileDialogPrivate::_q_selectionChanged()
{ {
const QFileDialog::FileMode fileMode = q_func()->fileMode(); const QFileDialog::FileMode fileMode = q_func()->fileMode();
const QModelIndexList indexes = qFileDialogUi->listView->selectionModel()->selectedRows(); const QModelIndexList indexes = qFileDialogUi->listView->selectionModel()->selectedRows();
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
bool stripDirs = (fileMode != QFileDialog::DirectoryOnly && fileMode != QFileDialog::Directory); bool stripDirs = (fileMode != QFileDialog::DirectoryOnly && fileMode != QFileDialog::Directory);
QT_WARNING_POP
QStringList allFiles; QStringList allFiles;
for (const auto &index : indexes) { for (const auto &index : indexes) {
@ -3892,10 +3922,13 @@ void QFileDialogPrivate::_q_rowsInserted(const QModelIndex &parent)
void QFileDialogPrivate::_q_fileRenamed(const QString &path, const QString &oldName, const QString &newName) void QFileDialogPrivate::_q_fileRenamed(const QString &path, const QString &oldName, const QString &newName)
{ {
const QFileDialog::FileMode fileMode = q_func()->fileMode(); const QFileDialog::FileMode fileMode = q_func()->fileMode();
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
if (fileMode == QFileDialog::Directory || fileMode == QFileDialog::DirectoryOnly) { if (fileMode == QFileDialog::Directory || fileMode == QFileDialog::DirectoryOnly) {
if (path == rootPath() && lineEdit()->text() == oldName) if (path == rootPath() && lineEdit()->text() == oldName)
lineEdit()->setText(newName); lineEdit()->setText(newName);
} }
QT_WARNING_POP
} }
void QFileDialogPrivate::_q_emitUrlSelected(const QUrl &file) void QFileDialogPrivate::_q_emitUrlSelected(const QUrl &file)

View File

@ -164,13 +164,9 @@ public:
QDir::Filters filterForMode(QDir::Filters filters) const QDir::Filters filterForMode(QDir::Filters filters) const
{ {
const QFileDialog::FileMode fileMode = q_func()->fileMode(); filters |= QDir::Drives | QDir::AllDirs | QDir::Dirs | QDir::Files;
if (fileMode == QFileDialog::DirectoryOnly) { if (q_func()->testOption(QFileDialog::ShowDirsOnly))
filters |= QDir::Drives | QDir::AllDirs | QDir::Dirs;
filters &= ~QDir::Files; filters &= ~QDir::Files;
} else {
filters |= QDir::Drives | QDir::AllDirs | QDir::Files | QDir::Dirs;
}
return filters; return filters;
} }

View File

@ -2306,7 +2306,7 @@ void QGraphicsWidget::paintWindowFrame(QPainter *painter, const QStyleOptionGrap
QStyleHintReturnMask mask; QStyleHintReturnMask mask;
bool setMask = style()->styleHint(QStyle::SH_WindowFrame_Mask, &bar, widget, &mask) && !mask.region.isEmpty(); bool setMask = style()->styleHint(QStyle::SH_WindowFrame_Mask, &bar, widget, &mask) && !mask.region.isEmpty();
bool hasBorder = !style()->styleHint(QStyle::SH_TitleBar_NoBorder, &bar, widget); bool hasBorder = !style()->styleHint(QStyle::SH_TitleBar_NoBorder, &bar, widget);
int frameWidth = style()->pixelMetric(QStyle::PM_MDIFrameWidth, &bar, widget); int frameWidth = style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, &bar, widget);
if (setMask) { if (setMask) {
painter->save(); painter->save();
painter->setClipRegion(mask.region, Qt::IntersectClip); painter->setClipRegion(mask.region, Qt::IntersectClip);

View File

@ -789,7 +789,7 @@ void QItemDelegate::drawCheck(QPainter *painter,
const QWidget *widget = d->widget(option); const QWidget *widget = d->widget(option);
QStyle *style = widget ? widget->style() : QApplication::style(); QStyle *style = widget ? widget->style() : QApplication::style();
style->drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &opt, painter, widget); style->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &opt, painter, widget);
} }
/*! /*!

View File

@ -194,7 +194,7 @@ void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, Q
opt->state & (State_Sunken | State_On), 1, opt->state & (State_Sunken | State_On), 1,
&opt->palette.brush(QPalette::Button)); &opt->palette.brush(QPalette::Button));
break; break;
case PE_IndicatorViewItemCheck: case PE_IndicatorItemViewItemCheck:
proxy()->drawPrimitive(PE_IndicatorCheckBox, opt, p, widget); proxy()->drawPrimitive(PE_IndicatorCheckBox, opt, p, widget);
break; break;
case PE_IndicatorCheckBox: case PE_IndicatorCheckBox:
@ -2283,7 +2283,7 @@ void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt,
option.state |= QStyle::State_On; option.state |= QStyle::State_On;
break; break;
} }
proxy()->drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &option, p, widget); proxy()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &option, p, widget);
} }
// draw the icon // draw the icon

View File

@ -558,7 +558,7 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem,
qt_fusion_draw_arrow(arrow, painter, option, option->rect, arrowColor); qt_fusion_draw_arrow(arrow, painter, option, option->rect, arrowColor);
} }
break; break;
case PE_IndicatorViewItemCheck: case PE_IndicatorItemViewItemCheck:
{ {
QStyleOptionButton button; QStyleOptionButton button;
button.QStyleOption::operator=(*option); button.QStyleOption::operator=(*option);
@ -3673,7 +3673,7 @@ int QFusionStyle::styleHint(StyleHint hint, const QStyleOption *option, const QW
case SH_FontDialog_SelectAssociatedText: case SH_FontDialog_SelectAssociatedText:
case SH_MenuBar_AltKeyNavigation: case SH_MenuBar_AltKeyNavigation:
case SH_ComboBox_ListMouseTracking: case SH_ComboBox_ListMouseTracking:
case SH_ScrollBar_StopMouseOverSlider: case SH_Slider_StopMouseOverSlider:
case SH_ScrollBar_MiddleClickAbsolutePosition: case SH_ScrollBar_MiddleClickAbsolutePosition:
case SH_EtchDisabledText: case SH_EtchDisabledText:
case SH_TitleBar_AutoRaise: case SH_TitleBar_AutoRaise:

View File

@ -4304,7 +4304,7 @@ void QStyleSheetStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *op
switch (pe) { switch (pe) {
case PE_FrameStatusBar: { case PE_FrameStatusBarItem: {
QRenderRule subRule = renderRule(w ? w->parentWidget() : nullptr, opt, PseudoElement_Item); QRenderRule subRule = renderRule(w ? w->parentWidget() : nullptr, opt, PseudoElement_Item);
if (subRule.hasDrawable()) { if (subRule.hasDrawable()) {
subRule.drawRule(p, opt->rect); subRule.drawRule(p, opt->rect);
@ -4325,7 +4325,7 @@ void QStyleSheetStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *op
pseudoElement = PseudoElement_ExclusiveIndicator; pseudoElement = PseudoElement_ExclusiveIndicator;
break; break;
case PE_IndicatorViewItemCheck: case PE_IndicatorItemViewItemCheck:
pseudoElement = PseudoElement_ViewItemIndicator; pseudoElement = PseudoElement_ViewItemIndicator;
break; break;

View File

@ -554,7 +554,7 @@ int QWindowsStyle::styleHint(StyleHint hint, const QStyleOption *opt, const QWid
case SH_MenuBar_MouseTracking: case SH_MenuBar_MouseTracking:
case SH_Menu_MouseTracking: case SH_Menu_MouseTracking:
case SH_ComboBox_ListMouseTracking: case SH_ComboBox_ListMouseTracking:
case SH_ScrollBar_StopMouseOverSlider: case SH_Slider_StopMouseOverSlider:
case SH_MainWindow_SpaceBelowMenuBar: case SH_MainWindow_SpaceBelowMenuBar:
ret = 1; ret = 1;
@ -827,13 +827,13 @@ void QWindowsStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt,
p->setPen(opt->palette.text().color()); p->setPen(opt->palette.text().color());
} }
Q_FALLTHROUGH(); Q_FALLTHROUGH();
case PE_IndicatorViewItemCheck: case PE_IndicatorItemViewItemCheck:
if (!doRestore) { if (!doRestore) {
p->save(); p->save();
doRestore = true; doRestore = true;
} }
#if QT_CONFIG(itemviews) #if QT_CONFIG(itemviews)
if (pe == PE_IndicatorViewItemCheck) { if (pe == PE_IndicatorItemViewItemCheck) {
const QStyleOptionViewItem *itemViewOpt = qstyleoption_cast<const QStyleOptionViewItem *>(opt); const QStyleOptionViewItem *itemViewOpt = qstyleoption_cast<const QStyleOptionViewItem *>(opt);
p->setPen(itemViewOpt p->setPen(itemViewOpt
&& itemViewOpt->showDecorationSelected && itemViewOpt->showDecorationSelected

View File

@ -1389,7 +1389,10 @@ void QComboBoxPrivate::_q_emitCurrentIndexChanged(const QModelIndex &index)
const QString text = itemText(index); const QString text = itemText(index);
emit q->currentIndexChanged(index.row()); emit q->currentIndexChanged(index.row());
#if QT_DEPRECATED_SINCE(5, 13) #if QT_DEPRECATED_SINCE(5, 13)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
emit q->currentIndexChanged(text); emit q->currentIndexChanged(text);
QT_WARNING_POP
#endif #endif
// signal lineEdit.textChanged already connected to signal currentTextChanged, so don't emit double here // signal lineEdit.textChanged already connected to signal currentTextChanged, so don't emit double here
if (!lineEdit) if (!lineEdit)

View File

@ -18,6 +18,7 @@
"QtEventDispatcherSupport" => "$basedir/src/platformsupport/eventdispatchers", "QtEventDispatcherSupport" => "$basedir/src/platformsupport/eventdispatchers",
"QtFontDatabaseSupport" => "$basedir/src/platformsupport/fontdatabases", "QtFontDatabaseSupport" => "$basedir/src/platformsupport/fontdatabases",
"QtInputSupport" => "$basedir/src/platformsupport/input", "QtInputSupport" => "$basedir/src/platformsupport/input",
"QtXkbCommonSupport" => "$basedir/src/platformsupport/input/xkbcommon",
"QtPlatformCompositorSupport" => "$basedir/src/platformsupport/platformcompositor", "QtPlatformCompositorSupport" => "$basedir/src/platformsupport/platformcompositor",
"QtServiceSupport" => "$basedir/src/platformsupport/services", "QtServiceSupport" => "$basedir/src/platformsupport/services",
"QtThemeSupport" => "$basedir/src/platformsupport/themes", "QtThemeSupport" => "$basedir/src/platformsupport/themes",

View File

@ -67,3 +67,8 @@ winrt|!qtHaveModule(gui)|!qtConfig(accessibility): SUBDIRS -= qaccessibility
android: SUBDIRS += \ android: SUBDIRS += \
android android
qtConfig(xkbcommon): {
SUBDIRS += \
xkbkeyboard
}

View File

@ -0,0 +1,60 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtCore>
#include <QtGui>
#include <QtTest>
#include <qpa/qplatforminputcontextfactory_p.h>
#include <qpa/qplatforminputcontext.h>
class tst_XkbKeyboard : public QObject
{
Q_OBJECT
private slots:
void verifyComposeInputContextInterface();
};
void tst_XkbKeyboard::verifyComposeInputContextInterface()
{
QPlatformInputContext *inputContext = QPlatformInputContextFactory::create(QStringLiteral("compose"));
QVERIFY(inputContext);
const char *const inputContextClassName = "QComposeInputContext";
const char *const normalizedSignature = "setXkbContext(xkb_context*)";
QVERIFY(inputContext->objectName() == QLatin1String(inputContextClassName));
int methodIndex = inputContext->metaObject()->indexOfMethod(normalizedSignature);
QMetaMethod method = inputContext->metaObject()->method(methodIndex);
Q_ASSERT(method.isValid());
}
QTEST_MAIN(tst_XkbKeyboard)
#include "tst_xkbkeyboard.moc"

View File

@ -0,0 +1,7 @@
CONFIG += testcase
TARGET = tst_xkbkeyboard
SOURCES += tst_xkbkeyboard.cpp
QT = core-private gui-private testlib

View File

@ -113,7 +113,7 @@ int main(int argc, char **argv) {
outIndicesBuffer.write("] = {\n"); outIndicesBuffer.write("] = {\n");
int totalUtf8Size = 0; int totalUtf8Size = 0;
int chunkSize = 0; int chunkSize = 0; // strlen of the current chunk (sizeof is bigger by 1)
int stringUtf8Size = 0; int stringUtf8Size = 0;
QStringList chunks; QStringList chunks;
for (int a = 0; a < lineCount; a++) { for (int a = 0; a < lineCount; a++) {
@ -127,7 +127,8 @@ int main(int argc, char **argv) {
int quoteCount = strings.at(a).count('"'); int quoteCount = strings.at(a).count('"');
stringUtf8Size = strings.at(a).count() - (zeroCount + quoteCount + utf8CharsCount * 3); stringUtf8Size = strings.at(a).count() - (zeroCount + quoteCount + utf8CharsCount * 3);
chunkSize += stringUtf8Size; chunkSize += stringUtf8Size;
if (chunkSize > 65535) { // MSVC 2015 chokes if sizeof(a single string) > 0xffff
if (chunkSize >= 0xffff) {
static int chunkCount = 0; static int chunkCount = 0;
qWarning() << "chunk" << ++chunkCount << "has length" << chunkSize - stringUtf8Size; qWarning() << "chunk" << ++chunkCount << "has length" << chunkSize - stringUtf8Size;
outDataBuffer.write(",\n\n"); outDataBuffer.write(",\n\n");