Directory: | ./ |
---|---|
File: | server/input_method_v2.cpp |
Date: | 2024-01-22 17:25:27 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 154 | 164 | 93.9% |
Branches: | 18 | 36 | 50.0% |
Line | Branch | Exec | Source |
---|---|---|---|
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 "logging.h" | ||
12 | #include "seat_p.h" | ||
13 | #include "surface_p.h" | ||
14 | #include "text_input_v3_p.h" | ||
15 | #include "wayland/client.h" | ||
16 | |||
17 | #include <wayland-input-method-unstable-v2-server-protocol.h> | ||
18 | |||
19 | namespace Wrapland::Server | ||
20 | { | ||
21 | |||
22 | struct zwp_input_method_manager_v2_interface const input_method_manager_v2::Private::s_interface = { | ||
23 | cb<get_input_method_callback>, | ||
24 | resourceDestroyCallback, | ||
25 | }; | ||
26 | |||
27 | 31 | void input_method_manager_v2::Private::get_input_method_callback(input_method_manager_v2_bind* bind, | |
28 | wl_resource* wlSeat, | ||
29 | uint32_t id) | ||
30 | { | ||
31 | 31 | auto seat = SeatGlobal::get_handle(wlSeat); | |
32 |
1/2✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
|
31 | auto im = new input_method_v2(bind->client->handle, bind->version, id); |
33 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
|
31 | if (seat->get_input_method_v2()) { |
34 | // Seat already has an input method. | ||
35 | ✗ | im->d_ptr->send<zwp_input_method_v2_send_unavailable>(); | |
36 | ✗ | return; | |
37 | } | ||
38 | 31 | im->d_ptr->seat = seat; | |
39 | |||
40 | 31 | seat->d_ptr->input_method = im; | |
41 | |||
42 | 62 | QObject::connect(im, &input_method_v2::resourceDestroyed, seat, [seat] { | |
43 | 31 | seat->d_ptr->input_method = nullptr; | |
44 | 62 | Q_EMIT seat->input_method_v2_changed(); | |
45 | 62 | }); | |
46 | 31 | Q_EMIT seat->input_method_v2_changed(); | |
47 | 31 | } | |
48 | |||
49 | 66 | input_method_manager_v2::Private::Private(Display* display, input_method_manager_v2* q_ptr) | |
50 | 132 | : input_method_manager_v2_global(q_ptr, | |
51 | 66 | display, | |
52 | &zwp_input_method_manager_v2_interface, | ||
53 | &s_interface) | ||
54 | 66 | { | |
55 |
1/2✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
|
66 | create(); |
56 | 66 | } | |
57 | |||
58 | 66 | input_method_manager_v2::input_method_manager_v2(Display* display) | |
59 |
2/4✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 66 times.
|
66 | : d_ptr(new Private(display, this)) |
60 | 66 | { | |
61 | 66 | } | |
62 | |||
63 | 132 | 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 | |||
75 | 2 | void input_method_v2::Private::commit_string_callback([[maybe_unused]] wl_client* wlClient, | |
76 | wl_resource* wlResource, | ||
77 | char const* text) | ||
78 | { | ||
79 | 2 | auto priv = get_handle(wlResource)->d_ptr; | |
80 | 2 | priv->pending.commit_string.data = text; | |
81 | 2 | priv->pending.commit_string.update = true; | |
82 | 2 | } | |
83 | |||
84 | 2 | 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 | 2 | auto priv = get_handle(wlResource)->d_ptr; | |
91 | 2 | priv->pending.preedit_string.data = text; | |
92 | 2 | priv->pending.preedit_string.cursor_begin = cursor_begin; | |
93 | 2 | priv->pending.preedit_string.cursor_end = cursor_end; | |
94 | 2 | priv->pending.preedit_string.update = true; | |
95 | 2 | } | |
96 | |||
97 | 1 | 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 | 1 | auto priv = get_handle(wlResource)->d_ptr; | |
104 | 1 | priv->pending.delete_surrounding_text.before_length = beforeBytes; | |
105 | 1 | priv->pending.delete_surrounding_text.after_length = afterBytes; | |
106 | 1 | priv->pending.delete_surrounding_text.update = true; | |
107 | 1 | } | |
108 | |||
109 | 3 | void input_method_v2::Private::commit_callback([[maybe_unused]] wl_client* wlClient, | |
110 | wl_resource* wlResource, | ||
111 | uint32_t serial) | ||
112 | { | ||
113 | 3 | auto priv = get_handle(wlResource)->d_ptr; | |
114 | |||
115 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (priv->serial != serial) { |
116 | // Not on latest done event. Reset pending to current state and wait for next commit. | ||
117 | ✗ | priv->pending = priv->current; | |
118 | ✗ | return; | |
119 | } | ||
120 | |||
121 | 3 | priv->seat->text_inputs().sync_to_text_input(priv->current, priv->pending); | |
122 | 3 | priv->current = priv->pending; | |
123 | |||
124 | 3 | priv->pending.preedit_string.update = false; | |
125 | 3 | priv->pending.commit_string.update = false; | |
126 | 3 | priv->pending.delete_surrounding_text.update = false; | |
127 | |||
128 | 3 | Q_EMIT priv->handle->state_committed(); | |
129 | 3 | } | |
130 | |||
131 | 1 | void input_method_v2::Private::get_input_popup_surface_callback( | |
132 | [[maybe_unused]] wl_client* wlClient, | ||
133 | wl_resource* wlResource, | ||
134 | uint32_t id, | ||
135 | wl_resource* wlSurface) | ||
136 | { | ||
137 | 1 | auto priv = get_handle(wlResource)->d_ptr; | |
138 | 1 | auto surface = Surface::Private::get_handle(wlSurface); | |
139 | // TODO(romangg): should send error when surface already has a role. | ||
140 | |||
141 | 1 | auto popup | |
142 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | = new input_method_popup_surface_v2(priv->client->handle, priv->version, id, surface); |
143 | |||
144 | 1 | priv->popups.push_back(popup); | |
145 | 2 | QObject::connect(popup, | |
146 | &input_method_popup_surface_v2::resourceDestroyed, | ||
147 | 1 | priv->q_ptr, | |
148 | 1 | [priv, popup] { remove_one(priv->popups, popup); }); | |
149 | |||
150 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (auto ti = priv->seat->text_inputs().v3.text_input) { |
151 | ✗ | popup->set_text_input_rectangle(ti->state().cursor_rectangle); | |
152 | } | ||
153 | |||
154 | 1 | Q_EMIT priv->q_ptr->popup_surface_created(popup); | |
155 | 1 | } | |
156 | |||
157 | 1 | void input_method_v2::Private::grab_keyboard_callback([[maybe_unused]] wl_client* wlClient, | |
158 | wl_resource* wlResource, | ||
159 | uint32_t id) | ||
160 | { | ||
161 | 1 | auto priv = get_handle(wlResource)->d_ptr; | |
162 | 1 | auto grab | |
163 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | = new input_method_keyboard_grab_v2(priv->client->handle, priv->version, id, priv->seat); |
164 | 1 | Q_EMIT priv->q_ptr->keyboard_grabbed(grab); | |
165 | 1 | } | |
166 | |||
167 | 62 | input_method_v2::Private::Private(Client* client, | |
168 | uint32_t version, | ||
169 | uint32_t id, | ||
170 | input_method_v2* q_ptr) | ||
171 | 62 | : Wayland::Resource<input_method_v2>(client, | |
172 | 31 | version, | |
173 | 31 | id, | |
174 | &zwp_input_method_v2_interface, | ||
175 | &s_interface, | ||
176 | 31 | q_ptr) | |
177 | 31 | , q_ptr{q_ptr} | |
178 | 31 | { | |
179 | 31 | } | |
180 | |||
181 | 31 | input_method_v2::input_method_v2(Client* client, uint32_t version, uint32_t id) | |
182 | 31 | : QObject(nullptr) | |
183 |
2/4✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 31 times.
|
31 | , d_ptr(new Private(client, version, id, this)) |
184 | 31 | { | |
185 | 31 | } | |
186 | |||
187 | 81 | void input_method_v2::set_active(bool active) | |
188 | { | ||
189 |
2/2✓ Branch 0 taken 54 times.
✓ Branch 1 taken 27 times.
|
81 | if (active) { |
190 | 54 | d_ptr->current = {}; | |
191 | 54 | d_ptr->pending = {}; | |
192 | 54 | d_ptr->send<zwp_input_method_v2_send_activate>(); | |
193 | 54 | } else { | |
194 | 27 | d_ptr->send<zwp_input_method_v2_send_deactivate>(); | |
195 | } | ||
196 | 81 | } | |
197 | |||
198 | 4 | void input_method_v2::set_surrounding_text( | |
199 | std::string const& text, | ||
200 | uint32_t cursor, | ||
201 | uint32_t anchor, | ||
202 | Wrapland::Server::text_input_v3_change_cause change_cause) | ||
203 | { | ||
204 | 4 | d_ptr->send<zwp_input_method_v2_send_surrounding_text>(text.c_str(), cursor, anchor); | |
205 | 4 | d_ptr->send<zwp_input_method_v2_send_text_change_cause>(convert_change_cause(change_cause)); | |
206 | 4 | } | |
207 | |||
208 | 24 | void input_method_v2::set_content_type(text_input_v3_content_hints hints, | |
209 | text_input_v3_content_purpose purpose) | ||
210 | { | ||
211 | 48 | d_ptr->send<zwp_input_method_v2_send_content_type>(convert_content_hints(hints), | |
212 | 24 | convert_content_purpose(purpose)); | |
213 | 24 | } | |
214 | |||
215 | 81 | void input_method_v2::done() | |
216 | { | ||
217 | 81 | d_ptr->serial++; | |
218 | 81 | d_ptr->send<zwp_input_method_v2_send_done>(); | |
219 | 81 | } | |
220 | |||
221 | 54 | input_method_v2_state const& input_method_v2::state() const | |
222 | { | ||
223 | 54 | return d_ptr->current; | |
224 | } | ||
225 | |||
226 | ✗ | std::vector<input_method_popup_surface_v2*> const& input_method_v2::get_popups() const | |
227 | { | ||
228 | ✗ | return d_ptr->popups; | |
229 | } | ||
230 | |||
231 | struct zwp_input_method_keyboard_grab_v2_interface const | ||
232 | input_method_keyboard_grab_v2::Private::s_interface{ | ||
233 | destroyCallback, | ||
234 | }; | ||
235 | |||
236 | 2 | input_method_keyboard_grab_v2::Private::Private(Client* client, | |
237 | uint32_t version, | ||
238 | uint32_t id, | ||
239 | Seat* seat, | ||
240 | input_method_keyboard_grab_v2* q_ptr) | ||
241 | 2 | : Wayland::Resource<input_method_keyboard_grab_v2>(client, | |
242 | 1 | version, | |
243 | 1 | id, | |
244 | &zwp_input_method_keyboard_grab_v2_interface, | ||
245 | &s_interface, | ||
246 | 1 | q_ptr) | |
247 | 1 | , seat{seat} | |
248 | 1 | { | |
249 | 1 | } | |
250 | |||
251 | 1 | input_method_keyboard_grab_v2::input_method_keyboard_grab_v2(Client* client, | |
252 | uint32_t version, | ||
253 | uint32_t id, | ||
254 | Wrapland::Server::Seat* seat) | ||
255 | 1 | : QObject(nullptr) | |
256 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
1 | , d_ptr(new Private(client, version, id, seat, this)) |
257 | 1 | { | |
258 | 1 | } | |
259 | |||
260 | 1 | void input_method_keyboard_grab_v2::set_keymap(std::string const& content) | |
261 | { | ||
262 | 1 | auto tmpf = std::tmpfile(); | |
263 | |||
264 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (auto rc = std::fputs(content.data(), tmpf); rc < 0) { |
265 | ✗ | qCWarning(WRAPLAND_SERVER, "Failed to set input-method keymap with %d.", rc); | |
266 | // TODO(romangg): Handle error by closing file here and returning? | ||
267 | } | ||
268 | |||
269 | 1 | std::rewind(tmpf); | |
270 | 2 | d_ptr->send<zwp_input_method_keyboard_grab_v2_send_keymap>( | |
271 | 1 | WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, fileno(tmpf), content.size()); | |
272 | 1 | d_ptr->keymap = file_wrap(tmpf); | |
273 | 1 | } | |
274 | |||
275 | 1 | void input_method_keyboard_grab_v2::key(uint32_t time, uint32_t key, key_state state) | |
276 | { | ||
277 | 1 | auto serial = d_ptr->client->display()->handle->nextSerial(); | |
278 | 2 | d_ptr->send<zwp_input_method_keyboard_grab_v2_send_key>(serial, | |
279 | time, | ||
280 | key, | ||
281 | 1 | state == key_state::pressed | |
282 | ? WL_KEYBOARD_KEY_STATE_PRESSED | ||
283 | : WL_KEYBOARD_KEY_STATE_RELEASED); | ||
284 | 1 | } | |
285 | |||
286 | 1 | void input_method_keyboard_grab_v2::update_modifiers(uint32_t depressed, | |
287 | uint32_t latched, | ||
288 | uint32_t locked, | ||
289 | uint32_t group) | ||
290 | { | ||
291 | 1 | auto serial = d_ptr->client->display()->handle->nextSerial(); | |
292 | 1 | d_ptr->send<zwp_input_method_keyboard_grab_v2_send_modifiers>( | |
293 | serial, depressed, latched, locked, group); | ||
294 | 1 | } | |
295 | |||
296 | 1 | void input_method_keyboard_grab_v2::set_repeat_info(int32_t rate, int32_t delay) | |
297 | { | ||
298 | 1 | d_ptr->send<zwp_input_method_keyboard_grab_v2_send_repeat_info>(rate, delay); | |
299 | 1 | } | |
300 | |||
301 | struct zwp_input_popup_surface_v2_interface const | ||
302 | input_method_popup_surface_v2::Private::s_interface{ | ||
303 | destroyCallback, | ||
304 | }; | ||
305 | |||
306 | 1 | input_method_popup_surface_v2::Private::Private(Client* client, | |
307 | uint32_t version, | ||
308 | uint32_t id, | ||
309 | Surface* surface, | ||
310 | input_method_popup_surface_v2* q_ptr) | ||
311 | 2 | : Wayland::Resource<input_method_popup_surface_v2>(client, | |
312 | 1 | version, | |
313 | 1 | id, | |
314 | &zwp_input_popup_surface_v2_interface, | ||
315 | &s_interface, | ||
316 | 1 | q_ptr) | |
317 | 1 | , surface{surface} | |
318 | 1 | { | |
319 | 1 | } | |
320 | |||
321 | 1 | input_method_popup_surface_v2::input_method_popup_surface_v2(Client* client, | |
322 | uint32_t version, | ||
323 | uint32_t id, | ||
324 | Wrapland::Server::Surface* surface) | ||
325 | 1 | : QObject(nullptr) | |
326 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
1 | , d_ptr(new Private(client, version, id, surface, this)) |
327 | 1 | { | |
328 | 1 | } | |
329 | |||
330 | ✗ | Surface* input_method_popup_surface_v2::surface() const | |
331 | { | ||
332 | ✗ | return d_ptr->surface; | |
333 | } | ||
334 | |||
335 | 1 | void input_method_popup_surface_v2::set_text_input_rectangle(QRect const& rect) | |
336 | { | ||
337 | 2 | d_ptr->send<zwp_input_popup_surface_v2_send_text_input_rectangle>( | |
338 | 1 | rect.x(), rect.y(), rect.width(), rect.height()); | |
339 | 1 | } | |
340 | |||
341 | } | ||
342 |