diff --git a/configure.ac b/configure.ac index db54ee09e..83e27a846 100644 --- a/configure.ac +++ b/configure.ac @@ -292,16 +292,20 @@ case $host_os in PKG_CHECK_MODULES(WACOM_PANEL, $COMMON_MODULES gnome-settings-daemon >= $GSD_REQUIRED_VERSION xi >= 1.2 x11 libwacom >= $LIBWACOM_REQUIRED_VERSION - gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION) - AC_DEFINE(BUILD_WACOM, 1, [Define to 1 to build the Wacom panel]) - have_wacom=yes + gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION + clutter-gtk-1.0 + clutter-1.0 >= $CLUTTER_REQUIRED_VERSION, [have_wacom=yes], [have_wacom=no]) + if test x${have_wacom} = xyes; then + AC_DEFINE(BUILD_WACOM, 1, [Define to 1 to build the Wacom panel]) + AC_DEFINE(HAVE_WACOM, 1, [Define to 1 is Wacom is supportted]) + fi fi ;; *) have_wacom=no ;; esac -AM_CONDITIONAL(BUILD_WACOM, [test x"$have_wacom" = x"yes"]) +AM_CONDITIONAL(BUILD_WACOM, [test x${have_wacom} = xyes]) # Kerberos kerberos support AC_PATH_PROG(KRB5_CONFIG, krb5-config, no) diff --git a/panels/wacom/calibrator/Makefile.am b/panels/wacom/calibrator/Makefile.am index 74d438502..72d755207 100644 --- a/panels/wacom/calibrator/Makefile.am +++ b/panels/wacom/calibrator/Makefile.am @@ -13,8 +13,12 @@ noinst_LTLIBRARIES = libwacom-calibrator.la libwacom-calibrator-test.la libwacom_calibrator_la_SOURCES = \ calibrator.c \ calibrator.h \ - gui_gtk.c \ - gui_gtk.h + calibrator-gui.c \ + calibrator-gui.h \ + cc-clock-actor.c \ + cc-clock-actor.h \ + cc-target-actor.c \ + cc-target-actor.h libwacom_calibrator_la_LIBADD = $(PANEL_LIBS) $(WACOM_PANEL_LIBS) libwacom_calibrator_la_LDFLAGS = $(PANEL_LDFLAGS) @@ -31,8 +35,12 @@ test_calibrator_SOURCES = \ main.c \ calibrator.c \ calibrator.h \ - gui_gtk.c \ - gui_gtk.h + calibrator-gui.c \ + calibrator-gui.h \ + cc-clock-actor.c \ + cc-clock-actor.h \ + cc-target-actor.c \ + cc-target-actor.h test_calibrator_CPPFLAGS = $(INCLUDES) test_calibrator_LDADD = $(PANEL_LIBS) $(WACOM_PANEL_LIBS) diff --git a/panels/wacom/calibrator/calibrator-gui.c b/panels/wacom/calibrator/calibrator-gui.c new file mode 100644 index 000000000..eab8ef21e --- /dev/null +++ b/panels/wacom/calibrator/calibrator-gui.c @@ -0,0 +1,787 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Author: Joaquim Rocha + * (based on previous work by Tias Guns and Soren Hauberg) + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "calibrator.h" +#include "calibrator-gui.h" +#include "cc-clock-actor.h" +#include "cc-target-actor.h" + +struct CalibArea +{ + struct Calib calibrator; + XYinfo axis; + gboolean swap; + gboolean success; + int device_id; + + double X[4], Y[4]; + int display_width, display_height; + + GtkWidget *window; + ClutterActor *stage; + ClutterActor *action_layer; + ClutterActor *clock; + ClutterActor *target; + ClutterActor *success_image; + ClutterActor *text_title_holder; + ClutterActor *helper_text_title; + ClutterActor *text_body_holder; + ClutterActor *helper_text_body; + ClutterActor *error_text; + ClutterTransition *clock_timeline; + GdkPixbuf *icon_success; + + FinishCallback callback; + gpointer user_data; +}; + +#define TARGET_SHOW_ANIMATION_DURATION 500 +#define TARGET_HIDE_ANIMATION_DURATION 200 + +#define COLOR_GRAY 127 + +/* Window parameters */ +#define WINDOW_OPACITY 0.9 + +/* Timeout parameters */ +#define MAX_TIME 15000 /* 5000 = 5 sec */ +#define END_TIME 750 /* 750 = 0.75 sec */ + +/* Text printed on screen */ +#define HELP_TEXT_TITLE N_("Screen Calibration") +#define HELP_TEXT_MAIN N_("Please tap the target markers as they " \ + "appear on screen to calibrate the tablet.") +#define HELP_TEXT_ANIMATION_DURATION 300 + +#define ERROR_MESSAGE N_("Mis-click detected, restarting...") +#define ERROR_MESSAGE_ANIMATION_DURATION 500 + +#define ICON_SUCCESS "emblem-ok-symbolic" +#define ICON_SIZE 300 + +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 +resize_display(CalibArea *calib_area) +{ + gfloat width, height; + + clutter_actor_get_size (calib_area->stage, &width, &height); + if (calib_area->display_width != width || + calib_area->display_height != height) + { + gint i = calib_area->calibrator.num_clicks; + set_display_size(calib_area, width, height); + cc_target_actor_move (CC_TARGET_ACTOR (calib_area->target), + calib_area->X[i], + calib_area->Y[i]); + } +} + +static void +on_allocation_changed (ClutterActor *actor, + ClutterActorBox *box, + ClutterAllocationFlags flags, + CalibArea *area) +{ + resize_display (area); +} + +static gboolean +on_delete_event (GtkWidget *widget, + GdkEvent *event, + CalibArea *area) +{ + clutter_timeline_stop (CLUTTER_TIMELINE (area->clock_timeline)); + + gtk_widget_hide (area->window); + + (*area->callback) (area, area->user_data); + + return TRUE; +} + +static gboolean +draw_success_end_wait_callback (CalibArea *area) +{ + on_delete_event (NULL, NULL, area); + + return FALSE; +} + +static void +set_success (CalibArea *area) +{ + ClutterImage *image; + GdkPixbuf *icon = area->icon_success; + + if (icon == NULL) + return; + + image = CLUTTER_IMAGE (clutter_actor_get_content (area->success_image)); + clutter_image_set_data (image, + gdk_pixbuf_get_pixels (icon), + gdk_pixbuf_get_has_alpha (icon) + ? COGL_PIXEL_FORMAT_RGBA_8888 + : COGL_PIXEL_FORMAT_RGB_888, + gdk_pixbuf_get_width (icon), + gdk_pixbuf_get_height (icon), + gdk_pixbuf_get_rowstride (icon), + NULL); + clutter_actor_set_size (area->success_image, + gdk_pixbuf_get_width (icon), + gdk_pixbuf_get_height (icon)); + + clutter_actor_show (area->success_image); + clutter_actor_hide (area->action_layer); +} + +static void +set_calibration_status (CalibArea *area) +{ + GtkIconTheme *icon_theme; + GtkIconInfo *icon_info; + GdkRGBA white; + + icon_theme = gtk_icon_theme_get_default (); + icon_info = gtk_icon_theme_lookup_icon (icon_theme, + ICON_SUCCESS, + ICON_SIZE, + GTK_ICON_LOOKUP_USE_BUILTIN); + if (icon_info == NULL) + { + g_warning ("Failed to find icon \"%s\"", ICON_SUCCESS); + goto out; + } + + gdk_rgba_parse (&white, "White"); + area->icon_success = gtk_icon_info_load_symbolic (icon_info, + &white, + NULL, + NULL, + NULL, + NULL, + NULL); + g_object_unref (icon_info); + + if (!area->icon_success) + g_warning ("Failed to load icon \"%s\"", ICON_SUCCESS); + + out: + area->success = finish (&area->calibrator, &area->axis, &area->swap); + if (area->success && area->icon_success) + { + set_success (area); + g_timeout_add (END_TIME, + (GSourceFunc) draw_success_end_wait_callback, + area); + } + else + { + on_delete_event (NULL, NULL, area); + } +} + +static ClutterTransition * +get_error_message_transition (CalibArea *area) +{ + ClutterTransition *transition; + + clutter_actor_show (area->error_text); + transition = clutter_property_transition_new ("opacity"); + clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition), + CLUTTER_EASE_OUT); + clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), + ERROR_MESSAGE_ANIMATION_DURATION); + clutter_transition_set_animatable (transition, + CLUTTER_ANIMATABLE (area->error_text)); + clutter_transition_set_from (transition, G_TYPE_UINT, 0); + clutter_transition_set_to (transition, G_TYPE_UINT, 255); + + return transition; +} + +static void +show_error_message (CalibArea *area) +{ + ClutterTransition *transition; + clutter_actor_show (area->error_text); + transition = get_error_message_transition (area); + clutter_timeline_start (CLUTTER_TIMELINE (transition)); +} + +static void +on_error_message_transparent (ClutterTimeline *timeline, + CalibArea *area) +{ + clutter_actor_hide (area->error_text); +} + +static void +hide_error_message (CalibArea *area) +{ + ClutterTransition *transition; + transition = get_error_message_transition (area); + clutter_transition_set_from (transition, G_TYPE_UINT, 255); + clutter_transition_set_to (transition, G_TYPE_UINT, 0); + g_signal_connect (CLUTTER_TIMELINE (transition), + "completed", + G_CALLBACK (on_error_message_transparent), + area); + clutter_timeline_start (CLUTTER_TIMELINE (transition)); +} + +static gboolean +on_button_press_event(ClutterActor *actor, + ClutterButtonEvent *event, + CalibArea *area) +{ + gint num_clicks; + gboolean success; + + if (area->success) + return FALSE; + + /* Check matching device ID if a device ID was provided */ + if (area->device_id > -1) + { + GdkDevice *device; + + device = gdk_event_get_source_device ((GdkEvent *) event); + if (device != NULL && gdk_x11_device_get_id (device) != area->device_id) + return FALSE; + } + + /* Handle click */ + clutter_timeline_stop (CLUTTER_TIMELINE (area->clock_timeline)); + clutter_timeline_start (CLUTTER_TIMELINE (area->clock_timeline)); + success = add_click(&area->calibrator, + (int) event->x, + (int) event->y); + + num_clicks = area->calibrator.num_clicks; + + if (!success && num_clicks == 0) + show_error_message (area); + else + { + gboolean visible; + g_object_get (area->error_text, "visible", &visible, NULL); + + if (visible) + hide_error_message (area); + } + + /* Are we done yet? */ + if (num_clicks >= 4) + { + set_calibration_status (area); + return FALSE; + } + + cc_target_actor_move (CC_TARGET_ACTOR (area->target), + area->X[num_clicks], + area->Y[num_clicks]); + + + return FALSE; +} + +static gboolean +on_key_release_event(ClutterActor *actor, + ClutterKeyEvent *event, + CalibArea *area) +{ + if (area->success || + event->type != CLUTTER_KEY_RELEASE || + event->keyval != CLUTTER_KEY_Escape) + { + return FALSE; + } + + on_delete_event (area->window, NULL, area); + + return FALSE; +} + +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... */ + on_delete_event (widget, NULL, area); + + return FALSE; +} + +static void +on_timeout (ClutterTimeline *timeline, + CalibArea *area) +{ + set_calibration_status (area); +} + +static void +show_helper_text_body (CalibArea *area) +{ + ClutterTransition *transition; + gfloat height; + + height = clutter_actor_get_height (area->helper_text_body); + clutter_actor_show (area->helper_text_body); + + transition = clutter_property_transition_new ("y"); + clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition), + CLUTTER_EASE_OUT); + clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), + HELP_TEXT_ANIMATION_DURATION); + clutter_transition_set_animatable (transition, + CLUTTER_ANIMATABLE (area->helper_text_body)); + clutter_transition_set_from (transition, G_TYPE_FLOAT, -height); + clutter_transition_set_to (transition, G_TYPE_FLOAT, 0.0); + clutter_timeline_start (CLUTTER_TIMELINE (transition)); +} + +static void +on_helper_text_title_shown (ClutterTimeline *timelines, + CalibArea *area) +{ + show_helper_text_body (area); +} + +static void +show_helper_text_title (CalibArea *area) +{ + ClutterTransition *transition; + + gfloat height = clutter_actor_get_height (area->helper_text_title); + clutter_actor_set_y (area->helper_text_title, + - clutter_actor_get_height (area->helper_text_title)); + clutter_actor_show (area->helper_text_title); + + transition = clutter_property_transition_new ("y"); + clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition), + CLUTTER_EASE_OUT); + clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), + HELP_TEXT_ANIMATION_DURATION); + clutter_transition_set_animatable (transition, + CLUTTER_ANIMATABLE (area->helper_text_title)); + clutter_transition_set_from (transition, G_TYPE_FLOAT, -height); + clutter_transition_set_to (transition, G_TYPE_FLOAT, 0.0); + + g_signal_connect (CLUTTER_TIMELINE (transition), + "completed", + G_CALLBACK (on_helper_text_title_shown), + area); + + clutter_timeline_start (CLUTTER_TIMELINE (transition)); +} + +static void +on_fullscreen (GtkWindow *window, + GdkEventWindowState *event, + CalibArea *area) +{ + ClutterRect rect; + + if ((event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) == 0) + return; + + clutter_actor_show (area->action_layer); + clutter_actor_show (area->clock); + + rect.origin.x = 0; + rect.origin.y = 0; + clutter_actor_get_size (area->helper_text_title, + &rect.size.width, + &rect.size.height); + g_object_set (area->text_title_holder, "clip-rect", &rect, NULL); + + clutter_actor_get_size (area->helper_text_body, + &rect.size.width, + &rect.size.height); + g_object_set (area->text_body_holder, "clip-rect", &rect, NULL); + clutter_actor_set_y (area->helper_text_body, + - clutter_actor_get_height (area->helper_text_body)); + + show_helper_text_title (area); +} + +static void +set_up_stage (CalibArea *calib_area, ClutterActor *stage) +{ + ClutterPoint anchor; + ClutterColor color; + ClutterContent *success_content; + gfloat height; + gchar *markup; + + calib_area->stage = stage; + calib_area->action_layer = clutter_actor_new (); + calib_area->clock = cc_clock_actor_new (); + calib_area->target = cc_target_actor_new (); + calib_area->text_title_holder = clutter_actor_new (); + calib_area->helper_text_title = clutter_text_new (); + calib_area->text_body_holder = clutter_actor_new (); + calib_area->helper_text_body = clutter_text_new (); + calib_area->error_text = clutter_text_new (); + calib_area->success_image = clutter_actor_new (); + + clutter_stage_set_use_alpha (CLUTTER_STAGE (stage), TRUE); + + /* bind the action layer's geometry to the stage's */ + clutter_actor_add_constraint (calib_area->action_layer, + clutter_bind_constraint_new (stage, + CLUTTER_BIND_SIZE, + 0)); + clutter_actor_add_child (stage, calib_area->action_layer); + + g_signal_connect (stage, + "allocation-changed", + G_CALLBACK (on_allocation_changed), + calib_area); + + calib_area->calibrator.geometry.x = clutter_actor_get_x (stage); + calib_area->calibrator.geometry.y = clutter_actor_get_y (stage); + calib_area->calibrator.geometry.width = clutter_actor_get_width (stage); + calib_area->calibrator.geometry.height = clutter_actor_get_height (stage); + + clutter_color_from_string (&color, "#000"); + color.alpha = WINDOW_OPACITY * 255; + clutter_actor_set_background_color (stage, &color); + + clutter_actor_add_child (calib_area->action_layer, calib_area->clock); + clutter_actor_add_constraint (calib_area->clock, + clutter_align_constraint_new (stage, + CLUTTER_ALIGN_BOTH, + 0.5)); + + clutter_actor_add_child (calib_area->action_layer, calib_area->target); + + /* set the helper text */ + anchor.x = 0; + g_object_set (calib_area->text_title_holder, "pivot-point", &anchor, NULL); + + clutter_actor_add_child (calib_area->action_layer, + calib_area->text_title_holder); + clutter_actor_add_child (calib_area->text_title_holder, + calib_area->helper_text_title); + height = clutter_actor_get_height (calib_area->clock); + clutter_actor_add_constraint (calib_area->text_title_holder, + clutter_bind_constraint_new (calib_area->clock, + CLUTTER_BIND_Y, + height * 1.5)); + clutter_actor_add_constraint (calib_area->text_title_holder, + clutter_align_constraint_new (stage, + CLUTTER_ALIGN_X_AXIS, + .5)); + + clutter_text_set_line_alignment (CLUTTER_TEXT (calib_area->helper_text_title), + PANGO_ALIGN_CENTER); + + color.red = COLOR_GRAY; + color.green = COLOR_GRAY; + color.blue = COLOR_GRAY; + color.alpha = 255; + + markup = g_strdup_printf ("%s", + _(HELP_TEXT_TITLE)); + clutter_text_set_markup (CLUTTER_TEXT (calib_area->helper_text_title), markup); + clutter_text_set_color (CLUTTER_TEXT (calib_area->helper_text_title), &color); + g_free (markup); + + g_object_set (calib_area->text_body_holder, "pivot-point", &anchor, NULL); + + clutter_actor_add_child (calib_area->action_layer, + calib_area->text_body_holder); + clutter_actor_add_child (calib_area->text_body_holder, + calib_area->helper_text_body); + height = clutter_actor_get_height (calib_area->helper_text_title); + clutter_actor_add_constraint (calib_area->text_body_holder, + clutter_bind_constraint_new (calib_area->text_title_holder, + CLUTTER_BIND_Y, + height * 1.2)); + clutter_actor_add_constraint (calib_area->text_body_holder, + clutter_align_constraint_new (stage, + CLUTTER_ALIGN_X_AXIS, + .5)); + + clutter_text_set_line_alignment (CLUTTER_TEXT (calib_area->helper_text_body), + PANGO_ALIGN_CENTER); + markup = g_strdup_printf ("%s", + _(HELP_TEXT_MAIN)); + clutter_text_set_markup (CLUTTER_TEXT (calib_area->helper_text_body), markup); + g_free (markup); + + /* set the error text */ + g_object_set (calib_area->error_text, "pivot-point", &anchor, NULL); + + clutter_actor_add_child (calib_area->action_layer, calib_area->error_text); + height = clutter_actor_get_height (calib_area->helper_text_body); + clutter_actor_add_constraint (calib_area->error_text, + clutter_bind_constraint_new (calib_area->text_title_holder, + CLUTTER_BIND_Y, + height * 3)); + clutter_actor_add_constraint (calib_area->error_text, + clutter_align_constraint_new (stage, + CLUTTER_ALIGN_X_AXIS, + .5)); + + clutter_text_set_line_alignment (CLUTTER_TEXT (calib_area->error_text), + PANGO_ALIGN_CENTER); + markup = g_strdup_printf ("" + "%s", + ERROR_MESSAGE); + clutter_text_set_markup (CLUTTER_TEXT (calib_area->error_text), markup); + g_free (markup); + + clutter_actor_hide (calib_area->error_text); + + /* configure success image */ + success_content = clutter_image_new (); + clutter_actor_set_content (calib_area->success_image, + success_content); + g_object_unref (success_content); + clutter_actor_add_child (stage, calib_area->success_image); + clutter_actor_add_constraint (calib_area->success_image, + clutter_align_constraint_new (stage, + CLUTTER_ALIGN_BOTH, + .5)); + + /* animate clock */ + calib_area->clock_timeline = clutter_property_transition_new ("angle"); + clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (calib_area->clock_timeline), + CLUTTER_LINEAR); + clutter_timeline_set_duration (CLUTTER_TIMELINE (calib_area->clock_timeline), + MAX_TIME); + clutter_transition_set_animatable (calib_area->clock_timeline, + CLUTTER_ANIMATABLE (calib_area->clock)); + clutter_transition_set_from (calib_area->clock_timeline, G_TYPE_FLOAT, .0); + clutter_transition_set_to (calib_area->clock_timeline, G_TYPE_FLOAT, 360.0); + clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (calib_area->clock_timeline), + -1); + clutter_timeline_start (CLUTTER_TIMELINE (calib_area->clock_timeline)); + g_signal_connect (CLUTTER_TIMELINE (calib_area->clock_timeline), + "completed", + G_CALLBACK (on_timeout), + calib_area); + + g_signal_connect (stage, + "button-press-event", + G_CALLBACK (on_button_press_event), + calib_area); + g_signal_connect (stage, + "key-release-event", + G_CALLBACK (on_key_release_event), + calib_area); +} + +/** + * 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 monitor, + int device_id, + FinishCallback callback, + gpointer user_data, + XYinfo *old_axis, + int threshold_doubleclick, + int threshold_misclick) +{ + CalibArea *calib_area; + GdkRectangle rect; + GdkVisual *visual; +#ifndef FAKE_AREA + GdkWindow *window; + GdkCursor *cursor; +#endif /* FAKE_AREA */ + GtkWidget *clutter_embed; + ClutterActor *stage; + + g_return_val_if_fail (old_axis, NULL); + g_return_val_if_fail (callback, NULL); + + g_debug ("Current calibration: %d, %d, %d, %d\n", + old_axis->x_min, + old_axis->y_min, + old_axis->x_max, + old_axis->y_max); + + calib_area = g_new0 (CalibArea, 1); + calib_area->callback = callback; + calib_area->user_data = user_data; + calib_area->device_id = device_id; + calib_area->calibrator.old_axis.x_min = old_axis->x_min; + calib_area->calibrator.old_axis.x_max = old_axis->x_max; + calib_area->calibrator.old_axis.y_min = old_axis->y_min; + calib_area->calibrator.old_axis.y_max = old_axis->y_max; + calib_area->calibrator.threshold_doubleclick = threshold_doubleclick; + calib_area->calibrator.threshold_misclick = threshold_misclick; + + calib_area->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + +#ifndef FAKE_AREA + /* No cursor */ + gtk_widget_realize (calib_area->window); + window = gtk_widget_get_window (calib_area->window); + cursor = gdk_cursor_new (GDK_BLANK_CURSOR); + gdk_window_set_cursor (window, cursor); + g_object_unref (cursor); + + gtk_widget_set_can_focus (calib_area->window, TRUE); + gtk_window_set_keep_above (GTK_WINDOW (calib_area->window), TRUE); +#endif /* FAKE_AREA */ + + /* Set up the embedded stage */ + clutter_embed = gtk_clutter_embed_new (); + gtk_container_add (GTK_CONTAINER (calib_area->window), + clutter_embed); + + stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (clutter_embed)); + + /* Move to correct screen */ + if (screen == NULL) + screen = gdk_screen_get_default (); + gdk_screen_get_monitor_geometry (screen, monitor, &rect); + gtk_window_move (GTK_WINDOW (calib_area->window), rect.x, rect.y); + gtk_window_set_default_size (GTK_WINDOW (calib_area->window), + rect.width, + rect.height); + + set_up_stage (calib_area, stage); + + 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); + + gtk_window_fullscreen (GTK_WINDOW (calib_area->window)); + + visual = gdk_screen_get_rgba_visual (screen); + if (visual != NULL) + gtk_widget_set_visual (GTK_WIDGET (calib_area->window), visual); + + gtk_widget_show_all (calib_area->window); + clutter_actor_hide (calib_area->action_layer); + + return calib_area; +} + +/* Finishes the calibration. Note that CalibArea + * needs to be destroyed with calib_area_free() afterwards */ +gboolean +calib_area_finish (CalibArea *area, + XYinfo *new_axis, + gboolean *swap_xy) +{ + g_return_val_if_fail (area != NULL, FALSE); + + *new_axis = area->axis; + *swap_xy = area->swap; + + if (area->success) + g_debug ("Final calibration: %d, %d, %d, %d\n", + new_axis->x_min, + new_axis->y_min, + new_axis->x_max, + new_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); + + g_clear_object (&area->icon_success); + + 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; +} diff --git a/panels/wacom/calibrator/gui_gtk.h b/panels/wacom/calibrator/calibrator-gui.h similarity index 90% rename from panels/wacom/calibrator/gui_gtk.h rename to panels/wacom/calibrator/calibrator-gui.h index d2d3bd265..6741215f7 100644 --- a/panels/wacom/calibrator/gui_gtk.h +++ b/panels/wacom/calibrator/calibrator-gui.h @@ -21,8 +21,8 @@ * THE SOFTWARE. */ -#ifndef _gui_gtk_h -#define _gui_gtk_h +#ifndef __CALIBRATOR_GUI_H__ +#define __CALIBRATOR_GUI_H__ #include @@ -53,6 +53,8 @@ gboolean calib_area_finish (CalibArea *area, void calib_area_free (CalibArea *area); -void calib_area_get_display_size (CalibArea *area, gint *width, gint *height); +void calib_area_get_display_size (CalibArea *area, + gint *width, + gint *height); -#endif /* _gui_gtk_h */ +#endif /* __CALIBRATOR_GUI_H__ */ diff --git a/panels/wacom/calibrator/calibrator.h b/panels/wacom/calibrator/calibrator.h index 75d72d799..659232f7a 100644 --- a/panels/wacom/calibrator/calibrator.h +++ b/panels/wacom/calibrator/calibrator.h @@ -25,7 +25,7 @@ #define _calibrator_h #include -#include "gui_gtk.h" +#include "calibrator-gui.h" /* * Number of blocks. We partition the screen into 'num_blocks' x 'num_blocks' diff --git a/panels/wacom/calibrator/cc-clock-actor.c b/panels/wacom/calibrator/cc-clock-actor.c new file mode 100644 index 000000000..1039d74d2 --- /dev/null +++ b/panels/wacom/calibrator/cc-clock-actor.c @@ -0,0 +1,186 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Author: Joaquim Rocha + */ + +#include +#include "cc-clock-actor.h" + +#define CLOCK_RADIUS 50 +#define CLOCK_LINE_WIDTH 10 +#define CLOCK_LINE_PADDING 10 +#define CLOCK_SIZE_EXTRA +#define ANGLE "angle" +#define EXTRA_SPACE 2 + +G_DEFINE_TYPE (CcClockActor, cc_clock_actor, CLUTTER_TYPE_ACTOR); + +enum { + PROP_0, + PROP_ANGLE, + N_PROPERTIES +}; + +static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; + +static void +draw_clock (ClutterCairoTexture *texture, + cairo_t *cr, + gint width, + gint height, + CcClockActor *self) +{ + cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); + cairo_paint (cr); + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + + /* Draw the clock background */ + cairo_arc (cr, width / 2, height / 2, CLOCK_RADIUS / 2, 0.0, 2.0 * M_PI); + cairo_set_source_rgb (cr, 0.5, 0.5, 0.5); + cairo_fill_preserve (cr); + cairo_stroke (cr); + + cairo_set_line_width (cr, CLOCK_LINE_WIDTH); + + cairo_arc (cr, + width / 2, + height / 2, + (CLOCK_RADIUS - CLOCK_LINE_WIDTH - CLOCK_LINE_PADDING) / 2, + 3 * M_PI_2, + 3 * M_PI_2 + self->angle * M_PI / 180.0); + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + cairo_stroke (cr); +} + +static void +cc_clock_actor_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + CcClockActor *self = CC_CLOCK_ACTOR (object); + ClutterContent *content; + content = clutter_actor_get_content (CLUTTER_ACTOR (self)); + + switch (property_id) + { + case PROP_ANGLE: + self->angle = g_value_get_float (value); + if (content) + clutter_content_invalidate (content); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +cc_clock_actor_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + CcClockActor *self = CC_CLOCK_ACTOR (object); + + switch (property_id) + { + case PROP_ANGLE: + g_value_set_float (value, self->angle); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +cc_clock_actor_init (CcClockActor *self) +{ + self->angle = 0; + + ClutterContent *content; + content = clutter_canvas_new (); + /* Extra space is needed because when drawing without it, + it will miss 1 pixel in each of the edges */ + clutter_canvas_set_size (CLUTTER_CANVAS (content), + CLOCK_RADIUS + EXTRA_SPACE, + CLOCK_RADIUS + EXTRA_SPACE); + clutter_actor_set_content (CLUTTER_ACTOR (self), content); + g_signal_connect (CLUTTER_CANVAS (content), + "draw", + G_CALLBACK (draw_clock), + self); + g_object_unref (content); +} + +static void +cc_clock_actor_get_preferred_width (ClutterActor *actor, + gfloat for_height, + gfloat *min_width_p, + gfloat *natural_width_p) +{ + *min_width_p = CLOCK_RADIUS + EXTRA_SPACE; + *natural_width_p = CLOCK_RADIUS + EXTRA_SPACE; +} + +static void +cc_clock_actor_get_preferred_height (ClutterActor *actor, + gfloat for_width, + gfloat *min_height_p, + gfloat *natural_height_p) +{ + *min_height_p = CLOCK_RADIUS + EXTRA_SPACE; + *natural_height_p = CLOCK_RADIUS + EXTRA_SPACE; +} + + +static void +cc_clock_actor_class_init (CcClockActorClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *clutter_actor_class = CLUTTER_ACTOR_CLASS (klass); + + gobject_class->set_property = cc_clock_actor_set_property; + gobject_class->get_property = cc_clock_actor_get_property; + + clutter_actor_class->get_preferred_width = cc_clock_actor_get_preferred_width; + clutter_actor_class->get_preferred_height = cc_clock_actor_get_preferred_height; + + obj_properties[PROP_ANGLE] = + g_param_spec_float (ANGLE, + "The angle of the clock's progress", + "Set the angle of the clock's progress", + .0, + 360.0, + .0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (gobject_class, + N_PROPERTIES, + obj_properties); +} + +ClutterActor * +cc_clock_actor_new (void) +{ + return g_object_new (CC_CLOCK_ACTOR_TYPE, NULL); +} diff --git a/panels/wacom/calibrator/cc-clock-actor.h b/panels/wacom/calibrator/cc-clock-actor.h new file mode 100644 index 000000000..6a2b14798 --- /dev/null +++ b/panels/wacom/calibrator/cc-clock-actor.h @@ -0,0 +1,56 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Author: Joaquim Rocha + */ + +#ifndef __CC_CLOCK_ACTOR_H__ +#define __CC_CLOCK_ACTOR_H__ + +#include +#include + +G_BEGIN_DECLS + +#define CC_CLOCK_ACTOR_TYPE (cc_clock_actor_get_type ()) +#define CC_CLOCK_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CC_CLOCK_ACTOR_TYPE, CcClockActor)) +#define CC_IS_CLOCK_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CC_CLOCK_ACTOR_TYPE)) +#define CC_CLOCK_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CC_CLOCK_ACTOR_TYPE, CcClockActorClass)) +#define CC_IS_CLOCK_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CC_CLOCK_ACTOR_TYPE)) +#define CC_CLOCK_ACTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CC_CLOCK_ACTOR_TYPE, CcClockActorClass)) + +typedef struct _CcClockActor CcClockActor; +typedef struct _CcClockActorClass CcClockActorClass; + +struct _CcClockActor +{ + ClutterActor parent_instance; + + /*< private >*/ + gfloat angle; +}; + +struct _CcClockActorClass +{ + ClutterActorClass parent_class; +}; + +ClutterActor * cc_clock_actor_new (void); + +GType cc_clock_actor_get_type (void); + +#endif /* __CC_CLOCK_ACTOR_H__ */ diff --git a/panels/wacom/calibrator/cc-target-actor.c b/panels/wacom/calibrator/cc-target-actor.c new file mode 100644 index 000000000..69f4012e7 --- /dev/null +++ b/panels/wacom/calibrator/cc-target-actor.c @@ -0,0 +1,229 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Author: Joaquim Rocha + */ + +#include +#include "cc-target-actor.h" + +#define CC_TARGET_ACTOR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CC_TARGET_ACTOR_TYPE, CcTargetActorPrivate)) + +#define CROSS_LINES 47 +#define CROSS_CIRCLE 7 +#define CROSS_CIRCLE2 27 +#define TARGET_SHOW_ANIMATION_DURATION 500 +#define TARGET_HIDE_ANIMATION_DURATION 200 + +struct _CcTargetActorPrivate +{ + gdouble pos_x; + gdouble pos_y; +}; + +G_DEFINE_TYPE (CcTargetActor, cc_target_actor, CLUTTER_TYPE_ACTOR); + +static ClutterTransition * +get_target_show_animation (ClutterActor *target, const gchar *property) +{ + ClutterTransition *transition; + + transition = clutter_property_transition_new (property); + clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition), + CLUTTER_EASE_OUT_BACK); + clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), + TARGET_SHOW_ANIMATION_DURATION); + clutter_transition_set_animatable (transition, + CLUTTER_ANIMATABLE (target)); + clutter_transition_set_from (transition, G_TYPE_FLOAT, 0.0); + clutter_transition_set_to (transition, G_TYPE_FLOAT, 1.0); + + return transition; +} + +static ClutterTransition * +get_target_hide_animation (ClutterActor *target, const gchar *property) +{ + ClutterTransition *transition; + + transition = get_target_show_animation (target, property); + clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition), + CLUTTER_EASE_OUT); + clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), + TARGET_HIDE_ANIMATION_DURATION); + clutter_transition_set_from (transition, G_TYPE_FLOAT, 1.0); + clutter_transition_set_to (transition, G_TYPE_FLOAT, 0.0); + + return transition; +} + +static void +show_target (CcTargetActor *self) +{ + ClutterTransition *transition; + + transition = get_target_show_animation (CLUTTER_ACTOR (self), "scale-x"); + clutter_timeline_start (CLUTTER_TIMELINE (transition)); + + transition = get_target_show_animation (CLUTTER_ACTOR (self), "scale-y"); + clutter_timeline_start (CLUTTER_TIMELINE (transition)); +} + +static void +on_target_animation_complete (ClutterTimeline *timeline, + CcTargetActor *self) +{ + CcTargetActorPrivate *priv; + priv = CC_TARGET_ACTOR_GET_PRIVATE (self); + + clutter_actor_show (CLUTTER_ACTOR (self)); + clutter_actor_set_position (CLUTTER_ACTOR (self), + priv->pos_x, + priv->pos_y); + + show_target (self); +} + +static void +draw_target (ClutterCairoTexture *texture, + cairo_t *cr, + gint width, + gint height, + gpointer data) +{ + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + + cairo_set_line_width(cr, 1); + cairo_move_to(cr, 0, height / 2.0); + cairo_rel_line_to(cr, width, 0); + cairo_move_to(cr, width / 2.0, 0); + cairo_rel_line_to(cr, 0, height * 2); + cairo_stroke(cr); + + cairo_set_line_width(cr, 2); + cairo_arc(cr, width / 2.0, height / 2.0, CROSS_CIRCLE, 0.0, 2.0 * M_PI); + cairo_stroke(cr); + + cairo_set_line_width(cr, 5); + cairo_arc(cr, width / 2.0, height / 2.0, CROSS_CIRCLE2, 0.0, 2.0 * M_PI); + cairo_stroke(cr); +} + +static void +cc_target_actor_init (CcTargetActor *self) +{ + ClutterContent *content; + ClutterPoint anchor; + CcTargetActorPrivate *priv; + + priv = CC_TARGET_ACTOR_GET_PRIVATE (self); + priv->pos_x = .0; + priv->pos_y = .0; + + content = clutter_canvas_new (); + clutter_actor_set_content (CLUTTER_ACTOR (self), content); + g_object_unref (content); + + clutter_canvas_set_size (CLUTTER_CANVAS (content), + CROSS_LINES * 2, + CROSS_LINES * 2); + g_signal_connect (CLUTTER_CANVAS (content), + "draw", + G_CALLBACK (draw_target), + self); + clutter_content_invalidate (content); + + anchor.x = .5; + anchor.y = .5; + + g_object_set (self, "pivot-point", &anchor, NULL); +} + +static void +cc_target_actor_get_preferred_width (ClutterActor *actor, + gfloat for_height, + gfloat *min_width_p, + gfloat *natural_width_p) +{ + *min_width_p = CROSS_LINES * 2; + *natural_width_p = CROSS_LINES * 2; +} + +static void +cc_target_actor_get_preferred_height (ClutterActor *actor, + gfloat for_width, + gfloat *min_height_p, + gfloat *natural_height_p) +{ + *min_height_p = CROSS_LINES * 2; + *natural_height_p = CROSS_LINES * 2; +} + + +static void +cc_target_actor_class_init (CcTargetActorClass *klass) +{ + ClutterActorClass *clutter_actor_class = CLUTTER_ACTOR_CLASS (klass); + + clutter_actor_class->get_preferred_width = cc_target_actor_get_preferred_width; + clutter_actor_class->get_preferred_height = cc_target_actor_get_preferred_height; + + g_type_class_add_private (klass, sizeof (CcTargetActorPrivate)); +} + +void +cc_target_actor_move (CcTargetActor *self, gdouble x, gdouble y) +{ + g_return_if_fail (CC_IS_TARGET_ACTOR (self)); + + CcTargetActorPrivate *priv; + ClutterTransition *transition; + gboolean target_visible; + + priv = CC_TARGET_ACTOR_GET_PRIVATE (self); + priv->pos_x = x; + priv->pos_y = y; + + g_object_get (self, "visible", &target_visible, NULL); + + if (target_visible) + { + transition = get_target_hide_animation (CLUTTER_ACTOR (self), "scale-x"); + clutter_timeline_start (CLUTTER_TIMELINE (transition)); + transition = get_target_hide_animation (CLUTTER_ACTOR (self), "scale-y"); + clutter_timeline_start (CLUTTER_TIMELINE (transition)); + + g_signal_connect (CLUTTER_TIMELINE (transition), + "completed", + G_CALLBACK (on_target_animation_complete), + self); + } + else + { + clutter_actor_show (CLUTTER_ACTOR (self)); + + clutter_actor_set_position (CLUTTER_ACTOR (self), x, y); + + show_target (self); + } +} + +ClutterActor * +cc_target_actor_new (void) +{ + return g_object_new (CC_TARGET_ACTOR_TYPE, NULL); +} diff --git a/panels/wacom/calibrator/cc-target-actor.h b/panels/wacom/calibrator/cc-target-actor.h new file mode 100644 index 000000000..c7d63f866 --- /dev/null +++ b/panels/wacom/calibrator/cc-target-actor.h @@ -0,0 +1,60 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Author: Joaquim Rocha + */ + +#ifndef __CC_TARGET_ACTOR_H__ +#define __CC_TARGET_ACTOR_H__ + +#include +#include + +G_BEGIN_DECLS + +#define CC_TARGET_ACTOR_TYPE (cc_target_actor_get_type ()) +#define CC_TARGET_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CC_TARGET_ACTOR_TYPE, CcTargetActor)) +#define CC_IS_TARGET_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CC_TARGET_ACTOR_TYPE)) +#define CC_TARGET_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CC_TARGET_ACTOR_TYPE, CcTargetActorClass)) +#define CC_IS_TARGET_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CC_TARGET_ACTOR_TYPE)) +#define CC_TARGET_ACTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CC_TARGET_ACTOR_TYPE, CcTargetActorClass)) + +typedef struct _CcTargetActor CcTargetActor; +typedef struct _CcTargetActorClass CcTargetActorClass; +typedef struct _CcTargetActorPrivate CcTargetActorPrivate; + +struct _CcTargetActor +{ + ClutterActor parent_instance; + + /*< private >*/ + CcTargetActorPrivate *priv; +}; + +struct _CcTargetActorClass +{ + ClutterActorClass parent_class; +}; + +ClutterActor * cc_target_actor_new (void); +void cc_target_actor_move (CcTargetActor *target, + gdouble x, + gdouble y); + +GType cc_target_actor_get_type (void); + +#endif /* __CC_TARGET_ACTOR_H__ */ diff --git a/panels/wacom/calibrator/gui_gtk.c b/panels/wacom/calibrator/gui_gtk.c deleted file mode 100644 index e6a398cc4..000000000 --- a/panels/wacom/calibrator/gui_gtk.c +++ /dev/null @@ -1,591 +0,0 @@ -/* - * Copyright (c) 2009 Tias Guns - * Copyright (c) 2009 Soren Hauberg - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "calibrator.h" -#include "gui_gtk.h" - -struct CalibArea -{ - struct Calib calibrator; - XYinfo axis; - gboolean swap; - gboolean success; - int device_id; - - double X[4], Y[4]; - int display_width, display_height; - int time_elapsed; - - const char* message; - - guint anim_id; - - GtkWidget *window; - GdkPixbuf *icon_success; - - FinishCallback callback; - gpointer user_data; -}; - - -/* Window parameters */ -#define WINDOW_OPACITY 0.9 - -/* Timeout parameters */ -#define TIME_STEP 100 /* in milliseconds */ -#define MAX_TIME 15000 /* 5000 = 5 sec */ -#define END_TIME 750 /* 750 = 0.75 sec */ - -/* Clock appereance */ -#define CROSS_LINES 47 -#define CROSS_CIRCLE 7 -#define CROSS_CIRCLE2 27 -#define CLOCK_RADIUS 50 -#define CLOCK_LINE_WIDTH 10 -#define CLOCK_LINE_PADDING 10 - -/* Text printed on screen */ -#define HELP_TEXT_TITLE N_("Screen Calibration") -#define HELP_TEXT_MAIN N_("Please tap the target markers as they appear on screen to calibrate the tablet.") - -#define ICON_SUCCESS "emblem-ok-symbolic" -#define ICON_SIZE 300 - -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 -resize_display(CalibArea *calib_area) -{ - /* check that screensize did not change (if no manually specified geometry) */ - GtkAllocation allocation; - gtk_widget_get_allocation(calib_area->window, &allocation); - if (calib_area->display_width != allocation.width || - calib_area->display_height != allocation.height) - { - set_display_size(calib_area, allocation.width, allocation.height); - } -} - -static void -draw_success_icon (CalibArea *area, cairo_t *cr) -{ - GtkWidget *widget; - GtkStyleContext *context; - gint icon_width, icon_height; - gdouble x; - gdouble y; - - widget = GTK_WIDGET (area->window); - context = gtk_widget_get_style_context (widget); - - icon_width = gdk_pixbuf_get_width (area->icon_success); - icon_height = gdk_pixbuf_get_height (area->icon_success); - - x = (area->display_width - icon_width) / 2; - y = (area->display_height - icon_height) / 2; - - gtk_render_icon (context, cr, area->icon_success, x, y); -} - -static void -draw(GtkWidget *widget, cairo_t *cr, gpointer data) -{ - CalibArea *calib_area = (CalibArea*)data; - int i; - double x; - double y; - PangoLayout *layout; - PangoRectangle logical_rect; - GtkStyleContext *context; - char *markup; - - resize_display(calib_area); - - context = gtk_widget_get_style_context (widget); - - /* Black background and reset the operator */ - cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); - cairo_paint (cr); - cairo_set_operator (cr, CAIRO_OPERATOR_OVER); - - /* If calibration is successful, just draw the tick */ - if (calib_area->icon_success) { - draw_success_icon (calib_area, cr); - return; - } - - /* Print the help lines */ - layout = pango_layout_new (gtk_widget_get_pango_context (widget)); - pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER); - markup = g_strdup_printf ("%s\n%s", - _(HELP_TEXT_TITLE), - _(HELP_TEXT_MAIN)); - pango_layout_set_markup (layout, markup, -1); - g_free (markup); - - pango_layout_get_pixel_extents (layout, NULL, &logical_rect); - - x = (calib_area->display_width - logical_rect.width) / 2 + logical_rect.x; - y = (calib_area->display_height - logical_rect.height) / 2 - logical_rect.height - 40 + logical_rect.y; - - gtk_render_layout (context, cr, - x + logical_rect.x, - y + logical_rect.y, - layout); - g_object_unref (layout); - - /* Draw the points */ - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - - i = calib_area->calibrator.num_clicks; - - cairo_set_line_width(cr, 1); - cairo_move_to(cr, calib_area->X[i] - CROSS_LINES, calib_area->Y[i] - 0.5); - cairo_rel_line_to(cr, CROSS_LINES*2, 0); - cairo_move_to(cr, calib_area->X[i] - 0.5, calib_area->Y[i] - CROSS_LINES); - cairo_rel_line_to(cr, 0, CROSS_LINES*2); - cairo_stroke(cr); - - cairo_set_line_width(cr, 2); - cairo_arc(cr, calib_area->X[i] - 0.5, calib_area->Y[i] - 0.5, CROSS_CIRCLE, 0.0, 2.0 * M_PI); - cairo_stroke(cr); - - cairo_set_line_width(cr, 5); - cairo_arc(cr, calib_area->X[i] - 0.5, calib_area->Y[i] - 0.5, CROSS_CIRCLE2, 0.0, 2.0 * M_PI); - cairo_stroke(cr); - - /* Draw the clock background */ - cairo_arc(cr, calib_area->display_width/2, calib_area->display_height/2, CLOCK_RADIUS/2, 0.0, 2.0 * M_PI); - cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); - cairo_fill_preserve(cr); - cairo_stroke(cr); - - cairo_set_line_width(cr, CLOCK_LINE_WIDTH); - cairo_arc(cr, calib_area->display_width/2, calib_area->display_height/2, (CLOCK_RADIUS - CLOCK_LINE_WIDTH - CLOCK_LINE_PADDING)/2, - 3/2.0*M_PI, (3/2.0*M_PI) + ((double)calib_area->time_elapsed/(double)MAX_TIME) * 2*M_PI); - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - cairo_stroke(cr); - - /* Draw the message (if any) */ - if (calib_area->message != NULL) - { - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - - /* Frame the message */ - layout = pango_layout_new (gtk_widget_get_pango_context (widget)); - pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER); - markup = g_strdup_printf ("%s", - _(calib_area->message)); - pango_layout_set_markup (layout, markup, -1); - g_free (markup); - pango_layout_get_pixel_extents (layout, NULL, &logical_rect); - - x = (calib_area->display_width - logical_rect.width) / 2 + logical_rect.x; - y = (calib_area->display_height - logical_rect.height + CLOCK_RADIUS) / 2 + 60 + logical_rect.y; - cairo_set_line_width(cr, 2); - cairo_rectangle(cr, x - 10 - 0.5 , y - 10 - 0.5, - logical_rect.width + 20 + 1, logical_rect.height + 20 + 1); - cairo_stroke (cr); - - /* Print the message */ - gtk_render_layout (context, cr, - x + logical_rect.x, - y + logical_rect.y, - layout); - g_object_unref (layout); - } -} - -static void -draw_message(CalibArea *calib_area, - const char *msg) -{ - calib_area->message = msg; -} - -static void -redraw(CalibArea *calib_area) -{ - GdkWindow *win = gtk_widget_get_window(calib_area->window); - if (win) - { - GdkRectangle rect; - rect.x = 0; - rect.y = 0; - rect.width = calib_area->display_width; - rect.height = calib_area->display_height; - gdk_window_invalidate_rect(win, &rect, FALSE); - } -} - -static gboolean -on_delete_event (GtkWidget *widget, - GdkEvent *event, - CalibArea *area) -{ - if (area->anim_id > 0) { - g_source_remove (area->anim_id); - area->anim_id = 0; - } - - gtk_widget_hide (area->window); - - (*area->callback) (area, area->user_data); - - return TRUE; -} - -static gboolean -draw_success_end_wait_callback (CalibArea *area) -{ - on_delete_event (NULL, NULL, area); - - return FALSE; -} - -static void -set_calibration_status (CalibArea *area) -{ - GtkIconTheme *icon_theme; - GtkIconInfo *icon_info; - GdkRGBA white; - - icon_theme = gtk_icon_theme_get_default (); - icon_info = gtk_icon_theme_lookup_icon (icon_theme, - ICON_SUCCESS, - ICON_SIZE, - GTK_ICON_LOOKUP_USE_BUILTIN); - if (icon_info == NULL) { - g_warning ("Failed to find icon \"%s\"", ICON_SUCCESS); - goto out; - } - - gdk_rgba_parse (&white, "White"); - area->icon_success = gtk_icon_info_load_symbolic (icon_info, - &white, - NULL, - NULL, - NULL, - NULL, - NULL); - g_object_unref (icon_info); - if (!area->icon_success) - g_warning ("Failed to load icon \"%s\"", ICON_SUCCESS); - -out: - area->success = finish (&area->calibrator, &area->axis, &area->swap); - if (area->success && area->icon_success) { - redraw(area); - g_timeout_add(END_TIME, (GSourceFunc) draw_success_end_wait_callback, area); - } else { - on_delete_event (NULL, NULL, area); - } -} - -static gboolean -on_button_press_event(GtkWidget *widget, - GdkEventButton *event, - CalibArea *area) -{ - gboolean success; - - if (area->success) - return FALSE; - - /* Check matching device ID if a device ID was provided */ - if (area->device_id > -1) { - GdkDevice *device; - - device = gdk_event_get_source_device ((GdkEvent *) event); - if (device != NULL && gdk_x11_device_get_id (device) != area->device_id) - return FALSE; - } - - /* Handle click */ - area->time_elapsed = 0; - success = add_click(&area->calibrator, (int)event->x_root, (int)event->y_root); - - if (!success && area->calibrator.num_clicks == 0) - draw_message(area, N_("Mis-click detected, restarting...")); - else - draw_message(area, NULL); - - /* Are we done yet? */ - if (area->calibrator.num_clicks >= 4) - { - set_calibration_status (area); - return FALSE; - } - - /* Force a redraw */ - redraw(area); - - return FALSE; -} - -static gboolean -on_key_release_event(GtkWidget *widget, - GdkEventKey *event, - CalibArea *area) -{ - if (area->success) - return FALSE; - if (event->type != GDK_KEY_RELEASE) - return FALSE; - if (event->keyval != GDK_KEY_Escape) - return FALSE; - - on_delete_event (widget, NULL, area); - - return FALSE; -} - -static gboolean -on_focus_out_event (GtkWidget *widget, - GdkEvent *event, - CalibArea *area) -{ - if (area->success) - return FALSE; - - /* If the calibrator window looses focus, simply bail out... */ - on_delete_event (widget, NULL, area); - - return FALSE; -} - -static gboolean -on_timer_signal(CalibArea *area) -{ - GdkWindow *win; - - area->time_elapsed += TIME_STEP; - if (area->time_elapsed > MAX_TIME) - { - set_calibration_status (area); - return FALSE; - } - - /* Update clock */ - win = gtk_widget_get_window (area->window); - if (win) - { - GdkRectangle rect; - rect.x = area->display_width/2 - CLOCK_RADIUS - CLOCK_LINE_WIDTH; - rect.y = area->display_height/2 - CLOCK_RADIUS - CLOCK_LINE_WIDTH; - rect.width = 2 * CLOCK_RADIUS + 1 + 2 * CLOCK_LINE_WIDTH; - rect.height = 2 * CLOCK_RADIUS + 1 + 2 * CLOCK_LINE_WIDTH; - gdk_window_invalidate_rect(win, &rect, FALSE); - } - - return TRUE; -} - -/** - * 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 monitor, - int device_id, - FinishCallback callback, - gpointer user_data, - XYinfo *old_axis, - int threshold_doubleclick, - int threshold_misclick) -{ - CalibArea *calib_area; - GdkRectangle rect; - GdkWindow *window; - GdkRGBA black; -#ifndef FAKE_AREA - GdkCursor *cursor; -#endif /* FAKE_AREA */ - - g_return_val_if_fail (old_axis, NULL); - g_return_val_if_fail (callback, NULL); - - g_debug ("Current calibration: %d, %d, %d, %d\n", - old_axis->x_min, - old_axis->y_min, - old_axis->x_max, - old_axis->y_max); - - calib_area = g_new0 (CalibArea, 1); - calib_area->callback = callback; - calib_area->user_data = user_data; - calib_area->device_id = device_id; - calib_area->calibrator.old_axis.x_min = old_axis->x_min; - calib_area->calibrator.old_axis.x_max = old_axis->x_max; - calib_area->calibrator.old_axis.y_min = old_axis->y_min; - calib_area->calibrator.old_axis.y_max = old_axis->y_max; - calib_area->calibrator.threshold_doubleclick = threshold_doubleclick; - calib_area->calibrator.threshold_misclick = threshold_misclick; - - /* Set up the window */ - calib_area->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_widget_set_app_paintable (GTK_WIDGET (calib_area->window), TRUE); - - /* Black background */ - gdk_rgba_parse (&black, "rgb(0,0,0)"); - gtk_widget_set_opacity (GTK_WIDGET (calib_area->window), WINDOW_OPACITY); - - gtk_widget_realize (calib_area->window); - window = gtk_widget_get_window (calib_area->window); - gdk_window_set_background_rgba (window, &black); - -#ifndef FAKE_AREA - /* No cursor (unless we're faking the area - which might be convenient) */ - cursor = gdk_cursor_new (GDK_BLANK_CURSOR); - gdk_window_set_cursor (window, cursor); - g_object_unref (cursor); -#endif /* FAKE_AREA */ - - /* Listen for mouse events */ - gtk_widget_add_events (calib_area->window, GDK_KEY_RELEASE_MASK | GDK_BUTTON_PRESS_MASK); - gtk_widget_set_can_focus (calib_area->window, TRUE); - gtk_window_fullscreen (GTK_WINDOW (calib_area->window)); - gtk_window_set_keep_above (GTK_WINDOW (calib_area->window), TRUE); - - /* Connect callbacks */ - g_signal_connect (calib_area->window, "draw", - G_CALLBACK(draw), calib_area); - g_signal_connect (calib_area->window, "button-press-event", - G_CALLBACK(on_button_press_event), calib_area); - 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); - - /* Setup timer for animation */ - calib_area->anim_id = g_timeout_add(TIME_STEP, (GSourceFunc)on_timer_signal, calib_area); - - /* Move to correct screen */ - if (screen == NULL) - screen = gdk_screen_get_default (); - gdk_screen_get_monitor_geometry (screen, monitor, &rect); - gtk_window_move (GTK_WINDOW (calib_area->window), rect.x, rect.y); - gtk_window_set_default_size (GTK_WINDOW (calib_area->window), rect.width, rect.height); - - calib_area->calibrator.geometry.x = rect.x; - calib_area->calibrator.geometry.y = rect.y; - calib_area->calibrator.geometry.width = rect.width; - calib_area->calibrator.geometry.height = rect.height; - - gtk_widget_show_all (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, - XYinfo *new_axis, - gboolean *swap_xy) -{ - g_return_val_if_fail (area != NULL, FALSE); - - *new_axis = area->axis; - *swap_xy = area->swap; - - if (area->success) - g_debug ("Final calibration: %d, %d, %d, %d\n", - new_axis->x_min, - new_axis->y_min, - new_axis->x_max, - new_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); - - if (area->anim_id > 0) { - g_source_remove (area->anim_id); - area->anim_id = 0; - } - - if (area->icon_success) - g_object_unref (area->icon_success); - - gtk_widget_destroy (area->window); - g_free (area); -} - -void -calib_area_get_display_size (CalibArea *area, gint *width, gint *height) -{ - *width = area->display_width; - *height = area->display_height; -} diff --git a/panels/wacom/calibrator/main.c b/panels/wacom/calibrator/main.c index 2368a1005..775871377 100644 --- a/panels/wacom/calibrator/main.c +++ b/panels/wacom/calibrator/main.c @@ -28,10 +28,11 @@ #include #include #include +#include #include -#include "gui_gtk.h" +#include "calibrator-gui.h" #include "calibrator.h" /** @@ -397,7 +398,11 @@ int main(int argc, char** argv) g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); /* GTK setup */ - gtk_init(&argc, &argv); + if (gtk_clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) + { + g_critical ("Unable to initialize Clutter"); + return 1; + } calib_area = calib_area_new (NULL, 0, /* monitor */ diff --git a/panels/wacom/cc-wacom-page.c b/panels/wacom/cc-wacom-page.c index 5e10c5b03..72111c896 100644 --- a/panels/wacom/cc-wacom-page.c +++ b/panels/wacom/cc-wacom-page.c @@ -34,7 +34,7 @@ #include "cc-wacom-mapping-panel.h" #include "cc-wacom-stylus-page.h" #include "gsd-enums.h" -#include "gui_gtk.h" +#include "calibrator-gui.h" #include diff --git a/panels/wacom/test-wacom.c b/panels/wacom/test-wacom.c index f2860719a..68dd23847 100644 --- a/panels/wacom/test-wacom.c +++ b/panels/wacom/test-wacom.c @@ -2,9 +2,11 @@ #include "config.h" #include +#include #include "cc-wacom-page.h" #include "gsd-wacom-device.h" +#include "clutter/clutter.h" #define FIXED_WIDTH 675 @@ -69,7 +71,10 @@ int main (int argc, char **argv) bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); - gtk_init (&argc, &argv); + if (gtk_clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) { + g_critical ("Unable to initialize Clutter"); + return 1; + } window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_resizable (GTK_WINDOW (window), FALSE); diff --git a/shell/Makefile.am b/shell/Makefile.am index 68b70e91a..ad7a68faf 100644 --- a/shell/Makefile.am +++ b/shell/Makefile.am @@ -2,6 +2,7 @@ INCLUDES = \ -I$(top_srcdir) \ $(SHELL_CFLAGS) \ $(CHEESE_CFLAGS) \ + $(WACOM_PANEL_CFLAGS) \ -I$(top_srcdir)/panels/common/ \ -I$(top_srcdir)/libgd diff --git a/shell/cc-application.c b/shell/cc-application.c index 72a61436b..e7de6f753 100644 --- a/shell/cc-application.c +++ b/shell/cc-application.c @@ -30,9 +30,12 @@ #include "cc-shell-log.h" #include "cc-window.h" +#if defined(HAVE_CHEESE) || defined(HAVE_WACOM) +#include +#endif /* HAVE_CHEESE || HAVE_WACOM */ + #ifdef HAVE_CHEESE #include -#include #endif /* HAVE_CHEESE */ struct _CcApplicationPrivate @@ -277,13 +280,13 @@ cc_application_startup (GApplication *application) G_APPLICATION_CLASS (cc_application_parent_class)->startup (application); -#ifdef HAVE_CHEESE +#if defined(HAVE_CHEESE) || defined(HAVE_WACOM) if (gtk_clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS) { g_critical ("Unable to initialize Clutter"); return; } -#endif /* HAVE_CHEESE */ +#endif /* HAVE_CHEESE || HAVE_WACOM */ /* register a symbolic icon size for use in sidebar lists */ gtk_icon_size_register ("cc-sidebar-list", 24, 24);