summaryrefslogtreecommitdiffstats
path: root/panels/mouse
diff options
context:
space:
mode:
Diffstat (limited to 'panels/mouse')
-rw-r--r--panels/mouse/cc-mouse-caps-helper.c148
-rw-r--r--panels/mouse/cc-mouse-caps-helper.h33
-rw-r--r--panels/mouse/cc-mouse-panel.c414
-rw-r--r--panels/mouse/cc-mouse-panel.h30
-rw-r--r--panels/mouse/cc-mouse-panel.ui219
-rw-r--r--panels/mouse/cc-mouse-test.c389
-rw-r--r--panels/mouse/cc-mouse-test.h32
-rw-r--r--panels/mouse/cc-mouse-test.ui69
-rw-r--r--panels/mouse/gnome-mouse-panel.desktop.in.in18
-rw-r--r--panels/mouse/icons/meson.build4
-rw-r--r--panels/mouse/icons/scalable/org.gnome.Settings-mouse-symbolic.svg4
-rw-r--r--panels/mouse/meson.build70
-rw-r--r--panels/mouse/mouse.gresource.xml9
-rw-r--r--panels/mouse/scroll-test-gegl.svg1
-rw-r--r--panels/mouse/scroll-test.svg1
-rw-r--r--panels/mouse/test-gnome-mouse-test.c24
16 files changed, 1465 insertions, 0 deletions
diff --git a/panels/mouse/cc-mouse-caps-helper.c b/panels/mouse/cc-mouse-caps-helper.c
new file mode 100644
index 0000000..6658f41
--- /dev/null
+++ b/panels/mouse/cc-mouse-caps-helper.c
@@ -0,0 +1,148 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2015 Red Hat, Inc,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Felipe Borges <feborges@redhat.com>
+ */
+
+#include <gdk/x11/gdkx.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/XInput2.h>
+
+#include "cc-mouse-caps-helper.h"
+
+static gboolean
+touchpad_check_capabilities_x11 (gboolean *have_two_finger_scrolling,
+ gboolean *have_edge_scrolling,
+ gboolean *have_tap_to_click)
+{
+ GdkDisplay *gdisplay;
+ Display *display;
+ g_autoptr(GList) devicelist = NULL;
+ GList *l;
+ Atom realtype, prop_scroll_methods, prop_tapping_enabled;
+ int realformat;
+ unsigned long nitems, bytes_after;
+ unsigned char *data;
+
+ gdisplay = gdk_display_get_default ();
+ display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+ prop_scroll_methods = XInternAtom (display, "libinput Scroll Methods Available", False);
+ prop_tapping_enabled = XInternAtom (display, "libinput Tapping Enabled", False);
+ if (!prop_scroll_methods || !prop_tapping_enabled)
+ return FALSE;
+
+ *have_two_finger_scrolling = FALSE;
+ *have_edge_scrolling = FALSE;
+ *have_tap_to_click = FALSE;
+
+ gdk_x11_display_error_trap_push (gdisplay);
+
+ devicelist = gdk_seat_get_devices (gdk_display_get_default_seat (gdk_display_get_default ()),
+ GDK_SEAT_CAPABILITY_ALL_POINTING);
+ for (l = devicelist; l != NULL; l = l->next) {
+ GdkDevice *device = l->data;
+ if (gdk_device_get_source (device) != GDK_SOURCE_TOUCHPAD)
+ continue;
+
+ /* xorg-x11-drv-libinput */
+ if ((XIGetProperty (display, gdk_x11_device_get_id (device), prop_scroll_methods,
+ 0, 2, False, XA_INTEGER, &realtype, &realformat, &nitems,
+ &bytes_after, &data) == Success) && (realtype != None)) {
+ /* Property data is booleans for two-finger, edge, on-button scroll available. */
+
+ if (data[0])
+ *have_two_finger_scrolling = TRUE;
+
+ if (data[1])
+ *have_edge_scrolling = TRUE;
+
+ XFree (data);
+ }
+
+ if ((XIGetProperty (display, gdk_x11_device_get_id (device), prop_tapping_enabled,
+ 0, 1, False, XA_INTEGER, &realtype, &realformat, &nitems,
+ &bytes_after, &data) == Success) && (realtype != None)) {
+ /* Property data is boolean for tapping enabled. */
+ *have_tap_to_click = TRUE;
+
+ XFree (data);
+ }
+ }
+
+ gdk_x11_display_error_trap_pop_ignored (gdisplay);
+
+ return TRUE;
+}
+
+gboolean
+cc_touchpad_check_capabilities (gboolean *have_two_finger_scrolling,
+ gboolean *have_edge_scrolling,
+ gboolean *have_tap_to_click)
+{
+ if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
+ return touchpad_check_capabilities_x11 (have_two_finger_scrolling,
+ have_edge_scrolling,
+ have_tap_to_click);
+ /* else we unconditionally show all touchpad knobs */
+ *have_two_finger_scrolling = TRUE;
+ *have_edge_scrolling = TRUE;
+ *have_tap_to_click = TRUE;
+ return FALSE;
+}
+
+gboolean
+cc_synaptics_check (void)
+{
+ GdkDisplay *gdisplay;
+ Display *display;
+ g_autoptr(GList) devicelist = NULL;
+ GList *l;
+ Atom prop, realtype;
+ int realformat;
+ unsigned long nitems, bytes_after;
+ unsigned char *data;
+ gboolean have_synaptics = FALSE;
+
+ if (!GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
+ return FALSE;
+
+ gdisplay = gdk_display_get_default ();
+ display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+ prop = XInternAtom (display, "Synaptics Capabilities", False);
+
+ gdk_x11_display_error_trap_push (gdisplay);
+
+ devicelist = gdk_seat_get_devices (gdk_display_get_default_seat (gdk_display_get_default ()),
+ GDK_SEAT_CAPABILITY_ALL_POINTING);
+ for (l = devicelist; l != NULL; l = l->next) {
+ GdkDevice *device = l->data;
+
+ if ((XIGetProperty (display, gdk_x11_device_get_id (device), prop,
+ 0, 2, False, XA_INTEGER, &realtype, &realformat, &nitems,
+ &bytes_after, &data) == Success) && (realtype != None)) {
+ have_synaptics = TRUE;
+ XFree (data);
+ }
+
+ if (have_synaptics)
+ break;
+ }
+
+ gdk_x11_display_error_trap_pop_ignored (gdisplay);
+
+ return have_synaptics;
+}
diff --git a/panels/mouse/cc-mouse-caps-helper.h b/panels/mouse/cc-mouse-caps-helper.h
new file mode 100644
index 0000000..93d9082
--- /dev/null
+++ b/panels/mouse/cc-mouse-caps-helper.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2015 Red Hat, Inc,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Felipe Borges <feborges@redhat.com>
+ */
+
+#pragma once
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+gboolean cc_touchpad_check_capabilities (gboolean *have_two_finger_scrolling,
+ gboolean *have_edge_scrolling,
+ gboolean *have_tap_to_click);
+
+gboolean cc_synaptics_check (void);
+
+G_END_DECLS
diff --git a/panels/mouse/cc-mouse-panel.c b/panels/mouse/cc-mouse-panel.c
new file mode 100644
index 0000000..cdd28ab
--- /dev/null
+++ b/panels/mouse/cc-mouse-panel.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2010 Intel, Inc
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Thomas Wood <thomas.wood@intel.com>
+ * Rodrigo Moya <rodrigo@gnome.org>
+ * Ondrej Holy <oholy@redhat.com>
+ *
+ */
+
+#include <gdesktop-enums.h>
+#include <gtk/gtk.h>
+
+#include "cc-mouse-caps-helper.h"
+#include "cc-mouse-panel.h"
+#include "cc-mouse-resources.h"
+#include "cc-mouse-test.h"
+#include "gsd-device-manager.h"
+#include "gsd-input-helper.h"
+
+struct _CcMousePanel
+{
+ CcPanel parent_instance;
+
+ GtkListBoxRow *edge_scrolling_row;
+ GtkSwitch *edge_scrolling_switch;
+ AdwPreferencesGroup *mouse_group;
+ GtkSwitch *mouse_natural_scrolling_switch;
+ GtkScale *mouse_speed_scale;
+ CcMouseTest *mouse_test;
+ GtkBox *primary_button_box;
+ GtkToggleButton *primary_button_left;
+ GtkToggleButton *primary_button_right;
+ AdwPreferencesPage*preferences;
+ GtkStack *stack;
+ GtkListBoxRow *tap_to_click_row;
+ GtkSwitch *tap_to_click_switch;
+ GtkButton *test_button;
+ AdwPreferencesGroup *touchpad_group;
+ GtkListBoxRow *touchpad_natural_scrolling_row;
+ GtkSwitch *touchpad_natural_scrolling_switch;
+ GtkListBoxRow *touchpad_speed_row;
+ GtkScale *touchpad_speed_scale;
+ GtkSwitch *touchpad_toggle_switch;
+ GtkListBoxRow *two_finger_scrolling_row;
+ GtkSwitch *two_finger_scrolling_switch;
+
+ GSettings *mouse_settings;
+ GSettings *touchpad_settings;
+
+ gboolean have_mouse;
+ gboolean have_touchpad;
+ gboolean have_touchscreen;
+ gboolean have_synaptics;
+
+ gboolean left_handed;
+ GtkGesture *left_gesture;
+ GtkGesture *right_gesture;
+};
+
+CC_PANEL_REGISTER (CcMousePanel, cc_mouse_panel)
+
+static void
+setup_touchpad_options (CcMousePanel *self)
+{
+ gboolean edge_scroll_enabled;
+ gboolean two_finger_scroll_enabled;
+ gboolean have_two_finger_scrolling;
+ gboolean have_edge_scrolling;
+ gboolean have_tap_to_click;
+
+ if (self->have_synaptics || !self->have_touchpad) {
+ gtk_widget_hide (GTK_WIDGET (self->touchpad_group));
+ return;
+ }
+
+ cc_touchpad_check_capabilities (&have_two_finger_scrolling, &have_edge_scrolling, &have_tap_to_click);
+
+ gtk_widget_show (GTK_WIDGET (self->touchpad_group));
+
+ gtk_widget_set_visible (GTK_WIDGET (self->two_finger_scrolling_row), have_two_finger_scrolling);
+ gtk_widget_set_visible (GTK_WIDGET (self->edge_scrolling_row), have_edge_scrolling);
+ gtk_widget_set_visible (GTK_WIDGET (self->tap_to_click_row), have_tap_to_click);
+
+ edge_scroll_enabled = g_settings_get_boolean (self->touchpad_settings, "edge-scrolling-enabled");
+ two_finger_scroll_enabled = g_settings_get_boolean (self->touchpad_settings, "two-finger-scrolling-enabled");
+ if (edge_scroll_enabled && two_finger_scroll_enabled)
+ {
+ /* You cunning user set both, but you can only have one set in that UI */
+ gtk_switch_set_active (self->edge_scrolling_switch, FALSE);
+ }
+}
+
+static void
+two_finger_scrolling_changed_event (CcMousePanel *self,
+ gboolean state)
+{
+ /* Updating the setting will cause the "state" of the switch to be updated. */
+ g_settings_set_boolean (self->touchpad_settings, "two-finger-scrolling-enabled", state);
+
+ /* Disable edge scrolling if two-finger scrolling is enabled */
+ if (state && gtk_widget_get_visible (GTK_WIDGET (self->edge_scrolling_row)))
+ gtk_switch_set_active (self->edge_scrolling_switch, FALSE);
+}
+
+static void
+edge_scrolling_changed_event (CcMousePanel *self,
+ gboolean state)
+{
+ /* Updating the setting will cause the "state" of the switch to be updated. */
+ g_settings_set_boolean (self->touchpad_settings, "edge-scrolling-enabled", state);
+
+ /* Disable two-finger scrolling if edge scrolling is enabled */
+ if (state && gtk_widget_get_visible (GTK_WIDGET (self->two_finger_scrolling_row)))
+ gtk_switch_set_active (self->two_finger_scrolling_switch, FALSE);
+}
+
+static gboolean
+get_touchpad_enabled (GSettings *settings)
+{
+ GDesktopDeviceSendEvents send_events;
+
+ send_events = g_settings_get_enum (settings, "send-events");
+
+ return send_events == G_DESKTOP_DEVICE_SEND_EVENTS_ENABLED;
+}
+
+static gboolean
+show_touchpad_enabling_switch (CcMousePanel *self)
+{
+ if (!self->have_touchpad)
+ return FALSE;
+
+ g_debug ("Should we show the touchpad disable switch: have_mouse: %s have_touchscreen: %s\n",
+ self->have_mouse ? "true" : "false",
+ self->have_touchscreen ? "true" : "false");
+
+ /* Let's show the button when a mouse or touchscreen is present */
+ if (self->have_mouse || self->have_touchscreen)
+ return TRUE;
+
+ /* Let's also show when the touchpad is disabled. */
+ if (!get_touchpad_enabled (self->touchpad_settings))
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+touchpad_enabled_get_mapping (GValue *value,
+ GVariant *variant,
+ gpointer user_data)
+{
+ gboolean enabled;
+
+ enabled = g_strcmp0 (g_variant_get_string (variant, NULL), "enabled") == 0;
+ g_value_set_boolean (value, enabled);
+
+ return TRUE;
+}
+
+static GVariant *
+touchpad_enabled_set_mapping (const GValue *value,
+ const GVariantType *type,
+ gpointer user_data)
+{
+ gboolean enabled;
+
+ enabled = g_value_get_boolean (value);
+
+ return g_variant_new_string (enabled ? "enabled" : "disabled");
+}
+
+static void
+pressed_cb (GtkButton *button)
+{
+ g_signal_emit_by_name (button, "activate");
+}
+
+static void
+handle_secondary_button (CcMousePanel *self,
+ GtkToggleButton *button,
+ GtkGesture *gesture)
+{
+ gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), FALSE);
+ gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (gesture), TRUE);
+ gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY);
+ g_signal_connect_swapped (gesture, "pressed", G_CALLBACK (pressed_cb), button);
+ gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), GTK_PHASE_BUBBLE);
+ gtk_widget_add_controller (GTK_WIDGET (button), GTK_EVENT_CONTROLLER (gesture));
+}
+
+/* Set up the property editors in the dialog. */
+static void
+setup_dialog (CcMousePanel *self)
+{
+ GtkToggleButton *button;
+
+ gtk_widget_set_direction (GTK_WIDGET (self->primary_button_box), GTK_TEXT_DIR_LTR);
+
+ self->left_handed = g_settings_get_boolean (self->mouse_settings, "left-handed");
+ button = self->left_handed ? self->primary_button_right : self->primary_button_left;
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+
+ g_settings_bind (self->mouse_settings, "left-handed",
+ self->primary_button_left, "active",
+ G_SETTINGS_BIND_DEFAULT | G_SETTINGS_BIND_INVERT_BOOLEAN);
+ g_settings_bind (self->mouse_settings, "left-handed",
+ self->primary_button_right, "active",
+ G_SETTINGS_BIND_DEFAULT);
+
+ /* Allow changing orientation with either button */
+ button = self->primary_button_right;
+ self->right_gesture = gtk_gesture_click_new ();
+ handle_secondary_button (self, button, self->right_gesture);
+ button = self->primary_button_left;
+ self->left_gesture = gtk_gesture_click_new ();
+ handle_secondary_button (self, button, self->left_gesture);
+
+ g_settings_bind (self->mouse_settings, "natural-scroll",
+ self->mouse_natural_scrolling_switch, "active",
+ G_SETTINGS_BIND_DEFAULT);
+
+ /* Mouse section */
+ gtk_widget_set_visible (GTK_WIDGET (self->mouse_group), self->have_mouse);
+
+ g_settings_bind (self->mouse_settings, "speed",
+ gtk_range_get_adjustment (GTK_RANGE (self->mouse_speed_scale)), "value",
+ G_SETTINGS_BIND_DEFAULT);
+
+ /* Touchpad section */
+ gtk_widget_set_visible (GTK_WIDGET (self->touchpad_toggle_switch), show_touchpad_enabling_switch (self));
+
+ g_settings_bind_with_mapping (self->touchpad_settings, "send-events",
+ self->touchpad_toggle_switch, "active",
+ G_SETTINGS_BIND_DEFAULT,
+ touchpad_enabled_get_mapping,
+ touchpad_enabled_set_mapping,
+ NULL, NULL);
+ g_settings_bind_with_mapping (self->touchpad_settings, "send-events",
+ self->touchpad_natural_scrolling_row, "sensitive",
+ G_SETTINGS_BIND_GET,
+ touchpad_enabled_get_mapping,
+ touchpad_enabled_set_mapping,
+ NULL, NULL);
+ g_settings_bind_with_mapping (self->touchpad_settings, "send-events",
+ self->touchpad_speed_row, "sensitive",
+ G_SETTINGS_BIND_GET,
+ touchpad_enabled_get_mapping,
+ touchpad_enabled_set_mapping,
+ NULL, NULL);
+ g_settings_bind_with_mapping (self->touchpad_settings, "send-events",
+ self->tap_to_click_row, "sensitive",
+ G_SETTINGS_BIND_GET,
+ touchpad_enabled_get_mapping,
+ touchpad_enabled_set_mapping,
+ NULL, NULL);
+ g_settings_bind_with_mapping (self->touchpad_settings, "send-events",
+ self->two_finger_scrolling_row, "sensitive",
+ G_SETTINGS_BIND_GET,
+ touchpad_enabled_get_mapping,
+ touchpad_enabled_set_mapping,
+ NULL, NULL);
+ g_settings_bind_with_mapping (self->touchpad_settings, "send-events",
+ self->edge_scrolling_row, "sensitive",
+ G_SETTINGS_BIND_GET,
+ touchpad_enabled_get_mapping,
+ touchpad_enabled_set_mapping,
+ NULL, NULL);
+
+ g_settings_bind (self->touchpad_settings, "natural-scroll",
+ self->touchpad_natural_scrolling_switch, "active",
+ G_SETTINGS_BIND_DEFAULT);
+
+ g_settings_bind (self->touchpad_settings, "speed",
+ gtk_range_get_adjustment (GTK_RANGE (self->touchpad_speed_scale)), "value",
+ G_SETTINGS_BIND_DEFAULT);
+
+ g_settings_bind (self->touchpad_settings, "tap-to-click",
+ self->tap_to_click_switch, "active",
+ G_SETTINGS_BIND_DEFAULT);
+
+ g_settings_bind (self->touchpad_settings, "two-finger-scrolling-enabled",
+ self->two_finger_scrolling_switch, "state",
+ G_SETTINGS_BIND_GET);
+
+ g_settings_bind (self->touchpad_settings, "edge-scrolling-enabled",
+ self->edge_scrolling_switch, "state",
+ G_SETTINGS_BIND_GET);
+
+ setup_touchpad_options (self);
+}
+
+/* Callback issued when a button is clicked on the dialog */
+static void
+device_changed (CcMousePanel *self)
+{
+ self->have_touchpad = touchpad_is_present ();
+
+ setup_touchpad_options (self);
+
+ self->have_mouse = mouse_is_present ();
+ gtk_widget_set_visible (GTK_WIDGET (self->mouse_group), self->have_mouse);
+ gtk_widget_set_visible (GTK_WIDGET (self->touchpad_toggle_switch), show_touchpad_enabling_switch (self));
+}
+
+static void
+cc_mouse_panel_dispose (GObject *object)
+{
+ CcMousePanel *self = CC_MOUSE_PANEL (object);
+
+ g_clear_object (&self->mouse_settings);
+ g_clear_object (&self->touchpad_settings);
+
+ G_OBJECT_CLASS (cc_mouse_panel_parent_class)->dispose (object);
+}
+
+static const char *
+cc_mouse_panel_get_help_uri (CcPanel *panel)
+{
+ return "help:gnome-help/mouse";
+}
+
+static void
+test_button_toggled_cb (CcMousePanel *self)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->test_button)))
+ gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->mouse_test));
+ else
+ gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->preferences));
+}
+
+static void
+cc_mouse_panel_init (CcMousePanel *self)
+{
+ GsdDeviceManager *device_manager;
+
+ g_resources_register (cc_mouse_get_resource ());
+
+ cc_mouse_test_get_type ();
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ self->mouse_settings = g_settings_new ("org.gnome.desktop.peripherals.mouse");
+ self->touchpad_settings = g_settings_new ("org.gnome.desktop.peripherals.touchpad");
+
+ device_manager = gsd_device_manager_get ();
+ g_signal_connect_object (device_manager, "device-added",
+ G_CALLBACK (device_changed), self, G_CONNECT_SWAPPED);
+ g_signal_connect_object (device_manager, "device-removed",
+ G_CALLBACK (device_changed), self, G_CONNECT_SWAPPED);
+
+ self->have_mouse = mouse_is_present ();
+ self->have_touchpad = touchpad_is_present ();
+ self->have_touchscreen = touchscreen_is_present ();
+ self->have_synaptics = cc_synaptics_check ();
+ if (self->have_synaptics)
+ g_warning ("Detected synaptics X driver, please migrate to libinput");
+
+ setup_dialog (self);
+}
+
+static void
+cc_mouse_panel_class_init (CcMousePanelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
+
+ panel_class->get_help_uri = cc_mouse_panel_get_help_uri;
+
+ object_class->dispose = cc_mouse_panel_dispose;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/mouse/cc-mouse-panel.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, edge_scrolling_row);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, edge_scrolling_switch);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, mouse_group);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, mouse_natural_scrolling_switch);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, mouse_speed_scale);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, mouse_test);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, primary_button_box);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, primary_button_left);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, primary_button_right);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, preferences);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, stack);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, tap_to_click_row);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, tap_to_click_switch);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, test_button);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, touchpad_group);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, touchpad_natural_scrolling_row);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, touchpad_natural_scrolling_switch);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, touchpad_speed_row);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, touchpad_speed_scale);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, touchpad_toggle_switch);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, two_finger_scrolling_row);
+ gtk_widget_class_bind_template_child (widget_class, CcMousePanel, two_finger_scrolling_switch);
+
+ gtk_widget_class_bind_template_callback (widget_class, edge_scrolling_changed_event);
+ gtk_widget_class_bind_template_callback (widget_class, test_button_toggled_cb);
+ gtk_widget_class_bind_template_callback (widget_class, two_finger_scrolling_changed_event);
+}
diff --git a/panels/mouse/cc-mouse-panel.h b/panels/mouse/cc-mouse-panel.h
new file mode 100644
index 0000000..ae83e8e
--- /dev/null
+++ b/panels/mouse/cc-mouse-panel.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 Intel, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Thomas Wood <thomas.wood@intel.com>
+ *
+ */
+
+#pragma once
+
+#include <shell/cc-panel.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_MOUSE_PANEL (cc_mouse_panel_get_type ())
+G_DECLARE_FINAL_TYPE (CcMousePanel, cc_mouse_panel, CC, MOUSE_PANEL, CcPanel)
+
+G_END_DECLS
diff --git a/panels/mouse/cc-mouse-panel.ui b/panels/mouse/cc-mouse-panel.ui
new file mode 100644
index 0000000..c5d1351
--- /dev/null
+++ b/panels/mouse/cc-mouse-panel.ui
@@ -0,0 +1,219 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="CcMousePanel" parent="CcPanel">
+
+ <child type="titlebar-end">
+ <object class="GtkToggleButton" id="test_button">
+ <property name="use_underline">True</property>
+ <property name="valign">center</property>
+ <property name="label" translatable="yes">Test Your _Settings</property>
+ <signal name="toggled" handler="test_button_toggled_cb" object="CcMousePanel" swapped="yes"/>
+ <style>
+ <class name="text-button"/>
+ </style>
+ </object>
+ </child>
+
+ <child type="content">
+ <object class="GtkStack" id="stack">
+ <child>
+ <object class="AdwPreferencesPage" id="preferences">
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">General</property>
+ <child>
+ <object class="AdwActionRow" id="primary_button_row">
+ <property name="title" translatable="yes">Primary Button</property>
+ <property name="subtitle" translatable="yes">Sets the order of physical buttons on mice and touchpads.</property>
+ <property name="subtitle-lines">0</property>
+ <child>
+ <object class="GtkBox" id="primary_button_box">
+ <property name="valign">center</property>
+ <property name="homogeneous">True</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <style>
+ <class name="linked"/>
+ </style>
+ <child>
+ <object class="GtkToggleButton" id="primary_button_left">
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Left</property>
+ <property name="height_request">35</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="primary_button_right">
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Right</property>
+ <property name="group">primary_button_left</property>
+ <property name="height_request">35</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup" id="mouse_group">
+ <property name="title" translatable="yes">Mouse</property>
+ <child>
+ <object class="AdwActionRow" id="mouse_row">
+ <property name="title" translatable="yes">Mouse Speed</property>
+ <child>
+ <object class="GtkScale" id="mouse_speed_scale">
+ <property name="adjustment">mouse_speed_adjustment</property>
+ <property name="draw_value">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <accessibility>
+ <property name="label">Mouse speed</property>
+ </accessibility>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow" id="mouse_natural_scrolling_row">
+ <property name="title" translatable="yes" comments="Translators: This switch reverses the scrolling direction for mices. The term used comes from OS X so use the same translation if possible.">Natural Scrolling</property>
+ <property name="subtitle" translatable="yes">Scrolling moves the content, not the view.</property>
+ <property name="activatable-widget">mouse_natural_scrolling_switch</property>
+ <child>
+ <object class="GtkSwitch" id="mouse_natural_scrolling_switch">
+ <property name="valign">center</property>
+ <accessibility>
+ <property name="label" translatable="yes">Natural Scrolling</property>
+ </accessibility>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup" id="touchpad_group">
+ <property name="title" translatable="yes">Touchpad</property>
+ <child>
+ <object class="AdwActionRow" id="touchpad_toggle_row">
+ <property name="title" translatable="yes">Touchpad</property>
+ <property name="activatable-widget">touchpad_toggle_switch</property>
+ <child>
+ <object class="GtkSwitch" id="touchpad_toggle_switch">
+ <property name="valign">center</property>
+ <accessibility>
+ <property name="label" translatable="yes">Touchpad</property>
+ </accessibility>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow" id="touchpad_natural_scrolling_row">
+ <property name="title" translatable="yes" comments="Translators: This switch reverses the scrolling direction for touchpads. The term used comes from OS X so use the same translation if possible. ">Natural Scrolling</property>
+ <property name="subtitle" translatable="yes">Scrolling moves the content, not the view.</property>
+ <property name="activatable-widget">touchpad_natural_scrolling_switch</property>
+ <child>
+ <object class="GtkSwitch" id="touchpad_natural_scrolling_switch">
+ <property name="valign">center</property>
+ <accessibility>
+ <property name="label" translatable="yes">Natural Scrolling</property>
+ </accessibility>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow" id="touchpad_speed_row">
+ <property name="title" translatable="yes">Touchpad Speed</property>
+ <child>
+ <object class="GtkScale" id="touchpad_speed_scale">
+ <property name="adjustment">touchpad_speed_adjustment</property>
+ <property name="draw_value">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <accessibility>
+ <property name="label">Double-click timeout</property>
+ </accessibility>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow" id="tap_to_click_row">
+ <property name="visible">False</property>
+ <property name="title" translatable="yes">Tap to Click</property>
+ <property name="activatable-widget">tap_to_click_switch</property>
+ <child>
+ <object class="GtkSwitch" id="tap_to_click_switch">
+ <property name="valign">center</property>
+ <accessibility>
+ <property name="label" translatable="yes">Tap to click</property>
+ </accessibility>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow" id="two_finger_scrolling_row">
+ <property name="visible">False</property>
+ <property name="title" translatable="yes">Two-finger Scrolling</property>
+ <property name="activatable-widget">two_finger_scrolling_switch</property>
+ <child>
+ <object class="GtkSwitch" id="two_finger_scrolling_switch">
+ <property name="valign">center</property>
+ <accessibility>
+ <property name="label" translatable="yes">Two-finger Scrolling</property>
+ </accessibility>
+ <signal name="state-set" handler="two_finger_scrolling_changed_event" object="CcMousePanel" swapped="yes"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow" id="edge_scrolling_row">
+ <property name="visible">False</property>
+ <property name="title" translatable="yes">Edge Scrolling</property>
+ <property name="activatable-widget">edge_scrolling_switch</property>
+ <child>
+ <object class="GtkSwitch" id="edge_scrolling_switch">
+ <property name="valign">center</property>
+ <accessibility>
+ <property name="label" translatable="yes">Edge Scrolling</property>
+ </accessibility>
+ <signal name="state-set" handler="edge_scrolling_changed_event" object="CcMousePanel" swapped="yes"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ </object>
+ </child>
+ <child>
+ <object class="CcMouseTest" id="mouse_test">
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkAdjustment" id="mouse_speed_adjustment">
+ <property name="lower">-1</property>
+ <property name="upper">1</property>
+ </object>
+ <object class="GtkAdjustment" id="touchpad_speed_adjustment">
+ <property name="lower">-1</property>
+ <property name="upper">1</property>
+ </object>
+ <object class="GtkSizeGroup">
+ <property name="mode">horizontal</property>
+ <widgets>
+ <widget name="mouse_speed_scale"/>
+ <widget name="touchpad_speed_scale"/>
+ </widgets>
+ </object>
+</interface>
diff --git a/panels/mouse/cc-mouse-test.c b/panels/mouse/cc-mouse-test.c
new file mode 100644
index 0000000..145cb74
--- /dev/null
+++ b/panels/mouse/cc-mouse-test.c
@@ -0,0 +1,389 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Written by: Ondrej Holy <oholy@redhat.com>,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <glib/gi18n.h>
+#include <string.h>
+#include <gdk/gdk.h>
+#include <gdk/x11/gdkx.h>
+#include <math.h>
+
+#include "cc-mouse-test.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/* Click test button sizes. */
+#define SHADOW_SIZE (10.0 / 180 * size)
+#define SHADOW_SHIFT_Y (-1.0 / 180 * size)
+#define SHADOW_OPACITY (0.15 / 180 * size)
+#define OUTER_CIRCLE_SIZE (22.0 / 180 * size)
+#define ANNULUS_SIZE (6.0 / 180 * size)
+#define INNER_CIRCLE_SIZE (52.0 / 180 * size)
+
+static void setup_information_label (CcMouseTest *self);
+static void setup_scroll_image (CcMouseTest *self);
+
+enum
+{
+ DOUBLE_CLICK_TEST_OFF,
+ DOUBLE_CLICK_TEST_MAYBE,
+ DOUBLE_CLICK_TEST_ON,
+ DOUBLE_CLICK_TEST_STILL_ON,
+ DOUBLE_CLICK_TEST_ALMOST_THERE,
+ DOUBLE_CLICK_TEST_GEGL
+};
+
+struct _CcMouseTest
+{
+ AdwBin parent_instance;
+
+ GtkWidget *button_drawing_area;
+ GtkWidget *information_label;
+ GtkWidget *image;
+ GtkWidget *scrolled_window_adjustment;
+ GtkWidget *viewport;
+
+ guint32 double_click_timestamp;
+ gint double_click_state;
+ gint button_state;
+
+ GSettings *mouse_settings;
+
+ gint information_label_timeout_id;
+ gint button_drawing_area_timeout_id;
+ gint scroll_image_timeout_id;
+};
+
+G_DEFINE_TYPE (CcMouseTest, cc_mouse_test, ADW_TYPE_BIN);
+
+/* Timeout for the double click test */
+
+static gboolean
+test_maybe_timeout (CcMouseTest *self)
+{
+ self->double_click_state = DOUBLE_CLICK_TEST_OFF;
+
+ gtk_widget_queue_draw (self->button_drawing_area);
+
+ self->button_drawing_area_timeout_id = 0;
+
+ return FALSE;
+}
+
+/* Timeout for the information label */
+
+static gboolean
+information_label_timeout (CcMouseTest *self)
+{
+ setup_information_label (self);
+
+ self->information_label_timeout_id = 0;
+
+ return FALSE;
+}
+
+/* Timeout for the scroll image */
+
+static gboolean
+scroll_image_timeout (CcMouseTest *self)
+{
+ setup_scroll_image (self);
+
+ self->scroll_image_timeout_id = 0;
+
+ return FALSE;
+}
+
+/* Set information label */
+
+static void
+setup_information_label (CcMouseTest *self)
+{
+ const gchar *message = NULL;
+ g_autofree gchar *label_text = NULL;
+ gboolean double_click;
+
+ if (self->information_label_timeout_id != 0) {
+ g_source_remove (self->information_label_timeout_id);
+ self->information_label_timeout_id = 0;
+ }
+
+ if (self->double_click_state == DOUBLE_CLICK_TEST_OFF) {
+ gtk_label_set_label (GTK_LABEL (self->information_label), _("Try clicking, double clicking, scrolling"));
+ return;
+ }
+
+ if (self->double_click_state == DOUBLE_CLICK_TEST_GEGL) {
+ message = _("Five clicks, GEGL time!");
+ } else {
+ double_click = (self->double_click_state >= DOUBLE_CLICK_TEST_ON);
+ switch (self->button_state) {
+ case 1:
+ message = (double_click) ? _("Double click, primary button") : _("Single click, primary button");
+ break;
+ case 2:
+ message = (double_click) ? _("Double click, middle button") : _("Single click, middle button");
+ break;
+ case 3:
+ message = (double_click) ? _("Double click, secondary button") : _("Single click, secondary button");
+ break;
+ }
+ }
+
+ label_text = g_strconcat ("<b>", message, "</b>", NULL);
+ gtk_label_set_markup (GTK_LABEL (self->information_label), label_text);
+
+ self->information_label_timeout_id = g_timeout_add (2500, (GSourceFunc) information_label_timeout, self);
+}
+
+/* Update scroll image */
+
+static void
+setup_scroll_image (CcMouseTest *self)
+{
+ const char *resource;
+
+ if (self->scroll_image_timeout_id != 0) {
+ g_source_remove (self->scroll_image_timeout_id);
+ self->scroll_image_timeout_id = 0;
+ }
+
+ if (self->double_click_state == DOUBLE_CLICK_TEST_GEGL)
+ resource = "/org/gnome/control-center/mouse/scroll-test-gegl.svg";
+ else
+ resource = "/org/gnome/control-center/mouse/scroll-test.svg";
+ gtk_picture_set_resource (GTK_PICTURE (self->image), resource);
+
+ if (self->double_click_state != DOUBLE_CLICK_TEST_GEGL)
+ return;
+
+ self->scroll_image_timeout_id = g_timeout_add (5000, (GSourceFunc) scroll_image_timeout, self);
+}
+
+/* Callback issued when the user clicks the double click testing area. */
+
+static void
+button_drawing_area_button_pressed_cb (GtkGestureClick *click_gesture,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ CcMouseTest *self)
+{
+ guint32 event_time;
+ guint current_button;
+ gint double_click_time;
+
+ current_button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (click_gesture));
+
+ if (current_button > 3)
+ return;
+
+ double_click_time = g_settings_get_int (self->mouse_settings, "double-click");
+
+ if (self->button_drawing_area_timeout_id != 0) {
+ g_source_remove (self->button_drawing_area_timeout_id);
+ self->button_drawing_area_timeout_id = 0;
+ }
+
+ /* Ignore fake double click using different buttons. */
+ if (self->double_click_state != DOUBLE_CLICK_TEST_OFF && self->button_state != current_button)
+ self->double_click_state = DOUBLE_CLICK_TEST_OFF;
+
+ event_time = gtk_event_controller_get_current_event_time (GTK_EVENT_CONTROLLER (click_gesture));
+ switch (self->double_click_state) {
+ case DOUBLE_CLICK_TEST_OFF:
+ self->double_click_state = DOUBLE_CLICK_TEST_MAYBE;
+ self->button_drawing_area_timeout_id = g_timeout_add (double_click_time, (GSourceFunc) test_maybe_timeout, self);
+ break;
+ case DOUBLE_CLICK_TEST_MAYBE:
+ case DOUBLE_CLICK_TEST_ON:
+ case DOUBLE_CLICK_TEST_STILL_ON:
+ case DOUBLE_CLICK_TEST_ALMOST_THERE:
+ if (event_time - self->double_click_timestamp < double_click_time) {
+ self->double_click_state++;
+ self->button_drawing_area_timeout_id = g_timeout_add (2500, (GSourceFunc) test_maybe_timeout, self);
+ } else {
+ test_maybe_timeout (self);
+ }
+ break;
+ case DOUBLE_CLICK_TEST_GEGL:
+ self->double_click_state = DOUBLE_CLICK_TEST_OFF;
+ break;
+ }
+
+ self->double_click_timestamp = event_time;
+
+ gtk_widget_queue_draw (self->button_drawing_area);
+
+ self->button_state = current_button;
+ setup_information_label (self);
+ setup_scroll_image (self);
+}
+
+static void
+button_drawing_area_draw_func (GtkDrawingArea *drawing_area,
+ cairo_t *cr,
+ int width,
+ int height,
+ gpointer user_data)
+{
+ CcMouseTest *self = CC_MOUSE_TEST (user_data);
+ gdouble center_x, center_y, size;
+ GdkRGBA inner_color, outer_color;
+ cairo_pattern_t *pattern;
+
+ size = MAX (MIN (width, height), 1);
+ center_x = width / 2.0;
+ center_y = height / 2.0;
+
+ switch (self->double_click_state) {
+ case DOUBLE_CLICK_TEST_ON:
+ case DOUBLE_CLICK_TEST_STILL_ON:
+ case DOUBLE_CLICK_TEST_ALMOST_THERE:
+ case DOUBLE_CLICK_TEST_GEGL:
+ gdk_rgba_parse (&outer_color, "#729fcf");
+ gdk_rgba_parse (&inner_color, "#729fcf");
+ break;
+ case DOUBLE_CLICK_TEST_MAYBE:
+ gdk_rgba_parse (&outer_color, "#729fcf");
+ gdk_rgba_parse (&inner_color, "#ffffff");
+ break;
+ case DOUBLE_CLICK_TEST_OFF:
+ gdk_rgba_parse (&outer_color, "#ffffff");
+ gdk_rgba_parse (&inner_color, "#ffffff");
+ break;
+ }
+
+ /* Draw shadow. */
+ cairo_rectangle (cr, center_x - size / 2, center_y - size / 2, size, size);
+ pattern = cairo_pattern_create_radial (center_x, center_y, 0, center_x, center_y, size);
+ cairo_pattern_add_color_stop_rgba (pattern, 0.5 - SHADOW_SIZE / size, 0, 0, 0, SHADOW_OPACITY);
+ cairo_pattern_add_color_stop_rgba (pattern, 0.5, 0, 0, 0, 0);
+ cairo_set_source (cr, pattern);
+ cairo_fill (cr);
+
+ /* Draw outer circle. */
+ cairo_set_line_width (cr, OUTER_CIRCLE_SIZE);
+ cairo_arc (cr, center_x, center_y + SHADOW_SHIFT_Y,
+ INNER_CIRCLE_SIZE + ANNULUS_SIZE + OUTER_CIRCLE_SIZE / 2,
+ 0, 2 * G_PI);
+ gdk_cairo_set_source_rgba (cr, &outer_color);
+ cairo_stroke (cr);
+
+ /* Draw inner circle. */
+ cairo_set_line_width (cr, 0);
+ cairo_arc (cr, center_x, center_y + SHADOW_SHIFT_Y,
+ INNER_CIRCLE_SIZE,
+ 0, 2 * G_PI);
+ gdk_cairo_set_source_rgba (cr, &inner_color);
+ cairo_fill (cr);
+}
+
+static void
+setup_dialog (CcMouseTest *self)
+{
+ GtkAdjustment *adjustment;
+ GtkStyleProvider *provider;
+
+ adjustment = GTK_ADJUSTMENT (self->scrolled_window_adjustment);
+ gtk_adjustment_set_value (adjustment,
+ gtk_adjustment_get_upper (adjustment));
+
+ provider = GTK_STYLE_PROVIDER (gtk_css_provider_new ());
+ gtk_css_provider_load_from_data (GTK_CSS_PROVIDER (provider), "* {background: #26a269;}", -1);
+ gtk_style_context_add_provider (gtk_widget_get_style_context (self->viewport),
+ provider,
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+ g_object_unref (provider);
+}
+
+static void
+cc_mouse_test_finalize (GObject *object)
+{
+ CcMouseTest *self = CC_MOUSE_TEST (object);
+
+ g_clear_object (&self->mouse_settings);
+
+ if (self->information_label_timeout_id != 0) {
+ g_source_remove (self->information_label_timeout_id);
+ self->information_label_timeout_id = 0;
+ }
+
+ if (self->scroll_image_timeout_id != 0) {
+ g_source_remove (self->scroll_image_timeout_id);
+ self->scroll_image_timeout_id = 0;
+ }
+
+ if (self->button_drawing_area_timeout_id != 0) {
+ g_source_remove (self->button_drawing_area_timeout_id);
+ self->button_drawing_area_timeout_id = 0;
+ }
+
+ G_OBJECT_CLASS (cc_mouse_test_parent_class)->finalize (object);
+}
+
+static void
+cc_mouse_test_class_init (CcMouseTestClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = cc_mouse_test_finalize;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/mouse/cc-mouse-test.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, CcMouseTest, button_drawing_area);
+ gtk_widget_class_bind_template_child (widget_class, CcMouseTest, information_label);
+ gtk_widget_class_bind_template_child (widget_class, CcMouseTest, image);
+ gtk_widget_class_bind_template_child (widget_class, CcMouseTest, scrolled_window_adjustment);
+ gtk_widget_class_bind_template_child (widget_class, CcMouseTest, viewport);
+
+ gtk_widget_class_bind_template_callback (widget_class, button_drawing_area_button_pressed_cb);
+}
+
+static void
+cc_mouse_test_init (CcMouseTest *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (self->button_drawing_area),
+ button_drawing_area_draw_func,
+ self, NULL);
+
+ self->double_click_timestamp = 0;
+ self->double_click_state = DOUBLE_CLICK_TEST_OFF;
+ self->button_state = 0;
+
+ self->mouse_settings = g_settings_new ("org.gnome.desktop.peripherals.mouse");
+
+ self->information_label_timeout_id = 0;
+ self->button_drawing_area_timeout_id = 0;
+ self->scroll_image_timeout_id = 0;
+
+ setup_dialog (self);
+}
+
+GtkWidget *
+cc_mouse_test_new (void)
+{
+ return (GtkWidget *) g_object_new (CC_TYPE_MOUSE_TEST, NULL);
+}
diff --git a/panels/mouse/cc-mouse-test.h b/panels/mouse/cc-mouse-test.h
new file mode 100644
index 0000000..dc425f8
--- /dev/null
+++ b/panels/mouse/cc-mouse-test.h
@@ -0,0 +1,32 @@
+/* -*- mode: c; style: linux -*-
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Written by: Ondrej Holy <oholy@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <adwaita.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_MOUSE_TEST (cc_mouse_test_get_type ())
+G_DECLARE_FINAL_TYPE (CcMouseTest, cc_mouse_test, CC, MOUSE_TEST, AdwBin)
+
+GtkWidget *cc_mouse_test_new (void);
+
+G_END_DECLS
diff --git a/panels/mouse/cc-mouse-test.ui b/panels/mouse/cc-mouse-test.ui
new file mode 100644
index 0000000..c007361
--- /dev/null
+++ b/panels/mouse/cc-mouse-test.ui
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkAdjustment" id="scrolled_window_adjustment">
+ <property name="upper">100</property>
+ <property name="value">100</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <template class="CcMouseTest" parent="AdwBin">
+ <child>
+ <object class="GtkBox" id="test_widget">
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow">
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="vadjustment">scrolled_window_adjustment</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">always</property>
+ <property name="max_content_height">440</property>
+ <child>
+ <object class="GtkViewport" id="viewport">
+ <child>
+ <object class="GtkOverlay" id="grid1">
+ <child>
+ <object class="GtkPicture" id="image">
+ <property name="margin_start">25</property>
+ <property name="margin_end">25</property>
+ <property name="can-shrink">False</property>
+ <property name="file">resource:///org/gnome/control-center/mouse/scroll-test.svg</property>
+ </object>
+ </child>
+ <child type="overlay">
+ <object class="GtkDrawingArea" id="button_drawing_area">
+ <property name="width_request">180</property>
+ <property name="height_request">180</property>
+ <property name="valign">end</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ <child>
+ <object class="GtkGestureClick">
+ <property name="button">0</property>
+ <signal name="pressed" handler="button_drawing_area_button_pressed_cb" object="CcMouseTest" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="information_label">
+ <property name="margin_top">10</property>
+ <property name="margin_bottom">5</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Try clicking, double clicking, scrolling</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/panels/mouse/gnome-mouse-panel.desktop.in.in b/panels/mouse/gnome-mouse-panel.desktop.in.in
new file mode 100644
index 0000000..2d9ed7c
--- /dev/null
+++ b/panels/mouse/gnome-mouse-panel.desktop.in.in
@@ -0,0 +1,18 @@
+[Desktop Entry]
+Name=Mouse & Touchpad
+Comment=Change your mouse or touchpad sensitivity and select right or left-handed
+Exec=gnome-control-center mouse
+# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+Icon=org.gnome.Settings-mouse-symbolic
+Terminal=false
+Type=Application
+NoDisplay=true
+StartupNotify=true
+Categories=GNOME;GTK;Settings;HardwareSettings;X-GNOME-Settings-Panel;X-GNOME-DevicesSettings;
+OnlyShowIn=GNOME;Unity;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-control-center
+X-GNOME-Bugzilla-Component=mouse
+X-GNOME-Bugzilla-Version=@VERSION@
+# Translators: Search terms to find the Mouse and Touchpad panel. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
+Keywords=Trackpad;Pointer;Click;Tap;Double;Button;Trackball;Scroll;
diff --git a/panels/mouse/icons/meson.build b/panels/mouse/icons/meson.build
new file mode 100644
index 0000000..02e9235
--- /dev/null
+++ b/panels/mouse/icons/meson.build
@@ -0,0 +1,4 @@
+install_data(
+ 'scalable/org.gnome.Settings-mouse-symbolic.svg',
+ install_dir: join_paths(control_center_icondir, 'hicolor', 'scalable', 'apps')
+)
diff --git a/panels/mouse/icons/scalable/org.gnome.Settings-mouse-symbolic.svg b/panels/mouse/icons/scalable/org.gnome.Settings-mouse-symbolic.svg
new file mode 100644
index 0000000..cdcaa63
--- /dev/null
+++ b/panels/mouse/icons/scalable/org.gnome.Settings-mouse-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 7.03125 0 c -2.753906 0 -5 2.246094 -5 5 v 6 c 0 2.753906 2.246094 5 5 5 h 1.96875 c 2.753906 0 5 -2.246094 5 -5 v -6 c 0 -2.753906 -2.246094 -5 -5 -5 z m 0 2 h 1.96875 c 1.679688 0 3 1.320312 3 3 v 6 c 0 1.679688 -1.320312 3 -3 3 h -1.96875 c -1.679688 0 -3 -1.320312 -3 -3 v -6 c 0 -1.679688 1.320312 -3 3 -3 z m 0.96875 1 c -0.554688 0 -1 0.445312 -1 1 v 2 c 0 0.554688 0.445312 1 1 1 s 1 -0.445312 1 -1 v -2 c 0 -0.554688 -0.445312 -1 -1 -1 z m 0 0" fill="#2e3436"/>
+</svg>
diff --git a/panels/mouse/meson.build b/panels/mouse/meson.build
new file mode 100644
index 0000000..8ebf3fc
--- /dev/null
+++ b/panels/mouse/meson.build
@@ -0,0 +1,70 @@
+panels_list += cappletname
+desktop = 'gnome-@0@-panel.desktop'.format(cappletname)
+
+desktop_in = configure_file(
+ input: desktop + '.in.in',
+ output: desktop + '.in',
+ configuration: desktop_conf
+)
+
+i18n.merge_file(
+ type: 'desktop',
+ input: desktop_in,
+ output: desktop,
+ po_dir: po_dir,
+ install: true,
+ install_dir: control_center_desktopdir
+)
+
+resource_data = files(
+ 'cc-mouse-panel.ui',
+ 'cc-mouse-test.ui',
+ 'scroll-test-gegl.svg',
+ 'scroll-test.svg'
+)
+
+common_sources = gnome.compile_resources(
+ 'cc-' + cappletname + '-resources',
+ cappletname + '.gresource.xml',
+ c_name: 'cc_' + cappletname,
+ dependencies: resource_data,
+ export: true
+)
+
+sources = common_sources + files(
+ 'cc-mouse-panel.c',
+ 'cc-mouse-caps-helper.c',
+ 'cc-mouse-test.c',
+)
+
+deps = common_deps + [
+ gnome_settings_dep,
+ libdevice_dep,
+ x11_dep,
+ xi_dep
+]
+
+panels_libs += static_library(
+ cappletname + '-properties',
+ sources: sources,
+ include_directories: top_inc,
+ dependencies: deps,
+ c_args: cflags
+)
+
+test_name = 'test-gnome-mouse-test'
+
+sources = common_sources + files(
+ 'cc-mouse-test.c',
+ test_name + '.c'
+)
+
+executable(
+ test_name,
+ sources,
+ include_directories: top_inc,
+ dependencies: deps,
+ c_args: cflags
+)
+
+subdir('icons')
diff --git a/panels/mouse/mouse.gresource.xml b/panels/mouse/mouse.gresource.xml
new file mode 100644
index 0000000..0568705
--- /dev/null
+++ b/panels/mouse/mouse.gresource.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/control-center/mouse">
+ <file preprocess="xml-stripblanks">cc-mouse-panel.ui</file>
+ <file preprocess="xml-stripblanks">cc-mouse-test.ui</file>
+ <file>scroll-test.svg</file>
+ <file>scroll-test-gegl.svg</file>
+ </gresource>
+</gresources>
diff --git a/panels/mouse/scroll-test-gegl.svg b/panels/mouse/scroll-test-gegl.svg
new file mode 100644
index 0000000..81ed22b
--- /dev/null
+++ b/panels/mouse/scroll-test-gegl.svg
@@ -0,0 +1 @@
+<svg width="640" height="1600" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="a"><stop style="stop-color:#eeeeec;stop-opacity:1" offset="0"/><stop style="stop-color:#fff;stop-opacity:1" offset="1"/></linearGradient><linearGradient id="b"><stop style="stop-color:#f57900;stop-opacity:1" offset="0"/><stop style="stop-color:#204a87;stop-opacity:1" offset="1"/></linearGradient></defs><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 69.359 479.233)"/></g><path style="fill:#1a724b;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M304.117-174.224 221.605 18.303H386.63z" transform="translate(-36.19 666.069) scale(.66774)"/><path style="fill:none;stroke:#3d3846;stroke-width:160;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M102.416-1167.375v321.533a100 100 45 0 0 100 100H345.68a100 100 45 0 1 100 100V399.245a100 100 135 0 1-100 100H185.963a100 100 135 0 0-100 100v247.592a100.119 100.119 45.034 0 0 100 100.12l521.828.62a94.738 94.738 45.034 0 1 94.625 94.737v190.048" transform="translate(27.86 739.504) scale(.66774)"/><path style="fill:none;stroke:#fff;stroke-width:10;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:40,80;stroke-dashoffset:0;stroke-opacity:1" d="M445.681-134.823v-511.02a100 100 45 0 0-100-100H202.416a100 100 45 0 1-100-100V-1167.374M445.68 61.57v337.676a100 100 135 0 1-100 100H185.963a100 100 135 0 0-100 100v247.592a100.119 100.119 45.034 0 0 100 100.12l526.705.625a89.855 89.855 45.034 0 1 89.748 89.855v274.925" transform="translate(27.86 739.504) scale(.66774)"/><g transform="translate(244.77 965.81) scale(.66774)"><path style="fill:none;stroke:#63452c;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M157.574 392.456V272.495"/><circle style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#1b754c;fill-opacity:1;stroke:none;stroke-width:160;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000;stop-opacity:1" cx="157.574" cy="246.53" r="73.723"/></g><g transform="translate(378.319 965.81) scale(.66774)"><path style="fill:none;stroke:#63452c;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M157.574 392.456V272.495"/><circle style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#1b754c;fill-opacity:1;stroke:none;stroke-width:160;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000;stop-opacity:1" cx="157.574" cy="246.53" r="73.723"/></g><g transform="matrix(-.41934 0 0 .41934 702.502 1098.89)"><circle style="opacity:1;vector-effect:none;fill:#63452c;fill-opacity:1;stroke:none;stroke-width:31.2335;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" cx="760.59" cy="270.262" r="16.398"/><g transform="translate(-2284.074 -1604.387) scale(7.08287)" id="c"><circle style="opacity:1;vector-effect:none;fill:#f6f5f4;fill-opacity:1;stroke:none;stroke-width:5.29167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" cx="436.72" cy="303.109" r="5.379"/><circle r="3.508" cy="303.109" cx="436.72" style="opacity:1;vector-effect:none;fill:#9a9996;fill-opacity:1;stroke:none;stroke-width:3.45109;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><circle style="opacity:1;vector-effect:none;fill:#3d3846;fill-opacity:1;stroke:none;stroke-width:1.28841;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" cx="436.72" cy="303.109" r="1.31"/></g><path d="m801.54 513.675 7.95 28.821" style="fill:#9a9996;stroke:#77767b;stroke-width:7.49604;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/><path d="m816.9 529.13-7.926 13.468" style="fill:#9a9996;stroke:#c0bfbc;stroke-width:11.2441;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/><path d="M813.57 498.034c-23.928-3.877-47.532 8.562-57.86 30.493l92.166.176c2.942.17 4.99-2.397 3.358-5.223a54.348 54.348 0 0 0-37.664-25.443z" style="opacity:1;vector-effect:none;fill:#e01b24;fill-opacity:1;stroke:none;stroke-width:37.4802;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><path d="M679.629 505.061c5.475 26.176 17.308 36.128 29.152 41.41l-45.716.663-2.32-35.447z" style="fill:#c01c28;fill-opacity:1;stroke:none;stroke-width:1.87401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/><path d="M633.3 452.04c-19.064 0-34.41 13.374-34.41 29.985h-2.247a9.378 9.378 0 0 0-9.4 9.4v6.968c0 5.208 4.192 9.4 9.4 9.4h15.87c.871 0 1.698-.154 2.497-.374 5.296 2.895 11.55 4.59 18.29 4.59h30.551c19.064 0 34.413-13.373 34.413-29.984 0-16.611-15.35-29.984-34.413-29.984z" style="opacity:1;vector-effect:none;fill:#63452c;fill-opacity:1;stroke:none;stroke-width:34.4328;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><path style="color:#bebebe;display:inline;overflow:visible;visibility:visible;opacity:.514644;fill:none;stroke:none;stroke-width:5.62203;marker:none;enable-background:accumulate" d="M574.594 382.115h239.873v239.873H574.594z"/><ellipse ry="10.397" rx="10.776" cy="498.044" cx="589.586" style="opacity:1;vector-effect:none;fill:#e01b24;fill-opacity:1;stroke:none;stroke-width:30.2472;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><path style="fill:none;stroke:#63452c;stroke-width:37.4802;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m737.553 519.543-15.38-54.384-59.969-20.146"/><circle r="19.677" cy="252.927" cx="715.145" style="opacity:1;vector-effect:none;fill:#63452c;fill-opacity:1;stroke:none;stroke-width:37.4802;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><path d="M629.409 446.887c-16.402 0-30.222 10.577-35.105 25.3h125.024c-4.882-14.723-18.702-25.3-35.104-25.3z" style="opacity:1;vector-effect:none;fill:#77767b;fill-opacity:1;stroke:none;stroke-width:37.4802;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><path style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#63452c;stroke-width:28.1101;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" d="M702.027 373.332s3.975 26.223 17.562 32.313c13.586 6.09 36.817 4.685 36.817 4.685"/><path d="m728.263 292.282-68.401 139.613" style="fill:none;stroke:#865e3c;stroke-width:93.7005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/><path d="m747.94 337.258-12.57 25.128" style="fill:#deddda;stroke:#deddda;stroke-width:37.4802;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/><path d="m719.224 527.564-5.016-55.378-64.653-14.992" style="fill:none;stroke:#865e3c;stroke-width:37.4802;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/><circle r="5.856" cy="293.921" cx="741.85" style="opacity:1;vector-effect:none;fill:#000;fill-opacity:1;stroke:none;stroke-width:31.2335;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><circle style="opacity:1;vector-effect:none;fill:#000;fill-opacity:1;stroke:none;stroke-width:28.7348;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" cx="759.418" cy="300.715" r="5.388"/><circle r="10.776" cy="328.356" cx="755.905" style="opacity:1;vector-effect:none;fill:#000;fill-opacity:1;stroke:none;stroke-width:57.4696;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><path d="M774.313 411.281c-20.053 0-27.778 16.256-27.778 36.31 0 8.604 12.695 43.415 13.847 46.607 9.685 26.825-12.857 50.859-38.316 50.859-25.362 0-41.705-1.312-67.05-2.185-5.205-.18-10.337 5.1-10.337 8.817 0 3.717 5.072 1.556 10.28 1.556h70.054c3.878.275 8.205 0 13.323 0 25.41 0 55.517-17.773 55.517-46.624 0-8.037-1.99-15.965-5.786-23.165l-18.7-30.774c-1.54-2.91.487-4.452 1.646-4.452l20.967.172-.165-37.15s-14.417.043-17.501.029z" style="opacity:1;vector-effect:none;fill:#c01c28;fill-opacity:1;stroke:none;stroke-width:37.4802;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><use height="100%" width="100%" transform="translate(-186.677)" xlink:href="#c"/><path style="fill:#5e5c64;fill-opacity:1;stroke:none;stroke-width:1.70187px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M619.462 533.936c-6.121 0-11.083 4.962-11.083 11.082.001 6.12 4.963 11.08 11.083 11.08l.787.012 37.798-.075c1.722-3.63 2.892-8.704 3.835-12.223 0 0-18.157-.015-31.441-.176a11.081 11.081 0 0 0-10.98-9.7z"/><rect style="opacity:1;vector-effect:none;fill:#c01c28;fill-opacity:1;stroke:none;stroke-width:16.9557;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" width="54.993" height="29.153" x="587.201" y="481.541" rx="9.069" ry="10.932"/><circle style="opacity:1;vector-effect:none;fill:#9a9996;fill-opacity:1;stroke:none;stroke-width:7.34974;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" cx="621.335" cy="544.809" r="7.471"/><path d="M630.328 481.714a54.512 73.475 35.72 0 0-52.37 27.997 54.512 73.475 35.72 0 0-19.542 34.156h117.982a54.512 73.475 35.72 0 0-11.892-49.452 54.512 73.475 35.72 0 0-34.178-12.701z" style="opacity:1;vector-effect:none;fill:#e01b24;fill-opacity:1;stroke:none;stroke-width:35.1475;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><rect ry="0" rx="0" y="411.281" x="774.176" height="37.012" width="17.803" style="opacity:1;vector-effect:none;fill:#fff;fill-opacity:1;stroke:none;stroke-width:37.4802;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><rect style="opacity:1;vector-effect:none;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:23.7797;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" width="17.803" height="14.899" x="774.176" y="433.394" rx="0" ry="0"/><path d="M689.846 379.423s7.95 24.235 21.537 30.325 37.48 5.348 37.48 5.348" style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#986a44;stroke-width:28.1101;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"/><path d="M718.325 354.571c5.477 6.725 25.192 16.671 31.369 15.49-8.063-.866-25.128-11.175-29.8-18.649z" style="fill:#c0bfbc;fill-opacity:1;stroke:none;stroke-width:1.87401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/><path d="m703.495 450.47-39.39-9.335L706 445.917z" style="fill:#63452c;fill-opacity:.346457;stroke:none;stroke-width:1.28802px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/></g><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 -12.62 227.065)"/></g><path style="fill:#1a724b;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M304.117-174.224 221.605 18.303H386.63z" transform="translate(-118.168 413.9) scale(.66774)"/><path style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#3d3846;stroke-width:160;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000;stop-opacity:1" d="m502.416-47.638 164.618-103.916a326.49 326.49 118.869 0 0 152.21-276.084v-739.737" transform="translate(27.86 739.504) scale(.66774)"/><g transform="translate(2.764 739.504) scale(.66774)"><path style="fill:none;stroke:#63452c;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M157.574 392.456V272.495"/><circle style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#1b754c;fill-opacity:1;stroke:none;stroke-width:160;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000;stop-opacity:1" cx="157.574" cy="246.53" r="73.723"/></g><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 104.052 534.82)"/></g><path style="fill:#208b5b;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M304.117-174.224 221.605 18.303H386.63z" transform="translate(-1.496 721.655) scale(.66774)"/><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 48.956 576.018)"/></g><path style="fill:#2cbd7a;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M243.645-112.527 161.133 80.001h165.023z" transform="translate(-16.212 721.655) scale(.66774)"/><path style="fill:#77767b;stroke:#c0bfbc;stroke-width:3.99999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M552.701-181.649v-105.989" transform="translate(27.86 739.504) scale(.66774)"/><path style="fill:#77767b;stroke:#deddda;stroke-width:3.99999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M544.496 220.043v-98.955" transform="translate(27.86 739.504) scale(.66774)"/><path style="font-variation-settings:normal;opacity:1;fill:#fff;fill-opacity:1;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000;stop-opacity:1" transform="rotate(-135 167.087 363.982) scale(.66774)" d="M-481.009 289.025h58.926v58.926h-58.926z"/><path style="font-variation-settings:normal;opacity:1;fill:#f6d32d;fill-opacity:1;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000;stop-opacity:1" transform="rotate(-135 167.087 363.982) scale(.66774)" d="M-470.403 299.631h37.713v37.713h-37.713z"/><path style="opacity:1;fill:#e01b24;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stop-color:#000;stop-opacity:1" d="m552.701-271.531-41.667-65.667h83.334z" transform="translate(27.86 739.504) scale(.66774)"/><path style="opacity:1;fill:#fff;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stop-color:#000;stop-opacity:1" d="m564.313-256.492 25.972 40.931 25.973-40.931z" transform="translate(2.764 691.375) scale(.66774)"/><path style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#fff;stroke-width:10;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:40,80;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000;stop-opacity:1" d="m622.912-123.702 44.122-27.852a326.49 326.49 118.869 0 0 152.21-276.084v-739.737" transform="translate(27.86 739.504) scale(.66774)"/><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 386.014 741.792)"/></g><path style="fill:#1a724b;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M304.117-174.224 221.605 18.303H386.63z" transform="translate(280.466 928.627) scale(.66774)"/><g transform="translate(111.222 965.81) scale(.66774)"><path style="fill:none;stroke:#63452c;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M157.574 392.456V272.495"/><circle style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#1b754c;fill-opacity:1;stroke:none;stroke-width:160;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000;stop-opacity:1" cx="157.574" cy="246.53" r="73.723"/></g><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 419.03 817.966)"/></g><path style="fill:#2cbd7a;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M243.645-112.527 161.133 80.001h165.023z" transform="translate(353.863 963.604) scale(.66774)"/><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 57.38 267.065)"/></g><path style="fill:#208b5b;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M304.117-174.224 221.605 18.303H386.63z" transform="translate(-48.168 453.9) scale(.66774)"/><g transform="translate(-30.315 308.044) scale(.66774)"><path style="fill:none;stroke:#63452c;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M157.574 392.456V272.495"/><circle style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#2cbd7a;fill-opacity:1;stroke:none;stroke-width:160;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000;stop-opacity:1" cx="157.574" cy="246.53" r="73.723"/></g><g><path style="fill:#5e5c64;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M127.67-785.337v31.247h7.037l8.226-31.203zM94.578-785.337v31.247H87.54l-8.227-31.203z" transform="matrix(1.06303 0 0 1.06303 100.221 1023.04)"/><path style="fill:#707a79;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M92.62-830.64v-31.247h7.036l8.227 31.203z" transform="matrix(1.06303 0 0 1.06303 100.221 1023.04)"/><path style="font-variation-settings:normal;fill:#9a9996;fill-opacity:1;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000" d="M104.62-830.64c-16.794 0-30.324 13.445-30.474 30.201h-.007V-795.25c-.23 5.452-2.673 10.414-6.977 10.414v30.746H74.2l4.697-17.832c1.692-6.311 9.071-12.064 15.672-12.914h48.485l8.107 30.746h7.037v-46.348h-.008c-.15-16.756-13.68-30.2-30.472-30.2z" transform="matrix(1.06303 0 0 1.06303 100.221 1023.04)"/><g transform="matrix(1.06303 0 0 1.06303 108.682 1040.36)"><path style="fill:#deddda;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M165.399-810.538c-.451.17-.939.421-1.445.766-.573.383-1.108.933-1.562 1.645-.464.726-.714 1.62-.71 2.628.052.99.344 1.844.826 2.523.474.67 1.015 1.175 1.582 1.519 1.145.655 2.112.93 2.807 1.037.677.177 1.1.197 1.21.274.058.024.09 0 .062-.06-.037-.07-.03-.188-.034-.38.038-.2.078-.284.146-.311.061-.018.05.025.007.113-.083.21-.535.397-1.13.686-.66.256-1.49.608-2.352 1.213-.444.321-.849.744-1.183 1.252-.35.53-.553 1.16-.596 1.832-.02.687.142 1.295.421 1.796.289.518.652.918 1.042 1.214a5.81 5.81 0 0 0 2.361 1.015c.792.177 1.436.25 1.968.416.684.225 1.172.5 1.482.919.748.96.34 1.835.34 1.835s.816-.636.34-2.165c-.216-.66-.716-1.351-1.497-1.88-.658-.459-1.218-.708-1.905-1.091-.517-.31-1.029-.635-1.24-1.009-.102-.167-.171-.33-.17-.46.005-.119.028-.216.086-.23.034-.018.116-.058.192-.105.08-.051.214-.098.375-.16.307-.138.833-.176 1.566-.343.718-.11 1.654-.375 2.71-1.029.514-.334.998-.814 1.407-1.452.41-.637.644-1.429.643-2.338-.042-.876-.294-1.662-.728-2.303a5.305 5.305 0 0 0-1.442-1.466 9.62 9.62 0 0 0-2.648-1.182c-.629-.246-1.025-.341-1.036-.455-.007-.038.017-.013.112.08.103.103.156.28.195.578-.01.305-.04.499-.134.625-.083.113-.103.16-.097.142.013-.016.095-.047.232-.108.22-.087.45-.152.816-.255.676-.189 1.83-.51 2.814-1.165a5.677 5.677 0 0 0 1.594-1.582c.46-.7.797-1.6.815-2.586a4.907 4.907 0 0 0-.725-2.62c-.43-.706-1-1.253-1.551-1.644-.972-.69-2.146-1.046-2.839-1.248-.683-.2-.929-.269-1.129-.402-.028-.02.073.092.073.092l.169.563-.124.578s-.104.139-.095.132c.095-.075.316-.181.907-.367.607-.192 1.6-.468 2.525-1.003a6.203 6.203 0 0 0 1.558-1.264 5.236 5.236 0 0 0 1.14-2.143c.22-.875.182-1.771-.023-2.548a5.659 5.659 0 0 0-1.001-2.01c-.832-1.074-1.928-1.732-2.818-2.137-.767-.35-1.567-.596-2.158-.821-.535-.204-.771-.335-.93-.474a.739.739 0 0 1-.247-.6.739.739 0 0 1 .324-.56.739.739 0 0 1 .644-.085c.148.05.313.182.41.354.016.029.703-.276 1.426-.49.724-.213 1.464-.364 1.464-.364s.688-.313 1.315-.765c.627-.45 1.17-1.066 1.017-1.335a6.805 6.805 0 0 0-3.696-3.079 6.847 6.847 0 0 0-5.908.771 6.847 6.847 0 0 0-2.983 5.157 6.847 6.847 0 0 0 2.276 5.505c.903.793 1.969 1.274 2.754 1.573.728.277 1.286.437 1.804.672.395.18.527.307.56.35l-.054-.13.007-.454.165-.341s.026-.02-.048.023c-.268.155-.62.253-1.328.476-.724.228-1.83.599-2.817 1.377a5.641 5.641 0 0 0-1.443 1.681 4.942 4.942 0 0 0-.618 2.56c.038.972.383 1.855.843 2.54a5.745 5.745 0 0 0 1.58 1.557c.974.65 2.11.976 2.78 1.171.662.193.866.254 1.045.381.015.011-.099-.126-.099-.126l-.151-.613.171-.602s.114-.127.093-.113c-.19.126-.42.19-1.101.38-.38.105-.906.255-1.406.452z"/><path style="fill:#77767b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m176.424-847.638-.04-9.858c-.013-3.239-.07-6.966-.804-10.567a29.354 29.354 0 0 0-1.24-4.355 24.968 24.968 0 0 0-3.467-6.375 23.575 23.575 0 0 0-9.394-7.38A25.038 25.038 0 0 0 150-888.127a26.56 26.56 0 0 0-15.19 5.979c-4.506 3.729-7.284 8.59-8.508 13.336-1.487 5.794-.66 11.162.923 15.143 3.54 8.884 9.198 11.052 9.198 11.052s-4.765-3.574-6.34-11.903c-.703-3.708-.514-8.253 1.374-12.573 1.535-3.528 4.238-6.877 7.764-9.103 3.182-2.02 7.111-3.168 11.015-3.06 2.536.077 5.076.648 7.25 1.764a15.12 15.12 0 0 1 5.412 4.623c.845 1.162 1.528 2.504 2.067 3.936.342.92.614 1.88.815 2.87.549 2.689.59 5.426.603 8.608l.04 9.858z"/><path style="fill:#c0bfbc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.1606px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m171.606-843.464-.046-11.441c-.015-3.76-.081-8.085-.934-12.265a34.069 34.069 0 0 0-1.439-5.054 28.979 28.979 0 0 0-4.023-7.398 27.361 27.361 0 0 0-10.903-8.565 29.06 29.06 0 0 0-13.32-2.27 30.825 30.825 0 0 0-17.63 6.94c-5.23 4.327-8.454 9.968-9.875 15.477-1.726 6.725-.766 12.955 1.071 17.575 4.109 10.311 10.675 12.827 10.675 12.827s-5.53-4.148-7.357-13.815c-.817-4.303-.597-9.578 1.593-14.592 1.782-4.095 4.92-7.982 9.012-10.565 3.693-2.344 8.253-3.677 12.783-3.55 2.944.089 5.891.75 8.415 2.046a17.548 17.548 0 0 1 6.281 5.366c.98 1.348 1.774 2.905 2.4 4.567.396 1.07.711 2.183.945 3.33.637 3.122.685 6.3.7 9.992l.046 11.442z"/><path style="fill:#c0bfbc;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stop-color:#000" d="M146.424-847.638h30v40H165Z"/><circle r="3.678" cy="-832.139" cx="-166.549" style="vector-effect:none;fill:#000;fill-opacity:1;stroke:none;stroke-width:19.6145;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" transform="scale(-1 1)"/><circle r="3.678" cy="-832.139" cx="-176.364" style="vector-effect:none;fill:#000;fill-opacity:1;stroke:none;stroke-width:19.6145;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" transform="scale(-1 1)"/></g><path style="font-variation-settings:normal;opacity:1;fill:#9a9996;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000;stop-opacity:1" d="M101.493-830.496a10.834 10.834 0 0 1-5.417 9.383 10.834 10.834 0 0 1-10.834 0 10.834 10.834 0 0 1-5.418-9.383" transform="matrix(1.06303 0 0 1.06303 100.221 1023.04)"/></g><g><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 277.38 -32.935)"/></g><path style="fill:#1a724b;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M304.117-174.224 221.605 18.303H386.63z" transform="translate(171.832 153.9) scale(.66774)"/></g><g><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 357.38 7.065)"/></g><path style="fill:#208b5b;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M304.117-174.224 221.605 18.303H386.63z" transform="translate(251.832 193.9) scale(.66774)"/></g><g transform="translate(339.685 108.044) scale(.66774)"><path style="fill:none;stroke:#63452c;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M157.574 392.456V272.495"/><circle style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#2cbd7a;fill-opacity:1;stroke:none;stroke-width:160;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000;stop-opacity:1" cx="157.574" cy="246.53" r="73.723"/></g><g><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 484.853 661.663)"/></g><path style="fill:#208b5b;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M304.117-174.224 221.605 18.303H386.63z" transform="translate(379.305 848.498) scale(.66774)"/></g></svg> \ No newline at end of file
diff --git a/panels/mouse/scroll-test.svg b/panels/mouse/scroll-test.svg
new file mode 100644
index 0000000..8614713
--- /dev/null
+++ b/panels/mouse/scroll-test.svg
@@ -0,0 +1 @@
+<svg width="640" height="1600" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="a"><stop style="stop-color:#eeeeec;stop-opacity:1" offset="0"/><stop style="stop-color:#fff;stop-opacity:1" offset="1"/></linearGradient><linearGradient id="b"><stop style="stop-color:#f57900;stop-opacity:1" offset="0"/><stop style="stop-color:#204a87;stop-opacity:1" offset="1"/></linearGradient></defs><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 69.359 479.233)"/></g><path style="fill:#1a724b;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M304.117-174.224 221.605 18.303H386.63z" transform="translate(-36.19 666.069) scale(.66774)"/><path style="fill:none;stroke:#3d3846;stroke-width:160;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M102.416-1167.375v321.533a100 100 45 0 0 100 100H345.68a100 100 45 0 1 100 100V399.245a100 100 135 0 1-100 100H185.963a100 100 135 0 0-100 100v247.592a100.119 100.119 45.034 0 0 100 100.12l521.828.62a94.738 94.738 45.034 0 1 94.625 94.737v190.048" transform="translate(27.86 739.504) scale(.66774)"/><path style="fill:none;stroke:#fff;stroke-width:10;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:40,80;stroke-dashoffset:0;stroke-opacity:1" d="M445.681-134.823v-511.02a100 100 45 0 0-100-100H202.416a100 100 45 0 1-100-100V-1167.374M445.68 61.57v337.676a100 100 135 0 1-100 100H185.963a100 100 135 0 0-100 100v247.592a100.119 100.119 45.034 0 0 100 100.12l526.705.625a89.855 89.855 45.034 0 1 89.748 89.855v274.925" transform="translate(27.86 739.504) scale(.66774)"/><g transform="translate(244.77 965.81) scale(.66774)"><path style="fill:none;stroke:#63452c;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M157.574 392.456V272.495"/><circle style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#1b754c;fill-opacity:1;stroke:none;stroke-width:160;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000;stop-opacity:1" cx="157.574" cy="246.53" r="73.723"/></g><g transform="translate(378.319 965.81) scale(.66774)"><path style="fill:none;stroke:#63452c;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M157.574 392.456V272.495"/><circle style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#1b754c;fill-opacity:1;stroke:none;stroke-width:160;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000;stop-opacity:1" cx="157.574" cy="246.53" r="73.723"/></g><g transform="matrix(-.41934 0 0 .41934 702.502 1098.89)"><circle style="opacity:1;vector-effect:none;fill:#63452c;fill-opacity:1;stroke:none;stroke-width:31.2335;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" cx="760.59" cy="270.262" r="16.398"/><g transform="translate(-2284.074 -1604.387) scale(7.08287)" id="c"><circle style="opacity:1;vector-effect:none;fill:#f6f5f4;fill-opacity:1;stroke:none;stroke-width:5.29167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" cx="436.72" cy="303.109" r="5.379"/><circle r="3.508" cy="303.109" cx="436.72" style="opacity:1;vector-effect:none;fill:#9a9996;fill-opacity:1;stroke:none;stroke-width:3.45109;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><circle style="opacity:1;vector-effect:none;fill:#3d3846;fill-opacity:1;stroke:none;stroke-width:1.28841;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" cx="436.72" cy="303.109" r="1.31"/></g><path d="m801.54 513.675 7.95 28.821" style="fill:#9a9996;stroke:#77767b;stroke-width:7.49604;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/><path d="m816.9 529.13-7.926 13.468" style="fill:#9a9996;stroke:#c0bfbc;stroke-width:11.2441;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/><path d="M813.57 498.034c-23.928-3.877-47.532 8.562-57.86 30.493l92.166.176c2.942.17 4.99-2.397 3.358-5.223a54.348 54.348 0 0 0-37.664-25.443z" style="opacity:1;vector-effect:none;fill:#e01b24;fill-opacity:1;stroke:none;stroke-width:37.4802;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><path d="M679.629 505.061c5.475 26.176 17.308 36.128 29.152 41.41l-45.716.663-2.32-35.447z" style="fill:#c01c28;fill-opacity:1;stroke:none;stroke-width:1.87401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/><path d="M633.3 452.04c-19.064 0-34.41 13.374-34.41 29.985h-2.247a9.378 9.378 0 0 0-9.4 9.4v6.968c0 5.208 4.192 9.4 9.4 9.4h15.87c.871 0 1.698-.154 2.497-.374 5.296 2.895 11.55 4.59 18.29 4.59h30.551c19.064 0 34.413-13.373 34.413-29.984 0-16.611-15.35-29.984-34.413-29.984z" style="opacity:1;vector-effect:none;fill:#63452c;fill-opacity:1;stroke:none;stroke-width:34.4328;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><path style="color:#bebebe;display:inline;overflow:visible;visibility:visible;opacity:.514644;fill:none;stroke:none;stroke-width:5.62203;marker:none;enable-background:accumulate" d="M574.594 382.115h239.873v239.873H574.594z"/><ellipse ry="10.397" rx="10.776" cy="498.044" cx="589.586" style="opacity:1;vector-effect:none;fill:#e01b24;fill-opacity:1;stroke:none;stroke-width:30.2472;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><path style="fill:none;stroke:#63452c;stroke-width:37.4802;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m737.553 519.543-15.38-54.384-59.969-20.146"/><circle r="19.677" cy="252.927" cx="715.145" style="opacity:1;vector-effect:none;fill:#63452c;fill-opacity:1;stroke:none;stroke-width:37.4802;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><path d="M629.409 446.887c-16.402 0-30.222 10.577-35.105 25.3h125.024c-4.882-14.723-18.702-25.3-35.104-25.3z" style="opacity:1;vector-effect:none;fill:#77767b;fill-opacity:1;stroke:none;stroke-width:37.4802;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><path style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#63452c;stroke-width:28.1101;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" d="M702.027 373.332s3.975 26.223 17.562 32.313c13.586 6.09 36.817 4.685 36.817 4.685"/><path d="m728.263 292.282-68.401 139.613" style="fill:none;stroke:#865e3c;stroke-width:93.7005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/><path d="m747.94 337.258-12.57 25.128" style="fill:#deddda;stroke:#deddda;stroke-width:37.4802;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/><path d="m719.224 527.564-5.016-55.378-64.653-14.992" style="fill:none;stroke:#865e3c;stroke-width:37.4802;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/><circle r="5.856" cy="293.921" cx="741.85" style="opacity:1;vector-effect:none;fill:#000;fill-opacity:1;stroke:none;stroke-width:31.2335;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><circle style="opacity:1;vector-effect:none;fill:#000;fill-opacity:1;stroke:none;stroke-width:28.7348;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" cx="759.418" cy="300.715" r="5.388"/><circle r="10.776" cy="328.356" cx="755.905" style="opacity:1;vector-effect:none;fill:#000;fill-opacity:1;stroke:none;stroke-width:57.4696;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><path d="M774.313 411.281c-20.053 0-27.778 16.256-27.778 36.31 0 8.604 12.695 43.415 13.847 46.607 9.685 26.825-12.857 50.859-38.316 50.859-25.362 0-41.705-1.312-67.05-2.185-5.205-.18-10.337 5.1-10.337 8.817 0 3.717 5.072 1.556 10.28 1.556h70.054c3.878.275 8.205 0 13.323 0 25.41 0 55.517-17.773 55.517-46.624 0-8.037-1.99-15.965-5.786-23.165l-18.7-30.774c-1.54-2.91.487-4.452 1.646-4.452l20.967.172-.165-37.15s-14.417.043-17.501.029z" style="opacity:1;vector-effect:none;fill:#c01c28;fill-opacity:1;stroke:none;stroke-width:37.4802;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><use height="100%" width="100%" transform="translate(-186.677)" xlink:href="#c"/><path style="fill:#5e5c64;fill-opacity:1;stroke:none;stroke-width:1.70187px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M619.462 533.936c-6.121 0-11.083 4.962-11.083 11.082.001 6.12 4.963 11.08 11.083 11.08l.787.012 37.798-.075c1.722-3.63 2.892-8.704 3.835-12.223 0 0-18.157-.015-31.441-.176a11.081 11.081 0 0 0-10.98-9.7z"/><rect style="opacity:1;vector-effect:none;fill:#c01c28;fill-opacity:1;stroke:none;stroke-width:16.9557;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" width="54.993" height="29.153" x="587.201" y="481.541" rx="9.069" ry="10.932"/><circle style="opacity:1;vector-effect:none;fill:#9a9996;fill-opacity:1;stroke:none;stroke-width:7.34974;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" cx="621.335" cy="544.809" r="7.471"/><path d="M630.328 481.714a54.512 73.475 35.72 0 0-52.37 27.997 54.512 73.475 35.72 0 0-19.542 34.156h117.982a54.512 73.475 35.72 0 0-11.892-49.452 54.512 73.475 35.72 0 0-34.178-12.701z" style="opacity:1;vector-effect:none;fill:#e01b24;fill-opacity:1;stroke:none;stroke-width:35.1475;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><rect ry="0" rx="0" y="411.281" x="774.176" height="37.012" width="17.803" style="opacity:1;vector-effect:none;fill:#fff;fill-opacity:1;stroke:none;stroke-width:37.4802;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/><rect style="opacity:1;vector-effect:none;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:23.7797;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" width="17.803" height="14.899" x="774.176" y="433.394" rx="0" ry="0"/><path d="M689.846 379.423s7.95 24.235 21.537 30.325 37.48 5.348 37.48 5.348" style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#986a44;stroke-width:28.1101;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"/><path d="M718.325 354.571c5.477 6.725 25.192 16.671 31.369 15.49-8.063-.866-25.128-11.175-29.8-18.649z" style="fill:#c0bfbc;fill-opacity:1;stroke:none;stroke-width:1.87401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/><path d="m703.495 450.47-39.39-9.335L706 445.917z" style="fill:#63452c;fill-opacity:.346457;stroke:none;stroke-width:1.28802px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/></g><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 -12.62 227.065)"/></g><path style="fill:#1a724b;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M304.117-174.224 221.605 18.303H386.63z" transform="translate(-118.168 413.9) scale(.66774)"/><path style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#3d3846;stroke-width:160;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000;stop-opacity:1" d="m502.416-47.638 164.618-103.916a326.49 326.49 118.869 0 0 152.21-276.084v-739.737" transform="translate(27.86 739.504) scale(.66774)"/><g transform="translate(2.764 739.504) scale(.66774)"><path style="fill:none;stroke:#63452c;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M157.574 392.456V272.495"/><circle style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#1b754c;fill-opacity:1;stroke:none;stroke-width:160;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000;stop-opacity:1" cx="157.574" cy="246.53" r="73.723"/></g><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 104.052 534.82)"/></g><path style="fill:#208b5b;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M304.117-174.224 221.605 18.303H386.63z" transform="translate(-1.496 721.655) scale(.66774)"/><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 48.956 576.018)"/></g><path style="fill:#2cbd7a;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M243.645-112.527 161.133 80.001h165.023z" transform="translate(-16.212 721.655) scale(.66774)"/><path style="fill:#77767b;stroke:#c0bfbc;stroke-width:3.99999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M552.701-181.649v-105.989" transform="translate(27.86 739.504) scale(.66774)"/><path style="fill:#77767b;stroke:#deddda;stroke-width:3.99999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M544.496 220.043v-98.955" transform="translate(27.86 739.504) scale(.66774)"/><path style="font-variation-settings:normal;opacity:1;fill:#fff;fill-opacity:1;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000;stop-opacity:1" transform="rotate(-135 167.087 363.982) scale(.66774)" d="M-481.009 289.025h58.926v58.926h-58.926z"/><path style="font-variation-settings:normal;opacity:1;fill:#f6d32d;fill-opacity:1;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000;stop-opacity:1" transform="rotate(-135 167.087 363.982) scale(.66774)" d="M-470.403 299.631h37.713v37.713h-37.713z"/><path style="opacity:1;fill:#e01b24;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stop-color:#000;stop-opacity:1" d="m552.701-271.531-41.667-65.667h83.334z" transform="translate(27.86 739.504) scale(.66774)"/><path style="opacity:1;fill:#fff;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stop-color:#000;stop-opacity:1" d="m564.313-256.492 25.972 40.931 25.973-40.931z" transform="translate(2.764 691.375) scale(.66774)"/><path style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#fff;stroke-width:10;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:40,80;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000;stop-opacity:1" d="m622.912-123.702 44.122-27.852a326.49 326.49 118.869 0 0 152.21-276.084v-739.737" transform="translate(27.86 739.504) scale(.66774)"/><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 386.014 741.792)"/></g><path style="fill:#1a724b;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M304.117-174.224 221.605 18.303H386.63z" transform="translate(280.466 928.627) scale(.66774)"/><g transform="translate(111.222 965.81) scale(.66774)"><path style="fill:none;stroke:#63452c;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M157.574 392.456V272.495"/><circle style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#1b754c;fill-opacity:1;stroke:none;stroke-width:160;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000;stop-opacity:1" cx="157.574" cy="246.53" r="73.723"/></g><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 484.853 661.663)"/></g><path style="fill:#208b5b;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M304.117-174.224 221.605 18.303H386.63z" transform="translate(379.305 848.498) scale(.66774)"/><g><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 419.03 817.966)"/></g><path style="fill:#2cbd7a;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M243.645-112.527 161.133 80.001h165.023z" transform="translate(353.863 963.604) scale(.66774)"/></g><g><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 57.38 267.065)"/></g><path style="fill:#208b5b;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M304.117-174.224 221.605 18.303H386.63z" transform="translate(-48.168 453.9) scale(.66774)"/></g><g transform="translate(-30.315 308.044) scale(.66774)"><path style="fill:none;stroke:#63452c;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M157.574 392.456V272.495"/><circle style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#2cbd7a;fill-opacity:1;stroke:none;stroke-width:160;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000;stop-opacity:1" cx="157.574" cy="246.53" r="73.723"/></g><g><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 277.38 -32.935)"/></g><path style="fill:#1a724b;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M304.117-174.224 221.605 18.303H386.63z" transform="translate(171.832 153.9) scale(.66774)"/></g><g><g style="stroke-width:1.09075"><path style="fill:none;stroke:#63452c;stroke-width:21.8151;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M159.305 392.456v-77.297" transform="matrix(.61218 0 0 .61218 357.38 7.065)"/></g><path style="fill:#208b5b;fill-opacity:1;stroke:none;stroke-width:.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M304.117-174.224 221.605 18.303H386.63z" transform="translate(251.832 193.9) scale(.66774)"/></g><g transform="translate(339.685 108.044) scale(.66774)"><path style="fill:none;stroke:#63452c;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M157.574 392.456V272.495"/><circle style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#2cbd7a;fill-opacity:1;stroke:none;stroke-width:160;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000;stop-opacity:1" cx="157.574" cy="246.53" r="73.723"/></g></svg> \ No newline at end of file
diff --git a/panels/mouse/test-gnome-mouse-test.c b/panels/mouse/test-gnome-mouse-test.c
new file mode 100644
index 0000000..ca37f90
--- /dev/null
+++ b/panels/mouse/test-gnome-mouse-test.c
@@ -0,0 +1,24 @@
+#include <config.h>
+#include <gtk/gtk.h>
+
+#include "cc-mouse-resources.h"
+#include "cc-mouse-test.h"
+
+int main (int argc, char **argv)
+{
+ GtkWidget *widget;
+ GtkWidget *window;
+
+ gtk_init ();
+
+ widget = cc_mouse_test_new ();
+
+ window = gtk_window_new ();
+ gtk_window_set_child (GTK_WINDOW (window), widget);
+ gtk_window_present (GTK_WINDOW (window));
+
+ while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
+ g_main_context_iteration (NULL, TRUE);
+
+ return 0;
+}