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