GCC Code Coverage Report


Directory: ./
File: panels/wacom/calibrator/calibrator-gui.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 161 0.0%
Functions: 0 25 0.0%
Branches: 0 47 0.0%

Line Branch Exec Source
1 /*
2 * Copyright © 2013 Red Hat, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 *
17 * Author: Carlos Garnacho <carlosg@gnome.org>
18 * (based on previous work by Joaquim Rocha, Tias Guns and Soren Hauberg)
19 */
20
21 #include "config.h"
22
23 #include <math.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <glib/gi18n.h>
27 #include <gdk/x11/gdkx.h>
28 #include <gtk/gtk.h>
29
30 #include "calibrator.h"
31 #include "calibrator-gui.h"
32 #include "cc-clock.h"
33
34 struct _CcCalibArea
35 {
36 GtkWindow parent_instance;
37
38 struct Calib calibrator;
39 XYinfo axis;
40 gboolean swap;
41 gboolean success;
42 GsdDevice *device;
43
44 GtkWidget *error_revealer;
45 GtkWidget *title_revealer;
46 GtkWidget *subtitle_revealer;
47 GtkWidget *clock;
48 GtkWidget *target1, *target2, *target3, *target4;
49 GtkWidget *stack;
50 GtkWidget *success_page;
51 GtkCssProvider *style_provider;
52
53 FinishCallback callback;
54 gpointer user_data;
55 };
56
57 G_DEFINE_TYPE (CcCalibArea, cc_calib_area, GTK_TYPE_WINDOW)
58
59 /* Timeout parameters */
60 #define MAX_TIME 15000 /* 15000 = 15 sec */
61 #define END_TIME 750 /* 750 = 0.75 sec */
62
63 static void
64 cc_calib_area_notify_finish (CcCalibArea *area)
65 {
66 gtk_widget_set_visible (GTK_WIDGET (area), FALSE);
67
68 (*area->callback) (area, area->user_data);
69 }
70
71 static gboolean
72 on_close_request (GtkWidget *widget,
73 CcCalibArea *area)
74 {
75 cc_calib_area_notify_finish (area);
76 return GDK_EVENT_PROPAGATE;
77 }
78
79 static gboolean
80 cc_calib_area_finish_idle_cb (CcCalibArea *area)
81 {
82 cc_calib_area_notify_finish (area);
83 return FALSE;
84 }
85
86 static void
87 set_success (CcCalibArea *area)
88 {
89 gtk_stack_set_visible_child (GTK_STACK (area->stack), area->success_page);
90 }
91
92 static void
93 set_calibration_status (CcCalibArea *area)
94 {
95 area->success = finish (&area->calibrator, &area->axis, &area->swap);
96
97 if (area->success)
98 {
99 set_success (area);
100 g_timeout_add (END_TIME,
101 (GSourceFunc) cc_calib_area_finish_idle_cb,
102 area);
103 }
104 else
105 {
106 g_idle_add ((GSourceFunc) cc_calib_area_finish_idle_cb, area);
107 }
108 }
109
110 static void
111 show_error_message (CcCalibArea *area)
112 {
113 gtk_revealer_set_reveal_child (GTK_REVEALER (area->error_revealer), TRUE);
114 }
115
116 static void
117 hide_error_message (CcCalibArea *area)
118 {
119 gtk_revealer_set_reveal_child (GTK_REVEALER (area->error_revealer), FALSE);
120 }
121
122 static void
123 set_active_target (CcCalibArea *area,
124 int n_target)
125 {
126 GtkWidget *targets[] = {
127 area->target1,
128 area->target2,
129 area->target3,
130 area->target4,
131 };
132 int i;
133
134 for (i = 0; i < G_N_ELEMENTS (targets); i++)
135 gtk_widget_set_sensitive (targets[i], i == n_target);
136 }
137
138 static void
139 on_gesture_press (CcCalibArea *area,
140 guint n_press,
141 gdouble x,
142 gdouble y,
143 GtkGestureClick *gesture)
144 {
145 GsdDeviceManager *manager = gsd_device_manager_get ();
146 gint num_clicks;
147 gboolean success;
148 GdkDevice *source;
149 GsdDevice *device;
150
151 if (area->success)
152 return;
153
154 source = gtk_gesture_get_device (GTK_GESTURE (gesture));
155
156 if (gdk_device_get_source (source) == GDK_SOURCE_TOUCHSCREEN)
157 return;
158
159 device = gsd_device_manager_lookup_gdk_device (manager, source);
160
161 /* Check matching device if a device was provided */
162 if (area->device && area->device != device)
163 {
164 g_debug ("Ignoring input from device %s",
165 gdk_device_get_name (source));
166 return;
167 }
168
169 /* Handle click */
170 /* FIXME: reset clock */
171 success = add_click(&area->calibrator,
172 (int) x,
173 (int) y);
174
175 num_clicks = area->calibrator.num_clicks;
176
177 if (!success && num_clicks == 0)
178 show_error_message (area);
179 else
180 hide_error_message (area);
181
182 /* Are we done yet? */
183 if (num_clicks >= 4)
184 {
185 set_calibration_status (area);
186 return;
187 }
188
189 set_active_target (area, num_clicks);
190 }
191
192 static gboolean
193 on_key_release (CcCalibArea *area,
194 guint keyval,
195 guint keycode,
196 GdkModifierType state)
197 {
198 if (area->success || keyval != GDK_KEY_Escape)
199 return GDK_EVENT_PROPAGATE;
200
201 cc_calib_area_notify_finish (area);
202 return GDK_EVENT_STOP;
203 }
204
205 static void
206 on_clock_finished (CcCalibArea *area)
207 {
208 set_calibration_status (area);
209 }
210
211 static void
212 on_title_revealed (CcCalibArea *area)
213 {
214 gtk_revealer_set_reveal_child (GTK_REVEALER (area->subtitle_revealer), TRUE);
215 }
216
217 static void
218 on_fullscreen (GtkWindow *window,
219 GParamSpec *pspec,
220 CcCalibArea *area)
221 {
222 if (!gtk_window_is_fullscreen (window))
223 return;
224
225 g_signal_connect_swapped (area->title_revealer,
226 "notify::child-revealed",
227 G_CALLBACK (on_title_revealed),
228 area);
229 gtk_revealer_set_reveal_child (GTK_REVEALER (area->title_revealer), TRUE);
230
231 set_active_target (area, 0);
232 }
233
234 static void
235 cc_calib_area_finalize (GObject *object)
236 {
237 CcCalibArea *area = CC_CALIB_AREA (object);
238
239 gtk_style_context_remove_provider_for_display (gtk_widget_get_display (GTK_WIDGET (area)),
240 GTK_STYLE_PROVIDER (area->style_provider));
241
242 G_OBJECT_CLASS (cc_calib_area_parent_class)->finalize (object);
243 }
244
245 static void
246 cc_calib_area_size_allocate (GtkWidget *widget,
247 int width,
248 int height,
249 int baseline)
250 {
251 CcCalibArea *calib_area = CC_CALIB_AREA (widget);
252
253 if (calib_area->calibrator.geometry.width != width ||
254 calib_area->calibrator.geometry.height != height)
255 {
256 calib_area->calibrator.geometry.width = width;
257 calib_area->calibrator.geometry.height = height;
258
259 /* reset calibration if already started */
260 reset (&calib_area->calibrator);
261 set_active_target (calib_area, 0);
262 }
263
264 GTK_WIDGET_CLASS (cc_calib_area_parent_class)->size_allocate (widget,
265 width,
266 height,
267 baseline);
268 }
269
270 static void
271 cc_calib_area_class_init (CcCalibAreaClass *klass)
272 {
273 GObjectClass *object_class = G_OBJECT_CLASS (klass);
274 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
275
276 object_class->finalize = cc_calib_area_finalize;
277
278 widget_class->size_allocate = cc_calib_area_size_allocate;
279
280 g_type_ensure (CC_TYPE_CLOCK);
281
282 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/wacom/calibrator/calibrator.ui");
283
284 gtk_widget_class_bind_template_child (widget_class, CcCalibArea, error_revealer);
285 gtk_widget_class_bind_template_child (widget_class, CcCalibArea, title_revealer);
286 gtk_widget_class_bind_template_child (widget_class, CcCalibArea, subtitle_revealer);
287 gtk_widget_class_bind_template_child (widget_class, CcCalibArea, clock);
288 gtk_widget_class_bind_template_child (widget_class, CcCalibArea, target1);
289 gtk_widget_class_bind_template_child (widget_class, CcCalibArea, target2);
290 gtk_widget_class_bind_template_child (widget_class, CcCalibArea, target3);
291 gtk_widget_class_bind_template_child (widget_class, CcCalibArea, target4);
292 gtk_widget_class_bind_template_child (widget_class, CcCalibArea, stack);
293 gtk_widget_class_bind_template_child (widget_class, CcCalibArea, success_page);
294 }
295
296 static void
297 cc_calib_area_init (CcCalibArea *calib_area)
298 {
299 GtkGesture *click;
300 GtkEventController *key;
301
302 gtk_widget_init_template (GTK_WIDGET (calib_area));
303
304 calib_area->style_provider = gtk_css_provider_new ();
305 gtk_css_provider_load_from_resource (calib_area->style_provider, "/org/gnome/control-center/wacom/calibrator/calibrator.css");
306 gtk_style_context_add_provider_for_display (gtk_widget_get_display (GTK_WIDGET (calib_area)),
307 GTK_STYLE_PROVIDER (calib_area->style_provider),
308 GTK_STYLE_PROVIDER_PRIORITY_USER);
309
310 cc_clock_set_duration (CC_CLOCK (calib_area->clock), MAX_TIME);
311 g_signal_connect_swapped (calib_area->clock, "finished",
312 G_CALLBACK (on_clock_finished), calib_area);
313
314 #ifndef FAKE_AREA
315 /* No cursor */
316 gtk_widget_realize (GTK_WIDGET (calib_area));
317 gtk_widget_set_cursor_from_name (GTK_WIDGET (calib_area), "blank");
318
319 gtk_widget_set_can_focus (GTK_WIDGET (calib_area), TRUE);
320 #endif /* FAKE_AREA */
321
322 g_signal_connect (calib_area,
323 "close-request",
324 G_CALLBACK (on_close_request),
325 calib_area);
326 g_signal_connect (calib_area,
327 "notify::fullscreened",
328 G_CALLBACK (on_fullscreen),
329 calib_area);
330
331 click = gtk_gesture_click_new ();
332 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (click), GDK_BUTTON_PRIMARY);
333 g_signal_connect_swapped (click, "pressed",
334 G_CALLBACK (on_gesture_press), calib_area);
335 gtk_widget_add_controller (GTK_WIDGET (calib_area),
336 GTK_EVENT_CONTROLLER (click));
337
338 key = gtk_event_controller_key_new ();
339 g_signal_connect_swapped (key, "key-released",
340 G_CALLBACK (on_key_release), calib_area);
341 gtk_widget_add_controller (GTK_WIDGET (calib_area), key);
342 }
343
344 /**
345 * Creates the windows and other objects required to do calibration
346 * under GTK. When the window is closed (timed out, calibration finished
347 * or user cancellation), callback will be called, where you should call
348 * cc_calib_area_finish().
349 */
350 CcCalibArea *
351 cc_calib_area_new (GdkDisplay *display,
352 GdkMonitor *monitor,
353 GsdDevice *device,
354 FinishCallback callback,
355 gpointer user_data,
356 int threshold_doubleclick,
357 int threshold_misclick)
358 {
359 CcCalibArea *calib_area;
360
361 g_return_val_if_fail (callback, NULL);
362
363 calib_area = g_object_new (CC_TYPE_CALIB_AREA, NULL);
364 calib_area->callback = callback;
365 calib_area->user_data = user_data;
366 calib_area->device = device;
367 calib_area->calibrator.threshold_doubleclick = threshold_doubleclick;
368 calib_area->calibrator.threshold_misclick = threshold_misclick;
369
370 /* Move to correct screen */
371 if (monitor)
372 gtk_window_fullscreen_on_monitor (GTK_WINDOW (calib_area), monitor);
373 else
374 gtk_window_fullscreen (GTK_WINDOW (calib_area));
375
376 gtk_window_present(GTK_WINDOW (calib_area));
377
378 return calib_area;
379 }
380
381 /* Finishes the calibration. Note that CalibArea
382 * needs to be destroyed with Cccalib_area_free() afterwards */
383 gboolean
384 cc_calib_area_finish (CcCalibArea *area)
385 {
386 g_return_val_if_fail (area != NULL, FALSE);
387
388 if (area->success)
389 g_debug ("Final calibration: %f, %f, %f, %f\n",
390 area->axis.x_min,
391 area->axis.y_min,
392 area->axis.x_max,
393 area->axis.y_max);
394 else
395 g_debug ("Calibration was aborted or timed out");
396
397 return area->success;
398 }
399
400 void
401 cc_calib_area_free (CcCalibArea *area)
402 {
403 gtk_window_destroy (GTK_WINDOW (area));
404 }
405
406 void
407 cc_calib_area_get_axis (CcCalibArea *area,
408 XYinfo *new_axis,
409 gboolean *swap_xy)
410 {
411 g_return_if_fail (area != NULL);
412
413 *new_axis = area->axis;
414 *swap_xy = area->swap;
415 }
416
417 void
418 cc_calib_area_get_padding (CcCalibArea *area,
419 XYinfo *padding)
420 {
421 g_return_if_fail (area != NULL);
422
423 /* min/max values are monitor coordinates scaled to be between
424 * 0 and 1, padding starts at 0 on "the edge", and positive
425 * values grow towards the center of the rectangle.
426 */
427 padding->x_min = area->axis.x_min;
428 padding->y_min = area->axis.y_min;
429 padding->x_max = 1 - area->axis.x_max;
430 padding->y_max = 1 - area->axis.y_max;
431 }
432