diff options
Diffstat (limited to 'src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp')
-rw-r--r-- | src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp | 230 |
1 files changed, 204 insertions, 26 deletions
diff --git a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp index 673942b5d9..dca21245aa 100644 --- a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp +++ b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp @@ -39,6 +39,10 @@ #include <qwindow.h> #include <qevent.h> +#include <qpa/qplatformcursor.h> +#include <qpa/qplatformscreen.h> +#include <qpa/qwindowsysteminterface.h> + #include "qibusproxy.h" #include "qibusinputcontextproxy.h" #include "qibustypes.h" @@ -48,8 +52,14 @@ #include <QtDBus> +#ifndef IBUS_RELEASE_MASK +#define IBUS_RELEASE_MASK (1 << 30) +#endif + QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(qtQpaInputMethods, "qt.qpa.input.methods") + enum { debug = 0 }; class QIBusPlatformInputContextPrivate @@ -63,13 +73,18 @@ public: delete connection; } + static QString getSocketPath(); static QDBusConnection *createConnection(); + void initBus(); + void createBusProxy(); + QDBusConnection *connection; QIBusProxy *bus; QIBusInputContextProxy *context; bool valid; + bool busConnected; QString predit; bool needsSurroundingText; }; @@ -78,14 +93,30 @@ public: QIBusPlatformInputContext::QIBusPlatformInputContext () : d(new QIBusPlatformInputContextPrivate()) { - if (d->context) { - connect(d->context, SIGNAL(CommitText(QDBusVariant)), SLOT(commitText(QDBusVariant))); - connect(d->context, SIGNAL(UpdatePreeditText(QDBusVariant,uint,bool)), this, SLOT(updatePreeditText(QDBusVariant,uint,bool))); - connect(d->context, SIGNAL(DeleteSurroundingText(int,uint)), this, SLOT(deleteSurroundingText(int,uint))); - connect(d->context, SIGNAL(RequireSurroundingText()), this, SLOT(surroundingTextRequired())); + QString socketPath = QIBusPlatformInputContextPrivate::getSocketPath(); + QFile file(socketPath); + if (file.open(QFile::ReadOnly)) { + // If KDE session save is used or restart ibus-daemon, + // the applications could run before ibus-daemon runs. + // We watch the getSocketPath() to get the launching ibus-daemon. + m_socketWatcher.addPath(socketPath); + connect(&m_socketWatcher, SIGNAL(fileChanged(QString)), this, SLOT(socketChanged(QString))); } + + m_timer.setSingleShot(true); + connect(&m_timer, SIGNAL(timeout()), this, SLOT(connectToBus())); + + connectToContextSignals(); + QInputMethod *p = qApp->inputMethod(); connect(p, SIGNAL(cursorRectangleChanged()), this, SLOT(cursorRectChanged())); + m_eventFilterUseSynchronousMode = false; + if (qEnvironmentVariableIsSet("IBUS_ENABLE_SYNC_MODE")) { + bool ok; + int enableSync = qgetenv("IBUS_ENABLE_SYNC_MODE").toInt(&ok); + if (ok && enableSync == 1) + m_eventFilterUseSynchronousMode = true; + } } QIBusPlatformInputContext::~QIBusPlatformInputContext (void) @@ -100,7 +131,7 @@ bool QIBusPlatformInputContext::isValid() const void QIBusPlatformInputContext::invokeAction(QInputMethod::Action a, int) { - if (!d->valid) + if (!d->busConnected) return; if (a == QInputMethod::Click) @@ -111,7 +142,7 @@ void QIBusPlatformInputContext::reset() { QPlatformInputContext::reset(); - if (!d->valid) + if (!d->busConnected) return; d->context->Reset(); @@ -122,7 +153,7 @@ void QIBusPlatformInputContext::commit() { QPlatformInputContext::commit(); - if (!d->valid) + if (!d->busConnected) return; QObject *input = qApp->focusObject(); @@ -176,7 +207,7 @@ void QIBusPlatformInputContext::update(Qt::InputMethodQueries q) void QIBusPlatformInputContext::cursorRectChanged() { - if (!d->valid) + if (!d->busConnected) return; QRect r = qApp->inputMethod()->cursorRectangle().toRect(); @@ -194,7 +225,7 @@ void QIBusPlatformInputContext::cursorRectChanged() void QIBusPlatformInputContext::setFocusObject(QObject *object) { - if (!d->valid) + if (!d->busConnected) return; if (debug) @@ -272,33 +303,175 @@ void QIBusPlatformInputContext::deleteSurroundingText(int offset, uint n_chars) QCoreApplication::sendEvent(input, &event); } -bool -QIBusPlatformInputContext::x11FilterEvent(uint keyval, uint keycode, uint state, bool press) +bool QIBusPlatformInputContext::filterEvent(const QEvent *event) { - if (!d->valid) + if (!d->busConnected) return false; if (!inputMethodAccepted()) return false; - if (!press) - return false; + const QKeyEvent *keyEvent = static_cast<const QKeyEvent *>(event); + quint32 sym = keyEvent->nativeVirtualKey(); + quint32 code = keyEvent->nativeScanCode(); + quint32 state = keyEvent->nativeModifiers(); + quint32 ibusState = state; + + if (keyEvent->type() != QEvent::KeyPress) + ibusState |= IBUS_RELEASE_MASK; + + QDBusPendingReply<bool> reply = d->context->ProcessKeyEvent(sym, code - 8, ibusState); + + if (m_eventFilterUseSynchronousMode || reply.isFinished()) { + bool retval = reply.value(); + qCDebug(qtQpaInputMethods) << "filterEvent return" << code << sym << state << retval; + return retval; + } + + Qt::KeyboardModifiers modifiers = keyEvent->modifiers(); + const int qtcode = keyEvent->key(); + + // From QKeyEvent::modifiers() + switch (qtcode) { + case Qt::Key_Shift: + modifiers ^= Qt::ShiftModifier; + break; + case Qt::Key_Control: + modifiers ^= Qt::ControlModifier; + break; + case Qt::Key_Alt: + modifiers ^= Qt::AltModifier; + break; + case Qt::Key_Meta: + modifiers ^= Qt::MetaModifier; + break; + case Qt::Key_AltGr: + modifiers ^= Qt::GroupSwitchModifier; + break; + } + + QVariantList args; + args << QVariant::fromValue(keyEvent->timestamp()); + args << QVariant::fromValue(static_cast<uint>(keyEvent->type())); + args << QVariant::fromValue(qtcode); + args << QVariant::fromValue(code) << QVariant::fromValue(sym) << QVariant::fromValue(state); + args << QVariant::fromValue(keyEvent->text()); + args << QVariant::fromValue(keyEvent->isAutoRepeat()); + + QIBusFilterEventWatcher *watcher = new QIBusFilterEventWatcher(reply, this, QGuiApplication::focusWindow(), modifiers, args); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &QIBusPlatformInputContext::filterEventFinished); + + return true; +} + +void QIBusPlatformInputContext::filterEventFinished(QDBusPendingCallWatcher *call) +{ + QIBusFilterEventWatcher *watcher = (QIBusFilterEventWatcher *) call; + QDBusPendingReply<bool> reply = *call; + + if (reply.isError()) { + call->deleteLater(); + return; + } + + // Use watcher's window instead of the current focused window + // since there is a time lag until filterEventFinished() returns. + QWindow *window = watcher->window(); + + if (!window) { + call->deleteLater(); + return; + } + + Qt::KeyboardModifiers modifiers = watcher->modifiers(); + QVariantList args = watcher->arguments(); + const ulong time = static_cast<const ulong>(args.at(0).toUInt()); + const QEvent::Type type = static_cast<const QEvent::Type>(args.at(1).toUInt()); + const int qtcode = args.at(2).toInt(); + const quint32 code = args.at(3).toUInt(); + const quint32 sym = args.at(4).toUInt(); + const quint32 state = args.at(5).toUInt(); + const QString string = args.at(6).toString(); + const bool isAutoRepeat = args.at(7).toBool(); + + // copied from QXcbKeyboard::handleKeyEvent() + bool retval = reply.value(); + qCDebug(qtQpaInputMethods) << "filterEventFinished return" << code << sym << state << retval; + if (!retval) { + if (type == QEvent::KeyPress && qtcode == Qt::Key_Menu + && window != NULL) { + const QPoint globalPos = window->screen()->handle()->cursor()->pos(); + const QPoint pos = window->mapFromGlobal(globalPos); + QWindowSystemInterface::handleContextMenuEvent(window, false, pos, + globalPos, modifiers); + } + QWindowSystemInterface::handleExtendedKeyEvent(window, time, type, qtcode, modifiers, + code, sym, state, string, isAutoRepeat); + + } + call->deleteLater(); +} + +void QIBusPlatformInputContext::socketChanged(const QString &str) +{ + qCDebug(qtQpaInputMethods) << "socketChanged"; + Q_UNUSED (str); + + m_timer.stop(); - keycode -= 8; // ### - QDBusReply<bool> reply = d->context->ProcessKeyEvent(keyval, keycode, state); + if (d->context) + disconnect(d->context); + if (d->connection) + d->connection->disconnectFromBus(QLatin1String("QIBusProxy")); -// qDebug() << "x11FilterEvent return" << reply.value(); + m_timer.start(100); +} + +// When getSocketPath() is modified, the bus is not established yet +// so use m_timer. +void QIBusPlatformInputContext::connectToBus() +{ + qCDebug(qtQpaInputMethods) << "QIBusPlatformInputContext::connectToBus"; + d->initBus(); + connectToContextSignals(); + + if (m_socketWatcher.files().size() == 0) + m_socketWatcher.addPath(QIBusPlatformInputContextPrivate::getSocketPath()); +} - return reply.value(); +void QIBusPlatformInputContext::connectToContextSignals() +{ + if (d->context) { + connect(d->context, SIGNAL(CommitText(QDBusVariant)), SLOT(commitText(QDBusVariant))); + connect(d->context, SIGNAL(UpdatePreeditText(QDBusVariant,uint,bool)), this, SLOT(updatePreeditText(QDBusVariant,uint,bool))); + connect(d->context, SIGNAL(DeleteSurroundingText(int,uint)), this, SLOT(deleteSurroundingText(int,uint))); + connect(d->context, SIGNAL(RequireSurroundingText()), this, SLOT(surroundingTextRequired())); + } } QIBusPlatformInputContextPrivate::QIBusPlatformInputContextPrivate() - : connection(createConnection()), + : connection(0), bus(0), context(0), valid(false), + busConnected(false), needsSurroundingText(false) { + valid = !QStandardPaths::findExecutable(QString::fromLocal8Bit("ibus-daemon"), QStringList()).isEmpty(); + if (!valid) + return; + initBus(); +} + +void QIBusPlatformInputContextPrivate::initBus() +{ + connection = createConnection(); + busConnected = false; + createBusProxy(); +} + +void QIBusPlatformInputContextPrivate::createBusProxy() +{ if (!connection || !connection->isConnected()) return; @@ -334,13 +507,13 @@ QIBusPlatformInputContextPrivate::QIBusPlatformInputContextPrivate() context->SetCapabilities(IBUS_CAP_PREEDIT_TEXT|IBUS_CAP_FOCUS|IBUS_CAP_SURROUNDING_TEXT); if (debug) - qDebug(">>>> valid!"); - valid = true; + qDebug(">>>> bus connected!"); + busConnected = true; } -QDBusConnection *QIBusPlatformInputContextPrivate::createConnection() +QString QIBusPlatformInputContextPrivate::getSocketPath() { - QByteArray display(getenv("DISPLAY")); + QByteArray display(qgetenv("DISPLAY")); QByteArray host = "unix"; QByteArray displayNumber = "0"; @@ -356,9 +529,14 @@ QDBusConnection *QIBusPlatformInputContextPrivate::createConnection() if (debug) qDebug() << "host=" << host << "displayNumber" << displayNumber; - QFile file(QDir::homePath() + QLatin1String("/.config/ibus/bus/") + + return QDir::homePath() + QLatin1String("/.config/ibus/bus/") + QLatin1String(QDBusConnection::localMachineId()) + - QLatin1Char('-') + QString::fromLocal8Bit(host) + QLatin1Char('-') + QString::fromLocal8Bit(displayNumber)); + QLatin1Char('-') + QString::fromLocal8Bit(host) + QLatin1Char('-') + QString::fromLocal8Bit(displayNumber); +} + +QDBusConnection *QIBusPlatformInputContextPrivate::createConnection() +{ + QFile file(getSocketPath()); if (!file.open(QFile::ReadOnly)) return 0; |