summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorInho Lee <inho.lee@qt.io>2021-06-19 06:27:22 +0200
committerInho Lee <inho.lee@qt.io>2021-11-19 10:33:31 +0100
commit0cec50bece2dcd69127f19c87a3062e4b13f3723 (patch)
tree8ec11248518b1edfede9538a07ee5e0b0aa5624e
parent6f195a592b26ad8416a6f02d6bd7258ab3fadf65 (diff)
Support text-input-unstable-v4-wip
This feature can be enabled by -feature-wayland-text-input-v4-wip. It is disabled by default. TextInputManagerV4 is available in a compositor. zwp_text_input_v4 is available for QT_WAYLAND_TEXT_INPUT_PROTOCOL in a client It supports Hangul(Korean) with a qtvirtualkeyboard patchset (refs/changes/02/357902/3) It includes some workarounds for ibus because each ibus module has its own policy for focus-in/focus-out. enter/leave will synchronize with enable/disable and they will happen whenever focus-in/focus-out happen. Cursor/anchor positions are byte offsets. Surrounding text will be trimmed when it is over 4000 byte. For debugging, uses "qt.waylandcompositor.textinput" in a compositor side uses "qt.qpa.wayland.textinput" in a client side Tested on qtvirtualkeyboard and ibus TODO : * QTBUG-97248 - event:preedit_commit_mode is not implemented yet. Current preedit_commit_mode is 'commit'. * request:set_text_change_cause is not implemented. Task-number: QTBUG-94327 Change-Id: I72644893f40f30c4b03cd6a7d05483d12bde1070 Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
-rw-r--r--qt_cmdline.cmake1
-rw-r--r--src/3rdparty/protocol/qt_attribution.json15
-rw-r--r--src/3rdparty/protocol/text-input-unstable-v4-wip.xml467
-rw-r--r--src/client/CMakeLists.txt8
-rw-r--r--src/client/qwaylanddisplay.cpp47
-rw-r--r--src/client/qwaylanddisplay_p.h7
-rw-r--r--src/client/qwaylandinputcontext.cpp6
-rw-r--r--src/client/qwaylandinputdevice.cpp12
-rw-r--r--src/client/qwaylandintegration.cpp6
-rw-r--r--src/client/qwaylandtextinputinterface_p.h5
-rw-r--r--src/client/qwaylandtextinputv4.cpp409
-rw-r--r--src/client/qwaylandtextinputv4_p.h139
-rw-r--r--src/compositor/CMakeLists.txt10
-rw-r--r--src/compositor/compositor_api/qwaylandclient.h1
-rw-r--r--src/compositor/compositor_api/qwaylandcompositor.cpp3
-rw-r--r--src/compositor/compositor_api/qwaylandcompositor.h1
-rw-r--r--src/compositor/compositor_api/qwaylandcompositorquickextensions_p.h6
-rw-r--r--src/compositor/compositor_api/qwaylandinputmethodcontrol.cpp57
-rw-r--r--src/compositor/compositor_api/qwaylandinputmethodcontrol_p.h6
-rw-r--r--src/compositor/compositor_api/qwaylandquickitem.cpp11
-rw-r--r--src/compositor/compositor_api/qwaylandseat.cpp15
-rw-r--r--src/compositor/extensions/qwaylandtextinputmanagerv4.cpp143
-rw-r--r--src/compositor/extensions/qwaylandtextinputmanagerv4.h67
-rw-r--r--src/compositor/extensions/qwaylandtextinputmanagerv4_p.h72
-rw-r--r--src/compositor/extensions/qwaylandtextinputv4.cpp552
-rw-r--r--src/compositor/extensions/qwaylandtextinputv4.h88
-rw-r--r--src/compositor/extensions/qwaylandtextinputv4_p.h132
-rw-r--r--src/configure.cmake6
-rw-r--r--src/qt_cmdline.cmake1
-rw-r--r--src/shared/qwaylandinputmethodeventbuilder.cpp149
-rw-r--r--src/shared/qwaylandinputmethodeventbuilder_p.h7
-rw-r--r--sync.profile4
32 files changed, 2398 insertions, 55 deletions
diff --git a/qt_cmdline.cmake b/qt_cmdline.cmake
new file mode 100644
index 000000000..7151714fb
--- /dev/null
+++ b/qt_cmdline.cmake
@@ -0,0 +1 @@
+qt_commandline_subconfig(src)
diff --git a/src/3rdparty/protocol/qt_attribution.json b/src/3rdparty/protocol/qt_attribution.json
index 9b565a5a7..745fafe5e 100644
--- a/src/3rdparty/protocol/qt_attribution.json
+++ b/src/3rdparty/protocol/qt_attribution.json
@@ -119,6 +119,21 @@
},
{
+ "Id": "wayland-text-input-unstable-v4-wip",
+ "Name": "Wayland Text Input Protocol",
+ "QDocModule": "qtwaylandcompositor",
+ "QtUsage": "Used in the Qt Wayland Compositor, and the Qt Wayland platform plugin.",
+ "Files": "text-input-unstable-v4-wip.xml",
+ "Description": "Adds support for compositors to act as input methods and send text to applications.",
+ "Homepage": "https://wayland.freedesktop.org",
+ "Version": "unstable v4, WIP",
+ "LicenseId": "HPND",
+ "License": "HPND License",
+ "LicenseFile": "HPND_LICENSE.txt",
+ "Copyright": "Copyright © 2012, 2013 Intel Corporation\nCopyright © 2015, 2016 Jan Arne Petersen\nCopyright © 2017, 2018 Red Hat, Inc.\nCopyright © 2018 Purism SPC"
+ },
+
+ {
"Id": "wayland-viewporter-protocol",
"Name": "Wayland Viewporter Protocol",
"QDocModule": "qtwaylandcompositor",
diff --git a/src/3rdparty/protocol/text-input-unstable-v4-wip.xml b/src/3rdparty/protocol/text-input-unstable-v4-wip.xml
new file mode 100644
index 000000000..1041e6f74
--- /dev/null
+++ b/src/3rdparty/protocol/text-input-unstable-v4-wip.xml
@@ -0,0 +1,467 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<protocol name="text_input_unstable_v4_wip">
+ <copyright>
+ Copyright © 2012, 2013 Intel Corporation
+ Copyright © 2015, 2016 Jan Arne Petersen
+ Copyright © 2017, 2018 Red Hat, Inc.
+ Copyright © 2018 Purism SPC
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+ </copyright>
+
+ <description summary="Protocol for composing text">
+ This protocol allows compositors to act as input methods and to send text
+ to applications. A text input object is used to manage state of what are
+ typically text entry fields in the application.
+
+ This document adheres to the RFC 2119 when using words like "must",
+ "should", "may", etc.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible changes
+ may be added together with the corresponding interface version bump.
+ Backward incompatible changes are done by bumping the version number in
+ the protocol and interface names and resetting the interface version.
+ Once the protocol is to be declared stable, the 'z' prefix and the
+ version number in the protocol and interface names are removed and the
+ interface version number is reset.
+ </description>
+
+ <interface name="zwp_text_input_v4" version="1">
+ <description summary="text input">
+ The zwp_text_input_v4 interface represents text input and input methods
+ associated with a seat. It provides enter/leave events to follow the
+ text input focus for a seat.
+
+ Requests are used to enable/disable the text-input object and set
+ state information like surrounding and selected text or the content type.
+ The information about the entered text is sent to the text-input object
+ via the preedit_string and commit_string events.
+
+ Text is valid UTF-8 encoded, indices and lengths are in bytes. Indices
+ must not point to middle bytes inside a code point: they must either
+ point to the first byte of a code point or to the end of the buffer.
+ Lengths must be measured between two valid indices.
+
+ Focus moving throughout surfaces will result in the emission of
+ zwp_text_input_v4.enter and zwp_text_input_v4.leave events. The focused
+ surface must commit zwp_text_input_v4.enable and
+ zwp_text_input_v4.disable requests as the keyboard focus moves across
+ editable and non-editable elements of the UI. Those two requests are not
+ expected to be paired with each other, the compositor must be able to
+ handle consecutive series of the same request.
+
+ State is sent by the state requests (set_surrounding_text,
+ set_content_type and set_cursor_rectangle) and a commit request. After an
+ enter event or disable request all state information is invalidated and
+ needs to be resent by the client.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="Destroy the wp_text_input">
+ Destroy the wp_text_input object. Also disables all surfaces enabled
+ through this wp_text_input object.
+ </description>
+ </request>
+
+ <request name="enable">
+ <description summary="Request text input to be enabled">
+ Requests text input on the surface previously obtained from the enter
+ event.
+
+ This request must be issued every time the active text input changes
+ to a new one, including within the current surface. Use
+ zwp_text_input_v4.disable when there is no longer any input focus on
+ the current surface.
+
+ This request resets all state associated with previous enable, disable,
+ set_surrounding_text, set_text_change_cause, set_content_type, and
+ set_cursor_rectangle requests, as well as the state associated with
+ preedit_string, commit_string, and delete_surrounding_text events.
+
+ The set_surrounding_text, set_content_type and set_cursor_rectangle
+ requests must follow if the text input supports the necessary
+ functionality.
+
+ State set with this request is double-buffered. It will get applied on
+ the next zwp_text_input_v4.commit request, and stay valid until the
+ next committed enable or disable request.
+
+ The changes must be applied by the compositor after issuing a
+ zwp_text_input_v4.commit request.
+ </description>
+ </request>
+
+ <request name="disable">
+ <description summary="Disable text input on a surface">
+ Explicitly disable text input on the current surface (typically when
+ there is no focus on any text entry inside the surface).
+
+ State set with this request is double-buffered. It will get applied on
+ the next zwp_text_input_v4.commit request.
+ </description>
+ </request>
+
+ <request name="set_surrounding_text">
+ <description summary="sets the surrounding text">
+ Sets the surrounding plain text around the input, excluding the preedit
+ text.
+
+ The client should notify the compositor of any changes in any of the
+ values carried with this request, including changes caused by handling
+ incoming text-input events as well as changes caused by other
+ mechanisms like keyboard typing.
+
+ If the client is unaware of the text around the cursor, it should not
+ issue this request, to signify lack of support to the compositor.
+
+ Text is UTF-8 encoded, and should include the cursor position, the
+ complete selection and additional characters before and after them.
+ There is a maximum length of wayland messages, so text can not be
+ longer than 4000 bytes.
+
+ Cursor is the byte offset of the cursor within text buffer.
+
+ Anchor is the byte offset of the selection anchor within text buffer.
+ If there is no selected text, anchor is the same as cursor.
+
+ If any preedit text is present, it is replaced with a cursor for the
+ purpose of this event.
+
+ Values set with this request are double-buffered. They will get applied
+ on the next zwp_text_input_v4.commit request, and stay valid until the
+ next committed enable or disable request.
+
+ The initial state for affected fields is empty, meaning that the text
+ input does not support sending surrounding text. If the empty values
+ get applied, subsequent attempts to change them may have no effect.
+ </description>
+ <arg name="text" type="string"/>
+ <arg name="cursor" type="int"/>
+ <arg name="anchor" type="int"/>
+ </request>
+
+ <enum name="change_cause">
+ <description summary="text change reason">
+ Reason for the change of surrounding text or cursor posision.
+ </description>
+ <entry name="input_method" value="0" summary="input method caused the change"/>
+ <entry name="other" value="1" summary="something else than the input method caused the change"/>
+ </enum>
+
+ <request name="set_text_change_cause">
+ <description summary="indicates the cause of surrounding text change">
+ Tells the compositor why the text surrounding the cursor changed.
+
+ Whenever the client detects an external change in text, cursor, or
+ anchor posision, it must issue this request to the compositor. This
+ request is intended to give the input method a chance to update the
+ preedit text in an appropriate way, e.g. by removing it when the user
+ starts typing with a keyboard.
+
+ cause describes the source of the change.
+
+ The value set with this request is double-buffered. It must be applied
+ and reset to initial at the next zwp_text_input_v4.commit request.
+
+ The initial value of cause is input_method.
+ </description>
+ <arg name="cause" type="uint" enum="change_cause"/>
+ </request>
+
+ <enum name="content_hint" bitfield="true">
+ <description summary="content hint">
+ Content hint is a bitmask to allow to modify the behavior of the text
+ input.
+ </description>
+ <entry name="none" value="0x0" summary="no special behavior"/>
+ <entry name="completion" value="0x1" summary="suggest word completions"/>
+ <entry name="spellcheck" value="0x2" summary="suggest word corrections"/>
+ <entry name="auto_capitalization" value="0x4" summary="switch to uppercase letters at the start of a sentence"/>
+ <entry name="lowercase" value="0x8" summary="prefer lowercase letters"/>
+ <entry name="uppercase" value="0x10" summary="prefer uppercase letters"/>
+ <entry name="titlecase" value="0x20" summary="prefer casing for titles and headings (can be language dependent)"/>
+ <entry name="hidden_text" value="0x40" summary="characters should be hidden"/>
+ <entry name="sensitive_data" value="0x80" summary="typed text should not be stored"/>
+ <entry name="latin" value="0x100" summary="just Latin characters should be entered"/>
+ <entry name="multiline" value="0x200" summary="the text input is multiline"/>
+ </enum>
+
+ <enum name="content_purpose">
+ <description summary="content purpose">
+ The content purpose 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.
+ </description>
+ <entry name="normal" value="0" summary="default input, allowing all characters"/>
+ <entry name="alpha" value="1" summary="allow only alphabetic characters"/>
+ <entry name="digits" value="2" summary="allow only digits"/>
+ <entry name="number" value="3" summary="input a number (including decimal separator and sign)"/>
+ <entry name="phone" value="4" summary="input a phone number"/>
+ <entry name="url" value="5" summary="input an URL"/>
+ <entry name="email" value="6" summary="input an email address"/>
+ <entry name="name" value="7" summary="input a name of a person"/>
+ <entry name="password" value="8" summary="input a password (combine with sensitive_data hint)"/>
+ <entry name="pin" value="9" summary="input is a numeric password (combine with sensitive_data hint)"/>
+ <entry name="date" value="10" summary="input a date"/>
+ <entry name="time" value="11" summary="input a time"/>
+ <entry name="datetime" value="12" summary="input a date and time"/>
+ <entry name="terminal" value="13" summary="input for a terminal"/>
+ </enum>
+
+ <request name="set_content_type">
+ <description summary="set content purpose and hint">
+ Sets the content purpose and content hint. While the purpose is the
+ basic purpose of an input field, the hint flags allow to modify some of
+ the behavior.
+
+ Values set with this request are double-buffered. They will get applied
+ on the next zwp_text_input_v4.commit request.
+ Subsequent attempts to update them may have no effect. The values
+ remain valid until the next committed enable or disable request.
+
+ The initial value for hint is none, and the initial value for purpose
+ is normal.
+ </description>
+ <arg name="hint" type="uint" enum="content_hint"/>
+ <arg name="purpose" type="uint" enum="content_purpose"/>
+ </request>
+
+ <request name="set_cursor_rectangle">
+ <description summary="set cursor position">
+ Marks an area around the cursor as a x, y, width, height rectangle in
+ surface local coordinates.
+
+ Allows the compositor to put a window with word suggestions near the
+ cursor, without obstructing the text being input.
+
+ If the client is unaware of the position of edited text, it should not
+ issue this request, to signify lack of support to the compositor.
+
+ Values set with this request are double-buffered. They will get applied
+ on the next zwp_text_input_v4.commit request, and stay valid until the
+ next committed enable or disable request.
+
+ The initial values describing a cursor rectangle are empty. That means
+ the text input does not support describing the cursor area. If the
+ empty values get applied, subsequent attempts to change them may have
+ no effect.
+ </description>
+ <arg name="x" type="int"/>
+ <arg name="y" type="int"/>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </request>
+
+ <request name="commit">
+ <description summary="commit state">
+ Atomically applies state changes recently sent to the compositor.
+
+ The commit request establishes and updates the state of the client, and
+ must be issued after any changes to apply them.
+
+ Text input state (enabled status, content purpose, content hint,
+ surrounding text and change cause, cursor rectangle) is conceptually
+ double-buffered within the context of a text input, i.e. between a
+ committed enable request and the following committed enable or disable
+ request.
+
+ Protocol requests modify the pending state, as opposed to the current
+ state in use by the input method. A commit request atomically applies
+ all pending state, replacing the current state. After commit, the new
+ pending state is as documented for each related request.
+
+ Requests are applied in the order of arrival.
+
+ Neither current nor pending state are modified unless noted otherwise.
+
+ The compositor must count the number of commit requests coming from
+ each zwp_text_input_v4 object and use the count as the serial in done
+ events.
+ </description>
+ </request>
+
+ <event name="enter">
+ <description summary="enter event">
+ Notification that this seat's text-input focus is on a certain surface.
+
+ When the seat has the keyboard capability the text-input focus follows
+ the keyboard focus. This event sets the current surface for the
+ text-input object.
+ </description>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </event>
+
+ <event name="leave">
+ <description summary="leave event">
+ Notification that this seat's text-input focus is no longer on a
+ certain surface. The client should reset any preedit string previously
+ set.
+
+ The leave notification clears the current surface. It is sent before
+ the enter notification for the new focus.
+
+ When the seat has the keyboard capability the text-input focus follows
+ the keyboard focus.
+ </description>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </event>
+
+ <event name="preedit_string">
+ <description summary="pre-edit">
+ Notify when a new composing text (pre-edit) should be set at the
+ current cursor position. Any previously set composing text must be
+ removed. Any previously existing selected text must be removed.
+
+ The argument text contains the pre-edit string buffer.
+
+ The parameters cursor_begin and cursor_end are counted in bytes
+ relative to the beginning of the submitted text buffer. Cursor should
+ be hidden when both are equal to -1.
+
+ They could be represented by the client as a line if both values are
+ the same, or as a text highlight otherwise.
+
+ Values set with this event are double-buffered. They must be applied
+ and reset to initial on the next zwp_text_input_v4.done event.
+
+ The initial value of text is an empty string, and cursor_begin,
+ cursor_end and cursor_hidden are all 0.
+ </description>
+ <arg name="text" type="string" allow-null="true"/>
+ <arg name="cursor_begin" type="int"/>
+ <arg name="cursor_end" type="int"/>
+ </event>
+
+ <event name="commit_string">
+ <description summary="text commit">
+ Notify when text should be inserted into the editor widget. The text to
+ commit could be either just a single character after a key press or the
+ result of some composing (pre-edit).
+
+ Values set with this event are double-buffered. They must be applied
+ and reset to initial on the next zwp_text_input_v4.done event.
+
+ The initial value of text is an empty string.
+ </description>
+ <arg name="text" type="string" allow-null="true"/>
+ </event>
+
+ <event name="delete_surrounding_text">
+ <description summary="delete surrounding text">
+ Notify when the text around the current cursor position should be
+ deleted.
+
+ Before_length and after_length are the number of bytes before and after
+ the current cursor index (excluding the selection) to delete.
+
+ If a preedit text is present, in effect before_length is counted from
+ the beginning of it, and after_length from its end (see done event
+ sequence).
+
+ Values set with this event are double-buffered. They must be applied
+ and reset to initial on the next zwp_text_input_v4.done event.
+
+ The initial values of both before_length and after_length are 0.
+ </description>
+ <arg name="before_length" type="uint" summary="length of text before current cursor position"/>
+ <arg name="after_length" type="uint" summary="length of text after current cursor position"/>
+ </event>
+
+ <event name="done">
+ <description summary="apply changes">
+ Instruct the application to apply changes to state requested by the
+ preedit_string, commit_string and delete_surrounding_text events. The
+ state relating to these events is double-buffered, and each one
+ modifies the pending state. This event replaces the current state with
+ the pending state.
+
+ The application must proceed by evaluating the changes in the following
+ order:
+
+ 1. Replace existing preedit string with the cursor.
+ 2. Delete requested surrounding text.
+ 3. Insert commit string with the cursor at its end.
+ 4. Calculate surrounding text to send.
+ 5. Insert new preedit text in cursor position.
+ 6. Place cursor inside preedit text.
+
+ The serial number reflects the last state of the zwp_text_input_v4
+ object known to the compositor. The value of the serial argument must
+ be equal to the number of commit requests already issued on that object.
+ When the client receives a done event with a serial different than the
+ number of past commit requests, it must proceed as normal, except it
+ should not change the current state of the zwp_text_input_v4 object.
+ </description>
+ <arg name="serial" type="uint"/>
+ </event>
+
+ <enum name="commit_mode">
+ <description summary="focus commit mode">
+ Pre-edit commit mode when the focus widget or the cursor position
+ is changed.
+ </description>
+ <entry name="clear" value="0" summary="pre-edit text is cleared"/>
+ <entry name="commit" value="1" summary="pre-edit text is committed"/>
+ </enum>
+
+ <event name="preedit_commit_mode">
+ <description summary="pre-edit commit mode">
+ Specify how the visible preedit should be handled
+ when switching the focus widget or changing the cursor position,
+ whether to commit the preedit text or clear the preedit text.
+
+ This is usually used together with the preedit_string event.
+
+ The commit behavior is the same for focus switch and
+ cursor position change.
+
+ The parameter mode selects the desired behavior and
+ its value is one from the commit mode enum.
+ </description>
+ <arg name="mode" type="uint" enum="commit_mode"/>
+ </event>
+ </interface>
+
+ <interface name="zwp_text_input_manager_v4" version="1">
+ <description summary="text input manager">
+ A factory for text-input objects. This object is a global singleton.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="Destroy the wp_text_input_manager">
+ Destroy the wp_text_input_manager object.
+ </description>
+ </request>
+
+ <request name="get_text_input">
+ <description summary="create a new text input object">
+ Creates a new text-input object for a given seat.
+ </description>
+ <arg name="id" type="new_id" interface="zwp_text_input_v4"/>
+ <arg name="seat" type="object" interface="wl_seat"/>
+ </request>
+ </interface>
+</protocol>
diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt
index 79a070332..c7264d677 100644
--- a/src/client/CMakeLists.txt
+++ b/src/client/CMakeLists.txt
@@ -76,6 +76,7 @@ qt6_generate_wayland_protocol_client_sources(WaylandClient
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/pointer-gestures-unstable-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/tablet-unstable-v2.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/text-input-unstable-v2.xml
+ ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/text-input-unstable-v4-wip.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/wayland.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/wp-primary-selection-unstable-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/xdg-output-unstable-v1.xml
@@ -105,6 +106,13 @@ qt6_generate_wayland_protocol_client_sources(WaylandClient
# )
# special case end
+qt_internal_extend_target(WaylandClient CONDITION QT_FEATURE_wayland_text_input_v4_wip
+ SOURCES
+ qwaylandtextinputv4.cpp qwaylandtextinputv4_p.h
+ DEFINES
+ QT_WAYLAND_TEXT_INPUT_V4_WIP=1
+)
+
qt_internal_extend_target(WaylandClient CONDITION QT_FEATURE_clipboard
SOURCES
qwaylandclipboard.cpp qwaylandclipboard_p.h
diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp
index 523de1f3c..8043115af 100644
--- a/src/client/qwaylanddisplay.cpp
+++ b/src/client/qwaylanddisplay.cpp
@@ -61,6 +61,9 @@
#endif
#include "qwaylandhardwareintegration_p.h"
#include "qwaylandtextinputv2_p.h"
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+#include "qwaylandtextinputv4_p.h"
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
#include "qwaylandinputcontext_p.h"
#include "qwaylandinputmethodcontext_p.h"
@@ -76,6 +79,7 @@
#include "qwaylandqtkey_p.h"
#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
+#include <QtWaylandClient/private/qwayland-text-input-unstable-v4-wip.h>
#include <QtWaylandClient/private/qwayland-wp-primary-selection-unstable-v1.h>
#include <QtWaylandClient/private/qwayland-qt-text-input-method-unstable-v1.h>
@@ -450,6 +454,10 @@ void QWaylandDisplay::checkTextInputProtocol()
<< QLatin1String(QtWayland::zwp_text_input_v2::interface()->name);
timps << QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)
<< QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name);
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ tips << QLatin1String(QtWayland::zwp_text_input_v4::interface()->name);
+ timps << QLatin1String(QtWayland::zwp_text_input_manager_v4::interface()->name);
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
QString tiProtocols = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_TEXT_INPUT_PROTOCOL"));
qCDebug(lcQpaWayland) << "QT_WAYLAND_TEXT_INPUT_PROTOCOL=" << tiProtocols;
@@ -528,7 +536,10 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
&& (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
qCDebug(lcQpaWayland) << "text input: register qt_text_input_method_manager_v1";
if (mTextInputManagerIndex < INT_MAX) {
- mTextInputManager.reset();
+ mTextInputManagerv2.reset();
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ mTextInputManagerv4.reset();
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
inputDevice->setTextInput(nullptr);
}
@@ -543,15 +554,35 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v2";
if (mTextInputManagerIndex < INT_MAX) {
mTextInputMethodManager.reset();
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ mTextInputManagerv4.reset();
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
inputDevice->setTextInputMethod(nullptr);
}
- mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1));
+ mTextInputManagerv2.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1));
for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
- inputDevice->setTextInput(new QWaylandTextInputv2(this, mTextInputManager->get_text_input(inputDevice->wl_seat())));
+ inputDevice->setTextInput(new QWaylandTextInputv2(this, mTextInputManagerv2->get_text_input(inputDevice->wl_seat())));
mWaylandIntegration->reconfigureInputContext();
mTextInputManagerIndex = mTextInputManagerList.indexOf(interface);
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v4::interface()->name)
+ && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
+ qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v4";
+ if (mTextInputManagerIndex < INT_MAX) {
+ mTextInputMethodManager.reset();
+ mTextInputManagerv2.reset();
+ for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
+ inputDevice->setTextInputMethod(nullptr);
+ }
+
+ mTextInputManagerv4.reset(new QtWayland::zwp_text_input_manager_v4(registry, id, 1));
+ for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
+ inputDevice->setTextInput(new QWaylandTextInputv4(this, mTextInputManagerv4->get_text_input(inputDevice->wl_seat())));
+ mWaylandIntegration->reconfigureInputContext();
+ mTextInputManagerIndex = mTextInputManagerList.indexOf(interface);
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
} else if (interface == QLatin1String(QWaylandHardwareIntegration::interface()->name)) {
bool disableHardwareIntegration = qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_HW_INTEGRATION");
if (!disableHardwareIntegration) {
@@ -599,11 +630,19 @@ void QWaylandDisplay::registry_global_remove(uint32_t id)
}
}
if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)) {
- mTextInputManager.reset();
+ mTextInputManagerv2.reset();
+ for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
+ inputDevice->setTextInput(nullptr);
+ mWaylandIntegration->reconfigureInputContext();
+ }
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v4::interface()->name)) {
+ mTextInputManagerv4.reset();
for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
inputDevice->setTextInput(nullptr);
mWaylandIntegration->reconfigureInputContext();
}
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
if (global.interface == QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)) {
mTextInputMethodManager.reset();
for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h
index b58099ee8..8be911188 100644
--- a/src/client/qwaylanddisplay_p.h
+++ b/src/client/qwaylanddisplay_p.h
@@ -83,6 +83,7 @@ class QPlatformPlaceholderScreen;
namespace QtWayland {
class qt_surface_extension;
class zwp_text_input_manager_v2;
+ class zwp_text_input_manager_v4;
class qt_text_input_method_manager_v1;
}
@@ -171,7 +172,8 @@ public:
QWaylandPointerGestures *pointerGestures() const { return mPointerGestures.data(); }
QWaylandTouchExtension *touchExtension() const { return mTouchExtension.data(); }
QtWayland::qt_text_input_method_manager_v1 *textInputMethodManager() const { return mTextInputMethodManager.data(); }
- QtWayland::zwp_text_input_manager_v2 *textInputManager() const { return mTextInputManager.data(); }
+ QtWayland::zwp_text_input_manager_v2 *textInputManagerv2() const { return mTextInputManagerv2.data(); }
+ QtWayland::zwp_text_input_manager_v4 *textInputManagerv4() const { return mTextInputManagerv4.data(); }
QWaylandHardwareIntegration *hardwareIntegration() const { return mHardwareIntegration.data(); }
QWaylandXdgOutputManagerV1 *xdgOutputManager() const { return mXdgOutputManager.data(); }
@@ -280,7 +282,8 @@ private:
QScopedPointer<QWaylandPrimarySelectionDeviceManagerV1> mPrimarySelectionManager;
#endif
QScopedPointer<QtWayland::qt_text_input_method_manager_v1> mTextInputMethodManager;
- QScopedPointer<QtWayland::zwp_text_input_manager_v2> mTextInputManager;
+ QScopedPointer<QtWayland::zwp_text_input_manager_v2> mTextInputManagerv2;
+ QScopedPointer<QtWayland::zwp_text_input_manager_v4> mTextInputManagerv4;
QScopedPointer<QWaylandHardwareIntegration> mHardwareIntegration;
QScopedPointer<QWaylandXdgOutputManagerV1> mXdgOutputManager;
int mFd = -1;
diff --git a/src/client/qwaylandinputcontext.cpp b/src/client/qwaylandinputcontext.cpp
index 9ac1c3df6..c59485277 100644
--- a/src/client/qwaylandinputcontext.cpp
+++ b/src/client/qwaylandinputcontext.cpp
@@ -70,7 +70,11 @@ QWaylandInputContext::~QWaylandInputContext()
bool QWaylandInputContext::isValid() const
{
- return mDisplay->textInputManager() != nullptr;
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ return mDisplay->textInputManagerv2() != nullptr || mDisplay->textInputManagerv4() != nullptr;
+#else // QT_WAYLAND_TEXT_INPUT_V4_WIP
+ return mDisplay->textInputManagerv2() != nullptr;
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
}
void QWaylandInputContext::reset()
diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp
index 5fbdd2418..7f503ee1e 100644
--- a/src/client/qwaylandinputdevice.cpp
+++ b/src/client/qwaylandinputdevice.cpp
@@ -58,6 +58,9 @@
#include "qwaylanddisplay_p.h"
#include "qwaylandshmbackingstore_p.h"
#include "qwaylandtextinputv2_p.h"
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+#include "qwaylandtextinputv4_p.h"
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
#include "qwaylandtextinputinterface_p.h"
#include "qwaylandinputcontext_p.h"
#include "qwaylandinputmethodcontext_p.h"
@@ -421,8 +424,13 @@ QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version,
setPrimarySelectionDevice(psm->createDevice(this));
#endif
- if (mQDisplay->textInputManager())
- mTextInput.reset(new QWaylandTextInputv2(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat())));
+ if (mQDisplay->textInputManagerv2())
+ mTextInput.reset(new QWaylandTextInputv2(mQDisplay, mQDisplay->textInputManagerv2()->get_text_input(wl_seat())));
+
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ if (mQDisplay->textInputManagerv4())
+ mTextInput.reset(new QWaylandTextInputv4(mQDisplay, mQDisplay->textInputManagerv4()->get_text_input(wl_seat())));
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
if (mQDisplay->textInputMethodManager())
mTextInputMethod.reset(new QWaylandTextInputMethod(mQDisplay, mQDisplay->textInputMethodManager()->get_text_input_method(wl_seat())));
diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp
index 67af87cb2..a1da5ccff 100644
--- a/src/client/qwaylandintegration.cpp
+++ b/src/client/qwaylandintegration.cpp
@@ -497,7 +497,11 @@ void QWaylandIntegration::reconfigureInputContext()
if (requested.isNull()) {
if (mDisplay->textInputMethodManager() != nullptr)
mInputContext.reset(new QWaylandInputMethodContext(mDisplay.data()));
- else if (mDisplay->textInputManager() != nullptr)
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ else if (mDisplay->textInputManagerv2() != nullptr || mDisplay->textInputManagerv4() != nullptr)
+#else // QT_WAYLAND_TEXT_INPUT_V4_WIP
+ else if (mDisplay->textInputManagerv2() != nullptr)
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
mInputContext.reset(new QWaylandInputContext(mDisplay.data()));
} else {
mInputContext.reset(QPlatformInputContextFactory::create(requested));
diff --git a/src/client/qwaylandtextinputinterface_p.h b/src/client/qwaylandtextinputinterface_p.h
index d918f0835..2b0f381b4 100644
--- a/src/client/qwaylandtextinputinterface_p.h
+++ b/src/client/qwaylandtextinputinterface_p.h
@@ -39,6 +39,7 @@
#ifndef QWAYLANDTEXTINPUTINTERFACE_P_H
#define QWAYLANDTEXTINPUTINTERFACE_P_H
+
//
// W A R N I N G
// -------------
@@ -68,8 +69,8 @@ public:
virtual void disableSurface(::wl_surface *surface) = 0;
virtual void enableSurface(::wl_surface *surface) = 0;
virtual void updateState(Qt::InputMethodQueries queries, uint32_t flags) = 0;
- virtual void showInputPanel() = 0;
- virtual void hideInputPanel() = 0;
+ virtual void showInputPanel() {}
+ virtual void hideInputPanel() {}
virtual bool isInputPanelVisible() const = 0;
virtual QRectF keyboardRect() const = 0;
virtual QLocale locale() const = 0;
diff --git a/src/client/qwaylandtextinputv4.cpp b/src/client/qwaylandtextinputv4.cpp
new file mode 100644
index 000000000..5cecaf7a4
--- /dev/null
+++ b/src/client/qwaylandtextinputv4.cpp
@@ -0,0 +1,409 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwaylandtextinputv4_p.h"
+
+#include "qwaylandwindow_p.h"
+#include "qwaylandinputmethodeventbuilder_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qwindow.h>
+#include <QTextCharFormat>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qLcQpaWaylandTextInput, "qt.qpa.wayland.textinput")
+
+namespace QtWaylandClient {
+
+QWaylandTextInputv4::QWaylandTextInputv4(QWaylandDisplay *display,
+ struct ::zwp_text_input_v4 *text_input)
+ : QtWayland::zwp_text_input_v4(text_input)
+ , m_display(display)
+{
+
+}
+
+QWaylandTextInputv4::~QWaylandTextInputv4()
+{
+}
+
+namespace {
+const Qt::InputMethodQueries supportedQueries = Qt::ImEnabled |
+ Qt::ImSurroundingText |
+ Qt::ImCursorPosition |
+ Qt::ImAnchorPosition |
+ Qt::ImHints |
+ Qt::ImCursorRectangle;
+}
+
+void QWaylandTextInputv4::zwp_text_input_v4_enter(struct ::wl_surface *surface)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO;
+
+ m_surface = surface;
+
+ m_pendingPreeditString.clear();
+ m_pendingCommitString.clear();
+ m_pendingDeleteBeforeText = 0;
+ m_pendingDeleteAfterText = 0;
+
+ enable();
+ updateState(supportedQueries, update_state_enter);
+}
+
+void QWaylandTextInputv4::zwp_text_input_v4_leave(struct ::wl_surface *surface)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO;
+
+ if (m_surface != surface) {
+ qCWarning(qLcQpaWaylandTextInput()) << Q_FUNC_INFO << "Got leave event for surface" << surface << "focused surface" << m_surface;
+ return;
+ }
+
+ // QTBUG-97248: check commit_mode
+ // Currently text-input-unstable-v4-wip is implemented with preedit_commit_mode
+ // 'commit'
+
+ m_currentPreeditString.clear();
+
+ m_surface = nullptr;
+ m_currentSerial = 0U;
+
+ disable();
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "Done";
+}
+
+void QWaylandTextInputv4::zwp_text_input_v4_preedit_string(const QString &text, int32_t cursorBegin, int32_t cursorEnd)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << text << cursorBegin << cursorEnd;
+
+ if (!QGuiApplication::focusObject())
+ return;
+
+ m_pendingPreeditString.text = text;
+ m_pendingPreeditString.cursorBegin = cursorBegin;
+ m_pendingPreeditString.cursorEnd = cursorEnd;
+}
+
+void QWaylandTextInputv4::zwp_text_input_v4_commit_string(const QString &text)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << text;
+
+ if (!QGuiApplication::focusObject())
+ return;
+
+ m_pendingCommitString = text;
+}
+
+void QWaylandTextInputv4::zwp_text_input_v4_delete_surrounding_text(uint32_t beforeText, uint32_t afterText)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << beforeText << afterText;
+
+ if (!QGuiApplication::focusObject())
+ return;
+
+ m_pendingDeleteBeforeText = QWaylandInputMethodEventBuilder::indexFromWayland(m_surroundingText, beforeText);
+ m_pendingDeleteAfterText = QWaylandInputMethodEventBuilder::indexFromWayland(m_surroundingText, afterText);
+}
+
+void QWaylandTextInputv4::zwp_text_input_v4_done(uint32_t serial)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "with serial" << serial << m_currentSerial;
+
+ // This is a case of double click.
+ // text_input_v4 will ignore this done signal and just keep the selection of the clicked word.
+ if (m_cursorPos != m_anchorPos && (m_pendingDeleteBeforeText != 0 || m_pendingDeleteAfterText != 0)) {
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "Ignore done";
+ m_pendingDeleteBeforeText = 0;
+ m_pendingDeleteAfterText = 0;
+ m_pendingPreeditString.clear();
+ m_pendingCommitString.clear();
+ return;
+ }
+
+ QObject *focusObject = QGuiApplication::focusObject();
+ if (!focusObject)
+ return;
+
+ if (!m_surface) {
+ qCWarning(qLcQpaWaylandTextInput) << Q_FUNC_INFO << serial << "Surface is not enabled yet";
+ return;
+ }
+
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "PREEDIT" << m_pendingPreeditString.text << m_pendingPreeditString.cursorBegin;
+
+ QList<QInputMethodEvent::Attribute> attributes;
+ {
+ if (m_pendingPreeditString.cursorBegin != -1 ||
+ m_pendingPreeditString.cursorEnd != -1) {
+ // Current supported cursor shape is just line.
+ // It means, cursorEnd and cursorBegin are the same.
+ QInputMethodEvent::Attribute attribute1(QInputMethodEvent::Cursor,
+ m_pendingPreeditString.text.length(),
+ 1);
+ attributes.append(attribute1);
+ }
+
+ // only use single underline style for now
+ QTextCharFormat format;
+ format.setFontUnderline(true);
+ format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
+ QInputMethodEvent::Attribute attribute2(QInputMethodEvent::TextFormat,
+ 0,
+ m_pendingPreeditString.text.length(), format);
+ attributes.append(attribute2);
+ }
+ QInputMethodEvent event(m_pendingPreeditString.text, attributes);
+
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "DELETE" << m_pendingDeleteBeforeText << m_pendingDeleteAfterText;
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "COMMIT" << m_pendingCommitString;
+
+ // A workaround for reselection
+ // It will disable redundant commit after reselection
+ if (m_pendingDeleteBeforeText != 0 || m_pendingDeleteAfterText != 0)
+ m_condReselection = true;
+
+ event.setCommitString(m_pendingCommitString,
+ -m_pendingDeleteBeforeText,
+ m_pendingDeleteBeforeText + m_pendingDeleteAfterText);
+ m_currentPreeditString = m_pendingPreeditString;
+ m_pendingPreeditString.clear();
+ m_pendingCommitString.clear();
+ m_pendingDeleteBeforeText = 0;
+ m_pendingDeleteAfterText = 0;
+ QCoreApplication::sendEvent(focusObject, &event);
+
+ if (serial == m_currentSerial)
+ updateState(supportedQueries, update_state_full);
+}
+
+void QWaylandTextInputv4::reset()
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO;
+
+ m_pendingPreeditString.clear();
+}
+
+void QWaylandTextInputv4::enableSurface(::wl_surface *)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO;
+}
+
+void QWaylandTextInputv4::disableSurface(::wl_surface *surface)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO;
+
+ if (m_surface != surface) {
+ qCWarning(qLcQpaWaylandTextInput()) << Q_FUNC_INFO << "for surface" << surface << "focused surface" << m_surface;
+ return;
+ }
+}
+
+void QWaylandTextInputv4::commit()
+{
+ m_currentSerial = (m_currentSerial < UINT_MAX) ? m_currentSerial + 1U: 0U;
+
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "with serial" << m_currentSerial;
+ QtWayland::zwp_text_input_v4::commit();
+}
+
+void QWaylandTextInputv4::updateState(Qt::InputMethodQueries queries, uint32_t flags)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << queries << flags;
+
+ if (!QGuiApplication::focusObject())
+ return;
+
+ if (!QGuiApplication::focusWindow() || !QGuiApplication::focusWindow()->handle())
+ return;
+
+ auto *window = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle());
+ auto *surface = window->wlSurface();
+ if (!surface || (surface != m_surface))
+ return;
+
+ queries &= supportedQueries;
+ bool needsCommit = false;
+
+ QInputMethodQueryEvent event(queries);
+ QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event);
+
+ // For some reason, a query for Qt::ImSurroundingText gives an empty string even though it is not.
+ if (!(queries & Qt::ImSurroundingText) && event.value(Qt::ImSurroundingText).toString().isEmpty()) {
+ return;
+ }
+
+ if (queries & Qt::ImCursorRectangle) {
+ const QRect &cRect = event.value(Qt::ImCursorRectangle).toRect();
+ const QRect &windowRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect);
+ const QMargins margins = window->frameMargins();
+ const QRect &surfaceRect = windowRect.translated(margins.left(), margins.top());
+ if (surfaceRect != m_cursorRect) {
+ set_cursor_rectangle(surfaceRect.x(), surfaceRect.y(), surfaceRect.width(), surfaceRect.height());
+ m_cursorRect = surfaceRect;
+ needsCommit = true;
+ }
+ }
+
+ if ((queries & Qt::ImSurroundingText) || (queries & Qt::ImCursorPosition) || (queries & Qt::ImAnchorPosition)) {
+ QString text = event.value(Qt::ImSurroundingText).toString();
+ int cursor = event.value(Qt::ImCursorPosition).toInt();
+ int anchor = event.value(Qt::ImAnchorPosition).toInt();
+
+ qCDebug(qLcQpaWaylandTextInput) << "Orginal surrounding_text from InputMethodQuery: " << text << cursor << anchor;
+
+ // Make sure text is not too big
+ // surround_text cannot exceed 4000byte in wayland protocol
+ // The worst case will be supposed here.
+ const int MAX_MESSAGE_SIZE = 4000;
+
+ if (text.toUtf8().size() > MAX_MESSAGE_SIZE) {
+ const int selectionStart = QWaylandInputMethodEventBuilder::indexToWayland(text, qMin(cursor, anchor));
+ const int selectionEnd = QWaylandInputMethodEventBuilder::indexToWayland(text, qMax(cursor, anchor));
+ const int selectionLength = selectionEnd - selectionStart;
+ // If selection is bigger than 4000 byte, it is fixed to 4000 byte.
+ // anchor will be moved in the 4000 byte boundary.
+ if (selectionLength > MAX_MESSAGE_SIZE) {
+ if (anchor > cursor) {
+ const int length = MAX_MESSAGE_SIZE;
+ anchor = QWaylandInputMethodEventBuilder::trimmedIndexFromWayland(text, length, cursor);
+ anchor -= cursor;
+ text = text.mid(cursor, anchor);
+ cursor = 0;
+ } else {
+ const int length = -MAX_MESSAGE_SIZE;
+ anchor = QWaylandInputMethodEventBuilder::trimmedIndexFromWayland(text, length, cursor);
+ cursor -= anchor;
+ text = text.mid(anchor, cursor);
+ anchor = 0;
+ }
+ } else {
+ const int offset = (MAX_MESSAGE_SIZE - selectionLength) / 2;
+
+ int textStart = QWaylandInputMethodEventBuilder::trimmedIndexFromWayland(text, -offset, qMin(cursor, anchor));
+ int textEnd = QWaylandInputMethodEventBuilder::trimmedIndexFromWayland(text, MAX_MESSAGE_SIZE, textStart);
+
+ anchor -= textStart;
+ cursor -= textStart;
+ text = text.mid(textStart, textEnd - textStart);
+ }
+ }
+ qCDebug(qLcQpaWaylandTextInput) << "Modified surrounding_text: " << text << cursor << anchor;
+
+ const int cursorPos = QWaylandInputMethodEventBuilder::indexToWayland(text, cursor);
+ const int anchorPos = QWaylandInputMethodEventBuilder::indexToWayland(text, anchor);
+
+ if (m_surroundingText != text || m_cursorPos != cursorPos || m_anchorPos != anchorPos) {
+ qCDebug(qLcQpaWaylandTextInput) << "Current surrounding_text: " << m_surroundingText << m_cursorPos << m_anchorPos;
+ qCDebug(qLcQpaWaylandTextInput) << "New surrounding_text: " << text << cursorPos << anchorPos;
+
+ set_surrounding_text(text, cursorPos, anchorPos);
+
+ // A workaround in the case of reselection
+ // It will work when re-clicking a preedit text
+ if (m_condReselection) {
+ qCDebug(qLcQpaWaylandTextInput) << "\"commit\" is disabled when Reselection by changing focus";
+ m_condReselection = false;
+ needsCommit = false;
+
+ }
+
+ m_surroundingText = text;
+ m_cursorPos = cursorPos;
+ m_anchorPos = anchorPos;
+ m_cursor = cursor;
+ }
+ }
+
+ if (queries & Qt::ImHints) {
+ QWaylandInputMethodContentType contentType = QWaylandInputMethodContentType::convertV4(static_cast<Qt::InputMethodHints>(event.value(Qt::ImHints).toInt()));
+ qCDebug(qLcQpaWaylandTextInput) << m_contentHint << contentType.hint;
+ qCDebug(qLcQpaWaylandTextInput) << m_contentPurpose << contentType.purpose;
+
+ if (m_contentHint != contentType.hint || m_contentPurpose != contentType.purpose) {
+ qCDebug(qLcQpaWaylandTextInput) << "set_content_type: " << contentType.hint << contentType.purpose;
+ set_content_type(contentType.hint, contentType.purpose);
+
+ m_contentHint = contentType.hint;
+ m_contentPurpose = contentType.purpose;
+ needsCommit = true;
+ }
+ }
+
+ if (needsCommit
+ && (flags == update_state_change || flags == update_state_enter))
+ commit();
+}
+
+void QWaylandTextInputv4::setCursorInsidePreedit(int cursor)
+{
+ Q_UNUSED(cursor);
+ qCWarning(qLcQpaWaylandTextInput) << "QWaylandTextInputV4: Input protocol \"text-input-unstable-v4-wip\" does not support setting cursor inside preedit. Use qt-text-input-method-unstable-v1 instead for full support of Qt input method events.";
+}
+
+bool QWaylandTextInputv4::isInputPanelVisible() const
+{
+ qCWarning(qLcQpaWaylandTextInput) << "QWaylandTextInputV4: Input protocol \"text-input-unstable-v4-wip\" does not support querying input method visibility. Use qt-text-input-method-unstable-v1 instead for full support of Qt input method events.";
+ return false;
+}
+
+QRectF QWaylandTextInputv4::keyboardRect() const
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO;
+ return m_cursorRect;
+}
+
+QLocale QWaylandTextInputv4::locale() const
+{
+ qCWarning(qLcQpaWaylandTextInput) << "QWaylandTextInputV4: Input protocol \"text-input-unstable-v4-wip\" does not support querying input language. Use qt-text-input-method-unstable-v1 instead for full support of Qt input method events.";
+ return QLocale();
+}
+
+Qt::LayoutDirection QWaylandTextInputv4::inputDirection() const
+{
+ qCWarning(qLcQpaWaylandTextInput) << "QWaylandTextInputV4: Input protocol \"text-input-unstable-v4-wip\" does not support querying input direction. Use qt-text-input-method-unstable-v1 instead for full support of Qt input method events.";
+ return Qt::LeftToRight;
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/client/qwaylandtextinputv4_p.h b/src/client/qwaylandtextinputv4_p.h
new file mode 100644
index 000000000..d273cef27
--- /dev/null
+++ b/src/client/qwaylandtextinputv4_p.h
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDTEXTINPUTV4_P_H
+#define QWAYLANDTEXTINPUTV4_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qwaylandtextinputinterface_p.h"
+#include <QtWaylandClient/private/qwayland-text-input-unstable-v4-wip.h>
+#include <qwaylandinputmethodeventbuilder_p.h>
+
+struct wl_callback;
+struct wl_callback_listener;
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qLcQpaWaylandTextInput)
+
+namespace QtWaylandClient {
+
+class QWaylandDisplay;
+
+class QWaylandTextInputv4 : public QtWayland::zwp_text_input_v4, public QWaylandTextInputInterface
+{
+public:
+ QWaylandTextInputv4(QWaylandDisplay *display, struct ::zwp_text_input_v4 *text_input);
+ ~QWaylandTextInputv4() override;
+
+ void reset() override;
+ void commit() override;
+ void updateState(Qt::InputMethodQueries queries, uint32_t flags) override;
+ // TODO: not supported yet
+ void setCursorInsidePreedit(int cursor) override;
+
+ bool isInputPanelVisible() const override;
+ QRectF keyboardRect() const override;
+
+ QLocale locale() const override;
+ Qt::LayoutDirection inputDirection() const override;
+
+ void enableSurface(::wl_surface *surface) override;
+ void disableSurface(::wl_surface *surface) override;
+
+protected:
+ void zwp_text_input_v4_enter(struct ::wl_surface *surface) override;
+ void zwp_text_input_v4_leave(struct ::wl_surface *surface) override;
+ void zwp_text_input_v4_preedit_string(const QString &text, int32_t cursor_begin, int32_t cursor_end) override;
+ void zwp_text_input_v4_commit_string(const QString &text) override;
+ void zwp_text_input_v4_delete_surrounding_text(uint32_t before_length, uint32_t after_length) override;
+ void zwp_text_input_v4_done(uint32_t serial) override;
+
+private:
+ QWaylandDisplay *m_display;
+ QWaylandInputMethodEventBuilder m_builder;
+
+ ::wl_surface *m_surface = nullptr; // ### Here for debugging purposes
+
+ struct PreeditInfo {
+ QString text;
+ int cursorBegin = 0;
+ int cursorEnd = 0;
+
+ void clear() {
+ text.clear();
+ cursorBegin = 0;
+ cursorEnd = 0;
+ }
+ };
+
+ PreeditInfo m_pendingPreeditString;
+ PreeditInfo m_currentPreeditString;
+ QString m_pendingCommitString;
+ uint m_pendingDeleteBeforeText = 0;
+ uint m_pendingDeleteAfterText = 0;
+
+ QString m_surroundingText;
+ int m_cursor; // cursor position in QString
+ int m_cursorPos; // cursor position in wayland index
+ int m_anchorPos; // anchor position in wayland index
+ uint32_t m_contentHint = 0;
+ uint32_t m_contentPurpose = 0;
+ QRect m_cursorRect;
+
+ uint m_currentSerial = 0;
+
+ bool m_condReselection = false;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDTEXTINPUTV4_P_H
diff --git a/src/compositor/CMakeLists.txt b/src/compositor/CMakeLists.txt
index 7d1de2a44..95118afbd 100644
--- a/src/compositor/CMakeLists.txt
+++ b/src/compositor/CMakeLists.txt
@@ -89,13 +89,13 @@ qt_internal_add_resource(WaylandCompositor "compositor"
${compositor_resource_files}
)
-
qt6_generate_wayland_protocol_server_sources(WaylandCompositor
FILES
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/idle-inhibit-unstable-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/ivi-application.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/scaler.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/text-input-unstable-v2.xml
+ ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/text-input-unstable-v4-wip.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/viewporter.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/wayland.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/xdg-decoration-unstable-v1.xml
@@ -116,6 +116,14 @@ qt6_generate_wayland_protocol_server_sources(WaylandCompositor
## Scopes:
#####################################################################
+qt_internal_extend_target(WaylandCompositor CONDITION QT_FEATURE_wayland_text_input_v4_wip
+ SOURCES
+ extensions/qwaylandtextinputv4.cpp extensions/qwaylandtextinputv4.h extensions/qwaylandtextinputv4_p.h
+ extensions/qwaylandtextinputmanagerv4.cpp extensions/qwaylandtextinputmanagerv4.h extensions/qwaylandtextinputmanagerv4_p.h
+ DEFINES
+ QT_WAYLAND_TEXT_INPUT_V4_WIP=1
+)
+
qt_internal_extend_target(WaylandCompositor CONDITION QT_FEATURE_opengl
SOURCES
hardware_integration/qwlclientbufferintegrationfactory.cpp hardware_integration/qwlclientbufferintegrationfactory_p.h
diff --git a/src/compositor/compositor_api/qwaylandclient.h b/src/compositor/compositor_api/qwaylandclient.h
index 667d7f644..b2e27f690 100644
--- a/src/compositor/compositor_api/qwaylandclient.h
+++ b/src/compositor/compositor_api/qwaylandclient.h
@@ -65,6 +65,7 @@ public:
NoProtocol = 0,
QtTextInputMethodV1 = 1,
TextInputV2 = 2,
+ TextInputV4 = 4,
QtTextInputMethod = QtTextInputMethodV1,
TextInput = TextInputV2
diff --git a/src/compositor/compositor_api/qwaylandcompositor.cpp b/src/compositor/compositor_api/qwaylandcompositor.cpp
index 5647bf533..98a0bb7f1 100644
--- a/src/compositor/compositor_api/qwaylandcompositor.cpp
+++ b/src/compositor/compositor_api/qwaylandcompositor.cpp
@@ -88,6 +88,9 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(qLcWaylandCompositor, "qt.waylandcompositor")
Q_LOGGING_CATEGORY(qLcWaylandCompositorHardwareIntegration, "qt.waylandcompositor.hardwareintegration")
Q_LOGGING_CATEGORY(qLcWaylandCompositorInputMethods, "qt.waylandcompositor.inputmethods")
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+Q_LOGGING_CATEGORY(qLcWaylandCompositorTextInput, "qt.waylandcompositor.textinput")
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
namespace QtWayland {
diff --git a/src/compositor/compositor_api/qwaylandcompositor.h b/src/compositor/compositor_api/qwaylandcompositor.h
index 28431b466..c5272a5d7 100644
--- a/src/compositor/compositor_api/qwaylandcompositor.h
+++ b/src/compositor/compositor_api/qwaylandcompositor.h
@@ -63,6 +63,7 @@ class QWaylandBufferRef;
Q_WAYLAND_COMPOSITOR_EXPORT Q_DECLARE_LOGGING_CATEGORY(qLcWaylandCompositor)
Q_WAYLAND_COMPOSITOR_EXPORT Q_DECLARE_LOGGING_CATEGORY(qLcWaylandCompositorHardwareIntegration)
Q_DECLARE_LOGGING_CATEGORY(qLcWaylandCompositorInputMethods)
+Q_DECLARE_LOGGING_CATEGORY(qLcWaylandCompositorTextInput)
class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandCompositor : public QWaylandObject
{
diff --git a/src/compositor/compositor_api/qwaylandcompositorquickextensions_p.h b/src/compositor/compositor_api/qwaylandcompositorquickextensions_p.h
index 184267135..d592afbe7 100644
--- a/src/compositor/compositor_api/qwaylandcompositorquickextensions_p.h
+++ b/src/compositor/compositor_api/qwaylandcompositorquickextensions_p.h
@@ -49,6 +49,9 @@
#include <QtWaylandCompositor/qwaylandquickcompositor.h>
#include <QtWaylandCompositor/qwaylandqtwindowmanager.h>
#include <QtWaylandCompositor/qwaylandtextinputmanager.h>
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+#include <QtWaylandCompositor/qwaylandtextinputmanagerv4.h>
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
#include <QtWaylandCompositor/qwaylandqttextinputmethodmanager.h>
#include <QtWaylandCompositor/qwaylandidleinhibitv1.h>
@@ -107,6 +110,9 @@ private:
Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_NAMED_CLASS(QWaylandQtWindowManager, QtWindowManager)
Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_NAMED_CLASS(QWaylandIdleInhibitManagerV1, IdleInhibitManagerV1)
Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_NAMED_CLASS(QWaylandTextInputManager, TextInputManager)
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_NAMED_CLASS(QWaylandTextInputManagerV4, TextInputManagerV4)
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_NAMED_CLASS(QWaylandQtTextInputMethodManager, QtTextInputMethodManager)
QT_END_NAMESPACE
diff --git a/src/compositor/compositor_api/qwaylandinputmethodcontrol.cpp b/src/compositor/compositor_api/qwaylandinputmethodcontrol.cpp
index 9c52f6469..be019b5b0 100644
--- a/src/compositor/compositor_api/qwaylandinputmethodcontrol.cpp
+++ b/src/compositor/compositor_api/qwaylandinputmethodcontrol.cpp
@@ -35,6 +35,9 @@
#include "qwaylandsurface.h"
#include "qwaylandview.h"
#include "qwaylandtextinput.h"
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+#include "qwaylandtextinputv4.h"
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
#include "qwaylandqttextinputmethod.h"
#include <QtGui/QInputMethodEvent>
@@ -51,6 +54,15 @@ QWaylandInputMethodControl::QWaylandInputMethodControl(QWaylandSurface *surface)
connect(textInput, &QWaylandTextInput::updateInputMethod, this, &QWaylandInputMethodControl::updateInputMethod);
}
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ QWaylandTextInputV4 *textInputV4 = d_func()->textInputV4();
+ if (textInputV4) {
+ connect(textInputV4, &QWaylandTextInputV4::surfaceEnabled, this, &QWaylandInputMethodControl::surfaceEnabled);
+ connect(textInputV4, &QWaylandTextInputV4::surfaceDisabled, this, &QWaylandInputMethodControl::surfaceDisabled);
+ connect(textInputV4, &QWaylandTextInputV4::updateInputMethod, this, &QWaylandInputMethodControl::updateInputMethod);
+ }
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
+
QWaylandQtTextInputMethod *textInputMethod = d_func()->textInputMethod();
if (textInputMethod) {
connect(textInputMethod, &QWaylandQtTextInputMethod::surfaceEnabled, this, &QWaylandInputMethodControl::surfaceEnabled);
@@ -67,6 +79,12 @@ QVariant QWaylandInputMethodControl::inputMethodQuery(Qt::InputMethodQuery query
if (textInput != nullptr && textInput->focus() == d->surface)
return textInput->inputMethodQuery(query, argument);
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ QWaylandTextInputV4 *textInputV4 = d->textInputV4();
+ if (textInputV4 != nullptr && textInputV4->focus() == d->surface)
+ return textInputV4->inputMethodQuery(query, argument);
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
+
QWaylandQtTextInputMethod *textInputMethod = d_func()->textInputMethod();
if (textInputMethod && textInputMethod->focusedSurface() == d->surface)
return textInputMethod->inputMethodQuery(query, argument);
@@ -78,11 +96,13 @@ void QWaylandInputMethodControl::inputMethodEvent(QInputMethodEvent *event)
{
Q_D(QWaylandInputMethodControl);
- QWaylandTextInput *textInput = d->textInput();
- QWaylandQtTextInputMethod *textInputMethod = d->textInputMethod();
- if (textInput) {
+ if (QWaylandTextInput *textInput = d->textInput()) {
textInput->sendInputMethodEvent(event);
- } else if (textInputMethod) {
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ } else if (QWaylandTextInputV4 *textInputV4 = d->textInputV4()) {
+ textInputV4->sendInputMethodEvent(event);
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
+ } else if (QWaylandQtTextInputMethod *textInputMethod = d->textInputMethod()) {
textInputMethod->sendInputMethodEvent(event);
} else {
event->ignore();
@@ -134,8 +154,14 @@ void QWaylandInputMethodControl::setSurface(QWaylandSurface *surface)
d->surface = surface;
QWaylandTextInput *textInput = d->textInput();
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ QWaylandTextInputV4 *textInputV4 = d->textInputV4();
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
QWaylandQtTextInputMethod *textInputMethod = d->textInputMethod();
setEnabled((textInput && textInput->isSurfaceEnabled(d->surface))
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ || (textInputV4 && textInputV4->isSurfaceEnabled(d->surface))
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
|| (textInputMethod && textInputMethod->isSurfaceEnabled(d->surface)));
}
@@ -144,10 +170,16 @@ void QWaylandInputMethodControl::defaultSeatChanged()
Q_D(QWaylandInputMethodControl);
disconnect(d->textInput(), nullptr, this, nullptr);
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ disconnect(d->textInputV4(), nullptr, this, nullptr);
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
disconnect(d->textInputMethod(), nullptr, this, nullptr);
d->seat = d->compositor->defaultSeat();
QWaylandTextInput *textInput = d->textInput();
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ QWaylandTextInputV4 *textInputV4 = d->textInputV4();
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
QWaylandQtTextInputMethod *textInputMethod = d->textInputMethod();
if (textInput) {
@@ -155,12 +187,22 @@ void QWaylandInputMethodControl::defaultSeatChanged()
connect(textInput, &QWaylandTextInput::surfaceDisabled, this, &QWaylandInputMethodControl::surfaceDisabled);
}
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ if (textInputV4) {
+ connect(textInputV4, &QWaylandTextInputV4::surfaceEnabled, this, &QWaylandInputMethodControl::surfaceEnabled);
+ connect(textInputV4, &QWaylandTextInputV4::surfaceDisabled, this, &QWaylandInputMethodControl::surfaceDisabled);
+ }
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
+
if (textInputMethod) {
connect(textInputMethod, &QWaylandQtTextInputMethod::surfaceEnabled, this, &QWaylandInputMethodControl::surfaceEnabled);
connect(textInputMethod, &QWaylandQtTextInputMethod::surfaceDisabled, this, &QWaylandInputMethodControl::surfaceDisabled);
}
setEnabled((textInput && textInput->isSurfaceEnabled(d->surface))
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ || (textInputV4 && textInputV4->isSurfaceEnabled(d->surface))
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
|| (textInputMethod && textInputMethod->isSurfaceEnabled(d->surface)));
}
@@ -185,4 +227,11 @@ QWaylandTextInput *QWaylandInputMethodControlPrivate::textInput() const
return QWaylandTextInput::findIn(seat);
}
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+QWaylandTextInputV4 *QWaylandInputMethodControlPrivate::textInputV4() const
+{
+ return QWaylandTextInputV4::findIn(seat);
+}
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
+
#include "moc_qwaylandinputmethodcontrol.cpp"
diff --git a/src/compositor/compositor_api/qwaylandinputmethodcontrol_p.h b/src/compositor/compositor_api/qwaylandinputmethodcontrol_p.h
index e25ac48c5..4ca46d135 100644
--- a/src/compositor/compositor_api/qwaylandinputmethodcontrol_p.h
+++ b/src/compositor/compositor_api/qwaylandinputmethodcontrol_p.h
@@ -52,6 +52,9 @@ class QWaylandCompositor;
class QWaylandSeat;
class QWaylandSurface;
class QWaylandTextInput;
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+class QWaylandTextInputV4;
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
class QWaylandQtTextInputMethod;
class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandInputMethodControlPrivate : public QObjectPrivate
@@ -62,6 +65,9 @@ public:
explicit QWaylandInputMethodControlPrivate(QWaylandSurface *surface);
QWaylandTextInput *textInput() const;
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ QWaylandTextInputV4 *textInputV4() const;
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
QWaylandQtTextInputMethod *textInputMethod() const;
QWaylandCompositor *compositor = nullptr;
diff --git a/src/compositor/compositor_api/qwaylandquickitem.cpp b/src/compositor/compositor_api/qwaylandquickitem.cpp
index d1b14ceb5..9796b72ad 100644
--- a/src/compositor/compositor_api/qwaylandquickitem.cpp
+++ b/src/compositor/compositor_api/qwaylandquickitem.cpp
@@ -32,6 +32,9 @@
#include "qwaylandquicksurface.h"
#include "qwaylandinputmethodcontrol.h"
#include "qwaylandtextinput.h"
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+#include "qwaylandtextinputv4.h"
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
#include "qwaylandqttextinputmethod.h"
#include "qwaylandquickoutput.h"
#include <QtWaylandCompositor/qwaylandcompositor.h>
@@ -1129,6 +1132,14 @@ void QWaylandQuickItem::takeFocus(QWaylandSeat *device)
textInput->setFocus(surface());
}
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ if (surface()->client()->textInputProtocols().testFlag(QWaylandClient::TextInputProtocol::TextInputV4)) {
+ QWaylandTextInputV4 *textInputV4 = QWaylandTextInputV4::findIn(target);
+ if (textInputV4)
+ textInputV4->setFocus(surface());
+ }
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
+
if (surface()->client()->textInputProtocols().testFlag(QWaylandClient::TextInputProtocol::QtTextInputMethodV1)) {
QWaylandQtTextInputMethod *textInputMethod = QWaylandQtTextInputMethod::findIn(target);
if (textInputMethod)
diff --git a/src/compositor/compositor_api/qwaylandseat.cpp b/src/compositor/compositor_api/qwaylandseat.cpp
index 9f607c5d9..54538d1d0 100644
--- a/src/compositor/compositor_api/qwaylandseat.cpp
+++ b/src/compositor/compositor_api/qwaylandseat.cpp
@@ -48,6 +48,9 @@
#include "extensions/qwlqtkey_p.h"
#include "extensions/qwaylandtextinput.h"
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+#include "extensions/qwaylandtextinputv4.h"
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
#include "extensions/qwaylandqttextinputmethod.h"
QT_BEGIN_NAMESPACE
@@ -484,6 +487,18 @@ void QWaylandSeat::sendFullKeyEvent(QKeyEvent *event)
return;
}
}
+
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ if (keyboardFocus()->client()->textInputProtocols().testFlag(QWaylandClient::TextInputProtocol::TextInputV4)) {
+ QWaylandTextInputV4 *textInputV4 = QWaylandTextInputV4::findIn(this);
+ if (textInputV4 && !event->text().isEmpty()) {
+ // it will just commit the text for text-input-unstable-v4-wip when keyPress
+ if (event->type() == QEvent::KeyPress)
+ textInputV4->sendKeyEvent(event);
+ return;
+ }
+ }
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
}
#endif
diff --git a/src/compositor/extensions/qwaylandtextinputmanagerv4.cpp b/src/compositor/extensions/qwaylandtextinputmanagerv4.cpp
new file mode 100644
index 000000000..09d70c0c3
--- /dev/null
+++ b/src/compositor/extensions/qwaylandtextinputmanagerv4.cpp
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwaylandtextinputmanagerv4.h"
+#include "qwaylandtextinputmanagerv4_p.h"
+
+#include <QtWaylandCompositor/QWaylandCompositor>
+#include <QtWaylandCompositor/QWaylandSeat>
+
+#include "qwaylandtextinputv4.h"
+
+QT_BEGIN_NAMESPACE
+
+QWaylandTextInputManagerV4Private::QWaylandTextInputManagerV4Private()
+{
+}
+
+void QWaylandTextInputManagerV4Private::zwp_text_input_manager_v4_get_text_input(Resource *resource, uint32_t id, struct ::wl_resource *seatResource)
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
+
+ Q_Q(QWaylandTextInputManagerV4);
+ QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(q->extensionContainer());
+ QWaylandSeat *seat = QWaylandSeat::fromSeatResource(seatResource);
+ QWaylandTextInputV4 *textInput = QWaylandTextInputV4::findIn(seat);
+ if (!textInput) {
+ textInput = new QWaylandTextInputV4(seat, compositor);
+ }
+ textInput->add(resource->client(), id, wl_resource_get_version(resource->handle));
+ QWaylandClient *client = QWaylandClient::fromWlClient(compositor, resource->client());
+ QWaylandClient::TextInputProtocols p = client->textInputProtocols();
+ client->setTextInputProtocols(p.setFlag(QWaylandClient::TextInputProtocol::TextInputV4));
+ if (!textInput->isInitialized())
+ textInput->initialize();
+}
+
+/*!
+ \internal
+ \preliminary
+
+ \qmltype TextInputManagerV4
+ \instantiates QWaylandTextInputManagerV4
+ \inqmlmodule QtWayland.Compositor
+ \brief Provides access to input methods in the compositor.
+
+ The \c TextInputManagerV4 corresponds to the \c zwp_text_input_manager_v4 interface
+ in the \c text_input_unstable_v4 extension protocol.
+
+ Instantiating this as child of a \l WaylandCompositor adds it to the list of interfaces available
+ to the client. If a client binds to it, then it will be used to communciate text input to
+ that client.
+
+ \note This protocol is currently a work-in-progress and only exists in Qt for validation purposes. It may change at any time.
+*/
+
+/*!
+ \internal
+ \preliminary
+ \class QWaylandTextInputManagerV4
+ \inmodule QtWaylandCompositor
+ \brief Provides access to input methods in the compositor.
+
+ The \c QWaylandTextInputManagerV4 corresponds to the \c zwp_text_input_manager_v4 interface
+ in the \c text_input_unstable_v4 extension protocol.
+
+ Instantiating this as child of a \l WaylandCompositor adds it to the list of interfaces available
+ to the client. If a client binds to it, then it will be used to communciate text input to
+ that client.
+ \note This protocol is currently a work-in-progress and only exists in Qt for validation purposes. It may change at any time.
+*/
+
+QWaylandTextInputManagerV4::QWaylandTextInputManagerV4()
+ : QWaylandCompositorExtensionTemplate<QWaylandTextInputManagerV4>(*new QWaylandTextInputManagerV4Private)
+{
+}
+
+QWaylandTextInputManagerV4::QWaylandTextInputManagerV4(QWaylandCompositor *compositor)
+ : QWaylandCompositorExtensionTemplate<QWaylandTextInputManagerV4>(compositor, *new QWaylandTextInputManagerV4Private)
+{
+}
+
+void QWaylandTextInputManagerV4::initialize()
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
+
+ Q_D(QWaylandTextInputManagerV4);
+
+ QWaylandCompositorExtensionTemplate::initialize();
+ QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer());
+ if (!compositor) {
+ qWarning() << "Failed to find QWaylandCompositor when initializing QWaylandTextInputManagerV4";
+ return;
+ }
+ d->init(compositor->display(), 1);
+}
+
+const wl_interface *QWaylandTextInputManagerV4::interface()
+{
+ return QWaylandTextInputManagerV4Private::interface();
+}
+
+QByteArray QWaylandTextInputManagerV4::interfaceName()
+{
+ return QWaylandTextInputManagerV4Private::interfaceName();
+}
+
+QT_END_NAMESPACE
diff --git a/src/compositor/extensions/qwaylandtextinputmanagerv4.h b/src/compositor/extensions/qwaylandtextinputmanagerv4.h
new file mode 100644
index 000000000..f004648c1
--- /dev/null
+++ b/src/compositor/extensions/qwaylandtextinputmanagerv4.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDTEXTINPUTMANAGERV4_H
+#define QWAYLANDTEXTINPUTMANAGERV4_H
+
+#include <QtWaylandCompositor/QWaylandCompositorExtension>
+
+#include <QtCore/QSize>
+
+QT_BEGIN_NAMESPACE
+
+class QWaylandTextInputManagerV4Private;
+
+class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandTextInputManagerV4 : public QWaylandCompositorExtensionTemplate<QWaylandTextInputManagerV4>
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QWaylandTextInputManagerV4)
+public:
+ QWaylandTextInputManagerV4();
+ QWaylandTextInputManagerV4(QWaylandCompositor *compositor);
+
+ void initialize() override;
+
+ static const struct wl_interface *interface();
+ static QByteArray interfaceName();
+};
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDTEXTINPUTMANAGERV4_H
diff --git a/src/compositor/extensions/qwaylandtextinputmanagerv4_p.h b/src/compositor/extensions/qwaylandtextinputmanagerv4_p.h
new file mode 100644
index 000000000..e0d65afe9
--- /dev/null
+++ b/src/compositor/extensions/qwaylandtextinputmanagerv4_p.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDTEXTINPUTMANAGERV4_P_H
+#define QWAYLANDTEXTINPUTMANAGERV4_P_H
+
+#include <QtWaylandCompositor/private/qwaylandcompositorextension_p.h>
+
+#include <QtWaylandCompositor/private/qwayland-server-text-input-unstable-v4-wip.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandTextInputManagerV4Private : public QWaylandCompositorExtensionPrivate, public QtWaylandServer::zwp_text_input_manager_v4
+{
+ Q_DECLARE_PUBLIC(QWaylandTextInputManagerV4)
+public:
+ QWaylandTextInputManagerV4Private();
+
+protected:
+ void zwp_text_input_manager_v4_get_text_input(Resource *resource, uint32_t id, struct ::wl_resource *seatResource) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDTEXTINPUTMANAGERV4_P_H
diff --git a/src/compositor/extensions/qwaylandtextinputv4.cpp b/src/compositor/extensions/qwaylandtextinputv4.cpp
new file mode 100644
index 000000000..fa25ba019
--- /dev/null
+++ b/src/compositor/extensions/qwaylandtextinputv4.cpp
@@ -0,0 +1,552 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwaylandtextinputv4.h"
+#include "qwaylandtextinputv4_p.h"
+
+#include <QtWaylandCompositor/QWaylandCompositor>
+#include <QtWaylandCompositor/private/qwaylandseat_p.h>
+
+#include "qwaylandsurface.h"
+#include "qwaylandview.h"
+#include "qwaylandinputmethodeventbuilder_p.h"
+
+#include <QGuiApplication>
+#include <QInputMethodEvent>
+#include <qpa/qwindowsysteminterface.h>
+
+#if QT_CONFIG(xkbcommon)
+#include <QtGui/private/qxkbcommon_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qLcWaylandCompositorTextInput)
+
+QWaylandTextInputV4ClientState::QWaylandTextInputV4ClientState()
+{
+}
+
+Qt::InputMethodQueries QWaylandTextInputV4ClientState::updatedQueries(const QWaylandTextInputV4ClientState &other) const
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
+
+ Qt::InputMethodQueries queries;
+
+ if (hints != other.hints)
+ queries |= Qt::ImHints;
+ if (cursorRectangle != other.cursorRectangle)
+ queries |= Qt::ImCursorRectangle;
+ if (surroundingText != other.surroundingText)
+ queries |= Qt::ImSurroundingText | Qt::ImCurrentSelection;
+ if (cursorPosition != other.cursorPosition)
+ queries |= Qt::ImCursorPosition | Qt::ImCurrentSelection;
+ if (anchorPosition != other.anchorPosition)
+ queries |= Qt::ImAnchorPosition | Qt::ImCurrentSelection;
+
+ return queries;
+}
+
+Qt::InputMethodQueries QWaylandTextInputV4ClientState::mergeChanged(const QWaylandTextInputV4ClientState &other) {
+
+ Qt::InputMethodQueries queries;
+
+ if ((other.changedState & Qt::ImHints) && hints != other.hints) {
+ hints = other.hints;
+ queries |= Qt::ImHints;
+ }
+
+ if ((other.changedState & Qt::ImCursorRectangle) && cursorRectangle != other.cursorRectangle) {
+ cursorRectangle = other.cursorRectangle;
+ queries |= Qt::ImCursorRectangle;
+ }
+
+ if ((other.changedState & Qt::ImSurroundingText) && surroundingText != other.surroundingText) {
+ surroundingText = other.surroundingText;
+ queries |= Qt::ImSurroundingText | Qt::ImCurrentSelection;
+ }
+
+ if ((other.changedState & Qt::ImCursorPosition) && cursorPosition != other.cursorPosition) {
+ cursorPosition = other.cursorPosition;
+ queries |= Qt::ImCursorPosition | Qt::ImCurrentSelection;
+ }
+
+ if ((other.changedState & Qt::ImAnchorPosition) && anchorPosition != other.anchorPosition) {
+ anchorPosition = other.anchorPosition;
+ queries |= Qt::ImAnchorPosition | Qt::ImCurrentSelection;
+ }
+
+ return queries;
+}
+
+QWaylandTextInputV4Private::QWaylandTextInputV4Private(QWaylandCompositor *compositor)
+ : compositor(compositor)
+ , currentState(new QWaylandTextInputV4ClientState)
+ , pendingState(new QWaylandTextInputV4ClientState)
+{
+}
+
+void QWaylandTextInputV4Private::sendInputMethodEvent(QInputMethodEvent *event)
+{
+ Q_Q(QWaylandTextInputV4);
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
+
+ if (!focusResource || !focusResource->handle)
+ return;
+
+ bool needsDone = false;
+
+ const QString &newPreeditString = event->preeditString();
+
+ // Current cursor shape is only line. It means both cursorBegin
+ // and cursorEnd will be the same values.
+ int32_t preeditCursorPos = newPreeditString.length();
+
+ if (event->replacementLength() > 0 || event->replacementStart() < 0) {
+ if (event->replacementStart() <= 0 && (event->replacementLength() >= -event->replacementStart())) {
+ const int selectionStart = qMin(currentState->cursorPosition, currentState->anchorPosition);
+ const int selectionEnd = qMax(currentState->cursorPosition, currentState->anchorPosition);
+ const int before = QWaylandInputMethodEventBuilder::indexToWayland(currentState->surroundingText, -event->replacementStart(), selectionStart + event->replacementStart());
+ const int after = QWaylandInputMethodEventBuilder::indexToWayland(currentState->surroundingText, event->replacementLength() + event->replacementStart(), selectionEnd);
+ send_delete_surrounding_text(focusResource->handle, before, after);
+ needsDone = true;
+ } else {
+ qCWarning(qLcWaylandCompositorTextInput) << "Not yet supported case of replacement. Start:" << event->replacementStart() << "length:" << event->replacementLength();
+ }
+ preeditCursorPos = event->replacementStart() + event->replacementLength();
+ }
+
+ if (currentPreeditString != newPreeditString) {
+ currentPreeditString = newPreeditString;
+ send_preedit_string(focusResource->handle, currentPreeditString, preeditCursorPos, preeditCursorPos);
+ needsDone = true;
+ }
+ if (!event->commitString().isEmpty()) {
+ send_commit_string(focusResource->handle, event->commitString());
+ needsDone = true;
+ }
+
+ if (needsDone)
+ send_done(focusResource->handle, serial);
+}
+
+
+void QWaylandTextInputV4Private::sendKeyEvent(QKeyEvent *event)
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
+
+ Q_Q(QWaylandTextInputV4);
+
+ if (!focusResource || !focusResource->handle)
+ return;
+
+ send_commit_string(focusResource->handle, event->text());
+
+ send_done(focusResource->handle, serial);
+}
+
+QVariant QWaylandTextInputV4Private::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO << property;
+
+ switch (property) {
+ case Qt::ImHints:
+ return QVariant(static_cast<int>(currentState->hints));
+ case Qt::ImCursorRectangle:
+ return currentState->cursorRectangle;
+ case Qt::ImFont:
+ // Not supported
+ return QVariant();
+ case Qt::ImCursorPosition:
+ qCDebug(qLcWaylandCompositorTextInput) << currentState->cursorPosition;
+ return currentState->cursorPosition;
+ case Qt::ImSurroundingText:
+ qCDebug(qLcWaylandCompositorTextInput) << currentState->surroundingText;
+ return currentState->surroundingText;
+ case Qt::ImCurrentSelection:
+ return currentState->surroundingText.mid(qMin(currentState->cursorPosition, currentState->anchorPosition),
+ qAbs(currentState->anchorPosition - currentState->cursorPosition));
+ case Qt::ImMaximumTextLength:
+ // Not supported
+ return QVariant();
+ case Qt::ImAnchorPosition:
+ qCDebug(qLcWaylandCompositorTextInput) << currentState->anchorPosition;
+ return currentState->anchorPosition;
+ case Qt::ImAbsolutePosition:
+ // We assume the surrounding text is our whole document for now
+ return currentState->cursorPosition;
+ case Qt::ImTextAfterCursor:
+ if (argument.isValid())
+ return currentState->surroundingText.mid(currentState->cursorPosition, argument.toInt());
+ return currentState->surroundingText.mid(currentState->cursorPosition);
+ case Qt::ImTextBeforeCursor:
+ if (argument.isValid())
+ return currentState->surroundingText.left(currentState->cursorPosition).right(argument.toInt());
+ return currentState->surroundingText.left(currentState->cursorPosition);
+
+ default:
+ return QVariant();
+ }
+}
+
+void QWaylandTextInputV4Private::setFocus(QWaylandSurface *surface)
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
+ Q_Q(QWaylandTextInputV4);
+
+ if (focusResource && focus) {
+ // sync before leave
+ // IBUS commits by itself but qtvirtualkeyboard doesn't
+ // And when handling chinese input, it is required to commit
+ // before leaving the focus.
+ if (qgetenv("QT_IM_MODULE") != QByteArrayLiteral("ibus")
+ || qApp->inputMethod()->locale().language() == QLocale::Chinese) {
+ qApp->inputMethod()->commit();
+ }
+
+ qApp->inputMethod()->hide();
+ inputPanelVisible = false;
+ send_leave(focusResource->handle, focus->resource());
+ currentPreeditString.clear();
+ }
+
+ if (focus != surface)
+ focusDestroyListener.reset();
+
+ Resource *resource = surface ? resourceMap().value(surface->waylandClient()) : 0;
+ if (resource && surface) {
+ send_enter(resource->handle, surface->resource());
+
+ if (focus != surface)
+ focusDestroyListener.listenForDestruction(surface->resource());
+ }
+
+ focus = surface;
+ focusResource = resource;
+}
+
+void QWaylandTextInputV4Private::zwp_text_input_v4_bind_resource(Resource *resource)
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
+
+ Q_UNUSED(resource);
+}
+
+void QWaylandTextInputV4Private::zwp_text_input_v4_destroy_resource(Resource *resource)
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
+
+ if (focusResource == resource)
+ focusResource = nullptr;
+}
+
+void QWaylandTextInputV4Private::zwp_text_input_v4_destroy(Resource *resource)
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
+
+ wl_resource_destroy(resource->handle);
+}
+
+void QWaylandTextInputV4Private::zwp_text_input_v4_enable(Resource *resource)
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
+
+ Q_Q(QWaylandTextInputV4);
+
+ pendingState.reset(new QWaylandTextInputV4ClientState);
+
+ enabledSurfaces.insert(resource, focus);
+ emit q->surfaceEnabled(focus);
+
+ serial = 0;
+ inputPanelVisible = true;
+ qApp->inputMethod()->show();
+}
+
+void QWaylandTextInputV4Private::zwp_text_input_v4_disable(QtWaylandServer::zwp_text_input_v4::Resource *resource)
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
+
+ Q_Q(QWaylandTextInputV4);
+
+ QWaylandSurface *s = enabledSurfaces.take(resource);
+ emit q->surfaceDisabled(s);
+
+ // When reselecting a word by setFocus
+ if (qgetenv("QT_IM_MODULE") != QByteArrayLiteral("ibus")
+ || qApp->inputMethod()->locale().language() == QLocale::Chinese) {
+ qApp->inputMethod()->commit();
+ }
+ qApp->inputMethod()->reset();
+ pendingState.reset(new QWaylandTextInputV4ClientState);
+}
+
+void QWaylandTextInputV4Private::zwp_text_input_v4_set_cursor_rectangle(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO << x << y << width << height;
+
+ Q_Q(QWaylandTextInputV4);
+
+ if (resource != focusResource)
+ return;
+
+ pendingState->cursorRectangle = QRect(x, y, width, height);
+
+ pendingState->changedState |= Qt::ImCursorRectangle;
+}
+
+void QWaylandTextInputV4Private::zwp_text_input_v4_commit(Resource *resource)
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
+
+ Q_Q(QWaylandTextInputV4);
+
+ if (resource != focusResource) {
+ qCDebug(qLcWaylandCompositorTextInput) << "OBS: Disabled surface!!";
+ return;
+ }
+
+ serial = serial < UINT_MAX ? serial + 1U : 0U;
+
+ // Just increase serials and ignore empty commits
+ if (!pendingState->changedState) {
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO << "pendingState is not changed";
+ return;
+ }
+
+ // Selection starts.
+ // But since qtvirtualkeyboard with hunspell does not reset its preedit string,
+ // compositor forces to reset inputMethod.
+ if ((currentState->cursorPosition == currentState->anchorPosition)
+ && (pendingState->cursorPosition != pendingState->anchorPosition))
+ qApp->inputMethod()->reset();
+
+ // Enable reselection
+ // This is a workaround to make qtvirtualkeyboad's state empty by clearing State::InputMethodClick.
+ if (currentState->surroundingText == pendingState->surroundingText && currentState->cursorPosition != pendingState->cursorPosition)
+ qApp->inputMethod()->invokeAction(QInputMethod::Click, pendingState->cursorPosition);
+
+ Qt::InputMethodQueries queries = currentState->mergeChanged(*pendingState.data());
+ pendingState.reset(new QWaylandTextInputV4ClientState);
+
+ if (queries) {
+ qCDebug(qLcWaylandCompositorTextInput) << "QInputMethod::update() after commit with" << queries;
+
+ qApp->inputMethod()->update(queries);
+ }
+}
+
+void QWaylandTextInputV4Private::zwp_text_input_v4_set_content_type(Resource *resource, uint32_t hint, uint32_t purpose)
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO << hint << purpose;
+
+ if (resource != focusResource)
+ return;
+
+ pendingState->hints = Qt::ImhNone;
+
+ if ((hint & content_hint_completion) == 0)
+ pendingState->hints |= Qt::ImhNoPredictiveText;
+ if ((hint & content_hint_auto_capitalization) == 0)
+ pendingState->hints |= Qt::ImhNoAutoUppercase;
+ if ((hint & content_hint_lowercase) != 0)
+ pendingState->hints |= Qt::ImhPreferLowercase;
+ if ((hint & content_hint_uppercase) != 0)
+ pendingState->hints |= Qt::ImhPreferUppercase;
+ if ((hint & content_hint_hidden_text) != 0)
+ pendingState->hints |= Qt::ImhHiddenText;
+ if ((hint & content_hint_sensitive_data) != 0)
+ pendingState->hints |= Qt::ImhSensitiveData;
+ if ((hint & content_hint_latin) != 0)
+ pendingState->hints |= Qt::ImhLatinOnly;
+ if ((hint & content_hint_multiline) != 0)
+ pendingState->hints |= Qt::ImhMultiLine;
+
+ switch (purpose) {
+ case content_purpose_normal:
+ break;
+ case content_purpose_alpha:
+ pendingState->hints |= Qt::ImhUppercaseOnly | Qt::ImhLowercaseOnly;
+ break;
+ case content_purpose_digits:
+ pendingState->hints |= Qt::ImhDigitsOnly;
+ break;
+ case content_purpose_number:
+ pendingState->hints |= Qt::ImhFormattedNumbersOnly;
+ break;
+ case content_purpose_phone:
+ pendingState->hints |= Qt::ImhDialableCharactersOnly;
+ break;
+ case content_purpose_url:
+ pendingState->hints |= Qt::ImhUrlCharactersOnly;
+ break;
+ case content_purpose_email:
+ pendingState->hints |= Qt::ImhEmailCharactersOnly;
+ break;
+ case content_purpose_name:
+ case content_purpose_password:
+ break;
+ case content_purpose_date:
+ pendingState->hints |= Qt::ImhDate;
+ break;
+ case content_purpose_time:
+ pendingState->hints |= Qt::ImhTime;
+ break;
+ case content_purpose_datetime:
+ pendingState->hints |= Qt::ImhDate | Qt::ImhTime;
+ break;
+ case content_purpose_terminal:
+ default:
+ break;
+ }
+
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO << pendingState->hints;
+
+ pendingState->changedState |= Qt::ImHints;
+}
+
+void QWaylandTextInputV4Private::zwp_text_input_v4_set_surrounding_text(Resource *resource, const QString &text, int32_t cursor, int32_t anchor)
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO << text << cursor << anchor;
+
+ if (resource != focusResource)
+ return;
+
+ pendingState->surroundingText = text;
+ pendingState->cursorPosition = QWaylandInputMethodEventBuilder::indexFromWayland(text, cursor);
+ pendingState->anchorPosition = QWaylandInputMethodEventBuilder::indexFromWayland(text, anchor);
+
+ pendingState->changedState |= Qt::ImSurroundingText | Qt::ImCursorPosition | Qt::ImAnchorPosition;
+}
+
+void QWaylandTextInputV4Private::zwp_text_input_v4_set_text_change_cause(Resource *resource, uint32_t cause)
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
+
+ Q_UNUSED(resource);
+ Q_UNUSED(cause);
+}
+
+QWaylandTextInputV4::QWaylandTextInputV4(QWaylandObject *container, QWaylandCompositor *compositor)
+ : QWaylandCompositorExtensionTemplate(container, *new QWaylandTextInputV4Private(compositor))
+{
+ connect(&d_func()->focusDestroyListener, &QWaylandDestroyListener::fired,
+ this, &QWaylandTextInputV4::focusSurfaceDestroyed);
+}
+
+QWaylandTextInputV4::~QWaylandTextInputV4()
+{
+}
+
+void QWaylandTextInputV4::sendInputMethodEvent(QInputMethodEvent *event)
+{
+ Q_D(QWaylandTextInputV4);
+
+ d->sendInputMethodEvent(event);
+}
+
+void QWaylandTextInputV4::sendKeyEvent(QKeyEvent *event)
+{
+ Q_D(QWaylandTextInputV4);
+
+ d->sendKeyEvent(event);
+}
+
+QVariant QWaylandTextInputV4::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const
+{
+ const Q_D(QWaylandTextInputV4);
+
+ return d->inputMethodQuery(property, argument);
+}
+
+QWaylandSurface *QWaylandTextInputV4::focus() const
+{
+ const Q_D(QWaylandTextInputV4);
+
+ return d->focus;
+}
+
+void QWaylandTextInputV4::setFocus(QWaylandSurface *surface)
+{
+ Q_D(QWaylandTextInputV4);
+
+ d->setFocus(surface);
+}
+
+void QWaylandTextInputV4::focusSurfaceDestroyed(void *)
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
+
+ Q_D(QWaylandTextInputV4);
+
+ d->focusDestroyListener.reset();
+
+ d->focus = nullptr;
+ d->focusResource = nullptr;
+}
+
+bool QWaylandTextInputV4::isSurfaceEnabled(QWaylandSurface *surface) const
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
+
+ const Q_D(QWaylandTextInputV4);
+
+ return d->enabledSurfaces.values().contains(surface);
+}
+
+void QWaylandTextInputV4::add(::wl_client *client, uint32_t id, int version)
+{
+ qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
+
+ Q_D(QWaylandTextInputV4);
+
+ d->add(client, id, version);
+}
+
+const wl_interface *QWaylandTextInputV4::interface()
+{
+ return QWaylandTextInputV4Private::interface();
+}
+
+QByteArray QWaylandTextInputV4::interfaceName()
+{
+ return QWaylandTextInputV4Private::interfaceName();
+}
+
+QT_END_NAMESPACE
diff --git a/src/compositor/extensions/qwaylandtextinputv4.h b/src/compositor/extensions/qwaylandtextinputv4.h
new file mode 100644
index 000000000..ffb556cbf
--- /dev/null
+++ b/src/compositor/extensions/qwaylandtextinputv4.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDTEXTINPUTV4_H
+#define QWAYLANDTEXTINPUTV4_H
+
+#include <QtWaylandCompositor/QWaylandCompositorExtension>
+
+struct wl_client;
+
+QT_BEGIN_NAMESPACE
+
+class QWaylandTextInputV4Private;
+
+class QInputMethodEvent;
+class QKeyEvent;
+class QWaylandSurface;
+
+class QWaylandTextInputV4 : public QWaylandCompositorExtensionTemplate<QWaylandTextInputV4>
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QWaylandTextInputV4)
+public:
+ explicit QWaylandTextInputV4(QWaylandObject *container, QWaylandCompositor *compositor);
+ ~QWaylandTextInputV4() override;
+
+ void sendInputMethodEvent(QInputMethodEvent *event);
+ void sendKeyEvent(QKeyEvent *event);
+
+ QVariant inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const;
+
+ QWaylandSurface *focus() const;
+ void setFocus(QWaylandSurface *surface);
+
+ bool isSurfaceEnabled(QWaylandSurface *surface) const;
+
+ void add(::wl_client *client, uint32_t id, int version);
+ static const struct wl_interface *interface();
+ static QByteArray interfaceName();
+
+Q_SIGNALS:
+ void updateInputMethod(Qt::InputMethodQueries queries);
+ void surfaceEnabled(QWaylandSurface *surface);
+ void surfaceDisabled(QWaylandSurface *surface);
+
+private:
+ void focusSurfaceDestroyed(void *);
+};
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDTEXTINPUTV4_H
diff --git a/src/compositor/extensions/qwaylandtextinputv4_p.h b/src/compositor/extensions/qwaylandtextinputv4_p.h
new file mode 100644
index 000000000..7db6ec635
--- /dev/null
+++ b/src/compositor/extensions/qwaylandtextinputv4_p.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDTEXTINPUTV4_P_H
+#define QWAYLANDTEXTINPUTV4_P_H
+
+#include <QtWaylandCompositor/private/qwaylandcompositorextension_p.h>
+#include <QtWaylandCompositor/private/qwayland-server-text-input-unstable-v4-wip.h>
+#include <QtWaylandCompositor/QWaylandDestroyListener>
+
+#include <QtCore/QObject>
+#include <QtCore/QRect>
+#include <QtGui/QInputMethod>
+#include <QtWaylandCompositor/QWaylandSurface>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QInputMethodEvent;
+class QKeyEvent;
+class QWaylandCompositor;
+class QWaylandView;
+
+class QWaylandTextInputV4ClientState {
+public:
+ QWaylandTextInputV4ClientState();
+
+ Qt::InputMethodQueries updatedQueries(const QWaylandTextInputV4ClientState &other) const;
+ Qt::InputMethodQueries mergeChanged(const QWaylandTextInputV4ClientState &other);
+
+ Qt::InputMethodHints hints = Qt::ImhNone;
+ QRect cursorRectangle;
+ QString surroundingText;
+ int cursorPosition = 0;
+ int anchorPosition = 0;
+
+ Qt::InputMethodQueries changedState;
+};
+
+class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandTextInputV4Private : public QWaylandCompositorExtensionPrivate, public QtWaylandServer::zwp_text_input_v4
+{
+ Q_DECLARE_PUBLIC(QWaylandTextInputV4)
+public:
+ explicit QWaylandTextInputV4Private(QWaylandCompositor *compositor);
+
+ void sendInputMethodEvent(QInputMethodEvent *event);
+ void sendKeyEvent(QKeyEvent *event);
+
+ QVariant inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const;
+
+ void setFocus(QWaylandSurface *surface);
+
+ QWaylandCompositor *compositor = nullptr;
+
+ QWaylandSurface *focus = nullptr;
+ Resource *focusResource = nullptr;
+ QWaylandDestroyListener focusDestroyListener;
+
+ bool inputPanelVisible = false;
+
+ QString currentPreeditString;
+
+ QScopedPointer<QWaylandTextInputV4ClientState> currentState;
+ QScopedPointer<QWaylandTextInputV4ClientState> pendingState;
+
+ uint32_t serial = 0;
+
+ QHash<Resource *, QWaylandSurface*> enabledSurfaces;
+
+protected:
+ void zwp_text_input_v4_bind_resource(Resource *resource) override;
+ void zwp_text_input_v4_destroy_resource(Resource *resource) override;
+
+ void zwp_text_input_v4_destroy(Resource *resource) override;
+ void zwp_text_input_v4_enable(Resource *resource) override;
+ void zwp_text_input_v4_disable(Resource *resource) override;
+ void zwp_text_input_v4_set_surrounding_text(Resource *resource, const QString &text, int32_t cursor, int32_t anchor) override;
+ void zwp_text_input_v4_set_text_change_cause(Resource *resource, uint32_t cause) override;
+ void zwp_text_input_v4_set_content_type(Resource *resource, uint32_t hint, uint32_t purpose) override;
+ void zwp_text_input_v4_set_cursor_rectangle(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) override;
+ void zwp_text_input_v4_commit(Resource *resource) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDTEXTINPUTV4_P_H
diff --git a/src/configure.cmake b/src/configure.cmake
index aaa426e19..ce7fcf591 100644
--- a/src/configure.cmake
+++ b/src/configure.cmake
@@ -1,6 +1,7 @@
# configure.cmake for the QtWaylandGlobalPrivate module
#### Inputs
+set(INPUT_wayland_text_input_v4_wip OFF CACHE BOOL "")
@@ -233,7 +234,12 @@ qt_feature("xcomposite-glx" PRIVATE
AND QT_FEATURE_opengl AND NOT QT_FEATURE_opengles2
AND QT_FEATURE_xlib AND XComposite_FOUND
)
+qt_feature("wayland-text-input-v4-wip" PRIVATE
+ LABEL "Qt Wayland TextInput Protocol V4(WIP)"
+ PURPOSE "Enables wayland_text_input_unstable_v4(wip)"
+)
+qt_configure_add_summary_entry(ARGS "wayland-text-input-v4-wip")
qt_configure_add_summary_entry(ARGS "wayland-client")
qt_configure_add_summary_entry(ARGS "wayland-server")
qt_configure_add_summary_section(NAME "Qt Wayland Drivers")
diff --git a/src/qt_cmdline.cmake b/src/qt_cmdline.cmake
new file mode 100644
index 000000000..0921d53e2
--- /dev/null
+++ b/src/qt_cmdline.cmake
@@ -0,0 +1 @@
+qt_commandline_option(wayland-text-input-v4-wip TYPE boolean)
diff --git a/src/shared/qwaylandinputmethodeventbuilder.cpp b/src/shared/qwaylandinputmethodeventbuilder.cpp
index 00e3ae14e..cfc4a99f8 100644
--- a/src/shared/qwaylandinputmethodeventbuilder.cpp
+++ b/src/shared/qwaylandinputmethodeventbuilder.cpp
@@ -44,8 +44,10 @@
#ifdef QT_BUILD_WAYLANDCOMPOSITOR_LIB
#include <QtWaylandCompositor/private/qwayland-server-text-input-unstable-v2.h>
+#include <QtWaylandCompositor/private/qwayland-server-text-input-unstable-v4-wip.h>
#else
#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
+#include <QtWaylandClient/private/qwayland-text-input-unstable-v4-wip.h>
#endif
QT_BEGIN_NAMESPACE
@@ -194,68 +196,105 @@ QWaylandInputMethodContentType QWaylandInputMethodContentType::convert(Qt::Input
uint32_t hint = ZWP_TEXT_INPUT_V2_CONTENT_HINT_NONE;
uint32_t purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_NORMAL;
- if (hints & Qt::ImhHiddenText) {
+ if (hints & Qt::ImhHiddenText)
hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_HIDDEN_TEXT;
- }
- if (hints & Qt::ImhSensitiveData) {
+ if (hints & Qt::ImhSensitiveData)
hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_SENSITIVE_DATA;
- }
- if ((hints & Qt::ImhNoAutoUppercase) == 0) {
+ if ((hints & Qt::ImhNoAutoUppercase) == 0)
hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_CAPITALIZATION;
- }
if (hints & Qt::ImhPreferNumbers) {
// Nothing yet
}
- if (hints & Qt::ImhPreferUppercase) {
+ if (hints & Qt::ImhPreferUppercase)
hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_UPPERCASE;
- }
- if (hints & Qt::ImhPreferLowercase) {
+ if (hints & Qt::ImhPreferLowercase)
hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_LOWERCASE;
- }
if ((hints & Qt::ImhNoPredictiveText) == 0) {
- hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_COMPLETION | ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_CORRECTION;
+ hint |= (ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_COMPLETION
+ | ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_CORRECTION);
}
- if ((hints & Qt::ImhDate) && (hints & Qt::ImhTime) == 0) {
+ if ((hints & Qt::ImhDate) && (hints & Qt::ImhTime) == 0)
purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DATE;
- } else if ((hints & Qt::ImhDate) && (hints & Qt::ImhTime)) {
+ else if ((hints & Qt::ImhDate) && (hints & Qt::ImhTime))
purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DATETIME;
- } else if ((hints & Qt::ImhDate) == 0 && (hints & Qt::ImhTime)) {
+ else if ((hints & Qt::ImhDate) == 0 && (hints & Qt::ImhTime))
purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_TIME;
- }
- if (hints & Qt::ImhPreferLatin) {
+ if (hints & Qt::ImhPreferLatin)
hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_LATIN;
- }
-
- if (hints & Qt::ImhMultiLine) {
+ if (hints & Qt::ImhMultiLine)
hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_MULTILINE;
- }
-
- if (hints & Qt::ImhDigitsOnly) {
+ if (hints & Qt::ImhDigitsOnly)
purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DIGITS;
- }
- if (hints & Qt::ImhFormattedNumbersOnly) {
+ if (hints & Qt::ImhFormattedNumbersOnly)
purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_NUMBER;
- }
- if (hints & Qt::ImhUppercaseOnly) {
+ if (hints & Qt::ImhUppercaseOnly)
hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_UPPERCASE;
- }
- if (hints & Qt::ImhLowercaseOnly) {
+ if (hints & Qt::ImhLowercaseOnly)
hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_LOWERCASE;
- }
- if (hints & Qt::ImhDialableCharactersOnly) {
+ if (hints & Qt::ImhDialableCharactersOnly)
purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_PHONE;
- }
- if (hints & Qt::ImhEmailCharactersOnly) {
+ if (hints & Qt::ImhEmailCharactersOnly)
purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_EMAIL;
- }
- if (hints & Qt::ImhUrlCharactersOnly) {
+ if (hints & Qt::ImhUrlCharactersOnly)
purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_URL;
- }
- if (hints & Qt::ImhLatinOnly) {
+ if (hints & Qt::ImhLatinOnly)
hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_LATIN;
+
+ return QWaylandInputMethodContentType{hint, purpose};
+}
+
+QWaylandInputMethodContentType QWaylandInputMethodContentType::convertV4(Qt::InputMethodHints hints)
+{
+ uint32_t hint = ZWP_TEXT_INPUT_V4_CONTENT_HINT_NONE;
+ uint32_t purpose = ZWP_TEXT_INPUT_V4_CONTENT_PURPOSE_NORMAL;
+
+ if (hints & Qt::ImhHiddenText)
+ hint |= ZWP_TEXT_INPUT_V4_CONTENT_HINT_HIDDEN_TEXT;
+ if (hints & Qt::ImhSensitiveData)
+ hint |= ZWP_TEXT_INPUT_V4_CONTENT_HINT_SENSITIVE_DATA;
+ if ((hints & Qt::ImhNoAutoUppercase) == 0)
+ hint |= ZWP_TEXT_INPUT_V4_CONTENT_HINT_AUTO_CAPITALIZATION;
+ if (hints & Qt::ImhPreferNumbers) {
+ // Nothing yet
+ }
+ if (hints & Qt::ImhPreferUppercase)
+ hint |= ZWP_TEXT_INPUT_V4_CONTENT_HINT_UPPERCASE;
+ if (hints & Qt::ImhPreferLowercase)
+ hint |= ZWP_TEXT_INPUT_V4_CONTENT_HINT_LOWERCASE;
+ if ((hints & Qt::ImhNoPredictiveText) == 0) {
+ hint |= (ZWP_TEXT_INPUT_V4_CONTENT_HINT_COMPLETION
+ | ZWP_TEXT_INPUT_V4_CONTENT_HINT_SPELLCHECK);
}
+
+ if ((hints & Qt::ImhDate) && (hints & Qt::ImhTime) == 0)
+ purpose = ZWP_TEXT_INPUT_V4_CONTENT_PURPOSE_DATE;
+ else if ((hints & Qt::ImhDate) && (hints & Qt::ImhTime))
+ purpose = ZWP_TEXT_INPUT_V4_CONTENT_PURPOSE_DATETIME;
+ else if ((hints & Qt::ImhDate) == 0 && (hints & Qt::ImhTime))
+ purpose = ZWP_TEXT_INPUT_V4_CONTENT_PURPOSE_TIME;
+ if (hints & Qt::ImhPreferLatin)
+ hint |= ZWP_TEXT_INPUT_V4_CONTENT_HINT_LATIN;
+ if (hints & Qt::ImhMultiLine)
+ hint |= ZWP_TEXT_INPUT_V4_CONTENT_HINT_MULTILINE;
+ if (hints & Qt::ImhDigitsOnly)
+ purpose = ZWP_TEXT_INPUT_V4_CONTENT_PURPOSE_DIGITS;
+ if (hints & Qt::ImhFormattedNumbersOnly)
+ purpose = ZWP_TEXT_INPUT_V4_CONTENT_PURPOSE_NUMBER;
+ if (hints & Qt::ImhUppercaseOnly)
+ hint |= ZWP_TEXT_INPUT_V4_CONTENT_HINT_UPPERCASE;
+ if (hints & Qt::ImhLowercaseOnly)
+ hint |= ZWP_TEXT_INPUT_V4_CONTENT_HINT_LOWERCASE;
+ if (hints & Qt::ImhDialableCharactersOnly)
+ purpose = ZWP_TEXT_INPUT_V4_CONTENT_PURPOSE_PHONE;
+ if (hints & Qt::ImhEmailCharactersOnly)
+ purpose = ZWP_TEXT_INPUT_V4_CONTENT_PURPOSE_EMAIL;
+ if (hints & Qt::ImhUrlCharactersOnly)
+ purpose = ZWP_TEXT_INPUT_V4_CONTENT_PURPOSE_URL;
+ if (hints & Qt::ImhLatinOnly)
+ hint |= ZWP_TEXT_INPUT_V4_CONTENT_HINT_LATIN;
+
return QWaylandInputMethodContentType{hint, purpose};
}
@@ -273,6 +312,44 @@ int QWaylandInputMethodEventBuilder::indexFromWayland(const QString &text, int l
}
}
+int QWaylandInputMethodEventBuilder::trimmedIndexFromWayland(const QString &text, int length, int base)
+{
+ if (length == 0)
+ return base;
+
+ if (length < 0) {
+ const QByteArray &utf8 = QStringView{text}.left(base).toUtf8();
+ const int len = utf8.size();
+ const int start = len + length;
+ if (start <= 0)
+ return 0;
+
+ for (int i = 0; i < 4; i++) {
+ if (start + i >= len)
+ return base;
+
+ const unsigned char ch = utf8.at(start + i);
+ // check if current character is a utf8's initial character.
+ if (ch < 0x80 || ch > 0xbf)
+ return QString::fromUtf8(utf8.left(start + i)).length();
+ }
+ } else {
+ const QByteArray &utf8 = QStringView{text}.mid(base).toUtf8();
+ const int len = utf8.size();
+ const int start = length;
+ if (start >= len)
+ return base + QString::fromUtf8(utf8).length();
+
+ for (int i = 0; i < 4; i++) {
+ const unsigned char ch = utf8.at(start - i);
+ // check if current character is a utf8's initial character.
+ if (ch < 0x80 || ch > 0xbf)
+ return base + QString::fromUtf8(utf8.left(start - i)).length();
+ }
+ }
+ return -1;
+}
+
int QWaylandInputMethodEventBuilder::indexToWayland(const QString &text, int length, int base)
{
return QStringView{text}.mid(base, length).toUtf8().size();
diff --git a/src/shared/qwaylandinputmethodeventbuilder_p.h b/src/shared/qwaylandinputmethodeventbuilder_p.h
index 2499a563f..f362562fa 100644
--- a/src/shared/qwaylandinputmethodeventbuilder_p.h
+++ b/src/shared/qwaylandinputmethodeventbuilder_p.h
@@ -63,6 +63,8 @@ public:
static int indexFromWayland(const QString &text, int length, int base = 0);
static int indexToWayland(const QString &text, int length, int base = 0);
+
+ static int trimmedIndexFromWayland(const QString &text, int length, int base = 0);
private:
QPair<int, int> replacementForDeleteSurrounding();
@@ -76,10 +78,11 @@ private:
};
struct QWaylandInputMethodContentType {
- uint32_t hint;
- uint32_t purpose;
+ uint32_t hint = 0;
+ uint32_t purpose = 0;
static QWaylandInputMethodContentType convert(Qt::InputMethodHints hints);
+ static QWaylandInputMethodContentType convertV4(Qt::InputMethodHints hints);
};
diff --git a/sync.profile b/sync.profile
index 67026fa79..50825d468 100644
--- a/sync.profile
+++ b/sync.profile
@@ -31,6 +31,7 @@
"^qwayland-surface-extension.h",
"^qwayland-tablet-unstable-v2.h",
"^qwayland-text-input-unstable-v2.h",
+ "^qwayland-text-input-unstable-v4-wip.h",
"^qwayland-qt-text-input-method-unstable-v1.h",
"^qwayland-touch-extension.h",
"^qwayland-wayland.h",
@@ -44,6 +45,7 @@
"^wayland-surface-extension-client-protocol.h",
"^wayland-tablet-unstable-v2-client-protocol.h",
"^wayland-text-input-unstable-v2-client-protocol.h",
+ "^wayland-text-input-unstable-v4-wip-client-protocol.h",
"^wayland-qt-text-input-method-unstable-v1-client-protocol.h",
"^wayland-touch-extension-client-protocol.h",
"^wayland-wayland-client-protocol.h",
@@ -67,6 +69,7 @@
"^qwayland-server-scaler.h",
"^qwayland-server-server-buffer-extension.h",
"^qwayland-server-text-input-unstable-v2.h",
+ "^qwayland-server-text-input-unstable-v4-wip.h",
"^qwayland-server-qt-text-input-method-unstable-v1.h",
"^qwayland-server-touch-extension.h",
"^qwayland-server-viewporter.h",
@@ -82,6 +85,7 @@
"^wayland-scaler-server-protocol.h",
"^wayland-server-buffer-extension-server-protocol.h",
"^wayland-text-input-unstable-v2-server-protocol.h",
+ "^wayland-text-input-unstable-v4-wip-server-protocol.h",
"^wayland-qt-text-input-method-unstable-v1-server-protocol.h",
"^wayland-viewporter-server-protocol.h",
"^wayland-touch-extension-server-protocol.h",