GCC Code Coverage Report


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