1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/exo/wayland/zwp_text_input_manager.h"
6 
7 #include <text-input-unstable-v1-server-protocol.h>
8 #include <wayland-server-core.h>
9 #include <wayland-server-protocol-core.h>
10 #include <xkbcommon/xkbcommon.h>
11 
12 #include "base/strings/string_piece.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "components/exo/display.h"
15 #include "components/exo/text_input.h"
16 #include "components/exo/wayland/serial_tracker.h"
17 #include "components/exo/wayland/server_util.h"
18 #include "components/exo/xkb_tracker.h"
19 #include "ui/base/ime/utf_offset.h"
20 #include "ui/events/event.h"
21 #include "ui/events/keycodes/dom/keycode_converter.h"
22 
23 
24 namespace exo {
25 namespace wayland {
26 
27 namespace {
28 
29 ////////////////////////////////////////////////////////////////////////////////
30 // text_input_v1 interface:
31 
32 class WaylandTextInputDelegate : public TextInput::Delegate {
33  public:
WaylandTextInputDelegate(wl_resource * text_input,const XkbTracker * xkb_tracker,SerialTracker * serial_tracker)34   WaylandTextInputDelegate(wl_resource* text_input,
35                            const XkbTracker* xkb_tracker,
36                            SerialTracker* serial_tracker)
37       : text_input_(text_input),
38         xkb_tracker_(xkb_tracker),
39         serial_tracker_(serial_tracker) {}
40   ~WaylandTextInputDelegate() override = default;
41 
set_surface(wl_resource * surface)42   void set_surface(wl_resource* surface) { surface_ = surface; }
43 
44  private:
client()45   wl_client* client() { return wl_resource_get_client(text_input_); }
46 
47   // TextInput::Delegate:
Activated()48   void Activated() override {
49     zwp_text_input_v1_send_enter(text_input_, surface_);
50     wl_client_flush(client());
51   }
52 
Deactivated()53   void Deactivated() override {
54     zwp_text_input_v1_send_leave(text_input_);
55     wl_client_flush(client());
56   }
57 
OnVirtualKeyboardVisibilityChanged(bool is_visible)58   void OnVirtualKeyboardVisibilityChanged(bool is_visible) override {
59     zwp_text_input_v1_send_input_panel_state(text_input_, is_visible);
60     wl_client_flush(client());
61   }
62 
SetCompositionText(const ui::CompositionText & composition)63   void SetCompositionText(const ui::CompositionText& composition) override {
64     for (const auto& span : composition.ime_text_spans) {
65       uint32_t style = ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_DEFAULT;
66       switch (span.type) {
67         case ui::ImeTextSpan::Type::kComposition:
68           if (span.thickness == ui::ImeTextSpan::Thickness::kThick) {
69             style = ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT;
70           } else {
71             style = ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_UNDERLINE;
72           }
73           break;
74         case ui::ImeTextSpan::Type::kSuggestion:
75           style = ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_SELECTION;
76           break;
77         case ui::ImeTextSpan::Type::kMisspellingSuggestion:
78         case ui::ImeTextSpan::Type::kAutocorrect:
79           style = ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_INCORRECT;
80           break;
81       }
82       const auto start =
83           ui::Utf8OffsetFromUtf16Offset(composition.text, span.start_offset);
84       if (!start)
85         continue;
86       const auto end =
87           ui::Utf8OffsetFromUtf16Offset(composition.text, span.end_offset);
88       if (!end)
89         continue;
90       zwp_text_input_v1_send_preedit_styling(text_input_, *start, *end - *start,
91                                              style);
92     }
93 
94     const auto pos = ui::Utf8OffsetFromUtf16Offset(
95         composition.text, composition.selection.start());
96     if (pos)
97       zwp_text_input_v1_send_preedit_cursor(text_input_, *pos);
98 
99     const std::string utf8 = base::UTF16ToUTF8(composition.text);
100     zwp_text_input_v1_send_preedit_string(
101         text_input_,
102         serial_tracker_->GetNextSerial(SerialTracker::EventType::OTHER_EVENT),
103         utf8.c_str(), utf8.c_str());
104 
105     wl_client_flush(client());
106   }
107 
Commit(const base::string16 & text)108   void Commit(const base::string16& text) override {
109     zwp_text_input_v1_send_commit_string(
110         text_input_,
111         serial_tracker_->GetNextSerial(SerialTracker::EventType::OTHER_EVENT),
112         base::UTF16ToUTF8(text).c_str());
113     wl_client_flush(client());
114   }
115 
SetCursor(const gfx::Range & selection)116   void SetCursor(const gfx::Range& selection) override {
117     zwp_text_input_v1_send_cursor_position(text_input_, selection.end(),
118                                            selection.start());
119   }
120 
DeleteSurroundingText(const gfx::Range & range)121   void DeleteSurroundingText(const gfx::Range& range) override {
122     zwp_text_input_v1_send_delete_surrounding_text(text_input_, range.start(),
123                                                    range.length());
124   }
125 
SendKey(const ui::KeyEvent & event)126   void SendKey(const ui::KeyEvent& event) override {
127     uint32_t keysym = xkb_tracker_->GetKeysym(
128         ui::KeycodeConverter::DomCodeToNativeKeycode(event.code()));
129     bool pressed = (event.type() == ui::ET_KEY_PRESSED);
130     // TODO(mukai): consolidate the definition of this modifiers_mask with other
131     // similar ones in components/exo/keyboard.cc or arc_ime_service.cc.
132     constexpr uint32_t modifiers_mask =
133         ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN |
134         ui::EF_COMMAND_DOWN | ui::EF_ALTGR_DOWN | ui::EF_MOD3_DOWN;
135     // 1-bit shifts to adjust the bitpattern for the modifiers; see also
136     // WaylandTextInputDelegate::SendModifiers().
137     uint32_t modifiers = (event.flags() & modifiers_mask) >> 1;
138 
139     zwp_text_input_v1_send_keysym(
140         text_input_, TimeTicksToMilliseconds(event.time_stamp()),
141         serial_tracker_->GetNextSerial(SerialTracker::EventType::OTHER_EVENT),
142         keysym,
143         pressed ? WL_KEYBOARD_KEY_STATE_PRESSED
144                 : WL_KEYBOARD_KEY_STATE_RELEASED,
145         modifiers);
146     wl_client_flush(client());
147   }
148 
OnTextDirectionChanged(base::i18n::TextDirection direction)149   void OnTextDirectionChanged(base::i18n::TextDirection direction) override {
150     uint32_t wayland_direction = ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_AUTO;
151     switch (direction) {
152       case base::i18n::RIGHT_TO_LEFT:
153         wayland_direction = ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_LTR;
154         break;
155       case base::i18n::LEFT_TO_RIGHT:
156         wayland_direction = ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_RTL;
157         break;
158       case base::i18n::UNKNOWN_DIRECTION:
159         LOG(ERROR) << "Unrecognized direction: " << direction;
160     }
161 
162     zwp_text_input_v1_send_text_direction(
163         text_input_,
164         serial_tracker_->GetNextSerial(SerialTracker::EventType::OTHER_EVENT),
165         wayland_direction);
166   }
167 
168   wl_resource* text_input_;
169   wl_resource* surface_ = nullptr;
170 
171   // Owned by Seat, which is updated before calling the callbacks of this
172   // class.
173   const XkbTracker* const xkb_tracker_;
174 
175   // Owned by Server, which always outlives this delegate.
176   SerialTracker* const serial_tracker_;
177 };
178 
text_input_activate(wl_client * client,wl_resource * resource,wl_resource * seat,wl_resource * surface_resource)179 void text_input_activate(wl_client* client,
180                          wl_resource* resource,
181                          wl_resource* seat,
182                          wl_resource* surface_resource) {
183   TextInput* text_input = GetUserDataAs<TextInput>(resource);
184   Surface* surface = GetUserDataAs<Surface>(surface_resource);
185   static_cast<WaylandTextInputDelegate*>(text_input->delegate())
186       ->set_surface(surface_resource);
187   text_input->Activate(surface);
188 
189   // Sending modifiers.
190   constexpr const char* kModifierNames[] = {
191       XKB_MOD_NAME_SHIFT,
192       XKB_MOD_NAME_CTRL,
193       XKB_MOD_NAME_ALT,
194       XKB_MOD_NAME_LOGO,
195       "Mod5",
196       "Mod3",
197   };
198   wl_array modifiers;
199   wl_array_init(&modifiers);
200   for (const char* modifier : kModifierNames) {
201     char* p =
202         static_cast<char*>(wl_array_add(&modifiers, ::strlen(modifier) + 1));
203     ::strcpy(p, modifier);
204   }
205   zwp_text_input_v1_send_modifiers_map(resource, &modifiers);
206   wl_array_release(&modifiers);
207 }
208 
text_input_deactivate(wl_client * client,wl_resource * resource,wl_resource * seat)209 void text_input_deactivate(wl_client* client,
210                            wl_resource* resource,
211                            wl_resource* seat) {
212   TextInput* text_input = GetUserDataAs<TextInput>(resource);
213   text_input->Deactivate();
214 }
215 
text_input_show_input_panel(wl_client * client,wl_resource * resource)216 void text_input_show_input_panel(wl_client* client, wl_resource* resource) {
217   GetUserDataAs<TextInput>(resource)->ShowVirtualKeyboardIfEnabled();
218 }
219 
text_input_hide_input_panel(wl_client * client,wl_resource * resource)220 void text_input_hide_input_panel(wl_client* client, wl_resource* resource) {
221   GetUserDataAs<TextInput>(resource)->HideVirtualKeyboard();
222 }
223 
text_input_reset(wl_client * client,wl_resource * resource)224 void text_input_reset(wl_client* client, wl_resource* resource) {
225   GetUserDataAs<TextInput>(resource)->Resync();
226 }
227 
text_input_set_surrounding_text(wl_client * client,wl_resource * resource,const char * text,uint32_t cursor,uint32_t anchor)228 void text_input_set_surrounding_text(wl_client* client,
229                                      wl_resource* resource,
230                                      const char* text,
231                                      uint32_t cursor,
232                                      uint32_t anchor) {
233   TextInput* text_input = GetUserDataAs<TextInput>(resource);
234   auto utf16_cursor = ui::Utf16OffsetFromUtf8Offset(text, cursor);
235   if (!utf16_cursor)
236     return;
237   auto utf16_anchor = ui::Utf16OffsetFromUtf8Offset(text, anchor);
238   if (!utf16_anchor)
239     return;
240   text_input->SetSurroundingText(base::UTF8ToUTF16(text), *utf16_cursor,
241                                  *utf16_anchor);
242 }
243 
text_input_set_content_type(wl_client * client,wl_resource * resource,uint32_t hint,uint32_t purpose)244 void text_input_set_content_type(wl_client* client,
245                                  wl_resource* resource,
246                                  uint32_t hint,
247                                  uint32_t purpose) {
248   TextInput* text_input = GetUserDataAs<TextInput>(resource);
249   ui::TextInputType type = ui::TEXT_INPUT_TYPE_TEXT;
250   ui::TextInputMode mode = ui::TEXT_INPUT_MODE_DEFAULT;
251   int flags = ui::TEXT_INPUT_FLAG_NONE;
252   bool should_do_learning = true;
253   if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_COMPLETION)
254     flags |= ui::TEXT_INPUT_FLAG_AUTOCOMPLETE_ON;
255   if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_CORRECTION)
256     flags |= ui::TEXT_INPUT_FLAG_AUTOCORRECT_ON;
257   if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_CAPITALIZATION)
258     flags |= ui::TEXT_INPUT_FLAG_AUTOCAPITALIZE_SENTENCES;
259   if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_LOWERCASE)
260     flags |= ui::TEXT_INPUT_FLAG_AUTOCAPITALIZE_NONE;
261   if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_UPPERCASE)
262     flags |= ui::TEXT_INPUT_FLAG_AUTOCAPITALIZE_CHARACTERS;
263   if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_TITLECASE)
264     flags |= ui::TEXT_INPUT_FLAG_AUTOCAPITALIZE_WORDS;
265   if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_HIDDEN_TEXT) {
266     flags |= ui::TEXT_INPUT_FLAG_AUTOCOMPLETE_OFF |
267              ui::TEXT_INPUT_FLAG_AUTOCORRECT_OFF;
268   }
269   if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_SENSITIVE_DATA)
270     should_do_learning = false;
271   // Unused hints: LATIN, MULTILINE.
272 
273   switch (purpose) {
274     case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DIGITS:
275       mode = ui::TEXT_INPUT_MODE_DECIMAL;
276       type = ui::TEXT_INPUT_TYPE_NUMBER;
277       break;
278     case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NUMBER:
279       mode = ui::TEXT_INPUT_MODE_NUMERIC;
280       type = ui::TEXT_INPUT_TYPE_NUMBER;
281       break;
282     case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_PHONE:
283       mode = ui::TEXT_INPUT_MODE_TEL;
284       type = ui::TEXT_INPUT_TYPE_TELEPHONE;
285       break;
286     case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_URL:
287       mode = ui::TEXT_INPUT_MODE_URL;
288       type = ui::TEXT_INPUT_TYPE_URL;
289       break;
290     case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_EMAIL:
291       mode = ui::TEXT_INPUT_MODE_EMAIL;
292       type = ui::TEXT_INPUT_TYPE_EMAIL;
293       break;
294     case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_PASSWORD:
295       DCHECK(!should_do_learning);
296       type = ui::TEXT_INPUT_TYPE_PASSWORD;
297       break;
298     case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DATE:
299       type = ui::TEXT_INPUT_TYPE_DATE;
300       break;
301     case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_TIME:
302       type = ui::TEXT_INPUT_TYPE_TIME;
303       break;
304     case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DATETIME:
305       type = ui::TEXT_INPUT_TYPE_DATE_TIME;
306       break;
307     case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NORMAL:
308     case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_ALPHA:
309     case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NAME:
310     case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_TERMINAL:
311       // No special type / mode are set.
312       break;
313   }
314 
315   text_input->SetTypeModeFlags(type, mode, flags, should_do_learning);
316 }
317 
text_input_set_cursor_rectangle(wl_client * client,wl_resource * resource,int32_t x,int32_t y,int32_t width,int32_t height)318 void text_input_set_cursor_rectangle(wl_client* client,
319                                      wl_resource* resource,
320                                      int32_t x,
321                                      int32_t y,
322                                      int32_t width,
323                                      int32_t height) {
324   GetUserDataAs<TextInput>(resource)->SetCaretBounds(
325       gfx::Rect(x, y, width, height));
326 }
327 
text_input_set_preferred_language(wl_client * client,wl_resource * resource,const char * language)328 void text_input_set_preferred_language(wl_client* client,
329                                        wl_resource* resource,
330                                        const char* language) {
331   // Nothing needs to be done.
332 }
333 
text_input_commit_state(wl_client * client,wl_resource * resource,uint32_t serial)334 void text_input_commit_state(wl_client* client,
335                              wl_resource* resource,
336                              uint32_t serial) {
337   // Nothing needs to be done.
338 }
339 
text_input_invoke_action(wl_client * client,wl_resource * resource,uint32_t button,uint32_t index)340 void text_input_invoke_action(wl_client* client,
341                               wl_resource* resource,
342                               uint32_t button,
343                               uint32_t index) {
344   GetUserDataAs<TextInput>(resource)->Resync();
345 }
346 
347 const struct zwp_text_input_v1_interface text_input_v1_implementation = {
348     text_input_activate,
349     text_input_deactivate,
350     text_input_show_input_panel,
351     text_input_hide_input_panel,
352     text_input_reset,
353     text_input_set_surrounding_text,
354     text_input_set_content_type,
355     text_input_set_cursor_rectangle,
356     text_input_set_preferred_language,
357     text_input_commit_state,
358     text_input_invoke_action,
359 };
360 
361 ////////////////////////////////////////////////////////////////////////////////
362 // text_input_manager_v1 interface:
363 
text_input_manager_create_text_input(wl_client * client,wl_resource * resource,uint32_t id)364 void text_input_manager_create_text_input(wl_client* client,
365                                           wl_resource* resource,
366                                           uint32_t id) {
367   auto* data = GetUserDataAs<WaylandTextInputManager>(resource);
368 
369   wl_resource* text_input_resource =
370       wl_resource_create(client, &zwp_text_input_v1_interface, 1, id);
371 
372   SetImplementation(
373       text_input_resource, &text_input_v1_implementation,
374       std::make_unique<TextInput>(std::make_unique<WaylandTextInputDelegate>(
375           text_input_resource, data->xkb_tracker, data->serial_tracker)));
376 }
377 
378 const struct zwp_text_input_manager_v1_interface
379     text_input_manager_implementation = {
380         text_input_manager_create_text_input,
381 };
382 
383 }  // namespace
384 
bind_text_input_manager(wl_client * client,void * data,uint32_t version,uint32_t id)385 void bind_text_input_manager(wl_client* client,
386                              void* data,
387                              uint32_t version,
388                              uint32_t id) {
389   wl_resource* resource =
390       wl_resource_create(client, &zwp_text_input_manager_v1_interface, 1, id);
391   wl_resource_set_implementation(resource, &text_input_manager_implementation,
392                                  data, nullptr);
393 }
394 
395 }  // namespace wayland
396 }  // namespace exo
397