summaryrefslogtreecommitdiffstats
path: root/src/compositor/extensions/qwaylandqttextinputmethod.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/compositor/extensions/qwaylandqttextinputmethod.cpp')
-rw-r--r--src/compositor/extensions/qwaylandqttextinputmethod.cpp435
1 files changed, 435 insertions, 0 deletions
diff --git a/src/compositor/extensions/qwaylandqttextinputmethod.cpp b/src/compositor/extensions/qwaylandqttextinputmethod.cpp
new file mode 100644
index 000000000..edcf28d30
--- /dev/null
+++ b/src/compositor/extensions/qwaylandqttextinputmethod.cpp
@@ -0,0 +1,435 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "qwaylandqttextinputmethod.h"
+#include "qwaylandqttextinputmethod_p.h"
+
+#include <QtGui/qevent.h>
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qinputmethod.h>
+#include <QtGui/qcolor.h>
+#include <QtGui/qtextformat.h>
+
+#include <QtWaylandCompositor/qwaylandcompositor.h>
+#include <QtWaylandCompositor/qwaylandsurface.h>
+
+QT_BEGIN_NAMESPACE
+
+QWaylandQtTextInputMethodPrivate::QWaylandQtTextInputMethodPrivate(QWaylandCompositor *c)
+ : compositor(c)
+{
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_enable(Resource *resource, struct ::wl_resource *surface)
+{
+ Q_Q(QWaylandQtTextInputMethod);
+ if (this->resource == resource) {
+ QWaylandSurface *waylandSurface = QWaylandSurface::fromResource(surface);
+ if (surface != nullptr) {
+ enabledSurfaces[resource] = waylandSurface;
+ emit q->surfaceEnabled(waylandSurface);
+ }
+ }
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_disable(Resource *resource, struct ::wl_resource *surface)
+{
+ Q_Q(QWaylandQtTextInputMethod);
+ if (this->resource == resource) {
+ QWaylandSurface *waylandSurface = QWaylandSurface::fromResource(surface);
+ QWaylandSurface *enabledSurface = enabledSurfaces.take(resource);
+
+ if (Q_UNLIKELY(enabledSurface != waylandSurface))
+ qCWarning(qLcWaylandCompositorInputMethods) << "Disabled surface does not match the one currently enabled";
+
+ emit q->surfaceEnabled(waylandSurface);
+ }
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_destroy(Resource *resource)
+{
+ if (this->resource == resource)
+ wl_resource_destroy(resource->handle);
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_reset(Resource *resource)
+{
+ if (this->resource == resource)
+ qApp->inputMethod()->reset();
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_commit(Resource *resource)
+{
+ if (this->resource == resource)
+ qApp->inputMethod()->commit();
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_show_input_panel(Resource *resource)
+{
+ if (this->resource == resource)
+ qApp->inputMethod()->show();
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_hide_input_panel(Resource *resource)
+{
+ if (this->resource == resource)
+ qApp->inputMethod()->hide();
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_invoke_action(Resource *resource, int32_t type, int32_t cursorPosition)
+{
+ if (this->resource == resource)
+ qApp->inputMethod()->invokeAction(QInputMethod::Action(type), cursorPosition);
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_update_cursor_rectangle(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ if (this->resource == resource)
+ cursorRectangle = QRect(x, y, width, height);
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_start_update(Resource *resource, int32_t queries)
+{
+ if (this->resource == resource)
+ updatingQueries = Qt::InputMethodQueries(queries);
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_update_hints(Resource *resource, int32_t hints)
+{
+ if (this->resource == resource)
+ this->hints = Qt::InputMethodHints(hints);
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_update_anchor_position(Resource *resource, int32_t anchorPosition)
+{
+ if (this->resource == resource)
+ this->anchorPosition = anchorPosition;
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_update_cursor_position(Resource *resource, int32_t cursorPosition)
+{
+ if (this->resource == resource)
+ this->cursorPosition = cursorPosition;
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_update_surrounding_text(Resource *resource, const QString &surroundingText, int32_t surroundingTextOffset)
+{
+ if (this->resource == resource) {
+ this->surroundingText = surroundingText;
+ this->surroundingTextOffset = surroundingTextOffset;
+ }
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_update_absolute_position(Resource *resource, int32_t absolutePosition)
+{
+ if (this->resource == resource)
+ this->absolutePosition = absolutePosition;
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_update_preferred_language(Resource *resource, const QString &preferredLanguage)
+{
+ if (this->resource == resource)
+ this->preferredLanguage = preferredLanguage;
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_end_update(Resource *resource)
+{
+ Q_Q(QWaylandQtTextInputMethod);
+ if (this->resource == resource && updatingQueries != 0) {
+ Qt::InputMethodQueries queries = updatingQueries;
+ updatingQueries = Qt::InputMethodQueries();
+ emit q->updateInputMethod(queries);
+ }
+}
+
+void QWaylandQtTextInputMethodPrivate::text_input_method_v1_acknowledge_input_method(Resource *resource)
+{
+ if (this->resource == resource)
+ waitingForSync = false;
+}
+
+QWaylandQtTextInputMethod::QWaylandQtTextInputMethod(QWaylandObject *container, QWaylandCompositor *compositor)
+ : QWaylandCompositorExtensionTemplate(container, *new QWaylandQtTextInputMethodPrivate(compositor))
+{
+ connect(&d_func()->focusDestroyListener, &QWaylandDestroyListener::fired,
+ this, &QWaylandQtTextInputMethod::focusSurfaceDestroyed);
+
+ connect(qGuiApp->inputMethod(), &QInputMethod::visibleChanged, this, &QWaylandQtTextInputMethod::sendVisibleChanged);
+ connect(qGuiApp->inputMethod(), &QInputMethod::keyboardRectangleChanged, this, &QWaylandQtTextInputMethod::sendKeyboardRectangleChanged);
+ connect(qGuiApp->inputMethod(), &QInputMethod::inputDirectionChanged, this, &QWaylandQtTextInputMethod::sendInputDirectionChanged);
+ connect(qGuiApp->inputMethod(), &QInputMethod::localeChanged, this, &QWaylandQtTextInputMethod::sendLocaleChanged);
+}
+
+
+QWaylandQtTextInputMethod::~QWaylandQtTextInputMethod()
+{
+}
+
+void QWaylandQtTextInputMethod::focusSurfaceDestroyed()
+{
+ Q_D(QWaylandQtTextInputMethod);
+ d->focusDestroyListener.reset();
+ d->waitingForSync = false;
+ d->resource = nullptr;
+ d->focusedSurface = nullptr;
+}
+
+QWaylandSurface *QWaylandQtTextInputMethod::focusedSurface() const
+{
+ Q_D(const QWaylandQtTextInputMethod);
+ return d->focusedSurface;
+}
+
+QVariant QWaylandQtTextInputMethod::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const
+{
+ Q_D(const QWaylandQtTextInputMethod);
+ switch (property) {
+ case Qt::ImHints:
+ return int(d->hints);
+ case Qt::ImCursorRectangle:
+ return d->cursorRectangle;
+ case Qt::ImCursorPosition:
+ return d->cursorPosition;
+ case Qt::ImSurroundingText:
+ return d->surroundingText;
+ case Qt::ImAbsolutePosition:
+ return d->absolutePosition;
+ case Qt::ImCurrentSelection:
+ return d->surroundingText.mid(qMin(d->cursorPosition, d->anchorPosition),
+ qAbs(d->anchorPosition - d->cursorPosition));
+ case Qt::ImAnchorPosition:
+ return d->anchorPosition;
+ case Qt::ImTextAfterCursor:
+ if (argument.isValid())
+ return d->surroundingText.mid(d->cursorPosition, argument.toInt());
+ return d->surroundingText.mid(d->cursorPosition);
+ case Qt::ImTextBeforeCursor:
+ if (argument.isValid())
+ return d->surroundingText.left(d->cursorPosition).right(argument.toInt());
+ return d->surroundingText.left(d->cursorPosition);
+ case Qt::ImPreferredLanguage:
+ return d->preferredLanguage;
+
+ default:
+ return QVariant();
+ }
+}
+
+void QWaylandQtTextInputMethod::sendKeyEvent(QKeyEvent *event)
+{
+ Q_D(QWaylandQtTextInputMethod);
+ if (d->resource == nullptr || d->resource->handle == nullptr)
+ return;
+
+ d->send_key(d->resource->handle,
+ int(event->type()),
+ event->key(),
+ event->modifiers(),
+ event->isAutoRepeat(),
+ event->count(),
+ event->nativeScanCode(),
+ event->nativeVirtualKey(),
+ event->nativeModifiers(),
+ event->text());
+}
+
+void QWaylandQtTextInputMethod::sendInputMethodEvent(QInputMethodEvent *event)
+{
+ Q_D(QWaylandQtTextInputMethod);
+ if (d->resource == nullptr || d->resource->handle == nullptr || d->compositor == nullptr)
+ return;
+
+ if (d->updatingQueries != 0) {
+ qCWarning(qLcWaylandCompositorInputMethods) << "Input method event sent while client is updating. Ignored.";
+ return;
+ }
+
+ Q_ASSERT(!d->waitingForSync);
+
+ QString oldSurroundText = d->surroundingText;
+ int oldCursorPosition = d->cursorPosition;
+ int oldAnchorPosition = d->anchorPosition;
+ int oldAbsolutePosition = d->absolutePosition;
+ QRect oldCursorRectangle = d->cursorRectangle;
+ QString oldPreferredLanguage = d->preferredLanguage;
+ Qt::InputMethodHints oldHints = d->hints;
+
+ uint serial = d->compositor->nextSerial(); // ### Not needed if we block on this?
+ d->send_start_input_method_event(d->resource->handle, serial, d->surroundingTextOffset);
+ for (const QInputMethodEvent::Attribute &attribute : event->attributes()) {
+ switch (attribute.type) {
+ case QInputMethodEvent::TextFormat:
+ {
+ auto properties = attribute.value.value<QTextFormat>().properties();
+ if (properties.size() != 2 || properties.firstKey() != QTextFormat::FontUnderline || properties.lastKey() != QTextFormat::TextUnderlineStyle) {
+ qCWarning(qLcWaylandCompositorInputMethods()) << "Only underline text formats currently supported";
+ }
+
+ d->send_input_method_event_attribute(d->resource->handle,
+ serial,
+ attribute.type,
+ attribute.start,
+ attribute.length,
+ QString());
+ break;
+ }
+ case QInputMethodEvent::Cursor:
+ d->cursorPosition = attribute.start;
+ d->send_input_method_event_attribute(d->resource->handle,
+ serial,
+ attribute.type,
+ attribute.start,
+ attribute.length,
+ attribute.value.typeId() == QMetaType::QColor ? attribute.value.value<QColor>().name() : QString());
+ break;
+ case QInputMethodEvent::Language: // ### What is the type of value? Is it string?
+ Q_FALLTHROUGH();
+ case QInputMethodEvent::Ruby:
+ d->send_input_method_event_attribute(d->resource->handle,
+ serial,
+ attribute.type,
+ attribute.start,
+ attribute.length,
+ attribute.value.toString());
+ break;
+ case QInputMethodEvent::Selection:
+ d->send_input_method_event_attribute(d->resource->handle,
+ serial,
+ attribute.type,
+ attribute.start,
+ attribute.length,
+ QString());
+ break;
+ }
+ }
+
+ d->waitingForSync = true;
+ d->send_end_input_method_event(d->resource->handle,
+ serial,
+ event->commitString(),
+ event->preeditString(),
+ event->replacementStart(),
+ event->replacementLength());
+
+ while (d->waitingForSync)
+ d->compositor->processWaylandEvents();
+
+ Qt::InputMethodQueries queries;
+ if (d->surroundingText != oldSurroundText)
+ queries |= Qt::ImSurroundingText;
+ if (d->cursorPosition != oldCursorPosition)
+ queries |= Qt::ImCursorPosition;
+ if (d->anchorPosition != oldAnchorPosition)
+ queries |= Qt::ImAnchorPosition;
+ if (d->absolutePosition != oldAbsolutePosition)
+ queries |= Qt::ImAbsolutePosition;
+ if (d->cursorRectangle != oldCursorRectangle)
+ queries |= Qt::ImCursorRectangle;
+ if (d->preferredLanguage != oldPreferredLanguage)
+ queries |= Qt::ImPreferredLanguage;
+ if (d->hints != oldHints)
+ queries |= Qt::ImHints;
+ if (queries != 0)
+ emit updateInputMethod(queries);
+}
+
+bool QWaylandQtTextInputMethod::isSurfaceEnabled(QWaylandSurface *surface) const
+{
+ Q_D(const QWaylandQtTextInputMethod);
+ return d->enabledSurfaces.values().contains(surface);
+}
+
+void QWaylandQtTextInputMethod::setFocus(QWaylandSurface *surface)
+{
+ Q_D(QWaylandQtTextInputMethod);
+
+ QWaylandQtTextInputMethodPrivate::Resource *resource = surface != nullptr ? d->resourceMap().value(surface->waylandClient()) : nullptr;
+ if (d->resource == resource)
+ return;
+
+ if (d->resource != nullptr && d->focusedSurface != nullptr) {
+ d->send_leave(d->resource->handle, d->focusedSurface->resource());
+ d->focusDestroyListener.reset();
+ }
+
+ d->resource = resource;
+ d->focusedSurface = surface;
+
+ if (d->resource != nullptr && d->focusedSurface != nullptr) {
+ d->surroundingText.clear();
+ d->cursorPosition = 0;
+ d->anchorPosition = 0;
+ d->absolutePosition = 0;
+ d->cursorRectangle = QRect();
+ d->preferredLanguage.clear();
+ d->hints = Qt::InputMethodHints();
+ d->send_enter(d->resource->handle, d->focusedSurface->resource());
+ sendInputDirectionChanged();
+ sendLocaleChanged();
+ sendInputDirectionChanged();
+ d->focusDestroyListener.listenForDestruction(surface->resource());
+ if (d->inputPanelVisible && d->enabledSurfaces.values().contains(surface))
+ qGuiApp->inputMethod()->show();
+ }
+}
+
+void QWaylandQtTextInputMethod::sendLocaleChanged()
+{
+ Q_D(QWaylandQtTextInputMethod);
+ if (d->resource == nullptr || d->resource->handle == nullptr)
+ return;
+
+ d->send_locale_changed(d->resource->handle, qGuiApp->inputMethod()->locale().bcp47Name());
+}
+
+void QWaylandQtTextInputMethod::sendInputDirectionChanged()
+{
+ Q_D(QWaylandQtTextInputMethod);
+ if (d->resource == nullptr || d->resource->handle == nullptr)
+ return;
+
+ d->send_input_direction_changed(d->resource->handle, int(qGuiApp->inputMethod()->inputDirection()));
+}
+
+void QWaylandQtTextInputMethod::sendKeyboardRectangleChanged()
+{
+ Q_D(QWaylandQtTextInputMethod);
+ if (d->resource == nullptr || d->resource->handle == nullptr)
+ return;
+
+ QRectF keyboardRectangle = qGuiApp->inputMethod()->keyboardRectangle();
+ d->send_keyboard_rectangle_changed(d->resource->handle,
+ wl_fixed_from_double(keyboardRectangle.x()),
+ wl_fixed_from_double(keyboardRectangle.y()),
+ wl_fixed_from_double(keyboardRectangle.width()),
+ wl_fixed_from_double(keyboardRectangle.height()));
+}
+
+void QWaylandQtTextInputMethod::sendVisibleChanged()
+{
+ Q_D(QWaylandQtTextInputMethod);
+ if (d->resource == nullptr || d->resource->handle == nullptr)
+ return;
+
+ d->send_visible_changed(d->resource->handle, int(qGuiApp->inputMethod()->isVisible()));
+}
+
+void QWaylandQtTextInputMethod::add(::wl_client *client, uint32_t id, int version)
+{
+ Q_D(QWaylandQtTextInputMethod);
+ d->add(client, id, version);
+}
+
+const struct wl_interface *QWaylandQtTextInputMethod::interface()
+{
+ return QWaylandQtTextInputMethodPrivate::interface();
+}
+
+QByteArray QWaylandQtTextInputMethod::interfaceName()
+{
+ return QWaylandQtTextInputMethodPrivate::interfaceName();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qwaylandqttextinputmethod.cpp"