/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * 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, see . */ #include #include "effects.h" #include "display-private.h" #include "ui.h" #include "window-private.h" #include "prefs.h" #include #define META_MINIMIZE_ANIMATION_LENGTH 0.25 #include typedef struct { MetaScreen *screen; double millisecs_duration; gint64 start_time; /** For wireframe window */ Window wireframe_xwindow; MetaRectangle start_rect; MetaRectangle end_rect; } BoxAnimationContext; static void draw_box_animation (MetaScreen *screen, MetaRectangle *initial_rect, MetaRectangle *destination_rect, double seconds_duration); void meta_effect_run_minimize (MetaWindow *window, MetaRectangle *window_rect, MetaRectangle *icon_rect) { g_return_if_fail (window != NULL); g_return_if_fail (icon_rect != NULL); if (meta_prefs_get_gnome_animations ()) { draw_box_animation (window->screen, window_rect, icon_rect, META_MINIMIZE_ANIMATION_LENGTH); } } /* old ugly minimization effect */ static void update_wireframe_window (MetaDisplay *display, Window xwindow, const MetaRectangle *rect) { XMoveResizeWindow (display->xdisplay, xwindow, rect->x, rect->y, rect->width, rect->height); #define OUTLINE_WIDTH 3 if (rect->width > OUTLINE_WIDTH * 2 && rect->height > OUTLINE_WIDTH * 2) { XRectangle xrect; Region inner_xregion; Region outer_xregion; inner_xregion = XCreateRegion (); outer_xregion = XCreateRegion (); xrect.x = 0; xrect.y = 0; xrect.width = rect->width; xrect.height = rect->height; XUnionRectWithRegion (&xrect, outer_xregion, outer_xregion); xrect.x += OUTLINE_WIDTH; xrect.y += OUTLINE_WIDTH; xrect.width -= OUTLINE_WIDTH * 2; xrect.height -= OUTLINE_WIDTH * 2; XUnionRectWithRegion (&xrect, inner_xregion, inner_xregion); XSubtractRegion (outer_xregion, inner_xregion, outer_xregion); XShapeCombineRegion (display->xdisplay, xwindow, ShapeBounding, 0, 0, outer_xregion, ShapeSet); XDestroyRegion (outer_xregion); XDestroyRegion (inner_xregion); } else { /* Unset the shape */ XShapeCombineMask (display->xdisplay, xwindow, ShapeBounding, 0, 0, None, ShapeSet); } } static gboolean effects_draw_box_animation_timeout (BoxAnimationContext *context) { double elapsed; gint64 current_time; MetaRectangle draw_rect; double fraction; current_time = g_get_real_time (); /* We use milliseconds for all times */ elapsed = (current_time - context->start_time) / 1000.0; if (elapsed < 0) { /* Probably the system clock was set backwards? */ g_warning ("System clock seemed to go backwards?"); elapsed = G_MAXDOUBLE; /* definitely done. */ } if (elapsed > context->millisecs_duration) { /* All done */ XDestroyWindow (context->screen->display->xdisplay, context->wireframe_xwindow); g_free (context); return FALSE; } 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; /* don't confuse X or gdk-pixbuf with bogus rectangles */ if (draw_rect.width < 1) draw_rect.width = 1; if (draw_rect.height < 1) draw_rect.height = 1; update_wireframe_window (context->screen->display, context->wireframe_xwindow, &draw_rect); /* kick changes onto the server */ XFlush (context->screen->display->xdisplay); return TRUE; } static void draw_box_animation (MetaScreen *screen, MetaRectangle *initial_rect, MetaRectangle *destination_rect, double seconds_duration) { BoxAnimationContext *context; XSetWindowAttributes attrs; g_return_if_fail (seconds_duration > 0.0); if (g_getenv ("METACITY_DEBUG_EFFECTS")) seconds_duration *= 10; /* slow things down */ /* Create the animation context */ context = g_new0 (BoxAnimationContext, 1); context->screen = screen; context->millisecs_duration = seconds_duration * 1000.0; context->start_rect = *initial_rect; context->end_rect = *destination_rect; attrs.override_redirect = True; attrs.background_pixel = BlackPixel (screen->display->xdisplay, screen->number); context->wireframe_xwindow = XCreateWindow (screen->display->xdisplay, screen->xroot, initial_rect->x, initial_rect->y, initial_rect->width, initial_rect->height, 0, CopyFromParent, CopyFromParent, (Visual *)CopyFromParent, CWOverrideRedirect | CWBackPixel, &attrs); update_wireframe_window (screen->display, context->wireframe_xwindow, initial_rect); XMapWindow (screen->display->xdisplay, context->wireframe_xwindow); /* Do this only after we get the pixbuf from the server, * so that the animation doesn't get truncated. */ context->start_time = g_get_real_time (); /* Add the timeout - a short one, could even use an idle, * but this is maybe more CPU-friendly. */ g_timeout_add (15, (GSourceFunc)effects_draw_box_animation_timeout, context); /* kick changes onto the server */ XFlush (context->screen->display->xdisplay); }