Use ibus ProcessKeyEvent asynchronously.

ibus-hangul sends "CommitText" dbus asynchronous API during
"ProcessKeyEvent" dbus API is called. If "ProcessKeyEvent" is
synchronous, "ProcessKeyEvent" finishes before "CommitText"
finishes and the order of Hangul characters and space is not correct.

Task-number: QTBUG-40541
Change-Id: Ia526bc11833853082205ef1c3d64cf7943e2274f
Reviewed-by: Lars Knoll <lars.knoll@theqtcompany.com>
This commit is contained in:
Takao Fujiwara 2015-07-01 14:24:43 +09:00 committed by Lars Knoll
parent 424e6386c6
commit a1bfa39651
2 changed files with 133 additions and 10 deletions

View File

@ -39,6 +39,10 @@
#include <qwindow.h> #include <qwindow.h>
#include <qevent.h> #include <qevent.h>
#include <qpa/qplatformcursor.h>
#include <qpa/qplatformscreen.h>
#include <qpa/qwindowsysteminterface.h>
#include "qibusproxy.h" #include "qibusproxy.h"
#include "qibusinputcontextproxy.h" #include "qibusinputcontextproxy.h"
#include "qibustypes.h" #include "qibustypes.h"
@ -48,8 +52,14 @@
#include <QtDBus> #include <QtDBus>
#ifndef IBUS_RELEASE_MASK
#define IBUS_RELEASE_MASK (1 << 30)
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(qtQpaInputMethods, "qt.qpa.input.methods")
enum { debug = 0 }; enum { debug = 0 };
class QIBusPlatformInputContextPrivate class QIBusPlatformInputContextPrivate
@ -86,6 +96,13 @@ QIBusPlatformInputContext::QIBusPlatformInputContext ()
} }
QInputMethod *p = qApp->inputMethod(); QInputMethod *p = qApp->inputMethod();
connect(p, SIGNAL(cursorRectangleChanged()), this, SLOT(cursorRectChanged())); 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) QIBusPlatformInputContext::~QIBusPlatformInputContext (void)
@ -272,8 +289,7 @@ void QIBusPlatformInputContext::deleteSurroundingText(int offset, uint n_chars)
QCoreApplication::sendEvent(input, &event); QCoreApplication::sendEvent(input, &event);
} }
bool bool QIBusPlatformInputContext::filterEvent(const QEvent *event)
QIBusPlatformInputContext::x11FilterEvent(uint keyval, uint keycode, uint state, bool press)
{ {
if (!d->valid) if (!d->valid)
return false; return false;
@ -281,15 +297,89 @@ QIBusPlatformInputContext::x11FilterEvent(uint keyval, uint keycode, uint state,
if (!inputMethodAccepted()) if (!inputMethodAccepted())
return false; return false;
if (!press) const QKeyEvent *keyEvent = static_cast<const QKeyEvent *>(event);
return false; quint32 sym = keyEvent->nativeVirtualKey();
quint32 code = keyEvent->nativeScanCode();
quint32 state = keyEvent->nativeModifiers();
keycode -= 8; // ### if (keyEvent->type() != QEvent::KeyPress)
QDBusReply<bool> reply = d->context->ProcessKeyEvent(keyval, keycode, state); state |= IBUS_RELEASE_MASK;
// qDebug() << "x11FilterEvent return" << reply.value(); code -= 8; // ###
QDBusPendingReply<bool> reply = d->context->ProcessKeyEvent(sym, code, state);
return reply.value(); if (m_eventFilterUseSynchronousMode || reply.isFinished()) {
bool retval = reply.value();
qCDebug(qtQpaInputMethods) << "filterEvent return" << code << sym << state << retval;
return retval;
}
Qt::KeyboardModifiers modifiers = keyEvent->modifiers();
QVariantList args;
args << QVariant::fromValue(keyEvent->timestamp());
args << QVariant::fromValue(static_cast<uint>(keyEvent->type()));
args << QVariant::fromValue(keyEvent->key());
args << QVariant::fromValue(code) << QVariant::fromValue(sym) << QVariant::fromValue(state);
args << QVariant::fromValue(keyEvent->text());
args << QVariant::fromValue(keyEvent->isAutoRepeat());
args << QVariant::fromValue(keyEvent->count());
QIBusFilterEventWatcher *watcher = new QIBusFilterEventWatcher(reply, this, qApp->focusObject(), 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.
QObject *input = watcher->input();
if (!input) {
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();
const int count = args.at(8).toInt();
// copied from QXcbKeyboard::handleKeyEvent()
bool retval = reply.value();
qCDebug(qtQpaInputMethods) << "filterEventFinished return" << code << sym << state << retval;
if (!retval) {
QWindow *window = dynamic_cast<QWindow *>(input);
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);
}
QKeyEvent event(type, qtcode, modifiers, code, sym,
state, string, isAutoRepeat, count);
event.setTimestamp(time);
QCoreApplication::sendEvent(input, &event);
}
call->deleteLater();
} }
QIBusPlatformInputContextPrivate::QIBusPlatformInputContextPrivate() QIBusPlatformInputContextPrivate::QIBusPlatformInputContextPrivate()

View File

@ -35,11 +35,43 @@
#include <qpa/qplatforminputcontext.h> #include <qpa/qplatforminputcontext.h>
#include <QtCore/qpointer.h>
#include <QtDBus/qdbuspendingreply.h>
#include <QLoggingCategory>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(qtQpaInputMethods)
class QIBusPlatformInputContextPrivate; class QIBusPlatformInputContextPrivate;
class QDBusVariant; class QDBusVariant;
class QIBusFilterEventWatcher: public QDBusPendingCallWatcher
{
public:
explicit QIBusFilterEventWatcher(const QDBusPendingCall &call,
QObject *parent = 0,
QObject *input = 0,
const Qt::KeyboardModifiers modifiers = 0,
const QVariantList arguments = QVariantList())
: QDBusPendingCallWatcher(call, parent)
, m_input(input)
, m_modifiers(modifiers)
, m_arguments(arguments)
{}
~QIBusFilterEventWatcher()
{}
inline QObject *input() const { return m_input; }
inline const Qt::KeyboardModifiers modifiers() const { return m_modifiers; }
inline const QVariantList arguments() const { return m_arguments; }
private:
QPointer<QObject> m_input;
const Qt::KeyboardModifiers m_modifiers;
const QVariantList m_arguments;
};
class QIBusPlatformInputContext : public QPlatformInputContext class QIBusPlatformInputContext : public QPlatformInputContext
{ {
Q_OBJECT Q_OBJECT
@ -54,8 +86,7 @@ public:
void reset() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE;
void commit() Q_DECL_OVERRIDE; void commit() Q_DECL_OVERRIDE;
void update(Qt::InputMethodQueries) Q_DECL_OVERRIDE; void update(Qt::InputMethodQueries) Q_DECL_OVERRIDE;
bool filterEvent(const QEvent *event) Q_DECL_OVERRIDE;
Q_INVOKABLE bool x11FilterEvent(uint keyval, uint keycode, uint state, bool press);
public Q_SLOTS: public Q_SLOTS:
void commitText(const QDBusVariant &text); void commitText(const QDBusVariant &text);
@ -63,9 +94,11 @@ public Q_SLOTS:
void cursorRectChanged(); void cursorRectChanged();
void deleteSurroundingText(int offset, uint n_chars); void deleteSurroundingText(int offset, uint n_chars);
void surroundingTextRequired(); void surroundingTextRequired();
void filterEventFinished(QDBusPendingCallWatcher *call);
private: private:
QIBusPlatformInputContextPrivate *d; QIBusPlatformInputContextPrivate *d;
bool m_eventFilterUseSynchronousMode;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE