diff --git a/plugins/mouse/Makefile.am b/plugins/mouse/Makefile.am index 9aeac69..05cd3c7 100644 --- a/plugins/mouse/Makefile.am +++ b/plugins/mouse/Makefile.am @@ -21,6 +21,8 @@ libmouse_la_SOURCES = \ gsd-mouse-manager.c \ gsd-locate-pointer.h \ gsd-locate-pointer.c \ + gsd-timeline.h \ + gsd-timeline.c \ $(NULL) libmouse_la_LDFLAGS = \ diff --git a/plugins/mouse/gsd-locate-pointer.c b/plugins/mouse/gsd-locate-pointer.c index 6824511..f80cd66 100644 --- a/plugins/mouse/gsd-locate-pointer.c +++ b/plugins/mouse/gsd-locate-pointer.c @@ -1,243 +1,287 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- +/* gsd-locate-pointer.c * - * Copyright © 2001 Jonathan Blandford + * Copyright (C) 2008 Carlos Garnacho * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of Red Hat not be used in advertising or - * publicity pertaining to distribution of the software without specific, - * written prior permission. Red Hat makes no representations about the - * suitability of this software for any purpose. It is provided "as is" - * without express or implied warranty. + * 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. * - * Authors: Jonathan Blandford + * 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. */ - #include - +#include "gsd-timeline.h" #include "gsd-locate-pointer.h" -#define LARGE_SIZE 101 -#define SMALL_SIZE 51 - -typedef enum { - STAGE_ONE, - STAGE_TWO, - STAGE_THREE, - STAGE_FOUR, - STAGE_DONE -} LocatePointerStage; - -static LocatePointerStage stage; -static GdkWindow *window = NULL; -static gint cursor_x, cursor_y; -static guint locate_pointer_id = 0; - -static gint -locate_pointer_expose (GtkWidget *widget, - GdkEventExpose *event, - gpointer data) +#define ANIMATION_LENGTH 750 +#define WINDOW_SIZE 101 +#define N_CIRCLES 4 + +/* All circles are supposed to be moving when progress + * reaches 0.5, and each of them are supposed to long + * for half of the progress, hence the need of 0.5 to + * get the circles interval, and the multiplication + * by 2 to know a circle progress */ +#define CIRCLES_PROGRESS_INTERVAL (0.5 / N_CIRCLES) +#define CIRCLE_PROGRESS(p) (MIN (1., ((gdouble) (p) * 2.))) + +typedef struct GsdLocatePointerData GsdLocatePointerData; + +struct GsdLocatePointerData { - gint size; - GdkPoint points[4]; + GsdTimeline *timeline; + GtkWidget *invisible; + GdkWindow *window; - if (event->window != window) - return FALSE; + gdouble progress; +}; + +static GsdLocatePointerData *data = NULL; - switch (stage) +static void +locate_pointer_paint (GsdLocatePointerData *data, + cairo_t *cr, + gboolean composite) +{ + GdkColor color; + gdouble progress, circle_progress; + gint width, height, i; + + progress = data->progress; + gdk_drawable_get_size (data->window, &width, &height); + color = data->invisible->style->bg[GTK_STATE_SELECTED]; + + cairo_set_source_rgba (cr, 1., 1., 1., 0.); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint (cr); + + for (i = 0; i <= N_CIRCLES; i++) { - case STAGE_ONE: - case STAGE_TWO: - size = LARGE_SIZE; - break; - case STAGE_THREE: - case STAGE_FOUR: - size = SMALL_SIZE; - break; - default: - return FALSE; + if (progress < 0.) + break; + + circle_progress = MIN (1., (progress * 2)); + progress -= CIRCLES_PROGRESS_INTERVAL; + + if (circle_progress >= 1.) + continue; + + if (composite) + { + cairo_set_source_rgba (cr, + color.red / 65535., + color.green / 65535., + color.blue / 65535., + 1 - circle_progress); + cairo_arc (cr, + width / 2, + height / 2, + circle_progress * width / 2, + 0, 2 * G_PI); + + cairo_fill (cr); + cairo_stroke (cr); + } + else + { + cairo_set_source_rgb (cr, 0., 0., 0.); + cairo_set_line_width (cr, 3.); + cairo_arc (cr, + width / 2, + height / 2, + circle_progress * width / 2, + 0, 2 * G_PI); + cairo_stroke (cr); + + cairo_set_source_rgb (cr, 1., 1., 1.); + cairo_set_line_width (cr, 1.); + cairo_arc (cr, + width / 2, + height / 2, + circle_progress * width / 2, + 0, 2 * G_PI); + cairo_stroke (cr); + } } +} + +static gboolean +locate_pointer_expose (GtkWidget *widget, + GdkEventExpose *event, + gpointer user_data) +{ + GsdLocatePointerData *data = (GsdLocatePointerData *) user_data; + cairo_t *cr; + GdkBitmap *mask; + + if (event->window != data->window) + return FALSE; + + cr = gdk_cairo_create (data->window); - gdk_draw_rectangle (event->window, - widget->style->black_gc, - TRUE, - 0, 0, size, size); - switch (stage) + if (gtk_widget_is_composited (data->invisible)) + locate_pointer_paint (data, cr, TRUE); + else { - case STAGE_ONE: - case STAGE_THREE: - gdk_draw_rectangle (event->window, - widget->style->white_gc, - FALSE, - 1, 1, size - 3, size - 3); - break; - case STAGE_TWO: - case STAGE_FOUR: - points[0].x = size/2; - points[0].y = 0 + 1; - points[1].x = size - 2; - points[1].y = size/2; - points[2].x = size/2; - points[2].y = size - 2; - points[3].x = 0 + 1; - points[3].y = size/2; - gdk_draw_polygon (event->window, - widget->style->white_gc, - FALSE, points, 4); - break; - default: - g_assert_not_reached (); + locate_pointer_paint (data, cr, FALSE); + cairo_destroy (cr); + + /* create a bitmap for the shape, reuse the cairo_t to paint on it */ + mask = gdk_pixmap_new (data->window, WINDOW_SIZE, WINDOW_SIZE, 1); + cr = gdk_cairo_create (mask); + locate_pointer_paint (data, cr, FALSE); + gdk_window_shape_combine_mask (data->window, mask, 0, 0); } + cairo_destroy (cr); + return TRUE; } static void -setup_window (void) +timeline_frame_cb (GtkTimeline *timeline, + gdouble progress, + gpointer user_data) { - gint size; - GdkBitmap *mask; - GdkGC *gc; - GdkColor col; - GdkPoint points[4]; + GsdLocatePointerData *data = (GsdLocatePointerData *) user_data; - gdk_window_hide (window); - switch (stage) + if (gtk_widget_is_composited (data->invisible)) { - case STAGE_ONE: - case STAGE_TWO: - size = LARGE_SIZE; - break; - case STAGE_THREE: - case STAGE_FOUR: - size = SMALL_SIZE; - break; - default: - return; + gdk_window_invalidate_rect (data->window, NULL, FALSE); + data->progress = progress; } - - gdk_window_move_resize (window, - cursor_x - size/2, - cursor_y - size/2, - size, size); - mask = gdk_pixmap_new (window, size, size, 1); - gc = gdk_gc_new (mask); - switch (stage) + else if (progress >= data->progress + CIRCLES_PROGRESS_INTERVAL) { - case STAGE_ONE: - case STAGE_THREE: - col.pixel = 1; - gdk_gc_set_foreground (gc, &col); - gdk_draw_rectangle (mask, gc, TRUE, 0, 0, size, size); - col.pixel = 0; - gdk_gc_set_foreground (gc, &col); - gdk_draw_rectangle (mask, gc, TRUE, 3, 3, size - 6, size - 6); - break; - case STAGE_TWO: - case STAGE_FOUR: - col.pixel = 0; - gdk_gc_set_foreground (gc, &col); - gdk_draw_rectangle (mask, gc, TRUE, 0, 0, size, size); - col.pixel = 1; - gdk_gc_set_foreground (gc, &col); - points[0].x = size/2; - points[0].y = 0; - points[1].x = size - 1; - points[1].y = size/2; - points[2].x = size/2; - points[2].y = size - 1; - points[3].x = 0; - points[3].y = size/2; - gdk_draw_polygon (mask, gc, FALSE, points, 4); - points[0].x = size/2; - points[0].y = 0 + 1; - points[1].x = size - 2; - points[1].y = size/2; - points[2].x = size/2; - points[2].y = size - 2; - points[3].x = 0 + 1; - points[3].y = size/2; - gdk_draw_polygon (mask, gc, FALSE, points, 4); - points[0].x = size/2; - points[0].y = 0 + 2; - points[1].x = size - 3; - points[1].y = size/2; - points[2].x = size/2; - points[2].y = size - 3; - points[3].x = 0 + 2; - points[3].y = size/2; - gdk_draw_polygon (mask, gc, FALSE, points, 4); - break; - default: - g_assert_not_reached (); + /* only invalidate window each circle interval */ + gdk_window_invalidate_rect (data->window, NULL, FALSE); + data->progress += CIRCLES_PROGRESS_INTERVAL; } +} + +static void +timeline_finished_cb (GtkTimeline *timeline, + gpointer user_data) +{ + GsdLocatePointerData *data = (GsdLocatePointerData *) user_data; - gdk_window_shape_combine_mask (window, mask, 0, 0); - g_object_unref (G_OBJECT (gc)); - g_object_unref (G_OBJECT (mask)); - gdk_window_show (window); + /* hide window and unset shape */ + gdk_window_hide (data->window); + gdk_window_shape_combine_mask (data->window, NULL, 0, 0); } static void -create_window (GdkScreen *screen) +create_window (GsdLocatePointerData *data, + GdkScreen *screen) { + GdkColormap *colormap; + GdkVisual *visual; GdkWindowAttr attributes; - GtkWidget *invisible; - invisible = gtk_invisible_new_for_screen (screen); + colormap = gdk_screen_get_rgba_colormap (screen); + visual = gdk_screen_get_rgba_visual (screen); + + if (!colormap) + { + colormap = gdk_screen_get_rgb_colormap (screen); + visual = gdk_screen_get_rgb_visual (screen); + } attributes.window_type = GDK_WINDOW_TEMP; attributes.wclass = GDK_INPUT_OUTPUT; - attributes.visual = gtk_widget_get_visual (invisible); - attributes.colormap = gtk_widget_get_colormap (invisible); + attributes.visual = visual; + attributes.colormap = colormap; attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK; attributes.width = 1; attributes.height = 1; - window = gdk_window_new (gdk_screen_get_root_window (screen), - &attributes, - GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP); - gdk_window_set_user_data (window, invisible); - g_signal_connect (G_OBJECT (invisible), - "expose_event", - (GCallback) locate_pointer_expose, - NULL); + + data->window = gdk_window_new (gdk_screen_get_root_window (screen), + &attributes, + GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP); + + gdk_window_set_user_data (data->window, data->invisible); } -static gboolean -locate_pointer_timeout (gpointer data) +static GsdLocatePointerData * +gsd_locate_pointer_data_new (GdkScreen *screen) { - stage++; - if (stage == STAGE_DONE) - { - gdk_window_hide (window); - locate_pointer_id = 0; - return FALSE; - } - setup_window (); - return TRUE; + GsdLocatePointerData *data; + + data = g_new0 (GsdLocatePointerData, 1); + + data->invisible = gtk_invisible_new_for_screen (screen); + g_signal_connect (G_OBJECT (data->invisible), "expose_event", + G_CALLBACK (locate_pointer_expose), + data); + + data->timeline = gsd_timeline_new (ANIMATION_LENGTH); + g_signal_connect (data->timeline, "frame", + G_CALLBACK (timeline_frame_cb), data); + g_signal_connect (data->timeline, "finished", + G_CALLBACK (timeline_finished_cb), data); + + create_window (data, screen); + + return data; +} + +static void +move_locate_pointer_window (GsdLocatePointerData *data, + GdkScreen *screen) +{ + gint cursor_x, cursor_y; + GdkBitmap *mask; + GdkColor col; + GdkGC *gc; + + gdk_window_get_pointer (gdk_screen_get_root_window (screen), &cursor_x, &cursor_y, NULL); + + gdk_window_move_resize (data->window, + cursor_x - WINDOW_SIZE / 2, + cursor_y - WINDOW_SIZE / 2, + WINDOW_SIZE, WINDOW_SIZE); + + col.pixel = 0; + mask = gdk_pixmap_new (data->window, WINDOW_SIZE, WINDOW_SIZE, 1); + + gc = gdk_gc_new (mask); + gdk_gc_set_foreground (gc, &col); + gdk_draw_rectangle (mask, gc, TRUE, 0, 0, WINDOW_SIZE, WINDOW_SIZE); + + /* allow events to happen through the window */ + gdk_window_input_shape_combine_mask (data->window, mask, 0, 0); + + g_object_unref (mask); + g_object_unref (gc); } void gsd_locate_pointer (GdkScreen *screen) { - gdk_window_get_pointer (gdk_screen_get_root_window (screen), &cursor_x, &cursor_y, NULL); + if (!data) + data = gsd_locate_pointer_data_new (screen); + + gsd_timeline_pause (data->timeline); + gsd_timeline_rewind (data->timeline); - if (locate_pointer_id) - gtk_timeout_remove (locate_pointer_id); + /* Create again the window if it is not for the current screen */ + if (gdk_screen_get_number (screen) != gdk_screen_get_number (gdk_drawable_get_screen (data->window))) + { + gdk_window_set_user_data (data->window, NULL); + gdk_window_destroy (data->window); - /* Create the window if it is not created OR if it is not for the - * current screen. - */ + create_window (data, screen); + } - if (window == NULL) - create_window (screen); - else if( gdk_screen_get_number (screen) != gdk_screen_get_number (gdk_drawable_get_screen (window))) - create_window (screen); + data->progress = 0.; + gdk_window_show (data->window); + move_locate_pointer_window (data, screen); - stage = STAGE_ONE; - setup_window (); - gdk_window_show (window); - locate_pointer_id = gtk_timeout_add (100, locate_pointer_timeout, NULL); + gsd_timeline_start (data->timeline); } diff --git a/plugins/mouse/gsd-timeline.c b/plugins/mouse/gsd-timeline.c new file mode 100644 index 0000000..61b54d4 --- /dev/null +++ b/plugins/mouse/gsd-timeline.c @@ -0,0 +1,849 @@ +/* gsdtimeline.c + * + * Copyright (C) 2007-2008 Carlos Garnacho + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include "gsd-timeline.h" + +#define GSD_TIMELINE_GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GSD_TYPE_TIMELINE, GsdTimelinePriv)) +#define MSECS_PER_SEC 1000 +#define FRAME_INTERVAL(nframes) (MSECS_PER_SEC / nframes) +#define DEFAULT_FPS 30 + +typedef struct GsdTimelinePriv GsdTimelinePriv; + +struct GsdTimelinePriv +{ + guint duration; + guint fps; + guint source_id; + + GTimer *timer; + + GdkScreen *screen; + GsdTimelineProgressType progress_type; + GsdTimelineProgressFunc progress_func; + + guint loop : 1; + guint direction : 1; +}; + +enum { + PROP_0, + PROP_FPS, + PROP_DURATION, + PROP_LOOP, + PROP_DIRECTION, + PROP_SCREEN, + PROP_PROGRESS_TYPE, +}; + +enum { + STARTED, + PAUSED, + FINISHED, + FRAME, + LAST_SIGNAL +}; + +static guint signals [LAST_SIGNAL] = { 0, }; + + +static void gsd_timeline_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gsd_timeline_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void gsd_timeline_finalize (GObject *object); + + +G_DEFINE_TYPE (GsdTimeline, gsd_timeline, G_TYPE_OBJECT) + + +GType +gsd_timeline_direction_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue values[] = { + { GSD_TIMELINE_DIRECTION_FORWARD, "GSD_TIMELINE_DIRECTION_FORWARD", "forward" }, + { GSD_TIMELINE_DIRECTION_BACKWARD, "GSD_TIMELINE_DIRECTION_BACKWARD", "backward" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static (g_intern_static_string ("GsdTimelineDirection"), values); + } + + return type; +} + +GType +gsd_timeline_progress_type_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue values[] = { + { GSD_TIMELINE_PROGRESS_LINEAR, "GSD_TIMELINE_PROGRESS_LINEAR", "linear" }, + { GSD_TIMELINE_PROGRESS_SINUSOIDAL, "GSD_TIMELINE_PROGRESS_SINUSOIDAL", "sinusoidal" }, + { GSD_TIMELINE_PROGRESS_EXPONENTIAL, "GSD_TIMELINE_PROGRESS_EXPONENTIAL", "exponential" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static (g_intern_static_string ("GsdTimelineProgressType"), values); + } + + return type; +} + +static void +gsd_timeline_class_init (GsdTimelineClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->set_property = gsd_timeline_set_property; + object_class->get_property = gsd_timeline_get_property; + object_class->finalize = gsd_timeline_finalize; + + g_object_class_install_property (object_class, + PROP_FPS, + g_param_spec_uint ("fps", + "FPS", + "Frames per second for the timeline", + 1, + G_MAXUINT, + DEFAULT_FPS, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_DURATION, + g_param_spec_uint ("duration", + "Animation Duration", + "Animation Duration", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_LOOP, + g_param_spec_boolean ("loop", + "Loop", + "Whether the timeline loops or not", + FALSE, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_DIRECTION, + g_param_spec_enum ("direction", + "Direction", + "Whether the timeline moves forward or backward in time", + GSD_TYPE_TIMELINE_DIRECTION, + GSD_TIMELINE_DIRECTION_FORWARD, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_DIRECTION, + g_param_spec_enum ("progress-type", + "Progress type", + "Type of progress through the timeline", + GSD_TYPE_TIMELINE_PROGRESS_TYPE, + GSD_TIMELINE_PROGRESS_LINEAR, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_SCREEN, + g_param_spec_object ("screen", + "Screen", + "Screen to get the settings from", + GDK_TYPE_SCREEN, + G_PARAM_READWRITE)); + + signals[STARTED] = + g_signal_new ("started", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsdTimelineClass, started), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[PAUSED] = + g_signal_new ("paused", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsdTimelineClass, paused), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[FINISHED] = + g_signal_new ("finished", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsdTimelineClass, finished), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[FRAME] = + g_signal_new ("frame", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsdTimelineClass, frame), + NULL, NULL, + g_cclosure_marshal_VOID__DOUBLE, + G_TYPE_NONE, 1, + G_TYPE_DOUBLE); + + g_type_class_add_private (class, sizeof (GsdTimelinePriv)); +} + +static void +gsd_timeline_init (GsdTimeline *timeline) +{ + GsdTimelinePriv *priv; + + priv = GSD_TIMELINE_GET_PRIV (timeline); + + priv->fps = DEFAULT_FPS; + priv->duration = 0; + priv->direction = GSD_TIMELINE_DIRECTION_FORWARD; + priv->screen = gdk_screen_get_default (); +} + +static void +gsd_timeline_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsdTimeline *timeline; + GsdTimelinePriv *priv; + + timeline = GSD_TIMELINE (object); + priv = GSD_TIMELINE_GET_PRIV (timeline); + + switch (prop_id) + { + case PROP_FPS: + gsd_timeline_set_fps (timeline, g_value_get_uint (value)); + break; + case PROP_DURATION: + gsd_timeline_set_duration (timeline, g_value_get_uint (value)); + break; + case PROP_LOOP: + gsd_timeline_set_loop (timeline, g_value_get_boolean (value)); + break; + case PROP_DIRECTION: + gsd_timeline_set_direction (timeline, g_value_get_enum (value)); + break; + case PROP_SCREEN: + gsd_timeline_set_screen (timeline, + GDK_SCREEN (g_value_get_object (value))); + break; + case PROP_PROGRESS_TYPE: + gsd_timeline_set_progress_type (timeline, g_value_get_enum (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gsd_timeline_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsdTimeline *timeline; + GsdTimelinePriv *priv; + + timeline = GSD_TIMELINE (object); + priv = GSD_TIMELINE_GET_PRIV (timeline); + + switch (prop_id) + { + case PROP_FPS: + g_value_set_uint (value, priv->fps); + break; + case PROP_DURATION: + g_value_set_uint (value, priv->duration); + break; + case PROP_LOOP: + g_value_set_boolean (value, priv->loop); + break; + case PROP_DIRECTION: + g_value_set_enum (value, priv->direction); + break; + case PROP_SCREEN: + g_value_set_object (value, priv->screen); + break; + case PROP_PROGRESS_TYPE: + g_value_set_enum (value, priv->progress_type); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gsd_timeline_finalize (GObject *object) +{ + GsdTimelinePriv *priv; + + priv = GSD_TIMELINE_GET_PRIV (object); + + if (priv->source_id) + { + g_source_remove (priv->source_id); + priv->source_id = 0; + } + + if (priv->timer) + g_timer_destroy (priv->timer); + + G_OBJECT_CLASS (gsd_timeline_parent_class)->finalize (object); +} + +/* Sinusoidal progress */ +static gdouble +sinusoidal_progress (gdouble progress) +{ + return (sinf ((progress * G_PI) / 2)); +} + +static gdouble +exponential_progress (gdouble progress) +{ + return progress * progress; +} + +static GsdTimelineProgressFunc +progress_type_to_func (GsdTimelineProgressType type) +{ + if (type == GSD_TIMELINE_PROGRESS_SINUSOIDAL) + return sinusoidal_progress; + else if (type == GSD_TIMELINE_PROGRESS_EXPONENTIAL) + return exponential_progress; + + return NULL; +} + +static gboolean +gsd_timeline_run_frame (GsdTimeline *timeline, + gboolean enable_animations) +{ + GsdTimelinePriv *priv; + gdouble linear_progress, progress; + guint elapsed_time; + GsdTimelineProgressFunc progress_func = NULL; + + priv = GSD_TIMELINE_GET_PRIV (timeline); + + if (enable_animations) + { + elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000); + + linear_progress = (gdouble) elapsed_time / priv->duration; + + if (priv->direction == GSD_TIMELINE_DIRECTION_BACKWARD) + linear_progress = 1 - linear_progress; + + linear_progress = CLAMP (linear_progress, 0., 1.); + + if (priv->progress_func) + progress_func = priv->progress_func; + else if (priv->progress_type) + progress_func = progress_type_to_func (priv->progress_type); + + if (progress_func) + progress = (progress_func) (linear_progress); + else + progress = linear_progress; + } + else + progress = (priv->direction == GSD_TIMELINE_DIRECTION_FORWARD) ? 1.0 : 0.0; + + g_signal_emit (timeline, signals [FRAME], 0, + CLAMP (progress, 0.0, 1.0)); + + if ((priv->direction == GSD_TIMELINE_DIRECTION_FORWARD && progress >= 1.0) || + (priv->direction == GSD_TIMELINE_DIRECTION_BACKWARD && progress <= 0.0)) + { + if (!priv->loop) + { + if (priv->source_id) + { + g_source_remove (priv->source_id); + priv->source_id = 0; + } + + g_signal_emit (timeline, signals [FINISHED], 0); + return FALSE; + } + else + gsd_timeline_rewind (timeline); + } + + return TRUE; +} + +static gboolean +gsd_timeline_frame_idle_func (GsdTimeline *timeline) +{ + return gsd_timeline_run_frame (timeline, TRUE); +} + +/** + * gsd_timeline_new: + * @duration: duration in milliseconds for the timeline + * + * Creates a new #GsdTimeline with the specified number of frames. + * + * Return Value: the newly created #GsdTimeline + **/ +GsdTimeline * +gsd_timeline_new (guint duration) +{ + return g_object_new (GSD_TYPE_TIMELINE, + "duration", duration, + NULL); +} + +GsdTimeline * +gsd_timeline_new_for_screen (guint duration, + GdkScreen *screen) +{ + return g_object_new (GSD_TYPE_TIMELINE, + "duration", duration, + "screen", screen, + NULL); +} + +/** + * gsd_timeline_start: + * @timeline: A #GsdTimeline + * + * Runs the timeline from the current frame. + **/ +void +gsd_timeline_start (GsdTimeline *timeline) +{ + GsdTimelinePriv *priv; + GtkSettings *settings; + gboolean enable_animations = FALSE; + + g_return_if_fail (GSD_IS_TIMELINE (timeline)); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + + if (priv->screen) + { + settings = gtk_settings_get_for_screen (priv->screen); + g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL); + } + + if (enable_animations) + { + if (!priv->source_id) + { + if (priv->timer) + g_timer_continue (priv->timer); + else + priv->timer = g_timer_new (); + + /* sanity check */ + g_assert (priv->fps > 0); + + g_signal_emit (timeline, signals [STARTED], 0); + + priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps), + (GSourceFunc) gsd_timeline_frame_idle_func, + timeline); + } + } + else + { + /* If animations are not enabled, only run the last frame, + * it take us instantaneously to the last state of the animation. + * The only potential flaw happens when people use the ::finished + * signal to trigger another animation, or even worse, finally + * loop into this animation again. + */ + g_signal_emit (timeline, signals [STARTED], 0); + gsd_timeline_run_frame (timeline, FALSE); + } +} + +/** + * gsd_timeline_pause: + * @timeline: A #GsdTimeline + * + * Pauses the timeline. + **/ +void +gsd_timeline_pause (GsdTimeline *timeline) +{ + GsdTimelinePriv *priv; + + g_return_if_fail (GSD_IS_TIMELINE (timeline)); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + + if (priv->source_id) + { + g_source_remove (priv->source_id); + priv->source_id = 0; + g_timer_stop (priv->timer); + g_signal_emit (timeline, signals [PAUSED], 0); + } +} + +/** + * gsd_timeline_rewind: + * @timeline: A #GsdTimeline + * + * Rewinds the timeline. + **/ +void +gsd_timeline_rewind (GsdTimeline *timeline) +{ + GsdTimelinePriv *priv; + + g_return_if_fail (GSD_IS_TIMELINE (timeline)); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + + /* destroy and re-create timer if neccesary */ + if (priv->timer) + { + g_timer_destroy (priv->timer); + + if (gsd_timeline_is_running (timeline)) + priv->timer = g_timer_new (); + else + priv->timer = NULL; + } +} + +/** + * gsd_timeline_is_running: + * @timeline: A #GsdTimeline + * + * Returns whether the timeline is running or not. + * + * Return Value: %TRUE if the timeline is running + **/ +gboolean +gsd_timeline_is_running (GsdTimeline *timeline) +{ + GsdTimelinePriv *priv; + + g_return_val_if_fail (GSD_IS_TIMELINE (timeline), FALSE); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + + return (priv->source_id != 0); +} + +/** + * gsd_timeline_get_fps: + * @timeline: A #GsdTimeline + * + * Returns the number of frames per second. + * + * Return Value: frames per second + **/ +guint +gsd_timeline_get_fps (GsdTimeline *timeline) +{ + GsdTimelinePriv *priv; + + g_return_val_if_fail (GSD_IS_TIMELINE (timeline), 1); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + return priv->fps; +} + +/** + * gsd_timeline_set_fps: + * @timeline: A #GsdTimeline + * @fps: frames per second + * + * Sets the number of frames per second that + * the timeline will play. + **/ +void +gsd_timeline_set_fps (GsdTimeline *timeline, + guint fps) +{ + GsdTimelinePriv *priv; + + g_return_if_fail (GSD_IS_TIMELINE (timeline)); + g_return_if_fail (fps > 0); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + + priv->fps = fps; + + if (gsd_timeline_is_running (timeline)) + { + g_source_remove (priv->source_id); + priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps), + (GSourceFunc) gsd_timeline_run_frame, + timeline); + } + + g_object_notify (G_OBJECT (timeline), "fps"); +} + +/** + * gsd_timeline_get_loop: + * @timeline: A #GsdTimeline + * + * Returns whether the timeline loops to the + * beginning when it has reached the end. + * + * Return Value: %TRUE if the timeline loops + **/ +gboolean +gsd_timeline_get_loop (GsdTimeline *timeline) +{ + GsdTimelinePriv *priv; + + g_return_val_if_fail (GSD_IS_TIMELINE (timeline), FALSE); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + return priv->loop; +} + +/** + * gsd_timeline_set_loop: + * @timeline: A #GsdTimeline + * @loop: %TRUE to make the timeline loop + * + * Sets whether the timeline loops to the beginning + * when it has reached the end. + **/ +void +gsd_timeline_set_loop (GsdTimeline *timeline, + gboolean loop) +{ + GsdTimelinePriv *priv; + + g_return_if_fail (GSD_IS_TIMELINE (timeline)); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + priv->loop = loop; + + g_object_notify (G_OBJECT (timeline), "loop"); +} + +void +gsd_timeline_set_duration (GsdTimeline *timeline, + guint duration) +{ + GsdTimelinePriv *priv; + + g_return_if_fail (GSD_IS_TIMELINE (timeline)); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + + priv->duration = duration; + + g_object_notify (G_OBJECT (timeline), "duration"); +} + +guint +gsd_timeline_get_duration (GsdTimeline *timeline) +{ + GsdTimelinePriv *priv; + + g_return_val_if_fail (GSD_IS_TIMELINE (timeline), 0); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + + return priv->duration; +} + +/** + * gsd_timeline_get_direction: + * @timeline: A #GsdTimeline + * + * Returns the direction of the timeline. + * + * Return Value: direction + **/ +GsdTimelineDirection +gsd_timeline_get_direction (GsdTimeline *timeline) +{ + GsdTimelinePriv *priv; + + g_return_val_if_fail (GSD_IS_TIMELINE (timeline), GSD_TIMELINE_DIRECTION_FORWARD); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + return priv->direction; +} + +/** + * gsd_timeline_set_direction: + * @timeline: A #GsdTimeline + * @direction: direction + * + * Sets the direction of the timeline. + **/ +void +gsd_timeline_set_direction (GsdTimeline *timeline, + GsdTimelineDirection direction) +{ + GsdTimelinePriv *priv; + + g_return_if_fail (GSD_IS_TIMELINE (timeline)); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + priv->direction = direction; + + g_object_notify (G_OBJECT (timeline), "direction"); +} + +GdkScreen * +gsd_timeline_get_screen (GsdTimeline *timeline) +{ + GsdTimelinePriv *priv; + + g_return_val_if_fail (GSD_IS_TIMELINE (timeline), NULL); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + return priv->screen; +} + +void +gsd_timeline_set_screen (GsdTimeline *timeline, + GdkScreen *screen) +{ + GsdTimelinePriv *priv; + + g_return_if_fail (GSD_IS_TIMELINE (timeline)); + g_return_if_fail (GDK_IS_SCREEN (screen)); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + + if (priv->screen) + g_object_unref (priv->screen); + + priv->screen = g_object_ref (screen); + + g_object_notify (G_OBJECT (timeline), "screen"); +} + +void +gsd_timeline_set_progress_type (GsdTimeline *timeline, + GsdTimelineProgressType type) +{ + GsdTimelinePriv *priv; + + g_return_if_fail (GSD_IS_TIMELINE (timeline)); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + + priv->progress_type = type; + + g_object_notify (G_OBJECT (timeline), "progress-type"); +} + +GsdTimelineProgressType +gsd_timeline_get_progress_type (GsdTimeline *timeline) +{ + GsdTimelinePriv *priv; + + g_return_val_if_fail (GSD_IS_TIMELINE (timeline), GSD_TIMELINE_PROGRESS_LINEAR); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + + if (priv->progress_func) + return GSD_TIMELINE_PROGRESS_LINEAR; + + return priv->progress_type; +} + +/** + * gsd_timeline_set_progress_func: + * @timeline: A #GsdTimeline + * @progress_func: progress function + * + * Sets the progress function. This function will be used to calculate + * a different progress to pass to the ::frame signal based on the + * linear progress through the timeline. Setting progress_func + * to %NULL will make the timeline use the default function, + * which is just a linear progress. + * + * All progresses are in the [0.0, 1.0] range. + **/ +void +gsd_timeline_set_progress_func (GsdTimeline *timeline, + GsdTimelineProgressFunc progress_func) +{ + GsdTimelinePriv *priv; + + g_return_if_fail (GSD_IS_TIMELINE (timeline)); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + priv->progress_func = progress_func; +} + +gdouble +gsd_timeline_get_progress (GsdTimeline *timeline) +{ + GsdTimelinePriv *priv; + GsdTimelineProgressFunc progress_func = NULL; + gdouble linear_progress, progress; + guint elapsed_time; + + g_return_val_if_fail (GSD_IS_TIMELINE (timeline), 0.0); + + priv = GSD_TIMELINE_GET_PRIV (timeline); + + if (!priv->timer) + return 0.; + + elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000); + + linear_progress = (gdouble) elapsed_time / priv->duration; + + if (priv->direction == GSD_TIMELINE_DIRECTION_BACKWARD) + linear_progress = 1 - linear_progress; + + linear_progress = CLAMP (linear_progress, 0., 1.); + + if (priv->progress_func) + progress_func = priv->progress_func; + else if (priv->progress_type) + progress_func = progress_type_to_func (priv->progress_type); + + if (progress_func) + progress = (progress_func) (linear_progress); + else + progress = linear_progress; + + return CLAMP (progress, 0., 1.); +} diff --git a/plugins/mouse/gsd-timeline.h b/plugins/mouse/gsd-timeline.h new file mode 100644 index 0000000..2eee4ec --- /dev/null +++ b/plugins/mouse/gsd-timeline.h @@ -0,0 +1,124 @@ +/* gsdtimeline.h + * + * Copyright (C) 2007 Carlos Garnacho + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GSD_TIMELINE_H__ +#define __GSD_TIMELINE_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GSD_TYPE_TIMELINE_DIRECTION (gsd_timeline_direction_get_type ()) +#define GSD_TYPE_TIMELINE_PROGRESS_TYPE (gsd_timeline_progress_type_get_type ()) +#define GSD_TYPE_TIMELINE (gsd_timeline_get_type ()) +#define GSD_TIMELINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSD_TYPE_TIMELINE, GsdTimeline)) +#define GSD_TIMELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSD_TYPE_TIMELINE, GsdTimelineClass)) +#define GSD_IS_TIMELINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSD_TYPE_TIMELINE)) +#define GSD_IS_TIMELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSD_TYPE_TIMELINE)) +#define GSD_TIMELINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSD_TYPE_TIMELINE, GsdTimelineClass)) + +typedef enum { + GSD_TIMELINE_DIRECTION_FORWARD, + GSD_TIMELINE_DIRECTION_BACKWARD +} GsdTimelineDirection; + +typedef enum { + GSD_TIMELINE_PROGRESS_LINEAR, + GSD_TIMELINE_PROGRESS_SINUSOIDAL, + GSD_TIMELINE_PROGRESS_EXPONENTIAL +} GsdTimelineProgressType; + +typedef struct GsdTimeline GsdTimeline; +typedef struct GsdTimelineClass GsdTimelineClass; + +struct GsdTimeline +{ + GObject parent_instance; +}; + +struct GsdTimelineClass +{ + GObjectClass parent_class; + + void (* started) (GsdTimeline *timeline); + void (* finished) (GsdTimeline *timeline); + void (* paused) (GsdTimeline *timeline); + + void (* frame) (GsdTimeline *timeline, + gdouble progress); + + void (* __gsd_reserved1) (void); + void (* __gsd_reserved2) (void); + void (* __gsd_reserved3) (void); + void (* __gsd_reserved4) (void); +}; + +typedef gdouble (*GsdTimelineProgressFunc) (gdouble progress); + + +GType gsd_timeline_get_type (void) G_GNUC_CONST; +GType gsd_timeline_direction_get_type (void) G_GNUC_CONST; +GType gsd_timeline_progress_type_get_type (void) G_GNUC_CONST; + +GsdTimeline *gsd_timeline_new (guint duration); +GsdTimeline *gsd_timeline_new_for_screen (guint duration, + GdkScreen *screen); + +void gsd_timeline_start (GsdTimeline *timeline); +void gsd_timeline_pause (GsdTimeline *timeline); +void gsd_timeline_rewind (GsdTimeline *timeline); + +gboolean gsd_timeline_is_running (GsdTimeline *timeline); + +guint gsd_timeline_get_fps (GsdTimeline *timeline); +void gsd_timeline_set_fps (GsdTimeline *timeline, + guint fps); + +gboolean gsd_timeline_get_loop (GsdTimeline *timeline); +void gsd_timeline_set_loop (GsdTimeline *timeline, + gboolean loop); + +guint gsd_timeline_get_duration (GsdTimeline *timeline); +void gsd_timeline_set_duration (GsdTimeline *timeline, + guint duration); + +GdkScreen *gsd_timeline_get_screen (GsdTimeline *timeline); +void gsd_timeline_set_screen (GsdTimeline *timeline, + GdkScreen *screen); + +GsdTimelineDirection gsd_timeline_get_direction (GsdTimeline *timeline); +void gsd_timeline_set_direction (GsdTimeline *timeline, + GsdTimelineDirection direction); + +GsdTimelineProgressType gsd_timeline_get_progress_type (GsdTimeline *timeline); +void gsd_timeline_set_progress_type (GsdTimeline *timeline, + GsdTimelineProgressType type); +void gsd_timeline_get_progress_func (GsdTimeline *timeline); + +void gsd_timeline_set_progress_func (GsdTimeline *timeline, + GsdTimelineProgressFunc progress_func); + +gdouble gsd_timeline_get_progress (GsdTimeline *timeline); + + +G_END_DECLS + +#endif /* __GSD_TIMELINE_H__ */