GCC Code Coverage Report


Directory: ./
File: panels/mouse/cc-mouse-panel.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 212 0.0%
Functions: 0 25 0.0%
Branches: 0 49 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (C) 2010 Intel, Inc
3 * Copyright (C) 2012 Red Hat, Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU 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 * Authors: Thomas Wood <thomas.wood@intel.com>
19 * Rodrigo Moya <rodrigo@gnome.org>
20 * Ondrej Holy <oholy@redhat.com>
21 *
22 */
23
24 #include <gdesktop-enums.h>
25 #include <gtk/gtk.h>
26
27 #include "cc-illustrated-row.h"
28 #include "cc-split-row.h"
29 #include "cc-list-row-info-button.h"
30
31 #include "cc-mouse-caps-helper.h"
32 #include "cc-mouse-panel.h"
33 #include "cc-mouse-resources.h"
34 #include "cc-mouse-test.h"
35 #include "gsd-device-manager.h"
36 #include "gsd-input-helper.h"
37
38 struct _CcMousePanel
39 {
40 CcPanel parent_instance;
41
42 GtkSwitch *mouse_accel_switch;
43 AdwPreferencesGroup *mouse_group;
44 CcSplitRow *mouse_scroll_direction_row;
45 GtkScale *mouse_speed_scale;
46 GtkWindow *mouse_test;
47 GtkBox *primary_button_box;
48 GtkToggleButton *primary_button_left;
49 GtkToggleButton *primary_button_right;
50 AdwPreferencesPage*preferences;
51 CcSplitRow *two_finger_push_row;
52 GtkStack *title_stack;
53 CcIllustratedRow *tap_to_click_row;
54 GtkSwitch *tap_to_click_switch;
55 AdwPreferencesGroup *touchpad_group;
56 AdwViewStackPage *touchpad_stack_page;
57 CcSplitRow *touchpad_scroll_direction_row;
58 CcSplitRow *touchpad_scroll_method_row;
59 GtkListBoxRow *touchpad_speed_row;
60 GtkScale *touchpad_speed_scale;
61 AdwSwitchRow *touchpad_toggle_row;
62 AdwSwitchRow *touchpad_typing_row;
63
64 GSettings *mouse_settings;
65 GSettings *touchpad_settings;
66
67 gboolean have_mouse;
68 gboolean have_touchpad;
69 gboolean have_touchscreen;
70 gboolean have_synaptics;
71
72 gboolean left_handed;
73 GtkGesture *left_gesture;
74 GtkGesture *right_gesture;
75 };
76
77 CC_PANEL_REGISTER (CcMousePanel, cc_mouse_panel)
78
79 #define ASSET_RESOURCES_PREFIX "/org/gnome/control-center/mouse/assets/"
80
81 static void
82 setup_illustrations (CcMousePanel *self)
83 {
84 AdwStyleManager *style_manager = adw_style_manager_get_default ();
85 gboolean use_dark = adw_style_manager_get_dark (style_manager);
86 struct {
87 CcSplitRow *row;
88 const gchar *default_resource;
89 const gchar *alternative_resource;
90 } row_resources[] = {
91 { self->mouse_scroll_direction_row, "scroll-traditional", "scroll-natural" },
92 { self->two_finger_push_row, "push-to-click-anywhere", "push-areas" },
93 { self->touchpad_scroll_method_row, "scroll-2finger", "edge-scroll" },
94 { self->touchpad_scroll_direction_row, "touch-scroll-traditional", "touch-scroll-natural" },
95 };
96
97 for (gsize i = 0; i < G_N_ELEMENTS (row_resources); i++)
98 {
99 g_autofree gchar *alternative_resource = NULL;
100 g_autofree gchar *default_resource = NULL;
101 const gchar *style_suffix;
102
103 style_suffix = use_dark ? "d" : "l";
104 default_resource = g_strdup_printf (ASSET_RESOURCES_PREFIX "%s-%s.webm",
105 row_resources[i].default_resource,
106 style_suffix);
107 alternative_resource = g_strdup_printf (ASSET_RESOURCES_PREFIX "%s-%s.webm",
108 row_resources[i].alternative_resource,
109 style_suffix);
110
111 cc_split_row_set_default_illustration_resource (row_resources[i].row, default_resource);
112 cc_split_row_set_alternative_illustration_resource (row_resources[i].row, alternative_resource);
113 }
114
115 /* Tap to click */
116 {
117 g_autofree gchar *resource = NULL;
118
119 resource = g_strdup_printf (ASSET_RESOURCES_PREFIX "%s-%s.webm",
120 "tap-to-click",
121 use_dark ? "d" : "l");
122
123 cc_illustrated_row_set_resource (self->tap_to_click_row, resource);
124 }
125 }
126
127 static void
128 setup_touchpad_options (CcMousePanel *self)
129 {
130 gboolean have_two_finger_scrolling;
131 gboolean have_edge_scrolling;
132 gboolean have_tap_to_click;
133
134 if (self->have_synaptics || !self->have_touchpad) {
135 adw_view_stack_page_set_visible (self->touchpad_stack_page, FALSE);
136 gtk_stack_set_visible_child_name (self->title_stack, "title");
137 return;
138 }
139
140 cc_touchpad_check_capabilities (&have_two_finger_scrolling, &have_edge_scrolling, &have_tap_to_click);
141
142 adw_view_stack_page_set_visible (self->touchpad_stack_page, TRUE);
143 gtk_stack_set_visible_child_name (self->title_stack, "switcher");
144
145 gtk_widget_set_visible (GTK_WIDGET (self->touchpad_scroll_method_row), have_two_finger_scrolling);
146 gtk_widget_set_visible (GTK_WIDGET (self->tap_to_click_row), have_tap_to_click);
147 }
148
149 static void
150 on_touchpad_scroll_method_changed_cb (CcMousePanel *self)
151 {
152 gboolean two_finger;
153
154 two_finger = cc_split_row_get_use_default (self->touchpad_scroll_method_row);
155
156 if (g_settings_get_boolean (self->touchpad_settings,
157 "two-finger-scrolling-enabled") == two_finger)
158 return;
159
160 g_settings_set_boolean (self->touchpad_settings, "two-finger-scrolling-enabled", two_finger);
161 }
162
163 static gboolean
164 get_touchpad_enabled (GSettings *settings)
165 {
166 GDesktopDeviceSendEvents send_events;
167
168 send_events = g_settings_get_enum (settings, "send-events");
169
170 return send_events == G_DESKTOP_DEVICE_SEND_EVENTS_ENABLED;
171 }
172
173 static gboolean
174 can_disable_touchpad (CcMousePanel *self)
175 {
176 if (!self->have_touchpad)
177 return FALSE;
178
179 g_debug ("Should we show the row to enable touchpad: have_mouse: %s have_touchscreen: %s\n",
180 self->have_mouse ? "true" : "false",
181 self->have_touchscreen ? "true" : "false");
182
183 /* Let's show the button when a mouse or touchscreen is present */
184 if (self->have_mouse || self->have_touchscreen)
185 return TRUE;
186
187 /* Let's also show when the touchpad is disabled. */
188 if (!get_touchpad_enabled (self->touchpad_settings))
189 return TRUE;
190
191 return FALSE;
192 }
193
194 static gboolean
195 touchpad_enabled_get_mapping (GValue *value,
196 GVariant *variant,
197 gpointer user_data)
198 {
199 gboolean enabled;
200
201 enabled = g_strcmp0 (g_variant_get_string (variant, NULL), "enabled") == 0;
202 g_value_set_boolean (value, enabled);
203
204 return TRUE;
205 }
206
207 static GVariant *
208 touchpad_enabled_set_mapping (const GValue *value,
209 const GVariantType *type,
210 gpointer user_data)
211 {
212 gboolean enabled;
213
214 enabled = g_value_get_boolean (value);
215
216 return g_variant_new_string (enabled ? "enabled" : "disabled");
217 }
218
219 static gboolean
220 click_method_get_mapping (GValue *value,
221 GVariant *variant,
222 gpointer user_data)
223 {
224 gboolean is_default;
225
226 is_default = g_strcmp0 (g_variant_get_string (variant, NULL), "fingers") == 0;
227 g_value_set_boolean (value, is_default);
228
229 return TRUE;
230 }
231
232 static GVariant *
233 click_method_set_mapping (const GValue *value,
234 const GVariantType *type,
235 gpointer user_data)
236 {
237 gboolean is_default;
238
239 is_default = g_value_get_boolean (value);
240
241 return g_variant_new_string (is_default ? "fingers" : "areas");
242 }
243
244 static void
245 pressed_cb (GtkButton *button)
246 {
247 g_signal_emit_by_name (button, "activate");
248 }
249
250 static void
251 handle_secondary_button (CcMousePanel *self,
252 GtkToggleButton *button,
253 GtkGesture *gesture)
254 {
255 gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), FALSE);
256 gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (gesture), TRUE);
257 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY);
258 g_signal_connect_swapped (gesture, "pressed", G_CALLBACK (pressed_cb), button);
259 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), GTK_PHASE_BUBBLE);
260 gtk_widget_add_controller (GTK_WIDGET (button), GTK_EVENT_CONTROLLER (gesture));
261 }
262
263 static gboolean
264 mouse_accel_get_mapping (GValue *value,
265 GVariant *variant,
266 gpointer user_data)
267 {
268 gboolean enabled;
269
270 enabled = g_strcmp0 (g_variant_get_string (variant, NULL), "flat") != 0;
271 g_value_set_boolean (value, enabled);
272
273 return TRUE;
274 }
275
276 static GVariant *
277 mouse_accel_set_mapping (const GValue *value,
278 const GVariantType *type,
279 gpointer user_data)
280 {
281 return g_variant_new_string (g_value_get_boolean (value) ? "default" : "flat");
282 }
283
284 static void
285 update_primary_mouse_button_order (CcMousePanel *self)
286 {
287 /* Manually reorder Left/Right buttons to preserve direction in RTL instead
288 * of calling gtk_widget_set_direction (self->primary_button_box, GTK_TEXT_DIR_LTR)
289 * which won't preserve the correct behavior of the CSS "linked" style class.
290 * See https://gitlab.gnome.org/GNOME/gnome-control-center/-/issues/1101
291 * and https://gitlab.gnome.org/GNOME/gnome-control-center/-/issues/2649 */
292 if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL) {
293 gtk_box_reorder_child_after (self->primary_button_box,
294 GTK_WIDGET (self->primary_button_left),
295 GTK_WIDGET (self->primary_button_right));
296 } else {
297 gtk_box_reorder_child_after (self->primary_button_box,
298 GTK_WIDGET (self->primary_button_right),
299 GTK_WIDGET (self->primary_button_left));
300 }
301 }
302
303 /* Set up the property editors in the dialog. */
304 static void
305 setup_dialog (CcMousePanel *self)
306 {
307 GtkToggleButton *button;
308
309 update_primary_mouse_button_order (self);
310 self->mouse_test = GTK_WINDOW (cc_mouse_test_new ());
311
312 self->left_handed = g_settings_get_boolean (self->mouse_settings, "left-handed");
313 button = self->left_handed ? self->primary_button_right : self->primary_button_left;
314 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
315
316 g_settings_bind (self->mouse_settings, "left-handed",
317 self->primary_button_left, "active",
318 G_SETTINGS_BIND_DEFAULT | G_SETTINGS_BIND_INVERT_BOOLEAN);
319 g_settings_bind (self->mouse_settings, "left-handed",
320 self->primary_button_right, "active",
321 G_SETTINGS_BIND_DEFAULT);
322
323 /* Allow changing orientation with either button */
324 button = self->primary_button_right;
325 self->right_gesture = gtk_gesture_click_new ();
326 handle_secondary_button (self, button, self->right_gesture);
327 button = self->primary_button_left;
328 self->left_gesture = gtk_gesture_click_new ();
329 handle_secondary_button (self, button, self->left_gesture);
330
331 g_settings_bind (self->mouse_settings, "natural-scroll",
332 self->mouse_scroll_direction_row, "use-default",
333 G_SETTINGS_BIND_INVERT_BOOLEAN);
334
335 /* Mouse section */
336 gtk_widget_set_visible (GTK_WIDGET (self->mouse_group), self->have_mouse);
337
338 g_settings_bind (self->mouse_settings, "speed",
339 gtk_range_get_adjustment (GTK_RANGE (self->mouse_speed_scale)), "value",
340 G_SETTINGS_BIND_DEFAULT);
341
342 g_settings_bind_with_mapping (self->mouse_settings, "accel-profile",
343 self->mouse_accel_switch, "active",
344 G_SETTINGS_BIND_DEFAULT,
345 mouse_accel_get_mapping,
346 mouse_accel_set_mapping,
347 NULL, NULL);
348
349 /* Touchpad section */
350 gtk_widget_set_visible (GTK_WIDGET (self->touchpad_toggle_row), can_disable_touchpad (self));
351
352 g_settings_bind_with_mapping (self->touchpad_settings, "send-events",
353 self->touchpad_toggle_row, "active",
354 G_SETTINGS_BIND_DEFAULT,
355 touchpad_enabled_get_mapping,
356 touchpad_enabled_set_mapping,
357 NULL, NULL);
358
359 g_settings_bind_with_mapping (self->touchpad_settings, "send-events",
360 self->touchpad_typing_row, "sensitive",
361 G_SETTINGS_BIND_GET,
362 touchpad_enabled_get_mapping,
363 touchpad_enabled_set_mapping,
364 NULL, NULL);
365
366 g_settings_bind (self->touchpad_settings, "natural-scroll",
367 self->touchpad_scroll_direction_row, "use-default",
368 G_SETTINGS_BIND_INVERT_BOOLEAN |
369 G_SETTINGS_BIND_NO_SENSITIVITY);
370
371 g_settings_bind (self->touchpad_settings, "speed",
372 gtk_range_get_adjustment (GTK_RANGE (self->touchpad_speed_scale)), "value",
373 G_SETTINGS_BIND_DEFAULT |
374 G_SETTINGS_BIND_NO_SENSITIVITY);
375
376 g_settings_bind (self->touchpad_settings, "tap-to-click",
377 self->tap_to_click_switch, "active",
378 G_SETTINGS_BIND_DEFAULT |
379 G_SETTINGS_BIND_NO_SENSITIVITY);
380
381 g_settings_bind_with_mapping (self->touchpad_settings, "click-method",
382 self->two_finger_push_row, "use-default",
383 G_SETTINGS_BIND_DEFAULT,
384 click_method_get_mapping,
385 click_method_set_mapping,
386 NULL, NULL);
387
388 g_settings_bind (self->touchpad_settings, "two-finger-scrolling-enabled",
389 self->touchpad_scroll_method_row, "use-default",
390 G_SETTINGS_BIND_DEFAULT |
391 G_SETTINGS_BIND_NO_SENSITIVITY);
392
393 g_settings_bind (self->touchpad_settings, "edge-scrolling-enabled",
394 self->touchpad_scroll_method_row, "use-default",
395 G_SETTINGS_BIND_INVERT_BOOLEAN |
396 G_SETTINGS_BIND_NO_SENSITIVITY);
397
398 g_settings_bind (self->touchpad_settings, "disable-while-typing",
399 self->touchpad_typing_row, "active",
400 G_SETTINGS_BIND_DEFAULT);
401
402 setup_touchpad_options (self);
403
404 g_signal_connect_object (adw_style_manager_get_default (),
405 "notify::dark",
406 G_CALLBACK (setup_illustrations),
407 self,
408 G_CONNECT_SWAPPED);
409 setup_illustrations (self);
410 }
411
412 /* Callback issued when a button is clicked on the dialog */
413 static void
414 device_changed (CcMousePanel *self)
415 {
416 self->have_touchpad = touchpad_is_present ();
417
418 setup_touchpad_options (self);
419
420 self->have_mouse = mouse_is_present ();
421 gtk_widget_set_visible (GTK_WIDGET (self->mouse_group), self->have_mouse);
422 gtk_widget_set_visible (GTK_WIDGET (self->touchpad_toggle_row), can_disable_touchpad (self));
423 }
424
425 static void
426 cc_mouse_panel_direction_changed (GtkWidget *widget,
427 GtkTextDirection previous_direction)
428 {
429 CcMousePanel *self = CC_MOUSE_PANEL (widget);
430
431 update_primary_mouse_button_order (self);
432
433 GTK_WIDGET_CLASS (cc_mouse_panel_parent_class)->direction_changed (widget, previous_direction);
434 }
435
436 static void
437 cc_mouse_panel_dispose (GObject *object)
438 {
439 CcMousePanel *self = CC_MOUSE_PANEL (object);
440
441 g_clear_object (&self->mouse_settings);
442 g_clear_object (&self->touchpad_settings);
443 g_clear_pointer (&self->mouse_test, gtk_window_destroy);
444
445 G_OBJECT_CLASS (cc_mouse_panel_parent_class)->dispose (object);
446 }
447
448 static const char *
449 cc_mouse_panel_get_help_uri (CcPanel *panel)
450 {
451 return "help:gnome-help/mouse";
452 }
453
454 static void
455 test_button_clicked_cb (CcMousePanel *self)
456 {
457 CcShell *shell = cc_panel_get_shell (CC_PANEL (self));
458
459 gtk_window_set_transient_for (self->mouse_test,
460 GTK_WINDOW (cc_shell_get_toplevel (shell)));
461 gtk_window_present (self->mouse_test);
462 }
463
464 static void
465 cc_mouse_panel_init (CcMousePanel *self)
466 {
467 GsdDeviceManager *device_manager;
468
469 g_resources_register (cc_mouse_get_resource ());
470
471 cc_mouse_test_get_type ();
472 gtk_widget_init_template (GTK_WIDGET (self));
473
474 self->mouse_settings = g_settings_new ("org.gnome.desktop.peripherals.mouse");
475 self->touchpad_settings = g_settings_new ("org.gnome.desktop.peripherals.touchpad");
476
477 device_manager = gsd_device_manager_get ();
478 g_signal_connect_object (device_manager, "device-added",
479 G_CALLBACK (device_changed), self, G_CONNECT_SWAPPED);
480 g_signal_connect_object (device_manager, "device-removed",
481 G_CALLBACK (device_changed), self, G_CONNECT_SWAPPED);
482
483 self->have_mouse = mouse_is_present ();
484 self->have_touchpad = touchpad_is_present ();
485 self->have_touchscreen = touchscreen_is_present ();
486 self->have_synaptics = cc_synaptics_check ();
487 if (self->have_synaptics)
488 g_warning ("Detected synaptics X driver, please migrate to libinput");
489
490 setup_dialog (self);
491 }
492
493 static void
494 cc_mouse_panel_class_init (CcMousePanelClass *klass)
495 {
496 GObjectClass *object_class = G_OBJECT_CLASS (klass);
497 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
498 CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
499
500 panel_class->get_help_uri = cc_mouse_panel_get_help_uri;
501
502 object_class->dispose = cc_mouse_panel_dispose;
503 widget_class->direction_changed = cc_mouse_panel_direction_changed;
504
505 g_type_ensure (CC_TYPE_ILLUSTRATED_ROW);
506 g_type_ensure (CC_TYPE_SPLIT_ROW);
507 g_type_ensure (CC_TYPE_LIST_ROW_INFO_BUTTON);
508
509 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/mouse/cc-mouse-panel.ui");
510
511 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, mouse_accel_switch);
512 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, mouse_group);
513 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, mouse_scroll_direction_row);
514 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, mouse_speed_scale);
515 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, primary_button_box);
516 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, primary_button_left);
517 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, primary_button_right);
518 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, preferences);
519 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, title_stack);
520 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, tap_to_click_row);
521 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, tap_to_click_switch);
522 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, touchpad_group);
523 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, touchpad_scroll_direction_row);
524 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, touchpad_scroll_method_row);
525 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, touchpad_speed_row);
526 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, touchpad_stack_page);
527 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, touchpad_speed_scale);
528 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, touchpad_toggle_row);
529 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, touchpad_typing_row);
530 gtk_widget_class_bind_template_child (widget_class, CcMousePanel, two_finger_push_row);
531
532 gtk_widget_class_bind_template_callback (widget_class, on_touchpad_scroll_method_changed_cb);
533 gtk_widget_class_bind_template_callback (widget_class, test_button_clicked_cb);
534 }
535