diff options
Diffstat (limited to 'panels/wacom/calibrator/calibrator-gui.c')
-rw-r--r-- | panels/wacom/calibrator/calibrator-gui.c | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/panels/wacom/calibrator/calibrator-gui.c b/panels/wacom/calibrator/calibrator-gui.c new file mode 100644 index 0000000..012291f --- /dev/null +++ b/panels/wacom/calibrator/calibrator-gui.c @@ -0,0 +1,461 @@ +/* + * Copyright © 2013 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: Carlos Garnacho <carlosg@gnome.org> + * (based on previous work by Joaquim Rocha, Tias Guns and Soren Hauberg) + */ + +#include "config.h" + +#include <math.h> +#include <stdlib.h> +#include <stdio.h> +#include <glib/gi18n.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> + +#include "calibrator.h" +#include "calibrator-gui.h" +#include "cc-clock.h" + +struct CalibArea +{ + struct Calib calibrator; + XYinfo axis; + gboolean swap; + gboolean success; + GdkDevice *device; + + double X[4], Y[4]; + int display_width, display_height; + + GtkWidget *window; + GtkBuilder *builder; + GtkWidget *error_revealer; + GtkWidget *clock; + GtkCssProvider *style_provider; + + FinishCallback callback; + gpointer user_data; +}; + +/* Timeout parameters */ +#define MAX_TIME 15000 /* 15000 = 15 sec */ +#define END_TIME 750 /* 750 = 0.75 sec */ + +static void +set_display_size (CalibArea *calib_area, + int width, + int height) +{ + int delta_x; + int delta_y; + + calib_area->display_width = width; + calib_area->display_height = height; + + /* Compute absolute circle centers */ + delta_x = calib_area->display_width/NUM_BLOCKS; + delta_y = calib_area->display_height/NUM_BLOCKS; + + calib_area->X[UL] = delta_x; + calib_area->Y[UL] = delta_y; + + calib_area->X[UR] = calib_area->display_width - delta_x - 1; + calib_area->Y[UR] = delta_y; + + calib_area->X[LL] = delta_x; + calib_area->Y[LL] = calib_area->display_height - delta_y - 1; + + calib_area->X[LR] = calib_area->display_width - delta_x - 1; + calib_area->Y[LR] = calib_area->display_height - delta_y - 1; + + /* reset calibration if already started */ + reset (&calib_area->calibrator); +} + +static void +calib_area_notify_finish (CalibArea *area) +{ + gtk_widget_hide (area->window); + + (*area->callback) (area, area->user_data); +} + +static gboolean +on_delete_event (GtkWidget *widget, + GdkEvent *event, + CalibArea *area) +{ + calib_area_notify_finish (area); + return TRUE; +} + +static gboolean +calib_area_finish_idle_cb (CalibArea *area) +{ + calib_area_notify_finish (area); + return FALSE; +} + +static void +set_success (CalibArea *area) +{ + GtkWidget *stack; + + stack = GTK_WIDGET (gtk_builder_get_object (area->builder, "stack")); + gtk_stack_set_visible_child_name (GTK_STACK (stack), "page1"); +} + +static void +set_calibration_status (CalibArea *area) +{ + area->success = finish (&area->calibrator, &area->axis, &area->swap); + + if (area->success) + { + set_success (area); + g_timeout_add (END_TIME, + (GSourceFunc) calib_area_finish_idle_cb, + area); + } + else + { + g_idle_add ((GSourceFunc) calib_area_finish_idle_cb, area); + } +} + +static void +show_error_message (CalibArea *area) +{ + gtk_revealer_set_reveal_child (GTK_REVEALER (area->error_revealer), TRUE); +} + +static void +hide_error_message (CalibArea *area) +{ + gtk_revealer_set_reveal_child (GTK_REVEALER (area->error_revealer), FALSE); +} + +static void +set_active_target (CalibArea *area, + int n_target) +{ + GtkWidget *targets[] = { + GTK_WIDGET (gtk_builder_get_object (area->builder, "target1")), + GTK_WIDGET (gtk_builder_get_object (area->builder, "target2")), + GTK_WIDGET (gtk_builder_get_object (area->builder, "target3")), + GTK_WIDGET (gtk_builder_get_object (area->builder, "target4")), + }; + int i; + + for (i = 0; i < G_N_ELEMENTS (targets); i++) + gtk_widget_set_sensitive (targets[i], i == n_target); +} + +static void +on_gesture_press (GtkGestureMultiPress *gesture, + guint n_press, + gdouble x, + gdouble y, + CalibArea *area) +{ + gint num_clicks; + gboolean success; + GdkDevice *source; + GdkEvent *event; + + if (area->success) + return; + + event = gtk_get_current_event (); + source = gdk_event_get_source_device ((GdkEvent *) event); + gdk_event_free (event); + + /* Check matching device if a device was provided */ + if (area->device && area->device != source) + { + g_debug ("Ignoring input from device %s", + gdk_device_get_name (source)); + return; + } + + /* Handle click */ + /* FIXME: reset clock */ + success = add_click(&area->calibrator, + (int) x, + (int) y); + + num_clicks = area->calibrator.num_clicks; + + if (!success && num_clicks == 0) + show_error_message (area); + else + hide_error_message (area); + + /* Are we done yet? */ + if (num_clicks >= 4) + { + set_calibration_status (area); + return; + } + + set_active_target (area, num_clicks); +} + +static gboolean +on_key_release_event (GtkWidget *widget, + GdkEventKey *event, + CalibArea *area) +{ + if (area->success || + event->keyval != GDK_KEY_Escape) + return GDK_EVENT_PROPAGATE; + + calib_area_notify_finish (area); + return GDK_EVENT_STOP; +} + +static gboolean +on_focus_out_event (GtkWidget *widget, + GdkEvent *event, + CalibArea *area) +{ + if (area->success) + return FALSE; + + /* If the calibrator window loses focus, simply bail out... */ + calib_area_notify_finish (area); + + return FALSE; +} + +static void +on_clock_finished (CcClock *clock, + CalibArea *area) +{ + set_calibration_status (area); +} + +static void +on_title_revealed (CalibArea *area) +{ + GtkWidget *revealer; + + revealer = GTK_WIDGET (gtk_builder_get_object (area->builder, "subtitle_revealer")); + gtk_revealer_set_reveal_child (GTK_REVEALER (revealer), TRUE); +} + +static gboolean +on_fullscreen (GtkWindow *window, + GdkEventWindowState *event, + CalibArea *area) +{ + GtkWidget *revealer; + + if ((event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) == 0) + return FALSE; + + revealer = GTK_WIDGET (gtk_builder_get_object (area->builder, "title_revealer")); + g_signal_connect_swapped (revealer, "notify::child-revealed", + G_CALLBACK (on_title_revealed), + area); + gtk_revealer_set_reveal_child (GTK_REVEALER (revealer), TRUE); + + set_active_target (area, 0); + + return FALSE; +} + +static void +on_size_allocate (GtkWidget *widget, + GtkAllocation *allocation, + CalibArea *area) +{ + set_display_size (area, allocation->width, allocation->height); +} + +/** + * Creates the windows and other objects required to do calibration + * under GTK. When the window is closed (timed out, calibration finished + * or user cancellation), callback will be called, where you should call + * calib_area_finish(). + */ +CalibArea * +calib_area_new (GdkScreen *screen, + int n_monitor, + GdkDevice *device, + FinishCallback callback, + gpointer user_data, + int threshold_doubleclick, + int threshold_misclick) +{ + CalibArea *calib_area; + GdkRectangle rect; + GdkVisual *visual; + GdkMonitor *monitor; +#ifndef FAKE_AREA + GdkWindow *window; + g_autoptr(GdkCursor) cursor = NULL; +#endif /* FAKE_AREA */ + GtkGesture *press; + + g_return_val_if_fail (callback, NULL); + + g_type_ensure (CC_TYPE_CLOCK); + + calib_area = g_new0 (CalibArea, 1); + calib_area->callback = callback; + calib_area->user_data = user_data; + calib_area->device = device; + calib_area->calibrator.threshold_doubleclick = threshold_doubleclick; + calib_area->calibrator.threshold_misclick = threshold_misclick; + + calib_area->builder = gtk_builder_new_from_resource ("/org/gnome/control-center/wacom/calibrator/calibrator.ui"); + calib_area->window = GTK_WIDGET (gtk_builder_get_object (calib_area->builder, "window")); + calib_area->error_revealer = GTK_WIDGET (gtk_builder_get_object (calib_area->builder, "error_revealer")); + calib_area->clock = GTK_WIDGET (gtk_builder_get_object (calib_area->builder, "clock")); + calib_area->style_provider = gtk_css_provider_new (); + gtk_css_provider_load_from_resource (calib_area->style_provider, "/org/gnome/control-center/wacom/calibrator/calibrator.css"); + gtk_style_context_add_provider_for_screen (gtk_widget_get_screen (calib_area->window), + GTK_STYLE_PROVIDER (calib_area->style_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + + cc_clock_set_duration (CC_CLOCK (calib_area->clock), MAX_TIME); + g_signal_connect (calib_area->clock, "finished", + G_CALLBACK (on_clock_finished), calib_area); + +#ifndef FAKE_AREA + /* No cursor */ + gtk_widget_realize (calib_area->window); + window = gtk_widget_get_window (calib_area->window); + cursor = gdk_cursor_new_for_display (gdk_display_get_default (), GDK_BLANK_CURSOR); + gdk_window_set_cursor (window, cursor); + + gtk_widget_set_can_focus (calib_area->window, TRUE); + gtk_window_set_keep_above (GTK_WINDOW (calib_area->window), TRUE); +#endif /* FAKE_AREA */ + + /* Move to correct screen */ + if (screen == NULL) + screen = gdk_screen_get_default (); + monitor = gdk_display_get_monitor (gdk_screen_get_display (screen), n_monitor); + gdk_monitor_get_geometry (monitor, &rect); + + calib_area->calibrator.geometry = rect; + + g_signal_connect (calib_area->window, + "key-release-event", + G_CALLBACK (on_key_release_event), + calib_area); + g_signal_connect (calib_area->window, + "delete-event", + G_CALLBACK (on_delete_event), + calib_area); + g_signal_connect (calib_area->window, + "focus-out-event", + G_CALLBACK(on_focus_out_event), + calib_area); + g_signal_connect (calib_area->window, + "window-state-event", + G_CALLBACK (on_fullscreen), + calib_area); + g_signal_connect (calib_area->window, + "size-allocate", + G_CALLBACK (on_size_allocate), + calib_area); + + press = gtk_gesture_multi_press_new (calib_area->window); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (press), GDK_BUTTON_PRIMARY); + g_signal_connect (press, "pressed", + G_CALLBACK (on_gesture_press), calib_area); + + gtk_window_fullscreen_on_monitor (GTK_WINDOW (calib_area->window), screen, n_monitor); + + visual = gdk_screen_get_rgba_visual (screen); + if (visual != NULL) + gtk_widget_set_visual (GTK_WIDGET (calib_area->window), visual); + + gtk_widget_show (calib_area->window); + + return calib_area; +} + +/* Finishes the calibration. Note that CalibArea + * needs to be destroyed with calib_area_free() afterwards */ +gboolean +calib_area_finish (CalibArea *area) +{ + g_return_val_if_fail (area != NULL, FALSE); + + if (area->success) + g_debug ("Final calibration: %f, %f, %f, %f\n", + area->axis.x_min, + area->axis.y_min, + area->axis.x_max, + area->axis.y_max); + else + g_debug ("Calibration was aborted or timed out"); + + return area->success; +} + +void +calib_area_free (CalibArea *area) +{ + g_return_if_fail (area != NULL); + + gtk_style_context_remove_provider_for_screen (gtk_widget_get_screen (area->window), + GTK_STYLE_PROVIDER (area->style_provider)); + gtk_widget_destroy (area->window); + g_free (area); +} + +void +calib_area_get_display_size (CalibArea *area, gint *width, gint *height) +{ + g_return_if_fail (area != NULL); + + *width = area->display_width; + *height = area->display_height; +} + +void +calib_area_get_axis (CalibArea *area, + XYinfo *new_axis, + gboolean *swap_xy) +{ + g_return_if_fail (area != NULL); + + *new_axis = area->axis; + *swap_xy = area->swap; +} + +void +calib_area_get_padding (CalibArea *area, + XYinfo *padding) +{ + g_return_if_fail (area != NULL); + + /* min/max values are monitor coordinates scaled to be between + * 0 and 1, padding starts at 0 on "the edge", and positive + * values grow towards the center of the rectangle. + */ + padding->x_min = area->axis.x_min; + padding->y_min = area->axis.y_min; + padding->x_max = 1 - area->axis.x_max; + padding->y_max = 1 - area->axis.y_max; +} |