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