1 /*
2     SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
3     SPDX-FileCopyrightText: 2021 Dorota Czaplejewicz <gihuac.dcz@porcupinefactory.org>
4     SPDX-FileCopyrightText: 2021 Roman Glig <subdiff@gmail.com>
5 
6     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only
7 */
8 #include "input_method_v2_p.h"
9 
10 #include "display.h"
11 #include "seat_p.h"
12 #include "surface_p.h"
13 #include "text_input_v3_p.h"
14 #include "wayland/client.h"
15 
16 #include <wayland-input-method-unstable-v2-server-protocol.h>
17 
18 namespace Wrapland::Server
19 {
20 
21 struct zwp_input_method_manager_v2_interface const input_method_manager_v2::Private::s_interface = {
22     cb<get_input_method_callback>,
23     resourceDestroyCallback,
24 };
25 
get_input_method_callback(input_method_manager_v2_bind * bind,wl_resource * wlSeat,uint32_t id)26 void input_method_manager_v2::Private::get_input_method_callback(input_method_manager_v2_bind* bind,
27                                                                  wl_resource* wlSeat,
28                                                                  uint32_t id)
29 {
30     auto seat = SeatGlobal::handle(wlSeat);
31     auto im = new input_method_v2(bind->client()->handle(), bind->version(), id);
32     if (seat->get_input_method_v2()) {
33         // Seat already has an input method.
34         im->d_ptr->send<zwp_input_method_v2_send_unavailable>();
35         return;
36     }
37     im->d_ptr->seat = seat;
38 
39     seat->d_ptr->input_method = im;
40 
41     QObject::connect(im, &input_method_v2::resourceDestroyed, seat, [seat] {
42         seat->d_ptr->input_method = nullptr;
43         Q_EMIT seat->input_method_v2_changed();
44     });
45     Q_EMIT seat->input_method_v2_changed();
46 }
47 
Private(Display * display,input_method_manager_v2 * q)48 input_method_manager_v2::Private::Private(Display* display, input_method_manager_v2* q)
49     : input_method_manager_v2_global(q,
50                                      display,
51                                      &zwp_input_method_manager_v2_interface,
52                                      &s_interface)
53 {
54     create();
55 }
56 
input_method_manager_v2(Display * display,QObject * parent)57 input_method_manager_v2::input_method_manager_v2(Display* display, QObject* parent)
58     : QObject(parent)
59     , d_ptr(new Private(display, this))
60 {
61 }
62 
63 input_method_manager_v2::~input_method_manager_v2() = default;
64 
65 struct zwp_input_method_v2_interface const input_method_v2::Private::s_interface = {
66     commit_string_callback,
67     preedit_string_callback,
68     delete_surrounding_text_callback,
69     commit_callback,
70     get_input_popup_surface_callback,
71     grab_keyboard_callback,
72     destroyCallback,
73 };
74 
commit_string_callback(wl_client * wlClient,wl_resource * wlResource,char const * text)75 void input_method_v2::Private::commit_string_callback([[maybe_unused]] wl_client* wlClient,
76                                                       wl_resource* wlResource,
77                                                       char const* text)
78 {
79     auto priv = handle(wlResource)->d_ptr;
80     priv->pending.commit_string.data = text;
81     priv->pending.commit_string.update = true;
82 }
83 
preedit_string_callback(wl_client * wlClient,wl_resource * wlResource,char const * text,int32_t cursor_begin,int32_t cursor_end)84 void input_method_v2::Private::preedit_string_callback([[maybe_unused]] wl_client* wlClient,
85                                                        wl_resource* wlResource,
86                                                        char const* text,
87                                                        int32_t cursor_begin,
88                                                        int32_t cursor_end)
89 {
90     auto priv = handle(wlResource)->d_ptr;
91     priv->pending.preedit_string.data = text;
92     priv->pending.preedit_string.cursor_begin = cursor_begin;
93     priv->pending.preedit_string.cursor_end = cursor_end;
94     priv->pending.preedit_string.update = true;
95 }
96 
delete_surrounding_text_callback(wl_client * wlClient,wl_resource * wlResource,uint32_t beforeBytes,uint32_t afterBytes)97 void input_method_v2::Private::delete_surrounding_text_callback(
98     [[maybe_unused]] wl_client* wlClient,
99     wl_resource* wlResource,
100     uint32_t beforeBytes,
101     uint32_t afterBytes)
102 {
103     auto priv = handle(wlResource)->d_ptr;
104     priv->pending.delete_surrounding_text.before_length = beforeBytes;
105     priv->pending.delete_surrounding_text.after_length = afterBytes;
106     priv->pending.delete_surrounding_text.update = true;
107 }
108 
commit_callback(wl_client * wlClient,wl_resource * wlResource,uint32_t serial)109 void input_method_v2::Private::commit_callback([[maybe_unused]] wl_client* wlClient,
110                                                wl_resource* wlResource,
111                                                uint32_t serial)
112 {
113     auto priv = handle(wlResource)->d_ptr;
114     if (priv->serial != serial) {
115         // Not on latest done event. Reset pending to current state and wait for next commit.
116         priv->pending = priv->current;
117         return;
118     }
119     priv->current = priv->pending;
120     Q_EMIT priv->handle()->state_committed();
121 }
122 
get_input_popup_surface_callback(wl_client * wlClient,wl_resource * wlResource,uint32_t id,wl_resource * wlSurface)123 void input_method_v2::Private::get_input_popup_surface_callback(
124     [[maybe_unused]] wl_client* wlClient,
125     wl_resource* wlResource,
126     uint32_t id,
127     wl_resource* wlSurface)
128 {
129     auto priv = handle(wlResource)->d_ptr;
130     auto surface = Surface::Private::handle(wlSurface);
131     // TODO(romangg): should send error when surface already has a role.
132 
133     auto popup
134         = new input_method_popup_surface_v2(priv->client()->handle(), priv->version(), id, surface);
135     Q_EMIT priv->q_ptr->popup_surface_created(popup);
136 }
137 
grab_keyboard_callback(wl_client * wlClient,wl_resource * wlResource,uint32_t id)138 void input_method_v2::Private::grab_keyboard_callback([[maybe_unused]] wl_client* wlClient,
139                                                       wl_resource* wlResource,
140                                                       uint32_t id)
141 {
142     auto priv = handle(wlResource)->d_ptr;
143     auto grab = new input_method_keyboard_grab_v2(
144         priv->client()->handle(), priv->version(), id, priv->seat);
145     Q_EMIT priv->q_ptr->keyboard_grabbed(grab);
146 }
147 
Private(Client * client,uint32_t version,uint32_t id,input_method_v2 * q)148 input_method_v2::Private::Private(Client* client, uint32_t version, uint32_t id, input_method_v2* q)
149     : Wayland::Resource<input_method_v2>(client,
150                                          version,
151                                          id,
152                                          &zwp_input_method_v2_interface,
153                                          &s_interface,
154                                          q)
155     , q_ptr{q}
156 {
157 }
158 
input_method_v2(Client * client,uint32_t version,uint32_t id)159 input_method_v2::input_method_v2(Client* client, uint32_t version, uint32_t id)
160     : QObject(nullptr)
161     , d_ptr(new Private(client, version, id, this))
162 {
163 }
164 
set_active(bool active)165 void input_method_v2::set_active(bool active)
166 {
167     if (active) {
168         d_ptr->current = {};
169         d_ptr->pending = {};
170         d_ptr->send<zwp_input_method_v2_send_activate>();
171     } else {
172         d_ptr->send<zwp_input_method_v2_send_deactivate>();
173     }
174 }
175 
set_surrounding_text(std::string const & text,uint32_t cursor,uint32_t anchor,Wrapland::Server::text_input_v3_change_cause change_cause)176 void input_method_v2::set_surrounding_text(
177     std::string const& text,
178     uint32_t cursor,
179     uint32_t anchor,
180     Wrapland::Server::text_input_v3_change_cause change_cause)
181 {
182     d_ptr->send<zwp_input_method_v2_send_surrounding_text>(text.c_str(), cursor, anchor);
183     d_ptr->send<zwp_input_method_v2_send_text_change_cause>(convert_change_cause(change_cause));
184 }
185 
set_content_type(text_input_v3_content_hints hints,text_input_v3_content_purpose purpose)186 void input_method_v2::set_content_type(text_input_v3_content_hints hints,
187                                        text_input_v3_content_purpose purpose)
188 {
189     d_ptr->send<zwp_input_method_v2_send_content_type>(convert_content_hints(hints),
190                                                        convert_content_purpose(purpose));
191 }
192 
done()193 void input_method_v2::done()
194 {
195     d_ptr->serial++;
196     d_ptr->send<zwp_input_method_v2_send_done>();
197 }
198 
state() const199 input_method_v2_state const& input_method_v2::state() const
200 {
201     return d_ptr->current;
202 }
203 
204 struct zwp_input_method_keyboard_grab_v2_interface const
205     input_method_keyboard_grab_v2::Private::s_interface{
206         destroyCallback,
207     };
208 
Private(Client * client,uint32_t version,uint32_t id,Seat * seat,input_method_keyboard_grab_v2 * q)209 input_method_keyboard_grab_v2::Private::Private(Client* client,
210                                                 uint32_t version,
211                                                 uint32_t id,
212                                                 Seat* seat,
213                                                 input_method_keyboard_grab_v2* q)
214     : Wayland::Resource<input_method_keyboard_grab_v2>(client,
215                                                        version,
216                                                        id,
217                                                        &zwp_input_method_keyboard_grab_v2_interface,
218                                                        &s_interface,
219                                                        q)
220     , seat{seat}
221 {
222 }
223 
input_method_keyboard_grab_v2(Client * client,uint32_t version,uint32_t id,Wrapland::Server::Seat * seat)224 input_method_keyboard_grab_v2::input_method_keyboard_grab_v2(Client* client,
225                                                              uint32_t version,
226                                                              uint32_t id,
227                                                              Wrapland::Server::Seat* seat)
228     : QObject(nullptr)
229     , d_ptr(new Private(client, version, id, seat, this))
230 {
231 }
232 
set_keymap(std::string const & content)233 void input_method_keyboard_grab_v2::set_keymap(std::string const& content)
234 {
235     auto tmpf = std::tmpfile();
236 
237     std::fputs(content.data(), tmpf);
238     std::rewind(tmpf);
239 
240     d_ptr->send<zwp_input_method_keyboard_grab_v2_send_keymap>(
241         WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, fileno(tmpf), content.size());
242     d_ptr->keymap = file_wrap(tmpf);
243 }
244 
press_key(uint32_t time,uint32_t key)245 void input_method_keyboard_grab_v2::press_key(uint32_t time, uint32_t key)
246 {
247     auto serial = d_ptr->client()->display()->handle()->nextSerial();
248     d_ptr->send<zwp_input_method_keyboard_grab_v2_send_key>(
249         serial, time, key, WL_KEYBOARD_KEY_STATE_PRESSED);
250 }
251 
release_key(uint32_t time,uint32_t key)252 void input_method_keyboard_grab_v2::release_key(uint32_t time, uint32_t key)
253 {
254     auto serial = d_ptr->client()->display()->handle()->nextSerial();
255     d_ptr->send<zwp_input_method_keyboard_grab_v2_send_key>(
256         serial, time, key, WL_KEYBOARD_KEY_STATE_RELEASED);
257 }
258 
update_modifiers(uint32_t depressed,uint32_t latched,uint32_t locked,uint32_t group)259 void input_method_keyboard_grab_v2::update_modifiers(uint32_t depressed,
260                                                      uint32_t latched,
261                                                      uint32_t locked,
262                                                      uint32_t group)
263 {
264     auto serial = d_ptr->client()->display()->handle()->nextSerial();
265     d_ptr->send<zwp_input_method_keyboard_grab_v2_send_modifiers>(
266         serial, depressed, latched, locked, group);
267 }
268 
set_repeat_info(int32_t rate,int32_t delay)269 void input_method_keyboard_grab_v2::set_repeat_info(int32_t rate, int32_t delay)
270 {
271     d_ptr->send<zwp_input_method_keyboard_grab_v2_send_repeat_info>(rate, delay);
272 }
273 
274 struct zwp_input_popup_surface_v2_interface const
275     input_method_popup_surface_v2::Private::s_interface{
276         destroyCallback,
277     };
278 
Private(Client * client,uint32_t version,uint32_t id,Surface * surface,input_method_popup_surface_v2 * q)279 input_method_popup_surface_v2::Private::Private(Client* client,
280                                                 uint32_t version,
281                                                 uint32_t id,
282                                                 Surface* surface,
283                                                 input_method_popup_surface_v2* q)
284     : Wayland::Resource<input_method_popup_surface_v2>(client,
285                                                        version,
286                                                        id,
287                                                        &zwp_input_popup_surface_v2_interface,
288                                                        &s_interface,
289                                                        q)
290     , surface{surface}
291 {
292 }
293 
input_method_popup_surface_v2(Client * client,uint32_t version,uint32_t id,Wrapland::Server::Surface * surface)294 input_method_popup_surface_v2::input_method_popup_surface_v2(Client* client,
295                                                              uint32_t version,
296                                                              uint32_t id,
297                                                              Wrapland::Server::Surface* surface)
298     : QObject(nullptr)
299     , d_ptr(new Private(client, version, id, surface, this))
300 {
301 }
302 
surface() const303 Surface* input_method_popup_surface_v2::surface() const
304 {
305     return d_ptr->surface;
306 }
307 
set_text_input_rectangle(QRect const & rect)308 void input_method_popup_surface_v2::set_text_input_rectangle(QRect const& rect)
309 {
310     return d_ptr->send<zwp_input_popup_surface_v2_send_text_input_rectangle>(
311         rect.x(), rect.y(), rect.width(), rect.height());
312 }
313 
314 }
315