Merge remote-tracking branch 'origin/5.13' into dev
Change-Id: Ic0037eac1d85a0e60e7b1a590d49b5ee6205bfc8
This commit is contained in:
commit
84e15d6f48
@ -200,7 +200,9 @@ CMAKE_QT5_MODULE_DEPS = $$join(lib_deps, ";")
|
||||
CMAKE_INTERFACE_MODULE_DEPS = $$join(aux_mod_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 {
|
||||
!isEmpty(CMAKE_STATIC_TYPE) {
|
||||
|
@ -338,7 +338,7 @@ defineTest(qtConfParseCommandLine) {
|
||||
qtConfAddWarning("Command line option -skip is only effective in top-level builds.")
|
||||
skipOptionWarningAdded = 1
|
||||
}
|
||||
$$qtConfGetNextCommandlineArg()
|
||||
val = $$qtConfGetNextCommandlineArg()
|
||||
next()
|
||||
}
|
||||
|
||||
|
@ -532,7 +532,7 @@ MakefileGenerator::init()
|
||||
QStack<int> state;
|
||||
enum { IN_CONDITION, MET_CONDITION, PENDING_CONDITION };
|
||||
for (int count = 1; !in.atEnd(); ++count) {
|
||||
QString line = QString::fromUtf8(in.readLine());
|
||||
QString line = QString::fromLatin1(in.readLine());
|
||||
if (line.startsWith("!!IF ")) {
|
||||
if (state.isEmpty() || state.top() == IN_CONDITION) {
|
||||
QString test = line.mid(5, line.length()-(5+1));
|
||||
@ -578,7 +578,7 @@ MakefileGenerator::init()
|
||||
contents += project->expand(line, in.fileName(), count);
|
||||
}
|
||||
}
|
||||
contentBytes = contents.toUtf8();
|
||||
contentBytes = contents.toLatin1();
|
||||
}
|
||||
QFile out(outn);
|
||||
QFileInfo outfi(out);
|
||||
|
@ -344,7 +344,9 @@ public class QtActivityDelegate
|
||||
}
|
||||
} else if ((inputHints & ImhHiddenText) != 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
35
src/platformsupport/input/input-support.pro
Normal file
35
src/platformsupport/input/input-support.pro
Normal 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)
|
@ -1,35 +1,8 @@
|
||||
TARGET = QtInputSupport
|
||||
MODULE = input_support
|
||||
TEMPLATE = subdirs
|
||||
QT_FOR_CONFIG += gui-private
|
||||
|
||||
QT = core-private gui-private devicediscovery_support-private
|
||||
CONFIG += static internal_module
|
||||
qtConfig(xkbcommon): SUBDIRS += xkbcommon
|
||||
|
||||
DEFINES += QT_NO_CAST_FROM_ASCII
|
||||
PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h
|
||||
SUBDIRS += input-support.pro ### FIXME - QTBUG-52657
|
||||
|
||||
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)
|
||||
CONFIG += ordered
|
||||
|
@ -14,4 +14,7 @@ QMAKE_USE_PRIVATE += libudev libinput
|
||||
|
||||
INCLUDEPATH += $$PWD/../shared
|
||||
|
||||
qtConfig(xkbcommon): QMAKE_USE_PRIVATE += xkbcommon
|
||||
qtConfig(xkbcommon): {
|
||||
QMAKE_USE_PRIVATE += xkbcommon
|
||||
QT += xkbcommon_support-private
|
||||
}
|
||||
|
@ -47,6 +47,7 @@
|
||||
#if QT_CONFIG(xkbcommon)
|
||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||
#include <xkbcommon/xkbcommon-names.h>
|
||||
#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@ -56,88 +57,7 @@ Q_DECLARE_LOGGING_CATEGORY(qLcLibInput)
|
||||
const int REPEAT_DELAY = 500;
|
||||
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()
|
||||
#if QT_CONFIG(xkbcommon)
|
||||
: m_ctx(0),
|
||||
m_keymap(0),
|
||||
m_state(0),
|
||||
m_mods(Qt::NoModifier)
|
||||
#endif
|
||||
{
|
||||
#if QT_CONFIG(xkbcommon)
|
||||
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);
|
||||
if (!m_keymap) {
|
||||
qWarning("Failed to compile keymap");
|
||||
qCWarning(qLcLibInput, "Failed to compile keymap");
|
||||
return;
|
||||
}
|
||||
m_state = xkb_state_new(m_keymap);
|
||||
if (!m_state) {
|
||||
qWarning("Failed to create xkb state");
|
||||
qCWarning(qLcLibInput, "Failed to create xkb state");
|
||||
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);
|
||||
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)
|
||||
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;
|
||||
|
||||
QVarLengthArray<char, 32> chars(32);
|
||||
const int size = xkb_state_key_get_utf8(m_state, k, chars.data(), chars.size());
|
||||
if (Q_UNLIKELY(size + 1 > chars.size())) { // +1 for NUL
|
||||
chars.resize(size + 1);
|
||||
xkb_state_key_get_utf8(m_state, k, chars.data(), chars.size());
|
||||
}
|
||||
const QString text = QString::fromUtf8(chars.constData(), size);
|
||||
// Modifiers here is the modifier state before the event, i.e. not
|
||||
// including the current key in case it is a modifier. See the XOR
|
||||
// logic in QKeyEvent::modifiers(). ### QTBUG-73826
|
||||
Qt::KeyboardModifiers modifiers = QXkbCommon::modifiers(m_state);
|
||||
|
||||
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
|
||||
// including the current key in case it is a modifier.
|
||||
Qt::KeyboardModifiers mods = Qt::NoModifier;
|
||||
const int qtkey = keysymToQtKey(sym, &mods, text);
|
||||
xkb_state_update_key(m_state, keycode, pressed ? XKB_KEY_DOWN : XKB_KEY_UP);
|
||||
|
||||
if (qtkey == Qt::Key_Control)
|
||||
mods |= Qt::ControlModifier;
|
||||
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);
|
||||
Qt::KeyboardModifiers modifiersAfterStateChange = QXkbCommon::modifiers(m_state);
|
||||
QGuiApplicationPrivate::inputDeviceManager()->setKeyboardModifiers(modifiersAfterStateChange);
|
||||
|
||||
if (mods != Qt::NoModifier) {
|
||||
if (pressed)
|
||||
m_mods |= mods;
|
||||
else
|
||||
m_mods &= ~mods;
|
||||
|
||||
QGuiApplicationPrivate::inputDeviceManager()->setKeyboardModifiers(m_mods);
|
||||
}
|
||||
QWindowSystemInterface::handleExtendedKeyEvent(nullptr,
|
||||
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.mods = mods;
|
||||
m_repeatData.nativeScanCode = k;
|
||||
m_repeatData.mods = modifiers;
|
||||
m_repeatData.nativeScanCode = keycode;
|
||||
m_repeatData.virtualKey = sym;
|
||||
m_repeatData.nativeMods = mods;
|
||||
m_repeatData.nativeMods = modifiers;
|
||||
m_repeatData.unicodeText = text;
|
||||
m_repeatData.repeatCount = 1;
|
||||
m_repeatTimer.setInterval(REPEAT_DELAY);
|
||||
@ -256,50 +153,6 @@ void QLibInputKeyboard::handleRepeat()
|
||||
m_repeatTimer.setInterval(REPEAT_RATE);
|
||||
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
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -79,10 +79,9 @@ private:
|
||||
int keysymToQtKey(xkb_keysym_t key) const;
|
||||
int keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers *modifiers, const QString &text) const;
|
||||
|
||||
xkb_context *m_ctx;
|
||||
xkb_keymap *m_keymap;
|
||||
xkb_state *m_state;
|
||||
xkb_mod_index_t m_modindex[4];
|
||||
xkb_context *m_ctx = nullptr;
|
||||
xkb_keymap *m_keymap = nullptr;
|
||||
xkb_state *m_state = nullptr;
|
||||
|
||||
QTimer m_repeatTimer;
|
||||
|
||||
@ -95,7 +94,6 @@ private:
|
||||
QString unicodeText;
|
||||
int repeatCount;
|
||||
} m_repeatData;
|
||||
Qt::KeyboardModifiers m_mods;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
828
src/platformsupport/input/xkbcommon/qxkbcommon.cpp
Normal file
828
src/platformsupport/input/xkbcommon/qxkbcommon.cpp
Normal 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
|
@ -1,9 +1,9 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** 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$
|
||||
** Commercial License Usage
|
||||
@ -37,10 +37,7 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
/* XConvertCase was copied from src/3rdparty/xkbcommon/src/keysym.c,
|
||||
which contains the following license information:
|
||||
|
||||
Copyright 1985, 1987, 1990, 1998 The Open Group
|
||||
/* Copyright 1985, 1987, 1990, 1998 The Open Group
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
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:
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <QtCore/QChar>
|
||||
#include "qxkbcommon_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
#include <QtCore/QChar>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 */
|
||||
if (sym < 0x100) {
|
||||
@ -220,14 +217,3 @@ void xkbcommon_XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t
|
||||
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
|
122
src/platformsupport/input/xkbcommon/qxkbcommon_p.h
Normal file
122
src/platformsupport/input/xkbcommon/qxkbcommon_p.h
Normal 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
|
23
src/platformsupport/input/xkbcommon/xkbcommon.pro
Normal file
23
src/platformsupport/input/xkbcommon/xkbcommon.pro
Normal 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)
|
@ -3,18 +3,14 @@ TARGET = composeplatforminputcontextplugin
|
||||
QT += core-private gui-private
|
||||
|
||||
SOURCES += $$PWD/qcomposeplatforminputcontextmain.cpp \
|
||||
$$PWD/qcomposeplatforminputcontext.cpp \
|
||||
$$PWD/generator/qtablegenerator.cpp \
|
||||
$$PWD/qcomposeplatforminputcontext.cpp
|
||||
|
||||
HEADERS += $$PWD/qcomposeplatforminputcontext.h \
|
||||
$$PWD/generator/qtablegenerator.h \
|
||||
HEADERS += $$PWD/qcomposeplatforminputcontext.h
|
||||
|
||||
QMAKE_USE_PRIVATE += xkbcommon
|
||||
|
||||
include($$OUT_PWD/../../../gui/qtgui-config.pri)
|
||||
|
||||
DEFINES += X11_PREFIX='\\"$$QMAKE_X11_PREFIX\\"'
|
||||
|
||||
OTHER_FILES += $$PWD/compose.json
|
||||
|
||||
PLUGIN_TYPE = platforminputcontexts
|
||||
|
@ -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 user’s 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());
|
||||
}
|
||||
|
@ -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
|
@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the plugins of the Qt Toolkit.
|
||||
@ -36,131 +36,100 @@
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qcomposeplatforminputcontext.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtGui/QKeyEvent>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include <algorithm>
|
||||
#include <locale.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
//#define DEBUG_COMPOSING
|
||||
|
||||
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
|
||||
};
|
||||
Q_LOGGING_CATEGORY(lcXkbCompose, "qt.xkb.compose")
|
||||
|
||||
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)
|
||||
{
|
||||
const QKeyEvent *keyEvent = (const QKeyEvent *)event;
|
||||
// should pass only the key presses
|
||||
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)
|
||||
auto keyEvent = static_cast<const QKeyEvent *>(event);
|
||||
if (keyEvent->type() != QEvent::KeyPress)
|
||||
return false;
|
||||
|
||||
int keyval = keyEvent->key();
|
||||
int keysym = 0;
|
||||
|
||||
if (ignoreKey(keyval))
|
||||
if (!inputMethodAccepted())
|
||||
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;
|
||||
|
||||
keysym = keyEvent->nativeVirtualKey();
|
||||
xkb_compose_state_feed(m_composeState, keyEvent->nativeVirtualKey());
|
||||
|
||||
int nCompose = 0;
|
||||
while (nCompose < QT_KEYSEQUENCE_MAX_LEN && m_composeBuffer[nCompose] != 0)
|
||||
nCompose++;
|
||||
|
||||
if (nCompose == QT_KEYSEQUENCE_MAX_LEN) {
|
||||
reset();
|
||||
nCompose = 0;
|
||||
}
|
||||
|
||||
m_composeBuffer[nCompose] = keysym;
|
||||
// check sequence
|
||||
if (checkComposeTable())
|
||||
switch (xkb_compose_state_get_status(m_composeState)) {
|
||||
case XKB_COMPOSE_COMPOSING:
|
||||
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
|
||||
@ -175,7 +144,8 @@ void QComposeInputContext::setFocusObject(QObject *object)
|
||||
|
||||
void QComposeInputContext::reset()
|
||||
{
|
||||
clearComposeBuffer();
|
||||
if (m_composeState)
|
||||
xkb_compose_state_reset(m_composeState);
|
||||
}
|
||||
|
||||
void QComposeInputContext::update(Qt::InputMethodQueries q)
|
||||
@ -183,125 +153,4 @@ void QComposeInputContext::update(Qt::InputMethodQueries 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
|
||||
|
@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the plugins of the Qt Toolkit.
|
||||
@ -36,24 +36,24 @@
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QCOMPOSEPLATFORMINPUTCONTEXT_H
|
||||
#define QCOMPOSEPLATFORMINPUTCONTEXT_H
|
||||
|
||||
#include <QtCore/QLoggingCategory>
|
||||
|
||||
#include <qpa/qplatforminputcontext.h>
|
||||
|
||||
#include <QtCore/QList>
|
||||
|
||||
#include "generator/qtablegenerator.h"
|
||||
#include <xkbcommon/xkbcommon-compose.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(lcXkbCompose)
|
||||
|
||||
class QEvent;
|
||||
|
||||
class QComposeInputContext : public QPlatformInputContext
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QComposeInputContext();
|
||||
~QComposeInputContext();
|
||||
@ -62,21 +62,22 @@ public:
|
||||
void setFocusObject(QObject *object) override;
|
||||
void reset() override;
|
||||
void update(Qt::InputMethodQueries) 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:
|
||||
void clearComposeBuffer();
|
||||
bool ignoreKey(int keyval) const;
|
||||
bool composeKey(int keyval) const;
|
||||
bool checkComposeTable();
|
||||
void commitText(uint character) const;
|
||||
void ensureInitialized();
|
||||
|
||||
private:
|
||||
QObject *m_focusObject;
|
||||
QVector<QComposeTableElement> m_composeTable;
|
||||
uint m_composeBuffer[QT_KEYSEQUENCE_MAX_LEN];
|
||||
TableGenerator::TableState m_tableState;
|
||||
bool m_compositionTableInitialized;
|
||||
bool m_initialized = false;
|
||||
xkb_context *m_context = nullptr;
|
||||
xkb_compose_table *m_composeTable = nullptr;
|
||||
xkb_compose_state *m_composeState = nullptr;
|
||||
QObject *m_focusObject = nullptr;
|
||||
struct xkb_context *m_XkbContext = nullptr;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** 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
|
||||
|| system.compare(system, QLatin1String("xim"), Qt::CaseInsensitive) == 0)
|
||||
return new QComposeInputContext;
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -565,10 +565,15 @@ static void quitQtAndroidPlugin(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
|
||||
if (!QAndroidEventDispatcherStopper::instance()->stopped()) {
|
||||
sem_wait(&m_terminateSemaphore);
|
||||
sem_destroy(&m_terminateSemaphore);
|
||||
if (QAndroidEventDispatcherStopper::instance()->stopped()) {
|
||||
QAndroidEventDispatcherStopper::instance()->startAll();
|
||||
QCoreApplication::quit();
|
||||
QAndroidEventDispatcherStopper::instance()->goingToStop(false);
|
||||
}
|
||||
|
||||
sem_wait(&m_terminateSemaphore);
|
||||
sem_destroy(&m_terminateSemaphore);
|
||||
|
||||
env->DeleteGlobalRef(m_applicationClass);
|
||||
env->DeleteGlobalRef(m_classLoaderObject);
|
||||
if (m_resourcesObj)
|
||||
@ -588,10 +593,7 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/)
|
||||
m_androidPlatformIntegration = nullptr;
|
||||
delete m_androidAssetsFileEngineHandler;
|
||||
m_androidAssetsFileEngineHandler = nullptr;
|
||||
|
||||
if (!QAndroidEventDispatcherStopper::instance()->stopped()) {
|
||||
sem_post(&m_exitSemaphore);
|
||||
}
|
||||
sem_post(&m_exitSemaphore);
|
||||
}
|
||||
|
||||
static void setSurface(JNIEnv *env, jobject /*thiz*/, jint id, jobject jSurface, jint w, jint h)
|
||||
|
@ -47,6 +47,7 @@
|
||||
|
||||
#include <QSurfaceFormat>
|
||||
#include <QtGui/private/qwindow_p.h>
|
||||
#include <QtGui/qguiapplication.h>
|
||||
|
||||
#include <qpa/qwindowsysteminterface.h>
|
||||
#include <qpa/qplatformscreen.h>
|
||||
@ -121,7 +122,7 @@ void QAndroidPlatformOpenGLWindow::setGeometry(const QRect &rect)
|
||||
|
||||
EGLSurface QAndroidPlatformOpenGLWindow::eglSurface(EGLConfig config)
|
||||
{
|
||||
if (QAndroidEventDispatcherStopper::stopped())
|
||||
if (QAndroidEventDispatcherStopper::stopped() || QGuiApplication::applicationState() == Qt::ApplicationSuspended)
|
||||
return m_eglSurface;
|
||||
|
||||
QMutexLocker lock(&m_surfaceMutex);
|
||||
|
@ -357,6 +357,8 @@ void QXcbIntegration::initialize()
|
||||
m_inputContext.reset(QPlatformInputContextFactory::create(icStr));
|
||||
if (!m_inputContext && icStr != defaultInputContext && icStr != QLatin1String("none"))
|
||||
m_inputContext.reset(QPlatformInputContextFactory::create(defaultInputContext));
|
||||
|
||||
defaultConnection()->keyboard()->initialize();
|
||||
}
|
||||
|
||||
void QXcbIntegration::moveToScreen(QWindow *window, int screen)
|
||||
|
@ -39,7 +39,6 @@
|
||||
#include "qxcbkeyboard.h"
|
||||
#include "qxcbwindow.h"
|
||||
#include "qxcbscreen.h"
|
||||
#include "qxcbxkbcommon.h"
|
||||
|
||||
#include <qpa/qwindowsysteminterface.h>
|
||||
#include <qpa/qplatforminputcontext.h>
|
||||
@ -49,404 +48,20 @@
|
||||
#include <QtCore/QMetaEnum>
|
||||
|
||||
#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)
|
||||
#include <xcb/xinput.h>
|
||||
#endif
|
||||
|
||||
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 ret = 0;
|
||||
Qt::KeyboardModifiers ret = Qt::NoModifier;
|
||||
if (s & XCB_MOD_MASK_SHIFT)
|
||||
ret |= Qt::ShiftModifier;
|
||||
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 xupper;
|
||||
xkbcommon_XConvertCase(unshifted, &xlower, &xupper);
|
||||
QXkbCommon::xkbcommon_XConvertCase(unshifted, &xlower, &xupper);
|
||||
|
||||
if (xlower != xupper // Check if symbol is cased
|
||||
&& unshifted == xupper) { // Unshifted must be upper case
|
||||
@ -805,7 +420,12 @@ void QXcbKeyboard::updateKeymap()
|
||||
|
||||
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)
|
||||
@ -918,272 +538,6 @@ void QXcbKeyboard::updateXKBMods()
|
||||
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)
|
||||
: QXcbObject(connection)
|
||||
{
|
||||
@ -1211,6 +565,12 @@ QXcbKeyboard::~QXcbKeyboard()
|
||||
xcb_key_symbols_free(m_key_symbols);
|
||||
}
|
||||
|
||||
void QXcbKeyboard::initialize()
|
||||
{
|
||||
auto inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
|
||||
QXkbCommon::setXkbContext(inputContext, m_xkbContext.get());
|
||||
}
|
||||
|
||||
void QXcbKeyboard::selectEvents()
|
||||
{
|
||||
#if QT_CONFIG(xkb)
|
||||
@ -1514,6 +874,12 @@ void QXcbKeyboard::resolveMaskConflicts()
|
||||
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,
|
||||
@ -1529,7 +895,7 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
|
||||
if (type == QEvent::KeyPress)
|
||||
targetWindow->updateNetWmUserTime(time);
|
||||
|
||||
ScopedXKBState sendEventState;
|
||||
QXkbCommon::ScopedXKBState sendEventState;
|
||||
if (fromSendEvent) {
|
||||
// Have a temporary keyboard state filled in from 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();
|
||||
|
||||
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);
|
||||
if (sym >= XKB_KEY_KP_Space && sym <= XKB_KEY_KP_9)
|
||||
if (QXkbCommon::isKeypad(sym))
|
||||
modifiers |= Qt::KeypadModifier;
|
||||
|
||||
// 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.
|
||||
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);
|
||||
int qtcode = QXkbCommon::keysymToQtKey(sym, modifiers, xkbState, code, m_superAsMeta, m_hyperAsMeta);
|
||||
|
||||
if (type == QEvent::KeyPress) {
|
||||
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)
|
||||
{
|
||||
// From X11 protocol: Every event contains an 8-bit type code. The most
|
||||
|
@ -50,16 +50,12 @@
|
||||
#endif
|
||||
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#if QT_CONFIG(xkb)
|
||||
#include <xkbcommon/xkbcommon-x11.h>
|
||||
#endif
|
||||
#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
|
||||
|
||||
#include <QEvent>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QWindow;
|
||||
|
||||
class QXcbKeyboard : public QXcbObject
|
||||
{
|
||||
public:
|
||||
@ -67,6 +63,7 @@ public:
|
||||
|
||||
~QXcbKeyboard();
|
||||
|
||||
void initialize();
|
||||
void selectEvents();
|
||||
|
||||
void handleKeyPressEvent(const xcb_key_press_event_t *event);
|
||||
@ -75,7 +72,7 @@ public:
|
||||
Qt::KeyboardModifiers translateModifiers(int s) const;
|
||||
void updateKeymap(xcb_mapping_notify_event_t *event);
|
||||
void updateKeymap();
|
||||
QList<int> possibleKeys(const QKeyEvent *e) const;
|
||||
QList<int> possibleKeys(const QKeyEvent *event) const;
|
||||
|
||||
// when XKEYBOARD not present on the X server
|
||||
void updateXKBMods();
|
||||
@ -96,10 +93,6 @@ protected:
|
||||
quint16 state, xcb_timestamp_t time, bool fromSendEvent);
|
||||
|
||||
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;
|
||||
struct xkb_keymap *keymapFromCore(const KeysymModifierMap &keysymMods);
|
||||
@ -111,9 +104,6 @@ protected:
|
||||
void updateVModMapping();
|
||||
void updateVModToRModMapping();
|
||||
|
||||
xkb_keysym_t lookupLatinKeysym(xkb_keycode_t keycode) const;
|
||||
void checkForLatinLayout() const;
|
||||
|
||||
private:
|
||||
bool m_config = false;
|
||||
bool m_isAutoRepeat = false;
|
||||
@ -148,22 +138,12 @@ private:
|
||||
int core_device_id;
|
||||
#endif
|
||||
|
||||
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>;
|
||||
QXkbCommon::ScopedXKBState m_xkbState;
|
||||
QXkbCommon::ScopedXKBKeymap m_xkbKeymap;
|
||||
QXkbCommon::ScopedXKBContext m_xkbContext;
|
||||
|
||||
ScopedXKBState m_xkbState;
|
||||
ScopedXKBKeymap m_xkbKeymap;
|
||||
ScopedXKBContext m_xkbContext;
|
||||
bool m_superAsMeta = false;
|
||||
bool m_hyperAsMeta = false;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -6,7 +6,8 @@ QT += \
|
||||
core-private gui-private \
|
||||
service_support-private theme_support-private \
|
||||
fontdatabase_support-private \
|
||||
edid_support-private
|
||||
edid_support-private \
|
||||
xkbcommon_support-private
|
||||
|
||||
qtHaveModule(linuxaccessibility_support-private): \
|
||||
QT += linuxaccessibility_support-private
|
||||
@ -52,7 +53,6 @@ HEADERS = \
|
||||
qxcbimage.h \
|
||||
qxcbxsettings.h \
|
||||
qxcbsystemtraytracker.h \
|
||||
qxcbxkbcommon.h \
|
||||
qxcbeventqueue.h \
|
||||
qxcbeventdispatcher.h \
|
||||
qxcbconnection_basic.h \
|
||||
|
@ -780,7 +780,10 @@ void QDialog::setVisible(bool visible)
|
||||
|
||||
QWidget::setVisible(visible);
|
||||
#if QT_DEPRECATED_SINCE(5, 13)
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_DEPRECATED
|
||||
showExtension(d->doShowExtension);
|
||||
QT_WARNING_POP
|
||||
#endif
|
||||
QWidget *fw = window()->focusWidget();
|
||||
if (!fw)
|
||||
|
@ -587,10 +587,13 @@ void QFileDialogPrivate::retranslateWindowTitle()
|
||||
return;
|
||||
if (q->acceptMode() == QFileDialog::AcceptOpen) {
|
||||
const QFileDialog::FileMode fileMode = q->fileMode();
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_DEPRECATED
|
||||
if (fileMode == QFileDialog::DirectoryOnly || fileMode == QFileDialog::Directory)
|
||||
q->setWindowTitle(QFileDialog::tr("Find Directory"));
|
||||
else
|
||||
q->setWindowTitle(QFileDialog::tr("Open"));
|
||||
QT_WARNING_POP
|
||||
} else
|
||||
q->setWindowTitle(QFileDialog::tr("Save As"));
|
||||
|
||||
@ -614,7 +617,10 @@ void QFileDialogPrivate::updateFileNameLabel()
|
||||
setLabelTextControl(QFileDialog::FileName, options->labelText(QFileDialogOptions::FileName));
|
||||
} else {
|
||||
switch (q_func()->fileMode()) {
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_DEPRECATED
|
||||
case QFileDialog::DirectoryOnly:
|
||||
QT_WARNING_POP
|
||||
case QFileDialog::Directory:
|
||||
setLabelTextControl(QFileDialog::FileName, QFileDialog::tr("Directory:"));
|
||||
break;
|
||||
@ -642,7 +648,10 @@ void QFileDialogPrivate::updateOkButtonText(bool saveAsOnFolder)
|
||||
return;
|
||||
} else {
|
||||
switch (q->fileMode()) {
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_DEPRECATED
|
||||
case QFileDialog::DirectoryOnly:
|
||||
QT_WARNING_POP
|
||||
case QFileDialog::Directory:
|
||||
setLabelTextControl(QFileDialog::Accept, QFileDialog::tr("&Choose"));
|
||||
break;
|
||||
@ -1709,7 +1718,10 @@ void QFileDialog::setFileMode(QFileDialog::FileMode mode)
|
||||
d->options->setFileMode(static_cast<QFileDialogOptions::FileMode>(mode));
|
||||
|
||||
// keep ShowDirsOnly option in sync with fileMode (BTW, DirectoryOnly is obsolete)
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_DEPRECATED
|
||||
setOption(ShowDirsOnly, mode == DirectoryOnly);
|
||||
QT_WARNING_POP
|
||||
|
||||
if (!d->usingWidgets())
|
||||
return;
|
||||
@ -1727,11 +1739,14 @@ void QFileDialog::setFileMode(QFileDialog::FileMode mode)
|
||||
// set filter
|
||||
d->model->setFilter(d->filterForMode(filter()));
|
||||
// setup file type for directory
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_DEPRECATED
|
||||
if (mode == DirectoryOnly || mode == Directory) {
|
||||
d->qFileDialogUi->fileTypeCombo->clear();
|
||||
d->qFileDialogUi->fileTypeCombo->addItem(tr("Directories"));
|
||||
d->qFileDialogUi->fileTypeCombo->setEnabled(false);
|
||||
}
|
||||
QT_WARNING_POP
|
||||
d->updateFileNameLabel();
|
||||
d->updateOkButtonText();
|
||||
d->qFileDialogUi->fileTypeCombo->setEnabled(!testOption(ShowDirsOnly));
|
||||
@ -2634,7 +2649,10 @@ QUrl QFileDialog::getExistingDirectoryUrl(QWidget *parent,
|
||||
args.parent = parent;
|
||||
args.caption = caption;
|
||||
args.directory = QFileDialogPrivate::workingDirectory(dir);
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_DEPRECATED
|
||||
args.mode = (options & ShowDirsOnly ? DirectoryOnly : Directory);
|
||||
QT_WARNING_POP
|
||||
args.options = options;
|
||||
|
||||
QFileDialog dialog(args);
|
||||
@ -2747,7 +2765,10 @@ void QFileDialog::accept()
|
||||
}
|
||||
|
||||
switch (fileMode()) {
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_DEPRECATED
|
||||
case DirectoryOnly:
|
||||
QT_WARNING_POP
|
||||
case Directory: {
|
||||
QString fn = files.first();
|
||||
QFileInfo info(fn);
|
||||
@ -2783,7 +2804,7 @@ void QFileDialog::accept()
|
||||
}
|
||||
|
||||
// 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));
|
||||
QDialog::accept();
|
||||
#if QT_CONFIG(messagebox)
|
||||
@ -3651,7 +3672,10 @@ void QFileDialogPrivate::_q_updateOkButton()
|
||||
isOpenDirectory = true;
|
||||
} else {
|
||||
switch (fileMode) {
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_DEPRECATED
|
||||
case QFileDialog::DirectoryOnly:
|
||||
QT_WARNING_POP
|
||||
case QFileDialog::Directory: {
|
||||
QString fn = files.first();
|
||||
QModelIndex idx = model->index(fn);
|
||||
@ -3743,12 +3767,15 @@ void QFileDialogPrivate::_q_enterDirectory(const QModelIndex &index)
|
||||
const QFileDialog::FileMode fileMode = q->fileMode();
|
||||
q->setDirectory(path);
|
||||
emit q->directoryEntered(path);
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_DEPRECATED
|
||||
if (fileMode == QFileDialog::Directory
|
||||
|| fileMode == QFileDialog::DirectoryOnly) {
|
||||
// ### find out why you have to do both of these.
|
||||
lineEdit()->setText(QString());
|
||||
lineEdit()->clear();
|
||||
}
|
||||
QT_WARNING_POP
|
||||
} else {
|
||||
// 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)
|
||||
@ -3840,7 +3867,10 @@ void QFileDialogPrivate::_q_selectionChanged()
|
||||
{
|
||||
const QFileDialog::FileMode fileMode = q_func()->fileMode();
|
||||
const QModelIndexList indexes = qFileDialogUi->listView->selectionModel()->selectedRows();
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_DEPRECATED
|
||||
bool stripDirs = (fileMode != QFileDialog::DirectoryOnly && fileMode != QFileDialog::Directory);
|
||||
QT_WARNING_POP
|
||||
|
||||
QStringList allFiles;
|
||||
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)
|
||||
{
|
||||
const QFileDialog::FileMode fileMode = q_func()->fileMode();
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_DEPRECATED
|
||||
if (fileMode == QFileDialog::Directory || fileMode == QFileDialog::DirectoryOnly) {
|
||||
if (path == rootPath() && lineEdit()->text() == oldName)
|
||||
lineEdit()->setText(newName);
|
||||
}
|
||||
QT_WARNING_POP
|
||||
}
|
||||
|
||||
void QFileDialogPrivate::_q_emitUrlSelected(const QUrl &file)
|
||||
|
@ -164,13 +164,9 @@ public:
|
||||
|
||||
QDir::Filters filterForMode(QDir::Filters filters) const
|
||||
{
|
||||
const QFileDialog::FileMode fileMode = q_func()->fileMode();
|
||||
if (fileMode == QFileDialog::DirectoryOnly) {
|
||||
filters |= QDir::Drives | QDir::AllDirs | QDir::Dirs;
|
||||
filters |= QDir::Drives | QDir::AllDirs | QDir::Dirs | QDir::Files;
|
||||
if (q_func()->testOption(QFileDialog::ShowDirsOnly))
|
||||
filters &= ~QDir::Files;
|
||||
} else {
|
||||
filters |= QDir::Drives | QDir::AllDirs | QDir::Files | QDir::Dirs;
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
||||
|
@ -2306,7 +2306,7 @@ void QGraphicsWidget::paintWindowFrame(QPainter *painter, const QStyleOptionGrap
|
||||
QStyleHintReturnMask mask;
|
||||
bool setMask = style()->styleHint(QStyle::SH_WindowFrame_Mask, &bar, widget, &mask) && !mask.region.isEmpty();
|
||||
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) {
|
||||
painter->save();
|
||||
painter->setClipRegion(mask.region, Qt::IntersectClip);
|
||||
|
@ -789,7 +789,7 @@ void QItemDelegate::drawCheck(QPainter *painter,
|
||||
|
||||
const QWidget *widget = d->widget(option);
|
||||
QStyle *style = widget ? widget->style() : QApplication::style();
|
||||
style->drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &opt, painter, widget);
|
||||
style->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &opt, painter, widget);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -194,7 +194,7 @@ void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, Q
|
||||
opt->state & (State_Sunken | State_On), 1,
|
||||
&opt->palette.brush(QPalette::Button));
|
||||
break;
|
||||
case PE_IndicatorViewItemCheck:
|
||||
case PE_IndicatorItemViewItemCheck:
|
||||
proxy()->drawPrimitive(PE_IndicatorCheckBox, opt, p, widget);
|
||||
break;
|
||||
case PE_IndicatorCheckBox:
|
||||
@ -2283,7 +2283,7 @@ void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt,
|
||||
option.state |= QStyle::State_On;
|
||||
break;
|
||||
}
|
||||
proxy()->drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &option, p, widget);
|
||||
proxy()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &option, p, widget);
|
||||
}
|
||||
|
||||
// draw the icon
|
||||
|
@ -558,7 +558,7 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem,
|
||||
qt_fusion_draw_arrow(arrow, painter, option, option->rect, arrowColor);
|
||||
}
|
||||
break;
|
||||
case PE_IndicatorViewItemCheck:
|
||||
case PE_IndicatorItemViewItemCheck:
|
||||
{
|
||||
QStyleOptionButton button;
|
||||
button.QStyleOption::operator=(*option);
|
||||
@ -3673,7 +3673,7 @@ int QFusionStyle::styleHint(StyleHint hint, const QStyleOption *option, const QW
|
||||
case SH_FontDialog_SelectAssociatedText:
|
||||
case SH_MenuBar_AltKeyNavigation:
|
||||
case SH_ComboBox_ListMouseTracking:
|
||||
case SH_ScrollBar_StopMouseOverSlider:
|
||||
case SH_Slider_StopMouseOverSlider:
|
||||
case SH_ScrollBar_MiddleClickAbsolutePosition:
|
||||
case SH_EtchDisabledText:
|
||||
case SH_TitleBar_AutoRaise:
|
||||
|
@ -4304,7 +4304,7 @@ void QStyleSheetStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *op
|
||||
|
||||
switch (pe) {
|
||||
|
||||
case PE_FrameStatusBar: {
|
||||
case PE_FrameStatusBarItem: {
|
||||
QRenderRule subRule = renderRule(w ? w->parentWidget() : nullptr, opt, PseudoElement_Item);
|
||||
if (subRule.hasDrawable()) {
|
||||
subRule.drawRule(p, opt->rect);
|
||||
@ -4325,7 +4325,7 @@ void QStyleSheetStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *op
|
||||
pseudoElement = PseudoElement_ExclusiveIndicator;
|
||||
break;
|
||||
|
||||
case PE_IndicatorViewItemCheck:
|
||||
case PE_IndicatorItemViewItemCheck:
|
||||
pseudoElement = PseudoElement_ViewItemIndicator;
|
||||
break;
|
||||
|
||||
|
@ -554,7 +554,7 @@ int QWindowsStyle::styleHint(StyleHint hint, const QStyleOption *opt, const QWid
|
||||
case SH_MenuBar_MouseTracking:
|
||||
case SH_Menu_MouseTracking:
|
||||
case SH_ComboBox_ListMouseTracking:
|
||||
case SH_ScrollBar_StopMouseOverSlider:
|
||||
case SH_Slider_StopMouseOverSlider:
|
||||
case SH_MainWindow_SpaceBelowMenuBar:
|
||||
ret = 1;
|
||||
|
||||
@ -827,13 +827,13 @@ void QWindowsStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt,
|
||||
p->setPen(opt->palette.text().color());
|
||||
}
|
||||
Q_FALLTHROUGH();
|
||||
case PE_IndicatorViewItemCheck:
|
||||
case PE_IndicatorItemViewItemCheck:
|
||||
if (!doRestore) {
|
||||
p->save();
|
||||
doRestore = true;
|
||||
}
|
||||
#if QT_CONFIG(itemviews)
|
||||
if (pe == PE_IndicatorViewItemCheck) {
|
||||
if (pe == PE_IndicatorItemViewItemCheck) {
|
||||
const QStyleOptionViewItem *itemViewOpt = qstyleoption_cast<const QStyleOptionViewItem *>(opt);
|
||||
p->setPen(itemViewOpt
|
||||
&& itemViewOpt->showDecorationSelected
|
||||
|
@ -1389,7 +1389,10 @@ void QComboBoxPrivate::_q_emitCurrentIndexChanged(const QModelIndex &index)
|
||||
const QString text = itemText(index);
|
||||
emit q->currentIndexChanged(index.row());
|
||||
#if QT_DEPRECATED_SINCE(5, 13)
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_DEPRECATED
|
||||
emit q->currentIndexChanged(text);
|
||||
QT_WARNING_POP
|
||||
#endif
|
||||
// signal lineEdit.textChanged already connected to signal currentTextChanged, so don't emit double here
|
||||
if (!lineEdit)
|
||||
|
@ -18,6 +18,7 @@
|
||||
"QtEventDispatcherSupport" => "$basedir/src/platformsupport/eventdispatchers",
|
||||
"QtFontDatabaseSupport" => "$basedir/src/platformsupport/fontdatabases",
|
||||
"QtInputSupport" => "$basedir/src/platformsupport/input",
|
||||
"QtXkbCommonSupport" => "$basedir/src/platformsupport/input/xkbcommon",
|
||||
"QtPlatformCompositorSupport" => "$basedir/src/platformsupport/platformcompositor",
|
||||
"QtServiceSupport" => "$basedir/src/platformsupport/services",
|
||||
"QtThemeSupport" => "$basedir/src/platformsupport/themes",
|
||||
|
@ -67,3 +67,8 @@ winrt|!qtHaveModule(gui)|!qtConfig(accessibility): SUBDIRS -= qaccessibility
|
||||
|
||||
android: SUBDIRS += \
|
||||
android
|
||||
|
||||
qtConfig(xkbcommon): {
|
||||
SUBDIRS += \
|
||||
xkbkeyboard
|
||||
}
|
||||
|
60
tests/auto/other/xkbkeyboard/tst_xkbkeyboard.cpp
Normal file
60
tests/auto/other/xkbkeyboard/tst_xkbkeyboard.cpp
Normal 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"
|
||||
|
7
tests/auto/other/xkbkeyboard/xkbkeyboard.pro
Normal file
7
tests/auto/other/xkbkeyboard/xkbkeyboard.pro
Normal file
@ -0,0 +1,7 @@
|
||||
CONFIG += testcase
|
||||
TARGET = tst_xkbkeyboard
|
||||
|
||||
SOURCES += tst_xkbkeyboard.cpp
|
||||
|
||||
QT = core-private gui-private testlib
|
||||
|
@ -113,7 +113,7 @@ int main(int argc, char **argv) {
|
||||
outIndicesBuffer.write("] = {\n");
|
||||
|
||||
int totalUtf8Size = 0;
|
||||
int chunkSize = 0;
|
||||
int chunkSize = 0; // strlen of the current chunk (sizeof is bigger by 1)
|
||||
int stringUtf8Size = 0;
|
||||
QStringList chunks;
|
||||
for (int a = 0; a < lineCount; a++) {
|
||||
@ -127,7 +127,8 @@ int main(int argc, char **argv) {
|
||||
int quoteCount = strings.at(a).count('"');
|
||||
stringUtf8Size = strings.at(a).count() - (zeroCount + quoteCount + utf8CharsCount * 3);
|
||||
chunkSize += stringUtf8Size;
|
||||
if (chunkSize > 65535) {
|
||||
// MSVC 2015 chokes if sizeof(a single string) > 0xffff
|
||||
if (chunkSize >= 0xffff) {
|
||||
static int chunkCount = 0;
|
||||
qWarning() << "chunk" << ++chunkCount << "has length" << chunkSize - stringUtf8Size;
|
||||
outDataBuffer.write(",\n\n");
|
||||
|
Loading…
Reference in New Issue
Block a user