0

[ozone/wayland] Text input refactors part 2: Split interfaces

Split the legacy ZWPTextInputWrapper interface in two and add impls for
each of them:
- ZwpTextInputV1[Impl]
- ZwpTextInputV3[Impl]

In this change the text input v1 and v3 objects are still
multi-instance, i.e. each WaylandInputMethodContext owns a separate
instance of either ZwpTextInputV1 or ZwpTextInputV3.

ZwpTextInputV* objects will be converted to singletons in a
subsequent change.

Bug: 414284073
Change-Id: I99751d0ec27f646dacd836808d25b5b9d96ff7b4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6495371
Commit-Queue: Orko Garai <orko@igalia.com>
Reviewed-by: Kramer Ge <fangzhoug@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1454042}
This commit is contained in:
Orko Garai
2025-04-30 10:41:13 -07:00
committed by Chromium LUCI CQ
parent ff527e1256
commit dc46fa8321
20 changed files with 929 additions and 764 deletions

@ -68,6 +68,7 @@ source_set("wayland") {
"host/overlay_prioritizer.h",
"host/single_pixel_buffer.cc",
"host/single_pixel_buffer.h",
"host/span_style.h",
"host/toplevel_icon_manager.cc",
"host/toplevel_icon_manager.h",
"host/wayland_async_cursor.cc",
@ -209,11 +210,10 @@ source_set("wayland") {
"host/zwp_primary_selection_device_manager.h",
"host/zwp_primary_selection_offer.cc",
"host/zwp_primary_selection_offer.h",
"host/zwp_text_input_wrapper.h",
"host/zwp_text_input_wrapper_v1.cc",
"host/zwp_text_input_wrapper_v1.h",
"host/zwp_text_input_wrapper_v3.cc",
"host/zwp_text_input_wrapper_v3.h",
"host/zwp_text_input_v1.cc",
"host/zwp_text_input_v1.h",
"host/zwp_text_input_v3.cc",
"host/zwp_text_input_v3.h",
"ozone_platform_wayland.cc",
"ozone_platform_wayland.h",
"wayland_utils.cc",
@ -397,8 +397,10 @@ source_set("test_support") {
"test/mock_zwp_linux_dmabuf.h",
"test/mock_zwp_text_input.cc",
"test/mock_zwp_text_input.h",
"test/mock_zwp_text_input_wrapper_client.cc",
"test/mock_zwp_text_input_wrapper_client.h",
"test/mock_zwp_text_input_v1_client.cc",
"test/mock_zwp_text_input_v1_client.h",
"test/mock_zwp_text_input_v3_client.cc",
"test/mock_zwp_text_input_v3_client.h",
"test/scoped_wl_array.cc",
"test/scoped_wl_array.h",
"test/server_object.cc",
@ -552,8 +554,8 @@ source_set("wayland_unittests") {
"host/wayland_zwp_pointer_gestures_unittest.cc",
"host/xdg_activation_unittest.cc",
"host/xdg_toplevel_icon_unittest.cc",
"host/zwp_text_input_wrapper_v1_unittest.cc",
"host/zwp_text_input_wrapper_v3_unittest.cc",
"host/zwp_text_input_v1_unittest.cc",
"host/zwp_text_input_v3_unittest.cc",
"mojom/wayland_overlay_config_mojom_traits_unittest.cc",
"test/wayland_drag_drop_test.cc",
"test/wayland_drag_drop_test.h",

@ -0,0 +1,34 @@
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_SPAN_STYLE_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_SPAN_STYLE_H_
#include <optional>
#include "ui/base/ime/ime_text_span.h"
namespace ui {
struct SpanStyle {
struct Style {
bool operator==(const Style& other) const = default;
ImeTextSpan::Type type;
ImeTextSpan::Thickness thickness;
};
bool operator==(const SpanStyle& other) const = default;
// Byte offset.
uint32_t index;
// Length in bytes.
uint32_t length;
// One of preedit_style.
std::optional<Style> style;
};
} // namespace ui
#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_SPAN_STYLE_H_

@ -36,11 +36,10 @@
#include "ui/events/types/event_type.h"
#include "ui/gfx/range/range.h"
#include "ui/ozone/platform/wayland/common/wayland_util.h"
#include "ui/ozone/platform/wayland/host/span_style.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_seat.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
#include "ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.h"
#include "ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v3.h"
#include "ui/ozone/public/ozone_switches.h"
#if BUILDFLAG(USE_XKBCOMMON)
@ -177,6 +176,67 @@ std::optional<OffsetText> TrimSurroundingTextForStandard(
truncated_range.start()};
}
class WaylandInputMethodContextV1Client : public ZwpTextInputV1Client {
public:
explicit WaylandInputMethodContextV1Client(WaylandInputMethodContext* context)
: context_(context) {}
void OnPreeditString(std::string_view text,
const std::vector<SpanStyle>& spans,
const gfx::Range& preedit_cursor) override {
context_->OnPreeditString(text, spans, preedit_cursor);
}
void OnCommitString(std::string_view text) override {
context_->OnCommitString(text);
}
void OnCursorPosition(int32_t index, int32_t anchor) override {
context_->OnCursorPosition(index, anchor);
}
void OnDeleteSurroundingText(int32_t index, uint32_t length) override {
context_->OnDeleteSurroundingText(index, length);
}
void OnKeysym(uint32_t key,
uint32_t state,
uint32_t modifiers,
uint32_t time) override {
context_->OnKeysym(key, state, modifiers, time);
}
void OnInputPanelState(uint32_t state) override {
context_->OnInputPanelState(state);
}
void OnModifiersMap(std::vector<std::string> map) override {
context_->OnModifiersMap(std::move(map));
}
private:
raw_ptr<WaylandInputMethodContext> context_;
};
class WaylandInputmethodContextV3Client : public ZwpTextInputV3Client {
public:
explicit WaylandInputmethodContextV3Client(WaylandInputMethodContext* context)
: context_(context) {}
void OnPreeditString(std::string_view text,
const std::vector<SpanStyle>& spans,
const gfx::Range& preedit_cursor) override {
context_->OnPreeditString(text, spans, preedit_cursor);
}
void OnCommitString(std::string_view text) override {
context_->OnCommitString(text);
}
private:
raw_ptr<WaylandInputMethodContext> context_;
};
} // namespace
WaylandInputMethodContext::WaylandInputMethodContext(
@ -186,21 +246,23 @@ WaylandInputMethodContext::WaylandInputMethodContext(
: connection_(connection),
key_delegate_(key_delegate),
ime_delegate_(ime_delegate),
text_input_(nullptr),
text_input_v1_(nullptr),
character_composer_(kPreeditStringMode) {
connection_->window_manager()->AddObserver(this);
Init();
}
WaylandInputMethodContext::~WaylandInputMethodContext() {
if (text_input_) {
if (text_input_v3_) {
text_input_v3_->OnClientDestroyed(text_input_v3_client_.get());
} else if (text_input_v1_) {
DismissVirtualKeyboard();
text_input_->Deactivate();
text_input_v1_->OnClientDestroyed(text_input_v1_client_.get());
}
connection_->window_manager()->RemoveObserver(this);
}
void WaylandInputMethodContext::CreateTextInputWrapper() {
void WaylandInputMethodContext::CreateTextInput() {
// Can be specified as value for --wayland-ime-version to use text-input-v1 or
// text-input-v3.
constexpr char kWaylandTextInputVersion1[] = "1";
@ -213,49 +275,70 @@ void WaylandInputMethodContext::CreateTextInputWrapper() {
cmd_line->HasSwitch(switches::kEnableWaylandIme) &&
!version_from_cmd_line.empty();
if (base::FeatureList::IsEnabled(features::kWaylandTextInputV3) ||
(enable_using_cmd_line_version &&
version_from_cmd_line == kWaylandTextInputVersion3)) {
if (connection_->text_input_manager_v3()) {
text_input_ = std::make_unique<ZWPTextInputWrapperV3>(
connection_, this, connection_->text_input_manager_v3());
} else {
LOG(WARNING) << "text-input-v3 not available.";
if (enable_using_cmd_line_version &&
version_from_cmd_line == kWaylandTextInputVersion1) {
if (!connection_->text_input_manager_v1()) {
LOG(WARNING) << "text-input-v1 is not supported by the compositor.";
return;
}
return;
} else if (enable_using_cmd_line_version &&
version_from_cmd_line != kWaylandTextInputVersion1) {
LOG(WARNING) << "text input version should be either 1 or 3. Defaulting to "
"text-input-v1.";
}
if (connection_->text_input_manager_v1()) {
text_input_ = std::make_unique<ZWPTextInputWrapperV1>(
connection_, this, connection_->text_input_manager_v1());
} else {
LOG(WARNING) << "text-input-v1 not available.";
text_input_v1_ = std::make_unique<ZwpTextInputV1Impl>(
connection_, connection_->text_input_manager_v1());
text_input_v1_client_ =
std::make_unique<WaylandInputMethodContextV1Client>(this);
} else if (base::FeatureList::IsEnabled(features::kWaylandTextInputV3) ||
enable_using_cmd_line_version) {
if (!version_from_cmd_line.empty() &&
version_from_cmd_line != kWaylandTextInputVersion3) {
LOG(WARNING) << "--wayland-text-input-version should have a value of "
"either 1 or 3 and "
"--enable-wayland-ime should be present. Defaulting to "
"text-input-v3.";
}
if (!connection_->text_input_manager_v3()) {
LOG(WARNING) << "text-input-v3 is not supported by the compositor.";
return;
}
text_input_v3_ = std::make_unique<ZwpTextInputV3Impl>(
connection_, connection_->text_input_manager_v3());
text_input_v3_client_ =
std::make_unique<WaylandInputmethodContextV3Client>(this);
}
}
void WaylandInputMethodContext::Init(
bool initialize_for_testing,
std::unique_ptr<ZWPTextInputWrapper> wrapper_for_testing,
std::optional<base::nix::DesktopEnvironment> desktop_for_testing) {
desktop_environment_ = desktop_for_testing.value_or(
base::nix::GetDesktopEnvironment(base::Environment::Create().get()));
if (wrapper_for_testing) {
text_input_ = std::move(wrapper_for_testing);
return;
}
bool use_ozone_wayland_vkb = initialize_for_testing || IsImeEnabled();
void WaylandInputMethodContext::Init() {
bool use_ozone_wayland_ime = IsImeEnabled();
// If text input instance is not created then all ime context operations
// are noop. This option is because in some environments someone might not
// want to enable ime/virtual keyboard even if it's available.
if (!use_ozone_wayland_vkb || text_input_) {
if (!use_ozone_wayland_ime || text_input_v3_ || text_input_v1_) {
return;
}
CreateTextInputWrapper();
CreateTextInput();
CHECK(!(text_input_v3_ && text_input_v1_))
<< "Both text-input-v1 and text-input-v3 used at the same time.";
}
void WaylandInputMethodContext::SetTextInputV1ForTesting(
std::unique_ptr<ZwpTextInputV1> text_input_v1) {
text_input_v1_ = std::move(text_input_v1);
if (!text_input_v1_client_) {
text_input_v1_client_ =
std::make_unique<WaylandInputMethodContextV1Client>(this);
}
text_input_v1_->SetClient(text_input_v1_client_.get());
text_input_v3_ = nullptr;
}
void WaylandInputMethodContext::SetTextInputV3ForTesting(
std::unique_ptr<ZwpTextInputV3> text_input_v3) {
text_input_v3_ = std::move(text_input_v3);
if (!text_input_v3_client_) {
text_input_v3_client_ =
std::make_unique<WaylandInputmethodContextV3Client>(this);
}
text_input_v3_->SetClient(text_input_v3_client_.get());
text_input_v1_ = nullptr;
}
bool WaylandInputMethodContext::DispatchKeyEvent(const KeyEvent& key_event) {
@ -308,8 +391,11 @@ void WaylandInputMethodContext::Reset() {
// intentions. Introduce a dedicated extended Wayland API for resetting only
// the composition.
surrounding_text_tracker_.CancelComposition();
if (text_input_)
text_input_->Reset();
if (text_input_v3_) {
text_input_v3_->Reset();
} else if (text_input_v1_) {
text_input_v1_->Reset();
}
}
void WaylandInputMethodContext::WillUpdateFocus(TextInputClient* old_client,
@ -334,23 +420,21 @@ void WaylandInputMethodContext::UpdateFocus(
if (old_type != TEXT_INPUT_TYPE_NONE)
Blur(skip_vk_update);
if (new_type != TEXT_INPUT_TYPE_NONE)
Focus(skip_vk_update, reason);
Focus(skip_vk_update);
}
void WaylandInputMethodContext::Focus(bool skip_virtual_keyboard_update,
TextInputClient::FocusReason reason) {
void WaylandInputMethodContext::Focus(bool skip_virtual_keyboard_update) {
focused_ = true;
MaybeUpdateActivated(skip_virtual_keyboard_update, reason);
MaybeUpdateActivated(skip_virtual_keyboard_update);
}
void WaylandInputMethodContext::Blur(bool skip_virtual_keyboard_update) {
focused_ = false;
MaybeUpdateActivated(skip_virtual_keyboard_update,
TextInputClient::FOCUS_REASON_NONE);
MaybeUpdateActivated(skip_virtual_keyboard_update);
}
void WaylandInputMethodContext::SetCursorLocation(const gfx::Rect& rect) {
if (!text_input_) {
if (!text_input_v3_ && !text_input_v1_) {
return;
}
WaylandWindow* focused_window =
@ -358,8 +442,13 @@ void WaylandInputMethodContext::SetCursorLocation(const gfx::Rect& rect) {
if (!focused_window) {
return;
}
text_input_->SetCursorRect(
rect - focused_window->GetBoundsInDIP().OffsetFromOrigin());
if (text_input_v3_) {
text_input_v3_->SetCursorRect(
rect - focused_window->GetBoundsInDIP().OffsetFromOrigin());
} else {
text_input_v1_->SetCursorRect(
rect - focused_window->GetBoundsInDIP().OffsetFromOrigin());
}
}
void WaylandInputMethodContext::SetSurroundingText(
@ -387,8 +476,9 @@ void WaylandInputMethodContext::SetSurroundingText(
size_t utf16_offset = text_range.GetMin();
surrounding_text_tracker_.Update(text, utf16_offset, selection_range);
if (!text_input_)
if (!text_input_v3_ && !text_input_v1_) {
return;
}
// Convert into UTF8 unit.
std::vector<size_t> offsets_for_adjustment = {
@ -451,30 +541,44 @@ void WaylandInputMethodContext::SetSurroundingText(
gfx::Range relocated_selection_range(
selection_range_utf8.start() - surrounding_text_offset_,
selection_range_utf8.end() - surrounding_text_offset_);
text_input_->SetSurroundingText(text_utf8, relocated_preedit_range,
relocated_selection_range);
if (text_input_v3_) {
text_input_v3_->SetSurroundingText(text_utf8, relocated_preedit_range,
relocated_selection_range);
} else {
text_input_v1_->SetSurroundingText(text_utf8, relocated_preedit_range,
relocated_selection_range);
}
}
VirtualKeyboardController*
WaylandInputMethodContext::GetVirtualKeyboardController() {
if (!text_input_)
if (!text_input_v3_ && !text_input_v1_) {
return nullptr;
}
return this;
}
bool WaylandInputMethodContext::DisplayVirtualKeyboard() {
if (!text_input_)
if (!text_input_v3_ && !text_input_v1_) {
return false;
}
text_input_->ShowInputPanel();
// Text-input-v3 does not support input panel show/hide yet.
if (text_input_v1_) {
text_input_v1_->ShowInputPanel();
}
return true;
}
void WaylandInputMethodContext::DismissVirtualKeyboard() {
if (!text_input_)
if (!text_input_v3_ && !text_input_v1_) {
return;
}
text_input_->HideInputPanel();
// Text-input-v3 does not support input panel show/hide yet.
if (text_input_v1_) {
text_input_v1_->HideInputPanel();
}
}
void WaylandInputMethodContext::AddObserver(
@ -715,39 +819,57 @@ void WaylandInputMethodContext::OnModifiersMap(
}
void WaylandInputMethodContext::OnKeyboardFocusedWindowChanged() {
MaybeUpdateActivated(false, TextInputClient::FOCUS_REASON_OTHER);
if (text_input_v3_) {
// For text-input-v3, zwp_text_input_l3::{enter,leave} is used instead.
return;
}
MaybeUpdateActivated(false);
}
void WaylandInputMethodContext::MaybeUpdateActivated(
bool skip_virtual_keyboard_update,
TextInputClient::FocusReason reason) {
if (!text_input_)
bool skip_virtual_keyboard_update) {
if (!text_input_v3_ && !text_input_v1_) {
return;
}
WaylandWindow* window =
WaylandWindow* focused_window =
connection_->window_manager()->GetCurrentKeyboardFocusedWindow();
if (!window && !connection_->seat()->keyboard())
window = connection_->window_manager()->GetCurrentActiveWindow();
// Activate Wayland IME only if 1) InputMethod in Chrome has some
// TextInputClient connected, and 2) the actual keyboard focus of Wayland
// is given to Chrome, which is notified via wl_keyboard::enter.
// If no keyboard is connected, the current active window is used for 2)
// instead (https://crbug.com/1168411).
bool activated = focused_ && window;
if (!focused_window && !connection_->seat()->keyboard()) {
// If no keyboard is connected, the current active window is used.
focused_window = connection_->window_manager()->GetCurrentActiveWindow();
}
// Activate Wayland IME only if the following conditions are met:
// 1) InputMethod has some TextInputClient connected.
// 2) There is a focused Chromium window.
// 3) The focused window matches the window containing this
// WaylandInputMethodContext.
bool activated = focused_ && focused_window;
if (activated_ == activated)
return;
activated_ = activated;
if (activated) {
text_input_->Activate(window, reason);
text_input_->SetContentType(attributes_.input_type, attributes_.flags,
attributes_.should_do_learning);
if (text_input_v3_) {
text_input_v3_->SetClient(text_input_v3_client_.get());
text_input_v3_->Enable();
text_input_v3_->SetContentType(attributes_.input_type, attributes_.flags,
attributes_.should_do_learning);
} else {
text_input_v1_->SetClient(text_input_v1_client_.get());
text_input_v1_->Activate(focused_window);
text_input_v1_->SetContentType(attributes_.input_type, attributes_.flags,
attributes_.should_do_learning);
}
if (!skip_virtual_keyboard_update)
DisplayVirtualKeyboard();
} else {
if (!skip_virtual_keyboard_update)
DismissVirtualKeyboard();
text_input_->Deactivate();
if (text_input_v3_) {
text_input_v3_->Disable();
} else {
text_input_v1_->Deactivate();
}
}
}

@ -21,7 +21,8 @@
#include "ui/gfx/range/range.h"
#include "ui/ozone/platform/wayland/host/wayland_keyboard.h"
#include "ui/ozone/platform/wayland/host/wayland_window_observer.h"
#include "ui/ozone/platform/wayland/host/zwp_text_input_wrapper.h"
#include "ui/ozone/platform/wayland/host/zwp_text_input_v1.h"
#include "ui/ozone/platform/wayland/host/zwp_text_input_v3.h"
namespace ui {
@ -29,8 +30,7 @@ class WaylandConnection;
class WaylandInputMethodContext : public LinuxInputMethodContext,
public VirtualKeyboardController,
public WaylandWindowObserver,
public ZWPTextInputWrapperClient {
public WaylandWindowObserver {
public:
class Delegate;
@ -42,10 +42,7 @@ class WaylandInputMethodContext : public LinuxInputMethodContext,
delete;
~WaylandInputMethodContext() override;
void Init(bool initialize_for_testing = false,
std::unique_ptr<ZWPTextInputWrapper> wrapper_for_testing = nullptr,
std::optional<base::nix::DesktopEnvironment> desktop_for_testing =
std::nullopt);
void Init();
// LinuxInputMethodContext overrides:
bool DispatchKeyEvent(const KeyEvent& key_event) override;
@ -76,37 +73,44 @@ class WaylandInputMethodContext : public LinuxInputMethodContext,
// WaylandWindowObserver overrides:
void OnKeyboardFocusedWindowChanged() override;
// ZWPTextInputWrapperClient overrides:
// Callbacks from the v1 and v3 clients.
void OnPreeditString(std::string_view text,
const std::vector<SpanStyle>& spans,
const gfx::Range& preedit_cursor) override;
void OnCommitString(std::string_view text) override;
void OnCursorPosition(int32_t index, int32_t anchor) override;
void OnDeleteSurroundingText(int32_t index, uint32_t length) override;
const gfx::Range& preedit_cursor);
void OnCommitString(std::string_view text);
void OnCursorPosition(int32_t index, int32_t anchor);
void OnDeleteSurroundingText(int32_t index, uint32_t length);
void OnKeysym(uint32_t keysym,
uint32_t state,
uint32_t modifiers,
uint32_t time) override;
uint32_t time);
void OnInputPanelState(uint32_t state) override;
void OnModifiersMap(std::vector<std::string> modifiers_map) override;
void OnInputPanelState(uint32_t state);
void OnModifiersMap(std::vector<std::string> modifiers_map);
const SurroundingTextTracker::State& predicted_state_for_testing() const {
return surrounding_text_tracker_.predicted_state();
}
void SetTextInputV1ForTesting(std::unique_ptr<ZwpTextInputV1> text_input_v1);
void SetTextInputV3ForTesting(std::unique_ptr<ZwpTextInputV3> text_input_v3);
void SetDesktopEnvironmentForTesting(
base::nix::DesktopEnvironment desktop_environment) {
desktop_environment_ = desktop_environment;
}
private:
void CreateTextInputWrapper();
void Focus(bool skip_virtual_keyboard_update,
TextInputClient::FocusReason reason);
void CreateTextInput();
void Focus(bool skip_virtual_keyboard_update);
void Blur(bool skip_virtual_keyboard_update);
void UpdatePreeditText(const std::u16string& preedit_text);
// If |skip_virtual_keyboard_update| is true, no virtual keyboard show/hide
// requests will be sent. This is used to prevent flickering the virtual
// keyboard when it would be immediately reshown anyway, e.g. when changing
// focus from one text input to another.
void MaybeUpdateActivated(bool skip_virtual_keyboard_update,
TextInputClient::FocusReason reason);
void MaybeUpdateActivated(bool skip_virtual_keyboard_update);
const raw_ptr<WaylandConnection>
connection_; // TODO(jani) Handle this better
@ -117,7 +121,10 @@ class WaylandInputMethodContext : public LinuxInputMethodContext,
// Delegate IME-specific events to be handled by //ui code.
const raw_ptr<LinuxInputMethodContextDelegate> ime_delegate_;
std::unique_ptr<ZWPTextInputWrapper> text_input_;
std::unique_ptr<ZwpTextInputV1Client> text_input_v1_client_;
std::unique_ptr<ZwpTextInputV3Client> text_input_v3_client_;
std::unique_ptr<ZwpTextInputV1> text_input_v1_;
std::unique_ptr<ZwpTextInputV3> text_input_v3_;
// Tracks whether InputMethod in Chrome has some focus.
bool focused_ = false;

@ -30,6 +30,7 @@
#include "ui/ozone/platform/wayland/host/wayland_event_source.h"
#include "ui/ozone/platform/wayland/host/wayland_seat.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
#include "ui/ozone/platform/wayland/host/zwp_text_input_v1.h"
#include "ui/ozone/platform/wayland/test/mock_surface.h"
#include "ui/ozone/platform/wayland/test/mock_zwp_text_input.h"
#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
@ -151,20 +152,20 @@ class MockTextInputClient : public TextInputClient {
base::WeakPtrFactory<MockTextInputClient> weak_ptr_factory_{this};
};
class MockZWPTextInputWrapper : public ZWPTextInputWrapper {
class MockZwpTextInputV3 : public ZwpTextInputV3 {
public:
~MockZWPTextInputWrapper() override = default;
~MockZwpTextInputV3() override = default;
MOCK_METHOD(void, Reset, (), (override));
MOCK_METHOD(void, SetClient, (ZwpTextInputV3Client * context), (override));
MOCK_METHOD(void,
Activate,
(WaylandWindow * window, ui::TextInputClient::FocusReason reason),
OnClientDestroyed,
(ZwpTextInputV3Client * context),
(override));
MOCK_METHOD(void, Deactivate, (), (override));
MOCK_METHOD(void, ShowInputPanel, (), (override));
MOCK_METHOD(void, HideInputPanel, (), (override));
MOCK_METHOD(void, Enable, (), (override));
MOCK_METHOD(void, Disable, (), (override));
MOCK_METHOD(void, Reset, (), (override));
MOCK_METHOD(void, SetCursorRect, (const gfx::Rect& rect), (override));
MOCK_METHOD(void,
@ -327,8 +328,11 @@ class WaylandInputMethodContextTest : public WaylandTest {
input_method_context_ = std::make_unique<WaylandInputMethodContext>(
connection_.get(), keyboard_delegate_.get(),
input_method_context_delegate_.get());
input_method_context_->Init(
true, nullptr,
auto text_input_v1 = std::make_unique<ZwpTextInputV1Impl>(
connection_.get(), connection_->text_input_manager_v1());
text_input_v1_ = text_input_v1.get();
input_method_context_->SetTextInputV1ForTesting(std::move(text_input_v1));
input_method_context_->SetDesktopEnvironmentForTesting(
// Ensure by default it doesn't pick the current desktop from the system
// the tests are running on.
base::nix::DesktopEnvironment::DESKTOP_ENVIRONMENT_OTHER);
@ -350,11 +354,12 @@ class WaylandInputMethodContextTest : public WaylandTest {
input_method_context_delegate_;
std::unique_ptr<TestKeyboardDelegate> keyboard_delegate_;
std::unique_ptr<WaylandInputMethodContext> input_method_context_;
raw_ptr<ZwpTextInputV1> text_input_v1_;
uint32_t surface_id_ = 0u;
};
INSTANTIATE_TEST_SUITE_P(TextInput,
INSTANTIATE_TEST_SUITE_P(TextInputV1,
WaylandInputMethodContextTest,
::testing::Values(wl::ServerConfig{}));
@ -1167,7 +1172,7 @@ class WaylandInputMethodContextNoKeyboardTest
}
};
INSTANTIATE_TEST_SUITE_P(TextInput,
INSTANTIATE_TEST_SUITE_P(TextInputV1,
WaylandInputMethodContextNoKeyboardTest,
::testing::Values(wl::ServerConfig{}));
@ -1254,9 +1259,9 @@ TEST_P(WaylandInputMethodContextNoKeyboardTest, UpdateFocusBetweenTextFields) {
});
}
// For use in tests that simply test the WaylandInputMethodContext in isolation
// without using a real v1/v3 wrapper.
class WaylandInputMethodContextWithMockWrapperTest : public WaylandTestSimple {
// For use in tests that test the WaylandInputMethodContext in isolation with a
// mock V3 client.
class WaylandInputMethodContextWithMockV3Test : public WaylandTestSimple {
public:
void SetUp() override {
WaylandTestSimple::SetUp();
@ -1266,10 +1271,10 @@ class WaylandInputMethodContextWithMockWrapperTest : public WaylandTestSimple {
input_method_context_ = std::make_unique<WaylandInputMethodContext>(
connection_.get(), keyboard_delegate_.get(),
input_method_context_delegate_.get());
auto mock_wrapper = std::make_unique<MockZWPTextInputWrapper>();
auto mock_wrapper = std::make_unique<MockZwpTextInputV3>();
mock_wrapper_ = mock_wrapper.get();
input_method_context_->Init(
true, std::move(mock_wrapper),
input_method_context_->SetTextInputV3ForTesting(std::move(mock_wrapper));
input_method_context_->SetDesktopEnvironmentForTesting(
// Ensure by default it doesn't pick the current desktop from the system
// the tests are running on.
base::nix::DesktopEnvironment::DESKTOP_ENVIRONMENT_OTHER);
@ -1280,10 +1285,10 @@ class WaylandInputMethodContextWithMockWrapperTest : public WaylandTestSimple {
input_method_context_delegate_;
std::unique_ptr<TestKeyboardDelegate> keyboard_delegate_;
std::unique_ptr<WaylandInputMethodContext> input_method_context_;
raw_ptr<MockZWPTextInputWrapper> mock_wrapper_;
raw_ptr<MockZwpTextInputV3> mock_wrapper_;
};
TEST_F(WaylandInputMethodContextWithMockWrapperTest,
TEST_F(WaylandInputMethodContextWithMockV3Test,
SetSurroundingShortTextWithCompositionRange) {
const std::u16string text(50, u'');
constexpr gfx::Range range(20, 30);
@ -1304,7 +1309,7 @@ TEST_F(WaylandInputMethodContextWithMockWrapperTest,
connection_->Flush();
}
TEST_F(WaylandInputMethodContextWithMockWrapperTest,
TEST_F(WaylandInputMethodContextWithMockV3Test,
SetSurroundingLongTextWithCompositionRange) {
const std::u16string text(5000, u'');
constexpr gfx::Range kRange(2800, 3200);
@ -1325,7 +1330,7 @@ TEST_F(WaylandInputMethodContextWithMockWrapperTest,
kRange);
}
TEST_F(WaylandInputMethodContextWithMockWrapperTest,
TEST_F(WaylandInputMethodContextWithMockV3Test,
SetSurroundingLongTextWithCompositionRangeOutsideSurroundingTextRange) {
const std::u16string text(5000, u'');
constexpr gfx::Range kSelectionRange(2800, 3200);
@ -1362,7 +1367,7 @@ TEST_F(WaylandInputMethodContextWithMockWrapperTest,
kSelectionRange);
}
TEST_F(WaylandInputMethodContextWithMockWrapperTest,
TEST_F(WaylandInputMethodContextWithMockV3Test,
SetSurroundingWithCompositionRangeOutideText) {
const std::u16string text(5000, u'');
constexpr gfx::Range kSelectionRange(2800, 3200);
@ -1377,7 +1382,7 @@ TEST_F(WaylandInputMethodContextWithMockWrapperTest,
kSelectionRange);
}
TEST_F(WaylandInputMethodContextWithMockWrapperTest,
TEST_F(WaylandInputMethodContextWithMockV3Test,
SetSurroundingWithCompositionRangeInvalid) {
const std::u16string text(5000, u'');
constexpr gfx::Range kSelectionRange(2800, 3200);
@ -1399,7 +1404,7 @@ TEST_F(WaylandInputMethodContextWithMockWrapperTest,
kSelectionRange);
}
TEST_F(WaylandInputMethodContextWithMockWrapperTest, OnPreeditChanged) {
TEST_F(WaylandInputMethodContextWithMockV3Test, OnPreeditChanged) {
const std::u16string text(50, u'');
const std::string text_utf8 = base::UTF16ToUTF8(text);
@ -1419,7 +1424,7 @@ TEST_F(WaylandInputMethodContextWithMockWrapperTest, OnPreeditChanged) {
gfx::Range(20, 10));
}
TEST_F(WaylandInputMethodContextWithMockWrapperTest,
TEST_F(WaylandInputMethodContextWithMockV3Test,
OnPreeditChangedInvalidCursorEnd) {
const std::u16string text(50, u'');
const std::string text_utf8 = base::UTF16ToUTF8(text);
@ -1443,7 +1448,7 @@ TEST_F(WaylandInputMethodContextWithMockWrapperTest,
gfx::Range(0, 0));
}
TEST_F(WaylandInputMethodContextWithMockWrapperTest,
TEST_F(WaylandInputMethodContextWithMockV3Test,
OnPreeditChangedGnomeWorkaround) {
const std::u16string text(50, u'');
const std::string text_utf8 = base::UTF16ToUTF8(text);
@ -1453,9 +1458,8 @@ TEST_F(WaylandInputMethodContextWithMockWrapperTest,
input_method_context = std::make_unique<WaylandInputMethodContext>(
connection_.get(), keyboard_delegate_.get(),
input_method_context_delegate_.get());
auto mock_wrapper = std::make_unique<MockZWPTextInputWrapper>();
input_method_context->Init(true, std::move(mock_wrapper),
base::nix::DESKTOP_ENVIRONMENT_KDE3);
input_method_context->SetDesktopEnvironmentForTesting(
base::nix::DESKTOP_ENVIRONMENT_KDE3);
input_method_context->OnPreeditString(text_utf8, {}, {60, 30});
EXPECT_EQ(
@ -1468,9 +1472,8 @@ TEST_F(WaylandInputMethodContextWithMockWrapperTest,
input_method_context = std::make_unique<WaylandInputMethodContext>(
connection_.get(), keyboard_delegate_.get(),
input_method_context_delegate_.get());
mock_wrapper = std::make_unique<MockZWPTextInputWrapper>();
input_method_context->Init(true, std::move(mock_wrapper),
base::nix::DESKTOP_ENVIRONMENT_GNOME);
input_method_context->SetDesktopEnvironmentForTesting(
base::nix::DESKTOP_ENVIRONMENT_GNOME);
input_method_context->OnPreeditString(text_utf8, {}, {60, 30});
EXPECT_EQ(
input_method_context->predicted_state_for_testing().surrounding_text,

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.h"
#include "ui/ozone/platform/wayland/host/zwp_text_input_v1.h"
#include <sys/mman.h>
@ -74,21 +74,28 @@ uint32_t InputTypeToContentPurpose(TextInputType input_type) {
// Converts Chrome's TextInputType into wayland's content_hint.
uint32_t InputFlagsToContentHint(int input_flags) {
uint32_t hint = 0;
if (input_flags & TEXT_INPUT_FLAG_AUTOCOMPLETE_ON)
if (input_flags & TEXT_INPUT_FLAG_AUTOCOMPLETE_ON) {
hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_COMPLETION;
if (input_flags & TEXT_INPUT_FLAG_AUTOCORRECT_ON)
}
if (input_flags & TEXT_INPUT_FLAG_AUTOCORRECT_ON) {
hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_CORRECTION;
}
// No good match. Fallback to AUTO_CORRECTION.
if (input_flags & TEXT_INPUT_FLAG_SPELLCHECK_ON)
if (input_flags & TEXT_INPUT_FLAG_SPELLCHECK_ON) {
hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_CORRECTION;
if (input_flags & TEXT_INPUT_FLAG_AUTOCAPITALIZE_CHARACTERS)
}
if (input_flags & TEXT_INPUT_FLAG_AUTOCAPITALIZE_CHARACTERS) {
hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_UPPERCASE;
if (input_flags & TEXT_INPUT_FLAG_AUTOCAPITALIZE_WORDS)
}
if (input_flags & TEXT_INPUT_FLAG_AUTOCAPITALIZE_WORDS) {
hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_TITLECASE;
if (input_flags & TEXT_INPUT_FLAG_AUTOCAPITALIZE_SENTENCES)
}
if (input_flags & TEXT_INPUT_FLAG_AUTOCAPITALIZE_SENTENCES) {
hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_CAPITALIZATION;
if (input_flags & TEXT_INPUT_FLAG_HAS_BEEN_PASSWORD)
}
if (input_flags & TEXT_INPUT_FLAG_HAS_BEEN_PASSWORD) {
hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_PASSWORD;
}
return hint;
}
@ -106,8 +113,7 @@ std::vector<std::string> ParseModifiersMap(wl_array* array) {
// Returns ImeTextSpan style to be assigned. Maybe nullopt if it is not
// supported.
std::optional<ZWPTextInputWrapperClient::SpanStyle::Style> ConvertStyle(
uint32_t style) {
std::optional<SpanStyle::Style> ConvertStyle(uint32_t style) {
switch (style) {
case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_DEFAULT:
return {{ImeTextSpan::Type::kComposition, ImeTextSpan::Thickness::kNone}};
@ -132,11 +138,10 @@ std::optional<ZWPTextInputWrapperClient::SpanStyle::Style> ConvertStyle(
} // namespace
ZWPTextInputWrapperV1::ZWPTextInputWrapperV1(
ZwpTextInputV1Impl::ZwpTextInputV1Impl(
WaylandConnection* connection,
ZWPTextInputWrapperClient* client,
zwp_text_input_manager_v1* text_input_manager)
: connection_(connection), client_(client) {
: connection_(connection) {
static constexpr zwp_text_input_v1_listener kTextInputListener = {
.enter = &OnEnter,
.leave = &OnLeave,
@ -159,103 +164,108 @@ ZWPTextInputWrapperV1::ZWPTextInputWrapperV1(
zwp_text_input_v1_add_listener(obj_.get(), &kTextInputListener, this);
}
ZWPTextInputWrapperV1::~ZWPTextInputWrapperV1() = default;
ZwpTextInputV1Impl::~ZwpTextInputV1Impl() = default;
void ZWPTextInputWrapperV1::Reset() {
void ZwpTextInputV1Impl::Reset() {
ResetInputEventState();
zwp_text_input_v1_reset(obj_.get());
}
void ZWPTextInputWrapperV1::Activate(WaylandWindow* window,
TextInputClient::FocusReason reason) {
void ZwpTextInputV1Impl::Activate(WaylandWindow* window) {
DCHECK(connection_->seat());
zwp_text_input_v1_activate(obj_.get(), connection_->seat()->wl_object(),
window->root_surface()->surface());
}
void ZWPTextInputWrapperV1::Deactivate() {
void ZwpTextInputV1Impl::SetClient(ZwpTextInputV1Client* context) {
client_ = context;
}
void ZwpTextInputV1Impl::OnClientDestroyed(ZwpTextInputV1Client* context) {
if (client_ == context) {
client_ = nullptr;
Deactivate();
}
}
void ZwpTextInputV1Impl::Deactivate() {
DCHECK(connection_->seat());
zwp_text_input_v1_deactivate(obj_.get(), connection_->seat()->wl_object());
}
void ZWPTextInputWrapperV1::ShowInputPanel() {
void ZwpTextInputV1Impl::ShowInputPanel() {
zwp_text_input_v1_show_input_panel(obj_.get());
}
void ZWPTextInputWrapperV1::HideInputPanel() {
void ZwpTextInputV1Impl::HideInputPanel() {
zwp_text_input_v1_hide_input_panel(obj_.get());
}
void ZWPTextInputWrapperV1::SetCursorRect(const gfx::Rect& rect) {
void ZwpTextInputV1Impl::SetCursorRect(const gfx::Rect& rect) {
zwp_text_input_v1_set_cursor_rectangle(obj_.get(), rect.x(), rect.y(),
rect.width(), rect.height());
}
void ZWPTextInputWrapperV1::SetSurroundingText(
const std::string& text,
const gfx::Range& preedit_range,
const gfx::Range& selection_range) {
void ZwpTextInputV1Impl::SetSurroundingText(const std::string& text,
const gfx::Range& preedit_range,
const gfx::Range& selection_range) {
zwp_text_input_v1_set_surrounding_text(
obj_.get(), text.c_str(), selection_range.start(), selection_range.end());
}
void ZWPTextInputWrapperV1::SetContentType(ui::TextInputType type,
uint32_t flags,
bool should_do_learning) {
// If wayland compositor supports the extended version of set input type,
// use it to avoid losing the info.
// Otherwise, fallback to the standard set_content_type.
void ZwpTextInputV1Impl::SetContentType(ui::TextInputType type,
uint32_t flags,
bool should_do_learning) {
uint32_t content_purpose = InputTypeToContentPurpose(type);
uint32_t content_hint = InputFlagsToContentHint(flags);
if (!should_do_learning)
if (!should_do_learning) {
content_hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_SENSITIVE_DATA;
}
zwp_text_input_v1_set_content_type(obj_.get(), content_hint, content_purpose);
}
void ZWPTextInputWrapperV1::ResetInputEventState() {
void ZwpTextInputV1Impl::ResetInputEventState() {
spans_.clear();
preedit_cursor_ = -1;
}
// static
void ZWPTextInputWrapperV1::OnEnter(void* data,
struct zwp_text_input_v1* text_input,
struct wl_surface* surface) {
void ZwpTextInputV1Impl::OnEnter(void* data,
struct zwp_text_input_v1* text_input,
struct wl_surface* surface) {
NOTIMPLEMENTED_LOG_ONCE();
}
// static
void ZWPTextInputWrapperV1::OnLeave(void* data,
struct zwp_text_input_v1* text_input) {
void ZwpTextInputV1Impl::OnLeave(void* data,
struct zwp_text_input_v1* text_input) {
NOTIMPLEMENTED_LOG_ONCE();
}
// static
void ZWPTextInputWrapperV1::OnModifiersMap(void* data,
struct zwp_text_input_v1* text_input,
struct wl_array* map) {
auto* self = static_cast<ZWPTextInputWrapperV1*>(data);
void ZwpTextInputV1Impl::OnModifiersMap(void* data,
struct zwp_text_input_v1* text_input,
struct wl_array* map) {
auto* self = static_cast<ZwpTextInputV1Impl*>(data);
self->client_->OnModifiersMap(ParseModifiersMap(map));
}
// static
void ZWPTextInputWrapperV1::OnInputPanelState(
void* data,
struct zwp_text_input_v1* text_input,
uint32_t state) {
auto* self = static_cast<ZWPTextInputWrapperV1*>(data);
void ZwpTextInputV1Impl::OnInputPanelState(void* data,
struct zwp_text_input_v1* text_input,
uint32_t state) {
auto* self = static_cast<ZwpTextInputV1Impl*>(data);
self->client_->OnInputPanelState(state);
}
// static
void ZWPTextInputWrapperV1::OnPreeditString(
void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
const char* text,
const char* commit) {
auto* self = static_cast<ZWPTextInputWrapperV1*>(data);
void ZwpTextInputV1Impl::OnPreeditString(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
const char* text,
const char* commit) {
auto* self = static_cast<ZwpTextInputV1Impl*>(data);
auto spans = std::move(self->spans_);
int32_t preedit_cursor = self->preedit_cursor_;
self->ResetInputEventState();
@ -266,82 +276,77 @@ void ZWPTextInputWrapperV1::OnPreeditString(
}
// static
void ZWPTextInputWrapperV1::OnPreeditStyling(
void* data,
struct zwp_text_input_v1* text_input,
uint32_t index,
uint32_t length,
uint32_t style) {
auto* self = static_cast<ZWPTextInputWrapperV1*>(data);
self->spans_.push_back(
ZWPTextInputWrapperClient::SpanStyle{index, length, ConvertStyle(style)});
void ZwpTextInputV1Impl::OnPreeditStyling(void* data,
struct zwp_text_input_v1* text_input,
uint32_t index,
uint32_t length,
uint32_t style) {
auto* self = static_cast<ZwpTextInputV1Impl*>(data);
self->spans_.push_back(SpanStyle{index, length, ConvertStyle(style)});
}
// static
void ZWPTextInputWrapperV1::OnPreeditCursor(
void* data,
struct zwp_text_input_v1* text_input,
int32_t index) {
auto* self = static_cast<ZWPTextInputWrapperV1*>(data);
void ZwpTextInputV1Impl::OnPreeditCursor(void* data,
struct zwp_text_input_v1* text_input,
int32_t index) {
auto* self = static_cast<ZwpTextInputV1Impl*>(data);
self->preedit_cursor_ = index;
}
// static
void ZWPTextInputWrapperV1::OnCommitString(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
const char* text) {
auto* self = static_cast<ZWPTextInputWrapperV1*>(data);
void ZwpTextInputV1Impl::OnCommitString(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
const char* text) {
auto* self = static_cast<ZwpTextInputV1Impl*>(data);
self->ResetInputEventState();
self->client_->OnCommitString(text);
}
// static
void ZWPTextInputWrapperV1::OnCursorPosition(
void* data,
struct zwp_text_input_v1* text_input,
int32_t index,
int32_t anchor) {
auto* self = static_cast<ZWPTextInputWrapperV1*>(data);
void ZwpTextInputV1Impl::OnCursorPosition(void* data,
struct zwp_text_input_v1* text_input,
int32_t index,
int32_t anchor) {
auto* self = static_cast<ZwpTextInputV1Impl*>(data);
self->client_->OnCursorPosition(index, anchor);
}
// static
void ZWPTextInputWrapperV1::OnDeleteSurroundingText(
void ZwpTextInputV1Impl::OnDeleteSurroundingText(
void* data,
struct zwp_text_input_v1* text_input,
int32_t index,
uint32_t length) {
auto* self = static_cast<ZWPTextInputWrapperV1*>(data);
auto* self = static_cast<ZwpTextInputV1Impl*>(data);
self->client_->OnDeleteSurroundingText(index, length);
}
// static
void ZWPTextInputWrapperV1::OnKeysym(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
uint32_t time,
uint32_t key,
uint32_t state,
uint32_t modifiers) {
auto* self = static_cast<ZWPTextInputWrapperV1*>(data);
void ZwpTextInputV1Impl::OnKeysym(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
uint32_t time,
uint32_t key,
uint32_t state,
uint32_t modifiers) {
auto* self = static_cast<ZwpTextInputV1Impl*>(data);
self->client_->OnKeysym(key, state, modifiers, time);
}
// static
void ZWPTextInputWrapperV1::OnLanguage(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
const char* language) {
void ZwpTextInputV1Impl::OnLanguage(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
const char* language) {
NOTIMPLEMENTED_LOG_ONCE();
}
// static
void ZWPTextInputWrapperV1::OnTextDirection(
void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
uint32_t direction) {
void ZwpTextInputV1Impl::OnTextDirection(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
uint32_t direction) {
NOTIMPLEMENTED_LOG_ONCE();
}

@ -0,0 +1,200 @@
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_ZWP_TEXT_INPUT_V1_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_ZWP_TEXT_INPUT_V1_H_
#include <text-input-unstable-v1-client-protocol.h>
#include <cstdint>
#include <string>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/host/span_style.h"
namespace gfx {
class Rect;
class Range;
} // namespace gfx
namespace ui {
class WaylandConnection;
class WaylandWindow;
// Client interface which handles wayland text input callbacks
class ZwpTextInputV1Client {
public:
virtual ~ZwpTextInputV1Client() = default;
// Called when a new composing text (pre-edit) should be set around the
// current cursor position. Any previously set composing text should
// be removed.
// Note that the preedit_cursor is byte-offset. It is the pre-edit cursor
// position if the range is empty and selection otherwise.
virtual void OnPreeditString(std::string_view text,
const std::vector<SpanStyle>& spans,
const gfx::Range& preedit_cursor) = 0;
// Called when a complete input sequence has been entered. The text to
// commit could be either just a single character after a key press or the
// result of some composing (pre-edit).
virtual void OnCommitString(std::string_view text) = 0;
// Called when the cursor position or selection should be modified. The new
// cursor position is applied on the next OnCommitString. |index| and |anchor|
// are measured in UTF-8 bytes.
virtual void OnCursorPosition(int32_t index, int32_t anchor) = 0;
// Called when client needs to delete all or part of the text surrounding
// the cursor. |index| and |length| are expected to be a byte offset of |text|
// passed via ZWPTextInputWrapper::SetSurroundingText.
virtual void OnDeleteSurroundingText(int32_t index, uint32_t length) = 0;
// Notify when a key event was sent. Key events should not be used
// for normal text input operations, which should be done with
// commit_string, delete_surrounding_text, etc.
virtual void OnKeysym(uint32_t key,
uint32_t state,
uint32_t modifiers,
uint32_t time) = 0;
// Called when the visibility state of the input panel changed.
// There's no detailed spec of |state|, and no actual implementor except
// components/exo is found in the world at this moment.
// Thus, in ozone/wayland use the lowest bit as boolean
// (visible=1/invisible=0), and ignore other bits for future compatibility.
virtual void OnInputPanelState(uint32_t state) = 0;
// Called when the modifiers map is updated.
// Each element holds the XKB name represents a modifier, such as "Shift".
// The position of the element represents the bit position of modifiers
// on OnKeysym. E.g., if LSB of modifiers is set, modifiers_map[0] is
// set, if (1 << 1) of modifiers is set, modifiers_map[1] is set, and so on.
virtual void OnModifiersMap(std::vector<std::string> modifiers_map) = 0;
};
// A wrapper around different versions of wayland text input protocols.
// Wayland compositors support various different text input protocols which
// all from Chromium point of view provide the functionality needed by Chromium
// IME. This interface collects the functionality behind one wrapper API.
class ZwpTextInputV1 {
public:
virtual ~ZwpTextInputV1() = default;
virtual void Reset() = 0;
virtual void SetClient(ZwpTextInputV1Client* context) = 0;
virtual void OnClientDestroyed(ZwpTextInputV1Client* context) = 0;
virtual void Activate(WaylandWindow* window) = 0;
virtual void Deactivate() = 0;
virtual void ShowInputPanel() = 0;
virtual void HideInputPanel() = 0;
virtual void SetCursorRect(const gfx::Rect& rect) = 0;
virtual void SetSurroundingText(const std::string& text,
const gfx::Range& preedit_range,
const gfx::Range& selection_range) = 0;
virtual void SetContentType(TextInputType type,
uint32_t flags,
bool should_do_learning) = 0;
};
// Text input wrapper for text-input-unstable-v1
class ZwpTextInputV1Impl : public ZwpTextInputV1 {
public:
ZwpTextInputV1Impl(WaylandConnection* connection,
zwp_text_input_manager_v1* text_input_manager);
ZwpTextInputV1Impl(const ZwpTextInputV1Impl&) = delete;
ZwpTextInputV1Impl& operator=(const ZwpTextInputV1Impl&) = delete;
~ZwpTextInputV1Impl() override;
void Reset() override;
void SetClient(ZwpTextInputV1Client* context) override;
void OnClientDestroyed(ZwpTextInputV1Client* context) override;
void Activate(WaylandWindow* window) override;
void Deactivate() override;
void ShowInputPanel() override;
void HideInputPanel() override;
void SetCursorRect(const gfx::Rect& rect) override;
void SetSurroundingText(const std::string& text,
const gfx::Range& preedit_range,
const gfx::Range& selection_range) override;
void SetContentType(TextInputType type,
uint32_t flags,
bool should_do_learning) override;
private:
void ResetInputEventState();
// zwp_text_input_v1_listener callbacks:
static void OnEnter(void* data,
struct zwp_text_input_v1* text_input,
struct wl_surface* surface);
static void OnLeave(void* data, struct zwp_text_input_v1* text_input);
static void OnModifiersMap(void* data,
struct zwp_text_input_v1* text_input,
struct wl_array* map);
static void OnInputPanelState(void* data,
struct zwp_text_input_v1* text_input,
uint32_t state);
static void OnPreeditString(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
const char* text,
const char* commit);
static void OnPreeditStyling(void* data,
struct zwp_text_input_v1* text_input,
uint32_t index,
uint32_t length,
uint32_t style);
static void OnPreeditCursor(void* data,
struct zwp_text_input_v1* text_input,
int32_t index);
static void OnCommitString(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
const char* text);
static void OnCursorPosition(void* data,
struct zwp_text_input_v1* text_input,
int32_t index,
int32_t anchor);
static void OnDeleteSurroundingText(void* data,
struct zwp_text_input_v1* text_input,
int32_t index,
uint32_t length);
static void OnKeysym(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
uint32_t time,
uint32_t key,
uint32_t state,
uint32_t modifiers);
static void OnLanguage(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
const char* language);
static void OnTextDirection(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
uint32_t direction);
const raw_ptr<WaylandConnection> connection_;
wl::Object<zwp_text_input_v1> obj_;
raw_ptr<ZwpTextInputV1Client> client_;
std::vector<SpanStyle> spans_;
int32_t preedit_cursor_ = -1;
};
} // namespace ui
#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_ZWP_TEXT_INPUT_V1_H_

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.h"
#include "ui/ozone/platform/wayland/host/zwp_text_input_v1.h"
#include <sys/mman.h>
@ -12,9 +12,10 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "text-input-unstable-v1-server-protocol.h"
#include "ui/ozone/platform/wayland/host/span_style.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/test/mock_zwp_text_input.h"
#include "ui/ozone/platform/wayland/test/mock_zwp_text_input_wrapper_client.h"
#include "ui/ozone/platform/wayland/test/mock_zwp_text_input_v1_client.h"
#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
#include "ui/ozone/platform/wayland/test/wayland_test.h"
@ -24,26 +25,27 @@ using ::testing::Mock;
namespace ui {
class ZWPTextInputWrapperV1Test : public WaylandTestSimple {
class ZwpTextInputV1Test : public WaylandTestSimple {
public:
void SetUp() override {
WaylandTestSimple::SetUp();
wrapper_ = std::make_unique<ZWPTextInputWrapperV1>(
connection_.get(), &test_client_, connection_->text_input_manager_v1());
text_input_v1_ = std::make_unique<ZwpTextInputV1Impl>(
connection_.get(), connection_->text_input_manager_v1());
text_input_v1_->SetClient(&test_client_);
}
protected:
MockZWPTextInputWrapperClient test_client_;
std::unique_ptr<ZWPTextInputWrapperV1> wrapper_;
MockZwpTextInputV1Client test_client_;
std::unique_ptr<ZwpTextInputV1> text_input_v1_;
};
TEST_F(ZWPTextInputWrapperV1Test, OnPreeditString) {
TEST_F(ZwpTextInputV1Test, OnPreeditString) {
constexpr std::string_view kPreeditString("PreeditString");
constexpr int32_t kPreeditCursor = kPreeditString.size();
EXPECT_CALL(test_client_,
OnPreeditString(kPreeditString,
std::vector<ZWPTextInputWrapperClient::SpanStyle>{
std::vector<SpanStyle>{
{0,
static_cast<uint32_t>(kPreeditString.size()),
{{ImeTextSpan::Type::kComposition,
@ -62,7 +64,7 @@ TEST_F(ZWPTextInputWrapperV1Test, OnPreeditString) {
});
}
TEST_F(ZWPTextInputWrapperV1Test, OnKeySym_TimestampPropagated) {
TEST_F(ZwpTextInputV1Test, OnKeySym_TimestampPropagated) {
uint32_t test_time = 666;
EXPECT_CALL(test_client_, OnKeysym(_, _, _, test_time));

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v3.h"
#include "ui/ozone/platform/wayland/host/zwp_text_input_v3.h"
#include <string>
#include <utility>
@ -13,6 +13,7 @@
#include "base/numerics/safe_conversions.h"
#include "ui/base/wayland/wayland_client_input_types.h"
#include "ui/gfx/range/range.h"
#include "ui/ozone/platform/wayland/host/span_style.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_seat.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
@ -96,11 +97,10 @@ uint32_t InputFlagsToContentHint(int input_flags) {
} // namespace
ZWPTextInputWrapperV3::ZWPTextInputWrapperV3(
ZwpTextInputV3Impl::ZwpTextInputV3Impl(
WaylandConnection* connection,
ZWPTextInputWrapperClient* client,
zwp_text_input_manager_v3* text_input_manager)
: connection_(connection), client_(client) {
: connection_(connection) {
static constexpr zwp_text_input_v3_listener kTextInputListener = {
&OnEnter,
&OnLeave,
@ -118,9 +118,9 @@ ZWPTextInputWrapperV3::ZWPTextInputWrapperV3(
zwp_text_input_v3_add_listener(text_input, &kTextInputListener, this);
}
ZWPTextInputWrapperV3::~ZWPTextInputWrapperV3() = default;
ZwpTextInputV3Impl::~ZwpTextInputV3Impl() = default;
void ZWPTextInputWrapperV3::Reset() {
void ZwpTextInputV3Impl::Reset() {
// Clear last sent values.
ResetLastSentValues();
// There is no explicit reset API in v3. See [1].
@ -151,8 +151,18 @@ void ZWPTextInputWrapperV3::Reset() {
Commit();
}
void ZWPTextInputWrapperV3::Activate(WaylandWindow* window,
TextInputClient::FocusReason reason) {
void ZwpTextInputV3Impl::SetClient(ZwpTextInputV3Client* context) {
client_ = context;
}
void ZwpTextInputV3Impl::OnClientDestroyed(ZwpTextInputV3Client* context) {
if (client_ == context) {
client_ = nullptr;
Disable();
}
}
void ZwpTextInputV3Impl::Enable() {
// Pending state is reset on enable.
ResetPendingSetRequests();
ResetPendingInputEvents();
@ -160,7 +170,7 @@ void ZWPTextInputWrapperV3::Activate(WaylandWindow* window,
Commit();
}
void ZWPTextInputWrapperV3::Deactivate() {
void ZwpTextInputV3Impl::Disable() {
// Avoid sending pending requests if done is received after disabling.
ResetPendingSetRequests();
// Do not process pending input events after deactivating.
@ -169,33 +179,7 @@ void ZWPTextInputWrapperV3::Deactivate() {
Commit();
}
void ZWPTextInputWrapperV3::ShowInputPanel() {
VLOG(1) << __func__;
// Unsupported in zwp_text_input_v3 yet. To be supported soon as per wayland
// governance meeting on 2024-07-02:
// https://gitlab.freedesktop.org/wayland/wayland-protocols/-/wikis/meetings
//
// Some earlier notes in
// https://lists.freedesktop.org/archives/wayland-devel/2018-March/037341.html
//
// Calling enable here could be problematic, as enable clears state, so for
// instance cursor position sent previously will be reset and the input method
// popup will not appear next to the cursor after this.
NOTIMPLEMENTED_LOG_ONCE();
}
void ZWPTextInputWrapperV3::HideInputPanel() {
VLOG(1) << __func__;
// Unsupported in zwp_text_input_v3 yet. To be supported soon as per wayland
// governance meeting on 2024-07-02:
// https://gitlab.freedesktop.org/wayland/wayland-protocols/-/wikis/meetings
//
// Some earlier notes in
// https://lists.freedesktop.org/archives/wayland-devel/2018-March/037341.html
NOTIMPLEMENTED_LOG_ONCE();
}
void ZWPTextInputWrapperV3::SetCursorRect(const gfx::Rect& rect) {
void ZwpTextInputV3Impl::SetCursorRect(const gfx::Rect& rect) {
if (last_sent_cursor_rect_ == rect) {
// This is to avoid a loop in sending cursor rect and receiving pre-edit
// string.
@ -209,14 +193,14 @@ void ZWPTextInputWrapperV3::SetCursorRect(const gfx::Rect& rect) {
Commit();
}
void ZWPTextInputWrapperV3::SendCursorRect(const gfx::Rect& rect) {
void ZwpTextInputV3Impl::SendCursorRect(const gfx::Rect& rect) {
CHECK_EQ(commit_count_, last_done_serial_);
zwp_text_input_v3_set_cursor_rectangle(obj_.get(), rect.x(), rect.y(),
rect.width(), rect.height());
last_sent_cursor_rect_ = rect;
}
void ZWPTextInputWrapperV3::SetSurroundingText(
void ZwpTextInputV3Impl::SetSurroundingText(
const std::string& text_with_preedit,
const gfx::Range& preedit_range,
const gfx::Range& selection_range) {
@ -264,7 +248,7 @@ void ZWPTextInputWrapperV3::SetSurroundingText(
Commit();
}
void ZWPTextInputWrapperV3::SendSurroundingText(
void ZwpTextInputV3Impl::SendSurroundingText(
const SetSurroundingTextData& data) {
CHECK_EQ(commit_count_, last_done_serial_);
zwp_text_input_v3_set_surrounding_text(obj_.get(), data.text.c_str(),
@ -272,14 +256,17 @@ void ZWPTextInputWrapperV3::SendSurroundingText(
last_sent_surrounding_text_data_ = data;
}
void ZWPTextInputWrapperV3::SetContentType(ui::TextInputType type,
uint32_t flags,
bool should_do_learning) {
void ZwpTextInputV3Impl::SetContentType(ui::TextInputType type,
uint32_t flags,
bool should_do_learning) {
uint32_t content_hint = InputFlagsToContentHint(flags);
if (!should_do_learning) {
content_hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA;
}
uint32_t content_purpose = InputTypeToContentPurpose(type);
if (!should_do_learning) {
content_hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA;
}
ContentType content_type{content_hint, content_purpose};
if (last_sent_content_type_ == content_type) {
return;
@ -292,14 +279,14 @@ void ZWPTextInputWrapperV3::SetContentType(ui::TextInputType type,
Commit();
}
void ZWPTextInputWrapperV3::SendContentType(const ContentType& content_type) {
void ZwpTextInputV3Impl::SendContentType(const ContentType& content_type) {
CHECK_EQ(commit_count_, last_done_serial_);
zwp_text_input_v3_set_content_type(obj_.get(), content_type.content_hint,
content_type.content_purpose);
last_sent_content_type_ = content_type;
}
void ZWPTextInputWrapperV3::ApplyPendingSetRequests() {
void ZwpTextInputV3Impl::ApplyPendingSetRequests() {
bool commit = false;
if (auto content_type = pending_set_content_type_) {
SendContentType(*content_type);
@ -319,67 +306,66 @@ void ZWPTextInputWrapperV3::ApplyPendingSetRequests() {
}
}
void ZWPTextInputWrapperV3::ResetPendingSetRequests() {
void ZwpTextInputV3Impl::ResetPendingSetRequests() {
pending_set_cursor_rect_.reset();
pending_set_content_type_.reset();
pending_set_surrounding_text_.reset();
}
void ZWPTextInputWrapperV3::ResetLastSentValues() {
void ZwpTextInputV3Impl::ResetLastSentValues() {
last_sent_cursor_rect_.reset();
last_sent_content_type_.reset();
last_sent_surrounding_text_data_.reset();
}
void ZWPTextInputWrapperV3::ResetPendingInputEvents() {
void ZwpTextInputV3Impl::ResetPendingInputEvents() {
pending_preedit_.reset();
pending_commit_.reset();
}
void ZWPTextInputWrapperV3::Commit() {
void ZwpTextInputV3Impl::Commit() {
zwp_text_input_v3_commit(obj_.get());
// It will wrap around to 0 once it reaches uint32_t max value. It is
// expected that this will occur on the compositor side as well.
++commit_count_;
}
void ZWPTextInputWrapperV3::OnEnter(void* data,
struct zwp_text_input_v3* text_input,
struct wl_surface* surface) {
void ZwpTextInputV3Impl::OnEnter(void* data,
struct zwp_text_input_v3* text_input,
struct wl_surface* surface) {
// Same as text-input-v1, we don't use this for text-input focus changes and
// instead use wayland keyboard enter/leave events to activate or deactivate
// text-input.
NOTIMPLEMENTED_LOG_ONCE();
}
void ZWPTextInputWrapperV3::OnLeave(void* data,
struct zwp_text_input_v3* text_input,
struct wl_surface* surface) {
void ZwpTextInputV3Impl::OnLeave(void* data,
struct zwp_text_input_v3* text_input,
struct wl_surface* surface) {
// Same as text-input-v1, we don't use this for text-input focus changes and
// instead use wayland keyboard enter/leave events to activate or deactivate
// text-input.
NOTIMPLEMENTED_LOG_ONCE();
}
void ZWPTextInputWrapperV3::OnPreeditString(
void* data,
struct zwp_text_input_v3* text_input,
const char* text,
int32_t cursor_begin,
int32_t cursor_end) {
auto* self = static_cast<ZWPTextInputWrapperV3*>(data);
void ZwpTextInputV3Impl::OnPreeditString(void* data,
struct zwp_text_input_v3* text_input,
const char* text,
int32_t cursor_begin,
int32_t cursor_end) {
auto* self = static_cast<ZwpTextInputV3Impl*>(data);
self->pending_preedit_ = {text ? text : std::string(), cursor_begin,
cursor_end};
}
void ZWPTextInputWrapperV3::OnCommitString(void* data,
struct zwp_text_input_v3* text_input,
const char* text) {
auto* self = static_cast<ZWPTextInputWrapperV3*>(data);
void ZwpTextInputV3Impl::OnCommitString(void* data,
struct zwp_text_input_v3* text_input,
const char* text) {
auto* self = static_cast<ZwpTextInputV3Impl*>(data);
self->pending_commit_ = text ? text : std::string();
}
void ZWPTextInputWrapperV3::OnDeleteSurroundingText(
void ZwpTextInputV3Impl::OnDeleteSurroundingText(
void* data,
struct zwp_text_input_v3* text_input,
uint32_t before_length,
@ -387,11 +373,16 @@ void ZWPTextInputWrapperV3::OnDeleteSurroundingText(
NOTIMPLEMENTED_LOG_ONCE();
}
void ZWPTextInputWrapperV3::OnDone(void* data,
struct zwp_text_input_v3* text_input,
uint32_t serial) {
void ZwpTextInputV3Impl::OnDone(void* data,
struct zwp_text_input_v3* text_input,
uint32_t serial) {
// TODO(crbug.com/40113488) apply delete surrounding
auto* self = static_cast<ZWPTextInputWrapperV3*>(data);
auto* self = static_cast<ZwpTextInputV3Impl*>(data);
if (!self->client_) {
self->last_done_serial_ = serial;
return;
}
if (const auto& commit_string = self->pending_commit_) {
// Replace the existing preedit with the commit string.

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_ZWP_TEXT_INPUT_WRAPPER_V3_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_ZWP_TEXT_INPUT_WRAPPER_V3_H_
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_ZWP_TEXT_INPUT_V3_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_ZWP_TEXT_INPUT_V3_H_
#include <text-input-unstable-v3-client-protocol.h>
@ -11,34 +11,69 @@
#include <string>
#include "base/memory/raw_ptr.h"
#include "ui/base/ime/text_input_mode.h"
#include "ui/base/ime/text_input_type.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/range/range.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/host/zwp_text_input_wrapper.h"
namespace ui {
struct SpanStyle;
class WaylandConnection;
class WaylandWindow;
// Text input wrapper for text-input-unstable-v3
class ZWPTextInputWrapperV3 : public ZWPTextInputWrapper {
class ZwpTextInputV3Client {
public:
ZWPTextInputWrapperV3(WaylandConnection* connection,
ZWPTextInputWrapperClient* client,
zwp_text_input_manager_v3* text_input_manager);
ZWPTextInputWrapperV3(const ZWPTextInputWrapperV3&) = delete;
ZWPTextInputWrapperV3& operator=(const ZWPTextInputWrapperV3&) = delete;
~ZWPTextInputWrapperV3() override;
virtual ~ZwpTextInputV3Client() = default;
// Called when a new composing text (pre-edit) should be set around the
// current cursor position. Any previously set composing text should
// be removed.
// Note that the preedit_cursor is byte-offset. It is the pre-edit cursor
// position if the range is empty and selection otherwise.
virtual void OnPreeditString(std::string_view text,
const std::vector<SpanStyle>& spans,
const gfx::Range& preedit_cursor) = 0;
// Called when a complete input sequence has been entered. The text to
// commit could be either just a single character after a key press or the
// result of some composing (pre-edit).
virtual void OnCommitString(std::string_view text) = 0;
};
class ZwpTextInputV3 {
public:
virtual ~ZwpTextInputV3() = default;
virtual void SetClient(ZwpTextInputV3Client* context) = 0;
virtual void OnClientDestroyed(ZwpTextInputV3Client* context) = 0;
virtual void Enable() = 0;
virtual void Disable() = 0;
virtual void Reset() = 0;
virtual void SetCursorRect(const gfx::Rect& rect) = 0;
virtual void SetSurroundingText(const std::string& text,
const gfx::Range& preedit_range,
const gfx::Range& selection_range) = 0;
virtual void SetContentType(TextInputType type,
uint32_t flags,
bool should_do_learning) = 0;
};
// Represents a zwp_text_input_v3 object.
class ZwpTextInputV3Impl : public ZwpTextInputV3 {
public:
ZwpTextInputV3Impl(WaylandConnection* connection,
zwp_text_input_manager_v3* text_input_manager);
ZwpTextInputV3Impl(const ZwpTextInputV3Impl&) = delete;
ZwpTextInputV3Impl& operator=(const ZwpTextInputV3Impl&) = delete;
~ZwpTextInputV3Impl() override;
void SetClient(ZwpTextInputV3Client* context) override;
void OnClientDestroyed(ZwpTextInputV3Client* context) override;
void Enable() override;
void Disable() override;
void Reset() override;
void Activate(WaylandWindow* window,
ui::TextInputClient::FocusReason reason) override;
void Deactivate() override;
void ShowInputPanel() override;
void HideInputPanel() override;
void SetCursorRect(const gfx::Rect& rect) override;
void SetSurroundingText(const std::string& text,
const gfx::Range& preedit_range,
@ -114,7 +149,7 @@ class ZWPTextInputWrapperV3 : public ZWPTextInputWrapper {
const raw_ptr<WaylandConnection> connection_;
wl::Object<zwp_text_input_v3> obj_;
const raw_ptr<ZWPTextInputWrapperClient> client_;
raw_ptr<ZwpTextInputV3Client> client_;
uint32_t commit_count_ = 0;
uint32_t last_done_serial_ = 0;
@ -135,4 +170,4 @@ class ZWPTextInputWrapperV3 : public ZWPTextInputWrapper {
} // namespace ui
#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_ZWP_TEXT_INPUT_WRAPPER_V3_H_
#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_ZWP_TEXT_INPUT_V3_H_

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v3.h"
#include "ui/ozone/platform/wayland/host/zwp_text_input_v3.h"
#include <text-input-unstable-v3-client-protocol.h>
#include <text-input-unstable-v3-server-protocol.h>
@ -14,9 +14,10 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/ime/text_input_flags.h"
#include "ui/gfx/range/range.h"
#include "ui/ozone/platform/wayland/host/span_style.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/test/mock_zwp_text_input.h"
#include "ui/ozone/platform/wayland/test/mock_zwp_text_input_wrapper_client.h"
#include "ui/ozone/platform/wayland/test/mock_zwp_text_input_v3_client.h"
#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
#include "ui/ozone/platform/wayland/test/wayland_test.h"
@ -26,17 +27,17 @@ using ::testing::Mock;
namespace ui {
class ZWPTextInputWrapperV3Test : public WaylandTestSimple {
class ZwpTextInputV3Test : public WaylandTestSimple {
public:
ZWPTextInputWrapperV3Test()
: WaylandTestSimple(
{.text_input_wrapper_type = wl::ZWPTextInputWrapperType::kV3}) {}
ZwpTextInputV3Test()
: WaylandTestSimple({.text_input_type = wl::ZwpTextInputType::kV3}) {}
void SetUp() override {
WaylandTestSimple::SetUp();
wrapper_ = std::make_unique<ZWPTextInputWrapperV3>(
connection_.get(), &test_client_, connection_->text_input_manager_v3());
text_input_v3_ = std::make_unique<ZwpTextInputV3Impl>(
connection_.get(), connection_->text_input_manager_v3());
text_input_v3_->SetClient(&test_client_);
}
protected:
@ -44,11 +45,11 @@ class ZWPTextInputWrapperV3Test : public WaylandTestSimple {
Mock::VerifyAndClearExpectations(&test_client_);
}
MockZWPTextInputWrapperClient test_client_;
std::unique_ptr<ZWPTextInputWrapperV3> wrapper_;
MockZwpTextInputV3Client test_client_;
std::unique_ptr<ZwpTextInputV3> text_input_v3_;
};
TEST_F(ZWPTextInputWrapperV3Test, Activate) {
TEST_F(ZwpTextInputV3Test, Enable) {
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
InSequence s;
EXPECT_CALL(*server->text_input_manager_v3()->text_input(), Enable())
@ -56,10 +57,10 @@ TEST_F(ZWPTextInputWrapperV3Test, Activate) {
EXPECT_CALL(*server->text_input_manager_v3()->text_input(), Commit())
.Times(1);
});
wrapper_->Activate(window_.get(), ui::TextInputClient::FOCUS_REASON_NONE);
text_input_v3_->Enable();
}
TEST_F(ZWPTextInputWrapperV3Test, Deactivate) {
TEST_F(ZwpTextInputV3Test, Disable) {
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
InSequence s;
EXPECT_CALL(*server->text_input_manager_v3()->text_input(), Disable())
@ -67,10 +68,10 @@ TEST_F(ZWPTextInputWrapperV3Test, Deactivate) {
EXPECT_CALL(*server->text_input_manager_v3()->text_input(), Commit())
.Times(1);
});
wrapper_->Deactivate();
text_input_v3_->Disable();
}
TEST_F(ZWPTextInputWrapperV3Test, Reset) {
TEST_F(ZwpTextInputV3Test, Reset) {
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
InSequence s;
EXPECT_CALL(*server->text_input_manager_v3()->text_input(), Disable())
@ -82,34 +83,10 @@ TEST_F(ZWPTextInputWrapperV3Test, Reset) {
EXPECT_CALL(*server->text_input_manager_v3()->text_input(), Commit())
.Times(1);
});
wrapper_->Reset();
text_input_v3_->Reset();
}
TEST_F(ZWPTextInputWrapperV3Test, ShowInputPanel) {
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->text_input_manager_v3()->text_input(), Enable())
.Times(0);
EXPECT_CALL(*server->text_input_manager_v3()->text_input(), Disable())
.Times(0);
EXPECT_CALL(*server->text_input_manager_v3()->text_input(), Commit())
.Times(0);
});
wrapper_->ShowInputPanel();
}
TEST_F(ZWPTextInputWrapperV3Test, HideInputPanel) {
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->text_input_manager_v3()->text_input(), Enable())
.Times(0);
EXPECT_CALL(*server->text_input_manager_v3()->text_input(), Disable())
.Times(0);
EXPECT_CALL(*server->text_input_manager_v3()->text_input(), Commit())
.Times(0);
});
wrapper_->HideInputPanel();
}
TEST_F(ZWPTextInputWrapperV3Test, SetContentType) {
TEST_F(ZwpTextInputV3Test, SetContentType) {
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
InSequence s;
auto* zwp_text_input = server->text_input_manager_v3()->text_input();
@ -121,8 +98,8 @@ TEST_F(ZWPTextInputWrapperV3Test, SetContentType) {
.Times(1);
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
});
wrapper_->SetContentType(TEXT_INPUT_TYPE_EMAIL,
TEXT_INPUT_FLAG_AUTOCORRECT_ON, false);
text_input_v3_->SetContentType(TEXT_INPUT_TYPE_EMAIL,
TEXT_INPUT_FLAG_AUTOCORRECT_ON, false);
VerifyAndClearExpectations();
// Calling again with the same values should be a no-op.
@ -133,8 +110,8 @@ TEST_F(ZWPTextInputWrapperV3Test, SetContentType) {
// Commit has been called once. So send done serial matching commit.
zwp_text_input_v3_send_done(zwp_text_input->resource(), 1);
});
wrapper_->SetContentType(TEXT_INPUT_TYPE_EMAIL,
TEXT_INPUT_FLAG_AUTOCORRECT_ON, false);
text_input_v3_->SetContentType(TEXT_INPUT_TYPE_EMAIL,
TEXT_INPUT_FLAG_AUTOCORRECT_ON, false);
VerifyAndClearExpectations();
// Calling with different values should work.
@ -148,11 +125,11 @@ TEST_F(ZWPTextInputWrapperV3Test, SetContentType) {
.Times(1);
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
});
wrapper_->SetContentType(TEXT_INPUT_TYPE_NUMBER,
TEXT_INPUT_FLAG_AUTOCAPITALIZE_WORDS, false);
text_input_v3_->SetContentType(TEXT_INPUT_TYPE_NUMBER,
TEXT_INPUT_FLAG_AUTOCAPITALIZE_WORDS, false);
}
TEST_F(ZWPTextInputWrapperV3Test, SetCursorRect) {
TEST_F(ZwpTextInputV3Test, SetCursorRect) {
constexpr gfx::Rect kRect(50, 20, 1, 1);
PostToServerAndWait([kRect](wl::TestWaylandServerThread* server) {
InSequence s;
@ -161,7 +138,7 @@ TEST_F(ZWPTextInputWrapperV3Test, SetCursorRect) {
kRect.width(), kRect.height()));
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
});
wrapper_->SetCursorRect(kRect);
text_input_v3_->SetCursorRect(kRect);
VerifyAndClearExpectations();
// Calling again with the same values should be a no-op.
@ -172,7 +149,7 @@ TEST_F(ZWPTextInputWrapperV3Test, SetCursorRect) {
// Commit has been called once. So send done serial matching commit.
zwp_text_input_v3_send_done(zwp_text_input->resource(), 1);
});
wrapper_->SetCursorRect(kRect);
text_input_v3_->SetCursorRect(kRect);
VerifyAndClearExpectations();
// Calling again with different values should work.
@ -185,11 +162,11 @@ TEST_F(ZWPTextInputWrapperV3Test, SetCursorRect) {
SetCursorRect(kRect2.x(), kRect2.y(), kRect2.width(), kRect2.height()));
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
});
wrapper_->SetCursorRect(kRect2);
text_input_v3_->SetCursorRect(kRect2);
VerifyAndClearExpectations();
}
TEST_F(ZWPTextInputWrapperV3Test, SetSurroundingText) {
TEST_F(ZwpTextInputV3Test, SetSurroundingText) {
const std::string text("surroundingすしはおいしいですtext");
constexpr std::string kSurroundingText("surroundingtext");
constexpr gfx::Range kPreeditRange(11, 38);
@ -204,7 +181,7 @@ TEST_F(ZWPTextInputWrapperV3Test, SetSurroundingText) {
EXPECT_CALL(*server->text_input_manager_v3()->text_input(), Commit())
.Times(1);
});
wrapper_->SetSurroundingText(text, kPreeditRange, selection_range);
text_input_v3_->SetSurroundingText(text, kPreeditRange, selection_range);
VerifyAndClearExpectations();
// values unchanged
@ -218,7 +195,7 @@ TEST_F(ZWPTextInputWrapperV3Test, SetSurroundingText) {
// Ensure done serial matches commit count.
zwp_text_input_v3_send_done(zwp_text_input->resource(), 1);
});
wrapper_->SetSurroundingText(text2, preedit_range2, selection_range);
text_input_v3_->SetSurroundingText(text2, preedit_range2, selection_range);
VerifyAndClearExpectations();
// selection before preedit
@ -230,7 +207,7 @@ TEST_F(ZWPTextInputWrapperV3Test, SetSurroundingText) {
.Times(1);
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
});
wrapper_->SetSurroundingText(text, kPreeditRange, selection_range);
text_input_v3_->SetSurroundingText(text, kPreeditRange, selection_range);
VerifyAndClearExpectations();
// selection bounded by preedit
@ -243,7 +220,7 @@ TEST_F(ZWPTextInputWrapperV3Test, SetSurroundingText) {
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
zwp_text_input_v3_send_done(zwp_text_input->resource(), 2);
});
wrapper_->SetSurroundingText(text, kPreeditRange, selection_range);
text_input_v3_->SetSurroundingText(text, kPreeditRange, selection_range);
VerifyAndClearExpectations();
// selection starts inside preedit and ends after preedit
@ -256,7 +233,7 @@ TEST_F(ZWPTextInputWrapperV3Test, SetSurroundingText) {
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
zwp_text_input_v3_send_done(zwp_text_input->resource(), 3);
});
wrapper_->SetSurroundingText(text, kPreeditRange, selection_range);
text_input_v3_->SetSurroundingText(text, kPreeditRange, selection_range);
VerifyAndClearExpectations();
// selection starts inside preedit and ends after preedit (inverted selection)
@ -269,7 +246,7 @@ TEST_F(ZWPTextInputWrapperV3Test, SetSurroundingText) {
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
zwp_text_input_v3_send_done(zwp_text_input->resource(), 4);
});
wrapper_->SetSurroundingText(text, kPreeditRange, selection_range);
text_input_v3_->SetSurroundingText(text, kPreeditRange, selection_range);
VerifyAndClearExpectations();
// selection starts before preedit and ends inside preedit
@ -282,7 +259,7 @@ TEST_F(ZWPTextInputWrapperV3Test, SetSurroundingText) {
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
zwp_text_input_v3_send_done(zwp_text_input->resource(), 5);
});
wrapper_->SetSurroundingText(text, kPreeditRange, selection_range);
text_input_v3_->SetSurroundingText(text, kPreeditRange, selection_range);
VerifyAndClearExpectations();
// selection starts before preedit and ends inside preedit (inverted
@ -296,7 +273,7 @@ TEST_F(ZWPTextInputWrapperV3Test, SetSurroundingText) {
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
zwp_text_input_v3_send_done(zwp_text_input->resource(), 6);
});
wrapper_->SetSurroundingText(text, kPreeditRange, selection_range);
text_input_v3_->SetSurroundingText(text, kPreeditRange, selection_range);
VerifyAndClearExpectations();
// selection starts before preedit and ends after preedit
@ -309,7 +286,7 @@ TEST_F(ZWPTextInputWrapperV3Test, SetSurroundingText) {
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
zwp_text_input_v3_send_done(zwp_text_input->resource(), 7);
});
wrapper_->SetSurroundingText(text, kPreeditRange, selection_range);
text_input_v3_->SetSurroundingText(text, kPreeditRange, selection_range);
VerifyAndClearExpectations();
// selection after preedit
@ -322,7 +299,7 @@ TEST_F(ZWPTextInputWrapperV3Test, SetSurroundingText) {
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
zwp_text_input_v3_send_done(zwp_text_input->resource(), 8);
});
wrapper_->SetSurroundingText(text, kPreeditRange, selection_range);
text_input_v3_->SetSurroundingText(text, kPreeditRange, selection_range);
VerifyAndClearExpectations();
// invalid preedit
@ -335,8 +312,8 @@ TEST_F(ZWPTextInputWrapperV3Test, SetSurroundingText) {
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
zwp_text_input_v3_send_done(zwp_text_input->resource(), 9);
});
wrapper_->SetSurroundingText(kSurroundingText, gfx::Range::InvalidRange(),
selection_range);
text_input_v3_->SetSurroundingText(
kSurroundingText, gfx::Range::InvalidRange(), selection_range);
VerifyAndClearExpectations();
// invalid selection
@ -348,7 +325,8 @@ TEST_F(ZWPTextInputWrapperV3Test, SetSurroundingText) {
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
zwp_text_input_v3_send_done(zwp_text_input->resource(), 10);
});
wrapper_->SetSurroundingText(text, kPreeditRange, gfx::Range::InvalidRange());
text_input_v3_->SetSurroundingText(text, kPreeditRange,
gfx::Range::InvalidRange());
VerifyAndClearExpectations();
// invalid preedit and selection
@ -359,12 +337,12 @@ TEST_F(ZWPTextInputWrapperV3Test, SetSurroundingText) {
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
zwp_text_input_v3_send_done(zwp_text_input->resource(), 11);
});
wrapper_->SetSurroundingText(text, gfx::Range::InvalidRange(),
gfx::Range::InvalidRange());
text_input_v3_->SetSurroundingText(text, gfx::Range::InvalidRange(),
gfx::Range::InvalidRange());
VerifyAndClearExpectations();
}
TEST_F(ZWPTextInputWrapperV3Test, PendingRequestsSentOnDone) {
TEST_F(ZwpTextInputV3Test, PendingRequestsSentOnDone) {
constexpr gfx::Rect kRect(50, 20, 1, 1);
const std::string text("surroundingすしはおいしいですtext");
constexpr std::string kSurroundingText("surroundingtext");
@ -375,18 +353,18 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingRequestsSentOnDone) {
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
auto* zwp_text_input = server->text_input_manager_v3()->text_input();
InSequence s;
EXPECT_CALL(*zwp_text_input, Enable()).Times(1);
EXPECT_CALL(*zwp_text_input, Enable());
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
});
wrapper_->Activate(window_.get(), ui::TextInputClient::FOCUS_REASON_NONE);
text_input_v3_->Enable();
VerifyAndClearExpectations();
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
auto* zwp_text_input = server->text_input_manager_v3()->text_input();
InSequence s;
EXPECT_CALL(*zwp_text_input, Enable()).Times(1);
EXPECT_CALL(*zwp_text_input, Enable());
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
});
wrapper_->Activate(window_.get(), ui::TextInputClient::FOCUS_REASON_NONE);
text_input_v3_->Enable();
VerifyAndClearExpectations();
// Now if commit number doesn't match done serial it shouldn't send a request.
@ -399,36 +377,36 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingRequestsSentOnDone) {
// Commit has been called twice. So done serial 1 should not match.
zwp_text_input_v3_send_done(zwp_text_input->resource(), 1);
});
wrapper_->SetSurroundingText(text, kPreeditRange, kSelectionRange);
wrapper_->SetCursorRect(kRect);
wrapper_->SetContentType(TEXT_INPUT_TYPE_EMAIL,
TEXT_INPUT_FLAG_AUTOCORRECT_ON, true);
text_input_v3_->SetSurroundingText(text, kPreeditRange, kSelectionRange);
text_input_v3_->SetCursorRect(kRect);
text_input_v3_->SetContentType(TEXT_INPUT_TYPE_EMAIL,
TEXT_INPUT_FLAG_AUTOCORRECT_ON, true);
VerifyAndClearExpectations();
// Multiple pending requests should be sent when commit number finally
// matches.
PostToServerAndWait([kRect,
kSurroundingText](wl::TestWaylandServerThread* server) {
auto* zwp_text_input = server->text_input_manager_v3()->text_input();
InSequence s;
EXPECT_CALL(*zwp_text_input,
SetContentType(ZWP_TEXT_INPUT_V3_CONTENT_HINT_SPELLCHECK,
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_EMAIL))
.Times(1);
EXPECT_CALL(
*server->text_input_manager_v3()->text_input(),
SetCursorRect(kRect.x(), kRect.y(), kRect.width(), kRect.height()));
EXPECT_CALL(*zwp_text_input,
SetSurroundingText(kSurroundingText, gfx::Range{11, 11}))
.Times(1);
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
// Commit has been called twice. So done serial 2 should match.
zwp_text_input_v3_send_done(zwp_text_input->resource(), 2);
});
PostToServerAndWait(
[kRect, kSurroundingText](wl::TestWaylandServerThread* server) {
auto* zwp_text_input = server->text_input_manager_v3()->text_input();
InSequence s;
EXPECT_CALL(*zwp_text_input,
SetContentType(ZWP_TEXT_INPUT_V3_CONTENT_HINT_SPELLCHECK,
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_EMAIL))
.Times(1);
EXPECT_CALL(
*server->text_input_manager_v3()->text_input(),
SetCursorRect(kRect.x(), kRect.y(), kRect.width(), kRect.height()));
EXPECT_CALL(*zwp_text_input,
SetSurroundingText(kSurroundingText, gfx::Range{11, 11}))
.Times(1);
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
// Commit has been called twice. So done serial 2 should match.
zwp_text_input_v3_send_done(zwp_text_input->resource(), 2);
});
VerifyAndClearExpectations();
}
TEST_F(ZWPTextInputWrapperV3Test, PendingRequestsClearedOnEnable) {
TEST_F(ZwpTextInputV3Test, PendingRequestsClearedOnEnable) {
constexpr gfx::Rect kRect(50, 20, 1, 1);
const std::string text("surroundingすしはおいしいですtext");
constexpr gfx::Range kPreeditRange(11, 38);
@ -438,10 +416,10 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingRequestsClearedOnEnable) {
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
auto* zwp_text_input = server->text_input_manager_v3()->text_input();
InSequence s;
EXPECT_CALL(*zwp_text_input, Enable()).Times(1);
EXPECT_CALL(*zwp_text_input, Enable());
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
});
wrapper_->Activate(window_.get(), ui::TextInputClient::FOCUS_REASON_NONE);
text_input_v3_->Enable();
VerifyAndClearExpectations();
// Pending set requests should not be sent without matching done event.
@ -452,20 +430,20 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingRequestsClearedOnEnable) {
EXPECT_CALL(*zwp_text_input, SetSurroundingText(_, _)).Times(0);
EXPECT_CALL(*zwp_text_input, Commit()).Times(0);
});
wrapper_->SetSurroundingText(text, kPreeditRange, kSelectionRange);
wrapper_->SetCursorRect(kRect);
wrapper_->SetContentType(TEXT_INPUT_TYPE_EMAIL,
TEXT_INPUT_FLAG_AUTOCORRECT_ON, true);
text_input_v3_->SetSurroundingText(text, kPreeditRange, kSelectionRange);
text_input_v3_->SetCursorRect(kRect);
text_input_v3_->SetContentType(TEXT_INPUT_TYPE_EMAIL,
TEXT_INPUT_FLAG_AUTOCORRECT_ON, true);
VerifyAndClearExpectations();
// Enable should clear pending requests.
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
auto* zwp_text_input = server->text_input_manager_v3()->text_input();
InSequence s;
EXPECT_CALL(*zwp_text_input, Enable()).Times(1);
EXPECT_CALL(*zwp_text_input, Enable());
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
});
wrapper_->Activate(window_.get(), ui::TextInputClient::FOCUS_REASON_NONE);
text_input_v3_->Enable();
VerifyAndClearExpectations();
// Since there are no more pending requests nothing should be sent even if
@ -482,7 +460,7 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingRequestsClearedOnEnable) {
VerifyAndClearExpectations();
}
TEST_F(ZWPTextInputWrapperV3Test, PendingRequestsClearedOnDisable) {
TEST_F(ZwpTextInputV3Test, PendingRequestsClearedOnDisable) {
constexpr gfx::Rect kRect(50, 20, 1, 1);
const std::string text("surroundingすしはおいしいですtext");
constexpr gfx::Range kPreeditRange(11, 38);
@ -492,10 +470,10 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingRequestsClearedOnDisable) {
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
auto* zwp_text_input = server->text_input_manager_v3()->text_input();
InSequence s;
EXPECT_CALL(*zwp_text_input, Enable()).Times(1);
EXPECT_CALL(*zwp_text_input, Enable());
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
});
wrapper_->Activate(window_.get(), ui::TextInputClient::FOCUS_REASON_NONE);
text_input_v3_->Enable();
VerifyAndClearExpectations();
// Pending set requests should not be sent without matching done event.
@ -506,10 +484,10 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingRequestsClearedOnDisable) {
EXPECT_CALL(*zwp_text_input, SetSurroundingText(_, _)).Times(0);
EXPECT_CALL(*zwp_text_input, Commit()).Times(0);
});
wrapper_->SetSurroundingText(text, kPreeditRange, kSelectionRange);
wrapper_->SetCursorRect(kRect);
wrapper_->SetContentType(TEXT_INPUT_TYPE_EMAIL,
TEXT_INPUT_FLAG_AUTOCORRECT_ON, true);
text_input_v3_->SetSurroundingText(text, kPreeditRange, kSelectionRange);
text_input_v3_->SetCursorRect(kRect);
text_input_v3_->SetContentType(TEXT_INPUT_TYPE_EMAIL,
TEXT_INPUT_FLAG_AUTOCORRECT_ON, true);
VerifyAndClearExpectations();
// Disable should clear pending requests.
@ -519,7 +497,7 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingRequestsClearedOnDisable) {
EXPECT_CALL(*zwp_text_input, Disable()).Times(1);
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
});
wrapper_->Deactivate();
text_input_v3_->Disable();
VerifyAndClearExpectations();
// Since there are no more pending requests nothing should be sent even if
@ -536,7 +514,7 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingRequestsClearedOnDisable) {
VerifyAndClearExpectations();
}
TEST_F(ZWPTextInputWrapperV3Test, PendingRequestsClearedOnReset) {
TEST_F(ZwpTextInputV3Test, PendingRequestsClearedOnReset) {
constexpr gfx::Rect kRect(50, 20, 1, 1);
const std::string text("surroundingすしはおいしいですtext");
constexpr gfx::Range kPreeditRange(11, 38);
@ -546,10 +524,10 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingRequestsClearedOnReset) {
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
auto* zwp_text_input = server->text_input_manager_v3()->text_input();
InSequence s;
EXPECT_CALL(*zwp_text_input, Enable()).Times(1);
EXPECT_CALL(*zwp_text_input, Enable());
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
});
wrapper_->Activate(window_.get(), ui::TextInputClient::FOCUS_REASON_NONE);
text_input_v3_->Enable();
// Pending set requests should not be sent without matching done event.
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
@ -559,10 +537,10 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingRequestsClearedOnReset) {
EXPECT_CALL(*zwp_text_input, SetSurroundingText(_, _)).Times(0);
EXPECT_CALL(*zwp_text_input, Commit()).Times(0);
});
wrapper_->SetSurroundingText(text, kPreeditRange, kSelectionRange);
wrapper_->SetCursorRect(kRect);
wrapper_->SetContentType(TEXT_INPUT_TYPE_EMAIL,
TEXT_INPUT_FLAG_AUTOCORRECT_ON, true);
text_input_v3_->SetSurroundingText(text, kPreeditRange, kSelectionRange);
text_input_v3_->SetCursorRect(kRect);
text_input_v3_->SetContentType(TEXT_INPUT_TYPE_EMAIL,
TEXT_INPUT_FLAG_AUTOCORRECT_ON, true);
VerifyAndClearExpectations();
// Reset should clear pending requests.
@ -571,10 +549,10 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingRequestsClearedOnReset) {
InSequence s;
EXPECT_CALL(*zwp_text_input, Disable()).Times(1);
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
EXPECT_CALL(*zwp_text_input, Enable()).Times(1);
EXPECT_CALL(*zwp_text_input, Enable());
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
});
wrapper_->Reset();
text_input_v3_->Reset();
VerifyAndClearExpectations();
// Since there are no more pending requests nothing should be sent even if
@ -592,14 +570,12 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingRequestsClearedOnReset) {
VerifyAndClearExpectations();
}
TEST_F(ZWPTextInputWrapperV3Test, OnPreeditString) {
TEST_F(ZwpTextInputV3Test, OnPreeditString) {
constexpr std::string_view kPreeditString("PreeditString");
constexpr gfx::Range kPreeditCursor{0, 13};
EXPECT_CALL(
test_client_,
OnPreeditString(kPreeditString,
std::vector<ZWPTextInputWrapperClient::SpanStyle>{},
kPreeditCursor));
EXPECT_CALL(test_client_,
OnPreeditString(kPreeditString, std::vector<SpanStyle>{},
kPreeditCursor));
PostToServerAndWait(
[kPreeditString, kPreeditCursor](wl::TestWaylandServerThread* server) {
auto* text_input = server->text_input_manager_v3()->text_input();
@ -611,11 +587,9 @@ TEST_F(ZWPTextInputWrapperV3Test, OnPreeditString) {
VerifyAndClearExpectations();
// Invalid range if negative cursor begin
EXPECT_CALL(
test_client_,
OnPreeditString(kPreeditString,
std::vector<ZWPTextInputWrapperClient::SpanStyle>{},
gfx::Range::InvalidRange()));
EXPECT_CALL(test_client_,
OnPreeditString(kPreeditString, std::vector<SpanStyle>{},
gfx::Range::InvalidRange()));
PostToServerAndWait(
[kPreeditString, kPreeditCursor](wl::TestWaylandServerThread* server) {
auto* text_input = server->text_input_manager_v3()->text_input();
@ -627,11 +601,9 @@ TEST_F(ZWPTextInputWrapperV3Test, OnPreeditString) {
VerifyAndClearExpectations();
// Invalid range if negative cursor end
EXPECT_CALL(
test_client_,
OnPreeditString(kPreeditString,
std::vector<ZWPTextInputWrapperClient::SpanStyle>{},
gfx::Range::InvalidRange()));
EXPECT_CALL(test_client_,
OnPreeditString(kPreeditString, std::vector<SpanStyle>{},
gfx::Range::InvalidRange()));
PostToServerAndWait(
[kPreeditString, kPreeditCursor](wl::TestWaylandServerThread* server) {
auto* text_input = server->text_input_manager_v3()->text_input();
@ -643,7 +615,7 @@ TEST_F(ZWPTextInputWrapperV3Test, OnPreeditString) {
VerifyAndClearExpectations();
}
TEST_F(ZWPTextInputWrapperV3Test, OnCommitString) {
TEST_F(ZwpTextInputV3Test, OnCommitString) {
constexpr std::string kCommitString("CommitString");
EXPECT_CALL(test_client_, OnCommitString(kCommitString));
PostToServerAndWait([kCommitString](wl::TestWaylandServerThread* server) {
@ -655,17 +627,15 @@ TEST_F(ZWPTextInputWrapperV3Test, OnCommitString) {
VerifyAndClearExpectations();
}
TEST_F(ZWPTextInputWrapperV3Test, OnDoneWithCommitAndPreedit) {
TEST_F(ZwpTextInputV3Test, OnDoneWithCommitAndPreedit) {
constexpr std::string kPreeditString("PreeditString");
constexpr gfx::Range kPreeditCursor{0, 13};
constexpr std::string kCommitString("CommitString");
InSequence s;
EXPECT_CALL(test_client_, OnCommitString(kCommitString));
EXPECT_CALL(
test_client_,
OnPreeditString(kPreeditString,
std::vector<ZWPTextInputWrapperClient::SpanStyle>{},
kPreeditCursor));
EXPECT_CALL(test_client_,
OnPreeditString(kPreeditString, std::vector<SpanStyle>{},
kPreeditCursor));
PostToServerAndWait([kPreeditString, kPreeditCursor,
kCommitString](wl::TestWaylandServerThread* server) {
auto* text_input = server->text_input_manager_v3()->text_input();
@ -679,7 +649,7 @@ TEST_F(ZWPTextInputWrapperV3Test, OnDoneWithCommitAndPreedit) {
VerifyAndClearExpectations();
}
TEST_F(ZWPTextInputWrapperV3Test, PendingInputEventsClearedOnEnable) {
TEST_F(ZwpTextInputV3Test, PendingInputEventsClearedOnEnable) {
constexpr std::string kCommitString("CommitString");
constexpr std::string_view kPreeditString("PreeditString");
constexpr gfx::Range kPreeditCursor{0, 13};
@ -697,10 +667,10 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingInputEventsClearedOnEnable) {
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
auto* zwp_text_input = server->text_input_manager_v3()->text_input();
InSequence s;
EXPECT_CALL(*zwp_text_input, Enable()).Times(1);
EXPECT_CALL(*zwp_text_input, Enable());
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
});
wrapper_->Activate(window_.get(), ui::TextInputClient::FOCUS_REASON_NONE);
text_input_v3_->Enable();
VerifyAndClearExpectations();
// Sending done should have no effect.
@ -713,7 +683,7 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingInputEventsClearedOnEnable) {
VerifyAndClearExpectations();
}
TEST_F(ZWPTextInputWrapperV3Test, PendingInputEventsClearedOnDisable) {
TEST_F(ZwpTextInputV3Test, PendingInputEventsClearedOnDisable) {
constexpr std::string_view kPreeditString("PreeditString");
constexpr gfx::Range kPreeditCursor{0, 13};
PostToServerAndWait(
@ -731,7 +701,7 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingInputEventsClearedOnDisable) {
EXPECT_CALL(*zwp_text_input, Disable()).Times(1);
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
});
wrapper_->Deactivate();
text_input_v3_->Disable();
VerifyAndClearExpectations();
// Sending done should have no effect.
@ -743,7 +713,7 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingInputEventsClearedOnDisable) {
VerifyAndClearExpectations();
}
TEST_F(ZWPTextInputWrapperV3Test, PendingInputEventsClearedOnReset) {
TEST_F(ZwpTextInputV3Test, PendingInputEventsClearedOnReset) {
constexpr std::string kCommitString("CommitString");
constexpr std::string_view kPreeditString("PreeditString");
constexpr gfx::Range kPreeditCursor{0, 13};
@ -763,10 +733,10 @@ TEST_F(ZWPTextInputWrapperV3Test, PendingInputEventsClearedOnReset) {
InSequence s;
EXPECT_CALL(*zwp_text_input, Disable()).Times(1);
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
EXPECT_CALL(*zwp_text_input, Enable()).Times(1);
EXPECT_CALL(*zwp_text_input, Enable());
EXPECT_CALL(*zwp_text_input, Commit()).Times(1);
});
wrapper_->Reset();
text_input_v3_->Reset();
VerifyAndClearExpectations();
// Sending done should have no effect.

@ -1,128 +0,0 @@
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_ZWP_TEXT_INPUT_WRAPPER_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_ZWP_TEXT_INPUT_WRAPPER_H_
#include <stdint.h>
#include <string>
#include <string_view>
#include <vector>
#include "ui/base/ime/grammar_fragment.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/base/ime/text_input_mode.h"
#include "ui/base/ime/text_input_type.h"
#include "url/gurl.h"
namespace gfx {
class Rect;
class Range;
} // namespace gfx
namespace ui {
class WaylandWindow;
// Client interface which handles wayland text input callbacks
class ZWPTextInputWrapperClient {
public:
struct SpanStyle {
struct Style {
bool operator==(const Style& other) const = default;
ImeTextSpan::Type type;
ImeTextSpan::Thickness thickness;
};
bool operator==(const SpanStyle& other) const = default;
// Byte offset.
uint32_t index;
// Length in bytes.
uint32_t length;
// One of preedit_style.
std::optional<Style> style;
};
virtual ~ZWPTextInputWrapperClient() = default;
// Called when a new composing text (pre-edit) should be set around the
// current cursor position. Any previously set composing text should
// be removed.
// Note that the preedit_cursor is byte-offset. It is the pre-edit cursor
// position if the range is empty and selection otherwise.
virtual void OnPreeditString(std::string_view text,
const std::vector<SpanStyle>& spans,
const gfx::Range& preedit_cursor) = 0;
// Called when a complete input sequence has been entered. The text to
// commit could be either just a single character after a key press or the
// result of some composing (pre-edit).
virtual void OnCommitString(std::string_view text) = 0;
// Called when the cursor position or selection should be modified. The new
// cursor position is applied on the next OnCommitString. |index| and |anchor|
// are measured in UTF-8 bytes.
virtual void OnCursorPosition(int32_t index, int32_t anchor) = 0;
// Called when client needs to delete all or part of the text surrounding
// the cursor. |index| and |length| are expected to be a byte offset of |text|
// passed via ZWPTextInputWrapper::SetSurroundingText.
virtual void OnDeleteSurroundingText(int32_t index, uint32_t length) = 0;
// Notify when a key event was sent. Key events should not be used
// for normal text input operations, which should be done with
// commit_string, delete_surrounding_text, etc.
virtual void OnKeysym(uint32_t key,
uint32_t state,
uint32_t modifiers,
uint32_t time) = 0;
// Called when the visibility state of the input panel changed.
// There's no detailed spec of |state|, and no actual implementor except
// components/exo is found in the world at this moment.
// Thus, in ozone/wayland use the lowest bit as boolean
// (visible=1/invisible=0), and ignore other bits for future compatibility.
// This behavior must be consistent with components/exo.
virtual void OnInputPanelState(uint32_t state) = 0;
// Called when the modifiers map is updated.
// Each element holds the XKB name represents a modifier, such as "Shift".
// The position of the element represents the bit position of modifiers
// on OnKeysym. E.g., if LSB of modifiers is set, modifiers_map[0] is
// set, if (1 << 1) of modifiers is set, modifiers_map[1] is set, and so on.
virtual void OnModifiersMap(std::vector<std::string> modifiers_map) = 0;
};
// A wrapper around different versions of wayland text input protocols.
// Wayland compositors support various different text input protocols which
// all from Chromium point of view provide the functionality needed by Chromium
// IME. This interface collects the functionality behind one wrapper API.
class ZWPTextInputWrapper {
public:
virtual ~ZWPTextInputWrapper() = default;
virtual void Reset() = 0;
virtual void Activate(WaylandWindow* window,
ui::TextInputClient::FocusReason reason) = 0;
virtual void Deactivate() = 0;
virtual void ShowInputPanel() = 0;
virtual void HideInputPanel() = 0;
virtual void SetCursorRect(const gfx::Rect& rect) = 0;
virtual void SetSurroundingText(const std::string& text,
const gfx::Range& preedit_range,
const gfx::Range& selection_range) = 0;
virtual void SetContentType(ui::TextInputType type,
uint32_t flags,
bool should_do_learning) = 0;
};
} // namespace ui
#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_ZWP_TEXT_INPUT_WRAPPER_H_

@ -1,120 +0,0 @@
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_ZWP_TEXT_INPUT_WRAPPER_V1_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_ZWP_TEXT_INPUT_WRAPPER_V1_H_
#include <text-input-unstable-v1-client-protocol.h>
#include <cstdint>
#include <string>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/timer/timer.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/host/zwp_text_input_wrapper.h"
namespace gfx {
class Rect;
}
namespace ui {
class WaylandConnection;
class WaylandWindow;
// Text input wrapper for text-input-unstable-v1
class ZWPTextInputWrapperV1 : public ZWPTextInputWrapper {
public:
ZWPTextInputWrapperV1(WaylandConnection* connection,
ZWPTextInputWrapperClient* client,
zwp_text_input_manager_v1* text_input_manager);
ZWPTextInputWrapperV1(const ZWPTextInputWrapperV1&) = delete;
ZWPTextInputWrapperV1& operator=(const ZWPTextInputWrapperV1&) = delete;
~ZWPTextInputWrapperV1() override;
void Reset() override;
void Activate(WaylandWindow* window,
ui::TextInputClient::FocusReason reason) override;
void Deactivate() override;
void ShowInputPanel() override;
void HideInputPanel() override;
void SetCursorRect(const gfx::Rect& rect) override;
void SetSurroundingText(const std::string& text,
const gfx::Range& preedit_range,
const gfx::Range& selection_range) override;
void SetContentType(TextInputType type,
uint32_t flags,
bool should_do_learning) override;
private:
void ResetInputEventState();
// zwp_text_input_v1_listener callbacks:
static void OnEnter(void* data,
struct zwp_text_input_v1* text_input,
struct wl_surface* surface);
static void OnLeave(void* data, struct zwp_text_input_v1* text_input);
static void OnModifiersMap(void* data,
struct zwp_text_input_v1* text_input,
struct wl_array* map);
static void OnInputPanelState(void* data,
struct zwp_text_input_v1* text_input,
uint32_t state);
static void OnPreeditString(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
const char* text,
const char* commit);
static void OnPreeditStyling(void* data,
struct zwp_text_input_v1* text_input,
uint32_t index,
uint32_t length,
uint32_t style);
static void OnPreeditCursor(void* data,
struct zwp_text_input_v1* text_input,
int32_t index);
static void OnCommitString(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
const char* text);
static void OnCursorPosition(void* data,
struct zwp_text_input_v1* text_input,
int32_t index,
int32_t anchor);
static void OnDeleteSurroundingText(void* data,
struct zwp_text_input_v1* text_input,
int32_t index,
uint32_t length);
static void OnKeysym(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
uint32_t time,
uint32_t key,
uint32_t state,
uint32_t modifiers);
static void OnLanguage(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
const char* language);
static void OnTextDirection(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
uint32_t direction);
const raw_ptr<WaylandConnection> connection_;
wl::Object<zwp_text_input_v1> obj_;
const raw_ptr<ZWPTextInputWrapperClient> client_;
std::vector<ZWPTextInputWrapperClient::SpanStyle> spans_;
int32_t preedit_cursor_ = -1;
};
} // namespace ui
#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_ZWP_TEXT_INPUT_WRAPPER_V1_H_

@ -0,0 +1,12 @@
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/platform/wayland/test/mock_zwp_text_input_v1_client.h"
namespace ui {
MockZwpTextInputV1Client::MockZwpTextInputV1Client() = default;
MockZwpTextInputV1Client::~MockZwpTextInputV1Client() = default;
} // namespace ui

@ -1,22 +1,21 @@
// Copyright 2024 The Chromium Authors
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_TEXT_INPUT_WRAPPER_CLIENT_H_
#define UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_TEXT_INPUT_WRAPPER_CLIENT_H_
#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_TEXT_INPUT_V1_CLIENT_H_
#define UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_TEXT_INPUT_V1_CLIENT_H_
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/ozone/platform/wayland/host/zwp_text_input_wrapper.h"
#include "ui/ozone/platform/wayland/host/zwp_text_input_v1.h"
namespace ui {
class MockZWPTextInputWrapperClient : public ZWPTextInputWrapperClient {
class MockZwpTextInputV1Client : public ZwpTextInputV1Client {
public:
MockZWPTextInputWrapperClient();
MockZWPTextInputWrapperClient(const MockZWPTextInputWrapperClient&) = delete;
MockZWPTextInputWrapperClient& operator=(
const MockZWPTextInputWrapperClient&) = delete;
~MockZWPTextInputWrapperClient() override;
MockZwpTextInputV1Client();
MockZwpTextInputV1Client(const MockZwpTextInputV1Client&) = delete;
MockZwpTextInputV1Client& operator=(const MockZwpTextInputV1Client&) = delete;
~MockZwpTextInputV1Client() override;
MOCK_METHOD(void,
OnPreeditString,
@ -46,4 +45,4 @@ class MockZWPTextInputWrapperClient : public ZWPTextInputWrapperClient {
} // namespace ui
#endif // UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_TEXT_INPUT_WRAPPER_CLIENT_H_
#endif // UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_TEXT_INPUT_V1_CLIENT_H_

@ -0,0 +1,14 @@
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/platform/wayland/test/mock_zwp_text_input_v3_client.h"
#include "ui/ozone/platform/wayland/host/span_style.h"
namespace ui {
MockZwpTextInputV3Client::MockZwpTextInputV3Client() = default;
MockZwpTextInputV3Client::~MockZwpTextInputV3Client() = default;
} // namespace ui

@ -0,0 +1,31 @@
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_TEXT_INPUT_V3_CLIENT_H_
#define UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_TEXT_INPUT_V3_CLIENT_H_
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/ozone/platform/wayland/host/zwp_text_input_v3.h"
namespace ui {
class MockZwpTextInputV3Client : public ZwpTextInputV3Client {
public:
MockZwpTextInputV3Client();
MockZwpTextInputV3Client(const MockZwpTextInputV3Client&) = delete;
MockZwpTextInputV3Client& operator=(const MockZwpTextInputV3Client&) = delete;
~MockZwpTextInputV3Client() override;
MOCK_METHOD(void,
OnPreeditString,
(std::string_view text,
const std::vector<SpanStyle>& spans,
const gfx::Range& preedit_cursor),
(override));
MOCK_METHOD(void, OnCommitString, (std::string_view text), (override));
};
} // namespace ui
#endif // UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_ZWP_TEXT_INPUT_V3_CLIENT_H_

@ -1,12 +0,0 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/platform/wayland/test/mock_zwp_text_input_wrapper_client.h"
namespace ui {
MockZWPTextInputWrapperClient::MockZWPTextInputWrapperClient() = default;
MockZWPTextInputWrapperClient::~MockZWPTextInputWrapperClient() = default;
} // namespace ui

@ -131,7 +131,7 @@ bool TestWaylandServerThread::Start() {
if (!xdg_shell_.Initialize(display_.get()))
return false;
if (config_.text_input_wrapper_type == ZWPTextInputWrapperType::kV3) {
if (config_.text_input_type == ZwpTextInputType::kV3) {
if (!zwp_text_input_manager_v3_.Initialize(display_.get())) {
return false;
}

@ -17,7 +17,6 @@
#include "base/threading/thread.h"
#include "base/threading/thread_checker.h"
#include "ui/display/types/display_constants.h"
#include "ui/ozone/platform/wayland/host/zwp_text_input_wrapper.h"
#include "ui/ozone/platform/wayland/test/global_object.h"
#include "ui/ozone/platform/wayland/test/mock_wayland_zcr_color_manager.h"
#include "ui/ozone/platform/wayland/test/mock_wp_presentation.h"
@ -56,11 +55,10 @@ enum class PrimarySelectionProtocol { kNone, kGtk, kZwp };
enum class ShouldUseExplicitSynchronizationProtocol { kNone, kUse };
enum class ShouldUseLinuxDrmSyncobjProtocol { kNone, kUse };
// Text input protocol type.
enum class ZWPTextInputWrapperType { kV1, kV3 };
enum class ZwpTextInputType { kV1, kV3 };
struct ServerConfig {
ZWPTextInputWrapperType text_input_wrapper_type =
ZWPTextInputWrapperType::kV1;
ZwpTextInputType text_input_type = ZwpTextInputType::kV1;
TestCompositor::Version compositor_version = TestCompositor::Version::kV4;
PrimarySelectionProtocol primary_selection_protocol =
PrimarySelectionProtocol::kNone;