2001-08-05 12:04:52 -04:00
|
|
|
/* Metacity animation effects */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (C) 2001 Anders Carlsson, Havoc Pennington
|
|
|
|
*
|
|
|
|
* 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 "effects.h"
|
|
|
|
#include "display.h"
|
2001-08-06 03:58:49 -04:00
|
|
|
#include "ui.h"
|
2001-08-05 12:04:52 -04:00
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
MetaScreen *screen;
|
|
|
|
|
2001-08-06 01:12:22 -04:00
|
|
|
double millisecs_duration;
|
|
|
|
GTimeVal start_time;
|
|
|
|
|
|
|
|
gboolean first_time;
|
|
|
|
|
|
|
|
MetaRectangle start_rect;
|
|
|
|
MetaRectangle end_rect;
|
|
|
|
|
|
|
|
/* rect to erase */
|
|
|
|
MetaRectangle last_rect;
|
2001-08-06 03:58:49 -04:00
|
|
|
|
|
|
|
/* used instead of the global flag, since
|
|
|
|
* we don't want to change midstream.
|
|
|
|
*/
|
|
|
|
gboolean use_opaque;
|
|
|
|
|
|
|
|
/* For wireframe */
|
|
|
|
GC gc;
|
|
|
|
|
|
|
|
/* For opaque */
|
|
|
|
MetaImageWindow *image_window;
|
|
|
|
GdkPixbuf *orig_pixbuf;
|
2001-08-05 12:04:52 -04:00
|
|
|
|
|
|
|
} BoxAnimationContext;
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
effects_draw_box_animation_timeout (BoxAnimationContext *context)
|
|
|
|
{
|
2001-08-06 01:12:22 -04:00
|
|
|
double elapsed;
|
|
|
|
GTimeVal current_time;
|
|
|
|
MetaRectangle draw_rect;
|
|
|
|
double fraction;
|
|
|
|
|
|
|
|
if (!context->first_time)
|
2001-08-05 12:04:52 -04:00
|
|
|
{
|
2001-08-06 03:58:49 -04:00
|
|
|
if (!context->use_opaque)
|
|
|
|
{
|
|
|
|
/* Restore the previously drawn background */
|
|
|
|
XDrawRectangle (context->screen->display->xdisplay,
|
|
|
|
context->screen->xroot,
|
|
|
|
context->gc,
|
|
|
|
context->last_rect.x, context->last_rect.y,
|
|
|
|
context->last_rect.width, context->last_rect.height);
|
|
|
|
}
|
2001-08-05 12:04:52 -04:00
|
|
|
}
|
|
|
|
|
2001-08-06 01:12:22 -04:00
|
|
|
context->first_time = FALSE;
|
|
|
|
|
|
|
|
g_get_current_time (¤t_time);
|
|
|
|
|
|
|
|
/* We use milliseconds for all times */
|
|
|
|
elapsed =
|
|
|
|
((((double)current_time.tv_sec - context->start_time.tv_sec) * G_USEC_PER_SEC +
|
|
|
|
(current_time.tv_usec - context->start_time.tv_usec))) / 1000.0;
|
|
|
|
|
|
|
|
if (elapsed < 0)
|
2001-08-05 12:04:52 -04:00
|
|
|
{
|
2001-08-06 01:12:22 -04:00
|
|
|
/* Probably the system clock was set backwards? */
|
|
|
|
meta_warning ("System clock seemed to go backwards?\n");
|
|
|
|
elapsed = G_MAXDOUBLE; /* definitely done. */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (elapsed > context->millisecs_duration)
|
|
|
|
{
|
|
|
|
/* All done */
|
2001-08-06 03:58:49 -04:00
|
|
|
if (context->use_opaque)
|
|
|
|
{
|
|
|
|
g_object_unref (G_OBJECT (context->orig_pixbuf));
|
|
|
|
meta_image_window_free (context->image_window);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
meta_display_ungrab (context->screen->display);
|
2001-08-18 21:19:54 -04:00
|
|
|
meta_ui_pop_delay_exposes (context->screen->ui);
|
2001-08-06 03:58:49 -04:00
|
|
|
XFreeGC (context->screen->display->xdisplay,
|
|
|
|
context->gc);
|
|
|
|
}
|
|
|
|
|
2001-08-05 12:04:52 -04:00
|
|
|
g_free (context);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2001-08-06 01:12:22 -04:00
|
|
|
g_assert (context->millisecs_duration > 0.0);
|
|
|
|
fraction = elapsed / context->millisecs_duration;
|
|
|
|
|
|
|
|
draw_rect = context->start_rect;
|
|
|
|
|
|
|
|
/* Now add a delta proportional to elapsed time. */
|
|
|
|
draw_rect.x += (context->end_rect.x - context->start_rect.x) * fraction;
|
|
|
|
draw_rect.y += (context->end_rect.y - context->start_rect.y) * fraction;
|
|
|
|
draw_rect.width += (context->end_rect.width - context->start_rect.width) * fraction;
|
|
|
|
draw_rect.height += (context->end_rect.height - context->start_rect.height) * fraction;
|
|
|
|
|
2001-08-06 03:58:49 -04:00
|
|
|
/* don't confuse X or gdk-pixbuf with bogus rectangles */
|
2001-08-06 01:12:22 -04:00
|
|
|
if (draw_rect.width < 1)
|
|
|
|
draw_rect.width = 1;
|
|
|
|
if (draw_rect.height < 1)
|
|
|
|
draw_rect.height = 1;
|
|
|
|
|
|
|
|
context->last_rect = draw_rect;
|
2001-08-05 12:04:52 -04:00
|
|
|
|
2001-08-06 03:58:49 -04:00
|
|
|
if (context->use_opaque)
|
|
|
|
{
|
|
|
|
GdkPixbuf *scaled;
|
|
|
|
|
|
|
|
scaled = gdk_pixbuf_scale_simple (context->orig_pixbuf,
|
|
|
|
draw_rect.width,
|
|
|
|
draw_rect.height,
|
|
|
|
GDK_INTERP_BILINEAR);
|
|
|
|
meta_image_window_set_image (context->image_window,
|
|
|
|
scaled);
|
|
|
|
meta_image_window_set_position (context->image_window,
|
|
|
|
draw_rect.x, draw_rect.y);
|
|
|
|
g_object_unref (G_OBJECT (scaled));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Draw the rectangle */
|
|
|
|
XDrawRectangle (context->screen->display->xdisplay,
|
|
|
|
context->screen->xroot,
|
|
|
|
context->gc,
|
|
|
|
draw_rect.x, draw_rect.y,
|
|
|
|
draw_rect.width, draw_rect.height);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* kick changes onto the server */
|
|
|
|
XFlush (context->screen->display->xdisplay);
|
|
|
|
|
2001-08-05 12:04:52 -04:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2001-08-06 03:58:49 -04:00
|
|
|
|
|
|
|
/* I really don't want this to be a configuration option,
|
|
|
|
* but I think the wireframe is sucky from a UI standpoint
|
|
|
|
* (more confusing than opaque), but the opaque is maybe
|
|
|
|
* too slow on some systems; so perhaps we could autodetect
|
|
|
|
* system beefiness or someting, or have some global
|
|
|
|
* "my system is slow" config option.
|
|
|
|
*/
|
2001-08-18 22:00:26 -04:00
|
|
|
static gboolean use_opaque_animations = FALSE;
|
2001-08-06 03:58:49 -04:00
|
|
|
|
2001-08-05 12:04:52 -04:00
|
|
|
void
|
|
|
|
meta_effects_draw_box_animation (MetaScreen *screen,
|
|
|
|
MetaRectangle *initial_rect,
|
|
|
|
MetaRectangle *destination_rect,
|
2001-08-06 01:12:22 -04:00
|
|
|
double seconds_duration)
|
2001-08-05 12:04:52 -04:00
|
|
|
{
|
|
|
|
BoxAnimationContext *context;
|
|
|
|
|
2001-08-06 01:12:22 -04:00
|
|
|
g_return_if_fail (seconds_duration > 0.0);
|
|
|
|
|
|
|
|
if (g_getenv ("METACITY_DEBUG_EFFECTS"))
|
|
|
|
seconds_duration *= 10; /* slow things down */
|
|
|
|
|
2001-08-05 12:04:52 -04:00
|
|
|
/* Create the animation context */
|
2001-08-06 03:58:49 -04:00
|
|
|
context = g_new0 (BoxAnimationContext, 1);
|
|
|
|
|
2001-08-05 12:04:52 -04:00
|
|
|
context->screen = screen;
|
2001-08-06 01:12:22 -04:00
|
|
|
|
|
|
|
context->millisecs_duration = seconds_duration * 1000.0;
|
|
|
|
context->first_time = TRUE;
|
|
|
|
context->start_rect = *initial_rect;
|
|
|
|
context->end_rect = *destination_rect;
|
2001-08-06 03:58:49 -04:00
|
|
|
|
|
|
|
context->use_opaque = use_opaque_animations;
|
|
|
|
|
|
|
|
if (context->use_opaque)
|
|
|
|
{
|
|
|
|
GdkPixbuf *pix;
|
|
|
|
|
|
|
|
pix = meta_gdk_pixbuf_get_from_window (NULL,
|
|
|
|
screen->xroot,
|
|
|
|
initial_rect->x,
|
|
|
|
initial_rect->y,
|
|
|
|
0, 0,
|
|
|
|
initial_rect->width,
|
|
|
|
initial_rect->height);
|
|
|
|
|
|
|
|
if (pix == NULL)
|
|
|
|
{
|
|
|
|
/* Fall back to wireframe */
|
|
|
|
context->use_opaque = FALSE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
context->image_window = meta_image_window_new ();
|
|
|
|
context->orig_pixbuf = pix;
|
|
|
|
meta_image_window_set_position (context->image_window,
|
|
|
|
initial_rect->x,
|
|
|
|
initial_rect->y);
|
|
|
|
meta_image_window_set_showing (context->image_window, TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Not an else, so that fallback works */
|
|
|
|
if (!context->use_opaque)
|
|
|
|
{
|
|
|
|
XGCValues gc_values;
|
|
|
|
|
|
|
|
gc_values.subwindow_mode = IncludeInferiors;
|
|
|
|
gc_values.function = GXinvert;
|
|
|
|
|
|
|
|
context->gc = XCreateGC (screen->display->xdisplay,
|
|
|
|
screen->xroot,
|
|
|
|
GCSubwindowMode | GCFunction,
|
|
|
|
&gc_values);
|
|
|
|
|
|
|
|
/* Grab the X server to avoid screen dirt */
|
|
|
|
meta_display_grab (context->screen->display);
|
2001-08-18 21:19:54 -04:00
|
|
|
meta_ui_push_delay_exposes (context->screen->ui);
|
2001-08-06 03:58:49 -04:00
|
|
|
}
|
2001-08-06 04:03:48 -04:00
|
|
|
|
|
|
|
/* Do this only after we get the pixbuf from the server,
|
|
|
|
* so that the animation doesn't get truncated.
|
|
|
|
*/
|
|
|
|
g_get_current_time (&context->start_time);
|
2001-08-05 12:04:52 -04:00
|
|
|
|
2001-08-06 01:12:22 -04:00
|
|
|
/* Add the timeout - a short one, could even use an idle,
|
|
|
|
* but this is maybe more CPU-friendly.
|
|
|
|
*/
|
|
|
|
g_timeout_add (15,
|
2001-08-05 12:04:52 -04:00
|
|
|
(GSourceFunc)effects_draw_box_animation_timeout,
|
|
|
|
context);
|
2001-08-19 02:23:59 -04:00
|
|
|
|
|
|
|
/* kick changes onto the server */
|
|
|
|
XFlush (context->screen->display->xdisplay);
|
2001-08-05 12:04:52 -04:00
|
|
|
}
|
2001-08-06 03:58:49 -04:00
|
|
|
|
|
|
|
|
|
|
|
|