1
/* * This file is part of Maliit framework *
3
* Copyright (C) 2012 Canonical Ltd
5
* Contact: maliit-discuss@lists.maliit.org
7
* This library is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU Lesser General Public
9
* License version 2.1 as published by the Free Software Foundation
10
* and appearing in the file LICENSE.LGPL included in the packaging
14
#include <cerrno> // for errno
15
#include <cstring> // for strerror
16
#include <QGuiApplication>
18
#include <qpa/qplatformnativeinterface.h>
20
#include "wayland-client.h"
21
#include <qwayland-input-method.h>
22
#include <qwayland-text.h>
24
#include <xkbcommon/xkbcommon.h>
26
#include "waylandinputmethodconnection.h"
30
// TODO: Deduplicate it. Those values are used in
31
// minputcontextconnection, mimpluginmanager,
32
// mattributeextensionmanager and in input context implementations.
33
const char * const FocusStateAttribute = "focusState";
34
const char * const ContentTypeAttribute = "contentType";
35
const char * const CorrectionAttribute = "correctionEnabled";
36
const char * const PredictionAttribute = "predictionEnabled";
37
const char * const AutoCapitalizationAttribute = "autocapitalizationEnabled";
38
const char * const SurroundingTextAttribute = "surroundingText";
39
const char * const AnchorPositionAttribute = "anchorPosition";
40
const char * const CursorPositionAttribute = "cursorPosition";
41
const char * const HasSelectionAttribute = "hasSelection";
42
const char * const HiddenTextAttribute = "hiddenText";
44
typedef QPair<Qt::KeyboardModifiers, const char *> Modifier;
45
const Modifier modifiers[] = {
46
Modifier(Qt::ShiftModifier, XKB_MOD_NAME_SHIFT),
47
Modifier(Qt::ControlModifier, XKB_MOD_NAME_CTRL),
48
Modifier(Qt::AltModifier, XKB_MOD_NAME_ALT),
49
Modifier(Qt::MetaModifier, XKB_MOD_NAME_LOGO),
50
Modifier(Qt::KeypadModifier, XKB_LED_NAME_NUM)
53
QByteArray modifiersMap()
56
for (unsigned int iter(0); iter < (sizeof(modifiers) / sizeof(modifiers[0])); ++iter) {
57
mod_map.append(modifiers[iter].second);
62
xkb_mod_mask_t modifiersFromQt(const Qt::KeyboardModifiers qt_mods)
64
xkb_mod_mask_t mod_mask(0);
66
if (qt_mods == Qt::NoModifier) {
70
for (unsigned int iter(0); iter < (sizeof(modifiers) / sizeof(modifiers[0])); ++iter) {
71
if ((qt_mods & modifiers[iter].first) == modifiers[iter].first) {
72
mod_mask |= 1 << iter;
79
xkb_keysym_t keyFromQt(int qt_key)
82
case Qt::Key_Backspace:
83
return XKB_KEY_BackSpace;
85
return XKB_KEY_Return;
95
return XKB_KEY_NoSymbol;
99
QtWayland::wl_text_input::preedit_style preeditStyleFromMaliit(Maliit::PreeditFace face)
102
case Maliit::PreeditDefault:
103
return QtWayland::wl_text_input::preedit_style_default;
104
case Maliit::PreeditNoCandidates:
105
return QtWayland::wl_text_input::preedit_style_incorrect;
106
case Maliit::PreeditKeyPress:
107
return QtWayland::wl_text_input::preedit_style_highlight;
108
case Maliit::PreeditUnconvertible:
109
return QtWayland::wl_text_input::preedit_style_inactive;
110
case Maliit::PreeditActive:
111
return QtWayland::wl_text_input::preedit_style_active;
113
return QtWayland::wl_text_input::preedit_style_none;
117
Maliit::TextContentType contentTypeFromWayland(uint32_t purpose)
120
case QtWayland::wl_text_input::content_purpose_normal:
121
return Maliit::FreeTextContentType;
122
case QtWayland::wl_text_input::content_purpose_number:
123
return Maliit::NumberContentType;
124
case QtWayland::wl_text_input::content_purpose_phone:
125
return Maliit::PhoneNumberContentType;
126
case QtWayland::wl_text_input::content_purpose_url:
127
return Maliit::UrlContentType;
128
case QtWayland::wl_text_input::content_purpose_email:
129
return Maliit::EmailContentType;
131
return Maliit::CustomContentType;
135
bool matchesFlag(int value,
138
return ((value & flag) == flag);
141
const unsigned int wayland_connection_id(1);
143
} // unnamed namespace
148
class InputMethodContext;
150
class InputMethod : public QtWayland::wl_input_method
153
InputMethod(MInputContextConnection *connection, struct wl_registry *registry, int id);
156
InputMethodContext *context() const;
159
void input_method_activate(struct ::wl_input_method *object, struct ::wl_input_method_context *id) Q_DECL_OVERRIDE;
160
void input_method_deactivate(struct ::wl_input_method_context *context) Q_DECL_OVERRIDE;
163
MInputContextConnection *m_connection;
164
QScopedPointer<InputMethodContext> m_context;
167
class InputMethodContext : public QtWayland::wl_input_method_context
170
InputMethodContext(MInputContextConnection *connection, struct ::wl_input_method_context *object);
171
~InputMethodContext();
173
QString selection() const;
174
uint32_t serial() const;
177
void input_method_context_commit_state(uint32_t serial) Q_DECL_OVERRIDE;
178
void input_method_context_content_type(uint32_t hint, uint32_t purpose) Q_DECL_OVERRIDE;
179
void input_method_context_invoke_action(uint32_t button, uint32_t index) Q_DECL_OVERRIDE;
180
void input_method_context_preferred_language(const QString &language) Q_DECL_OVERRIDE;
181
void input_method_context_reset() Q_DECL_OVERRIDE;
182
void input_method_context_surrounding_text(const QString &text, uint32_t cursor, uint32_t anchor) Q_DECL_OVERRIDE;
185
MInputContextConnection *m_connection;
186
QVariantMap m_stateInfo;
194
struct WaylandInputMethodConnectionPrivate
196
Q_DECLARE_PUBLIC(WaylandInputMethodConnection)
198
WaylandInputMethodConnectionPrivate(WaylandInputMethodConnection *connection);
199
~WaylandInputMethodConnectionPrivate();
201
void handleRegistryGlobal(uint32_t name,
202
const char *interface,
204
void handleRegistryGlobalRemove(uint32_t name);
206
Maliit::Wayland::InputMethodContext *context();
208
WaylandInputMethodConnection *q_ptr;
210
wl_registry *registry;
211
QScopedPointer<Maliit::Wayland::InputMethod> input_method;
216
void registryGlobal(void *data,
217
wl_registry *registry,
219
const char *interface,
222
qDebug() << __PRETTY_FUNCTION__;
223
WaylandInputMethodConnectionPrivate *d =
224
static_cast<WaylandInputMethodConnectionPrivate *>(data);
227
d->handleRegistryGlobal(name, interface, version);
230
void registryGlobalRemove(void *data,
231
wl_registry *registry,
234
qDebug() << __PRETTY_FUNCTION__;
235
WaylandInputMethodConnectionPrivate *d =
236
static_cast<WaylandInputMethodConnectionPrivate *>(data);
239
d->handleRegistryGlobalRemove(name);
242
const wl_registry_listener maliit_registry_listener = {
248
} // unnamed namespace
250
WaylandInputMethodConnectionPrivate::WaylandInputMethodConnectionPrivate(WaylandInputMethodConnection *connection)
256
display = static_cast<wl_display *>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("display"));
258
qCritical() << Q_FUNC_INFO << "Failed to get a display.";
261
registry = wl_display_get_registry(display);
262
wl_registry_add_listener(registry, &maliit_registry_listener, this);
265
WaylandInputMethodConnectionPrivate::~WaylandInputMethodConnectionPrivate()
267
input_method.reset();
269
wl_registry_destroy(registry);
273
void WaylandInputMethodConnectionPrivate::handleRegistryGlobal(uint32_t name,
274
const char *interface,
278
Q_Q(WaylandInputMethodConnection);
280
if (!strcmp(interface, "wl_input_method")) {
281
input_method.reset(new Maliit::Wayland::InputMethod(q, registry, name));
285
void WaylandInputMethodConnectionPrivate::handleRegistryGlobalRemove(uint32_t name)
287
qDebug() << Q_FUNC_INFO << name;
290
Maliit::Wayland::InputMethodContext *WaylandInputMethodConnectionPrivate::context()
292
return input_method ? input_method->context() : 0;
295
// MInputContextWestonIMProtocolConnection
297
WaylandInputMethodConnection::WaylandInputMethodConnection()
298
: d_ptr(new WaylandInputMethodConnectionPrivate(this))
302
WaylandInputMethodConnection::~WaylandInputMethodConnection()
306
void WaylandInputMethodConnection::sendPreeditString(const QString &string,
307
const QList<Maliit::PreeditTextFormat> &preedit_formats,
312
Q_D(WaylandInputMethodConnection);
314
qDebug() << Q_FUNC_INFO << string << replace_start << replace_length << cursor_pos;
319
MInputContextConnection::sendPreeditString(string, preedit_formats,
320
replace_start, replace_length,
323
if (replace_length > 0) {
324
int cursor = widgetState().value(CursorPositionAttribute).toInt();
325
uint32_t index = string.midRef(qMin(cursor + replace_start, cursor), qAbs(replace_start)).toUtf8().size();
326
uint32_t length = string.midRef(cursor + replace_start, replace_length).toUtf8().size();
327
d->context()->delete_surrounding_text(index, length);
330
Q_FOREACH (const Maliit::PreeditTextFormat& format, preedit_formats) {
331
QtWayland::wl_text_input::preedit_style style = preeditStyleFromMaliit(format.preeditFace);
332
uint32_t index = string.leftRef(format.start).toUtf8().size();
333
uint32_t length = string.leftRef(format.start + format.length).toUtf8().size() - index;
334
qDebug() << Q_FUNC_INFO << "preedit_styling" << index << length;
335
d->context()->preedit_styling(index, length, style);
338
// TODO check if defined like that/required
339
if (cursor_pos < 0) {
340
cursor_pos = string.size() + 1 - cursor_pos;
343
qDebug() << Q_FUNC_INFO << "preedit_cursor" << string.leftRef(cursor_pos).toUtf8().size();
344
d->context()->preedit_cursor(string.leftRef(cursor_pos).toUtf8().size());
345
qDebug() << Q_FUNC_INFO << "preedit_string" << string;
346
d->context()->preedit_string(d->context()->serial(), string, string);
350
void WaylandInputMethodConnection::sendCommitString(const QString &string,
355
Q_D(WaylandInputMethodConnection);
357
qDebug() << Q_FUNC_INFO << string << replace_start << replace_length << cursor_pos;
362
MInputContextConnection::sendCommitString(string, replace_start, replace_length, cursor_pos);
364
if (cursor_pos != 0) {
365
qWarning() << Q_FUNC_INFO << "cursor_pos:" << cursor_pos << "!= 0 not supported yet";
369
if (replace_length > 0) {
370
int cursor = widgetState().value(CursorPositionAttribute).toInt();
371
uint32_t index = string.midRef(qMin(cursor + replace_start, cursor), qAbs(replace_start)).toUtf8().size();
372
uint32_t length = string.midRef(cursor + replace_start, replace_length).toUtf8().size();
373
d->context()->delete_surrounding_text(index, length);
376
cursor_pos = string.leftRef(cursor_pos).toUtf8().size();
377
d->context()->cursor_position(cursor_pos, cursor_pos);
378
d->context()->commit_string(d->context()->serial(), string);
381
void WaylandInputMethodConnection::sendKeyEvent(const QKeyEvent &keyEvent,
382
Maliit::EventRequestType requestType)
384
Q_D(WaylandInputMethodConnection);
386
qDebug() << Q_FUNC_INFO;
391
xkb_keysym_t sym(keyFromQt(keyEvent.key()));
393
if (sym == XKB_KEY_NoSymbol) {
394
qWarning() << "No conversion from Qt::Key:" << keyEvent.key() << "to XKB key. Update the keyFromQt() function.";
398
wl_keyboard_key_state state;
400
switch (keyEvent.type()) {
401
case QEvent::KeyPress:
402
state = WL_KEYBOARD_KEY_STATE_PRESSED;
405
case QEvent::KeyRelease:
406
state = WL_KEYBOARD_KEY_STATE_RELEASED;
410
qWarning() << "Unknown QKeyEvent type:" << keyEvent.type();
414
xkb_mod_mask_t modifiers(modifiersFromQt(keyEvent.modifiers()));
416
MInputContextConnection::sendKeyEvent(keyEvent, requestType);
418
d->context()->keysym(d->context()->serial(),
419
keyEvent.timestamp(),
420
sym, state, modifiers);
423
QString WaylandInputMethodConnection::selection(bool &valid)
425
Q_D(WaylandInputMethodConnection);
427
qDebug() << Q_FUNC_INFO;
429
Maliit::Wayland::InputMethodContext *context = d->input_method->context();
431
valid = context && !context->selection().isEmpty();
432
return context ? context->selection() : QString();
435
void WaylandInputMethodConnection::setLanguage(const QString &language)
437
Q_D(WaylandInputMethodConnection);
439
qDebug() << Q_FUNC_INFO;
444
d->context()->language(d->context()->serial(), language);
447
void WaylandInputMethodConnection::setSelection(int start, int length)
449
Q_D (WaylandInputMethodConnection);
451
qDebug() << Q_FUNC_INFO;
456
QString surrounding = widgetState().value(SurroundingTextAttribute).toString();
457
uint32_t index(surrounding.leftRef(start + length).toUtf8().size());
458
uint32_t anchor(surrounding.leftRef(start).toUtf8().size());
460
d->context()->cursor_position(index, anchor);
461
d->context()->commit_string(d->context()->serial(), QString());
467
InputMethod::InputMethod(MInputContextConnection *connection, struct wl_registry *registry, int id)
468
: QtWayland::wl_input_method(registry, id)
469
, m_connection(connection)
474
InputMethod::~InputMethod()
478
InputMethodContext *InputMethod::context() const
480
return m_context.data();
483
void InputMethod::input_method_activate(struct ::wl_input_method *, struct ::wl_input_method_context *id)
485
qDebug() << Q_FUNC_INFO;
487
m_context.reset(new InputMethodContext(m_connection, id));
489
m_context->modifiers_map(modifiersMap());
493
void InputMethod::input_method_deactivate(struct wl_input_method_context *)
495
qDebug() << Q_FUNC_INFO;
499
m_connection->handleDisconnection(wayland_connection_id);
502
InputMethodContext::InputMethodContext(MInputContextConnection *connection, struct ::wl_input_method_context *object)
503
: QtWayland::wl_input_method_context(object)
504
, m_connection(connection)
509
qDebug() << Q_FUNC_INFO;
511
m_stateInfo[FocusStateAttribute] = true;
512
m_connection->activateContext(wayland_connection_id);
513
m_connection->showInputMethod(wayland_connection_id);
516
InputMethodContext::~InputMethodContext()
518
qDebug() << Q_FUNC_INFO;
521
m_stateInfo[FocusStateAttribute] = false;
522
m_connection->updateWidgetInformation(wayland_connection_id, m_stateInfo, true);
523
m_connection->hideInputMethod(wayland_connection_id);
526
QString InputMethodContext::selection() const
531
uint32_t InputMethodContext::serial() const
536
void InputMethodContext::input_method_context_commit_state(uint32_t serial)
538
qDebug() << Q_FUNC_INFO;
541
m_connection->updateWidgetInformation(wayland_connection_id, m_stateInfo, false);
544
void InputMethodContext::input_method_context_content_type(uint32_t hint, uint32_t purpose)
546
qDebug() << Q_FUNC_INFO;
548
m_stateInfo[ContentTypeAttribute] = contentTypeFromWayland(purpose);
549
m_stateInfo[AutoCapitalizationAttribute] = matchesFlag(hint, QtWayland::wl_text_input::content_hint_auto_capitalization);
550
m_stateInfo[CorrectionAttribute] = matchesFlag(hint, QtWayland::wl_text_input::content_hint_auto_correction);
551
m_stateInfo[PredictionAttribute] = matchesFlag(hint, QtWayland::wl_text_input::content_hint_auto_completion);
552
m_stateInfo[HiddenTextAttribute] = matchesFlag(hint, QtWayland::wl_text_input::content_hint_hidden_text);
555
void InputMethodContext::input_method_context_invoke_action(uint32_t button, uint32_t index)
557
qDebug() << Q_FUNC_INFO << button << index;
560
void InputMethodContext::input_method_context_preferred_language(const QString &language)
562
qDebug() << Q_FUNC_INFO << language;
565
void InputMethodContext::input_method_context_reset()
567
qDebug() << Q_FUNC_INFO;
569
m_connection->reset(wayland_connection_id);
572
void InputMethodContext::input_method_context_surrounding_text(const QString &text, uint32_t cursor, uint32_t anchor)
574
qDebug() << Q_FUNC_INFO;
576
const QByteArray &utf8_text(text.toUtf8());
578
m_stateInfo[SurroundingTextAttribute] = text;
579
m_stateInfo[CursorPositionAttribute] = QString::fromUtf8(utf8_text.constData(), cursor).size();
580
m_stateInfo[AnchorPositionAttribute] = QString::fromUtf8(utf8_text.constData(), anchor).size();
581
if (cursor == anchor) {
582
m_stateInfo[HasSelectionAttribute] = false;
585
m_stateInfo[HasSelectionAttribute] = true;
586
uint32_t begin = qMin(anchor, cursor);
587
uint32_t end = qMax(anchor, cursor);
588
m_selection = QString::fromUtf8(utf8_text.constData() + begin, end - begin);