1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /*
3 * Copyright 2018 Red Hat, Inc.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * Author: Carlos Garnacho <carlosg@gnome.org>
19 */
20
21 #include "config.h"
22
23 #ifdef HAVE_LIBGUDEV
24 #include <gudev/gudev.h>
25 #endif
26
27 #include "backends/meta-input-device-private.h"
28 #include "meta-input-mapper-private.h"
29 #include "meta-monitor-manager-private.h"
30 #include "meta-logical-monitor.h"
31 #include "meta-backend-private.h"
32
33 #define MAX_SIZE_MATCH_DIFF 0.05
34
35 typedef struct _MetaMapperInputInfo MetaMapperInputInfo;
36 typedef struct _MetaMapperOutputInfo MetaMapperOutputInfo;
37 typedef struct _MappingHelper MappingHelper;
38 typedef struct _DeviceCandidates DeviceCandidates;
39 typedef struct _DeviceMatch DeviceMatch;
40
41 struct _MetaInputMapper
42 {
43 GObject parent_instance;
44 MetaMonitorManager *monitor_manager;
45 ClutterSeat *seat;
46 GHashTable *input_devices; /* ClutterInputDevice -> MetaMapperInputInfo */
47 GHashTable *output_devices; /* MetaLogicalMonitor -> MetaMapperOutputInfo */
48 #ifdef HAVE_LIBGUDEV
49 GUdevClient *udev_client;
50 #endif
51 };
52
53 typedef enum
54 {
55 META_INPUT_CAP_TOUCH = 1 << 0, /* touch device, either touchscreen or tablet */
56 META_INPUT_CAP_STYLUS = 1 << 1, /* tablet pen */
57 META_INPUT_CAP_ERASER = 1 << 2, /* tablet eraser */
58 META_INPUT_CAP_PAD = 1 << 3, /* pad device, most usually in tablets */
59 META_INPUT_CAP_CURSOR = 1 << 4 /* pointer-like device in tablets */
60 } MetaInputCapabilityFlags;
61
62 typedef enum
63 {
64 META_MATCH_EDID_VENDOR, /* EDID vendor match, eg. "WAC" for Wacom */
65 META_MATCH_EDID_PARTIAL, /* Partial EDID model match, eg. "Cintiq" */
66 META_MATCH_EDID_FULL, /* Full EDID model match, eg. "Cintiq 12WX" */
67 META_MATCH_SIZE, /* Size from input device and output match */
68 META_MATCH_IS_BUILTIN, /* Output is builtin, applies mainly to system-integrated devices */
69 META_MATCH_CONFIG, /* Specified by config */
70 N_OUTPUT_MATCHES
71 } MetaOutputMatchType;
72
73 struct _MetaMapperInputInfo
74 {
75 ClutterInputDevice *device;
76 MetaInputMapper *mapper;
77 MetaMapperOutputInfo *output;
78 GSettings *settings;
79 guint builtin : 1;
80 };
81
82 struct _MetaMapperOutputInfo
83 {
84 MetaLogicalMonitor *logical_monitor;
85 GList *input_devices;
86 MetaInputCapabilityFlags attached_caps;
87 };
88
89 struct _MappingHelper
90 {
91 GArray *device_maps;
92 };
93
94 struct _DeviceMatch
95 {
96 MetaMonitor *monitor;
97 uint32_t score;
98 };
99
100 struct _DeviceCandidates
101 {
102 MetaMapperInputInfo *input;
103
104 GArray *matches; /* Array of DeviceMatch */
105
106 int best;
107 };
108
109 enum
110 {
111 DEVICE_MAPPED,
112 DEVICE_ENABLED,
113 DEVICE_ASPECT_RATIO,
114 N_SIGNALS
115 };
116
117 static guint signals[N_SIGNALS] = { 0, };
118
119 static void mapper_output_info_remove_input (MetaMapperOutputInfo *output,
120 MetaMapperInputInfo *input);
121
122 static void mapper_recalculate_input (MetaInputMapper *mapper,
123 MetaMapperInputInfo *input);
124
G_DEFINE_TYPE(MetaInputMapper,meta_input_mapper,G_TYPE_OBJECT)125 G_DEFINE_TYPE (MetaInputMapper, meta_input_mapper, G_TYPE_OBJECT)
126
127 static GSettings *
128 get_device_settings (ClutterInputDevice *device)
129 {
130 const char *group, *schema, *vendor, *product;
131 ClutterInputDeviceType type;
132 GSettings *settings;
133 char *path;
134
135 type = clutter_input_device_get_device_type (device);
136
137 if (type == CLUTTER_TOUCHSCREEN_DEVICE)
138 {
139 group = "touchscreens";
140 schema = "org.gnome.desktop.peripherals.touchscreen";
141 }
142 else if (type == CLUTTER_TABLET_DEVICE ||
143 type == CLUTTER_PEN_DEVICE ||
144 type == CLUTTER_ERASER_DEVICE ||
145 type == CLUTTER_CURSOR_DEVICE ||
146 type == CLUTTER_PAD_DEVICE)
147 {
148 group = "tablets";
149 schema = "org.gnome.desktop.peripherals.tablet";
150 }
151 else
152 {
153 return NULL;
154 }
155
156 vendor = clutter_input_device_get_vendor_id (device);
157 product = clutter_input_device_get_product_id (device);
158 path = g_strdup_printf ("/org/gnome/desktop/peripherals/%s/%s:%s/",
159 group, vendor, product);
160
161 settings = g_settings_new_with_path (schema, path);
162 g_free (path);
163
164 return settings;
165 }
166
167 static void
settings_output_changed_cb(GSettings * settings,const char * key,MetaMapperInputInfo * info)168 settings_output_changed_cb (GSettings *settings,
169 const char *key,
170 MetaMapperInputInfo *info)
171 {
172 if (info->output != NULL)
173 mapper_output_info_remove_input (info->output, info);
174
175 mapper_recalculate_input (info->mapper, info);
176 }
177
178 static MetaMapperInputInfo *
mapper_input_info_new(ClutterInputDevice * device,MetaInputMapper * mapper)179 mapper_input_info_new (ClutterInputDevice *device,
180 MetaInputMapper *mapper)
181 {
182 MetaMapperInputInfo *info;
183
184 info = g_new0 (MetaMapperInputInfo, 1);
185 info->mapper = mapper;
186 info->device = device;
187 info->settings = get_device_settings (device);
188
189 g_signal_connect (info->settings, "changed::output",
190 G_CALLBACK (settings_output_changed_cb), info);
191
192 return info;
193 }
194
195 static void
mapper_input_info_free(MetaMapperInputInfo * info)196 mapper_input_info_free (MetaMapperInputInfo *info)
197 {
198 g_signal_handlers_disconnect_by_func (info->settings, settings_output_changed_cb, info);
199 g_object_unref (info->settings);
200 g_free (info);
201 }
202
203 static MetaMapperOutputInfo *
mapper_output_info_new(MetaLogicalMonitor * logical_monitor)204 mapper_output_info_new (MetaLogicalMonitor *logical_monitor)
205 {
206 MetaMapperOutputInfo *info;
207
208 info = g_new0 (MetaMapperOutputInfo, 1);
209 info->logical_monitor = logical_monitor;
210
211 return info;
212 }
213
214 static void
mapper_output_info_free(MetaMapperOutputInfo * info)215 mapper_output_info_free (MetaMapperOutputInfo *info)
216 {
217 g_free (info);
218 }
219
220 static MetaInputCapabilityFlags
mapper_input_info_get_caps(MetaMapperInputInfo * info)221 mapper_input_info_get_caps (MetaMapperInputInfo *info)
222 {
223 ClutterInputDeviceType type;
224
225 type = clutter_input_device_get_device_type (info->device);
226
227 switch (type)
228 {
229 case CLUTTER_TOUCHSCREEN_DEVICE:
230 return META_INPUT_CAP_TOUCH;
231 case CLUTTER_TABLET_DEVICE:
232 case CLUTTER_PEN_DEVICE:
233 return META_INPUT_CAP_STYLUS;
234 case CLUTTER_ERASER_DEVICE:
235 return META_INPUT_CAP_ERASER;
236 case CLUTTER_CURSOR_DEVICE:
237 return META_INPUT_CAP_CURSOR;
238 case CLUTTER_PAD_DEVICE:
239 return META_INPUT_CAP_PAD;
240 default:
241 return 0;
242 }
243 }
244
245 static void
mapper_input_info_set_output(MetaMapperInputInfo * input,MetaMapperOutputInfo * output,MetaMonitor * monitor)246 mapper_input_info_set_output (MetaMapperInputInfo *input,
247 MetaMapperOutputInfo *output,
248 MetaMonitor *monitor)
249 {
250 MetaInputMapper *mapper = input->mapper;
251 float matrix[6] = { 1, 0, 0, 0, 1, 0 };
252 double aspect_ratio;
253 int width, height;
254
255 if (input->output == output)
256 return;
257
258 input->output = output;
259
260 if (output && monitor)
261 {
262 meta_monitor_manager_get_monitor_matrix (mapper->monitor_manager,
263 monitor,
264 output->logical_monitor,
265 matrix);
266 meta_monitor_get_current_resolution (monitor, &width, &height);
267 }
268 else
269 {
270 meta_monitor_manager_get_screen_size (mapper->monitor_manager,
271 &width, &height);
272 }
273
274 aspect_ratio = (double) width / height;
275
276 g_signal_emit (input->mapper, signals[DEVICE_MAPPED], 0,
277 input->device, matrix);
278 g_signal_emit (input->mapper, signals[DEVICE_ASPECT_RATIO], 0,
279 input->device, aspect_ratio);
280 }
281
282 static void
mapper_output_info_add_input(MetaMapperOutputInfo * output,MetaMapperInputInfo * input,MetaMonitor * monitor)283 mapper_output_info_add_input (MetaMapperOutputInfo *output,
284 MetaMapperInputInfo *input,
285 MetaMonitor *monitor)
286 {
287 g_assert (input->output == NULL);
288
289 output->input_devices = g_list_prepend (output->input_devices, input);
290 output->attached_caps |= mapper_input_info_get_caps (input);
291
292 mapper_input_info_set_output (input, output, monitor);
293 }
294
295 static void
mapper_output_info_remove_input(MetaMapperOutputInfo * output,MetaMapperInputInfo * input)296 mapper_output_info_remove_input (MetaMapperOutputInfo *output,
297 MetaMapperInputInfo *input)
298 {
299 GList *l;
300
301 g_assert (input->output == output);
302
303 output->input_devices = g_list_remove (output->input_devices, input);
304 output->attached_caps = 0;
305
306 for (l = output->input_devices; l; l = l->next)
307 output->attached_caps |= mapper_input_info_get_caps (l->data);
308
309 mapper_input_info_set_output (input, NULL, NULL);
310 }
311
312 static void
mapper_output_info_clear_inputs(MetaMapperOutputInfo * output)313 mapper_output_info_clear_inputs (MetaMapperOutputInfo *output)
314 {
315 while (output->input_devices)
316 {
317 MetaMapperInputInfo *input = output->input_devices->data;
318
319 mapper_input_info_set_output (input, NULL, NULL);
320 output->input_devices = g_list_remove (output->input_devices, input);
321 }
322
323 output->attached_caps = 0;
324 }
325
326 static void
clear_candidates(DeviceCandidates * candidates)327 clear_candidates (DeviceCandidates *candidates)
328 {
329 g_clear_pointer (&candidates->matches, g_array_unref);
330 }
331
332 static void
mapping_helper_init(MappingHelper * helper)333 mapping_helper_init (MappingHelper *helper)
334 {
335 helper->device_maps = g_array_new (FALSE, FALSE, sizeof (DeviceCandidates));
336 g_array_set_clear_func (helper->device_maps,
337 (GDestroyNotify) clear_candidates);
338 }
339
340 static void
mapping_helper_release(MappingHelper * helper)341 mapping_helper_release (MappingHelper *helper)
342 {
343 g_array_unref (helper->device_maps);
344 }
345
346 static gboolean
match_edid(MetaMapperInputInfo * input,MetaMonitor * monitor,MetaOutputMatchType * match_type)347 match_edid (MetaMapperInputInfo *input,
348 MetaMonitor *monitor,
349 MetaOutputMatchType *match_type)
350 {
351 const gchar *dev_name;
352
353 dev_name = clutter_input_device_get_device_name (input->device);
354
355 if (strcasestr (dev_name, meta_monitor_get_vendor (monitor)) == NULL)
356 return FALSE;
357
358 *match_type = META_MATCH_EDID_VENDOR;
359
360 if (strcasestr (dev_name, meta_monitor_get_product (monitor)) != NULL)
361 {
362 *match_type = META_MATCH_EDID_FULL;
363 }
364 else
365 {
366 int i;
367 g_auto (GStrv) split = NULL;
368
369 split = g_strsplit (meta_monitor_get_product (monitor), " ", -1);
370
371 for (i = 0; split[i]; i++)
372 {
373 if (strcasestr (dev_name, split[i]) != NULL)
374 {
375 *match_type = META_MATCH_EDID_PARTIAL;
376 break;
377 }
378 }
379 }
380
381 return TRUE;
382 }
383
384 static gboolean
input_device_get_physical_size(MetaInputMapper * mapper,ClutterInputDevice * device,double * width,double * height)385 input_device_get_physical_size (MetaInputMapper *mapper,
386 ClutterInputDevice *device,
387 double *width,
388 double *height)
389 {
390 #ifdef HAVE_LIBGUDEV
391 g_autoptr (GUdevDevice) udev_device = NULL;
392 const char *node;
393
394 node = clutter_input_device_get_device_node (device);
395 if (!node)
396 return FALSE;
397
398 udev_device = g_udev_client_query_by_device_file (mapper->udev_client, node);
399
400 if (udev_device &&
401 g_udev_device_has_property (udev_device, "ID_INPUT_WIDTH_MM"))
402 {
403 *width = g_udev_device_get_property_as_double (udev_device,
404 "ID_INPUT_WIDTH_MM");
405 *height = g_udev_device_get_property_as_double (udev_device,
406 "ID_INPUT_HEIGHT_MM");
407 return TRUE;
408 }
409 #endif
410
411 return FALSE;
412 }
413
414 static gboolean
match_size(MetaMapperInputInfo * input,MetaMonitor * monitor)415 match_size (MetaMapperInputInfo *input,
416 MetaMonitor *monitor)
417 {
418 double w_diff, h_diff;
419 int o_width, o_height;
420 double i_width, i_height;
421
422 if (!input_device_get_physical_size (input->mapper, input->device,
423 &i_width, &i_height))
424 return FALSE;
425
426 meta_monitor_get_physical_dimensions (monitor, &o_width, &o_height);
427 w_diff = ABS (1 - ((double) o_width / i_width));
428 h_diff = ABS (1 - ((double) o_height / i_height));
429
430 return w_diff < MAX_SIZE_MATCH_DIFF && h_diff < MAX_SIZE_MATCH_DIFF;
431 }
432
433 static gboolean
match_builtin(MetaInputMapper * mapper,MetaMonitor * monitor)434 match_builtin (MetaInputMapper *mapper,
435 MetaMonitor *monitor)
436 {
437 return monitor == meta_monitor_manager_get_laptop_panel (mapper->monitor_manager);
438 }
439
440 static gboolean
match_config(MetaMapperInputInfo * info,MetaMonitor * monitor)441 match_config (MetaMapperInputInfo *info,
442 MetaMonitor *monitor)
443 {
444 gboolean match = FALSE;
445 char **edid;
446 guint n_values;
447
448 edid = g_settings_get_strv (info->settings, "output");
449 n_values = g_strv_length (edid);
450
451 if (n_values != 3)
452 {
453 g_warning ("EDID configuration for device '%s' "
454 "is incorrect, must have 3 values",
455 clutter_input_device_get_device_name (info->device));
456 goto out;
457 }
458
459 if (!*edid[0] && !*edid[1] && !*edid[2])
460 goto out;
461
462 match = (g_strcmp0 (meta_monitor_get_vendor (monitor), edid[0]) == 0 &&
463 g_strcmp0 (meta_monitor_get_product (monitor), edid[1]) == 0 &&
464 g_strcmp0 (meta_monitor_get_serial (monitor), edid[2]) == 0);
465
466 out:
467 g_strfreev (edid);
468
469 return match;
470 }
471
472 static int
sort_by_score(DeviceMatch * match1,DeviceMatch * match2)473 sort_by_score (DeviceMatch *match1,
474 DeviceMatch *match2)
475 {
476 return (int) match2->score - match1->score;
477 }
478
479 static void
guess_candidates(MetaInputMapper * mapper,MetaMapperInputInfo * input,DeviceCandidates * info)480 guess_candidates (MetaInputMapper *mapper,
481 MetaMapperInputInfo *input,
482 DeviceCandidates *info)
483 {
484 GList *monitors, *l;
485 gboolean builtin = FALSE;
486 gboolean integrated = TRUE;
487
488 #ifdef HAVE_LIBWACOM
489 if (clutter_input_device_get_device_type (input->device) != CLUTTER_TOUCHSCREEN_DEVICE)
490 {
491 WacomDevice *wacom_device;
492 WacomIntegrationFlags flags = 0;
493
494 wacom_device =
495 meta_input_device_get_wacom_device (META_INPUT_DEVICE (input->device));
496
497 if (wacom_device)
498 {
499 flags = libwacom_get_integration_flags (wacom_device);
500
501 integrated = (flags & (WACOM_DEVICE_INTEGRATED_SYSTEM |
502 WACOM_DEVICE_INTEGRATED_DISPLAY)) != 0;
503 builtin = (flags & WACOM_DEVICE_INTEGRATED_SYSTEM) != 0;
504 }
505 }
506 #endif
507
508 monitors = meta_monitor_manager_get_monitors (mapper->monitor_manager);
509
510 for (l = monitors; l; l = l->next)
511 {
512 MetaOutputMatchType edid_match;
513 DeviceMatch match = { l->data, 0 };
514
515 g_assert (META_IS_MONITOR (l->data));
516
517 if (match_edid (input, l->data, &edid_match))
518 match.score |= 1 << edid_match;
519
520 if (integrated && match_size (input, l->data))
521 match.score |= 1 << META_MATCH_SIZE;
522
523 if (builtin && match_builtin (mapper, l->data))
524 match.score |= 1 << META_MATCH_IS_BUILTIN;
525
526 if (match_config (input, l->data))
527 match.score |= 1 << META_MATCH_CONFIG;
528
529 if (match.score > 0)
530 g_array_append_val (info->matches, match);
531 }
532
533 if (info->matches->len == 0)
534 {
535 DeviceMatch match = { 0 };
536
537 match.monitor =
538 meta_monitor_manager_get_laptop_panel (mapper->monitor_manager);
539
540 if (match.monitor != NULL)
541 g_array_append_val (info->matches, match);
542
543 info->best = 0;
544 }
545 else
546 {
547 DeviceMatch *best;
548
549 g_array_sort (info->matches, (GCompareFunc) sort_by_score);
550 best = &g_array_index (info->matches, DeviceMatch, 0);
551 info->best = best->score;
552 }
553 }
554
555 static void
mapping_helper_add(MappingHelper * helper,MetaMapperInputInfo * input,MetaInputMapper * mapper)556 mapping_helper_add (MappingHelper *helper,
557 MetaMapperInputInfo *input,
558 MetaInputMapper *mapper)
559 {
560 DeviceCandidates info = { 0, };
561 guint i, pos = 0;
562
563 info.input = input;
564 info.matches = g_array_new (FALSE, TRUE, sizeof (DeviceMatch));
565
566 guess_candidates (mapper, input, &info);
567
568 for (i = 0; i < helper->device_maps->len; i++)
569 {
570 DeviceCandidates *elem;
571
572 elem = &g_array_index (helper->device_maps, DeviceCandidates, i);
573
574 if (elem->best > info.best)
575 pos = i;
576 }
577
578 if (pos >= helper->device_maps->len)
579 g_array_append_val (helper->device_maps, info);
580 else
581 g_array_insert_val (helper->device_maps, pos, info);
582 }
583
584 static void
mapping_helper_apply(MappingHelper * helper,MetaInputMapper * mapper)585 mapping_helper_apply (MappingHelper *helper,
586 MetaInputMapper *mapper)
587 {
588 guint i, j;
589
590 /* Now, decide which input claims which output */
591 for (i = 0; i < helper->device_maps->len; i++)
592 {
593 DeviceCandidates *info;
594
595 info = &g_array_index (helper->device_maps, DeviceCandidates, i);
596 g_debug ("Applying mapping %d to input device '%s', capabilities %x", i,
597 clutter_input_device_get_device_name (info->input->device),
598 mapper_input_info_get_caps (info->input));
599
600 for (j = 0; j < info->matches->len; j++)
601 {
602 MetaLogicalMonitor *logical_monitor;
603 MetaMapperOutputInfo *output;
604 MetaMonitor *monitor;
605 DeviceMatch *match;
606
607 match = &g_array_index (info->matches, DeviceMatch, j);
608 g_debug ("Output candidate '%s', score %x",
609 meta_monitor_get_display_name (match->monitor),
610 match->score);
611
612 monitor = match->monitor;
613 logical_monitor = meta_monitor_get_logical_monitor (monitor);
614 output = g_hash_table_lookup (mapper->output_devices,
615 logical_monitor);
616
617 if (!output)
618 continue;
619
620 if (output->attached_caps & mapper_input_info_get_caps (info->input))
621 continue;
622
623 g_debug ("Matched input '%s' with output '%s'",
624 clutter_input_device_get_device_name (info->input->device),
625 meta_monitor_get_display_name (match->monitor));
626 mapper_output_info_add_input (output, info->input, monitor);
627 break;
628 }
629 }
630 }
631
632 static void
mapper_recalculate_candidates(MetaInputMapper * mapper)633 mapper_recalculate_candidates (MetaInputMapper *mapper)
634 {
635 MetaMapperInputInfo *input;
636 MappingHelper helper;
637 GHashTableIter iter;
638
639 mapping_helper_init (&helper);
640 g_hash_table_iter_init (&iter, mapper->input_devices);
641
642 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &input))
643 mapping_helper_add (&helper, input, mapper);
644
645 mapping_helper_apply (&helper, mapper);
646 mapping_helper_release (&helper);
647 }
648
649 static void
mapper_recalculate_input(MetaInputMapper * mapper,MetaMapperInputInfo * input)650 mapper_recalculate_input (MetaInputMapper *mapper,
651 MetaMapperInputInfo *input)
652 {
653 MappingHelper helper;
654
655 mapping_helper_init (&helper);
656 mapping_helper_add (&helper, input, mapper);
657 mapping_helper_apply (&helper, mapper);
658 mapping_helper_release (&helper);
659 }
660
661 static void
mapper_update_outputs(MetaInputMapper * mapper)662 mapper_update_outputs (MetaInputMapper *mapper)
663 {
664 MetaMapperOutputInfo *output;
665 GList *logical_monitors, *l;
666 GHashTableIter iter;
667
668 g_hash_table_iter_init (&iter, mapper->output_devices);
669
670 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &output))
671 {
672 mapper_output_info_clear_inputs (output);
673 g_hash_table_iter_remove (&iter);
674 }
675
676 logical_monitors =
677 meta_monitor_manager_get_logical_monitors (mapper->monitor_manager);
678
679 for (l = logical_monitors; l; l = l->next)
680 {
681 MetaLogicalMonitor *logical_monitor = l->data;
682 MetaMapperOutputInfo *info;
683
684 info = mapper_output_info_new (logical_monitor);
685 g_hash_table_insert (mapper->output_devices, logical_monitor, info);
686 }
687
688 mapper_recalculate_candidates (mapper);
689 }
690
691 static void
input_mapper_monitors_changed_cb(MetaMonitorManager * monitor_manager,MetaInputMapper * mapper)692 input_mapper_monitors_changed_cb (MetaMonitorManager *monitor_manager,
693 MetaInputMapper *mapper)
694 {
695 mapper_update_outputs (mapper);
696 }
697
698 static void
input_mapper_power_save_mode_changed_cb(MetaMonitorManager * monitor_manager,MetaInputMapper * mapper)699 input_mapper_power_save_mode_changed_cb (MetaMonitorManager *monitor_manager,
700 MetaInputMapper *mapper)
701 {
702 ClutterInputDevice *device;
703 MetaLogicalMonitor *logical_monitor;
704 MetaMonitor *builtin;
705 MetaPowerSave power_save_mode;
706 gboolean on;
707
708 power_save_mode =
709 meta_monitor_manager_get_power_save_mode (mapper->monitor_manager);
710 on = power_save_mode == META_POWER_SAVE_ON;
711
712 builtin = meta_monitor_manager_get_laptop_panel (monitor_manager);
713 if (!builtin)
714 return;
715
716 logical_monitor = meta_monitor_get_logical_monitor (builtin);
717 if (!logical_monitor)
718 return;
719
720 device =
721 meta_input_mapper_get_logical_monitor_device (mapper,
722 logical_monitor,
723 CLUTTER_TOUCHSCREEN_DEVICE);
724 if (!device)
725 return;
726
727 g_signal_emit (mapper, signals[DEVICE_ENABLED], 0, device, on);
728 }
729
730 static void
input_mapper_device_removed_cb(ClutterSeat * seat,ClutterInputDevice * device,MetaInputMapper * mapper)731 input_mapper_device_removed_cb (ClutterSeat *seat,
732 ClutterInputDevice *device,
733 MetaInputMapper *mapper)
734 {
735 meta_input_mapper_remove_device (mapper, device);
736 }
737
738 static void
meta_input_mapper_finalize(GObject * object)739 meta_input_mapper_finalize (GObject *object)
740 {
741 MetaInputMapper *mapper = META_INPUT_MAPPER (object);
742
743 g_signal_handlers_disconnect_by_func (mapper->monitor_manager,
744 input_mapper_monitors_changed_cb,
745 mapper);
746 g_signal_handlers_disconnect_by_func (mapper->seat,
747 input_mapper_device_removed_cb,
748 mapper);
749
750 g_hash_table_unref (mapper->input_devices);
751 g_hash_table_unref (mapper->output_devices);
752 #ifdef HAVE_LIBGUDEV
753 g_clear_object (&mapper->udev_client);
754 #endif
755
756 G_OBJECT_CLASS (meta_input_mapper_parent_class)->finalize (object);
757 }
758
759 static void
meta_input_mapper_constructed(GObject * object)760 meta_input_mapper_constructed (GObject *object)
761 {
762 #ifdef HAVE_LIBGUDEV
763 const char *udev_subsystems[] = { "input", NULL };
764 #endif
765 MetaInputMapper *mapper = META_INPUT_MAPPER (object);
766 MetaBackend *backend;
767
768 G_OBJECT_CLASS (meta_input_mapper_parent_class)->constructed (object);
769
770 #ifdef HAVE_LIBGUDEV
771 mapper->udev_client = g_udev_client_new (udev_subsystems);
772 #endif
773
774 mapper->seat = clutter_backend_get_default_seat (clutter_get_default_backend ());
775 g_signal_connect (mapper->seat, "device-removed",
776 G_CALLBACK (input_mapper_device_removed_cb), mapper);
777
778 backend = meta_get_backend ();
779 mapper->monitor_manager = meta_backend_get_monitor_manager (backend);
780 g_signal_connect (mapper->monitor_manager, "monitors-changed-internal",
781 G_CALLBACK (input_mapper_monitors_changed_cb), mapper);
782 g_signal_connect (mapper->monitor_manager, "power-save-mode-changed",
783 G_CALLBACK (input_mapper_power_save_mode_changed_cb),
784 mapper);
785
786 mapper_update_outputs (mapper);
787 }
788
789 static void
meta_input_mapper_class_init(MetaInputMapperClass * klass)790 meta_input_mapper_class_init (MetaInputMapperClass *klass)
791 {
792 GObjectClass *object_class = G_OBJECT_CLASS (klass);
793
794 object_class->constructed = meta_input_mapper_constructed;
795 object_class->finalize = meta_input_mapper_finalize;
796
797 signals[DEVICE_MAPPED] =
798 g_signal_new ("device-mapped",
799 G_TYPE_FROM_CLASS (object_class),
800 G_SIGNAL_RUN_LAST,
801 0,
802 NULL, NULL, NULL,
803 G_TYPE_NONE, 2,
804 CLUTTER_TYPE_INPUT_DEVICE,
805 G_TYPE_POINTER);
806 signals[DEVICE_ENABLED] =
807 g_signal_new ("device-enabled",
808 G_TYPE_FROM_CLASS (object_class),
809 G_SIGNAL_RUN_LAST,
810 0,
811 NULL, NULL, NULL,
812 G_TYPE_NONE, 2,
813 CLUTTER_TYPE_INPUT_DEVICE,
814 G_TYPE_BOOLEAN);
815 signals[DEVICE_ASPECT_RATIO] =
816 g_signal_new ("device-aspect-ratio",
817 G_TYPE_FROM_CLASS (object_class),
818 G_SIGNAL_RUN_LAST,
819 0,
820 NULL, NULL, NULL,
821 G_TYPE_NONE, 2,
822 CLUTTER_TYPE_INPUT_DEVICE,
823 G_TYPE_DOUBLE);
824 }
825
826 static void
meta_input_mapper_init(MetaInputMapper * mapper)827 meta_input_mapper_init (MetaInputMapper *mapper)
828 {
829 mapper->input_devices =
830 g_hash_table_new_full (NULL, NULL, NULL,
831 (GDestroyNotify) mapper_input_info_free);
832 mapper->output_devices =
833 g_hash_table_new_full (NULL, NULL, NULL,
834 (GDestroyNotify) mapper_output_info_free);
835 }
836
837 MetaInputMapper *
meta_input_mapper_new(void)838 meta_input_mapper_new (void)
839 {
840 return g_object_new (META_TYPE_INPUT_MAPPER, NULL);
841 }
842
843 void
meta_input_mapper_add_device(MetaInputMapper * mapper,ClutterInputDevice * device)844 meta_input_mapper_add_device (MetaInputMapper *mapper,
845 ClutterInputDevice *device)
846 {
847 MetaMapperInputInfo *info;
848
849 g_return_if_fail (mapper != NULL);
850 g_return_if_fail (device != NULL);
851
852 if (g_hash_table_contains (mapper->input_devices, device))
853 return;
854
855 info = mapper_input_info_new (device, mapper);
856 g_hash_table_insert (mapper->input_devices, device, info);
857 mapper_recalculate_input (mapper, info);
858 }
859
860 void
meta_input_mapper_remove_device(MetaInputMapper * mapper,ClutterInputDevice * device)861 meta_input_mapper_remove_device (MetaInputMapper *mapper,
862 ClutterInputDevice *device)
863 {
864 MetaMapperInputInfo *input;
865
866 g_return_if_fail (mapper != NULL);
867 g_return_if_fail (device != NULL);
868
869 input = g_hash_table_lookup (mapper->input_devices, device);
870
871 if (input)
872 {
873 if (input->output)
874 mapper_output_info_remove_input (input->output, input);
875 g_hash_table_remove (mapper->input_devices, device);
876 }
877 }
878
879 ClutterInputDevice *
meta_input_mapper_get_logical_monitor_device(MetaInputMapper * mapper,MetaLogicalMonitor * logical_monitor,ClutterInputDeviceType device_type)880 meta_input_mapper_get_logical_monitor_device (MetaInputMapper *mapper,
881 MetaLogicalMonitor *logical_monitor,
882 ClutterInputDeviceType device_type)
883 {
884 MetaMapperOutputInfo *output;
885 GList *l;
886
887 output = g_hash_table_lookup (mapper->output_devices, logical_monitor);
888 if (!output)
889 return NULL;
890
891 for (l = output->input_devices; l; l = l->next)
892 {
893 MetaMapperInputInfo *input = l->data;
894
895 if (clutter_input_device_get_device_type (input->device) == device_type)
896 return input->device;
897 }
898
899 return NULL;
900 }
901
902 static ClutterInputDevice *
find_grouped_pen(ClutterInputDevice * device)903 find_grouped_pen (ClutterInputDevice *device)
904 {
905 GList *l, *devices;
906 ClutterInputDeviceType device_type;
907 ClutterInputDevice *pen = NULL;
908 ClutterSeat *seat;
909
910 device_type = clutter_input_device_get_device_type (device);
911
912 if (device_type == CLUTTER_TABLET_DEVICE ||
913 device_type == CLUTTER_PEN_DEVICE)
914 return device;
915
916 seat = clutter_input_device_get_seat (device);
917 devices = clutter_seat_list_devices (seat);
918
919 for (l = devices; l; l = l->next)
920 {
921 ClutterInputDevice *other_device = l->data;
922
923 device_type = clutter_input_device_get_device_type (other_device);
924
925 if ((device_type == CLUTTER_TABLET_DEVICE ||
926 device_type == CLUTTER_PEN_DEVICE) &&
927 clutter_input_device_is_grouped (device, other_device))
928 {
929 pen = other_device;
930 break;
931 }
932 }
933
934 g_list_free (devices);
935
936 return pen;
937 }
938
939 MetaLogicalMonitor *
meta_input_mapper_get_device_logical_monitor(MetaInputMapper * mapper,ClutterInputDevice * device)940 meta_input_mapper_get_device_logical_monitor (MetaInputMapper *mapper,
941 ClutterInputDevice *device)
942 {
943 MetaMapperOutputInfo *output;
944 MetaLogicalMonitor *logical_monitor;
945 GHashTableIter iter;
946 GList *l;
947
948 if (clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE)
949 {
950 device = find_grouped_pen (device);
951 if (!device)
952 return NULL;
953 }
954
955 g_hash_table_iter_init (&iter, mapper->output_devices);
956
957 while (g_hash_table_iter_next (&iter, (gpointer *) &logical_monitor,
958 (gpointer *) &output))
959 {
960 for (l = output->input_devices; l; l = l->next)
961 {
962 MetaMapperInputInfo *input = l->data;
963
964 if (input->device == device)
965 return logical_monitor;
966 }
967 }
968
969 return NULL;
970 }
971
972 GSettings *
meta_input_mapper_get_tablet_settings(MetaInputMapper * mapper,ClutterInputDevice * device)973 meta_input_mapper_get_tablet_settings (MetaInputMapper *mapper,
974 ClutterInputDevice *device)
975 {
976 MetaMapperInputInfo *input;
977
978 g_return_val_if_fail (META_IS_INPUT_MAPPER (mapper), NULL);
979 g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL);
980
981 input = g_hash_table_lookup (mapper->input_devices, device);
982 if (!input)
983 return NULL;
984
985 return input->settings;
986 }
987