Commit 3c5cce16 authored by Adrien Faveraux's avatar Adrien Faveraux Committed by Bhushan Shah
Browse files

Migrate zwp_text_input_v2 to new approach

- Drop the v0 support, it is no longer used by anything
- Adapt the text-input related methods in seat interface to include
  versioning, this will be useful for when zwp_text_input_v3 support is
  included in kwayland-server
- Refactor SeatInterface to get rid of globalTextInput, it is no longer
  needed with the new approach
- Refactor out the ContentHints and ContentPurpose enums in separate
  header file which can be shared by zwp_text_input_v2/3 implementation

Co-Author: Bhushan Shah <bshah@kde.org>
parent d95bfda6
This diff is collapsed.
......@@ -59,9 +59,7 @@ set(SERVER_LIB_SRCS
surface_interface.cpp
surfacerole.cpp
tablet_interface.cpp
textinput_interface.cpp
textinput_interface_v0.cpp
textinput_interface_v2.cpp
textinput_v2_interface.cpp
touch_interface.cpp
viewporter_interface.cpp
xdgdecoration_v1_interface.cpp
......@@ -158,18 +156,12 @@ ecm_add_qtwayland_server_protocol(SERVER_LIB_SRCS
BASENAME server-decoration
)
ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/text-input.xml
BASENAME text
)
ecm_add_qtwayland_server_protocol(SERVER_LIB_SRCS
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/text-input/text-input-unstable-v1.xml
BASENAME text-input-unstable-v1
)
ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
ecm_add_qtwayland_server_protocol(SERVER_LIB_SRCS
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/text-input-unstable-v2.xml
BASENAME text-input-unstable-v2
)
......@@ -426,7 +418,8 @@ set(SERVER_LIB_HEADERS
subcompositor_interface.h
surface_interface.h
tablet_interface.h
textinput_interface.h
textinput.h
textinput_v2_interface.h
touch_interface.h
viewporter_interface.h
xdgdecoration_v1_interface.h
......
......@@ -40,7 +40,7 @@
#include "slide_interface.h"
#include "subcompositor_interface.h"
#include "tablet_interface.h"
#include "textinput_interface_p.h"
#include "textinput_v2_interface_p.h"
#include "viewporter_interface.h"
#include "xdgdecoration_v1_interface.h"
#include "xdgforeign_v2_interface.h"
......@@ -343,19 +343,9 @@ ScreencastV1Interface *Display::createScreencastV1Interface(QObject *parent)
return s;
}
TextInputManagerInterface *Display::createTextInputManager(const TextInputInterfaceVersion &version, QObject *parent)
TextInputManagerV2Interface *Display::createTextInputManagerV2(QObject *parent)
{
TextInputManagerInterface *t = nullptr;
switch (version) {
case TextInputInterfaceVersion::UnstableV0:
t = new TextInputManagerUnstableV0Interface(this, parent);
break;
case TextInputInterfaceVersion::UnstableV1:
// unsupported
return nullptr;
case TextInputInterfaceVersion::UnstableV2:
t = new TextInputManagerUnstableV2Interface(this, parent);
}
auto t = new TextInputManagerV2Interface(this, parent);
connect(this, &Display::aboutToTerminate, t, [t] { delete t; });
return t;
}
......
......@@ -54,8 +54,7 @@ class ContrastManagerInterface;
class ServerSideDecorationManagerInterface;
class SlideManagerInterface;
class SubCompositorInterface;
enum class TextInputInterfaceVersion;
class TextInputManagerInterface;
class TextInputManagerV2Interface;
class XdgShellInterface;
enum class RelativePointerInterfaceVersion;
class RelativePointerManagerInterface;
......@@ -198,7 +197,7 @@ public:
* @returns The created manager object
* @since 5.23
**/
TextInputManagerInterface *createTextInputManager(const TextInputInterfaceVersion &version, QObject *parent = nullptr);
TextInputManagerV2Interface *createTextInputManagerV2(QObject *parent = nullptr);
/**
* Creates the XdgShell in interface @p version.
......
......@@ -14,7 +14,6 @@
#include <QHash>
#include "qwayland-server-input-method-unstable-v1.h"
#include "wayland-text-server-protocol.h"
#include "qwayland-server-text-input-unstable-v1.h"
namespace KWaylandServer
......@@ -101,13 +100,13 @@ public:
{
Qt::LayoutDirection qtDirection;
switch (direction) {
case WL_TEXT_INPUT_TEXT_DIRECTION_LTR:
case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_LTR:
qtDirection = Qt::LeftToRight;
break;
case WL_TEXT_INPUT_TEXT_DIRECTION_RTL:
case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_RTL:
qtDirection = Qt::RightToLeft;
break;
case WL_TEXT_INPUT_TEXT_DIRECTION_AUTO:
case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_AUTO:
qtDirection = Qt::LayoutDirectionAuto;
break;
}
......@@ -156,87 +155,87 @@ void InputMethodContextV1Interface::sendCommitState(uint32_t serial)
}
}
void InputMethodContextV1Interface::sendContentType(TextInputInterface::ContentHints hint, TextInputInterface::ContentPurpose purpose)
void InputMethodContextV1Interface::sendContentType(TextInputContentHints hint, TextInputContentPurpose purpose)
{
quint32 contentHint = QtWaylandServer::zwp_text_input_v1::content_hint_none;
quint32 contentPurpose;
if (hint.testFlag(TextInputInterface::ContentHint::AutoCapitalization)) {
if (hint.testFlag(TextInputContentHint::AutoCapitalization)) {
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_auto_capitalization;
}
if (hint.testFlag(TextInputInterface::ContentHint::AutoCorrection)) {
if (hint.testFlag(TextInputContentHint::AutoCorrection)) {
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_auto_correction;
}
if (hint.testFlag(TextInputInterface::ContentHint::AutoCapitalization)) {
if (hint.testFlag(TextInputContentHint::AutoCapitalization)) {
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_auto_capitalization;
}
if (hint.testFlag(TextInputInterface::ContentHint::LowerCase)) {
if (hint.testFlag(TextInputContentHint::LowerCase)) {
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_lowercase;
}
if (hint.testFlag(TextInputInterface::ContentHint::UpperCase)) {
if (hint.testFlag(TextInputContentHint::UpperCase)) {
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_uppercase;
}
if (hint.testFlag(TextInputInterface::ContentHint::TitleCase)) {
if (hint.testFlag(TextInputContentHint::TitleCase)) {
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_titlecase;
}
if (hint.testFlag(TextInputInterface::ContentHint::HiddenText)) {
if (hint.testFlag(TextInputContentHint::HiddenText)) {
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_hidden_text;
}
if (hint.testFlag(TextInputInterface::ContentHint::SensitiveData)) {
if (hint.testFlag(TextInputContentHint::SensitiveData)) {
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_lowercase;
}
if (hint.testFlag(TextInputInterface::ContentHint::Latin)) {
if (hint.testFlag(TextInputContentHint::Latin)) {
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_latin;
}
if (hint.testFlag(TextInputInterface::ContentHint::MultiLine)) {
if (hint.testFlag(TextInputContentHint::MultiLine)) {
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_multiline;
}
if (hint.testFlag(TextInputInterface::ContentHint::None)) {
if (hint.testFlag(TextInputContentHint::None)) {
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_none;
}
switch (purpose) {
case TextInputInterface::ContentPurpose::Alpha:
case TextInputContentPurpose::Alpha:
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_alpha;
break;
case TextInputInterface::ContentPurpose::Digits:
case TextInputContentPurpose::Digits:
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_digits;
break;
case TextInputInterface::ContentPurpose::Number:
case TextInputContentPurpose::Number:
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_number;
break;
case TextInputInterface::ContentPurpose::Phone:
case TextInputContentPurpose::Phone:
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_phone;
break;
case TextInputInterface::ContentPurpose::Url:
case TextInputContentPurpose::Url:
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_url;
break;
case TextInputInterface::ContentPurpose::Email:
case TextInputContentPurpose::Email:
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_email;
break;
case TextInputInterface::ContentPurpose::Name:
case TextInputContentPurpose::Name:
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_name;
break;
case TextInputInterface::ContentPurpose::Password:
case TextInputContentPurpose::Password:
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_password;
break;
case TextInputInterface::ContentPurpose::Date:
case TextInputContentPurpose::Date:
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_date;
break;
case TextInputInterface::ContentPurpose::Time:
case TextInputContentPurpose::Time:
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_time;
break;
case TextInputInterface::ContentPurpose::DateTime:
case TextInputContentPurpose::DateTime:
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_datetime;
break;
case TextInputInterface::ContentPurpose::Terminal:
case TextInputContentPurpose::Terminal:
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_terminal;
break;
case TextInputInterface::ContentPurpose::Normal:
case TextInputContentPurpose::Normal:
default:
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_alpha;
}
for (auto r : d->resourceMap()) {
d->send_content_type(r->handle, contentHint, contentPurpose);
}
......
......@@ -8,10 +8,11 @@
#define WAYLAND_SERVER_INPUTMETHOD_INTERFACE_H
#include <KWaylandServer/kwaylandserver_export.h>
#include <QVector>
#include <QObject>
#include "textinput_interface.h"
#include "textinput.h"
namespace KWaylandServer
{
......@@ -68,7 +69,7 @@ public:
void sendSurroundingText(const QString &text, quint32 cursor, quint32 anchor);
void sendReset();
void sendContentType(TextInputInterface::ContentHints hint, TextInputInterface::ContentPurpose purpose);
void sendContentType(KWaylandServer::TextInputContentHints hint, KWaylandServer::TextInputContentPurpose purpose);
void sendInvokeAction(quint32 button, quint32 index);
void sendCommitState(quint32 serial);
void sendPreferredLanguage(const QString &language);
......
......@@ -19,7 +19,7 @@
#include "primaryselectiondevice_v1_interface.h"
#include "primaryselectionsource_v1_interface.h"
#include "surface_interface.h"
#include "textinput_interface_p.h"
#include "textinput_v2_interface_p.h"
// Qt
#include <QFile>
// Wayland
......@@ -46,6 +46,7 @@ SeatInterface::Private::Private(SeatInterface *q, Display *display)
: Global::Private(display, &wl_seat_interface, s_version)
, q(q)
{
textInputV2 = new TextInputV2Interface(q);
}
#ifndef K_DOXYGEN
......@@ -247,11 +248,6 @@ QVector<DataDeviceInterface *> SeatInterface::Private::dataDevicesForSurface(Sur
return interfacesForSurface(surface, dataDevices);
}
TextInputInterface *SeatInterface::Private::textInputForSurface(SurfaceInterface *surface) const
{
return interfaceForSurface(surface, textInputs);
}
void SeatInterface::Private::registerDataDevice(DataDeviceInterface *dataDevice)
{
Q_ASSERT(dataDevice->seat() == q);
......@@ -401,32 +397,6 @@ void SeatInterface::Private::registerPrimarySelectionDevice(PrimarySelectionDevi
}
}
void SeatInterface::Private::registerTextInput(TextInputInterface *ti)
{
// text input version 0 might call this multiple times
if (textInputs.contains(ti)) {
return;
}
textInputs << ti;
if (textInput.focus.surface && textInput.focus.surface->client() == ti->client()) {
// this is a text input for the currently focused text input surface
if (!textInput.focus.textInput) {
textInput.focus.textInput = ti;
ti->d_func()->sendEnter(textInput.focus.surface, textInput.focus.serial);
emit q->focusedTextInputChanged();
}
}
QObject::connect(ti, &QObject::destroyed, q,
[this, ti] {
textInputs.removeAt(textInputs.indexOf(ti));
if (textInput.focus.textInput == ti) {
textInput.focus.textInput = nullptr;
emit q->focusedTextInputChanged();
}
}
);
}
void SeatInterface::Private::endDrag(quint32 serial)
{
auto target = drag.target;
......@@ -1608,48 +1578,38 @@ void SeatInterface::setFocusedTextInputSurface(SurfaceInterface *surface)
{
Q_D();
const quint32 serial = d->display->nextSerial();
const auto old = d->textInput.focus.textInput;
if (d->textInput.focus.textInput) {
// TODO: setFocusedSurface like in other interfaces
d->textInput.focus.textInput->d_func()->sendLeave(serial, d->textInput.focus.surface);
}
if (d->textInput.focus.surface) {
disconnect(d->textInput.focus.destroyConnection);
}
d->textInput.focus = Private::TextInput::Focus();
d->textInput.focus.surface = surface;
TextInputInterface *t = d->textInputForSurface(surface);
if (t && !t->resource()) {
t = nullptr;
}
d->textInput.focus.textInput = t;
if (d->textInput.focus.surface) {
d->textInput.focus.destroyConnection = connect(surface, &SurfaceInterface::aboutToBeDestroyed, this,
if (d->focusedTextInputSurface) {
disconnect(d->focusedSurfaceDestroyConnection);
}
if (d->focusedTextInputSurface != surface){
d->textInputV2->d->sendLeave(serial, d->focusedTextInputSurface);
emit focusedTextInputSurfaceChanged();
}
d->focusedTextInputSurface = surface;
if (d->focusedTextInputSurface) {
d->focusedSurfaceDestroyConnection = connect(surface, &SurfaceInterface::aboutToBeDestroyed, this,
[this] {
setFocusedTextInputSurface(nullptr);
}
);
d->textInput.focus.serial = serial;
}
if (t) {
// TODO: setFocusedSurface like in other interfaces
t->d_func()->sendEnter(surface, serial);
}
if (old != t) {
emit focusedTextInputChanged();
}
// TODO: setFocusedSurface like in other interfaces
d->textInputV2->d->sendEnter(surface, serial);
}
SurfaceInterface *SeatInterface::focusedTextInputSurface() const
{
Q_D();
return d->textInput.focus.surface;
return d->focusedTextInputSurface;
}
TextInputInterface *SeatInterface::focusedTextInput() const
TextInputV2Interface *SeatInterface::textInputV2() const
{
Q_D();
return d->textInput.focus.textInput;
return d->textInputV2;
}
AbstractDataSource *SeatInterface::selection() const
......
......@@ -26,7 +26,7 @@ class DataDeviceInterface;
class AbstractDataSource;
class Display;
class SurfaceInterface;
class TextInputInterface;
class TextInputV2Interface;
/**
* Describes the source types for axis events. This indicates to the
......@@ -654,10 +654,10 @@ public:
* If the SeatInterface has the keyboard capability this method will
* be invoked automatically when setting the focused keyboard surface.
*
* In case there is a TextInputInterface for the @p surface, the enter
* event will be triggered on the TextInputInterface for @p surface.
* The focusedTextInput will be set to that TextInputInterface. If there
* is no TextInputInterface for that @p surface, it might get updated later on.
* In case there is a TextInputV2Interface for the @p surface, the enter
* event will be triggered on the TextInputV2Interface for @p surface.
* The focusedTextInput will be set to that TextInputV2Interface. If there
* is no TextInputV2Interface for that @p surface, it might get updated later on.
* In both cases the signal focusedTextInputChanged will be emitted.
*
* @see focusedTextInputSurface
......@@ -679,13 +679,13 @@ public:
*
* The focused text input might not be enabled for the {@link focusedTextInputSurface}.
* It is recommended to check the enabled state before interacting with the
* TextInputInterface.
* TextInputV2Interface.
*
* @see focusedTextInputChanged
* @see focusedTextInputSurface
* @since 5.23
**/
TextInputInterface *focusedTextInput() const;
TextInputV2Interface *textInputV2() const;
///@}
/**
......@@ -772,15 +772,14 @@ Q_SIGNALS:
* @see focusedTextInput
* @since 5.23
**/
void focusedTextInputChanged();
void focusedTextInputSurfaceChanged();
private:
friend class Display;
friend class DataControlDeviceV1Interface;
friend class PrimarySelectionDeviceV1Interface;
friend class DataDeviceManagerInterface;
friend class TextInputManagerUnstableV0Interface;
friend class TextInputManagerUnstableV2Interface;
friend class TextInputManagerV2InterfacePrivate;
explicit SeatInterface(Display *display, QObject *parent);
class Private;
......
......@@ -23,7 +23,7 @@ class AbstractDataSource;
class DataDeviceInterface;
class DataSourceInterface;
class DataControlDeviceV1Interface;
class TextInputInterface;
class TextInputV2Interface;
class PrimarySelectionDeviceV1Interface;
class SeatInterface::Private : public Global::Private
......@@ -37,11 +37,9 @@ public:
QVector<KeyboardInterface *> keyboardsForSurface(SurfaceInterface *surface) const;
QVector<TouchInterface *> touchsForSurface(SurfaceInterface *surface) const;
QVector<DataDeviceInterface *> dataDevicesForSurface(SurfaceInterface *surface) const;
TextInputInterface *textInputForSurface(SurfaceInterface *surface) const;
void registerPrimarySelectionDevice(PrimarySelectionDeviceV1Interface *primarySelectionDevice);
void registerDataDevice(DataDeviceInterface *dataDevice);
void registerDataControlDevice(DataControlDeviceV1Interface *dataDevice);
void registerTextInput(TextInputInterface *textInput);
void endDrag(quint32 serial);
QString name;
......@@ -57,7 +55,10 @@ public:
QVector<PrimarySelectionDeviceV1Interface*> primarySelectionDevices;
QVector<DataControlDeviceV1Interface*> dataControlDevices;
QVector<TextInputInterface*> textInputs;
// TextInput v2
QPointer<TextInputV2Interface> textInputV2;
SurfaceInterface *focusedTextInputSurface = nullptr;
QMetaObject::Connection focusedSurfaceDestroyConnection;
// the last thing copied into the clipboard content
AbstractDataSource *currentSelection = nullptr;
......@@ -125,17 +126,6 @@ public:
Keyboard keys;
bool updateKey(quint32 key, Keyboard::State state);
struct TextInput {
struct Focus {
SurfaceInterface *surface = nullptr;
QMetaObject::Connection destroyConnection;
quint32 serial = 0;
TextInputInterface *textInput = nullptr;
};
Focus focus;
};
TextInput textInput;
// Touch related members
struct Touch {
struct Focus {
......
/*
SPDX-FileCopyrightText: 2020 Bhushan Shah <bshah@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef TEXTINPUT_H_INCLUDED
#define TEXTINPUT_H_INCLUDED
#include <KWaylandServer/kwaylandserver_export.h>
#include <QtGlobal>
#include <inttypes.h>
namespace KWaylandServer
{
KWAYLANDSERVER_EXPORT Q_NAMESPACE
/**
* ContentHint allows to modify the behavior of the text input.
**/
enum class TextInputContentHint {
/**
* no special behaviour
*/
None = 0,
/**
* suggest word completions
*/
AutoCompletion = 1 << 0,
/**
* suggest word corrections
*/
AutoCorrection = 1 << 1,
/**
* switch to uppercase letters at the start of a sentence
*/
AutoCapitalization = 1 << 2,
/**
* prefer lowercase letters
*/
LowerCase = 1 << 3,
/**
* prefer uppercase letters
*/
UpperCase = 1 << 4,
/**
* prefer casing for titles and headings (can be language dependent)
*/
TitleCase = 1 << 5,
/**
* characters should be hidden
*/
HiddenText = 1 << 6,
/**
* typed text should not be stored
*/
SensitiveData = 1 << 7,
/**
* just latin characters should be entered
*/
Latin = 1 << 8,
/**
* the text input is multi line
*/
MultiLine = 1 << 9
};
Q_DECLARE_FLAGS(TextInputContentHints, TextInputContentHint)
/**
* The ContentPurpose allows to specify the primary purpose of a text input.
*
* This allows an input method to show special purpose input panels with
* extra characters or to disallow some characters.
*/
enum class TextInputContentPurpose {
/**
* default input, allowing all characters
*/
Normal,
/**
* allow only alphabetic characters
**/
Alpha,
/**
* allow only digits
*/
Digits,
/**
* input a number (including decimal separator and sign)
*/
Number,
/**
* input a phone number
*/
Phone,
/**
* input an URL
*/
Url,
/**
* input an email address
**/
Email,
/**
* input a name of a person
*/
Name,
/**
* input a password
*/
Password,
/**
* input a date
*/
Date,
/**
* input a time
*/
Time,
/**
* input a date and time
*/
DateTime,