diff options
Diffstat (limited to 'sway/input/input-manager.c')
-rw-r--r-- | sway/input/input-manager.c | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c new file mode 100644 index 00000000..52da8f5e --- /dev/null +++ b/sway/input/input-manager.c | |||
@@ -0,0 +1,304 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #include <ctype.h> | ||
3 | #include <float.h> | ||
4 | #include <limits.h> | ||
5 | #include <stdio.h> | ||
6 | #include <string.h> | ||
7 | #include <libinput.h> | ||
8 | #include <math.h> | ||
9 | #include "sway/config.h" | ||
10 | #include "sway/input/input-manager.h" | ||
11 | #include "sway/input/seat.h" | ||
12 | #include "sway/server.h" | ||
13 | #include "stringop.h" | ||
14 | #include "list.h" | ||
15 | #include "log.h" | ||
16 | |||
17 | static const char *default_seat = "seat0"; | ||
18 | |||
19 | // TODO make me not global | ||
20 | struct sway_input_manager *input_manager; | ||
21 | |||
22 | struct input_config *current_input_config = NULL; | ||
23 | struct seat_config *current_seat_config = NULL; | ||
24 | |||
25 | static struct sway_seat *input_manager_get_seat( | ||
26 | struct sway_input_manager *input, const char *seat_name) { | ||
27 | struct sway_seat *seat = NULL; | ||
28 | wl_list_for_each(seat, &input->seats, link) { | ||
29 | if (strcmp(seat->wlr_seat->name, seat_name) == 0) { | ||
30 | return seat; | ||
31 | } | ||
32 | } | ||
33 | |||
34 | return sway_seat_create(input, seat_name); | ||
35 | } | ||
36 | |||
37 | static inline int strlen_num(int num) { | ||
38 | if (num == 0) { | ||
39 | return 2; | ||
40 | } | ||
41 | return (int)((ceil(log10(abs(num)))+2)); | ||
42 | } | ||
43 | |||
44 | static char *get_device_identifier(struct wlr_input_device *device) { | ||
45 | int vendor = device->vendor; | ||
46 | int product = device->product; | ||
47 | char *name = strdup(device->name); | ||
48 | name = strip_whitespace(name); | ||
49 | |||
50 | char *p = name; | ||
51 | for (; *p; ++p) { | ||
52 | if (*p == ' ') { | ||
53 | *p = '_'; | ||
54 | } | ||
55 | } | ||
56 | |||
57 | int len = | ||
58 | (strlen(name) + | ||
59 | strlen_num(device->vendor) + | ||
60 | strlen_num(device->product) + | ||
61 | 3) * sizeof(char); | ||
62 | |||
63 | char *identifier = malloc(len); | ||
64 | if (!identifier) { | ||
65 | sway_log(L_ERROR, "Unable to allocate unique input device name"); | ||
66 | return NULL; | ||
67 | } | ||
68 | |||
69 | const char *fmt = "%d:%d:%s"; | ||
70 | snprintf(identifier, len, fmt, vendor, product, name); | ||
71 | free(name); | ||
72 | return identifier; | ||
73 | } | ||
74 | |||
75 | static struct sway_input_device *input_sway_device_from_wlr( | ||
76 | struct sway_input_manager *input, struct wlr_input_device *device) { | ||
77 | struct sway_input_device *input_device = NULL; | ||
78 | wl_list_for_each(input_device, &input->devices, link) { | ||
79 | if (input_device->wlr_device == device) { | ||
80 | return input_device; | ||
81 | } | ||
82 | } | ||
83 | return NULL; | ||
84 | } | ||
85 | |||
86 | static bool input_has_seat_configuration(struct sway_input_manager *input) { | ||
87 | struct sway_seat *seat = NULL; | ||
88 | wl_list_for_each(seat, &input->seats, link) { | ||
89 | if (seat->config) { | ||
90 | return true; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | return false; | ||
95 | } | ||
96 | |||
97 | static void input_add_notify(struct wl_listener *listener, void *data) { | ||
98 | struct sway_input_manager *input = | ||
99 | wl_container_of(listener, input, input_add); | ||
100 | struct wlr_input_device *device = data; | ||
101 | |||
102 | struct sway_input_device *input_device = | ||
103 | calloc(1, sizeof(struct sway_input_device)); | ||
104 | if (!sway_assert(input_device, "could not allocate input device")) { | ||
105 | return; | ||
106 | } | ||
107 | |||
108 | input_device->wlr_device = device; | ||
109 | input_device->identifier = get_device_identifier(device); | ||
110 | wl_list_insert(&input->devices, &input_device->link); | ||
111 | |||
112 | sway_log(L_DEBUG, "adding device: '%s'", | ||
113 | input_device->identifier); | ||
114 | |||
115 | // find config | ||
116 | for (int i = 0; i < config->input_configs->length; ++i) { | ||
117 | struct input_config *input_config = config->input_configs->items[i]; | ||
118 | if (strcmp(input_config->identifier, input_device->identifier) == 0) { | ||
119 | input_device->config = input_config; | ||
120 | break; | ||
121 | } | ||
122 | } | ||
123 | |||
124 | struct sway_seat *seat = NULL; | ||
125 | if (!input_has_seat_configuration(input)) { | ||
126 | sway_log(L_DEBUG, "no seat configuration, using default seat"); | ||
127 | seat = input_manager_get_seat(input, default_seat); | ||
128 | sway_seat_add_device(seat, input_device); | ||
129 | return; | ||
130 | } | ||
131 | |||
132 | bool added = false; | ||
133 | wl_list_for_each(seat, &input->seats, link) { | ||
134 | if (seat->config && | ||
135 | (seat_config_get_attachment(seat->config, | ||
136 | input_device->identifier) || | ||
137 | seat_config_get_attachment(seat->config, "*"))) { | ||
138 | sway_seat_add_device(seat, input_device); | ||
139 | added = true; | ||
140 | } | ||
141 | } | ||
142 | |||
143 | if (!added) { | ||
144 | wl_list_for_each(seat, &input->seats, link) { | ||
145 | if (seat->config && seat->config->fallback == 1) { | ||
146 | sway_seat_add_device(seat, input_device); | ||
147 | added = true; | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | |||
152 | if (!added) { | ||
153 | sway_log(L_DEBUG, | ||
154 | "device '%s' is not configured on any seats", | ||
155 | input_device->identifier); | ||
156 | } | ||
157 | } | ||
158 | |||
159 | static void input_remove_notify(struct wl_listener *listener, void *data) { | ||
160 | struct sway_input_manager *input = | ||
161 | wl_container_of(listener, input, input_remove); | ||
162 | struct wlr_input_device *device = data; | ||
163 | |||
164 | struct sway_input_device *input_device = | ||
165 | input_sway_device_from_wlr(input, device); | ||
166 | |||
167 | if (!sway_assert(input_device, "could not find sway device")) { | ||
168 | return; | ||
169 | } | ||
170 | |||
171 | sway_log(L_DEBUG, "removing device: '%s'", | ||
172 | input_device->identifier); | ||
173 | |||
174 | struct sway_seat *seat = NULL; | ||
175 | wl_list_for_each(seat, &input->seats, link) { | ||
176 | sway_seat_remove_device(seat, input_device); | ||
177 | } | ||
178 | |||
179 | wl_list_remove(&input_device->link); | ||
180 | free(input_device->identifier); | ||
181 | free(input_device); | ||
182 | } | ||
183 | |||
184 | struct sway_input_manager *sway_input_manager_create( | ||
185 | struct sway_server *server) { | ||
186 | struct sway_input_manager *input = | ||
187 | calloc(1, sizeof(struct sway_input_manager)); | ||
188 | if (!input) { | ||
189 | return NULL; | ||
190 | } | ||
191 | input->server = server; | ||
192 | |||
193 | wl_list_init(&input->devices); | ||
194 | wl_list_init(&input->seats); | ||
195 | |||
196 | // create the default seat | ||
197 | input_manager_get_seat(input, default_seat); | ||
198 | |||
199 | input->input_add.notify = input_add_notify; | ||
200 | wl_signal_add(&server->backend->events.input_add, &input->input_add); | ||
201 | |||
202 | input->input_remove.notify = input_remove_notify; | ||
203 | wl_signal_add(&server->backend->events.input_remove, &input->input_remove); | ||
204 | |||
205 | return input; | ||
206 | } | ||
207 | |||
208 | bool sway_input_manager_has_focus(struct sway_input_manager *input, | ||
209 | swayc_t *container) { | ||
210 | struct sway_seat *seat = NULL; | ||
211 | wl_list_for_each(seat, &input->seats, link) { | ||
212 | if (seat->focus == container) { | ||
213 | return true; | ||
214 | } | ||
215 | } | ||
216 | |||
217 | return false; | ||
218 | } | ||
219 | |||
220 | void sway_input_manager_set_focus(struct sway_input_manager *input, | ||
221 | swayc_t *container) { | ||
222 | struct sway_seat *seat ; | ||
223 | wl_list_for_each(seat, &input->seats, link) { | ||
224 | sway_seat_set_focus(seat, container); | ||
225 | } | ||
226 | } | ||
227 | |||
228 | void sway_input_manager_apply_input_config(struct sway_input_manager *input, | ||
229 | struct input_config *input_config) { | ||
230 | struct sway_input_device *input_device = NULL; | ||
231 | wl_list_for_each(input_device, &input->devices, link) { | ||
232 | if (strcmp(input_device->identifier, input_config->identifier) == 0) { | ||
233 | input_device->config = input_config; | ||
234 | |||
235 | struct sway_seat *seat = NULL; | ||
236 | wl_list_for_each(seat, &input->seats, link) { | ||
237 | sway_seat_configure_device(seat, input_device); | ||
238 | } | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | |||
243 | void sway_input_manager_apply_seat_config(struct sway_input_manager *input, | ||
244 | struct seat_config *seat_config) { | ||
245 | sway_log(L_DEBUG, "applying new seat config for seat %s", | ||
246 | seat_config->name); | ||
247 | struct sway_seat *seat = input_manager_get_seat(input, seat_config->name); | ||
248 | if (!seat) { | ||
249 | return; | ||
250 | } | ||
251 | |||
252 | sway_seat_set_config(seat, seat_config); | ||
253 | |||
254 | // for every device, try to add it to a seat and if no seat has it | ||
255 | // attached, add it to the fallback seats. | ||
256 | struct sway_input_device *input_device = NULL; | ||
257 | wl_list_for_each(input_device, &input->devices, link) { | ||
258 | list_t *seat_list = create_list(); | ||
259 | struct sway_seat *seat = NULL; | ||
260 | wl_list_for_each(seat, &input->seats, link) { | ||
261 | if (!seat->config) { | ||
262 | continue; | ||
263 | } | ||
264 | if (seat_config_get_attachment(seat->config, "*") || | ||
265 | seat_config_get_attachment(seat->config, | ||
266 | input_device->identifier)) { | ||
267 | list_add(seat_list, seat); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | if (seat_list->length) { | ||
272 | wl_list_for_each(seat, &input->seats, link) { | ||
273 | bool attached = false; | ||
274 | for (int i = 0; i < seat_list->length; ++i) { | ||
275 | if (seat == seat_list->items[i]) { | ||
276 | attached = true; | ||
277 | break; | ||
278 | } | ||
279 | } | ||
280 | if (attached) { | ||
281 | sway_seat_add_device(seat, input_device); | ||
282 | } else { | ||
283 | sway_seat_remove_device(seat, input_device); | ||
284 | } | ||
285 | } | ||
286 | } else { | ||
287 | wl_list_for_each(seat, &input->seats, link) { | ||
288 | if (seat->config && seat->config->fallback == 1) { | ||
289 | sway_seat_add_device(seat, input_device); | ||
290 | } else { | ||
291 | sway_seat_remove_device(seat, input_device); | ||
292 | } | ||
293 | } | ||
294 | } | ||
295 | list_free(seat_list); | ||
296 | } | ||
297 | } | ||
298 | |||
299 | void sway_input_manager_configure_xcursor(struct sway_input_manager *input) { | ||
300 | struct sway_seat *seat = NULL; | ||
301 | wl_list_for_each(seat, &input->seats, link) { | ||
302 | sway_seat_configure_xcursor(seat); | ||
303 | } | ||
304 | } | ||