diff options
Diffstat (limited to 'savers')
31 files changed, 8191 insertions, 0 deletions
diff --git a/savers/Makefile.am b/savers/Makefile.am new file mode 100644 index 0000000..298177e --- /dev/null +++ b/savers/Makefile.am @@ -0,0 +1,142 @@ +## We require new-style dependency handling. +AUTOMAKE_OPTIONS = 1.7 + +NULL = + +INCLUDES = \ + -I. \ + -I$(srcdir) \ + $(MATE_SCREENSAVER_SAVER_CFLAGS) \ + $(DISABLE_DEPRECATED_CFLAGS) \ + -DLIBEXECDIR=\""$(libexecdir)"\" \ + -DMATELOCALEDIR=\""$(datadir)/locale"\" \ + -DDATADIR=\""$(datadir)"\" \ + $(WARN_CFLAGS) \ + $(NULL) + +DESKTOP_IN_IN_FILES = \ + cosmos-slideshow.desktop.in.in \ + footlogo-floaters.desktop.in.in \ + personal-slideshow.desktop.in.in \ + popsquares.desktop.in.in \ + $(NULL) + +cosmos-slideshow.desktop.in: cosmos-slideshow.desktop.in.in Makefile + $(edit) $< >$@ +footlogo-floaters.desktop.in: footlogo-floaters.desktop.in.in Makefile + $(edit) $< >$@ +personal-slideshow.desktop.in: personal-slideshow.desktop.in.in Makefile + $(edit) $< >$@ +popsquares.desktop.in: popsquares.desktop.in.in Makefile + $(edit) $< >$@ + +@INTLTOOL_DESKTOP_RULE@ + +edit = sed \ + -e 's|@SLIDESHOW_COSMOS_DIR[@]|$(SLIDESHOW_COSMOS_DIR)|g' \ + -e 's|@FLOATERS_FOOT_LOGO_IMAGE[@]|$(FLOATERS_FOOT_LOGO_IMAGE)|g' \ + -e 's|@SAVERDIR[@]|$(privlibexecdir)|g' + +themesdir = $(datadir)/applications/screensavers +themes_in_files = \ + cosmos-slideshow.desktop.in \ + footlogo-floaters.desktop.in \ + personal-slideshow.desktop.in \ + popsquares.desktop.in \ + $(NULL) +themes_DATA = $(themes_in_files:.desktop.in=.desktop) + +BUILT_SOURCES = \ + gs-theme-engine-marshal.c \ + gs-theme-engine-marshal.h \ + $(NULL) + +gs-theme-engine-marshal.c: gs-theme-engine-marshal.list + echo "#include \"gs-theme-engine-marshal.h\"" > $@ && \ + @GLIB_GENMARSHAL@ $< --prefix=gs_theme_engine_marshal --body >> $@ + +gs-theme-engine-marshal.h: gs-theme-engine-marshal.list + @GLIB_GENMARSHAL@ $< --prefix=gs_theme_engine_marshal --header > $@ + +noinst_LIBRARIES = \ + libgs-theme-engine.a + +libgs_theme_engine_a_CPPFLAGS = \ + -DDATADIR=\""$(datadir)"\" \ + $(NULL) + +libgs_theme_engine_a_SOURCES = \ + $(BUILT_SOURCES) \ + gs-theme-window.h \ + gs-theme-window.c \ + gs-theme-engine.c \ + gs-theme-engine.h \ + $(NULL) + +saverdir = $(libexecdir)/mate-screensaver +saver_PROGRAMS = \ + floaters \ + popsquares \ + slideshow \ + $(NULL) + +floaters_SOURCES = \ + floaters.c \ + $(NULL) + +floaters_LDADD = \ + libgs-theme-engine.a \ + $(MATE_SCREENSAVER_SAVER_LIBS) \ + -lm \ + $(NULL) + +popsquares_SOURCES = \ + gste-popsquares.c \ + gste-popsquares.h \ + popsquares.c \ + $(NULL) + +popsquares_LDADD = \ + libgs-theme-engine.a \ + $(MATE_SCREENSAVER_SAVER_LIBS) \ + $(NULL) + +slideshow_SOURCES = \ + gste-slideshow.c \ + gste-slideshow.h \ + xdg-user-dir-lookup.c \ + xdg-user-dir-lookup.h \ + slideshow.c \ + $(NULL) + +slideshow_LDADD = \ + libgs-theme-engine.a \ + $(MATE_SCREENSAVER_SAVER_LIBS) \ + $(NULL) + +EXTRA_DIST = \ + gs-theme-engine-marshal.list \ + $(DESKTOP_IN_IN_FILES) \ + $(themes_in_files) \ + $(NULL) + +CLEANFILES = \ + $(BUILT_SOURCES) \ + cosmos-slideshow.desktop.in \ + footlogo-floaters.desktop.in \ + popsquares.desktop.in \ + personal-slideshow.desktop.in \ + $(NULL) + +DISTCLEANFILES = \ + $(themes_DATA) \ + $(NULL) + +MAINTAINERCLEANFILES = \ + *~ \ + Makefile.in \ + cosmos-slideshow.desktop \ + footlogo-floaters.desktop \ + personal-slideshow.desktop \ + popsquares.desktop \ + $(NULL) diff --git a/savers/cosmos-slideshow.desktop.in.in b/savers/cosmos-slideshow.desktop.in.in new file mode 100644 index 0000000..cc51661 --- /dev/null +++ b/savers/cosmos-slideshow.desktop.in.in @@ -0,0 +1,10 @@ +[Desktop Entry] +_Name=Cosmos +_Comment=Display a slideshow of pictures of the cosmos +Exec=@SAVERDIR@/slideshow --location=@SLIDESHOW_COSMOS_DIR@ +TryExec=@SAVERDIR@/slideshow +StartupNotify=false +Terminal=false +Type=Application +Categories=MATE;Screensaver; +OnlyShowIn=MATE; diff --git a/savers/floaters.c b/savers/floaters.c new file mode 100644 index 0000000..8052e95 --- /dev/null +++ b/savers/floaters.c @@ -0,0 +1,1269 @@ +/* + * Copyright (C) 2005 Ray Strode <[email protected]>, + * Matthias Clasen <[email protected]>, + * Søren Sandmann <[email protected]> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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. + * + * Originally written by: Ray Strode <[email protected]> + * + * Later contributions by: Matthias Clasen <[email protected]> + * Søren Sandmann <[email protected]> + */ + +#include "config.h" + +#include <math.h> +#include <stdlib.h> +#include <sysexits.h> +#include <time.h> + +#include <glib.h> +#include <glib/gi18n.h> + +#include <gdk/gdk.h> +#include <gdk/gdkx.h> + +#include <gtk/gtk.h> + +#include "gs-theme-window.h" + +#ifndef trunc +#define trunc(x) (((x) > 0.0) ? floor((x)) : -floor(-(x))) +#endif + +#ifndef OPTIMAL_FRAME_RATE +#define OPTIMAL_FRAME_RATE (25.0) +#endif + +#ifndef STAT_PRINT_FREQUENCY +#define STAT_PRINT_FREQUENCY (2000) +#endif + +#ifndef FLOATER_MAX_SIZE +#define FLOATER_MAX_SIZE (128.0) +#endif + +#ifndef FLOATER_MIN_SIZE +#define FLOATER_MIN_SIZE (16.0) +#endif +#ifndef FLOATER_DEFAULT_COUNT +#define FLOATER_DEFAULT_COUNT (5) +#endif + +#ifndef SMALL_ANGLE +#define SMALL_ANGLE (0.025 * G_PI) +#endif + +#ifndef BIG_ANGLE +#define BIG_ANGLE (0.125 * G_PI) +#endif + +#ifndef GAMMA +#define GAMMA 2.2 +#endif + +static gboolean should_show_paths = FALSE; +static gboolean should_do_rotations = FALSE; +static gboolean should_print_stats = FALSE; +static gint max_floater_count = FLOATER_DEFAULT_COUNT; +static gchar *geometry = NULL; +static gchar **filenames = NULL; + +static GOptionEntry options[] = +{ + { + "show-paths", 'p', 0, G_OPTION_ARG_NONE, &should_show_paths, + N_("Show paths that images follow"), NULL + }, + + { + "do-rotations", 'r', 0, G_OPTION_ARG_NONE, &should_do_rotations, + N_("Occasionally rotate images as they move"), NULL + }, + + { + "print-stats", 's', 0, G_OPTION_ARG_NONE, &should_print_stats, + N_("Print out frame rate and other statistics"), NULL + }, + + { + "number-of-images", 'n', 0, G_OPTION_ARG_INT, &max_floater_count, + N_("The maximum number of images to keep on screen"), N_("MAX_IMAGES") + }, + + { + "geometry", 0, 0, G_OPTION_ARG_STRING, &geometry, + N_("The initial size and position of window"), N_("WIDTHxHEIGHT+X+Y") + }, + + { + G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, + N_("The source image to use"), NULL + }, + + {NULL} +}; + + +typedef struct _Point Point; +typedef struct _Path Path; +typedef struct _Rectangle Rectangle; +typedef struct _ScreenSaverFloater ScreenSaverFloater; +typedef struct _CachedSource CachedSource; +typedef struct _ScreenSaver ScreenSaver; + +struct _Point +{ + gdouble x, y; +}; + +struct _Path +{ + Point start_point; + Point start_control_point; + Point end_control_point; + Point end_point; + + gdouble x_linear_coefficient, + y_linear_coefficient; + + gdouble x_quadratic_coefficient, + y_quadratic_coefficient; + + gdouble x_cubic_coefficient, + y_cubic_coefficient; + + gdouble duration; +}; + +struct _CachedSource +{ + cairo_pattern_t *pattern; + gint width, height; +}; + +struct _Rectangle +{ + Point top_left_point; + Point bottom_right_point; +}; + +struct _ScreenSaverFloater +{ + GdkRectangle bounds; + + Point start_position; + Point position; + + gdouble scale; + gdouble opacity; + + Path *path; + gdouble path_start_time; + gdouble path_start_scale; + gdouble path_end_scale; + + gdouble angle; + gdouble angle_increment; +}; + +struct _ScreenSaver +{ + GtkWidget *drawing_area; + Rectangle canvas_rectangle; + GHashTable *cached_sources; + + char *filename; + + gdouble first_update_time; + + gdouble last_calculated_stats_time, + current_calculated_stats_time; + gint update_count, frame_count; + + gdouble updates_per_second; + gdouble frames_per_second; + + guint state_update_timeout_id; + guint stats_update_timeout_id; + + GList *floaters; + gint max_floater_count; + + guint should_do_rotations: 1; + guint should_show_paths : 1; + guint draw_ops_pending : 1; +}; + +static Path *path_new (Point *start_point, + Point *start_control_point, + Point *end_control_point, + Point *end_point, + gdouble duration); +static void path_free (Path *path); + +static ScreenSaverFloater *screen_saver_floater_new (ScreenSaver *screen_saver, + Point *position, + gdouble scale); +static void screen_saver_floater_free (ScreenSaver *screen_saver, + ScreenSaverFloater *floater); +static gboolean screen_saver_floater_is_off_canvas (ScreenSaver *screen_saver, + ScreenSaverFloater *floater); +static gboolean screen_saver_floater_should_bubble_up (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble performance_ratio, + gdouble *duration); +static gboolean screen_saver_floater_should_come_on_screen (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble performance_ratio, + gdouble *duration); +static Point screen_saver_floater_get_position_from_time (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble time); +static gdouble screen_saver_floater_get_scale_from_time (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble time); +static gdouble screen_saver_floater_get_angle_from_time (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble time); + +static Path *screen_saver_floater_create_path_to_on_screen (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble duration); +static Path *screen_saver_floater_create_path_to_bubble_up (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble duration); +static Path *screen_saver_floater_create_path_to_random_point (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble duration); +static Path *screen_saver_floater_create_path (ScreenSaver *screen_saver, + ScreenSaverFloater *floater); +static void screen_saver_floater_update_state (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble time); + +static gboolean screen_saver_floater_do_draw (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + cairo_t *context); + +static CachedSource *cached_source_new (cairo_pattern_t *pattern, + gint width, + gint height); +static void cached_source_free (CachedSource *source); + +static ScreenSaver *screen_saver_new (GtkDrawingArea *drawing_area, + const gchar *filename, + gint max_floater_count, + gboolean should_do_rotations, + gboolean should_show_paths); +static void screen_saver_free (ScreenSaver *screen_saver); +static gdouble screen_saver_get_timestamp (ScreenSaver *screen_saver); +static void screen_saver_get_initial_state (ScreenSaver *screen_saver); +static void screen_saver_update_state (ScreenSaver *screen_saver, + gdouble time); +static gboolean screen_saver_do_update_state (ScreenSaver *screen_saver); +static gboolean screen_saver_do_update_stats (ScreenSaver *screen_saver); +static gdouble screen_saver_get_updates_per_second (ScreenSaver *screen_saver); +static gdouble screen_saver_get_frames_per_second (ScreenSaver *screen_saver); +static gdouble screen_saver_get_image_cache_usage (ScreenSaver *screen_saver); +static void screen_saver_create_floaters (ScreenSaver *screen_saver); +static void screen_saver_destroy_floaters (ScreenSaver *screen_saver); +static void screen_saver_on_size_allocate (ScreenSaver *screen_saver, + GtkAllocation *allocation); +static void screen_saver_on_expose_event (ScreenSaver *screen_saver, + GdkEventExpose *event); +static gboolean do_print_screen_saver_stats (ScreenSaver *screen_saver); +static GdkPixbuf *gamma_correct (const GdkPixbuf *input_pixbuf); + +static CachedSource* +cached_source_new (cairo_pattern_t *pattern, + gint width, + gint height) +{ + CachedSource *source; + + source = g_new (CachedSource, 1); + source->pattern = cairo_pattern_reference (pattern); + source->width = width; + source->height = height; + + return source; +} + +static void +cached_source_free (CachedSource *source) +{ + if (source == NULL) + return; + + cairo_pattern_destroy (source->pattern); + + g_free (source); +} + +static Path * +path_new (Point *start_point, + Point *start_control_point, + Point *end_control_point, + Point *end_point, + gdouble duration) +{ + Path *path; + + path = g_new (Path, 1); + path->start_point = *start_point; + path->start_control_point = *start_control_point; + path->end_control_point = *end_control_point; + path->end_point = *end_point; + path->duration = duration; + + /* we precompute the coefficients to the cubic bezier curve here + * so that we don't have to do it repeatedly later The equation is: + * + * B(t) = A * t^3 + B * t^2 + C * t + start_point + */ + path->x_linear_coefficient = 3 * (start_control_point->x - start_point->x); + path->x_quadratic_coefficient = 3 * (end_control_point->x - + start_control_point->x) - + path->x_linear_coefficient; + path->x_cubic_coefficient = end_point->x - start_point->x - + path->x_linear_coefficient - + path->x_quadratic_coefficient; + + path->y_linear_coefficient = 3 * (start_control_point->y - start_point->y); + path->y_quadratic_coefficient = 3 * (end_control_point->y - + start_control_point->y) - + path->y_linear_coefficient; + path->y_cubic_coefficient = end_point->y - start_point->y - + path->y_linear_coefficient - + path->y_quadratic_coefficient; + return path; +} + +static void +path_free (Path *path) +{ + g_free (path); +} + +static ScreenSaverFloater* +screen_saver_floater_new (ScreenSaver *screen_saver, + Point *position, + gdouble scale) +{ + ScreenSaverFloater *floater; + + floater = g_new (ScreenSaverFloater, 1); + floater->bounds.width = 0; + floater->start_position = *position; + floater->position = *position; + floater->scale = scale; + floater->opacity = pow (scale, 1.0 / GAMMA); + floater->path = NULL; + floater->path_start_time = 0.0; + floater->path_start_scale = 1.0; + floater->path_end_scale = 0.0; + + floater->angle = 0.0; + floater->angle_increment = 0.0; + + return floater; +} + +void +screen_saver_floater_free (ScreenSaver *screen_saver, + ScreenSaverFloater *floater) +{ + if (floater == NULL) + return; + + path_free (floater->path); + + g_free (floater); +} + +static gboolean +screen_saver_floater_is_off_canvas (ScreenSaver *screen_saver, + ScreenSaverFloater *floater) +{ + if ((floater->position.x < screen_saver->canvas_rectangle.top_left_point.x) || + (floater->position.x > screen_saver->canvas_rectangle.bottom_right_point.x) || + (floater->position.y < screen_saver->canvas_rectangle.top_left_point.y) || + (floater->position.y > screen_saver->canvas_rectangle.bottom_right_point.y)) + return TRUE; + + return FALSE; +} + +static gboolean +screen_saver_floater_should_come_on_screen (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble performance_ratio, + gdouble *duration) +{ + + if (!screen_saver_floater_is_off_canvas (screen_saver, floater)) + return FALSE; + + if ((abs (performance_ratio - .5) >= G_MINDOUBLE) && + (g_random_double () > .5)) + { + if (duration) + *duration = g_random_double_range (3.0, 7.0); + + return TRUE; + } + + return FALSE; +} + +static gboolean +screen_saver_floater_should_bubble_up (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble performance_ratio, + gdouble *duration) +{ + + if ((performance_ratio < .5) && (g_random_double () > .5)) + { + if (duration) + *duration = performance_ratio * 30.0; + + return TRUE; + } + + if ((floater->scale < .3) && (g_random_double () > .6)) + { + if (duration) + *duration = 30.0; + + return TRUE; + } + + return FALSE; +} + +static Point +screen_saver_floater_get_position_from_time (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble time) +{ + Point point; + + time = time / floater->path->duration; + + point.x = floater->path->x_cubic_coefficient * (time * time * time) + + floater->path->x_quadratic_coefficient * (time * time) + + floater->path->x_linear_coefficient * (time) + + floater->path->start_point.x; + point.y = floater->path->y_cubic_coefficient * (time * time * time) + + floater->path->y_quadratic_coefficient * (time * time) + + floater->path->y_linear_coefficient * (time) + + floater->path->start_point.y; + + return point; +} + +static gdouble +screen_saver_floater_get_scale_from_time (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble time) +{ + gdouble completion_ratio, total_scale_growth, new_scale; + + completion_ratio = time / floater->path->duration; + total_scale_growth = (floater->path_end_scale - floater->path_start_scale); + new_scale = floater->path_start_scale + total_scale_growth * completion_ratio; + + return CLAMP (new_scale, 0.0, 1.0); +} + +static gdouble +screen_saver_floater_get_angle_from_time (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble time) +{ + gdouble completion_ratio; + gdouble total_rotation; + + completion_ratio = time / floater->path->duration; + total_rotation = floater->angle_increment * floater->path->duration; + + return floater->angle + total_rotation * completion_ratio; +} + +static Path * +screen_saver_floater_create_path_to_on_screen (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble duration) +{ + Point start_position, end_position, start_control_point, end_control_point; + start_position = floater->position; + + end_position.x = g_random_double_range (.25, .75) * + (screen_saver->canvas_rectangle.top_left_point.x + + screen_saver->canvas_rectangle.bottom_right_point.x); + end_position.y = g_random_double_range (.25, .75) * + (screen_saver->canvas_rectangle.top_left_point.y + + screen_saver->canvas_rectangle.bottom_right_point.y); + + start_control_point.x = start_position.x + .9 * (end_position.x - start_position.x); + start_control_point.y = start_position.y + .9 * (end_position.y - start_position.y); + + end_control_point.x = start_position.x + 1.0 * (end_position.x - start_position.x); + end_control_point.y = start_position.y + 1.0 * (end_position.y - start_position.y); + + return path_new (&start_position, &start_control_point, &end_control_point, + &end_position, duration); +} + +static Path * +screen_saver_floater_create_path_to_bubble_up (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble duration) +{ + Point start_position, end_position, start_control_point, end_control_point; + + start_position = floater->position; + end_position.x = start_position.x; + end_position.y = screen_saver->canvas_rectangle.top_left_point.y - FLOATER_MAX_SIZE; + start_control_point.x = .5 * start_position.x; + start_control_point.y = .5 * start_position.y; + end_control_point.x = 1.5 * end_position.x; + end_control_point.y = .5 * end_position.y; + + return path_new (&start_position, &start_control_point, &end_control_point, + &end_position, duration); +} + +static Path * +screen_saver_floater_create_path_to_random_point (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble duration) +{ + Point start_position, end_position, start_control_point, end_control_point; + + start_position = floater->position; + + end_position.x = start_position.x + + (g_random_double_range (-.5, .5) * 4 * FLOATER_MAX_SIZE); + end_position.y = start_position.y + + (g_random_double_range (-.5, .5) * 4 * FLOATER_MAX_SIZE); + + start_control_point.x = start_position.x + .95 * (end_position.x - start_position.x); + start_control_point.y = start_position.y + .95 * (end_position.y - start_position.y); + + end_control_point.x = start_position.x + 1.0 * (end_position.x - start_position.x); + end_control_point.y = start_position.y + 1.0 * (end_position.y - start_position.y); + + return path_new (&start_position, &start_control_point, &end_control_point, + &end_position, duration); +} + +static Path * +screen_saver_floater_create_path (ScreenSaver *screen_saver, + ScreenSaverFloater *floater) +{ + gdouble performance_ratio; + gdouble duration; + + performance_ratio = + screen_saver_get_frames_per_second (screen_saver) / OPTIMAL_FRAME_RATE; + + if (abs (performance_ratio) <= G_MINDOUBLE) + performance_ratio = 1.0; + + if (screen_saver_floater_should_bubble_up (screen_saver, floater, performance_ratio, &duration)) + return screen_saver_floater_create_path_to_bubble_up (screen_saver, floater, duration); + + if (screen_saver_floater_should_come_on_screen (screen_saver, floater, performance_ratio, &duration)) + return screen_saver_floater_create_path_to_on_screen (screen_saver, floater, duration); + + return screen_saver_floater_create_path_to_random_point (screen_saver, floater, + g_random_double_range (3.0, 7.0)); +} + +static void +screen_saver_floater_update_state (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble time) +{ + gdouble performance_ratio; + + performance_ratio = + screen_saver_get_frames_per_second (screen_saver) / OPTIMAL_FRAME_RATE; + + if (floater->path == NULL) + { + floater->path = screen_saver_floater_create_path (screen_saver, floater); + floater->path_start_time = time; + + floater->path_start_scale = floater->scale; + + if (g_random_double () > .5) + floater->path_end_scale = g_random_double_range (0.10, performance_ratio); + + /* poor man's distribution */ + if (screen_saver->should_do_rotations && + (g_random_double () < .75 * performance_ratio)) + { + gint r; + + r = g_random_int_range (0, 100); + if (r < 80) + floater->angle_increment = 0.0; + else if (r < 95) + floater->angle_increment = g_random_double_range (-SMALL_ANGLE, SMALL_ANGLE); + else + floater->angle_increment = g_random_double_range (-BIG_ANGLE, BIG_ANGLE); + } + } + + if (time < (floater->path_start_time + floater->path->duration)) + { + gdouble path_time; + + path_time = time - floater->path_start_time; + + floater->position = + screen_saver_floater_get_position_from_time (screen_saver, floater, + path_time); + floater->scale = + screen_saver_floater_get_scale_from_time (screen_saver, floater, path_time); + + floater->angle = + screen_saver_floater_get_angle_from_time (screen_saver, floater, path_time); + + floater->opacity = pow (floater->scale, 1.0 / GAMMA); + } + else + { + path_free (floater->path); + + floater->path = NULL; + floater->path_start_time = 0.0; + } +} + +static GdkPixbuf * +gamma_correct (const GdkPixbuf *input_pixbuf) +{ + gint x, y, width, height, rowstride; + GdkPixbuf *output_pixbuf; + guchar *pixels; + + output_pixbuf = gdk_pixbuf_copy (input_pixbuf); + pixels = gdk_pixbuf_get_pixels (output_pixbuf); + + width = gdk_pixbuf_get_width (output_pixbuf); + height = gdk_pixbuf_get_height (output_pixbuf); + rowstride = gdk_pixbuf_get_rowstride (output_pixbuf); + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + { + guchar *alpha_channel; + guchar opacity; + + alpha_channel = pixels + y * (rowstride / 4) + x + 3; + opacity = (guchar) (255 * pow ((*alpha_channel / 255.0), 1.0 / GAMMA)); + + *alpha_channel = opacity; + } + + return output_pixbuf; +} + +static gboolean +screen_saver_floater_do_draw (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + cairo_t *context) +{ + gint size; + CachedSource *source; + + size = CLAMP ((int) (FLOATER_MAX_SIZE * floater->scale), + FLOATER_MIN_SIZE, FLOATER_MAX_SIZE); + + source = g_hash_table_lookup (screen_saver->cached_sources, GINT_TO_POINTER (size)); + + if (source == NULL) + { + GdkPixbuf *pixbuf; + GError *error; + + pixbuf = NULL; + error = NULL; + + pixbuf = gdk_pixbuf_new_from_file_at_size (screen_saver->filename, size, -1, + &error); + if (pixbuf == NULL) + { + g_assert (error != NULL); + g_printerr ("%s", _(error->message)); + g_error_free (error); + return FALSE; + } + + if (gdk_pixbuf_get_has_alpha (pixbuf)) + gamma_correct (pixbuf); + + gdk_cairo_set_source_pixbuf (context, pixbuf, 0.0, 0.0); + + source = cached_source_new (cairo_get_source (context), + gdk_pixbuf_get_width (pixbuf), + gdk_pixbuf_get_height (pixbuf)); + g_object_unref (pixbuf); + g_hash_table_insert (screen_saver->cached_sources, GINT_TO_POINTER (size), + source); + } + + cairo_save (context); + + if (screen_saver->should_do_rotations && (abs (floater->angle) > G_MINDOUBLE)) + { + floater->bounds.width = G_SQRT2 * source->width + 2; + floater->bounds.height = G_SQRT2 * source->height + 2; + floater->bounds.x = (int) (floater->position.x - .5 * G_SQRT2 * source->width) - 1; + floater->bounds.y = (int) (floater->position.y - .5 * G_SQRT2 * source->height) - 1; + + cairo_translate (context, + trunc (floater->position.x), + trunc (floater->position.y)); + cairo_rotate (context, floater->angle); + cairo_translate (context, + -trunc (floater->position.x), + -trunc (floater->position.y)); + } + else + { + floater->bounds.width = source->width + 2; + floater->bounds.height = source->height + 2; + floater->bounds.x = (int) (floater->position.x - .5 * source->width) - 1; + floater->bounds.y = (int) (floater->position.y - .5 * source->height) - 1; + } + + cairo_translate (context, + trunc (floater->position.x - .5 * source->width), + trunc (floater->position.y - .5 * source->height)); + + cairo_set_source (context, source->pattern); + + cairo_rectangle (context, + trunc (.5 * (source->width - floater->bounds.width)), + trunc (.5 * (source->height - floater->bounds.height)), + floater->bounds.width, floater->bounds.height); + + cairo_clip (context); + cairo_paint_with_alpha (context, floater->opacity); + cairo_restore (context); + + if (screen_saver->should_show_paths && (floater->path != NULL)) + { + gdouble dash_pattern[] = { 5.0 }; + gint size; + + size = CLAMP ((int) (FLOATER_MAX_SIZE * floater->path_start_scale), + FLOATER_MIN_SIZE, FLOATER_MAX_SIZE); + + cairo_save (context); + cairo_set_source_rgba (context, 1.0, 1.0, 1.0, .2 * floater->opacity); + cairo_move_to (context, + floater->path->start_point.x, + floater->path->start_point.y); + cairo_curve_to (context, + floater->path->start_control_point.x, + floater->path->start_control_point.y, + floater->path->end_control_point.x, + floater->path->end_control_point.y, + floater->path->end_point.x, + floater->path->end_point.y); + cairo_set_line_cap (context, CAIRO_LINE_CAP_ROUND); + cairo_stroke (context); + cairo_set_source_rgba (context, 1.0, 0.0, 0.0, .5 * floater->opacity); + cairo_rectangle (context, + floater->path->start_point.x - 3, + floater->path->start_point.y - 3, + 6, 6); + cairo_fill (context); + cairo_set_source_rgba (context, 0.0, 0.5, 0.0, .5 * floater->opacity); + cairo_arc (context, + floater->path->start_control_point.x, + floater->path->start_control_point.y, + 3, 0.0, 2.0 * G_PI); + cairo_stroke (context); + cairo_set_source_rgba (context, 0.5, 0.0, 0.5, .5 * floater->opacity); + cairo_arc (context, + floater->path->end_control_point.x, + floater->path->end_control_point.y, + 3, 0.0, 2.0 * G_PI); + cairo_stroke (context); + cairo_set_source_rgba (context, 0.0, 0.0, 1.0, .5 * floater->opacity); + cairo_rectangle (context, + floater->path->end_point.x - 3, + floater->path->end_point.y - 3, + 6, 6); + cairo_fill (context); + + cairo_set_dash (context, dash_pattern, G_N_ELEMENTS (dash_pattern), 0); + cairo_set_source_rgba (context, .5, .5, .5, .2 * floater->scale); + cairo_move_to (context, floater->path->start_point.x, + floater->path->start_point.y); + cairo_line_to (context, floater->path->start_control_point.x, + floater->path->start_control_point.y); + cairo_stroke (context); + + cairo_move_to (context, floater->path->end_point.x, + floater->path->end_point.y); + cairo_line_to (context, floater->path->end_control_point.x, + floater->path->end_control_point.y); + cairo_stroke (context); + + cairo_restore (context); + } + + return TRUE; +} + +static ScreenSaver * +screen_saver_new (GtkDrawingArea *drawing_area, + const gchar *filename, + gint max_floater_count, + gboolean should_do_rotations, + gboolean should_show_paths) +{ + ScreenSaver *screen_saver; + + screen_saver = g_new (ScreenSaver, 1); + screen_saver->filename = g_strdup (filename); + screen_saver->drawing_area = GTK_WIDGET (drawing_area); + screen_saver->cached_sources = + g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) cached_source_free); + + g_signal_connect_swapped (G_OBJECT (drawing_area), "size-allocate", + G_CALLBACK (screen_saver_on_size_allocate), + screen_saver); + + g_signal_connect_swapped (G_OBJECT (drawing_area), "expose-event", + G_CALLBACK (screen_saver_on_expose_event), + screen_saver); + + screen_saver->first_update_time = 0.0; + screen_saver->current_calculated_stats_time = 0.0; + screen_saver->last_calculated_stats_time = 0.0; + screen_saver->update_count = 0; + screen_saver->frame_count = 0; + screen_saver->updates_per_second = 0.0; + screen_saver->frames_per_second = 0.0; + screen_saver->floaters = NULL; + screen_saver->max_floater_count = max_floater_count; + + screen_saver->should_show_paths = should_show_paths; + screen_saver->should_do_rotations = should_do_rotations; + + screen_saver_get_initial_state (screen_saver); + + screen_saver->state_update_timeout_id = + g_timeout_add (1000 / (2.0 * OPTIMAL_FRAME_RATE), + (GSourceFunc) screen_saver_do_update_state, screen_saver); + + screen_saver->stats_update_timeout_id = + g_timeout_add (1000, (GSourceFunc) screen_saver_do_update_stats, + screen_saver); + + return screen_saver; +} + +static void +screen_saver_free (ScreenSaver *screen_saver) +{ + if (screen_saver == NULL) + return; + + g_free (screen_saver->filename); + + g_hash_table_destroy (screen_saver->cached_sources); + + if (screen_saver->state_update_timeout_id != 0) + g_source_remove (screen_saver->state_update_timeout_id); + + if (screen_saver->stats_update_timeout_id != 0) + g_source_remove (screen_saver->stats_update_timeout_id); + + screen_saver_destroy_floaters (screen_saver); + + g_free (screen_saver); +} + +static gdouble +screen_saver_get_timestamp (ScreenSaver *screen_saver) +{ + const gdouble microseconds_per_second = (gdouble ) G_USEC_PER_SEC; + gdouble timestamp; + GTimeVal now = { 0L, /* zero-filled */ }; + + g_get_current_time (&now); + timestamp = ((microseconds_per_second * now.tv_sec) + now.tv_usec) / + microseconds_per_second; + + return timestamp; +} + +static void +screen_saver_create_floaters (ScreenSaver *screen_saver) +{ + gint i; + + for (i = 0; i < screen_saver->max_floater_count; i++) + { + ScreenSaverFloater *floater; + Point position; + gdouble scale; + + position.x = g_random_double_range (screen_saver->canvas_rectangle.top_left_point.x, + screen_saver->canvas_rectangle.bottom_right_point.x); + position.y = g_random_double_range (screen_saver->canvas_rectangle.top_left_point.y, + screen_saver->canvas_rectangle.bottom_right_point.y); + + scale = g_random_double (); + + floater = screen_saver_floater_new (screen_saver, &position, scale); + + screen_saver->floaters = g_list_prepend (screen_saver->floaters, + floater); + } +} + +static gdouble +screen_saver_get_updates_per_second (ScreenSaver *screen_saver) +{ + return screen_saver->updates_per_second; +} + +static gdouble +screen_saver_get_frames_per_second (ScreenSaver *screen_saver) +{ + return screen_saver->frames_per_second; +} + +static gdouble +screen_saver_get_image_cache_usage (ScreenSaver *screen_saver) +{ + static const gdouble cache_capacity = (FLOATER_MAX_SIZE - FLOATER_MIN_SIZE + 1); + + return g_hash_table_size (screen_saver->cached_sources) / cache_capacity; +} + +static void +screen_saver_destroy_floaters (ScreenSaver *screen_saver) +{ + if (screen_saver->floaters == NULL) + return; + + g_list_foreach (screen_saver->floaters, (GFunc) screen_saver_floater_free, + NULL); + g_list_free (screen_saver->floaters); + + screen_saver->floaters = NULL; +} + +static void +screen_saver_on_size_allocate (ScreenSaver *screen_saver, + GtkAllocation *allocation) +{ + Rectangle canvas_rectangle; + + canvas_rectangle.top_left_point.x = allocation->x - .1 * allocation->width; + canvas_rectangle.top_left_point.y = allocation->y - .1 * allocation->height; + + canvas_rectangle.bottom_right_point.x = allocation->x + (1.1 * allocation->width); + canvas_rectangle.bottom_right_point.y = allocation->y + (1.1 * allocation->height); + + screen_saver->canvas_rectangle = canvas_rectangle; +} + +static gint +compare_floaters (ScreenSaverFloater *a, + ScreenSaverFloater *b) +{ + if (a->scale > b->scale) + return 1; + else if (abs (a->scale - b->scale) <= G_MINDOUBLE) + return 0; + else + return -1; +} + +static void +screen_saver_on_expose_event (ScreenSaver *screen_saver, + GdkEventExpose *event) +{ + GList *tmp; + cairo_t *context; + + if (screen_saver->floaters == NULL) + screen_saver_create_floaters (screen_saver); + + context = gdk_cairo_create (screen_saver->drawing_area->window); + + cairo_rectangle (context, + (double) event->area.x, + (double) event->area.y, + (double) event->area.width, + (double) event->area.height); + cairo_clip (context); + + screen_saver->floaters = g_list_sort (screen_saver->floaters, + (GCompareFunc)compare_floaters); + + for (tmp = screen_saver->floaters; tmp != NULL; tmp = tmp->next) + { + ScreenSaverFloater *floater; + GdkRectangle rect; + gint size; + + floater = (ScreenSaverFloater *) tmp->data; + + size = CLAMP ((int) (FLOATER_MAX_SIZE * floater->scale), + FLOATER_MIN_SIZE, FLOATER_MAX_SIZE); + + rect.x = (int) (floater->position.x - .5 * G_SQRT2 * size); + rect.y = (int) (floater->position.y - .5 * G_SQRT2 * size); + rect.width = G_SQRT2 * size; + rect.height = G_SQRT2 * size; + + if (!gdk_region_rect_in (event->region, &rect)) + continue; + + if (!screen_saver_floater_do_draw (screen_saver, floater, context)) + { + gtk_main_quit (); + break; + } + } + + cairo_destroy (context); + + screen_saver->draw_ops_pending = TRUE; + screen_saver->frame_count++; +} + +static void +screen_saver_update_state (ScreenSaver *screen_saver, + gdouble time) +{ + GList *tmp; + + tmp = screen_saver->floaters; + while (tmp != NULL) + { + ScreenSaverFloater *floater; + floater = (ScreenSaverFloater *) tmp->data; + + screen_saver_floater_update_state (screen_saver, floater, time); + + if (GTK_WIDGET_REALIZED (screen_saver->drawing_area) + && (floater->bounds.width > 0) && (floater->bounds.height > 0)) + { + gint size; + size = CLAMP ((int) (FLOATER_MAX_SIZE * floater->scale), + FLOATER_MIN_SIZE, FLOATER_MAX_SIZE); + + gtk_widget_queue_draw_area (screen_saver->drawing_area, + floater->bounds.x, + floater->bounds.y, + floater->bounds.width, + floater->bounds.height); + + /* the edges could concievably be spread across two + * pixels so we add +2 to invalidated region + */ + if (screen_saver->should_do_rotations) + gtk_widget_queue_draw_area (screen_saver->drawing_area, + (int) (floater->position.x - + .5 * G_SQRT2 * size), + (int) (floater->position.y - + .5 * G_SQRT2 * size), + G_SQRT2 * size + 2, + G_SQRT2 * size + 2); + else + gtk_widget_queue_draw_area (screen_saver->drawing_area, + (int) (floater->position.x - + .5 * size), + (int) (floater->position.y - + .5 * size), + size + 2, size + 2); + + if (screen_saver->should_show_paths) + gtk_widget_queue_draw (screen_saver->drawing_area); + } + + tmp = tmp->next; + } +} + +static void +screen_saver_get_initial_state (ScreenSaver *screen_saver) +{ + screen_saver->first_update_time = screen_saver_get_timestamp (screen_saver); + screen_saver_update_state (screen_saver, 0.0); +} + +static gboolean +screen_saver_do_update_state (ScreenSaver *screen_saver) +{ + gdouble current_update_time; + + /* flush pending requests to the X server and block for + * replies before proceeding to help prevent the X server from + * getting overrun with requests + */ + if (screen_saver->draw_ops_pending) + { + gdk_flush (); + screen_saver->draw_ops_pending = FALSE; + } + + current_update_time = screen_saver_get_timestamp (screen_saver); + screen_saver_update_state (screen_saver, current_update_time - + screen_saver->first_update_time); + screen_saver->update_count++; + return TRUE; +} + +static gboolean +screen_saver_do_update_stats (ScreenSaver *screen_saver) +{ + gdouble last_calculated_stats_time, seconds_since_last_stats_update; + + last_calculated_stats_time = screen_saver->current_calculated_stats_time; + screen_saver->current_calculated_stats_time = + screen_saver_get_timestamp (screen_saver); + screen_saver->last_calculated_stats_time = last_calculated_stats_time; + + if (abs (last_calculated_stats_time) <= G_MINDOUBLE) + return TRUE; + + seconds_since_last_stats_update = + screen_saver->current_calculated_stats_time - last_calculated_stats_time; + + screen_saver->updates_per_second = + screen_saver->update_count / seconds_since_last_stats_update; + screen_saver->frames_per_second = + screen_saver->frame_count / seconds_since_last_stats_update; + + screen_saver->update_count = 0; + screen_saver->frame_count = 0; + + return TRUE; +} + +static gboolean +do_print_screen_saver_stats (ScreenSaver *screen_saver) +{ + + g_print ("updates per second: %.2f, frames per second: %.2f, " + "image cache %.0f%% full\n", + screen_saver_get_updates_per_second (screen_saver), + screen_saver_get_frames_per_second (screen_saver), + screen_saver_get_image_cache_usage (screen_saver) * 100.0); + + return TRUE; +} + +int +main (int argc, + char *argv[]) +{ + ScreenSaver *screen_saver; + GtkWidget *window; + GtkWidget *drawing_area; + + GtkStateType state; + + GError *error; + + error = NULL; + + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + gtk_init_with_args (&argc, &argv, + /* translators: the word "image" here + * represents a command line argument + */ + _("image - floats images around the screen"), + options, GETTEXT_PACKAGE, &error); + + + if (error != NULL) + { + g_printerr (_("%s. See --help for usage information.\n"), + _(error->message)); + g_error_free (error); + return EX_SOFTWARE; + } + + if ((filenames == NULL) || (filenames[0] == NULL) || + (filenames[1] != NULL)) + { + g_printerr (_("You must specify one image. See --help for usage " + "information.\n")); + return EX_USAGE; + } + + window = gs_theme_window_new (); + + g_signal_connect (G_OBJECT (window), "delete-event", + G_CALLBACK (gtk_main_quit), NULL); + + drawing_area = gtk_drawing_area_new (); + + state = (GtkStateType) 0; + while (state < (GtkStateType) G_N_ELEMENTS (drawing_area->style->bg)) + { + gtk_widget_modify_bg (drawing_area, state, &drawing_area->style->mid[state]); + state++; + } + + gtk_widget_show (drawing_area); + gtk_container_add (GTK_CONTAINER (window), drawing_area); + + screen_saver = screen_saver_new (GTK_DRAWING_AREA (drawing_area), + filenames[0], max_floater_count, + should_do_rotations, should_show_paths); + g_strfreev (filenames); + + if (should_print_stats) + g_timeout_add (STAT_PRINT_FREQUENCY, + (GSourceFunc) do_print_screen_saver_stats, + screen_saver); + + if ((geometry == NULL) + || !gtk_window_parse_geometry (GTK_WINDOW (window), geometry)) + gtk_window_set_default_size (GTK_WINDOW (window), 640, 480); + + gtk_widget_show (window); + + gtk_main (); + + screen_saver_free (screen_saver); + + return EX_OK; +} diff --git a/savers/floaters.c.orig b/savers/floaters.c.orig new file mode 100644 index 0000000..1fd84f5 --- /dev/null +++ b/savers/floaters.c.orig @@ -0,0 +1,1256 @@ +/* + * Copyright (C) 2005 Ray Strode <[email protected]>, + * Matthias Clasen <[email protected]>, + * Søren Sandmann <[email protected]> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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. + * + * Originally written by: Ray Strode <[email protected]> + * + * Later contributions by: Matthias Clasen <[email protected]> + * Søren Sandmann <[email protected]> + */ + +#include "config.h" + +#include <math.h> +#include <stdlib.h> +#include <sysexits.h> +#include <time.h> + +#include <glib.h> +#include <glib/gi18n.h> + +#include <gdk/gdk.h> +#include <gdk/gdkx.h> + +#include <gtk/gtk.h> + +#include "gs-theme-window.h" + +#ifndef trunc +#define trunc(x) (((x) > 0.0) ? floor((x)) : -floor(-(x))) +#endif + +#ifndef OPTIMAL_FRAME_RATE +#define OPTIMAL_FRAME_RATE (25.0) +#endif + +#ifndef STAT_PRINT_FREQUENCY +#define STAT_PRINT_FREQUENCY (2000) +#endif + +#ifndef FLOATER_MAX_SIZE +#define FLOATER_MAX_SIZE (128.0) +#endif + +#ifndef FLOATER_MIN_SIZE +#define FLOATER_MIN_SIZE (16.0) +#endif +#ifndef FLOATER_DEFAULT_COUNT +#define FLOATER_DEFAULT_COUNT (5) +#endif + +#ifndef SMALL_ANGLE +#define SMALL_ANGLE (0.025 * G_PI) +#endif + +#ifndef BIG_ANGLE +#define BIG_ANGLE (0.125 * G_PI) +#endif + +#ifndef GAMMA +#define GAMMA 2.2 +#endif + +static gboolean should_show_paths = FALSE; +static gboolean should_do_rotations = FALSE; +static gboolean should_print_stats = FALSE; +static gint max_floater_count = FLOATER_DEFAULT_COUNT; +static gchar *geometry = NULL; +static gchar **filenames = NULL; + +static GOptionEntry options[] = { + {"show-paths", 'p', 0, G_OPTION_ARG_NONE, &should_show_paths, + N_("Show paths that images follow"), NULL}, + + {"do-rotations", 'r', 0, G_OPTION_ARG_NONE, &should_do_rotations, + N_("Occasionally rotate images as they move"), NULL}, + + {"print-stats", 's', 0, G_OPTION_ARG_NONE, &should_print_stats, + N_("Print out frame rate and other statistics"), NULL}, + + {"number-of-images", 'n', 0, G_OPTION_ARG_INT, &max_floater_count, + N_("The maximum number of images to keep on screen"), N_("MAX_IMAGES")}, + + {"geometry", 0, 0, G_OPTION_ARG_STRING, &geometry, + N_("The initial size and position of window"), N_("WIDTHxHEIGHT+X+Y")}, + + {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, + N_("The source image to use"), NULL}, + + {NULL} +}; + + +typedef struct _Point Point; +typedef struct _Path Path; +typedef struct _Rectangle Rectangle; +typedef struct _ScreenSaverFloater ScreenSaverFloater; +typedef struct _CachedSource CachedSource; +typedef struct _ScreenSaver ScreenSaver; + +struct _Point +{ + gdouble x, y; +}; + +struct _Path +{ + Point start_point; + Point start_control_point; + Point end_control_point; + Point end_point; + + gdouble x_linear_coefficient, + y_linear_coefficient; + + gdouble x_quadratic_coefficient, + y_quadratic_coefficient; + + gdouble x_cubic_coefficient, + y_cubic_coefficient; + + gdouble duration; +}; + +struct _CachedSource +{ + cairo_pattern_t *pattern; + gint width, height; +}; + +struct _Rectangle +{ + Point top_left_point; + Point bottom_right_point; +}; + +struct _ScreenSaverFloater +{ + GdkRectangle bounds; + + Point start_position; + Point position; + + gdouble scale; + gdouble opacity; + + Path *path; + gdouble path_start_time; + gdouble path_start_scale; + gdouble path_end_scale; + + gdouble angle; + gdouble angle_increment; +}; + +struct _ScreenSaver +{ + GtkWidget *drawing_area; + Rectangle canvas_rectangle; + GHashTable *cached_sources; + + char *filename; + + gdouble first_update_time; + + gdouble last_calculated_stats_time, + current_calculated_stats_time; + gint update_count, frame_count; + + gdouble updates_per_second; + gdouble frames_per_second; + + guint state_update_timeout_id; + guint stats_update_timeout_id; + + GList *floaters; + gint max_floater_count; + + guint should_do_rotations: 1; + guint should_show_paths : 1; + guint draw_ops_pending : 1; +}; + +static Path *path_new (Point *start_point, + Point *start_control_point, + Point *end_control_point, + Point *end_point, + gdouble duration); +static void path_free (Path *path); + +static ScreenSaverFloater *screen_saver_floater_new (ScreenSaver *screen_saver, + Point *position, + gdouble scale); +static void screen_saver_floater_free (ScreenSaver *screen_saver, + ScreenSaverFloater *floater); +static gboolean screen_saver_floater_is_off_canvas (ScreenSaver *screen_saver, + ScreenSaverFloater *floater); +static gboolean screen_saver_floater_should_bubble_up (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble performance_ratio, + gdouble *duration); +static gboolean screen_saver_floater_should_come_on_screen (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble performance_ratio, + gdouble *duration); +static Point screen_saver_floater_get_position_from_time (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble time); +static gdouble screen_saver_floater_get_scale_from_time (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble time); +static gdouble screen_saver_floater_get_angle_from_time (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble time); + +static Path *screen_saver_floater_create_path_to_on_screen (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble duration); +static Path *screen_saver_floater_create_path_to_bubble_up (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble duration); +static Path *screen_saver_floater_create_path_to_random_point (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble duration); +static Path *screen_saver_floater_create_path (ScreenSaver *screen_saver, + ScreenSaverFloater *floater); +static void screen_saver_floater_update_state (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble time); + +static gboolean screen_saver_floater_do_draw (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + cairo_t *context); + +static CachedSource *cached_source_new (cairo_pattern_t *pattern, + gint width, + gint height); +static void cached_source_free (CachedSource *source); + +static ScreenSaver *screen_saver_new (GtkDrawingArea *drawing_area, + const gchar *filename, + gint max_floater_count, + gboolean should_do_rotations, + gboolean should_show_paths); +static void screen_saver_free (ScreenSaver *screen_saver); +static gdouble screen_saver_get_timestamp (ScreenSaver *screen_saver); +static void screen_saver_get_initial_state (ScreenSaver *screen_saver); +static void screen_saver_update_state (ScreenSaver *screen_saver, + gdouble time); +static gboolean screen_saver_do_update_state (ScreenSaver *screen_saver); +static gboolean screen_saver_do_update_stats (ScreenSaver *screen_saver); +static gdouble screen_saver_get_updates_per_second (ScreenSaver *screen_saver); +static gdouble screen_saver_get_frames_per_second (ScreenSaver *screen_saver); +static gdouble screen_saver_get_image_cache_usage (ScreenSaver *screen_saver); +static void screen_saver_create_floaters (ScreenSaver *screen_saver); +static void screen_saver_destroy_floaters (ScreenSaver *screen_saver); +static void screen_saver_on_size_allocate (ScreenSaver *screen_saver, + GtkAllocation *allocation); +static void screen_saver_on_expose_event (ScreenSaver *screen_saver, + GdkEventExpose *event); +static gboolean do_print_screen_saver_stats (ScreenSaver *screen_saver); +static GdkPixbuf *gamma_correct (const GdkPixbuf *input_pixbuf); + +static CachedSource* +cached_source_new (cairo_pattern_t *pattern, + gint width, + gint height) +{ + CachedSource *source; + + source = g_new (CachedSource, 1); + source->pattern = cairo_pattern_reference (pattern); + source->width = width; + source->height = height; + + return source; +} + +static void +cached_source_free (CachedSource *source) +{ + if (source == NULL) + return; + + cairo_pattern_destroy (source->pattern); + + g_free (source); +} + +static Path * +path_new (Point *start_point, + Point *start_control_point, + Point *end_control_point, + Point *end_point, + gdouble duration) +{ + Path *path; + + path = g_new (Path, 1); + path->start_point = *start_point; + path->start_control_point = *start_control_point; + path->end_control_point = *end_control_point; + path->end_point = *end_point; + path->duration = duration; + + /* we precompute the coefficients to the cubic bezier curve here + * so that we don't have to do it repeatedly later The equation is: + * + * B(t) = A * t^3 + B * t^2 + C * t + start_point + */ + path->x_linear_coefficient = 3 * (start_control_point->x - start_point->x); + path->x_quadratic_coefficient = 3 * (end_control_point->x - + start_control_point->x) - + path->x_linear_coefficient; + path->x_cubic_coefficient = end_point->x - start_point->x - + path->x_linear_coefficient - + path->x_quadratic_coefficient; + + path->y_linear_coefficient = 3 * (start_control_point->y - start_point->y); + path->y_quadratic_coefficient = 3 * (end_control_point->y - + start_control_point->y) - + path->y_linear_coefficient; + path->y_cubic_coefficient = end_point->y - start_point->y - + path->y_linear_coefficient - + path->y_quadratic_coefficient; + return path; +} + +static void +path_free (Path *path) +{ + g_free (path); +} + +static ScreenSaverFloater* +screen_saver_floater_new (ScreenSaver *screen_saver, + Point *position, + gdouble scale) +{ + ScreenSaverFloater *floater; + + floater = g_new (ScreenSaverFloater, 1); + floater->bounds.width = 0; + floater->start_position = *position; + floater->position = *position; + floater->scale = scale; + floater->opacity = pow (scale, 1.0 / GAMMA); + floater->path = NULL; + floater->path_start_time = 0.0; + floater->path_start_scale = 1.0; + floater->path_end_scale = 0.0; + + floater->angle = 0.0; + floater->angle_increment = 0.0; + + return floater; +} + +void +screen_saver_floater_free (ScreenSaver *screen_saver, + ScreenSaverFloater *floater) +{ + if (floater == NULL) + return; + + path_free (floater->path); + + g_free (floater); +} + +static gboolean +screen_saver_floater_is_off_canvas (ScreenSaver *screen_saver, + ScreenSaverFloater *floater) +{ + if ((floater->position.x < screen_saver->canvas_rectangle.top_left_point.x) || + (floater->position.x > screen_saver->canvas_rectangle.bottom_right_point.x) || + (floater->position.y < screen_saver->canvas_rectangle.top_left_point.y) || + (floater->position.y > screen_saver->canvas_rectangle.bottom_right_point.y)) + return TRUE; + + return FALSE; +} + +static gboolean +screen_saver_floater_should_come_on_screen (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble performance_ratio, + gdouble *duration) +{ + + if (!screen_saver_floater_is_off_canvas (screen_saver, floater)) + return FALSE; + + if ((abs (performance_ratio - .5) >= G_MINDOUBLE) && + (g_random_double () > .5)) + { + if (duration) + *duration = g_random_double_range (3.0, 7.0); + + return TRUE; + } + + return FALSE; +} + +static gboolean +screen_saver_floater_should_bubble_up (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble performance_ratio, + gdouble *duration) +{ + + if ((performance_ratio < .5) && (g_random_double () > .5)) + { + if (duration) + *duration = performance_ratio * 30.0; + + return TRUE; + } + + if ((floater->scale < .3) && (g_random_double () > .6)) + { + if (duration) + *duration = 30.0; + + return TRUE; + } + + return FALSE; +} + +static Point +screen_saver_floater_get_position_from_time (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble time) +{ + Point point; + + time = time / floater->path->duration; + + point.x = floater->path->x_cubic_coefficient * (time * time * time) + + floater->path->x_quadratic_coefficient * (time * time) + + floater->path->x_linear_coefficient * (time) + + floater->path->start_point.x; + point.y = floater->path->y_cubic_coefficient * (time * time * time) + + floater->path->y_quadratic_coefficient * (time * time) + + floater->path->y_linear_coefficient * (time) + + floater->path->start_point.y; + + return point; +} + +static gdouble +screen_saver_floater_get_scale_from_time (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble time) +{ + gdouble completion_ratio, total_scale_growth, new_scale; + + completion_ratio = time / floater->path->duration; + total_scale_growth = (floater->path_end_scale - floater->path_start_scale); + new_scale = floater->path_start_scale + total_scale_growth * completion_ratio; + + return CLAMP (new_scale, 0.0, 1.0); +} + +static gdouble +screen_saver_floater_get_angle_from_time (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble time) +{ + gdouble completion_ratio; + gdouble total_rotation; + + completion_ratio = time / floater->path->duration; + total_rotation = floater->angle_increment * floater->path->duration; + + return floater->angle + total_rotation * completion_ratio; +} + +static Path * +screen_saver_floater_create_path_to_on_screen (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble duration) +{ + Point start_position, end_position, start_control_point, end_control_point; + start_position = floater->position; + + end_position.x = g_random_double_range (.25, .75) * + (screen_saver->canvas_rectangle.top_left_point.x + + screen_saver->canvas_rectangle.bottom_right_point.x); + end_position.y = g_random_double_range (.25, .75) * + (screen_saver->canvas_rectangle.top_left_point.y + + screen_saver->canvas_rectangle.bottom_right_point.y); + + start_control_point.x = start_position.x + .9 * (end_position.x - start_position.x); + start_control_point.y = start_position.y + .9 * (end_position.y - start_position.y); + + end_control_point.x = start_position.x + 1.0 * (end_position.x - start_position.x); + end_control_point.y = start_position.y + 1.0 * (end_position.y - start_position.y); + + return path_new (&start_position, &start_control_point, &end_control_point, + &end_position, duration); +} + +static Path * +screen_saver_floater_create_path_to_bubble_up (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble duration) +{ + Point start_position, end_position, start_control_point, end_control_point; + + start_position = floater->position; + end_position.x = start_position.x; + end_position.y = screen_saver->canvas_rectangle.top_left_point.y - FLOATER_MAX_SIZE; + start_control_point.x = .5 * start_position.x; + start_control_point.y = .5 * start_position.y; + end_control_point.x = 1.5 * end_position.x; + end_control_point.y = .5 * end_position.y; + + return path_new (&start_position, &start_control_point, &end_control_point, + &end_position, duration); +} + +static Path * +screen_saver_floater_create_path_to_random_point (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble duration) +{ + Point start_position, end_position, start_control_point, end_control_point; + + start_position = floater->position; + + end_position.x = start_position.x + + (g_random_double_range (-.5, .5) * 4 * FLOATER_MAX_SIZE); + end_position.y = start_position.y + + (g_random_double_range (-.5, .5) * 4 * FLOATER_MAX_SIZE); + + start_control_point.x = start_position.x + .95 * (end_position.x - start_position.x); + start_control_point.y = start_position.y + .95 * (end_position.y - start_position.y); + + end_control_point.x = start_position.x + 1.0 * (end_position.x - start_position.x); + end_control_point.y = start_position.y + 1.0 * (end_position.y - start_position.y); + + return path_new (&start_position, &start_control_point, &end_control_point, + &end_position, duration); +} + +static Path * +screen_saver_floater_create_path (ScreenSaver *screen_saver, + ScreenSaverFloater *floater) +{ + gdouble performance_ratio; + gdouble duration; + + performance_ratio = + screen_saver_get_frames_per_second (screen_saver) / OPTIMAL_FRAME_RATE; + + if (abs (performance_ratio) <= G_MINDOUBLE) + performance_ratio = 1.0; + + if (screen_saver_floater_should_bubble_up (screen_saver, floater, performance_ratio, &duration)) + return screen_saver_floater_create_path_to_bubble_up (screen_saver, floater, duration); + + if (screen_saver_floater_should_come_on_screen (screen_saver, floater, performance_ratio, &duration)) + return screen_saver_floater_create_path_to_on_screen (screen_saver, floater, duration); + + return screen_saver_floater_create_path_to_random_point (screen_saver, floater, + g_random_double_range (3.0, 7.0)); +} + +static void +screen_saver_floater_update_state (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + gdouble time) +{ + gdouble performance_ratio; + + performance_ratio = + screen_saver_get_frames_per_second (screen_saver) / OPTIMAL_FRAME_RATE; + + if (floater->path == NULL) + { + floater->path = screen_saver_floater_create_path (screen_saver, floater); + floater->path_start_time = time; + + floater->path_start_scale = floater->scale; + + if (g_random_double () > .5) + floater->path_end_scale = g_random_double_range (0.10, performance_ratio); + + /* poor man's distribution */ + if (screen_saver->should_do_rotations && + (g_random_double () < .75 * performance_ratio)) + { + gint r; + + r = g_random_int_range (0, 100); + if (r < 80) + floater->angle_increment = 0.0; + else if (r < 95) + floater->angle_increment = g_random_double_range (-SMALL_ANGLE, SMALL_ANGLE); + else + floater->angle_increment = g_random_double_range (-BIG_ANGLE, BIG_ANGLE); + } + } + + if (time < (floater->path_start_time + floater->path->duration)) + { + gdouble path_time; + + path_time = time - floater->path_start_time; + + floater->position = + screen_saver_floater_get_position_from_time (screen_saver, floater, + path_time); + floater->scale = + screen_saver_floater_get_scale_from_time (screen_saver, floater, path_time); + + floater->angle = + screen_saver_floater_get_angle_from_time (screen_saver, floater, path_time); + + floater->opacity = pow (floater->scale, 1.0 / GAMMA); + } + else + { + path_free (floater->path); + + floater->path = NULL; + floater->path_start_time = 0.0; + } +} + +static GdkPixbuf * +gamma_correct (const GdkPixbuf *input_pixbuf) +{ + gint x, y, width, height, rowstride; + GdkPixbuf *output_pixbuf; + guchar *pixels; + + output_pixbuf = gdk_pixbuf_copy (input_pixbuf); + pixels = gdk_pixbuf_get_pixels (output_pixbuf); + + width = gdk_pixbuf_get_width (output_pixbuf); + height = gdk_pixbuf_get_height (output_pixbuf); + rowstride = gdk_pixbuf_get_rowstride (output_pixbuf); + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + { + guchar *alpha_channel; + guchar opacity; + + alpha_channel = pixels + y * (rowstride / 4) + x + 3; + opacity = (guchar) (255 * pow ((*alpha_channel / 255.0), 1.0 / GAMMA)); + + *alpha_channel = opacity; + } + + return output_pixbuf; +} + +static gboolean +screen_saver_floater_do_draw (ScreenSaver *screen_saver, + ScreenSaverFloater *floater, + cairo_t *context) +{ + gint size; + CachedSource *source; + + size = CLAMP ((int) (FLOATER_MAX_SIZE * floater->scale), + FLOATER_MIN_SIZE, FLOATER_MAX_SIZE); + + source = g_hash_table_lookup (screen_saver->cached_sources, GINT_TO_POINTER (size)); + + if (source == NULL) + { + GdkPixbuf *pixbuf; + GError *error; + + pixbuf = NULL; + error = NULL; + + pixbuf = gdk_pixbuf_new_from_file_at_size (screen_saver->filename, size, -1, + &error); + if (pixbuf == NULL) + { + g_assert (error != NULL); + g_printerr ("%s", _(error->message)); + g_error_free (error); + return FALSE; + } + + if (gdk_pixbuf_get_has_alpha (pixbuf)) + gamma_correct (pixbuf); + + gdk_cairo_set_source_pixbuf (context, pixbuf, 0.0, 0.0); + + source = cached_source_new (cairo_get_source (context), + gdk_pixbuf_get_width (pixbuf), + gdk_pixbuf_get_height (pixbuf)); + g_object_unref (pixbuf); + g_hash_table_insert (screen_saver->cached_sources, GINT_TO_POINTER (size), + source); + } + + cairo_save (context); + + if (screen_saver->should_do_rotations && (abs (floater->angle) > G_MINDOUBLE)) + { + floater->bounds.width = G_SQRT2 * source->width + 2; + floater->bounds.height = G_SQRT2 * source->height + 2; + floater->bounds.x = (int) (floater->position.x - .5 * G_SQRT2 * source->width) - 1; + floater->bounds.y = (int) (floater->position.y - .5 * G_SQRT2 * source->height) - 1; + + cairo_translate (context, + trunc (floater->position.x), + trunc (floater->position.y)); + cairo_rotate (context, floater->angle); + cairo_translate (context, + -trunc (floater->position.x), + -trunc (floater->position.y)); + } + else + { + floater->bounds.width = source->width + 2; + floater->bounds.height = source->height + 2; + floater->bounds.x = (int) (floater->position.x - .5 * source->width) - 1; + floater->bounds.y = (int) (floater->position.y - .5 * source->height) - 1; + } + + cairo_translate (context, + trunc (floater->position.x - .5 * source->width), + trunc (floater->position.y - .5 * source->height)); + + cairo_set_source (context, source->pattern); + + cairo_rectangle (context, + trunc (.5 * (source->width - floater->bounds.width)), + trunc (.5 * (source->height - floater->bounds.height)), + floater->bounds.width, floater->bounds.height); + + cairo_clip (context); + cairo_paint_with_alpha (context, floater->opacity); + cairo_restore (context); + + if (screen_saver->should_show_paths && (floater->path != NULL)) + { + gdouble dash_pattern[] = { 5.0 }; + gint size; + + size = CLAMP ((int) (FLOATER_MAX_SIZE * floater->path_start_scale), + FLOATER_MIN_SIZE, FLOATER_MAX_SIZE); + + cairo_save (context); + cairo_set_source_rgba (context, 1.0, 1.0, 1.0, .2 * floater->opacity); + cairo_move_to (context, + floater->path->start_point.x, + floater->path->start_point.y); + cairo_curve_to (context, + floater->path->start_control_point.x, + floater->path->start_control_point.y, + floater->path->end_control_point.x, + floater->path->end_control_point.y, + floater->path->end_point.x, + floater->path->end_point.y); + cairo_set_line_cap (context, CAIRO_LINE_CAP_ROUND); + cairo_stroke (context); + cairo_set_source_rgba (context, 1.0, 0.0, 0.0, .5 * floater->opacity); + cairo_rectangle (context, + floater->path->start_point.x - 3, + floater->path->start_point.y - 3, + 6, 6); + cairo_fill (context); + cairo_set_source_rgba (context, 0.0, 0.5, 0.0, .5 * floater->opacity); + cairo_arc (context, + floater->path->start_control_point.x, + floater->path->start_control_point.y, + 3, 0.0, 2.0 * G_PI); + cairo_stroke (context); + cairo_set_source_rgba (context, 0.5, 0.0, 0.5, .5 * floater->opacity); + cairo_arc (context, + floater->path->end_control_point.x, + floater->path->end_control_point.y, + 3, 0.0, 2.0 * G_PI); + cairo_stroke (context); + cairo_set_source_rgba (context, 0.0, 0.0, 1.0, .5 * floater->opacity); + cairo_rectangle (context, + floater->path->end_point.x - 3, + floater->path->end_point.y - 3, + 6, 6); + cairo_fill (context); + + cairo_set_dash (context, dash_pattern, G_N_ELEMENTS (dash_pattern), 0); + cairo_set_source_rgba (context, .5, .5, .5, .2 * floater->scale); + cairo_move_to (context, floater->path->start_point.x, + floater->path->start_point.y); + cairo_line_to (context, floater->path->start_control_point.x, + floater->path->start_control_point.y); + cairo_stroke (context); + + cairo_move_to (context, floater->path->end_point.x, + floater->path->end_point.y); + cairo_line_to (context, floater->path->end_control_point.x, + floater->path->end_control_point.y); + cairo_stroke (context); + + cairo_restore (context); + } + + return TRUE; +} + +static ScreenSaver * +screen_saver_new (GtkDrawingArea *drawing_area, + const gchar *filename, + gint max_floater_count, + gboolean should_do_rotations, + gboolean should_show_paths) +{ + ScreenSaver *screen_saver; + + screen_saver = g_new (ScreenSaver, 1); + screen_saver->filename = g_strdup (filename); + screen_saver->drawing_area = GTK_WIDGET (drawing_area); + screen_saver->cached_sources = + g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) cached_source_free); + + g_signal_connect_swapped (G_OBJECT (drawing_area), "size-allocate", + G_CALLBACK (screen_saver_on_size_allocate), + screen_saver); + + g_signal_connect_swapped (G_OBJECT (drawing_area), "expose-event", + G_CALLBACK (screen_saver_on_expose_event), + screen_saver); + + screen_saver->first_update_time = 0.0; + screen_saver->current_calculated_stats_time = 0.0; + screen_saver->last_calculated_stats_time = 0.0; + screen_saver->update_count = 0; + screen_saver->frame_count = 0; + screen_saver->updates_per_second = 0.0; + screen_saver->frames_per_second = 0.0; + screen_saver->floaters = NULL; + screen_saver->max_floater_count = max_floater_count; + + screen_saver->should_show_paths = should_show_paths; + screen_saver->should_do_rotations = should_do_rotations; + + screen_saver_get_initial_state (screen_saver); + + screen_saver->state_update_timeout_id = + g_timeout_add (1000 / (2.0 * OPTIMAL_FRAME_RATE), + (GSourceFunc) screen_saver_do_update_state, screen_saver); + + screen_saver->stats_update_timeout_id = + g_timeout_add (1000, (GSourceFunc) screen_saver_do_update_stats, + screen_saver); + + return screen_saver; +} + +static void +screen_saver_free (ScreenSaver *screen_saver) +{ + if (screen_saver == NULL) + return; + + g_free (screen_saver->filename); + + g_hash_table_destroy (screen_saver->cached_sources); + + if (screen_saver->state_update_timeout_id != 0) + g_source_remove (screen_saver->state_update_timeout_id); + + if (screen_saver->stats_update_timeout_id != 0) + g_source_remove (screen_saver->stats_update_timeout_id); + + screen_saver_destroy_floaters (screen_saver); + + g_free (screen_saver); +} + +static gdouble +screen_saver_get_timestamp (ScreenSaver *screen_saver) +{ + const gdouble microseconds_per_second = (gdouble ) G_USEC_PER_SEC; + gdouble timestamp; + GTimeVal now = { 0L, /* zero-filled */ }; + + g_get_current_time (&now); + timestamp = ((microseconds_per_second * now.tv_sec) + now.tv_usec) / + microseconds_per_second; + + return timestamp; +} + +static void +screen_saver_create_floaters (ScreenSaver *screen_saver) +{ + gint i; + + for (i = 0; i < screen_saver->max_floater_count; i++) + { + ScreenSaverFloater *floater; + Point position; + gdouble scale; + + position.x = g_random_double_range (screen_saver->canvas_rectangle.top_left_point.x, + screen_saver->canvas_rectangle.bottom_right_point.x); + position.y = g_random_double_range (screen_saver->canvas_rectangle.top_left_point.y, + screen_saver->canvas_rectangle.bottom_right_point.y); + + scale = g_random_double (); + + floater = screen_saver_floater_new (screen_saver, &position, scale); + + screen_saver->floaters = g_list_prepend (screen_saver->floaters, + floater); + } +} + +static gdouble +screen_saver_get_updates_per_second (ScreenSaver *screen_saver) +{ + return screen_saver->updates_per_second; +} + +static gdouble +screen_saver_get_frames_per_second (ScreenSaver *screen_saver) +{ + return screen_saver->frames_per_second; +} + +static gdouble +screen_saver_get_image_cache_usage (ScreenSaver *screen_saver) +{ + static const gdouble cache_capacity = (FLOATER_MAX_SIZE - FLOATER_MIN_SIZE + 1); + + return g_hash_table_size (screen_saver->cached_sources) / cache_capacity; +} + +static void +screen_saver_destroy_floaters (ScreenSaver *screen_saver) +{ + if (screen_saver->floaters == NULL) + return; + + g_list_foreach (screen_saver->floaters, (GFunc) screen_saver_floater_free, + NULL); + g_list_free (screen_saver->floaters); + + screen_saver->floaters = NULL; +} + +static void +screen_saver_on_size_allocate (ScreenSaver *screen_saver, + GtkAllocation *allocation) +{ + Rectangle canvas_rectangle; + + canvas_rectangle.top_left_point.x = allocation->x - .1 * allocation->width; + canvas_rectangle.top_left_point.y = allocation->y - .1 * allocation->height; + + canvas_rectangle.bottom_right_point.x = allocation->x + (1.1 * allocation->width); + canvas_rectangle.bottom_right_point.y = allocation->y + (1.1 * allocation->height); + + screen_saver->canvas_rectangle = canvas_rectangle; +} + +static gint +compare_floaters (ScreenSaverFloater *a, + ScreenSaverFloater *b) +{ + if (a->scale > b->scale) + return 1; + else if (abs (a->scale - b->scale) <= G_MINDOUBLE) + return 0; + else + return -1; +} + +static void +screen_saver_on_expose_event (ScreenSaver *screen_saver, + GdkEventExpose *event) +{ + GList *tmp; + cairo_t *context; + + if (screen_saver->floaters == NULL) + screen_saver_create_floaters (screen_saver); + + context = gdk_cairo_create (screen_saver->drawing_area->window); + + cairo_rectangle (context, + (double) event->area.x, + (double) event->area.y, + (double) event->area.width, + (double) event->area.height); + cairo_clip (context); + + screen_saver->floaters = g_list_sort (screen_saver->floaters, + (GCompareFunc)compare_floaters); + + for (tmp = screen_saver->floaters; tmp != NULL; tmp = tmp->next) + { + ScreenSaverFloater *floater; + GdkRectangle rect; + gint size; + + floater = (ScreenSaverFloater *) tmp->data; + + size = CLAMP ((int) (FLOATER_MAX_SIZE * floater->scale), + FLOATER_MIN_SIZE, FLOATER_MAX_SIZE); + + rect.x = (int) (floater->position.x - .5 * G_SQRT2 * size); + rect.y = (int) (floater->position.y - .5 * G_SQRT2 * size); + rect.width = G_SQRT2 * size; + rect.height = G_SQRT2 * size; + + if (!gdk_region_rect_in (event->region, &rect)) + continue; + + if (!screen_saver_floater_do_draw (screen_saver, floater, context)) + { + gtk_main_quit (); + break; + } + } + + cairo_destroy (context); + + screen_saver->draw_ops_pending = TRUE; + screen_saver->frame_count++; +} + +static void +screen_saver_update_state (ScreenSaver *screen_saver, + gdouble time) +{ + GList *tmp; + + tmp = screen_saver->floaters; + while (tmp != NULL) + { + ScreenSaverFloater *floater; + floater = (ScreenSaverFloater *) tmp->data; + + screen_saver_floater_update_state (screen_saver, floater, time); + + if (GTK_WIDGET_REALIZED (screen_saver->drawing_area) + && (floater->bounds.width > 0) && (floater->bounds.height > 0)) + { + gint size; + size = CLAMP ((int) (FLOATER_MAX_SIZE * floater->scale), + FLOATER_MIN_SIZE, FLOATER_MAX_SIZE); + + gtk_widget_queue_draw_area (screen_saver->drawing_area, + floater->bounds.x, + floater->bounds.y, + floater->bounds.width, + floater->bounds.height); + + /* the edges could concievably be spread across two + * pixels so we add +2 to invalidated region + */ + if (screen_saver->should_do_rotations) + gtk_widget_queue_draw_area (screen_saver->drawing_area, + (int) (floater->position.x - + .5 * G_SQRT2 * size), + (int) (floater->position.y - + .5 * G_SQRT2 * size), + G_SQRT2 * size + 2, + G_SQRT2 * size + 2); + else + gtk_widget_queue_draw_area (screen_saver->drawing_area, + (int) (floater->position.x - + .5 * size), + (int) (floater->position.y - + .5 * size), + size + 2, size + 2); + + if (screen_saver->should_show_paths) + gtk_widget_queue_draw (screen_saver->drawing_area); + } + + tmp = tmp->next; + } +} + +static void +screen_saver_get_initial_state (ScreenSaver *screen_saver) +{ + screen_saver->first_update_time = screen_saver_get_timestamp (screen_saver); + screen_saver_update_state (screen_saver, 0.0); +} + +static gboolean +screen_saver_do_update_state (ScreenSaver *screen_saver) +{ + gdouble current_update_time; + + /* flush pending requests to the X server and block for + * replies before proceeding to help prevent the X server from + * getting overrun with requests + */ + if (screen_saver->draw_ops_pending) + { + gdk_flush (); + screen_saver->draw_ops_pending = FALSE; + } + + current_update_time = screen_saver_get_timestamp (screen_saver); + screen_saver_update_state (screen_saver, current_update_time - + screen_saver->first_update_time); + screen_saver->update_count++; + return TRUE; +} + +static gboolean +screen_saver_do_update_stats (ScreenSaver *screen_saver) +{ + gdouble last_calculated_stats_time, seconds_since_last_stats_update; + + last_calculated_stats_time = screen_saver->current_calculated_stats_time; + screen_saver->current_calculated_stats_time = + screen_saver_get_timestamp (screen_saver); + screen_saver->last_calculated_stats_time = last_calculated_stats_time; + + if (abs (last_calculated_stats_time) <= G_MINDOUBLE) + return TRUE; + + seconds_since_last_stats_update = + screen_saver->current_calculated_stats_time - last_calculated_stats_time; + + screen_saver->updates_per_second = + screen_saver->update_count / seconds_since_last_stats_update; + screen_saver->frames_per_second = + screen_saver->frame_count / seconds_since_last_stats_update; + + screen_saver->update_count = 0; + screen_saver->frame_count = 0; + + return TRUE; +} + +static gboolean +do_print_screen_saver_stats (ScreenSaver *screen_saver) +{ + + g_print ("updates per second: %.2f, frames per second: %.2f, " + "image cache %.0f%% full\n", + screen_saver_get_updates_per_second (screen_saver), + screen_saver_get_frames_per_second (screen_saver), + screen_saver_get_image_cache_usage (screen_saver) * 100.0); + + return TRUE; +} + +int +main (int argc, + char *argv[]) +{ + ScreenSaver *screen_saver; + GtkWidget *window; + GtkWidget *drawing_area; + + GtkStateType state; + + GError *error; + + error = NULL; + + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + gtk_init_with_args (&argc, &argv, + /* translators: the word "image" here + * represents a command line argument + */ + _("image - floats images around the screen"), + options, GETTEXT_PACKAGE, &error); + + + if (error != NULL) + { + g_printerr (_("%s. See --help for usage information.\n"), + _(error->message)); + g_error_free (error); + return EX_SOFTWARE; + } + + if ((filenames == NULL) || (filenames[0] == NULL) || + (filenames[1] != NULL)) + { + g_printerr (_("You must specify one image. See --help for usage " + "information.\n")); + return EX_USAGE; + } + + window = gs_theme_window_new (); + + g_signal_connect (G_OBJECT (window), "delete-event", + G_CALLBACK (gtk_main_quit), NULL); + + drawing_area = gtk_drawing_area_new (); + + state = (GtkStateType) 0; + while (state < (GtkStateType) G_N_ELEMENTS (drawing_area->style->bg)) + { + gtk_widget_modify_bg (drawing_area, state, &drawing_area->style->mid[state]); + state++; + } + + gtk_widget_show (drawing_area); + gtk_container_add (GTK_CONTAINER (window), drawing_area); + + screen_saver = screen_saver_new (GTK_DRAWING_AREA (drawing_area), + filenames[0], max_floater_count, + should_do_rotations, should_show_paths); + g_strfreev (filenames); + + if (should_print_stats) + g_timeout_add (STAT_PRINT_FREQUENCY, + (GSourceFunc) do_print_screen_saver_stats, + screen_saver); + + if ((geometry == NULL) + || !gtk_window_parse_geometry (GTK_WINDOW (window), geometry)) + gtk_window_set_default_size (GTK_WINDOW (window), 640, 480); + + gtk_widget_show (window); + + gtk_main (); + + screen_saver_free (screen_saver); + + return EX_OK; +} diff --git a/savers/footlogo-floaters.desktop.in.in b/savers/footlogo-floaters.desktop.in.in new file mode 100644 index 0000000..03cdad4 --- /dev/null +++ b/savers/footlogo-floaters.desktop.in.in @@ -0,0 +1,10 @@ +[Desktop Entry] +_Name=Floating Feet +_Comment=Bubbles the MATE foot logo around the screen +Exec=@SAVERDIR@/floaters @FLOATERS_FOOT_LOGO_IMAGE@ +TryExec=@SAVERDIR@/floaters +StartupNotify=false +Terminal=false +Type=Application +Categories=MATE;Screensaver; +OnlyShowIn=MATE; diff --git a/savers/gs-theme-engine-marshal.list b/savers/gs-theme-engine-marshal.list new file mode 100644 index 0000000..70e7616 --- /dev/null +++ b/savers/gs-theme-engine-marshal.list @@ -0,0 +1 @@ +BOOLEAN:BOXED diff --git a/savers/gs-theme-engine.c b/savers/gs-theme-engine.c new file mode 100644 index 0000000..042bc53 --- /dev/null +++ b/savers/gs-theme-engine.c @@ -0,0 +1,226 @@ +/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*- + * + * Copyright (C) 2005 William Jon McCann <[email protected]> + * + * 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. + * + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <glib.h> +#include <gtk/gtk.h> + +#include "gs-theme-engine.h" +#include "gs-theme-engine-marshal.h" + +static void gs_theme_engine_class_init (GSThemeEngineClass *klass); +static void gs_theme_engine_init (GSThemeEngine *engine); +static void gs_theme_engine_finalize (GObject *object); + +struct GSThemeEnginePrivate +{ + gpointer reserved; +}; + +#define GS_THEME_ENGINE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_THEME_ENGINE, GSThemeEnginePrivate)) + +static GObjectClass *parent_class = NULL; + +G_DEFINE_ABSTRACT_TYPE (GSThemeEngine, gs_theme_engine, GTK_TYPE_DRAWING_AREA) + +void +_gs_theme_engine_profile_log (const char *func, + const char *note, + const char *format, + ...) +{ + va_list args; + char *str; + char *formatted; + + va_start (args, format); + formatted = g_strdup_vprintf (format, args); + va_end (args); + + if (func != NULL) + { + str = g_strdup_printf ("MARK: %s %s: %s %s", g_get_prgname(), func, note ? note : "", formatted); + } + else + { + str = g_strdup_printf ("MARK: %s: %s %s", g_get_prgname(), note ? note : "", formatted); + } + + g_free (formatted); + + access (str, F_OK); + g_free (str); +} + +static void +gs_theme_engine_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GSThemeEngine *self; + + self = GS_THEME_ENGINE (object); + + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gs_theme_engine_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GSThemeEngine *self; + + self = GS_THEME_ENGINE (object); + + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gs_theme_engine_clear (GtkWidget *widget) +{ + GdkColor color = { 0, 0x0000, 0x0000, 0x0000 }; + GdkColormap *colormap; + GtkStateType state; + + g_return_if_fail (GS_IS_THEME_ENGINE (widget)); + + if (! GTK_WIDGET_VISIBLE (widget)) + { + return; + } + + state = (GtkStateType) 0; + while (state < (GtkStateType) G_N_ELEMENTS (widget->style->bg)) + { + gtk_widget_modify_bg (widget, state, &color); + state++; + } + + colormap = gdk_drawable_get_colormap (widget->window); + gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE); + gdk_window_set_background (widget->window, &color); + gdk_window_clear (widget->window); + gdk_flush (); +} + +static gboolean +gs_theme_engine_real_map_event (GtkWidget *widget, + GdkEventAny *event) +{ + gboolean handled = FALSE; + + gs_theme_engine_clear (widget); + + return handled; +} + +static void +gs_theme_engine_class_init (GSThemeEngineClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gs_theme_engine_finalize; + object_class->get_property = gs_theme_engine_get_property; + object_class->set_property = gs_theme_engine_set_property; + + widget_class->map_event = gs_theme_engine_real_map_event; + + g_type_class_add_private (klass, sizeof (GSThemeEnginePrivate)); +} + +static void +gs_theme_engine_init (GSThemeEngine *engine) +{ + engine->priv = GS_THEME_ENGINE_GET_PRIVATE (engine); +} + +static void +gs_theme_engine_finalize (GObject *object) +{ + GSThemeEngine *engine; + + g_return_if_fail (object != NULL); + g_return_if_fail (GS_IS_THEME_ENGINE (object)); + + engine = GS_THEME_ENGINE (object); + + g_return_if_fail (engine->priv != NULL); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +void +gs_theme_engine_get_window_size (GSThemeEngine *engine, + int *width, + int *height) +{ + if (width != NULL) + { + *width = 0; + } + if (height != NULL) + { + *height = 0; + } + + g_return_if_fail (GS_IS_THEME_ENGINE (engine)); + + if (! GTK_WIDGET_VISIBLE (GTK_WIDGET (engine))) + { + return; + } + + gdk_window_get_geometry (GTK_WIDGET (engine)->window, + NULL, + NULL, + width, + height, + NULL); +} + +GdkWindow * +gs_theme_engine_get_window (GSThemeEngine *engine) +{ + g_return_val_if_fail (GS_IS_THEME_ENGINE (engine), NULL); + + return GTK_WIDGET (engine)->window; +} diff --git a/savers/gs-theme-engine.c.orig b/savers/gs-theme-engine.c.orig new file mode 100644 index 0000000..9724e5b --- /dev/null +++ b/savers/gs-theme-engine.c.orig @@ -0,0 +1,216 @@ +/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*- + * + * Copyright (C) 2005 William Jon McCann <[email protected]> + * + * 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. + * + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <glib.h> +#include <gtk/gtk.h> + +#include "gs-theme-engine.h" +#include "gs-theme-engine-marshal.h" + +static void gs_theme_engine_class_init (GSThemeEngineClass *klass); +static void gs_theme_engine_init (GSThemeEngine *engine); +static void gs_theme_engine_finalize (GObject *object); + +struct GSThemeEnginePrivate +{ + gpointer reserved; +}; + +#define GS_THEME_ENGINE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_THEME_ENGINE, GSThemeEnginePrivate)) + +static GObjectClass *parent_class = NULL; + +G_DEFINE_ABSTRACT_TYPE (GSThemeEngine, gs_theme_engine, GTK_TYPE_DRAWING_AREA) + +void +_gs_theme_engine_profile_log (const char *func, + const char *note, + const char *format, + ...) +{ + va_list args; + char *str; + char *formatted; + + va_start (args, format); + formatted = g_strdup_vprintf (format, args); + va_end (args); + + if (func != NULL) { + str = g_strdup_printf ("MARK: %s %s: %s %s", g_get_prgname(), func, note ? note : "", formatted); + } else { + str = g_strdup_printf ("MARK: %s: %s %s", g_get_prgname(), note ? note : "", formatted); + } + + g_free (formatted); + + access (str, F_OK); + g_free (str); +} + +static void +gs_theme_engine_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GSThemeEngine *self; + + self = GS_THEME_ENGINE (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gs_theme_engine_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GSThemeEngine *self; + + self = GS_THEME_ENGINE (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gs_theme_engine_clear (GtkWidget *widget) +{ + GdkColor color = { 0, 0x0000, 0x0000, 0x0000 }; + GdkColormap *colormap; + GtkStateType state; + + g_return_if_fail (GS_IS_THEME_ENGINE (widget)); + + if (! GTK_WIDGET_VISIBLE (widget)) { + return; + } + + state = (GtkStateType) 0; + while (state < (GtkStateType) G_N_ELEMENTS (widget->style->bg)) { + gtk_widget_modify_bg (widget, state, &color); + state++; + } + + colormap = gdk_drawable_get_colormap (widget->window); + gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE); + gdk_window_set_background (widget->window, &color); + gdk_window_clear (widget->window); + gdk_flush (); +} + +static gboolean +gs_theme_engine_real_map_event (GtkWidget *widget, + GdkEventAny *event) +{ + gboolean handled = FALSE; + + gs_theme_engine_clear (widget); + + return handled; +} + +static void +gs_theme_engine_class_init (GSThemeEngineClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gs_theme_engine_finalize; + object_class->get_property = gs_theme_engine_get_property; + object_class->set_property = gs_theme_engine_set_property; + + widget_class->map_event = gs_theme_engine_real_map_event; + + g_type_class_add_private (klass, sizeof (GSThemeEnginePrivate)); +} + +static void +gs_theme_engine_init (GSThemeEngine *engine) +{ + engine->priv = GS_THEME_ENGINE_GET_PRIVATE (engine); +} + +static void +gs_theme_engine_finalize (GObject *object) +{ + GSThemeEngine *engine; + + g_return_if_fail (object != NULL); + g_return_if_fail (GS_IS_THEME_ENGINE (object)); + + engine = GS_THEME_ENGINE (object); + + g_return_if_fail (engine->priv != NULL); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +void +gs_theme_engine_get_window_size (GSThemeEngine *engine, + int *width, + int *height) +{ + if (width != NULL) { + *width = 0; + } + if (height != NULL) { + *height = 0; + } + + g_return_if_fail (GS_IS_THEME_ENGINE (engine)); + + if (! GTK_WIDGET_VISIBLE (GTK_WIDGET (engine))) { + return; + } + + gdk_window_get_geometry (GTK_WIDGET (engine)->window, + NULL, + NULL, + width, + height, + NULL); +} + +GdkWindow * +gs_theme_engine_get_window (GSThemeEngine *engine) +{ + g_return_val_if_fail (GS_IS_THEME_ENGINE (engine), NULL); + + return GTK_WIDGET (engine)->window; +} diff --git a/savers/gs-theme-engine.h b/savers/gs-theme-engine.h new file mode 100644 index 0000000..b4dda3a --- /dev/null +++ b/savers/gs-theme-engine.h @@ -0,0 +1,88 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2005 William Jon McCann <[email protected]> + * + * 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. + * + * Authors: William Jon McCann <[email protected]> + * + */ + +#ifndef __GS_THEME_ENGINE_H +#define __GS_THEME_ENGINE_H + +#include <glib.h> +#include <gdk/gdk.h> + +G_BEGIN_DECLS + +#define GS_TYPE_THEME_ENGINE (gs_theme_engine_get_type ()) +#define GS_THEME_ENGINE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_THEME_ENGINE, GSThemeEngine)) +#define GS_THEME_ENGINE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_THEME_ENGINE, GSThemeEngineClass)) +#define GS_IS_THEME_ENGINE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_THEME_ENGINE)) +#define GS_IS_THEME_ENGINE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_THEME_ENGINE)) +#define GS_THEME_ENGINE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_THEME_ENGINE, GSThemeEngineClass)) + +typedef struct GSThemeEnginePrivate GSThemeEnginePrivate; + +typedef struct +{ + GtkDrawingArea parent; + GSThemeEnginePrivate *priv; +} GSThemeEngine; + +typedef struct +{ + GtkDrawingAreaClass parent_class; + + /* for signals later if needed */ + gpointer reserved_1; + gpointer reserved_2; + gpointer reserved_3; + gpointer reserved_4; +} GSThemeEngineClass; + +GType gs_theme_engine_get_type (void); + +void gs_theme_engine_get_window_size (GSThemeEngine *engine, + int *width, + int *height); +GdkWindow *gs_theme_engine_get_window (GSThemeEngine *engine); + +#define ENABLE_PROFILING 1 +#ifdef ENABLE_PROFILING +#ifdef G_HAVE_ISO_VARARGS +#define gs_theme_engine_profile_start(...) _gs_theme_engine_profile_log (G_STRFUNC, "start", __VA_ARGS__) +#define gs_theme_engine_profile_end(...) _gs_theme_engine_profile_log (G_STRFUNC, "end", __VA_ARGS__) +#define gs_theme_engine_profile_msg(...) _gs_theme_engine_profile_log (NULL, NULL, __VA_ARGS__) +#elif defined(G_HAVE_GNUC_VARARGS) +#define gs_theme_engine_profile_start(format...) _gs_theme_engine_profile_log (G_STRFUNC, "start", format) +#define gs_theme_engine_profile_end(format...) _gs_theme_engine_profile_log (G_STRFUNC, "end", format) +#define gs_theme_engine_profile_msg(format...) _gs_theme_engine_profile_log (NULL, NULL, format) +#endif +#else +#define gs_theme_engine_profile_start(...) +#define gs_theme_engine_profile_end(...) +#define gs_theme_engine_profile_msg(...) +#endif + +void _gs_theme_engine_profile_log (const char *func, + const char *note, + const char *format, + ...) G_GNUC_PRINTF (3, 4); + +G_END_DECLS + +#endif /* __GS_THEME_ENGINE_H */ diff --git a/savers/gs-theme-engine.h.orig b/savers/gs-theme-engine.h.orig new file mode 100644 index 0000000..5c6fd30 --- /dev/null +++ b/savers/gs-theme-engine.h.orig @@ -0,0 +1,88 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2005 William Jon McCann <[email protected]> + * + * 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. + * + * Authors: William Jon McCann <[email protected]> + * + */ + +#ifndef __GS_THEME_ENGINE_H +#define __GS_THEME_ENGINE_H + +#include <glib.h> +#include <gdk/gdk.h> + +G_BEGIN_DECLS + +#define GS_TYPE_THEME_ENGINE (gs_theme_engine_get_type ()) +#define GS_THEME_ENGINE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_THEME_ENGINE, GSThemeEngine)) +#define GS_THEME_ENGINE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_THEME_ENGINE, GSThemeEngineClass)) +#define GS_IS_THEME_ENGINE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_THEME_ENGINE)) +#define GS_IS_THEME_ENGINE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_THEME_ENGINE)) +#define GS_THEME_ENGINE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_THEME_ENGINE, GSThemeEngineClass)) + +typedef struct GSThemeEnginePrivate GSThemeEnginePrivate; + +typedef struct +{ + GtkDrawingArea parent; + GSThemeEnginePrivate *priv; +} GSThemeEngine; + +typedef struct +{ + GtkDrawingAreaClass parent_class; + + /* for signals later if needed */ + gpointer reserved_1; + gpointer reserved_2; + gpointer reserved_3; + gpointer reserved_4; +} GSThemeEngineClass; + +GType gs_theme_engine_get_type (void); + +void gs_theme_engine_get_window_size (GSThemeEngine *engine, + int *width, + int *height); +GdkWindow *gs_theme_engine_get_window (GSThemeEngine *engine); + +#define ENABLE_PROFILING 1 +#ifdef ENABLE_PROFILING +#ifdef G_HAVE_ISO_VARARGS +#define gs_theme_engine_profile_start(...) _gs_theme_engine_profile_log (G_STRFUNC, "start", __VA_ARGS__) +#define gs_theme_engine_profile_end(...) _gs_theme_engine_profile_log (G_STRFUNC, "end", __VA_ARGS__) +#define gs_theme_engine_profile_msg(...) _gs_theme_engine_profile_log (NULL, NULL, __VA_ARGS__) +#elif defined(G_HAVE_GNUC_VARARGS) +#define gs_theme_engine_profile_start(format...) _gs_theme_engine_profile_log (G_STRFUNC, "start", format) +#define gs_theme_engine_profile_end(format...) _gs_theme_engine_profile_log (G_STRFUNC, "end", format) +#define gs_theme_engine_profile_msg(format...) _gs_theme_engine_profile_log (NULL, NULL, format) +#endif +#else +#define gs_theme_engine_profile_start(...) +#define gs_theme_engine_profile_end(...) +#define gs_theme_engine_profile_msg(...) +#endif + +void _gs_theme_engine_profile_log (const char *func, + const char *note, + const char *format, + ...) G_GNUC_PRINTF (3, 4); + +G_END_DECLS + +#endif /* __GS_THEME_ENGINE_H */ diff --git a/savers/gs-theme-window.c b/savers/gs-theme-window.c new file mode 100644 index 0000000..bdc1c88 --- /dev/null +++ b/savers/gs-theme-window.c @@ -0,0 +1,214 @@ +/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*- + * + * gs-theme-window.c - special toplevel for screensavers + * + * Copyright (C) 2005 Ray Strode <[email protected]> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser 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. + * + * Originally written by: Ray Strode <[email protected]> + */ + +#include "config.h" + +#include <errno.h> +#include <stdlib.h> + +#include <glib.h> +#include <glib-object.h> +#include <glib/gi18n.h> + +#include <gdk/gdkx.h> +#include <gtk/gtk.h> + +#include "gs-theme-window.h" + +static void gs_theme_window_finalize (GObject *object); +static void gs_theme_window_real_realize (GtkWidget *widget); + +static GObjectClass *parent_class = NULL; + +G_DEFINE_TYPE (GSThemeWindow, gs_theme_window, GTK_TYPE_WINDOW) + +#define MIN_SIZE 10 + +static void +gs_theme_window_class_init (GSThemeWindowClass *klass) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + + object_class = G_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gs_theme_window_finalize; + + widget_class->realize = gs_theme_window_real_realize; +} + +static void +force_no_pixmap_background (GtkWidget *widget) +{ + static gboolean first_time = TRUE; + + if (first_time) + { + gtk_rc_parse_string ("\n" + " style \"gs-theme-engine-style\"\n" + " {\n" + " bg_pixmap[NORMAL] = \"<none>\"\n" + " bg_pixmap[ACTIVE] = \"<none>\"\n" + " bg_pixmap[PRELIGHT] = \"<none>\"\n" + " bg_pixmap[SELECTED] = \"<none>\"\n" + " bg_pixmap[INSENSITIVE] = \"<none>\"\n" + " bg[NORMAL] = \"#000000\"\n" + " bg[ACTIVE] = \"#000000\"\n" + " bg[PRELIGHT] = \"#000000\"\n" + " bg[SELECTED] = \"#000000\"\n" + " bg[INSENSITIVE] = \"#000000\"\n" + " }\n" + " widget \"gs-window*\" style : highest \"gs-theme-engine-style\"\n" + "\n"); + first_time = FALSE; + } + + gtk_widget_set_name (widget, "gs-window"); +} + +static void +gs_theme_window_init (GSThemeWindow *window) +{ + force_no_pixmap_background (GTK_WIDGET (window)); +} + +static void +gs_theme_window_finalize (GObject *object) +{ + GSThemeWindow *window; + GObjectClass *parent_class; + + window = GS_THEME_WINDOW (object); + + parent_class = G_OBJECT_CLASS (gs_theme_window_parent_class); + + if (parent_class->finalize != NULL) + parent_class->finalize (object); +} + +static void +gs_theme_window_real_realize (GtkWidget *widget) +{ + GdkWindow *window; + Window remote_xwindow; + GtkRequisition requisition; + GtkAllocation allocation; + const char *preview_xid; + int x; + int y; + int width; + int height; + int event_mask; + + event_mask = 0; + window = NULL; + preview_xid = g_getenv ("XSCREENSAVER_WINDOW"); + + if (preview_xid != NULL) + { + char *end; + + remote_xwindow = (Window) strtoul (preview_xid, &end, 0); + + if ((remote_xwindow != 0) && (end != NULL) && + ((*end == ' ') || (*end == '\0')) && + ((remote_xwindow < G_MAXULONG) || (errno != ERANGE))) + { + window = gdk_window_foreign_new (remote_xwindow); + + if (window != NULL) + { + /* This is a kludge; we need to set the same + * flags gs-window-x11.c does, to ensure they + * don't get unset by gtk_window_map() later. + */ + gtk_window_set_decorated (GTK_WINDOW (widget), FALSE); + + gtk_window_set_skip_taskbar_hint (GTK_WINDOW (widget), TRUE); + gtk_window_set_skip_pager_hint (GTK_WINDOW (widget), TRUE); + + gtk_window_set_keep_above (GTK_WINDOW (widget), TRUE); + + gtk_window_fullscreen (GTK_WINDOW (widget)); + + event_mask = GDK_EXPOSURE_MASK | GDK_STRUCTURE_MASK; + gtk_widget_set_events (widget, gtk_widget_get_events (widget) | event_mask); + } + } + } + + if (window == NULL) + { + GtkWidgetClass *parent_class; + + parent_class = GTK_WIDGET_CLASS (gs_theme_window_parent_class); + + if (parent_class->realize != NULL) + parent_class->realize (widget); + + return; + } + + gtk_style_set_background (widget->style, + window, + GTK_STATE_NORMAL); + gdk_window_set_decorations (window, (GdkWMDecoration) 0); + gdk_window_set_events (window, gdk_window_get_events (window) | event_mask); + + widget->window = window; + gdk_window_set_user_data (window, widget); + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + + gdk_window_get_geometry (window, &x, &y, &width, &height, NULL); + + if (width < MIN_SIZE || height < MIN_SIZE) + { + g_critical ("This window is way too small to use"); + exit (1); + } + + gtk_widget_size_request (widget, &requisition); + allocation.x = x; + allocation.y = y; + allocation.width = width; + allocation.height = height; + gtk_widget_size_allocate (widget, &allocation); + gtk_window_resize (GTK_WINDOW (widget), width, height); +} + +GtkWidget * +gs_theme_window_new (void) +{ + GSThemeWindow *window; + + window = g_object_new (GS_TYPE_THEME_WINDOW, + "type", GTK_WINDOW_TOPLEVEL, + NULL); + + return GTK_WIDGET (window); +} diff --git a/savers/gs-theme-window.c.orig b/savers/gs-theme-window.c.orig new file mode 100644 index 0000000..805387c --- /dev/null +++ b/savers/gs-theme-window.c.orig @@ -0,0 +1,208 @@ +/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*- + * + * gs-theme-window.c - special toplevel for screensavers + * + * Copyright (C) 2005 Ray Strode <[email protected]> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser 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. + * + * Originally written by: Ray Strode <[email protected]> + */ + +#include "config.h" + +#include <errno.h> +#include <stdlib.h> + +#include <glib.h> +#include <glib-object.h> +#include <glib/gi18n.h> + +#include <gdk/gdkx.h> +#include <gtk/gtk.h> + +#include "gs-theme-window.h" + +static void gs_theme_window_finalize (GObject *object); +static void gs_theme_window_real_realize (GtkWidget *widget); + +static GObjectClass *parent_class = NULL; + +G_DEFINE_TYPE (GSThemeWindow, gs_theme_window, GTK_TYPE_WINDOW) + +#define MIN_SIZE 10 + +static void +gs_theme_window_class_init (GSThemeWindowClass *klass) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + + object_class = G_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gs_theme_window_finalize; + + widget_class->realize = gs_theme_window_real_realize; +} + +static void +force_no_pixmap_background (GtkWidget *widget) +{ + static gboolean first_time = TRUE; + + if (first_time) { + gtk_rc_parse_string ("\n" + " style \"gs-theme-engine-style\"\n" + " {\n" + " bg_pixmap[NORMAL] = \"<none>\"\n" + " bg_pixmap[ACTIVE] = \"<none>\"\n" + " bg_pixmap[PRELIGHT] = \"<none>\"\n" + " bg_pixmap[SELECTED] = \"<none>\"\n" + " bg_pixmap[INSENSITIVE] = \"<none>\"\n" + " bg[NORMAL] = \"#000000\"\n" + " bg[ACTIVE] = \"#000000\"\n" + " bg[PRELIGHT] = \"#000000\"\n" + " bg[SELECTED] = \"#000000\"\n" + " bg[INSENSITIVE] = \"#000000\"\n" + " }\n" + " widget \"gs-window*\" style : highest \"gs-theme-engine-style\"\n" + "\n"); + first_time = FALSE; + } + + gtk_widget_set_name (widget, "gs-window"); +} + +static void +gs_theme_window_init (GSThemeWindow *window) +{ + force_no_pixmap_background (GTK_WIDGET (window)); +} + +static void +gs_theme_window_finalize (GObject *object) +{ + GSThemeWindow *window; + GObjectClass *parent_class; + + window = GS_THEME_WINDOW (object); + + parent_class = G_OBJECT_CLASS (gs_theme_window_parent_class); + + if (parent_class->finalize != NULL) + parent_class->finalize (object); +} + +static void +gs_theme_window_real_realize (GtkWidget *widget) +{ + GdkWindow *window; + Window remote_xwindow; + GtkRequisition requisition; + GtkAllocation allocation; + const char *preview_xid; + int x; + int y; + int width; + int height; + int event_mask; + + event_mask = 0; + window = NULL; + preview_xid = g_getenv ("XSCREENSAVER_WINDOW"); + + if (preview_xid != NULL) { + char *end; + + remote_xwindow = (Window) strtoul (preview_xid, &end, 0); + + if ((remote_xwindow != 0) && (end != NULL) && + ((*end == ' ') || (*end == '\0')) && + ((remote_xwindow < G_MAXULONG) || (errno != ERANGE))) { + window = gdk_window_foreign_new (remote_xwindow); + + if (window != NULL) { + /* This is a kludge; we need to set the same + * flags gs-window-x11.c does, to ensure they + * don't get unset by gtk_window_map() later. + */ + gtk_window_set_decorated (GTK_WINDOW (widget), FALSE); + + gtk_window_set_skip_taskbar_hint (GTK_WINDOW (widget), TRUE); + gtk_window_set_skip_pager_hint (GTK_WINDOW (widget), TRUE); + + gtk_window_set_keep_above (GTK_WINDOW (widget), TRUE); + + gtk_window_fullscreen (GTK_WINDOW (widget)); + + event_mask = GDK_EXPOSURE_MASK | GDK_STRUCTURE_MASK; + gtk_widget_set_events (widget, gtk_widget_get_events (widget) | event_mask); + } + } + } + + if (window == NULL) { + GtkWidgetClass *parent_class; + + parent_class = GTK_WIDGET_CLASS (gs_theme_window_parent_class); + + if (parent_class->realize != NULL) + parent_class->realize (widget); + + return; + } + + gtk_style_set_background (widget->style, + window, + GTK_STATE_NORMAL); + gdk_window_set_decorations (window, (GdkWMDecoration) 0); + gdk_window_set_events (window, gdk_window_get_events (window) | event_mask); + + widget->window = window; + gdk_window_set_user_data (window, widget); + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + + gdk_window_get_geometry (window, &x, &y, &width, &height, NULL); + + if (width < MIN_SIZE || height < MIN_SIZE) { + g_critical ("This window is way too small to use"); + exit (1); + } + + gtk_widget_size_request (widget, &requisition); + allocation.x = x; + allocation.y = y; + allocation.width = width; + allocation.height = height; + gtk_widget_size_allocate (widget, &allocation); + gtk_window_resize (GTK_WINDOW (widget), width, height); +} + +GtkWidget * +gs_theme_window_new (void) +{ + GSThemeWindow *window; + + window = g_object_new (GS_TYPE_THEME_WINDOW, + "type", GTK_WINDOW_TOPLEVEL, + NULL); + + return GTK_WIDGET (window); +} diff --git a/savers/gs-theme-window.h b/savers/gs-theme-window.h new file mode 100644 index 0000000..8223828 --- /dev/null +++ b/savers/gs-theme-window.h @@ -0,0 +1,72 @@ +/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*- + * + * gs-theme-window.h - special toplevel for screensavers + * + * Copyright (C) 2005 Ray Strode <[email protected]> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser 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. + * + * Originally written by: Ray Strode <[email protected]> + */ + +#ifndef GS_THEME_WINDOW_H +#define GS_THEME_WINDOW_H + +#include <glib.h> +#include <glib-object.h> + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GS_TYPE_THEME_WINDOW (gs_theme_window_get_type ()) +#define GS_THEME_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GS_TYPE_THEME_WINDOW, GSThemeWindow)) +#define GS_THEME_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GS_TYPE_THEME_WINDOW, GSThemeWindowClass)) +#define GS_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GS_TYPE_THEME_WINDOW)) +#define GS_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GS_TYPE_THEME_WINDOW)) +#define GS_THEME_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GS_TYPE_THEME_WINDOW, GSThemeWindowClass)) + +typedef struct _GSThemeWindow GSThemeWindow; +typedef struct _GSThemeWindowClass GSThemeWindowClass; + +struct _GSThemeWindow +{ + GtkWindow parent; + + /*< private >*/ + /* reserved for priv pointer */ + gpointer reserved; +}; + +struct _GSThemeWindowClass +{ + GtkWindowClass parent_class; + + /* for signals later if needed */ + gpointer reserved_1; + gpointer reserved_2; + gpointer reserved_3; + gpointer reserved_4; +}; + +#ifndef GS_HIDE_FUNCTION_DECLARATIONS +GType gs_theme_window_get_type (void); +GtkWidget *gs_theme_window_new (void); +#endif + +G_END_DECLS +#endif /* GS_THEME_WINDOW_H */ diff --git a/savers/gs-theme-window.h.orig b/savers/gs-theme-window.h.orig new file mode 100644 index 0000000..cfc5edf --- /dev/null +++ b/savers/gs-theme-window.h.orig @@ -0,0 +1,72 @@ +/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*- + * + * gs-theme-window.h - special toplevel for screensavers + * + * Copyright (C) 2005 Ray Strode <[email protected]> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser 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. + * + * Originally written by: Ray Strode <[email protected]> + */ + +#ifndef GS_THEME_WINDOW_H +#define GS_THEME_WINDOW_H + +#include <glib.h> +#include <glib-object.h> + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GS_TYPE_THEME_WINDOW (gs_theme_window_get_type ()) +#define GS_THEME_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GS_TYPE_THEME_WINDOW, GSThemeWindow)) +#define GS_THEME_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GS_TYPE_THEME_WINDOW, GSThemeWindowClass)) +#define GS_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GS_TYPE_THEME_WINDOW)) +#define GS_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GS_TYPE_THEME_WINDOW)) +#define GS_THEME_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GS_TYPE_THEME_WINDOW, GSThemeWindowClass)) + +typedef struct _GSThemeWindow GSThemeWindow; +typedef struct _GSThemeWindowClass GSThemeWindowClass; + +struct _GSThemeWindow +{ + GtkWindow parent; + + /*< private >*/ + /* reserved for priv pointer */ + gpointer reserved; +}; + +struct _GSThemeWindowClass +{ + GtkWindowClass parent_class; + + /* for signals later if needed */ + gpointer reserved_1; + gpointer reserved_2; + gpointer reserved_3; + gpointer reserved_4; +}; + +#ifndef GS_HIDE_FUNCTION_DECLARATIONS +GType gs_theme_window_get_type (void); +GtkWidget *gs_theme_window_new (void); +#endif + +G_END_DECLS +#endif /* GS_THEME_WINDOW_H */ diff --git a/savers/gste-popsquares.c b/savers/gste-popsquares.c new file mode 100644 index 0000000..6a6f2e3 --- /dev/null +++ b/savers/gste-popsquares.c @@ -0,0 +1,622 @@ +/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*- + * + * Copyright (C) 2005 William Jon McCann <[email protected]> + * + * 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. + * + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <glib.h> +#include <gtk/gtk.h> + +#include "gs-theme-engine.h" +#include "gste-popsquares.h" + +static void gste_popsquares_class_init (GSTEPopsquaresClass *klass); +static void gste_popsquares_init (GSTEPopsquares *engine); +static void gste_popsquares_finalize (GObject *object); + +typedef struct _square +{ + int x, y, w, h; + int color; +} square; + +struct GSTEPopsquaresPrivate +{ + guint timeout_id; + + int ncolors; + int subdivision; + + GdkGC *gc; + GdkColor *colors; + square *squares; + +}; + +#define GSTE_POPSQUARES_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSTE_TYPE_POPSQUARES, GSTEPopsquaresPrivate)) + +static GObjectClass *parent_class = NULL; + +G_DEFINE_TYPE (GSTEPopsquares, gste_popsquares, GS_TYPE_THEME_ENGINE) + +static void +hsv_to_rgb (int h, + double s, + double v, + unsigned short *r, + unsigned short *g, + unsigned short *b) +{ + double H, S, V, R, G, B; + double p1, p2, p3; + double f; + int i; + + if (s < 0) + { + s = 0; + } + if (v < 0) + { + v = 0; + } + if (s > 1) + { + s = 1; + } + if (v > 1) + { + v = 1; + } + + S = s; + V = v; + H = (h % 360) / 60.0; + i = H; + f = H - i; + p1 = V * (1 - S); + p2 = V * (1 - (S * f)); + p3 = V * (1 - (S * (1 - f))); + + if (i == 0) + { + R = V; + G = p3; + B = p1; + } + else if (i == 1) + { + R = p2; + G = V; + B = p1; + } + else if (i == 2) + { + R = p1; + G = V; + B = p3; + } + else if (i == 3) + { + R = p1; + G = p2; + B = V; + } + else if (i == 4) + { + R = p3; + G = p1; + B = V; + } + else + { + R = V; + G = p1; + B = p2; + } + + *r = R * 65535; + *g = G * 65535; + *b = B * 65535; +} + +static void +rgb_to_hsv (unsigned short r, + unsigned short g, + unsigned short b, + int *h, + double *s, + double *v) +{ + double R, G, B, H, S, V; + double cmax, cmin; + double cmm; + int imax; + + R = ((double) r) / 65535.0; + G = ((double) g) / 65535.0; + B = ((double) b) / 65535.0; + cmax = R; + cmin = G; + imax = 1; + + if (cmax < G) + { + cmax = G; + cmin = R; + imax = 2; + } + if (cmax < B) + { + cmax = B; + imax = 3; + } + if (cmin > B) + { + cmin = B; + } + + cmm = cmax - cmin; + V = cmax; + + if (cmm == 0) + { + S = H = 0; + } + else + { + S = cmm / cmax; + if (imax == 1) + { + H = (G - B) / cmm; + } + else if (imax == 2) + { + H = 2.0 + (B - R) / cmm; + } + else + { + /*if (imax == 3)*/ + H = 4.0 + (R - G) / cmm; + } + + if (H < 0) + { + H += 6.0; + } + } + + *h = (H * 60.0); + *s = S; + *v = V; +} + +static void +make_color_ramp (GdkColormap *colormap, + int h1, + double s1, + double v1, + int h2, + double s2, + double v2, + GdkColor *colors, + int n_colors, + gboolean closed, + gboolean allocate, + gboolean writable) +{ + double dh, ds, dv; /* deltas */ + int i; + int ncolors, wanted; + int total_ncolors = n_colors; + + wanted = total_ncolors; + if (closed) + { + wanted = (wanted / 2) + 1; + } + + ncolors = total_ncolors; + + memset (colors, 0, n_colors * sizeof (*colors)); + + if (closed) + { + ncolors = (ncolors / 2) + 1; + } + + /* Note: unlike other routines in this module, this function assumes that + if h1 and h2 are more than 180 degrees apart, then the desired direction + is always from h1 to h2 (rather than the shorter path.) make_uniform + depends on this. + */ + dh = ((double)h2 - (double)h1) / ncolors; + ds = (s2 - s1) / ncolors; + dv = (v2 - v1) / ncolors; + + for (i = 0; i < ncolors; i++) + { + hsv_to_rgb ((int) (h1 + (i * dh)), + (s1 + (i * ds)), + (v1 + (i * dv)), + &colors [i].red, + &colors [i].green, + &colors [i].blue); + if (allocate) + { + gdk_colormap_alloc_color (colormap, + &colors [i], + writable, + TRUE); + } + } + + if (closed) + { + for (i = ncolors; i < n_colors; i++) + { + colors [i] = colors [n_colors - i]; + } + } + +} + +static void +randomize_square_colors (square *squares, + int nsquares, + int ncolors) +{ + int i; + square *s; + + s = squares; + + for (i = 0; i < nsquares; i++) + { + s[i].color = g_random_int_range (0, ncolors); + } +} + +static void +set_colors (GdkWindow *window, + GdkColor *fg, + GdkColor *bg) +{ + GtkWidget *widget; + GdkColor color; + + widget = gtk_invisible_new (); + + color = widget->style->dark [GTK_STATE_SELECTED]; + fg->red = color.red; + fg->green = color.green; + fg->blue = color.blue; + color = widget->style->bg [GTK_STATE_SELECTED]; + bg->red = color.red; + bg->green = color.green; + bg->blue = color.blue; +} + +static void +gste_popsquares_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GSTEPopsquares *self; + + self = GSTE_POPSQUARES (object); + + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gste_popsquares_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GSTEPopsquares *self; + + self = GSTE_POPSQUARES (object); + + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +setup_squares (GSTEPopsquares *pop) +{ + int window_width; + int window_height; + int nsquares; + int x, y; + int sw, sh, gw, gh; + GdkWindow *window; + + window = gs_theme_engine_get_window (GS_THEME_ENGINE (pop)); + + if (window == NULL) + { + return; + } + + gs_theme_engine_get_window_size (GS_THEME_ENGINE (pop), &window_width, &window_height); + + sw = window_width / pop->priv->subdivision; + sh = window_height / pop->priv->subdivision; + + gw = pop->priv->subdivision; + gh = pop->priv->subdivision; + nsquares = gw * gh; + + if (pop->priv->squares) + { + g_free (pop->priv->squares); + } + pop->priv->squares = g_new0 (square, nsquares); + + for (y = 0; y < gh; y++) + { + for (x = 0; x < gw; x++) + { + square *s = (square *) &pop->priv->squares [gw * y + x]; + s->w = sw; + s->h = sh; + s->x = x * sw; + s->y = y * sh; + } + } +} + +static void +setup_colors (GSTEPopsquares *pop) +{ + double s1, v1, s2, v2 = 0; + int h1, h2 = 0; + int nsquares; + GdkColor fg; + GdkColor bg; + GdkWindow *window; + + window = gs_theme_engine_get_window (GS_THEME_ENGINE (pop)); + + if (window == NULL) + { + return; + } + + set_colors (window, &fg, &bg); + + if (pop->priv->gc) + { + g_object_unref (pop->priv->gc); + } + pop->priv->gc = gdk_gc_new (window); + + if (pop->priv->colors) + { + g_free (pop->priv->colors); + } + pop->priv->colors = g_new0 (GdkColor, pop->priv->ncolors); + + rgb_to_hsv (fg.red, fg.green, fg.blue, &h1, &s1, &v1); + rgb_to_hsv (bg.red, bg.green, bg.blue, &h2, &s2, &v2); + + make_color_ramp (gtk_widget_get_colormap (GTK_WIDGET (pop)), + h1, s1, v1, + h2, s2, v2, + pop->priv->colors, + pop->priv->ncolors, + TRUE, + TRUE, + FALSE); + + nsquares = pop->priv->subdivision * pop->priv->subdivision; + + randomize_square_colors (pop->priv->squares, nsquares, pop->priv->ncolors); +} + +static void +gste_popsquares_real_show (GtkWidget *widget) +{ + GSTEPopsquares *pop = GSTE_POPSQUARES (widget); + + /* start */ + setup_squares (pop); + setup_colors (pop); + + if (GTK_WIDGET_CLASS (parent_class)->show) + { + GTK_WIDGET_CLASS (parent_class)->show (widget); + } +} + +static gboolean +gste_popsquares_real_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + gboolean handled = FALSE; + + /* draw */ + + /* FIXME: should double buffer? */ + + if (GTK_WIDGET_CLASS (parent_class)->expose_event) + { + handled = GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event); + } + + return handled; +} + +static gboolean +gste_popsquares_real_configure (GtkWidget *widget, + GdkEventConfigure *event) +{ + GSTEPopsquares *pop = GSTE_POPSQUARES (widget); + gboolean handled = FALSE; + + /* resize */ + + /* just reset everything */ + setup_squares (pop); + setup_colors (pop); + + /* schedule a redraw */ + gtk_widget_queue_draw (widget); + + if (GTK_WIDGET_CLASS (parent_class)->configure_event) + { + handled = GTK_WIDGET_CLASS (parent_class)->configure_event (widget, event); + } + + return handled; +} + +static void +gste_popsquares_class_init (GSTEPopsquaresClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gste_popsquares_finalize; + object_class->get_property = gste_popsquares_get_property; + object_class->set_property = gste_popsquares_set_property; + + widget_class->show = gste_popsquares_real_show; + widget_class->expose_event = gste_popsquares_real_expose; + widget_class->configure_event = gste_popsquares_real_configure; + + g_type_class_add_private (klass, sizeof (GSTEPopsquaresPrivate)); +} + +static gboolean +draw_iter (GSTEPopsquares *pop) +{ + int border = 1; + gboolean twitch = FALSE; + int x, y; + int sw, sh, gw, gh; + int nsquares; + int window_width; + int window_height; + GdkWindow *window; + + window = gs_theme_engine_get_window (GS_THEME_ENGINE (pop)); + + if (window == NULL) + { + return TRUE; + } + + gs_theme_engine_get_window_size (GS_THEME_ENGINE (pop), + &window_width, + &window_height); + sw = window_width / pop->priv->subdivision; + sh = window_height / pop->priv->subdivision; + + gw = pop->priv->subdivision; + gh = pop->priv->subdivision; + nsquares = gw * gh; + + for (y = 0; y < gh; y++) + { + for (x = 0; x < gw; x++) + { + square *s = (square *) &pop->priv->squares [gw * y + x]; + + gdk_gc_set_foreground (pop->priv->gc, &(pop->priv->colors [s->color])); + gdk_draw_rectangle (window, pop->priv->gc, TRUE, s->x, s->y, + border ? s->w - border : s->w, + border ? s->h - border : s->h); + s->color++; + + if (s->color == pop->priv->ncolors) + { + if (twitch && ((g_random_int_range (0, 4)) == 0)) + { + randomize_square_colors (pop->priv->squares, nsquares, pop->priv->ncolors); + } + else + { + s->color = g_random_int_range (0, pop->priv->ncolors); + } + } + } + } + + return TRUE; +} + +static void +gste_popsquares_init (GSTEPopsquares *pop) +{ + int delay; + + pop->priv = GSTE_POPSQUARES_GET_PRIVATE (pop); + + pop->priv->ncolors = 128; + pop->priv->subdivision = 5; + + delay = 25; + pop->priv->timeout_id = g_timeout_add (delay, (GSourceFunc)draw_iter, pop); +} + +static void +gste_popsquares_finalize (GObject *object) +{ + GSTEPopsquares *pop; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSTE_IS_POPSQUARES (object)); + + pop = GSTE_POPSQUARES (object); + + g_return_if_fail (pop->priv != NULL); + + if (pop->priv->timeout_id > 0) + { + g_source_remove (pop->priv->timeout_id); + pop->priv->timeout_id = 0; + } + + g_free (pop->priv->squares); + g_free (pop->priv->colors); + g_object_unref (pop->priv->gc); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} diff --git a/savers/gste-popsquares.c.orig b/savers/gste-popsquares.c.orig new file mode 100644 index 0000000..cda78c7 --- /dev/null +++ b/savers/gste-popsquares.c.orig @@ -0,0 +1,563 @@ +/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*- + * + * Copyright (C) 2005 William Jon McCann <[email protected]> + * + * 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. + * + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <glib.h> +#include <gtk/gtk.h> + +#include "gs-theme-engine.h" +#include "gste-popsquares.h" + +static void gste_popsquares_class_init (GSTEPopsquaresClass *klass); +static void gste_popsquares_init (GSTEPopsquares *engine); +static void gste_popsquares_finalize (GObject *object); + +typedef struct _square { + int x, y, w, h; + int color; +} square; + +struct GSTEPopsquaresPrivate +{ + guint timeout_id; + + int ncolors; + int subdivision; + + GdkGC *gc; + GdkColor *colors; + square *squares; + +}; + +#define GSTE_POPSQUARES_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSTE_TYPE_POPSQUARES, GSTEPopsquaresPrivate)) + +static GObjectClass *parent_class = NULL; + +G_DEFINE_TYPE (GSTEPopsquares, gste_popsquares, GS_TYPE_THEME_ENGINE) + +static void +hsv_to_rgb (int h, + double s, + double v, + unsigned short *r, + unsigned short *g, + unsigned short *b) +{ + double H, S, V, R, G, B; + double p1, p2, p3; + double f; + int i; + + if (s < 0) { + s = 0; + } + if (v < 0) { + v = 0; + } + if (s > 1) { + s = 1; + } + if (v > 1) { + v = 1; + } + + S = s; V = v; + H = (h % 360) / 60.0; + i = H; + f = H - i; + p1 = V * (1 - S); + p2 = V * (1 - (S * f)); + p3 = V * (1 - (S * (1 - f))); + + if (i == 0) { + R = V; + G = p3; + B = p1; + } else if (i == 1) { + R = p2; + G = V; + B = p1; + } else if (i == 2) { + R = p1; + G = V; + B = p3; + } else if (i == 3) { + R = p1; + G = p2; + B = V; + } else if (i == 4) { + R = p3; + G = p1; + B = V; + } else { + R = V; + G = p1; + B = p2; + } + + *r = R * 65535; + *g = G * 65535; + *b = B * 65535; +} + +static void +rgb_to_hsv (unsigned short r, + unsigned short g, + unsigned short b, + int *h, + double *s, + double *v) +{ + double R, G, B, H, S, V; + double cmax, cmin; + double cmm; + int imax; + + R = ((double) r) / 65535.0; + G = ((double) g) / 65535.0; + B = ((double) b) / 65535.0; + cmax = R; + cmin = G; + imax = 1; + + if (cmax < G) { + cmax = G; cmin = R; imax = 2; + } + if (cmax < B) { + cmax = B; imax = 3; + } + if (cmin > B) { + cmin = B; + } + + cmm = cmax - cmin; + V = cmax; + + if (cmm == 0) { + S = H = 0; + } else { + S = cmm / cmax; + if (imax == 1) { + H = (G - B) / cmm; + } else if (imax == 2) { + H = 2.0 + (B - R) / cmm; + } else { + /*if (imax == 3)*/ + H = 4.0 + (R - G) / cmm; + } + + if (H < 0) { + H += 6.0; + } + } + + *h = (H * 60.0); + *s = S; + *v = V; +} + +static void +make_color_ramp (GdkColormap *colormap, + int h1, + double s1, + double v1, + int h2, + double s2, + double v2, + GdkColor *colors, + int n_colors, + gboolean closed, + gboolean allocate, + gboolean writable) +{ + double dh, ds, dv; /* deltas */ + int i; + int ncolors, wanted; + int total_ncolors = n_colors; + + wanted = total_ncolors; + if (closed) { + wanted = (wanted / 2) + 1; + } + + ncolors = total_ncolors; + + memset (colors, 0, n_colors * sizeof (*colors)); + + if (closed) { + ncolors = (ncolors / 2) + 1; + } + + /* Note: unlike other routines in this module, this function assumes that + if h1 and h2 are more than 180 degrees apart, then the desired direction + is always from h1 to h2 (rather than the shorter path.) make_uniform + depends on this. + */ + dh = ((double)h2 - (double)h1) / ncolors; + ds = (s2 - s1) / ncolors; + dv = (v2 - v1) / ncolors; + + for (i = 0; i < ncolors; i++) { + hsv_to_rgb ((int) (h1 + (i * dh)), + (s1 + (i * ds)), + (v1 + (i * dv)), + &colors [i].red, + &colors [i].green, + &colors [i].blue); + if (allocate) { + gdk_colormap_alloc_color (colormap, + &colors [i], + writable, + TRUE); + } + } + + if (closed) { + for (i = ncolors; i < n_colors; i++) { + colors [i] = colors [n_colors - i]; + } + } + +} + +static void +randomize_square_colors (square *squares, + int nsquares, + int ncolors) +{ + int i; + square *s; + + s = squares; + + for (i = 0; i < nsquares; i++) { + s[i].color = g_random_int_range (0, ncolors); + } +} + +static void +set_colors (GdkWindow *window, + GdkColor *fg, + GdkColor *bg) +{ + GtkWidget *widget; + GdkColor color; + + widget = gtk_invisible_new (); + + color = widget->style->dark [GTK_STATE_SELECTED]; + fg->red = color.red; + fg->green = color.green; + fg->blue = color.blue; + color = widget->style->bg [GTK_STATE_SELECTED]; + bg->red = color.red; + bg->green = color.green; + bg->blue = color.blue; +} + +static void +gste_popsquares_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GSTEPopsquares *self; + + self = GSTE_POPSQUARES (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gste_popsquares_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GSTEPopsquares *self; + + self = GSTE_POPSQUARES (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +setup_squares (GSTEPopsquares *pop) +{ + int window_width; + int window_height; + int nsquares; + int x, y; + int sw, sh, gw, gh; + GdkWindow *window; + + window = gs_theme_engine_get_window (GS_THEME_ENGINE (pop)); + + if (window == NULL) { + return; + } + + gs_theme_engine_get_window_size (GS_THEME_ENGINE (pop), &window_width, &window_height); + + sw = window_width / pop->priv->subdivision; + sh = window_height / pop->priv->subdivision; + + gw = pop->priv->subdivision; + gh = pop->priv->subdivision; + nsquares = gw * gh; + + if (pop->priv->squares) { + g_free (pop->priv->squares); + } + pop->priv->squares = g_new0 (square, nsquares); + + for (y = 0; y < gh; y++) { + for (x = 0; x < gw; x++) { + square *s = (square *) &pop->priv->squares [gw * y + x]; + s->w = sw; + s->h = sh; + s->x = x * sw; + s->y = y * sh; + } + } +} + +static void +setup_colors (GSTEPopsquares *pop) +{ + double s1, v1, s2, v2 = 0; + int h1, h2 = 0; + int nsquares; + GdkColor fg; + GdkColor bg; + GdkWindow *window; + + window = gs_theme_engine_get_window (GS_THEME_ENGINE (pop)); + + if (window == NULL) { + return; + } + + set_colors (window, &fg, &bg); + + if (pop->priv->gc) { + g_object_unref (pop->priv->gc); + } + pop->priv->gc = gdk_gc_new (window); + + if (pop->priv->colors) { + g_free (pop->priv->colors); + } + pop->priv->colors = g_new0 (GdkColor, pop->priv->ncolors); + + rgb_to_hsv (fg.red, fg.green, fg.blue, &h1, &s1, &v1); + rgb_to_hsv (bg.red, bg.green, bg.blue, &h2, &s2, &v2); + + make_color_ramp (gtk_widget_get_colormap (GTK_WIDGET (pop)), + h1, s1, v1, + h2, s2, v2, + pop->priv->colors, + pop->priv->ncolors, + TRUE, + TRUE, + FALSE); + + nsquares = pop->priv->subdivision * pop->priv->subdivision; + + randomize_square_colors (pop->priv->squares, nsquares, pop->priv->ncolors); +} + +static void +gste_popsquares_real_show (GtkWidget *widget) +{ + GSTEPopsquares *pop = GSTE_POPSQUARES (widget); + + /* start */ + setup_squares (pop); + setup_colors (pop); + + if (GTK_WIDGET_CLASS (parent_class)->show) { + GTK_WIDGET_CLASS (parent_class)->show (widget); + } +} + +static gboolean +gste_popsquares_real_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + gboolean handled = FALSE; + + /* draw */ + + /* FIXME: should double buffer? */ + + if (GTK_WIDGET_CLASS (parent_class)->expose_event) { + handled = GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event); + } + + return handled; +} + +static gboolean +gste_popsquares_real_configure (GtkWidget *widget, + GdkEventConfigure *event) +{ + GSTEPopsquares *pop = GSTE_POPSQUARES (widget); + gboolean handled = FALSE; + + /* resize */ + + /* just reset everything */ + setup_squares (pop); + setup_colors (pop); + + /* schedule a redraw */ + gtk_widget_queue_draw (widget); + + if (GTK_WIDGET_CLASS (parent_class)->configure_event) { + handled = GTK_WIDGET_CLASS (parent_class)->configure_event (widget, event); + } + + return handled; +} + +static void +gste_popsquares_class_init (GSTEPopsquaresClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gste_popsquares_finalize; + object_class->get_property = gste_popsquares_get_property; + object_class->set_property = gste_popsquares_set_property; + + widget_class->show = gste_popsquares_real_show; + widget_class->expose_event = gste_popsquares_real_expose; + widget_class->configure_event = gste_popsquares_real_configure; + + g_type_class_add_private (klass, sizeof (GSTEPopsquaresPrivate)); +} + +static gboolean +draw_iter (GSTEPopsquares *pop) +{ + int border = 1; + gboolean twitch = FALSE; + int x, y; + int sw, sh, gw, gh; + int nsquares; + int window_width; + int window_height; + GdkWindow *window; + + window = gs_theme_engine_get_window (GS_THEME_ENGINE (pop)); + + if (window == NULL) { + return TRUE; + } + + gs_theme_engine_get_window_size (GS_THEME_ENGINE (pop), + &window_width, + &window_height); + sw = window_width / pop->priv->subdivision; + sh = window_height / pop->priv->subdivision; + + gw = pop->priv->subdivision; + gh = pop->priv->subdivision; + nsquares = gw * gh; + + for (y = 0; y < gh; y++) { + for (x = 0; x < gw; x++) { + square *s = (square *) &pop->priv->squares [gw * y + x]; + + gdk_gc_set_foreground (pop->priv->gc, &(pop->priv->colors [s->color])); + gdk_draw_rectangle (window, pop->priv->gc, TRUE, s->x, s->y, + border ? s->w - border : s->w, + border ? s->h - border : s->h); + s->color++; + + if (s->color == pop->priv->ncolors) { + if (twitch && ((g_random_int_range (0, 4)) == 0)) { + randomize_square_colors (pop->priv->squares, nsquares, pop->priv->ncolors); + } else { + s->color = g_random_int_range (0, pop->priv->ncolors); + } + } + } + } + + return TRUE; +} + +static void +gste_popsquares_init (GSTEPopsquares *pop) +{ + int delay; + + pop->priv = GSTE_POPSQUARES_GET_PRIVATE (pop); + + pop->priv->ncolors = 128; + pop->priv->subdivision = 5; + + delay = 25; + pop->priv->timeout_id = g_timeout_add (delay, (GSourceFunc)draw_iter, pop); +} + +static void +gste_popsquares_finalize (GObject *object) +{ + GSTEPopsquares *pop; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSTE_IS_POPSQUARES (object)); + + pop = GSTE_POPSQUARES (object); + + g_return_if_fail (pop->priv != NULL); + + if (pop->priv->timeout_id > 0) { + g_source_remove (pop->priv->timeout_id); + pop->priv->timeout_id = 0; + } + + g_free (pop->priv->squares); + g_free (pop->priv->colors); + g_object_unref (pop->priv->gc); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} diff --git a/savers/gste-popsquares.h b/savers/gste-popsquares.h new file mode 100644 index 0000000..eefb363 --- /dev/null +++ b/savers/gste-popsquares.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2005 William Jon McCann <[email protected]> + * + * 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. + * + * Authors: William Jon McCann <[email protected]> + * + */ + +#ifndef __GSTE_POPSQUARES_H +#define __GSTE_POPSQUARES_H + +#include <glib.h> +#include <gdk/gdk.h> +#include "gs-theme-engine.h" + +G_BEGIN_DECLS + +#define GSTE_TYPE_POPSQUARES (gste_popsquares_get_type ()) +#define GSTE_POPSQUARES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSTE_TYPE_POPSQUARES, GSTEPopsquares)) +#define GSTE_POPSQUARES_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSTE_TYPE_POPSQUARES, GSTEPopsquaresClass)) +#define GSTE_IS_POPSQUARES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSTE_TYPE_POPSQUARES)) +#define GSTE_IS_POPSQUARES_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSTE_TYPE_POPSQUARES)) +#define GSTE_POPSQUARES_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSTE_TYPE_POPSQUARES, GSTEPopsquaresClass)) + +typedef struct GSTEPopsquaresPrivate GSTEPopsquaresPrivate; + +typedef struct +{ + GSThemeEngine parent; + GSTEPopsquaresPrivate *priv; +} GSTEPopsquares; + +typedef struct +{ + GSThemeEngineClass parent_class; +} GSTEPopsquaresClass; + +GType gste_popsquares_get_type (void); +GSThemeEngine *gste_popsquares_new (void); + +G_END_DECLS + +#endif /* __GSTE_POPSQUARES_H */ diff --git a/savers/gste-popsquares.h.orig b/savers/gste-popsquares.h.orig new file mode 100644 index 0000000..4139652 --- /dev/null +++ b/savers/gste-popsquares.h.orig @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2005 William Jon McCann <[email protected]> + * + * 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. + * + * Authors: William Jon McCann <[email protected]> + * + */ + +#ifndef __GSTE_POPSQUARES_H +#define __GSTE_POPSQUARES_H + +#include <glib.h> +#include <gdk/gdk.h> +#include "gs-theme-engine.h" + +G_BEGIN_DECLS + +#define GSTE_TYPE_POPSQUARES (gste_popsquares_get_type ()) +#define GSTE_POPSQUARES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSTE_TYPE_POPSQUARES, GSTEPopsquares)) +#define GSTE_POPSQUARES_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSTE_TYPE_POPSQUARES, GSTEPopsquaresClass)) +#define GSTE_IS_POPSQUARES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSTE_TYPE_POPSQUARES)) +#define GSTE_IS_POPSQUARES_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSTE_TYPE_POPSQUARES)) +#define GSTE_POPSQUARES_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSTE_TYPE_POPSQUARES, GSTEPopsquaresClass)) + +typedef struct GSTEPopsquaresPrivate GSTEPopsquaresPrivate; + +typedef struct +{ + GSThemeEngine parent; + GSTEPopsquaresPrivate *priv; +} GSTEPopsquares; + +typedef struct +{ + GSThemeEngineClass parent_class; +} GSTEPopsquaresClass; + +GType gste_popsquares_get_type (void); +GSThemeEngine *gste_popsquares_new (void); + +G_END_DECLS + +#endif /* __GSTE_POPSQUARES_H */ diff --git a/savers/gste-slideshow.c b/savers/gste-slideshow.c new file mode 100644 index 0000000..9180be8 --- /dev/null +++ b/savers/gste-slideshow.c @@ -0,0 +1,1082 @@ +/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*- + * + * Copyright (C) 2005-2006 William Jon McCann <[email protected]> + * + * 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. + * + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <glib.h> +#include <gtk/gtk.h> + +#include "gs-theme-engine.h" +#include "gste-slideshow.h" + +static void gste_slideshow_class_init (GSTESlideshowClass *klass); +static void gste_slideshow_init (GSTESlideshow *engine); +static void gste_slideshow_finalize (GObject *object); + +struct GSTESlideshowPrivate +{ + /* Image at full opacity */ + cairo_pattern_t *pat1; + /* Image at partial opacity */ + cairo_pattern_t *pat2; + /* Alpha of pat2 */ + gdouble alpha2; + /* edges of pat2 */ + int pat2top; + int pat2bottom; + int pat2left; + int pat2right; + + /* backbuffer that we do all the alpha drawing into (no round + * trips to the X server when the server doesn't support drawing + * pixmaps with alpha?) */ + cairo_surface_t *surf; + + gint64 fade_ticks; + + GThread *load_thread; + GAsyncQueue *op_q; + GAsyncQueue *results_q; + + guint results_pull_id; + guint update_image_id; + + GSList *filename_list; + char *images_location; + gboolean sort_images; + int window_width; + int window_height; + PangoColor *background_color; + gboolean no_stretch_hint; + + guint timeout_id; + + GTimer *timer; + gboolean fade_disabled; +}; + +enum +{ + PROP_0, + PROP_IMAGES_LOCATION, + PROP_SORT_IMAGES, + PROP_SOLID_BACKGROUND, + PROP_NO_STRETCH_HINT +}; + +#define GSTE_SLIDESHOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSTE_TYPE_SLIDESHOW, GSTESlideshowPrivate)) + +static GObjectClass *parent_class = NULL; + +G_DEFINE_TYPE (GSTESlideshow, gste_slideshow, GS_TYPE_THEME_ENGINE) + +#define N_FADE_TICKS 10 +#define MINIMUM_FPS 3.0 +#define DEFAULT_IMAGES_LOCATION DATADIR "/pixmaps/backgrounds" +#define IMAGE_LOAD_TIMEOUT 10000 + +typedef struct _Op +{ + char *location; + GSTESlideshow *slideshow; +} Op; + +typedef struct _OpResult +{ + GdkPixbuf *pixbuf; +} OpResult; + +static gboolean +push_load_image_func (GSTESlideshow *show) +{ + Op *op; + + gs_theme_engine_profile_msg ("Starting a new image load"); + + op = g_new (Op, 1); + + op->location = g_strdup (show->priv->images_location); + op->slideshow = g_object_ref (show); + + g_async_queue_push (show->priv->op_q, op); + + show->priv->update_image_id = 0; + + return FALSE; +} + +static void +start_new_load (GSTESlideshow *show, + guint timeout) +{ + gs_theme_engine_profile_msg ("Scheduling a new image load"); + + /* queue a new load */ + if (show->priv->update_image_id <= 0) + { + show->priv->update_image_id = g_timeout_add_full (G_PRIORITY_LOW, timeout, + (GSourceFunc)push_load_image_func, + show, NULL); + } +} + +static void +start_fade (GSTESlideshow *show, + GdkPixbuf *pixbuf) +{ + int pw; + int ph; + int x; + int y; + cairo_t *cr; + int window_width; + int window_height; + + gs_theme_engine_profile_start ("start"); + + window_width = show->priv->window_width; + window_height = show->priv->window_height; + + if (show->priv->pat2 != NULL) + { + cairo_pattern_destroy (show->priv->pat2); + } + + pw = gdk_pixbuf_get_width (pixbuf); + ph = gdk_pixbuf_get_height (pixbuf); + x = (window_width - pw) / 2; + y = (window_height - ph) / 2; + + if (gdk_pixbuf_get_has_alpha (pixbuf) && show->priv->background_color) + { + GdkPixbuf *colored; + guint32 color; + GdkPixmap *pixmap; + + color = (show->priv->background_color->red << 16) + + (show->priv->background_color->green / 256 << 8) + + show->priv->background_color->blue / 256; + colored = gdk_pixbuf_composite_color_simple (pixbuf, + pw, ph, + GDK_INTERP_BILINEAR, + 255, + 256, + color, + color); + pixmap = gdk_pixmap_new (NULL, ph, pw, gdk_visual_get_system ()->depth); + + gdk_draw_pixbuf (pixmap, NULL, colored, 0, 0, 0, 0, -1, -1, GDK_RGB_DITHER_MAX, 0, 0); + gdk_pixbuf_get_from_drawable (pixbuf, pixmap, NULL, 0, 0, 0, 0, -1, -1); + + g_object_unref (pixmap); + + g_object_unref(colored); + } + + cr = cairo_create (show->priv->surf); + + /* XXX Handle out of memory? */ + gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y); + show->priv->pat2 = cairo_pattern_reference (cairo_get_source (cr)); + show->priv->pat2top = y; + show->priv->pat2bottom = y + ph; + show->priv->pat2left = x; + show->priv->pat2right = x + pw; + + cairo_destroy (cr); + + show->priv->fade_ticks = 0; + g_timer_start (show->priv->timer); + + gs_theme_engine_profile_end ("end"); +} + +static void +finish_fade (GSTESlideshow *show) +{ + gs_theme_engine_profile_start ("start"); + + if (show->priv->pat1 != NULL) + { + cairo_pattern_destroy (show->priv->pat1); + } + + show->priv->pat1 = show->priv->pat2; + show->priv->pat2 = NULL; + + start_new_load (show, IMAGE_LOAD_TIMEOUT); + + gs_theme_engine_profile_end ("end"); +} + +static void +update_display (GSTESlideshow *show) +{ + int window_width; + int window_height; + cairo_t *cr; + + gs_theme_engine_profile_start ("start"); + + cr = cairo_create (show->priv->surf); + + gs_theme_engine_get_window_size (GS_THEME_ENGINE (show), + &window_width, + &window_height); + + if (show->priv->pat2 != NULL) + { + /* fade out areas not covered by the new image */ + /* top */ + cairo_rectangle (cr, 0, 0, window_width, show->priv->pat2top); + if (show->priv->background_color) + { + cairo_set_source_rgba (cr, show->priv->background_color->red / 65535.0, + show->priv->background_color->green / 65535.0, + show->priv->background_color->blue / 65535.0, show->priv->alpha2); + } + else + { + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, show->priv->alpha2); + } + cairo_fill (cr); + /* left (excluding what's covered by top and bottom) */ + cairo_rectangle (cr, 0, show->priv->pat2top, + show->priv->pat2left, + show->priv->pat2bottom - show->priv->pat2top); + if (show->priv->background_color) + { + cairo_set_source_rgba (cr, show->priv->background_color->red / 65535.0, + show->priv->background_color->green / 65535.0, + show->priv->background_color->blue / 65535.0, show->priv->alpha2); + } + else + { + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, show->priv->alpha2); + } + cairo_fill (cr); + /* bottom */ + cairo_rectangle (cr, 0, show->priv->pat2bottom, window_width, + window_height - show->priv->pat2bottom); + if (show->priv->background_color) + { + cairo_set_source_rgba (cr, show->priv->background_color->red / 65535.0, + show->priv->background_color->green / 65535.0, + show->priv->background_color->blue / 65535.0, show->priv->alpha2); + } + else + { + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, show->priv->alpha2); + } + cairo_fill (cr); + /* right (excluding what's covered by top and bottom) */ + cairo_rectangle (cr, show->priv->pat2right, + show->priv->pat2top, + window_width - show->priv->pat2right, + show->priv->pat2bottom - show->priv->pat2top); + if (show->priv->background_color) + { + cairo_set_source_rgba (cr, show->priv->background_color->red / 65535.0, + show->priv->background_color->green / 65535.0, + show->priv->background_color->blue / 65535.0, show->priv->alpha2); + } + else + { + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, show->priv->alpha2); + } + cairo_fill (cr); + + gs_theme_engine_profile_start ("paint pattern to surface"); + cairo_set_source (cr, show->priv->pat2); + + cairo_paint_with_alpha (cr, show->priv->alpha2); + gs_theme_engine_profile_end ("paint pattern to surface"); + } + else + { + if (show->priv->pat1 != NULL) + { + cairo_set_source (cr, show->priv->pat1); + cairo_paint (cr); + } + } + + cairo_destroy (cr); + + /* paint the image buffer into the window */ + cr = gdk_cairo_create (GTK_WIDGET (show)->window); + + cairo_set_source_surface (cr, show->priv->surf, 0, 0); + + gs_theme_engine_profile_start ("paint surface to window"); + cairo_paint (cr); + gs_theme_engine_profile_end ("paint surface to window"); + + cairo_destroy (cr); + + gs_theme_engine_profile_end ("end"); +} + +static gboolean +draw_iter (GSTESlideshow *show) +{ + double old_opacity; + double new_opacity; + + if (show->priv->pat2 != NULL) + { + gdouble fps; + gdouble elapsed; + + if (show->priv->fade_disabled) + { + show->priv->alpha2 = 1.0; + update_display (show); + finish_fade (show); + return TRUE; + } + + /* we are in a fade */ + show->priv->fade_ticks++; + + /* + * We have currently drawn pat2 with old_opacity, and we + * want to set alpha2 so that drawing pat2 at alpha2 + * yields it drawn with new_opacity + * + * Solving + * new_opacity = 1 - (1 - alpha2) * (1 - old_opacity) + * yields + * alpha2 = 1 - (1 - new_opacity) / (1 - old_opacity) + * + * XXX This assumes that cairo doesn't correct alpha for + * the color profile. However, any error is guaranteed + * to be cleaned up by the last iteration, where alpha2 + * becomes 1 because new_opacity is 1. + */ + old_opacity = (double) (show->priv->fade_ticks - 1) / + (double) N_FADE_TICKS; + new_opacity = (double) show->priv->fade_ticks / + (double) N_FADE_TICKS; + show->priv->alpha2 = 1.0 - (1.0 - new_opacity) / + (1.0 - old_opacity); + + update_display (show); + + elapsed = g_timer_elapsed (show->priv->timer, NULL); + fps = (gdouble)show->priv->fade_ticks / elapsed; + if (fps < MINIMUM_FPS) + { + g_warning ("Getting less than %.2f frames per second, disabling fade", MINIMUM_FPS); + show->priv->fade_ticks = N_FADE_TICKS - 1; + show->priv->fade_disabled = TRUE; + } + + if (show->priv->fade_ticks >= N_FADE_TICKS) + { + finish_fade (show); + } + } + + return TRUE; +} + +static void +process_new_pixbuf (GSTESlideshow *show, + GdkPixbuf *pixbuf) +{ + gs_theme_engine_profile_msg ("Processing a new image"); + + if (pixbuf != NULL) + { + start_fade (show, pixbuf); + } + else + { + start_new_load (show, 10); + } +} + +static void +op_result_free (OpResult *result) +{ + if (result == NULL) + { + return; + } + + if (result->pixbuf != NULL) + { + g_object_unref (result->pixbuf); + } + + g_free (result); +} + +static gboolean +results_pull_func (GSTESlideshow *show) +{ + OpResult *result; + + GDK_THREADS_ENTER (); + + g_async_queue_lock (show->priv->results_q); + + result = g_async_queue_try_pop_unlocked (show->priv->results_q); + g_assert (result); + + while (result != NULL) + { + process_new_pixbuf (show, result->pixbuf); + op_result_free (result); + + result = g_async_queue_try_pop_unlocked (show->priv->results_q); + } + + show->priv->results_pull_id = 0; + + g_async_queue_unlock (show->priv->results_q); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static GdkPixbuf * +scale_pixbuf (GdkPixbuf *pixbuf, + int max_width, + int max_height, + gboolean no_stretch_hint) +{ + int pw; + int ph; + float scale_factor_x = 1.0; + float scale_factor_y = 1.0; + float scale_factor = 1.0; + + pw = gdk_pixbuf_get_width (pixbuf); + ph = gdk_pixbuf_get_height (pixbuf); + + /* If the image is less than 256 wide or high then it + is probably a thumbnail and we should ignore it */ + if (pw < 256 || ph < 256) + { + return NULL; + } + + /* Determine which dimension requires the smallest scale. */ + scale_factor_x = (float) max_width / (float) pw; + scale_factor_y = (float) max_height / (float) ph; + + if (scale_factor_x > scale_factor_y) + { + scale_factor = scale_factor_y; + } + else + { + scale_factor = scale_factor_x; + } + + /* always scale down, allow to disable scaling up */ + if (scale_factor < 1.0 || !no_stretch_hint) + { + int scale_x; + int scale_y; + + scale_x = (int) (pw * scale_factor); + scale_y = (int) (ph * scale_factor); + return gdk_pixbuf_scale_simple (pixbuf, + scale_x, + scale_y, + GDK_INTERP_BILINEAR); + } + else + { + return g_object_ref (pixbuf); + } +} + +static void +add_files_to_list (GSList **list, + const char *base) +{ + GDir *d; + const char *d_name; + + d = g_dir_open (base, 0, NULL); + if (d == NULL) + { + g_warning ("Could not open directory: %s", base); + return; + } + + while ((d_name = g_dir_read_name (d)) != NULL) + { + char *path; + + /* skip hidden files */ + if (d_name[0] == '.') + { + continue; + } + + path = g_build_filename (base, d_name, NULL); + if (g_file_test (path, G_FILE_TEST_IS_DIR)) + { + add_files_to_list (list, path); + g_free (path); + } + else + { + *list = g_slist_prepend (*list, path); + } + } + + g_dir_close (d); +} + +static GSList * +build_filename_list_local_dir (const char *base) +{ + GSList *list = NULL; + + add_files_to_list (&list, base); + + return list; +} + +static int +gste_strcmp_compare_func (gconstpointer string_a, gconstpointer string_b) +{ + return strcmp (string_a == NULL ? "" : string_a, + string_b == NULL ? "" : string_b); +} + + +static GdkPixbuf * +get_pixbuf_from_local_dir (GSTESlideshow *show, + const char *location) +{ + GdkPixbuf *pixbuf, *transformed_pixbuf; + char *filename; + int i; + GSList *l; + + /* rebuild the cache */ + if (show->priv->filename_list == NULL) + { + show->priv->filename_list = build_filename_list_local_dir (location); + } + + if (show->priv->filename_list == NULL) + { + return NULL; + } + else + { + if (show->priv->sort_images) + { + show->priv->filename_list = g_slist_sort (show->priv->filename_list, gste_strcmp_compare_func); + } + } + + /* get a random filename if needed */ + if (! show->priv->sort_images) + { + i = g_random_int_range (0, g_slist_length (show->priv->filename_list)); + l = g_slist_nth (show->priv->filename_list, i); + } + else + { + l = show->priv->filename_list; + } + filename = l->data; + + pixbuf = gdk_pixbuf_new_from_file (filename, NULL); + + if (pixbuf != NULL) + { + transformed_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf); + g_object_unref (pixbuf); + } + else + { + transformed_pixbuf = NULL; + } + + g_free (filename); + show->priv->filename_list = g_slist_delete_link (show->priv->filename_list, l); + + return transformed_pixbuf; +} + +static GdkPixbuf * +get_pixbuf_from_location (GSTESlideshow *show, + const char *location) +{ + GdkPixbuf *pixbuf = NULL; + gboolean is_dir; + + if (location == NULL) + { + return NULL; + } + + is_dir = g_file_test (location, G_FILE_TEST_IS_DIR); + + if (is_dir) + { + pixbuf = get_pixbuf_from_local_dir (show, location); + } + + return pixbuf; +} + +static GdkPixbuf * +get_pixbuf (GSTESlideshow *show, + const char *location, + int width, + int height) +{ + GdkPixbuf *pixbuf; + GdkPixbuf *scaled = NULL; + + if (location == NULL) + { + return NULL; + } + + pixbuf = get_pixbuf_from_location (show, location); + + if (pixbuf != NULL) + { + scaled = scale_pixbuf (pixbuf, width, height, show->priv->no_stretch_hint); + g_object_unref (pixbuf); + } + + return scaled; +} + +static void +op_load_image (GSTESlideshow *show, + const char *location) +{ + OpResult *op_result; + int window_width; + int window_height; + + window_width = show->priv->window_width; + window_height = show->priv->window_height; + + op_result = g_new0 (OpResult, 1); + + op_result->pixbuf = get_pixbuf (show, + location, + window_width, + window_height); + + GDK_THREADS_ENTER (); + g_async_queue_lock (show->priv->results_q); + g_async_queue_push_unlocked (show->priv->results_q, op_result); + + if (show->priv->results_pull_id == 0) + { + show->priv->results_pull_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE, + (GSourceFunc)results_pull_func, + show, NULL); + } + + g_async_queue_unlock (show->priv->results_q); + GDK_THREADS_LEAVE (); +} + +static gpointer +load_threadfunc (GAsyncQueue *op_q) +{ + Op *op; + + op = g_async_queue_pop (op_q); + while (op) + { + op_load_image (op->slideshow, + op->location); + + if (op->slideshow != NULL) + { + g_object_unref (op->slideshow); + } + g_free (op->location); + g_free (op); + + op = g_async_queue_pop (op_q); + } + + return NULL; +} + +void +gste_slideshow_set_images_location (GSTESlideshow *show, + const char *location) +{ + g_return_if_fail (GSTE_IS_SLIDESHOW (show)); + + g_free (show->priv->images_location); + show->priv->images_location = g_strdup (location); +} + + +void +gste_slideshow_set_sort_images (GSTESlideshow *show, + gboolean sort_images) +{ + g_return_if_fail (GSTE_IS_SLIDESHOW (show)); + + show->priv->sort_images = sort_images; +} + +void +gste_slideshow_set_no_stretch_hint (GSTESlideshow *show, + gboolean no_stretch_hint) +{ + g_return_if_fail (GSTE_IS_SLIDESHOW (show)); + + show->priv->no_stretch_hint = no_stretch_hint; +} + +void +gste_slideshow_set_background_color (GSTESlideshow *show, + const char *background_color) +{ + g_return_if_fail (GSTE_IS_SLIDESHOW (show)); + + if (show->priv->background_color != NULL) + { + g_slice_free (PangoColor, show->priv->background_color); + show->priv->background_color = NULL; + } + + if (background_color != NULL) + { + show->priv->background_color = g_slice_new (PangoColor); + + if (pango_color_parse (show->priv->background_color, background_color) == FALSE) + { + g_slice_free (PangoColor, show->priv->background_color); + show->priv->background_color = NULL; + } + } +} + +static void +gste_slideshow_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GSTESlideshow *self; + + self = GSTE_SLIDESHOW (object); + + switch (prop_id) + { + case PROP_IMAGES_LOCATION: + gste_slideshow_set_images_location (self, g_value_get_string (value)); + break; + case PROP_SORT_IMAGES: + gste_slideshow_set_sort_images (self, g_value_get_boolean (value)); + break; + case PROP_SOLID_BACKGROUND: + gste_slideshow_set_background_color (self, g_value_get_string (value)); + break; + case PROP_NO_STRETCH_HINT: + gste_slideshow_set_no_stretch_hint (self, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gste_slideshow_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GSTESlideshow *self; + + self = GSTE_SLIDESHOW (object); + + switch (prop_id) + { + case PROP_IMAGES_LOCATION: + g_value_set_string (value, self->priv->images_location); + break; + case PROP_SORT_IMAGES: + g_value_set_boolean (value, self->priv->sort_images); + break; + case PROP_SOLID_BACKGROUND: + { + char *color = NULL; + color = pango_color_to_string (self->priv->background_color); + g_value_set_string (value, color); + g_free (color); + break; + } + case PROP_NO_STRETCH_HINT: + g_value_set_boolean (value, self->priv->no_stretch_hint); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gste_slideshow_real_show (GtkWidget *widget) +{ + GSTESlideshow *show = GSTE_SLIDESHOW (widget); + int delay; + + if (GTK_WIDGET_CLASS (parent_class)->show) + { + GTK_WIDGET_CLASS (parent_class)->show (widget); + } + + start_new_load (show, 10); + + delay = 25; + show->priv->timeout_id = g_timeout_add (delay, (GSourceFunc)draw_iter, show); + + if (show->priv->timer != NULL) + { + g_timer_destroy (show->priv->timer); + } + show->priv->timer = g_timer_new (); +} + +static gboolean +gste_slideshow_real_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + GSTESlideshow *show = GSTE_SLIDESHOW (widget); + gboolean handled = FALSE; + + update_display (show); + + if (GTK_WIDGET_CLASS (parent_class)->expose_event) + { + handled = GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event); + } + + return handled; +} + +static gboolean +gste_slideshow_real_configure (GtkWidget *widget, + GdkEventConfigure *event) +{ + GSTESlideshow *show = GSTE_SLIDESHOW (widget); + gboolean handled = FALSE; + cairo_t *cr; + + /* resize */ + gs_theme_engine_get_window_size (GS_THEME_ENGINE (show), + &show->priv->window_width, + &show->priv->window_height); + + gs_theme_engine_profile_msg ("Resize to x:%d y:%d", + show->priv->window_width, + show->priv->window_height); + + if (show->priv->surf != NULL) + { + cairo_surface_destroy (show->priv->surf); + } + + cr = gdk_cairo_create (widget->window); + show->priv->surf = cairo_surface_create_similar (cairo_get_target (cr), + CAIRO_CONTENT_COLOR, + show->priv->window_width, + show->priv->window_height); + cairo_destroy (cr); + + /* schedule a redraw */ + gtk_widget_queue_draw (widget); + + if (GTK_WIDGET_CLASS (parent_class)->configure_event) + { + handled = GTK_WIDGET_CLASS (parent_class)->configure_event (widget, event); + } + + return handled; +} + +static void +gste_slideshow_class_init (GSTESlideshowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gste_slideshow_finalize; + object_class->get_property = gste_slideshow_get_property; + object_class->set_property = gste_slideshow_set_property; + + widget_class->show = gste_slideshow_real_show; + widget_class->expose_event = gste_slideshow_real_expose; + widget_class->configure_event = gste_slideshow_real_configure; + + g_type_class_add_private (klass, sizeof (GSTESlideshowPrivate)); + + g_object_class_install_property (object_class, + PROP_IMAGES_LOCATION, + g_param_spec_string ("images-location", + NULL, + NULL, + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_SORT_IMAGES, + g_param_spec_boolean ("sort-images", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_SOLID_BACKGROUND, + g_param_spec_string ("background-color", + NULL, + NULL, + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_NO_STRETCH_HINT, + g_param_spec_boolean ("no-stretch", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); +} + +static void +set_colormap (GtkWidget *widget) +{ + GdkScreen *screen; + GdkColormap *colormap; + + screen = gtk_widget_get_screen (widget); + colormap = gdk_screen_get_rgba_colormap (screen); + if (colormap == NULL) + { + colormap = gdk_screen_get_rgb_colormap (screen); + } + + gtk_widget_set_colormap (widget, colormap); +} + +static void +gste_slideshow_init (GSTESlideshow *show) +{ + GError *error; + + show->priv = GSTE_SLIDESHOW_GET_PRIVATE (show); + + show->priv->images_location = g_strdup (DEFAULT_IMAGES_LOCATION); + + show->priv->op_q = g_async_queue_new (); + show->priv->results_q = g_async_queue_new (); + + error = NULL; + show->priv->load_thread = g_thread_create ((GThreadFunc)load_threadfunc, show->priv->op_q, FALSE, &error); + if (show->priv->load_thread == NULL) + { + g_error ("Could not create a thread to load images: %s", + error->message); + g_error_free (error); + exit (-1); + } + + set_colormap (GTK_WIDGET (show)); +} + +static void +gste_slideshow_finalize (GObject *object) +{ + GSTESlideshow *show; + gpointer result; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSTE_IS_SLIDESHOW (object)); + + show = GSTE_SLIDESHOW (object); + + g_return_if_fail (show->priv != NULL); + + if (show->priv->surf) + { + cairo_surface_destroy (show->priv->surf); + } + + if (show->priv->timeout_id > 0) + { + g_source_remove (show->priv->timeout_id); + show->priv->timeout_id = 0; + } + + if (show->priv->results_pull_id > 0) + { + g_source_remove (show->priv->results_pull_id); + } + + if (show->priv->results_q != NULL) + { + result = g_async_queue_try_pop (show->priv->results_q); + + while (result) + { + result = g_async_queue_try_pop (show->priv->results_q); + } + g_async_queue_unref (show->priv->results_q); + } + + g_free (show->priv->images_location); + show->priv->images_location = NULL; + + if (show->priv->background_color) + { + g_slice_free (PangoColor, show->priv->background_color); + show->priv->background_color = NULL; + } + + if (show->priv->timer != NULL) + { + g_timer_destroy (show->priv->timer); + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} diff --git a/savers/gste-slideshow.c.orig b/savers/gste-slideshow.c.orig new file mode 100644 index 0000000..8ea83bd --- /dev/null +++ b/savers/gste-slideshow.c.orig @@ -0,0 +1,1001 @@ +/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*- + * + * Copyright (C) 2005-2006 William Jon McCann <[email protected]> + * + * 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. + * + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <glib.h> +#include <gtk/gtk.h> + +#include "gs-theme-engine.h" +#include "gste-slideshow.h" + +static void gste_slideshow_class_init (GSTESlideshowClass *klass); +static void gste_slideshow_init (GSTESlideshow *engine); +static void gste_slideshow_finalize (GObject *object); + +struct GSTESlideshowPrivate +{ + /* Image at full opacity */ + cairo_pattern_t *pat1; + /* Image at partial opacity */ + cairo_pattern_t *pat2; + /* Alpha of pat2 */ + gdouble alpha2; + /* edges of pat2 */ + int pat2top; + int pat2bottom; + int pat2left; + int pat2right; + + /* backbuffer that we do all the alpha drawing into (no round + * trips to the X server when the server doesn't support drawing + * pixmaps with alpha?) */ + cairo_surface_t *surf; + + gint64 fade_ticks; + + GThread *load_thread; + GAsyncQueue *op_q; + GAsyncQueue *results_q; + + guint results_pull_id; + guint update_image_id; + + GSList *filename_list; + char *images_location; + gboolean sort_images; + int window_width; + int window_height; + PangoColor *background_color; + gboolean no_stretch_hint; + + guint timeout_id; + + GTimer *timer; + gboolean fade_disabled; +}; + +enum { + PROP_0, + PROP_IMAGES_LOCATION, + PROP_SORT_IMAGES, + PROP_SOLID_BACKGROUND, + PROP_NO_STRETCH_HINT +}; + +#define GSTE_SLIDESHOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSTE_TYPE_SLIDESHOW, GSTESlideshowPrivate)) + +static GObjectClass *parent_class = NULL; + +G_DEFINE_TYPE (GSTESlideshow, gste_slideshow, GS_TYPE_THEME_ENGINE) + +#define N_FADE_TICKS 10 +#define MINIMUM_FPS 3.0 +#define DEFAULT_IMAGES_LOCATION DATADIR "/pixmaps/backgrounds" +#define IMAGE_LOAD_TIMEOUT 10000 + +typedef struct _Op +{ + char *location; + GSTESlideshow *slideshow; +} Op; + +typedef struct _OpResult +{ + GdkPixbuf *pixbuf; +} OpResult; + +static gboolean +push_load_image_func (GSTESlideshow *show) +{ + Op *op; + + gs_theme_engine_profile_msg ("Starting a new image load"); + + op = g_new (Op, 1); + + op->location = g_strdup (show->priv->images_location); + op->slideshow = g_object_ref (show); + + g_async_queue_push (show->priv->op_q, op); + + show->priv->update_image_id = 0; + + return FALSE; +} + +static void +start_new_load (GSTESlideshow *show, + guint timeout) +{ + gs_theme_engine_profile_msg ("Scheduling a new image load"); + + /* queue a new load */ + if (show->priv->update_image_id <= 0) { + show->priv->update_image_id = g_timeout_add_full (G_PRIORITY_LOW, timeout, + (GSourceFunc)push_load_image_func, + show, NULL); + } +} + +static void +start_fade (GSTESlideshow *show, + GdkPixbuf *pixbuf) +{ + int pw; + int ph; + int x; + int y; + cairo_t *cr; + int window_width; + int window_height; + + gs_theme_engine_profile_start ("start"); + + window_width = show->priv->window_width; + window_height = show->priv->window_height; + + if (show->priv->pat2 != NULL) { + cairo_pattern_destroy (show->priv->pat2); + } + + pw = gdk_pixbuf_get_width (pixbuf); + ph = gdk_pixbuf_get_height (pixbuf); + x = (window_width - pw) / 2; + y = (window_height - ph) / 2; + + if (gdk_pixbuf_get_has_alpha (pixbuf) && show->priv->background_color) { + GdkPixbuf *colored; + guint32 color; + GdkPixmap *pixmap; + + color = (show->priv->background_color->red << 16) + + (show->priv->background_color->green / 256 << 8) + + show->priv->background_color->blue / 256; + colored = gdk_pixbuf_composite_color_simple (pixbuf, + pw, ph, + GDK_INTERP_BILINEAR, + 255, + 256, + color, + color); + pixmap = gdk_pixmap_new (NULL, ph, pw, gdk_visual_get_system ()->depth); + + gdk_draw_pixbuf (pixmap, NULL, colored, 0, 0, 0, 0, -1, -1, GDK_RGB_DITHER_MAX, 0, 0); + gdk_pixbuf_get_from_drawable (pixbuf, pixmap, NULL, 0, 0, 0, 0, -1, -1); + + g_object_unref (pixmap); + + g_object_unref(colored); + } + + cr = cairo_create (show->priv->surf); + + /* XXX Handle out of memory? */ + gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y); + show->priv->pat2 = cairo_pattern_reference (cairo_get_source (cr)); + show->priv->pat2top = y; + show->priv->pat2bottom = y + ph; + show->priv->pat2left = x; + show->priv->pat2right = x + pw; + + cairo_destroy (cr); + + show->priv->fade_ticks = 0; + g_timer_start (show->priv->timer); + + gs_theme_engine_profile_end ("end"); +} + +static void +finish_fade (GSTESlideshow *show) +{ + gs_theme_engine_profile_start ("start"); + + if (show->priv->pat1 != NULL) { + cairo_pattern_destroy (show->priv->pat1); + } + + show->priv->pat1 = show->priv->pat2; + show->priv->pat2 = NULL; + + start_new_load (show, IMAGE_LOAD_TIMEOUT); + + gs_theme_engine_profile_end ("end"); +} + +static void +update_display (GSTESlideshow *show) +{ + int window_width; + int window_height; + cairo_t *cr; + + gs_theme_engine_profile_start ("start"); + + cr = cairo_create (show->priv->surf); + + gs_theme_engine_get_window_size (GS_THEME_ENGINE (show), + &window_width, + &window_height); + + if (show->priv->pat2 != NULL) { + /* fade out areas not covered by the new image */ + /* top */ + cairo_rectangle (cr, 0, 0, window_width, show->priv->pat2top); + if (show->priv->background_color) { + cairo_set_source_rgba (cr, show->priv->background_color->red / 65535.0, + show->priv->background_color->green / 65535.0, + show->priv->background_color->blue / 65535.0, show->priv->alpha2); + } else { + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, show->priv->alpha2); + } + cairo_fill (cr); + /* left (excluding what's covered by top and bottom) */ + cairo_rectangle (cr, 0, show->priv->pat2top, + show->priv->pat2left, + show->priv->pat2bottom - show->priv->pat2top); + if (show->priv->background_color) { + cairo_set_source_rgba (cr, show->priv->background_color->red / 65535.0, + show->priv->background_color->green / 65535.0, + show->priv->background_color->blue / 65535.0, show->priv->alpha2); + } else { + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, show->priv->alpha2); + } + cairo_fill (cr); + /* bottom */ + cairo_rectangle (cr, 0, show->priv->pat2bottom, window_width, + window_height - show->priv->pat2bottom); + if (show->priv->background_color) { + cairo_set_source_rgba (cr, show->priv->background_color->red / 65535.0, + show->priv->background_color->green / 65535.0, + show->priv->background_color->blue / 65535.0, show->priv->alpha2); + } else { + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, show->priv->alpha2); + } + cairo_fill (cr); + /* right (excluding what's covered by top and bottom) */ + cairo_rectangle (cr, show->priv->pat2right, + show->priv->pat2top, + window_width - show->priv->pat2right, + show->priv->pat2bottom - show->priv->pat2top); + if (show->priv->background_color) { + cairo_set_source_rgba (cr, show->priv->background_color->red / 65535.0, + show->priv->background_color->green / 65535.0, + show->priv->background_color->blue / 65535.0, show->priv->alpha2); + } else { + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, show->priv->alpha2); + } + cairo_fill (cr); + + gs_theme_engine_profile_start ("paint pattern to surface"); + cairo_set_source (cr, show->priv->pat2); + + cairo_paint_with_alpha (cr, show->priv->alpha2); + gs_theme_engine_profile_end ("paint pattern to surface"); + } else { + if (show->priv->pat1 != NULL) { + cairo_set_source (cr, show->priv->pat1); + cairo_paint (cr); + } + } + + cairo_destroy (cr); + + /* paint the image buffer into the window */ + cr = gdk_cairo_create (GTK_WIDGET (show)->window); + + cairo_set_source_surface (cr, show->priv->surf, 0, 0); + + gs_theme_engine_profile_start ("paint surface to window"); + cairo_paint (cr); + gs_theme_engine_profile_end ("paint surface to window"); + + cairo_destroy (cr); + + gs_theme_engine_profile_end ("end"); +} + +static gboolean +draw_iter (GSTESlideshow *show) +{ + double old_opacity; + double new_opacity; + + if (show->priv->pat2 != NULL) { + gdouble fps; + gdouble elapsed; + + if (show->priv->fade_disabled) { + show->priv->alpha2 = 1.0; + update_display (show); + finish_fade (show); + return TRUE; + } + + /* we are in a fade */ + show->priv->fade_ticks++; + + /* + * We have currently drawn pat2 with old_opacity, and we + * want to set alpha2 so that drawing pat2 at alpha2 + * yields it drawn with new_opacity + * + * Solving + * new_opacity = 1 - (1 - alpha2) * (1 - old_opacity) + * yields + * alpha2 = 1 - (1 - new_opacity) / (1 - old_opacity) + * + * XXX This assumes that cairo doesn't correct alpha for + * the color profile. However, any error is guaranteed + * to be cleaned up by the last iteration, where alpha2 + * becomes 1 because new_opacity is 1. + */ + old_opacity = (double) (show->priv->fade_ticks - 1) / + (double) N_FADE_TICKS; + new_opacity = (double) show->priv->fade_ticks / + (double) N_FADE_TICKS; + show->priv->alpha2 = 1.0 - (1.0 - new_opacity) / + (1.0 - old_opacity); + + update_display (show); + + elapsed = g_timer_elapsed (show->priv->timer, NULL); + fps = (gdouble)show->priv->fade_ticks / elapsed; + if (fps < MINIMUM_FPS) { + g_warning ("Getting less than %.2f frames per second, disabling fade", MINIMUM_FPS); + show->priv->fade_ticks = N_FADE_TICKS - 1; + show->priv->fade_disabled = TRUE; + } + + if (show->priv->fade_ticks >= N_FADE_TICKS) { + finish_fade (show); + } + } + + return TRUE; +} + +static void +process_new_pixbuf (GSTESlideshow *show, + GdkPixbuf *pixbuf) +{ + gs_theme_engine_profile_msg ("Processing a new image"); + + if (pixbuf != NULL) { + start_fade (show, pixbuf); + } else { + start_new_load (show, 10); + } +} + +static void +op_result_free (OpResult *result) +{ + if (result == NULL) { + return; + } + + if (result->pixbuf != NULL) { + g_object_unref (result->pixbuf); + } + + g_free (result); +} + +static gboolean +results_pull_func (GSTESlideshow *show) +{ + OpResult *result; + + GDK_THREADS_ENTER (); + + g_async_queue_lock (show->priv->results_q); + + result = g_async_queue_try_pop_unlocked (show->priv->results_q); + g_assert (result); + + while (result != NULL) { + process_new_pixbuf (show, result->pixbuf); + op_result_free (result); + + result = g_async_queue_try_pop_unlocked (show->priv->results_q); + } + + show->priv->results_pull_id = 0; + + g_async_queue_unlock (show->priv->results_q); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static GdkPixbuf * +scale_pixbuf (GdkPixbuf *pixbuf, + int max_width, + int max_height, + gboolean no_stretch_hint) +{ + int pw; + int ph; + float scale_factor_x = 1.0; + float scale_factor_y = 1.0; + float scale_factor = 1.0; + + pw = gdk_pixbuf_get_width (pixbuf); + ph = gdk_pixbuf_get_height (pixbuf); + + /* If the image is less than 256 wide or high then it + is probably a thumbnail and we should ignore it */ + if (pw < 256 || ph < 256) { + return NULL; + } + + /* Determine which dimension requires the smallest scale. */ + scale_factor_x = (float) max_width / (float) pw; + scale_factor_y = (float) max_height / (float) ph; + + if (scale_factor_x > scale_factor_y) { + scale_factor = scale_factor_y; + } else { + scale_factor = scale_factor_x; + } + + /* always scale down, allow to disable scaling up */ + if (scale_factor < 1.0 || !no_stretch_hint) { + int scale_x; + int scale_y; + + scale_x = (int) (pw * scale_factor); + scale_y = (int) (ph * scale_factor); + return gdk_pixbuf_scale_simple (pixbuf, + scale_x, + scale_y, + GDK_INTERP_BILINEAR); + } else { + return g_object_ref (pixbuf); + } +} + +static void +add_files_to_list (GSList **list, + const char *base) +{ + GDir *d; + const char *d_name; + + d = g_dir_open (base, 0, NULL); + if (d == NULL) { + g_warning ("Could not open directory: %s", base); + return; + } + + while ((d_name = g_dir_read_name (d)) != NULL) { + char *path; + + /* skip hidden files */ + if (d_name[0] == '.') { + continue; + } + + path = g_build_filename (base, d_name, NULL); + if (g_file_test (path, G_FILE_TEST_IS_DIR)) { + add_files_to_list (list, path); + g_free (path); + } else { + *list = g_slist_prepend (*list, path); + } + } + + g_dir_close (d); +} + +static GSList * +build_filename_list_local_dir (const char *base) +{ + GSList *list = NULL; + + add_files_to_list (&list, base); + + return list; +} + +static int +gste_strcmp_compare_func (gconstpointer string_a, gconstpointer string_b) +{ + return strcmp (string_a == NULL ? "" : string_a, + string_b == NULL ? "" : string_b); +} + + +static GdkPixbuf * +get_pixbuf_from_local_dir (GSTESlideshow *show, + const char *location) +{ + GdkPixbuf *pixbuf, *transformed_pixbuf; + char *filename; + int i; + GSList *l; + + /* rebuild the cache */ + if (show->priv->filename_list == NULL) { + show->priv->filename_list = build_filename_list_local_dir (location); + } + + if (show->priv->filename_list == NULL) { + return NULL; + } else { + if (show->priv->sort_images) { + show->priv->filename_list = g_slist_sort (show->priv->filename_list, gste_strcmp_compare_func); + } + } + + /* get a random filename if needed */ + if (! show->priv->sort_images) { + i = g_random_int_range (0, g_slist_length (show->priv->filename_list)); + l = g_slist_nth (show->priv->filename_list, i); + } else { + l = show->priv->filename_list; + } + filename = l->data; + + pixbuf = gdk_pixbuf_new_from_file (filename, NULL); + + if (pixbuf != NULL) { + transformed_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf); + g_object_unref (pixbuf); + } else { + transformed_pixbuf = NULL; + } + + g_free (filename); + show->priv->filename_list = g_slist_delete_link (show->priv->filename_list, l); + + return transformed_pixbuf; +} + +static GdkPixbuf * +get_pixbuf_from_location (GSTESlideshow *show, + const char *location) +{ + GdkPixbuf *pixbuf = NULL; + gboolean is_dir; + + if (location == NULL) { + return NULL; + } + + is_dir = g_file_test (location, G_FILE_TEST_IS_DIR); + + if (is_dir) { + pixbuf = get_pixbuf_from_local_dir (show, location); + } + + return pixbuf; +} + +static GdkPixbuf * +get_pixbuf (GSTESlideshow *show, + const char *location, + int width, + int height) +{ + GdkPixbuf *pixbuf; + GdkPixbuf *scaled = NULL; + + if (location == NULL) { + return NULL; + } + + pixbuf = get_pixbuf_from_location (show, location); + + if (pixbuf != NULL) { + scaled = scale_pixbuf (pixbuf, width, height, show->priv->no_stretch_hint); + g_object_unref (pixbuf); + } + + return scaled; +} + +static void +op_load_image (GSTESlideshow *show, + const char *location) +{ + OpResult *op_result; + int window_width; + int window_height; + + window_width = show->priv->window_width; + window_height = show->priv->window_height; + + op_result = g_new0 (OpResult, 1); + + op_result->pixbuf = get_pixbuf (show, + location, + window_width, + window_height); + + GDK_THREADS_ENTER (); + g_async_queue_lock (show->priv->results_q); + g_async_queue_push_unlocked (show->priv->results_q, op_result); + + if (show->priv->results_pull_id == 0) { + show->priv->results_pull_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE, + (GSourceFunc)results_pull_func, + show, NULL); + } + + g_async_queue_unlock (show->priv->results_q); + GDK_THREADS_LEAVE (); +} + +static gpointer +load_threadfunc (GAsyncQueue *op_q) +{ + Op *op; + + op = g_async_queue_pop (op_q); + while (op) { + op_load_image (op->slideshow, + op->location); + + if (op->slideshow != NULL) { + g_object_unref (op->slideshow); + } + g_free (op->location); + g_free (op); + + op = g_async_queue_pop (op_q); + } + + return NULL; +} + +void +gste_slideshow_set_images_location (GSTESlideshow *show, + const char *location) +{ + g_return_if_fail (GSTE_IS_SLIDESHOW (show)); + + g_free (show->priv->images_location); + show->priv->images_location = g_strdup (location); +} + + +void +gste_slideshow_set_sort_images (GSTESlideshow *show, + gboolean sort_images) +{ + g_return_if_fail (GSTE_IS_SLIDESHOW (show)); + + show->priv->sort_images = sort_images; +} + +void +gste_slideshow_set_no_stretch_hint (GSTESlideshow *show, + gboolean no_stretch_hint) +{ + g_return_if_fail (GSTE_IS_SLIDESHOW (show)); + + show->priv->no_stretch_hint = no_stretch_hint; +} + +void +gste_slideshow_set_background_color (GSTESlideshow *show, + const char *background_color) +{ + g_return_if_fail (GSTE_IS_SLIDESHOW (show)); + + if (show->priv->background_color != NULL) { + g_slice_free (PangoColor, show->priv->background_color); + show->priv->background_color = NULL; + } + + if (background_color != NULL) { + show->priv->background_color = g_slice_new (PangoColor); + + if (pango_color_parse (show->priv->background_color, background_color) == FALSE) { + g_slice_free (PangoColor, show->priv->background_color); + show->priv->background_color = NULL; + } + } +} + +static void +gste_slideshow_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GSTESlideshow *self; + + self = GSTE_SLIDESHOW (object); + + switch (prop_id) { + case PROP_IMAGES_LOCATION: + gste_slideshow_set_images_location (self, g_value_get_string (value)); + break; + case PROP_SORT_IMAGES: + gste_slideshow_set_sort_images (self, g_value_get_boolean (value)); + break; + case PROP_SOLID_BACKGROUND: + gste_slideshow_set_background_color (self, g_value_get_string (value)); + break; + case PROP_NO_STRETCH_HINT: + gste_slideshow_set_no_stretch_hint (self, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gste_slideshow_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GSTESlideshow *self; + + self = GSTE_SLIDESHOW (object); + + switch (prop_id) { + case PROP_IMAGES_LOCATION: + g_value_set_string (value, self->priv->images_location); + break; + case PROP_SORT_IMAGES: + g_value_set_boolean (value, self->priv->sort_images); + break; + case PROP_SOLID_BACKGROUND: + { + char *color = NULL; + color = pango_color_to_string (self->priv->background_color); + g_value_set_string (value, color); + g_free (color); + break; + } + case PROP_NO_STRETCH_HINT: + g_value_set_boolean (value, self->priv->no_stretch_hint); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gste_slideshow_real_show (GtkWidget *widget) +{ + GSTESlideshow *show = GSTE_SLIDESHOW (widget); + int delay; + + if (GTK_WIDGET_CLASS (parent_class)->show) { + GTK_WIDGET_CLASS (parent_class)->show (widget); + } + + start_new_load (show, 10); + + delay = 25; + show->priv->timeout_id = g_timeout_add (delay, (GSourceFunc)draw_iter, show); + + if (show->priv->timer != NULL) { + g_timer_destroy (show->priv->timer); + } + show->priv->timer = g_timer_new (); +} + +static gboolean +gste_slideshow_real_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + GSTESlideshow *show = GSTE_SLIDESHOW (widget); + gboolean handled = FALSE; + + update_display (show); + + if (GTK_WIDGET_CLASS (parent_class)->expose_event) { + handled = GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event); + } + + return handled; +} + +static gboolean +gste_slideshow_real_configure (GtkWidget *widget, + GdkEventConfigure *event) +{ + GSTESlideshow *show = GSTE_SLIDESHOW (widget); + gboolean handled = FALSE; + cairo_t *cr; + + /* resize */ + gs_theme_engine_get_window_size (GS_THEME_ENGINE (show), + &show->priv->window_width, + &show->priv->window_height); + + gs_theme_engine_profile_msg ("Resize to x:%d y:%d", + show->priv->window_width, + show->priv->window_height); + + if (show->priv->surf != NULL) { + cairo_surface_destroy (show->priv->surf); + } + + cr = gdk_cairo_create (widget->window); + show->priv->surf = cairo_surface_create_similar (cairo_get_target (cr), + CAIRO_CONTENT_COLOR, + show->priv->window_width, + show->priv->window_height); + cairo_destroy (cr); + + /* schedule a redraw */ + gtk_widget_queue_draw (widget); + + if (GTK_WIDGET_CLASS (parent_class)->configure_event) { + handled = GTK_WIDGET_CLASS (parent_class)->configure_event (widget, event); + } + + return handled; +} + +static void +gste_slideshow_class_init (GSTESlideshowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gste_slideshow_finalize; + object_class->get_property = gste_slideshow_get_property; + object_class->set_property = gste_slideshow_set_property; + + widget_class->show = gste_slideshow_real_show; + widget_class->expose_event = gste_slideshow_real_expose; + widget_class->configure_event = gste_slideshow_real_configure; + + g_type_class_add_private (klass, sizeof (GSTESlideshowPrivate)); + + g_object_class_install_property (object_class, + PROP_IMAGES_LOCATION, + g_param_spec_string ("images-location", + NULL, + NULL, + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_SORT_IMAGES, + g_param_spec_boolean ("sort-images", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_SOLID_BACKGROUND, + g_param_spec_string ("background-color", + NULL, + NULL, + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_NO_STRETCH_HINT, + g_param_spec_boolean ("no-stretch", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); +} + +static void +set_colormap (GtkWidget *widget) +{ + GdkScreen *screen; + GdkColormap *colormap; + + screen = gtk_widget_get_screen (widget); + colormap = gdk_screen_get_rgba_colormap (screen); + if (colormap == NULL) { + colormap = gdk_screen_get_rgb_colormap (screen); + } + + gtk_widget_set_colormap (widget, colormap); +} + +static void +gste_slideshow_init (GSTESlideshow *show) +{ + GError *error; + + show->priv = GSTE_SLIDESHOW_GET_PRIVATE (show); + + show->priv->images_location = g_strdup (DEFAULT_IMAGES_LOCATION); + + show->priv->op_q = g_async_queue_new (); + show->priv->results_q = g_async_queue_new (); + + error = NULL; + show->priv->load_thread = g_thread_create ((GThreadFunc)load_threadfunc, show->priv->op_q, FALSE, &error); + if (show->priv->load_thread == NULL) { + g_error ("Could not create a thread to load images: %s", + error->message); + g_error_free (error); + exit (-1); + } + + set_colormap (GTK_WIDGET (show)); +} + +static void +gste_slideshow_finalize (GObject *object) +{ + GSTESlideshow *show; + gpointer result; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSTE_IS_SLIDESHOW (object)); + + show = GSTE_SLIDESHOW (object); + + g_return_if_fail (show->priv != NULL); + + if (show->priv->surf) { + cairo_surface_destroy (show->priv->surf); + } + + if (show->priv->timeout_id > 0) { + g_source_remove (show->priv->timeout_id); + show->priv->timeout_id = 0; + } + + if (show->priv->results_pull_id > 0) { + g_source_remove (show->priv->results_pull_id); + } + + if (show->priv->results_q != NULL) { + result = g_async_queue_try_pop (show->priv->results_q); + + while (result) { + result = g_async_queue_try_pop (show->priv->results_q); + } + g_async_queue_unref (show->priv->results_q); + } + + g_free (show->priv->images_location); + show->priv->images_location = NULL; + + if (show->priv->background_color) { + g_slice_free (PangoColor, show->priv->background_color); + show->priv->background_color = NULL; + } + + if (show->priv->timer != NULL) { + g_timer_destroy (show->priv->timer); + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} diff --git a/savers/gste-slideshow.h b/savers/gste-slideshow.h new file mode 100644 index 0000000..b76794f --- /dev/null +++ b/savers/gste-slideshow.h @@ -0,0 +1,69 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2005 William Jon McCann <[email protected]> + * + * 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. + * + * Authors: William Jon McCann <[email protected]> + * + */ + +#ifndef __GSTE_SLIDESHOW_H +#define __GSTE_SLIDESHOW_H + +#include <glib.h> +#include <gdk/gdk.h> +#include "gs-theme-engine.h" + +G_BEGIN_DECLS + +#define GSTE_TYPE_SLIDESHOW (gste_slideshow_get_type ()) +#define GSTE_SLIDESHOW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSTE_TYPE_SLIDESHOW, GSTESlideshow)) +#define GSTE_SLIDESHOW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSTE_TYPE_SLIDESHOW, GSTESlideshowClass)) +#define GSTE_IS_SLIDESHOW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSTE_TYPE_SLIDESHOW)) +#define GSTE_IS_SLIDESHOW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSTE_TYPE_SLIDESHOW)) +#define GSTE_SLIDESHOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSTE_TYPE_SLIDESHOW, GSTESlideshowClass)) + +typedef struct GSTESlideshowPrivate GSTESlideshowPrivate; + +typedef struct +{ + GSThemeEngine parent; + GSTESlideshowPrivate *priv; +} GSTESlideshow; + +typedef struct +{ + GSThemeEngineClass parent_class; +} GSTESlideshowClass; + +GType gste_slideshow_get_type (void); +GSThemeEngine *gste_slideshow_new (void); + +void gste_slideshow_set_images_location (GSTESlideshow *show, + const char *location); + +void gste_slideshow_set_sort_images (GSTESlideshow *show, + gboolean sort_image); + +void gste_slideshow_set_background_color (GSTESlideshow *show, + const char *background_color); + +void gste_slideshow_set_no_stretch_hint (GSTESlideshow *show, + gboolean no_stretch_hint); + +G_END_DECLS + +#endif /* __GSTE_SLIDESHOW_H */ diff --git a/savers/gste-slideshow.h.orig b/savers/gste-slideshow.h.orig new file mode 100644 index 0000000..5d7ec7c --- /dev/null +++ b/savers/gste-slideshow.h.orig @@ -0,0 +1,69 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2005 William Jon McCann <[email protected]> + * + * 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. + * + * Authors: William Jon McCann <[email protected]> + * + */ + +#ifndef __GSTE_SLIDESHOW_H +#define __GSTE_SLIDESHOW_H + +#include <glib.h> +#include <gdk/gdk.h> +#include "gs-theme-engine.h" + +G_BEGIN_DECLS + +#define GSTE_TYPE_SLIDESHOW (gste_slideshow_get_type ()) +#define GSTE_SLIDESHOW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSTE_TYPE_SLIDESHOW, GSTESlideshow)) +#define GSTE_SLIDESHOW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSTE_TYPE_SLIDESHOW, GSTESlideshowClass)) +#define GSTE_IS_SLIDESHOW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSTE_TYPE_SLIDESHOW)) +#define GSTE_IS_SLIDESHOW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSTE_TYPE_SLIDESHOW)) +#define GSTE_SLIDESHOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSTE_TYPE_SLIDESHOW, GSTESlideshowClass)) + +typedef struct GSTESlideshowPrivate GSTESlideshowPrivate; + +typedef struct +{ + GSThemeEngine parent; + GSTESlideshowPrivate *priv; +} GSTESlideshow; + +typedef struct +{ + GSThemeEngineClass parent_class; +} GSTESlideshowClass; + +GType gste_slideshow_get_type (void); +GSThemeEngine *gste_slideshow_new (void); + +void gste_slideshow_set_images_location (GSTESlideshow *show, + const char *location); + +void gste_slideshow_set_sort_images (GSTESlideshow *show, + gboolean sort_image); + +void gste_slideshow_set_background_color (GSTESlideshow *show, + const char *background_color); + +void gste_slideshow_set_no_stretch_hint (GSTESlideshow *show, + gboolean no_stretch_hint); + +G_END_DECLS + +#endif /* __GSTE_SLIDESHOW_H */ diff --git a/savers/personal-slideshow.desktop.in.in b/savers/personal-slideshow.desktop.in.in new file mode 100644 index 0000000..80d38bd --- /dev/null +++ b/savers/personal-slideshow.desktop.in.in @@ -0,0 +1,10 @@ +[Desktop Entry] +_Name=Pictures folder +_Comment=Display a slideshow from your Pictures folder +Exec=@SAVERDIR@/slideshow +TryExec=@SAVERDIR@/slideshow +StartupNotify=false +Terminal=false +Type=Application +Categories=MATE;Screensaver; +OnlyShowIn=MATE; diff --git a/savers/popsquares.c b/savers/popsquares.c new file mode 100644 index 0000000..22533b6 --- /dev/null +++ b/savers/popsquares.c @@ -0,0 +1,73 @@ +/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*- + * + * Copyright (C) 2005 William Jon McCann <[email protected]> + * + * 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. + * + */ + +#include "config.h" + +#include <string.h> +#include <gdk/gdk.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> + +#include <glib/gi18n.h> + +#include "gs-theme-window.h" +#include "gs-theme-engine.h" +#include "gste-popsquares.h" + +int +main (int argc, char **argv) +{ + GSThemeEngine *engine; + GtkWidget *window; + GError *error; + + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + error = NULL; + + if (!gtk_init_with_args (&argc, &argv, NULL, NULL, NULL, &error)) + { + g_printerr (_("%s. See --help for usage information.\n"), + error->message); + g_error_free (error); + exit (1); + } + + window = gs_theme_window_new (); + g_signal_connect (G_OBJECT (window), "delete-event", + G_CALLBACK (gtk_main_quit), NULL); + + g_set_prgname ("popsquares"); + + engine = g_object_new (GSTE_TYPE_POPSQUARES, NULL); + gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (engine)); + + gtk_widget_show (GTK_WIDGET (engine)); + + gtk_window_set_default_size (GTK_WINDOW (window), 640, 480); + gtk_widget_show (window); + + gtk_main (); + + return 0; +} diff --git a/savers/popsquares.c.orig b/savers/popsquares.c.orig new file mode 100644 index 0000000..04ea19d --- /dev/null +++ b/savers/popsquares.c.orig @@ -0,0 +1,72 @@ +/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*- + * + * Copyright (C) 2005 William Jon McCann <[email protected]> + * + * 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. + * + */ + +#include "config.h" + +#include <string.h> +#include <gdk/gdk.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> + +#include <glib/gi18n.h> + +#include "gs-theme-window.h" +#include "gs-theme-engine.h" +#include "gste-popsquares.h" + +int +main (int argc, char **argv) +{ + GSThemeEngine *engine; + GtkWidget *window; + GError *error; + + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + error = NULL; + + if (!gtk_init_with_args (&argc, &argv, NULL, NULL, NULL, &error)) { + g_printerr (_("%s. See --help for usage information.\n"), + error->message); + g_error_free (error); + exit (1); + } + + window = gs_theme_window_new (); + g_signal_connect (G_OBJECT (window), "delete-event", + G_CALLBACK (gtk_main_quit), NULL); + + g_set_prgname ("popsquares"); + + engine = g_object_new (GSTE_TYPE_POPSQUARES, NULL); + gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (engine)); + + gtk_widget_show (GTK_WIDGET (engine)); + + gtk_window_set_default_size (GTK_WINDOW (window), 640, 480); + gtk_widget_show (window); + + gtk_main (); + + return 0; +} diff --git a/savers/popsquares.desktop.in.in b/savers/popsquares.desktop.in.in new file mode 100644 index 0000000..261d4d1 --- /dev/null +++ b/savers/popsquares.desktop.in.in @@ -0,0 +1,10 @@ +[Desktop Entry] +_Name=Pop art squares +_Comment=A pop-art-ish grid of pulsing colors. +Exec=@SAVERDIR@/popsquares +TryExec=@SAVERDIR@/popsquares +StartupNotify=false +Terminal=false +Type=Application +Categories=MATE;Screensaver; +OnlyShowIn=MATE; diff --git a/savers/slideshow.c b/savers/slideshow.c new file mode 100644 index 0000000..e406d34 --- /dev/null +++ b/savers/slideshow.c @@ -0,0 +1,143 @@ +/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*- + * + * Copyright (C) 2005 William Jon McCann <[email protected]> + * + * 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. + * + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <gtk/gtk.h> + +#include "gs-theme-window.h" +#include "gs-theme-engine.h" +#include "gste-slideshow.h" + +#include "xdg-user-dir-lookup.h" + +int +main (int argc, char **argv) +{ + GSThemeEngine *engine; + GtkWidget *window; + GError *error; + gboolean ret; + char *location = NULL; + char *background_color = NULL; + gboolean sort_images = FALSE; + gboolean no_stretch = FALSE; + GOptionEntry entries [] = + { + { + "location", 0, 0, G_OPTION_ARG_STRING, &location, + N_("Location to get images from"), N_("PATH") + }, + { + "background-color", 0, 0, G_OPTION_ARG_STRING, &background_color, + N_("Color to use for images background"), N_("\"#rrggbb\"") + }, + { + "sort-images", 0, 0, G_OPTION_ARG_NONE, &sort_images, + N_("Do not randomize pictures from location"), NULL + }, + { + "no-stretch", 0, 0, G_OPTION_ARG_NONE, &no_stretch, + N_("Do not try to stretch images on screen"), NULL + }, + { NULL } + }; + + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + error = NULL; + + g_thread_init (NULL); + ret = gtk_init_with_args (&argc, &argv, + NULL, + entries, + NULL, + &error); + if (! ret) + { + g_message ("%s", error->message); + g_error_free (error); + exit (1); + } + + g_chdir (g_get_home_dir ()); + + g_set_prgname ("slideshow"); + + window = gs_theme_window_new (); + g_signal_connect (G_OBJECT (window), "delete-event", + G_CALLBACK (gtk_main_quit), NULL); + + engine = g_object_new (GSTE_TYPE_SLIDESHOW, NULL); + + if (location == NULL) + { + location = xdg_user_dir_lookup ("PICTURES"); + if (location == NULL || + strcmp (location, "/tmp") == 0 || + strcmp (location, g_get_home_dir ()) == 0) + { + free (location); + location = g_build_filename (g_get_home_dir (), "Pictures", NULL); + } + } + + if (location != NULL) + { + g_object_set (engine, "images-location", location, NULL); + } + + if (sort_images) + { + g_object_set (engine, "sort-images", sort_images, NULL); + } + + if (background_color != NULL) + { + g_object_set (engine, "background-color", background_color, NULL); + } + + if (no_stretch) + { + g_object_set (engine, "no-stretch", no_stretch, NULL); + } + + gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (engine)); + + gtk_widget_show (GTK_WIDGET (engine)); + + gtk_window_set_default_size (GTK_WINDOW (window), 640, 480); + gtk_widget_show (window); + + gtk_main (); + + return 0; +} diff --git a/savers/slideshow.c.orig b/savers/slideshow.c.orig new file mode 100644 index 0000000..f18dc5c --- /dev/null +++ b/savers/slideshow.c.orig @@ -0,0 +1,127 @@ +/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*- + * + * Copyright (C) 2005 William Jon McCann <[email protected]> + * + * 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. + * + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <gtk/gtk.h> + +#include "gs-theme-window.h" +#include "gs-theme-engine.h" +#include "gste-slideshow.h" + +#include "xdg-user-dir-lookup.h" + +int +main (int argc, char **argv) +{ + GSThemeEngine *engine; + GtkWidget *window; + GError *error; + gboolean ret; + char *location = NULL; + char *background_color = NULL; + gboolean sort_images = FALSE; + gboolean no_stretch = FALSE; + GOptionEntry entries [] = { + { "location", 0, 0, G_OPTION_ARG_STRING, &location, + N_("Location to get images from"), N_("PATH") }, + { "background-color", 0, 0, G_OPTION_ARG_STRING, &background_color, + N_("Color to use for images background"), N_("\"#rrggbb\"") }, + { "sort-images", 0, 0, G_OPTION_ARG_NONE, &sort_images, + N_("Do not randomize pictures from location"), NULL }, + { "no-stretch", 0, 0, G_OPTION_ARG_NONE, &no_stretch, + N_("Do not try to stretch images on screen"), NULL }, + { NULL } + }; + + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + error = NULL; + + g_thread_init (NULL); + ret = gtk_init_with_args (&argc, &argv, + NULL, + entries, + NULL, + &error); + if (! ret) { + g_message ("%s", error->message); + g_error_free (error); + exit (1); + } + + g_chdir (g_get_home_dir ()); + + g_set_prgname ("slideshow"); + + window = gs_theme_window_new (); + g_signal_connect (G_OBJECT (window), "delete-event", + G_CALLBACK (gtk_main_quit), NULL); + + engine = g_object_new (GSTE_TYPE_SLIDESHOW, NULL); + + if (location == NULL) { + location = xdg_user_dir_lookup ("PICTURES"); + if (location == NULL || + strcmp (location, "/tmp") == 0 || + strcmp (location, g_get_home_dir ()) == 0) { + free (location); + location = g_build_filename (g_get_home_dir (), "Pictures", NULL); + } + } + + if (location != NULL) { + g_object_set (engine, "images-location", location, NULL); + } + + if (sort_images) { + g_object_set (engine, "sort-images", sort_images, NULL); + } + + if (background_color != NULL) { + g_object_set (engine, "background-color", background_color, NULL); + } + + if (no_stretch) { + g_object_set (engine, "no-stretch", no_stretch, NULL); + } + + gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (engine)); + + gtk_widget_show (GTK_WIDGET (engine)); + + gtk_window_set_default_size (GTK_WINDOW (window), 640, 480); + gtk_widget_show (window); + + gtk_main (); + + return 0; +} diff --git a/savers/xdg-user-dir-lookup.c b/savers/xdg-user-dir-lookup.c new file mode 100644 index 0000000..c9e8fe2 --- /dev/null +++ b/savers/xdg-user-dir-lookup.c @@ -0,0 +1,168 @@ +/* + This file is not licenced under the GPL like the rest of the code. + Its is under the MIT license, to encourage reuse by cut-and-paste. + + Copyright (c) 2007 Red Hat, inc + + 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "xdg-user-dir-lookup.h" + +char * +xdg_user_dir_lookup (const char *type) +{ + FILE *file; + char *home_dir, *config_home, *config_file; + char buffer[512]; + char *user_dir; + char *p, *d; + int len; + int relative; + + home_dir = getenv ("HOME"); + + if (home_dir == NULL) + return strdup ("/tmp"); + + config_home = getenv ("XDG_CONFIG_HOME"); + if (config_home == NULL || config_home[0] == 0) + { + config_file = malloc (strlen (home_dir) + strlen ("/.config/user-dirs.dirs") + 1); + strcpy (config_file, home_dir); + strcat (config_file, "/.config/user-dirs.dirs"); + } + else + { + config_file = malloc (strlen (config_home) + strlen ("/user-dirs.dirs") + 1); + strcpy (config_file, config_home); + strcat (config_file, "/user-dirs.dirs"); + } + + file = fopen (config_file, "r"); + free (config_file); + if (file == NULL) + goto error; + + user_dir = NULL; + while (fgets (buffer, sizeof (buffer), file)) + { + /* Remove newline at end */ + len = strlen (buffer); + if (len > 0 && buffer[len-1] == '\n') + buffer[len-1] = 0; + + p = buffer; + while (*p == ' ' || *p == '\t') + p++; + + if (strncmp (p, "XDG_", 4) != 0) + continue; + p += 4; + if (strncmp (p, type, strlen (type)) != 0) + continue; + p += strlen (type); + if (strncmp (p, "_DIR", 4) != 0) + continue; + p += 4; + + while (*p == ' ' || *p == '\t') + p++; + + if (*p != '=') + continue; + p++; + + while (*p == ' ' || *p == '\t') + p++; + + if (*p != '"') + continue; + p++; + + relative = 0; + if (strncmp (p, "$HOME/", 6) == 0) + { + p += 6; + relative = 1; + } + else if (*p != '/') + continue; + + if (relative) + { + user_dir = malloc (strlen (home_dir) + 1 + strlen (p) + 1); + strcpy (user_dir, home_dir); + strcat (user_dir, "/"); + } + else + { + user_dir = malloc (strlen (p) + 1); + *user_dir = 0; + } + + d = user_dir + strlen (user_dir); + while (*p && *p != '"') + { + if ((*p == '\\') && (*(p+1) != 0)) + p++; + *d++ = *p++; + } + *d = 0; + } + fclose (file); + + if (user_dir) + return user_dir; + +error: + /* Special case desktop for historical compatibility */ + if (strcmp (type, "DESKTOP") == 0) + { + user_dir = malloc (strlen (home_dir) + strlen ("/Desktop") + 1); + strcpy (user_dir, home_dir); + strcat (user_dir, "/Desktop"); + return user_dir; + } + else + return strdup (home_dir); +} + +#ifdef STANDALONE + +int +main (int argc, char *argv[]) +{ + if (argc != 2) + { + fprintf (stderr, "Usage %s <dir-type>\n", argv[0]); + exit (1); + } + + printf ("%s\n", xdg_user_dir_lookup (argv[1])); + return 0; +} + +#endif diff --git a/savers/xdg-user-dir-lookup.c.orig b/savers/xdg-user-dir-lookup.c.orig new file mode 100644 index 0000000..a6b2fb1 --- /dev/null +++ b/savers/xdg-user-dir-lookup.c.orig @@ -0,0 +1,168 @@ +/* + This file is not licenced under the GPL like the rest of the code. + Its is under the MIT license, to encourage reuse by cut-and-paste. + + Copyright (c) 2007 Red Hat, inc + + 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "xdg-user-dir-lookup.h" + +char * +xdg_user_dir_lookup (const char *type) +{ + FILE *file; + char *home_dir, *config_home, *config_file; + char buffer[512]; + char *user_dir; + char *p, *d; + int len; + int relative; + + home_dir = getenv ("HOME"); + + if (home_dir == NULL) + return strdup ("/tmp"); + + config_home = getenv ("XDG_CONFIG_HOME"); + if (config_home == NULL || config_home[0] == 0) + { + config_file = malloc (strlen (home_dir) + strlen ("/.config/user-dirs.dirs") + 1); + strcpy (config_file, home_dir); + strcat (config_file, "/.config/user-dirs.dirs"); + } + else + { + config_file = malloc (strlen (config_home) + strlen ("/user-dirs.dirs") + 1); + strcpy (config_file, config_home); + strcat (config_file, "/user-dirs.dirs"); + } + + file = fopen (config_file, "r"); + free (config_file); + if (file == NULL) + goto error; + + user_dir = NULL; + while (fgets (buffer, sizeof (buffer), file)) + { + /* Remove newline at end */ + len = strlen (buffer); + if (len > 0 && buffer[len-1] == '\n') + buffer[len-1] = 0; + + p = buffer; + while (*p == ' ' || *p == '\t') + p++; + + if (strncmp (p, "XDG_", 4) != 0) + continue; + p += 4; + if (strncmp (p, type, strlen (type)) != 0) + continue; + p += strlen (type); + if (strncmp (p, "_DIR", 4) != 0) + continue; + p += 4; + + while (*p == ' ' || *p == '\t') + p++; + + if (*p != '=') + continue; + p++; + + while (*p == ' ' || *p == '\t') + p++; + + if (*p != '"') + continue; + p++; + + relative = 0; + if (strncmp (p, "$HOME/", 6) == 0) + { + p += 6; + relative = 1; + } + else if (*p != '/') + continue; + + if (relative) + { + user_dir = malloc (strlen (home_dir) + 1 + strlen (p) + 1); + strcpy (user_dir, home_dir); + strcat (user_dir, "/"); + } + else + { + user_dir = malloc (strlen (p) + 1); + *user_dir = 0; + } + + d = user_dir + strlen (user_dir); + while (*p && *p != '"') + { + if ((*p == '\\') && (*(p+1) != 0)) + p++; + *d++ = *p++; + } + *d = 0; + } + fclose (file); + + if (user_dir) + return user_dir; + + error: + /* Special case desktop for historical compatibility */ + if (strcmp (type, "DESKTOP") == 0) + { + user_dir = malloc (strlen (home_dir) + strlen ("/Desktop") + 1); + strcpy (user_dir, home_dir); + strcat (user_dir, "/Desktop"); + return user_dir; + } + else + return strdup (home_dir); +} + +#ifdef STANDALONE + +int +main (int argc, char *argv[]) +{ + if (argc != 2) + { + fprintf (stderr, "Usage %s <dir-type>\n", argv[0]); + exit (1); + } + + printf ("%s\n", xdg_user_dir_lookup (argv[1])); + return 0; +} + +#endif diff --git a/savers/xdg-user-dir-lookup.h b/savers/xdg-user-dir-lookup.h new file mode 100644 index 0000000..2803be3 --- /dev/null +++ b/savers/xdg-user-dir-lookup.h @@ -0,0 +1,28 @@ +/* + This file is not licenced under the GPL like the rest of the code. + Its is under the MIT license, to encourage reuse by cut-and-paste. + + Copyright (c) 2007 Red Hat, inc + + 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. +*/ + +char * xdg_user_dir_lookup (const char *type); |