Directory: | ./ |
---|---|
File: | server/text_input_pool.cpp |
Date: | 2024-01-22 17:25:27 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 123 | 183 | 67.2% |
Branches: | 44 | 104 | 42.3% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | #include "text_input_pool.h" | ||
2 | #include "display.h" | ||
3 | #include "input_method_v2.h" | ||
4 | #include "seat.h" | ||
5 | #include "seat_p.h" | ||
6 | #include "surface.h" | ||
7 | #include "text_input_v2.h" | ||
8 | #include "text_input_v2_p.h" | ||
9 | #include "text_input_v3.h" | ||
10 | #include "text_input_v3_p.h" | ||
11 | #include "utils.h" | ||
12 | |||
13 | namespace Wrapland::Server | ||
14 | { | ||
15 | |||
16 | 309 | text_input_pool::text_input_pool(Seat* seat) | |
17 | 309 | : seat{seat} | |
18 | { | ||
19 | 309 | } | |
20 | |||
21 | 39 | void text_input_pool::register_device(text_input_v2* ti) | |
22 | { | ||
23 | // Text input version 0 might call this multiple times. | ||
24 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
|
39 | if (std::find(v2_devices.begin(), v2_devices.end(), ti) != v2_devices.end()) { |
25 | ✗ | return; | |
26 | } | ||
27 | 39 | v2_devices.push_back(ti); | |
28 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
39 | if (focus.surface && focus.surface->client() == ti->d_ptr->client->handle) { |
29 | // This is a text input for the currently focused text input surface. | ||
30 | ✗ | if (!v2.text_input) { | |
31 | ✗ | v2.text_input = ti; | |
32 | ✗ | ti->d_ptr->send_enter(focus.surface, v2.serial); | |
33 | ✗ | Q_EMIT seat->focusedTextInputChanged(); | |
34 | } | ||
35 | } | ||
36 | 78 | QObject::connect(ti, &text_input_v2::resourceDestroyed, seat, [this, ti] { | |
37 | 39 | remove_one(v2_devices, ti); | |
38 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
|
39 | if (v2.text_input == ti) { |
39 | ✗ | v2.text_input = nullptr; | |
40 | ✗ | Q_EMIT seat->focusedTextInputChanged(); | |
41 | } | ||
42 | 39 | }); | |
43 | 39 | } | |
44 | |||
45 | 33 | void text_input_pool::register_device(text_input_v3* ti) | |
46 | { | ||
47 | // Text input version 0 might call this multiple times. | ||
48 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
|
33 | if (std::find(v3_devices.begin(), v3_devices.end(), ti) != v3_devices.end()) { |
49 | ✗ | return; | |
50 | } | ||
51 | 33 | v3_devices.push_back(ti); | |
52 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
33 | if (focus.surface && focus.surface->client() == ti->d_ptr->client->handle) { |
53 | // This is a text input for the currently focused text input surface. | ||
54 | ✗ | if (!v3.text_input) { | |
55 | ✗ | v3.text_input = ti; | |
56 | ✗ | ti->d_ptr->send_enter(focus.surface); | |
57 | ✗ | Q_EMIT seat->focusedTextInputChanged(); | |
58 | } | ||
59 | } | ||
60 | 66 | QObject::connect(ti, &text_input_v3::resourceDestroyed, seat, [this, ti] { | |
61 | 33 | remove_one(v3_devices, ti); | |
62 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
|
33 | if (v3.text_input == ti) { |
63 | ✗ | v3.text_input = nullptr; | |
64 | ✗ | Q_EMIT seat->focusedTextInputChanged(); | |
65 | } | ||
66 | 33 | }); | |
67 | 33 | } | |
68 | |||
69 | 195 | bool text_input_pool::set_v2_focused_surface(Surface* surface) | |
70 | { | ||
71 | 195 | auto const serial = seat->d_ptr->display()->handle->nextSerial(); | |
72 | 195 | auto const old_ti = v2.text_input; | |
73 | |||
74 |
2/2✓ Branch 0 taken 154 times.
✓ Branch 1 taken 41 times.
|
195 | if (old_ti) { |
75 | 41 | old_ti->d_ptr->send_leave(serial, focus.surface); | |
76 | 41 | } | |
77 | |||
78 | 195 | v2.text_input = nullptr; | |
79 | |||
80 |
2/2✓ Branch 0 taken 160 times.
✓ Branch 1 taken 35 times.
|
195 | if (!v3.text_input) { |
81 | // Only text-input v3 not set, we allow v2 to be active. | ||
82 | 160 | v2.text_input = interfaceForSurface(surface, v2_devices); | |
83 | 160 | } | |
84 | |||
85 |
2/2✓ Branch 0 taken 94 times.
✓ Branch 1 taken 101 times.
|
195 | if (surface) { |
86 | 101 | v2.serial = serial; | |
87 | 101 | } | |
88 | |||
89 |
2/2✓ Branch 0 taken 154 times.
✓ Branch 1 taken 41 times.
|
195 | if (v2.text_input) { |
90 | 41 | v2.text_input->d_ptr->send_enter(surface, serial); | |
91 | 41 | } | |
92 | |||
93 | 195 | return old_ti != v2.text_input; | |
94 | } | ||
95 | |||
96 | 195 | bool text_input_pool::set_v3_focused_surface(Surface* surface) | |
97 | { | ||
98 | 195 | auto const old_ti = v3.text_input; | |
99 | |||
100 |
2/2✓ Branch 0 taken 35 times.
✓ Branch 1 taken 160 times.
|
195 | if (old_ti) { |
101 | 35 | old_ti->d_ptr->send_leave(focus.surface); | |
102 | 35 | } | |
103 | |||
104 | 195 | v3.text_input = interfaceForSurface(surface, v3_devices); | |
105 | |||
106 |
2/2✓ Branch 0 taken 35 times.
✓ Branch 1 taken 160 times.
|
195 | if (v3.text_input) { |
107 | 35 | v3.text_input->d_ptr->send_enter(surface); | |
108 | 35 | } | |
109 | |||
110 | 195 | return old_ti != v3.text_input; | |
111 | } | ||
112 | |||
113 | 195 | void text_input_pool::set_focused_surface(Surface* surface) | |
114 | { | ||
115 |
2/2✓ Branch 0 taken 94 times.
✓ Branch 1 taken 101 times.
|
195 | if (focus.surface) { |
116 | 101 | QObject::disconnect(focus.destroy_connection); | |
117 | 101 | } | |
118 | |||
119 | 195 | auto changed = set_v3_focused_surface(surface); | |
120 | 195 | changed |= set_v2_focused_surface(surface); | |
121 | |||
122 | 195 | focus = {}; | |
123 | |||
124 |
2/2✓ Branch 0 taken 94 times.
✓ Branch 1 taken 101 times.
|
195 | if (surface) { |
125 | 101 | focus.surface = surface; | |
126 | 101 | focus.destroy_connection = QObject::connect( | |
127 | 184 | surface, &Surface::resourceDestroyed, seat, [this] { set_focused_surface(nullptr); }); | |
128 | 101 | } | |
129 | |||
130 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 146 times.
|
195 | if (changed) { |
131 | 146 | Q_EMIT seat->focusedTextInputChanged(); | |
132 | 146 | } | |
133 | 195 | } | |
134 | |||
135 | 3 | void sync_to_text_input_v2(text_input_v2* ti, | |
136 | input_method_v2_state const& prev, | ||
137 | input_method_v2_state const& next) | ||
138 | { | ||
139 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (!ti) { |
140 | 3 | return; | |
141 | } | ||
142 | |||
143 | ✗ | if (next.delete_surrounding_text.update) { | |
144 | ✗ | auto const& text = next.delete_surrounding_text; | |
145 | ✗ | ti->delete_surrounding_text(text.before_length, text.after_length); | |
146 | } | ||
147 | ✗ | if (prev.preedit_string.data != next.preedit_string.data) { | |
148 | ✗ | ti->set_preedit_string(next.preedit_string.data, ""); | |
149 | } | ||
150 | ✗ | if (prev.preedit_string.cursor_begin != next.preedit_string.cursor_begin | |
151 | ✗ | || prev.preedit_string.cursor_end != next.preedit_string.cursor_end) { | |
152 | ✗ | ti->set_cursor_position(static_cast<int32_t>(next.preedit_string.cursor_begin), | |
153 | ✗ | static_cast<int32_t>(next.preedit_string.cursor_end)); | |
154 | } | ||
155 | ✗ | if (prev.commit_string.data != next.commit_string.data) { | |
156 | ✗ | ti->commit(next.commit_string.data); | |
157 | } | ||
158 | 3 | } | |
159 | |||
160 | // TODO(romangg): With C++20's default comparision operator compare prev members with next members | ||
161 | // and maybe remove the "update" members in the state. Or are the string comparisons to expensive? | ||
162 | 3 | void sync_to_text_input_v3(text_input_v3* ti, | |
163 | input_method_v2_state const& /*prev*/, | ||
164 | input_method_v2_state const& next) | ||
165 | { | ||
166 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
|
3 | if (!ti) { |
167 | 2 | return; | |
168 | } | ||
169 | |||
170 | 1 | auto has_update{false}; | |
171 | |||
172 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (next.delete_surrounding_text.update) { |
173 | ✗ | auto const& text = next.delete_surrounding_text; | |
174 | ✗ | ti->delete_surrounding_text(text.before_length, text.after_length); | |
175 | ✗ | has_update = true; | |
176 | } | ||
177 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (next.preedit_string.update) { |
178 | 1 | auto const& preedit = next.preedit_string; | |
179 | 1 | ti->set_preedit_string(preedit.data, preedit.cursor_begin, preedit.cursor_end); | |
180 | 1 | has_update = true; | |
181 | 1 | } | |
182 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (next.commit_string.update) { |
183 | 1 | ti->commit_string(next.commit_string.data); | |
184 | 1 | has_update = true; | |
185 | 1 | } | |
186 | |||
187 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (has_update) { |
188 | 1 | ti->done(); | |
189 | 1 | } | |
190 | 3 | } | |
191 | |||
192 | 3 | void text_input_pool::sync_to_text_input(input_method_v2_state const& prev, | |
193 | input_method_v2_state const& next) const | ||
194 | { | ||
195 | 3 | sync_to_text_input_v2(v2.text_input, prev, next); | |
196 | 3 | sync_to_text_input_v3(v3.text_input, prev, next); | |
197 | 3 | } | |
198 | |||
199 | ✗ | text_input_v3_content_hints convert_hints_v2_to_v3(text_input_v2_content_hints hints) | |
200 | { | ||
201 | ✗ | auto hints_number = static_cast<uint32_t>(hints); | |
202 | ✗ | return static_cast<text_input_v3_content_hints>(hints_number); | |
203 | } | ||
204 | |||
205 | ✗ | text_input_v3_content_purpose convert_purpose_v2_to_v3(text_input_v2_content_purpose purpose) | |
206 | { | ||
207 | ✗ | return static_cast<text_input_v3_content_purpose>(purpose); | |
208 | } | ||
209 | |||
210 | 56 | void sync_to_input_method_v2(input_method_v2* im, | |
211 | text_input_v2_state const& prev, | ||
212 | text_input_v2_state const& next) | ||
213 | { | ||
214 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 56 times.
|
56 | if (!im) { |
215 | 56 | return; | |
216 | } | ||
217 | |||
218 | ✗ | auto has_update{false}; | |
219 | |||
220 | ✗ | if (prev.enabled != next.enabled) { | |
221 | ✗ | im->set_active(next.enabled); | |
222 | ✗ | has_update = true; | |
223 | } | ||
224 | ✗ | if (next.surrounding_text.data != prev.surrounding_text.data | |
225 | ✗ | || next.surrounding_text.cursor_position != prev.surrounding_text.cursor_position | |
226 | ✗ | || next.surrounding_text.selection_anchor != prev.surrounding_text.selection_anchor) { | |
227 | ✗ | auto const& text = next.surrounding_text; | |
228 | ✗ | im->set_surrounding_text(text.data, | |
229 | ✗ | text.cursor_position, | |
230 | ✗ | text.selection_anchor, | |
231 | text_input_v3_change_cause::input_method); | ||
232 | ✗ | has_update = true; | |
233 | } | ||
234 | ✗ | if (prev.content.hints != next.content.hints || prev.content.purpose != next.content.purpose) { | |
235 | ✗ | im->set_content_type(convert_hints_v2_to_v3(next.content.hints), | |
236 | ✗ | convert_purpose_v2_to_v3(next.content.purpose)); | |
237 | ✗ | has_update = true; | |
238 | } | ||
239 | |||
240 | ✗ | if (has_update) { | |
241 | ✗ | im->done(); | |
242 | } | ||
243 | |||
244 | ✗ | if (prev.cursor_rectangle != next.cursor_rectangle) { | |
245 | ✗ | for (auto popup : im->get_popups()) { | |
246 | ✗ | popup->set_text_input_rectangle(next.cursor_rectangle); | |
247 | } | ||
248 | } | ||
249 | 56 | } | |
250 | |||
251 | 58 | void sync_to_input_method_v2(input_method_v2* im, | |
252 | text_input_v3_state const& prev, | ||
253 | text_input_v3_state const& next) | ||
254 | { | ||
255 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 57 times.
|
58 | if (!im) { |
256 | 57 | return; | |
257 | } | ||
258 | |||
259 | 1 | auto has_update{false}; | |
260 | |||
261 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (prev.enabled != next.enabled) { |
262 | ✗ | im->set_active(next.enabled); | |
263 | ✗ | has_update = true; | |
264 | } | ||
265 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (next.surrounding_text.update) { |
266 | 1 | auto const& text = next.surrounding_text; | |
267 | 2 | im->set_surrounding_text( | |
268 | 1 | text.data, text.cursor_position, text.selection_anchor, text.change_cause); | |
269 | 1 | has_update = true; | |
270 | 1 | } | |
271 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
1 | if (prev.content.hints != next.content.hints || prev.content.purpose != next.content.purpose) { |
272 | ✗ | im->set_content_type(next.content.hints, next.content.purpose); | |
273 | ✗ | has_update = true; | |
274 | } | ||
275 | |||
276 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (has_update) { |
277 | 1 | im->done(); | |
278 | 1 | } | |
279 | |||
280 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (prev.cursor_rectangle != next.cursor_rectangle) { |
281 | ✗ | for (auto popup : im->get_popups()) { | |
282 | ✗ | popup->set_text_input_rectangle(next.cursor_rectangle); | |
283 | } | ||
284 | } | ||
285 | 58 | } | |
286 | |||
287 | 56 | void text_input_pool::sync_to_input_method(text_input_v2_state const& prev, | |
288 | text_input_v2_state const& next) const | ||
289 | { | ||
290 |
2/2✓ Branch 0 taken 51 times.
✓ Branch 1 taken 5 times.
|
56 | if (prev.enabled != next.enabled) { |
291 | 5 | Q_EMIT seat->text_input_v2_enabled_changed(next.enabled); | |
292 | 5 | } | |
293 | |||
294 | 56 | sync_to_input_method_v2(seat->get_input_method_v2(), prev, next); | |
295 | 56 | } | |
296 | |||
297 | 58 | void text_input_pool::sync_to_input_method(text_input_v3_state const& prev, | |
298 | text_input_v3_state const& next) const | ||
299 | { | ||
300 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 9 times.
|
58 | if (prev.enabled != next.enabled) { |
301 | 9 | Q_EMIT seat->text_input_v3_enabled_changed(next.enabled); | |
302 | 9 | } | |
303 | |||
304 | 58 | sync_to_input_method_v2(seat->get_input_method_v2(), prev, next); | |
305 | 58 | } | |
306 | |||
307 | } | ||
308 |