diff --git a/data/org_kde_kwin.categories b/data/org_kde_kwin.categories index 9667b916f..6c03919d5 100644 --- a/data/org_kde_kwin.categories +++ b/data/org_kde_kwin.categories @@ -1,21 +1,22 @@ kwin_core KWin Core DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_CORE] +kwin_virtualkeyboard KWin Virtual Keyboard Integration DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_VIRTUALKEYBOARD] kwineffects KWin Effects DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWINEFFECTS] libkwineffects KWin Effects Library DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [LIBKWINEFFECTS] libkwinglutils KWin OpenGL utility Library DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [LIBKWINGLUTILS] libkwinxrenderutils KWin XRender utility Library DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [LIBKWINXRENDERUTILS] kwin_wayland_drm KWin Wayland (DRM backend) DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_DRM] kwin_wayland_framebuffer KWin Wayland (Framebuffer backend) DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_FB] kwin_wayland_hwcomposer KWin Wayland (hwcomposer backend) DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_HWCOMPOSER] kwin_wayland_backend KWin Wayland (Wayland backend) DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_WAYLAND_BACKEND] kwin_wayland_x11windowed KWin Wayland (X11 backend) DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_X11WINDOWED] kwin_platform_x11_standalone KWin X11 Standalone Platform DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_X11STANDALONE] kwin_libinput KWin Libinput Integration DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_LIBINPUT] kwin_tabbox KWin Window Switcher DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_TABBOX] kwin_decorations KWin Decorations DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_DECORATIONS] kwin_scripting KWin Scripting DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_SCRIPTING] aurorae KWin Aurorae Window Decoration Engine DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [AURORAE] kwin_xkbcommon KWin xkbcommon integration DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_XKB] kwin_qpa_plugin KWin QtPlatformAbstraction plugin DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_QPA] kwin_scene_xrender KWin XRender based compositor scene plugin DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_XRENDER] kwin_scene_qpainter KWin QPainter based compositor scene plugin DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_QPAINTER] kwin_scene_opengl KWin OpenGL based compositor scene plugins DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_OPENGL] diff --git a/utils.cpp b/utils.cpp index 7314044f2..91f345a7d 100644 --- a/utils.cpp +++ b/utils.cpp @@ -1,200 +1,201 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ /* This file is for (very) small utility functions/classes. */ #include "utils.h" #include #include #ifndef KCMRULES #include #include #include #include #include #include "atoms.h" #include "platform.h" #include "workspace.h" #include #endif Q_LOGGING_CATEGORY(KWIN_CORE, "kwin_core", QtCriticalMsg) +Q_LOGGING_CATEGORY(KWIN_VIRTUALKEYBOARD, "kwin_virtualkeyboard", QtCriticalMsg) namespace KWin { #ifndef KCMRULES //************************************ // StrutRect //************************************ StrutRect::StrutRect(QRect rect, StrutArea area) : QRect(rect) , m_area(area) { } StrutRect::StrutRect(const StrutRect& other) : QRect(other) , m_area(other.area()) { } #endif #ifndef KCMRULES void updateXTime() { kwinApp()->platform()->updateXTime(); } static int server_grab_count = 0; void grabXServer() { if (++server_grab_count == 1) xcb_grab_server(connection()); } void ungrabXServer() { assert(server_grab_count > 0); if (--server_grab_count == 0) { xcb_ungrab_server(connection()); xcb_flush(connection()); } } static bool keyboard_grabbed = false; bool grabXKeyboard(xcb_window_t w) { if (QWidget::keyboardGrabber() != NULL) return false; if (keyboard_grabbed) return false; if (qApp->activePopupWidget() != NULL) return false; if (w == XCB_WINDOW_NONE) w = rootWindow(); const xcb_grab_keyboard_cookie_t c = xcb_grab_keyboard_unchecked(connection(), false, w, xTime(), XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); ScopedCPointer grab(xcb_grab_keyboard_reply(connection(), c, NULL)); if (grab.isNull()) { return false; } if (grab->status != XCB_GRAB_STATUS_SUCCESS) { return false; } keyboard_grabbed = true; return true; } void ungrabXKeyboard() { if (!keyboard_grabbed) { // grabXKeyboard() may fail sometimes, so don't fail, but at least warn anyway qCDebug(KWIN_CORE) << "ungrabXKeyboard() called but keyboard not grabbed!"; } keyboard_grabbed = false; xcb_ungrab_keyboard(connection(), XCB_TIME_CURRENT_TIME); } Process::Process(QObject *parent) : QProcess(parent) { } Process::~Process() = default; void Process::setupChildProcess() { sigset_t userSiganls; sigemptyset(&userSiganls); sigaddset(&userSiganls, SIGUSR1); sigaddset(&userSiganls, SIGUSR2); pthread_sigmask(SIG_UNBLOCK, &userSiganls, nullptr); } #endif // converting between X11 mouse/keyboard state mask and Qt button/keyboard states Qt::MouseButton x11ToQtMouseButton(int button) { if (button == XCB_BUTTON_INDEX_1) return Qt::LeftButton; if (button == XCB_BUTTON_INDEX_2) return Qt::MidButton; if (button == XCB_BUTTON_INDEX_3) return Qt::RightButton; if (button == XCB_BUTTON_INDEX_4) return Qt::XButton1; if (button == XCB_BUTTON_INDEX_5) return Qt::XButton2; return Qt::NoButton; } Qt::MouseButtons x11ToQtMouseButtons(int state) { Qt::MouseButtons ret = 0; if (state & XCB_KEY_BUT_MASK_BUTTON_1) ret |= Qt::LeftButton; if (state & XCB_KEY_BUT_MASK_BUTTON_2) ret |= Qt::MidButton; if (state & XCB_KEY_BUT_MASK_BUTTON_3) ret |= Qt::RightButton; if (state & XCB_KEY_BUT_MASK_BUTTON_4) ret |= Qt::XButton1; if (state & XCB_KEY_BUT_MASK_BUTTON_5) ret |= Qt::XButton2; return ret; } Qt::KeyboardModifiers x11ToQtKeyboardModifiers(int state) { Qt::KeyboardModifiers ret = 0; if (state & XCB_KEY_BUT_MASK_SHIFT) ret |= Qt::ShiftModifier; if (state & XCB_KEY_BUT_MASK_CONTROL) ret |= Qt::ControlModifier; if (state & KKeyServer::modXAlt()) ret |= Qt::AltModifier; if (state & KKeyServer::modXMeta()) ret |= Qt::MetaModifier; return ret; } } // namespace #ifndef KCMRULES #endif diff --git a/utils.h b/utils.h index 7fb253744..5f5efce89 100644 --- a/utils.h +++ b/utils.h @@ -1,242 +1,243 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #ifndef KWIN_UTILS_H #define KWIN_UTILS_H // cmake stuff #include #include // kwin #include // KDE #include // Qt #include #include #include #include #include #include // system #include Q_DECLARE_LOGGING_CATEGORY(KWIN_CORE) +Q_DECLARE_LOGGING_CATEGORY(KWIN_VIRTUALKEYBOARD) namespace KWin { // window types that are supported as normal windows (i.e. KWin actually manages them) const NET::WindowTypes SUPPORTED_MANAGED_WINDOW_TYPES_MASK = NET::NormalMask | NET::DesktopMask | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask /*| NET::OverrideMask*/ | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask | NET::NotificationMask | NET::OnScreenDisplayMask; // window types that are supported as unmanaged (mainly for compositing) const NET::WindowTypes SUPPORTED_UNMANAGED_WINDOW_TYPES_MASK = NET::NormalMask | NET::DesktopMask | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask /*| NET::OverrideMask*/ | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask | NET::DropdownMenuMask | NET::PopupMenuMask | NET::TooltipMask | NET::NotificationMask | NET::ComboBoxMask | NET::DNDIconMask | NET::OnScreenDisplayMask; const QPoint invalidPoint(INT_MIN, INT_MIN); class Toplevel; class Client; class Unmanaged; class Deleted; class Group; class Options; typedef QList< Toplevel* > ToplevelList; typedef QList< Client* > ClientList; typedef QList< const Client* > ConstClientList; typedef QList< Unmanaged* > UnmanagedList; typedef QList< Deleted* > DeletedList; typedef QList< Group* > GroupList; extern Options* options; enum Layer { UnknownLayer = -1, FirstLayer = 0, DesktopLayer = FirstLayer, BelowLayer, NormalLayer, DockLayer, AboveLayer, NotificationLayer, // layer for windows of type notification ActiveLayer, // active fullscreen, or active dialog OnScreenDisplayLayer, // layer for On Screen Display windows such as volume feedback UnmanagedLayer, // layer for override redirect windows. NumLayers // number of layers, must be last }; // yes, I know this is not 100% like standard operator++ inline void operator++(Layer& lay) { lay = static_cast< Layer >(lay + 1); } enum StrutArea { StrutAreaInvalid = 0, // Null StrutAreaTop = 1 << 0, StrutAreaRight = 1 << 1, StrutAreaBottom = 1 << 2, StrutAreaLeft = 1 << 3, StrutAreaAll = StrutAreaTop | StrutAreaRight | StrutAreaBottom | StrutAreaLeft }; Q_DECLARE_FLAGS(StrutAreas, StrutArea) class StrutRect : public QRect { public: explicit StrutRect(QRect rect = QRect(), StrutArea area = StrutAreaInvalid); StrutRect(const StrutRect& other); inline StrutArea area() const { return m_area; }; private: StrutArea m_area; }; typedef QVector StrutRects; enum ShadeMode { ShadeNone, // not shaded ShadeNormal, // normally shaded - isShade() is true only here ShadeHover, // "shaded", but visible due to hover unshade ShadeActivated // "shaded", but visible due to alt+tab to the window }; /** * Maximize mode. These values specify how a window is maximized. */ // these values are written to session files, don't change the order enum MaximizeMode { MaximizeRestore = 0, ///< The window is not maximized in any direction. MaximizeVertical = 1, ///< The window is maximized vertically. MaximizeHorizontal = 2, ///< The window is maximized horizontally. /// Equal to @p MaximizeVertical | @p MaximizeHorizontal MaximizeFull = MaximizeVertical | MaximizeHorizontal }; inline MaximizeMode operator^(MaximizeMode m1, MaximizeMode m2) { return MaximizeMode(int(m1) ^ int(m2)); } enum class QuickTileFlag { None = 0, Left = 1 << 0, Right = 1 << 1, Top = 1 << 2, Bottom = 1 << 3, Horizontal = Left | Right, Vertical = Top | Bottom, Maximize = Left | Right | Top | Bottom, }; Q_DECLARE_FLAGS(QuickTileMode, QuickTileFlag) template using ScopedCPointer = QScopedPointer; void KWIN_EXPORT updateXTime(); void KWIN_EXPORT grabXServer(); void KWIN_EXPORT ungrabXServer(); bool KWIN_EXPORT grabXKeyboard(xcb_window_t w = XCB_WINDOW_NONE); void KWIN_EXPORT ungrabXKeyboard(); /** * Small helper class which performs @link grabXServer in the ctor and * @link ungrabXServer in the dtor. Use this class to ensure that grab and * ungrab are matched. * * To simplify usage consider using the macro GRAB_SERVER_DURING_CONTEXT **/ class XServerGrabber { public: XServerGrabber() { grabXServer(); } ~XServerGrabber() { ungrabXServer(); } }; #define GRAB_SERVER_DURING_CONTEXT XServerGrabber xserverGrabber; // the docs say it's UrgencyHint, but it's often #defined as XUrgencyHint #ifndef UrgencyHint #define UrgencyHint XUrgencyHint #endif // converting between X11 mouse/keyboard state mask and Qt button/keyboard states Qt::MouseButton x11ToQtMouseButton(int button); Qt::MouseButton KWIN_EXPORT x11ToQtMouseButton(int button); Qt::MouseButtons KWIN_EXPORT x11ToQtMouseButtons(int state); Qt::KeyboardModifiers KWIN_EXPORT x11ToQtKeyboardModifiers(int state); /** * Separate the concept of an unet QPoint and 0,0 */ class ClearablePoint { public: inline bool isValid() const { return m_valid; } inline void clear(){ m_valid = false; } inline void setPoint(const QPoint &point) { m_point = point; m_valid = true; } inline QPoint point() const { return m_point; } private: QPoint m_point; bool m_valid = false; }; /** * QProcess subclass which unblocks SIGUSR in the child process. **/ class KWIN_EXPORT Process : public QProcess { Q_OBJECT public: explicit Process(QObject *parent = nullptr); virtual ~Process(); protected: void setupChildProcess() override; }; } // namespace // Must be outside namespace Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::StrutAreas) Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::QuickTileMode) #endif diff --git a/virtualkeyboard.cpp b/virtualkeyboard.cpp index f0c9cf771..018da529f 100644 --- a/virtualkeyboard.cpp +++ b/virtualkeyboard.cpp @@ -1,443 +1,449 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "virtualkeyboard.h" #include "virtualkeyboard_dbus.h" #include "input.h" #include "keyboard_input.h" #include "utils.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include "xkb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // xkbcommon #include using namespace KWayland::Server; namespace KWin { KWIN_SINGLETON_FACTORY(VirtualKeyboard) VirtualKeyboard::VirtualKeyboard(QObject *parent) : QObject(parent) { // this is actually too late. Other processes are started before init, // so might miss the availability of text input // but without Workspace we don't have the window listed at all connect(kwinApp(), &Application::workspaceCreated, this, &VirtualKeyboard::init); } VirtualKeyboard::~VirtualKeyboard() = default; void VirtualKeyboard::init() { // TODO: need a shared Qml engine + qCDebug(KWIN_VIRTUALKEYBOARD) << "Initializing window"; m_inputWindow.reset(new QQuickView(nullptr)); m_inputWindow->setFlags(Qt::FramelessWindowHint); m_inputWindow->setGeometry(screens()->geometry(screens()->current())); m_inputWindow->setResizeMode(QQuickView::SizeRootObjectToView); m_inputWindow->setSource(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral(KWIN_NAME "/virtualkeyboard/main.qml")))); if (m_inputWindow->status() != QQuickView::Status::Ready) { + qCWarning(KWIN_VIRTUALKEYBOARD) << "window not ready yet"; m_inputWindow.reset(); return; } m_inputWindow->setProperty("__kwin_input_method", true); if (waylandServer()) { m_enabled = !input()->hasAlphaNumericKeyboard(); + qCDebug(KWIN_VIRTUALKEYBOARD) << "enabled by default: " << m_enabled; connect(input(), &InputRedirection::hasAlphaNumericKeyboardChanged, this, [this] (bool set) { + qCDebug(KWIN_VIRTUALKEYBOARD) << "AlphaNumeric Keyboard changed:" << set << "toggling VirtualKeyboard."; setEnabled(!set); } ); } + qCDebug(KWIN_VIRTUALKEYBOARD) << "Registering the SNI"; m_sni = new KStatusNotifierItem(QStringLiteral("kwin-virtual-keyboard"), this); m_sni->setStandardActionsEnabled(false); m_sni->setCategory(KStatusNotifierItem::Hardware); m_sni->setStatus(KStatusNotifierItem::Passive); m_sni->setTitle(i18n("Virtual Keyboard")); updateSni(); connect(m_sni, &KStatusNotifierItem::activateRequested, this, [this] { setEnabled(!m_enabled); } ); connect(this, &VirtualKeyboard::enabledChanged, this, &VirtualKeyboard::updateSni); auto dbus = new VirtualKeyboardDBus(this); + qCDebug(KWIN_VIRTUALKEYBOARD) << "Registering the DBus interface"; dbus->setEnabled(m_enabled); connect(dbus, &VirtualKeyboardDBus::activateRequested, this, &VirtualKeyboard::setEnabled); connect(this, &VirtualKeyboard::enabledChanged, dbus, &VirtualKeyboardDBus::setEnabled); if (waylandServer()) { // we can announce support for the text input interface auto t = waylandServer()->display()->createTextInputManager(TextInputInterfaceVersion::UnstableV0, waylandServer()->display()); t->create(); auto t2 = waylandServer()->display()->createTextInputManager(TextInputInterfaceVersion::UnstableV2, waylandServer()->display()); t2->create(); connect(waylandServer()->seat(), &SeatInterface::focusedTextInputChanged, this, [this] { disconnect(m_waylandShowConnection); disconnect(m_waylandHideConnection); disconnect(m_waylandHintsConnection); disconnect(m_waylandSurroundingTextConnection); disconnect(m_waylandResetConnection); disconnect(m_waylandEnabledConnection); qApp->inputMethod()->reset(); if (auto t = waylandServer()->seat()->focusedTextInput()) { m_waylandShowConnection = connect(t, &TextInputInterface::requestShowInputPanel, this, &VirtualKeyboard::show); m_waylandHideConnection = connect(t, &TextInputInterface::requestHideInputPanel, this, &VirtualKeyboard::hide); m_waylandSurroundingTextConnection = connect(t, &TextInputInterface::surroundingTextChanged, this, [] { qApp->inputMethod()->update(Qt::ImSurroundingText | Qt::ImCursorPosition | Qt::ImAnchorPosition); } ); m_waylandHintsConnection = connect(t, &TextInputInterface::contentTypeChanged, this, [] { qApp->inputMethod()->update(Qt::ImHints); } ); m_waylandResetConnection = connect(t, &TextInputInterface::requestReset, qApp->inputMethod(), &QInputMethod::reset); m_waylandEnabledConnection = connect(t, &TextInputInterface::enabledChanged, this, [] { qApp->inputMethod()->update(Qt::ImQueryAll); } ); // TODO: calculate overlap t->setInputPanelState(m_inputWindow->isVisible(), QRect(0, 0, 0, 0)); } else { m_waylandShowConnection = QMetaObject::Connection(); m_waylandHideConnection = QMetaObject::Connection(); m_waylandHintsConnection = QMetaObject::Connection(); m_waylandSurroundingTextConnection = QMetaObject::Connection(); m_waylandResetConnection = QMetaObject::Connection(); m_waylandEnabledConnection = QMetaObject::Connection(); } qApp->inputMethod()->update(Qt::ImQueryAll); } ); } m_inputWindow->installEventFilter(this); connect(Workspace::self(), &Workspace::destroyed, this, [this] { m_inputWindow.reset(); } ); m_inputWindow->setColor(Qt::transparent); m_inputWindow->setMask(m_inputWindow->rootObject()->childrenRect().toRect()); connect(m_inputWindow->rootObject(), &QQuickItem::childrenRectChanged, m_inputWindow.data(), [this] { if (!m_inputWindow) { return; } m_inputWindow->setMask(m_inputWindow->rootObject()->childrenRect().toRect()); } ); connect(qApp->inputMethod(), &QInputMethod::visibleChanged, m_inputWindow.data(), [this] { m_inputWindow->setVisible(qApp->inputMethod()->isVisible()); if (qApp->inputMethod()->isVisible()) { m_inputWindow->setMask(m_inputWindow->rootObject()->childrenRect().toRect()); } if (waylandServer()) { if (auto t = waylandServer()->seat()->focusedTextInput()) { // TODO: calculate overlap t->setInputPanelState(m_inputWindow->isVisible(), QRect(0, 0, 0, 0)); } } } ); } void VirtualKeyboard::setEnabled(bool enabled) { if (m_enabled == enabled) { return; } m_enabled = enabled; qApp->inputMethod()->update(Qt::ImQueryAll); emit enabledChanged(m_enabled); // send OSD message QDBusMessage msg = QDBusMessage::createMethodCall( QStringLiteral("org.kde.plasmashell"), QStringLiteral("/org/kde/osdService"), QStringLiteral("org.kde.osdService"), QStringLiteral("virtualKeyboardEnabledChanged") ); msg.setArguments({enabled}); QDBusConnection::sessionBus().asyncCall(msg); } void VirtualKeyboard::updateSni() { if (!m_sni) { return; } if (m_enabled) { m_sni->setIconByName(QStringLiteral("input-keyboard-virtual-on")); m_sni->setToolTipTitle(i18n("Virtual Keyboard is enabled.")); } else { m_sni->setIconByName(QStringLiteral("input-keyboard-virtual-off")); m_sni->setToolTipTitle(i18n("Virtual Keyboard is disabled.")); } } void VirtualKeyboard::show() { if (m_inputWindow.isNull() || !m_enabled) { return; } m_inputWindow->setGeometry(screens()->geometry(screens()->current())); qApp->inputMethod()->show(); } void VirtualKeyboard::hide() { if (m_inputWindow.isNull()) { return; } qApp->inputMethod()->hide(); } bool VirtualKeyboard::event(QEvent *e) { if (e->type() == QEvent::InputMethod) { QInputMethodEvent *event = static_cast(e); if (m_enabled && waylandServer()) { bool isPreedit = false; for (auto attribute : event->attributes()) { switch (attribute.type) { case QInputMethodEvent::TextFormat: case QInputMethodEvent::Cursor: case QInputMethodEvent::Language: case QInputMethodEvent::Ruby: isPreedit = true; break; default: break; } } TextInputInterface *ti = waylandServer()->seat()->focusedTextInput(); if (ti && ti->isEnabled()) { if (!isPreedit && event->preeditString().isEmpty() && !event->commitString().isEmpty()) { ti->commit(event->commitString().toUtf8()); } else { ti->preEdit(event->preeditString().toUtf8(), event->commitString().toUtf8()); } } } } if (e->type() == QEvent::InputMethodQuery) { auto event = static_cast(e); TextInputInterface *ti = nullptr; if (waylandServer() && m_enabled) { ti = waylandServer()->seat()->focusedTextInput(); } if (event->queries().testFlag(Qt::ImEnabled)) { event->setValue(Qt::ImEnabled, QVariant(ti != nullptr && ti->isEnabled())); } if (event->queries().testFlag(Qt::ImCursorRectangle)) { // not used by virtual keyboard } if (event->queries().testFlag(Qt::ImFont)) { // not used by virtual keyboard } if (event->queries().testFlag(Qt::ImCursorPosition)) { // the virtual keyboard doesn't send us the cursor position in the preedit // this would break text input, thus we ignore it // see https://bugreports.qt.io/browse/QTBUG-53517 #if 0 event->setValue(Qt::ImCursorPosition, QString::fromUtf8(ti->surroundingText().left(ti->surroundingTextCursorPosition())).size()); #else event->setValue(Qt::ImCursorPosition, 0); #endif } if (event->queries().testFlag(Qt::ImSurroundingText)) { // the virtual keyboard doesn't send us the cursor position in the preedit // this would break text input, thus we ignore it // see https://bugreports.qt.io/browse/QTBUG-53517 #if 0 event->setValue(Qt::ImSurroundingText, QString::fromUtf8(ti->surroundingText())); #else event->setValue(Qt::ImSurroundingText, QString()); #endif } if (event->queries().testFlag(Qt::ImCurrentSelection)) { // TODO: should be text between cursor and anchor, but might be dangerous } if (event->queries().testFlag(Qt::ImMaximumTextLength)) { // not used by virtual keyboard } if (event->queries().testFlag(Qt::ImAnchorPosition)) { // not used by virtual keyboard } if (event->queries().testFlag(Qt::ImHints)) { if (ti && ti->isEnabled()) { Qt::InputMethodHints hints; const auto contentHints = ti->contentHints(); if (!contentHints.testFlag(TextInputInterface::ContentHint::AutoCompletion)) { hints |= Qt::ImhNoPredictiveText; } if (contentHints.testFlag(TextInputInterface::ContentHint::AutoCorrection)) { // no mapping in Qt } if (!contentHints.testFlag(TextInputInterface::ContentHint::AutoCapitalization)) { hints |= Qt::ImhNoAutoUppercase; } if (contentHints.testFlag(TextInputInterface::ContentHint::LowerCase)) { hints |= Qt::ImhPreferLowercase; } if (contentHints.testFlag(TextInputInterface::ContentHint::UpperCase)) { hints |= Qt::ImhPreferUppercase; } if (contentHints.testFlag(TextInputInterface::ContentHint::TitleCase)) { // no mapping in Qt } if (contentHints.testFlag(TextInputInterface::ContentHint::HiddenText)) { hints |= Qt::ImhHiddenText; } if (contentHints.testFlag(TextInputInterface::ContentHint::SensitiveData)) { hints |= Qt::ImhSensitiveData; } if (contentHints.testFlag(TextInputInterface::ContentHint::Latin)) { hints |= Qt::ImhPreferLatin; } if (contentHints.testFlag(TextInputInterface::ContentHint::MultiLine)) { hints |= Qt::ImhMultiLine; } switch (ti->contentPurpose()) { case TextInputInterface::ContentPurpose::Digits: hints |= Qt::ImhDigitsOnly; break; case TextInputInterface::ContentPurpose::Number: hints |= Qt::ImhFormattedNumbersOnly; break; case TextInputInterface::ContentPurpose::Phone: hints |= Qt::ImhDialableCharactersOnly; break; case TextInputInterface::ContentPurpose::Url: hints |= Qt::ImhUrlCharactersOnly; break; case TextInputInterface::ContentPurpose::Email: hints |= Qt::ImhEmailCharactersOnly; break; case TextInputInterface::ContentPurpose::Date: hints |= Qt::ImhDate; break; case TextInputInterface::ContentPurpose::Time: hints |= Qt::ImhTime; break; case TextInputInterface::ContentPurpose::DateTime: hints |= Qt::ImhDate; hints |= Qt::ImhTime; break; case TextInputInterface::ContentPurpose::Name: // no mapping in Qt case TextInputInterface::ContentPurpose::Password: // no mapping in Qt case TextInputInterface::ContentPurpose::Terminal: // no mapping in Qt case TextInputInterface::ContentPurpose::Normal: // that's the default case TextInputInterface::ContentPurpose::Alpha: // no mapping in Qt break; } event->setValue(Qt::ImHints, QVariant(int(hints))); } else { event->setValue(Qt::ImHints, Qt::ImhNone); } } if (event->queries().testFlag(Qt::ImPreferredLanguage)) { // not used by virtual keyboard } if (event->queries().testFlag(Qt::ImPlatformData)) { // not used by virtual keyboard } if (event->queries().testFlag(Qt::ImAbsolutePosition)) { // not used by virtual keyboard } if (event->queries().testFlag(Qt::ImTextBeforeCursor)) { // not used by virtual keyboard } if (event->queries().testFlag(Qt::ImTextAfterCursor)) { // not used by virtual keyboard } event->accept(); return true; } return QObject::event(e); } bool VirtualKeyboard::eventFilter(QObject *o, QEvent *e) { if (o != m_inputWindow.data() || !m_inputWindow->isVisible()) { return false; } if (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease) { QKeyEvent *event = static_cast(e); if (event->nativeScanCode() == 0) { // this is a key composed by the virtual keyboard - we need to send it to the client const auto sym = input()->keyboard()->xkb()->fromKeyEvent(event); if (sym != 0) { if (waylandServer()) { auto t = waylandServer()->seat()->focusedTextInput(); if (t && t->isEnabled()) { if (e->type() == QEvent::KeyPress) { t->keysymPressed(sym); } else if (e->type() == QEvent::KeyRelease) { t->keysymReleased(sym); } } } } return true; } } return false; } QWindow *VirtualKeyboard::inputPanel() const { return m_inputWindow.data(); } }