effects.c

Go to the documentation of this file.
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 
00039 /* 
00040  * Copyright (C) 2001 Anders Carlsson, Havoc Pennington
00041  * 
00042  * This program is free software; you can redistribute it and/or
00043  * modify it under the terms of the GNU General Public License as
00044  * published by the Free Software Foundation; either version 2 of the
00045  * License, or (at your option) any later version.
00046  *
00047  * This program is distributed in the hope that it will be useful, but
00048  * WITHOUT ANY WARRANTY; without even the implied warranty of
00049  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00050  * General Public License for more details.
00051  * 
00052  * You should have received a copy of the GNU General Public License
00053  * along with this program; if not, write to the Free Software
00054  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00055  * 02111-1307, USA.
00056  */
00057 
00058 #include <config.h>
00059 #include "effects.h"
00060 #include "display-private.h"
00061 #include "ui.h"
00062 #include "window-private.h"
00063 #include "prefs.h"
00064 
00065 #ifdef HAVE_SHAPE
00066 #include <X11/extensions/shape.h>
00067 #endif
00068 
00069 #define META_MINIMIZE_ANIMATION_LENGTH 0.25
00070 #define META_SHADE_ANIMATION_LENGTH 0.2
00071 
00072 #include <string.h>
00073 
00074 typedef struct MetaEffect MetaEffect;
00075 typedef struct MetaEffectPriv MetaEffectPriv;
00076 
00077 typedef struct
00078 {
00079   MetaScreen *screen;
00080 
00081   double millisecs_duration;
00082   GTimeVal start_time;
00083 
00084 #ifdef HAVE_SHAPE
00085 
00086   Window wireframe_xwindow;
00087 #else
00088 
00089   MetaRectangle last_rect;
00090 
00092   gboolean first_time;
00093 
00095   GC gc;
00096 #endif
00097 
00098   MetaRectangle start_rect;
00099   MetaRectangle end_rect;
00100   
00101 } BoxAnimationContext;
00102 
00106 typedef struct
00107 {
00109   MetaRectangle window_rect;
00111   MetaRectangle icon_rect;
00112 } MetaMinimizeEffect, MetaUnminimizeEffect;
00113 
00114 struct MetaEffectPriv
00115 {
00116   MetaEffectFinished finished;
00117   gpointer           finished_data;
00118 };
00119 
00120 struct MetaEffect
00121 {
00123   MetaWindow *window;
00125   MetaEffectType type;
00127   gpointer info;
00128 
00129   union
00130   {
00131     MetaMinimizeEffect      minimize;
00132     /* ... and theoretically anything else */
00133   } u;
00134   
00135   MetaEffectPriv *priv;
00136 };
00137 
00138 static void run_default_effect_handler (MetaEffect *effect);
00139 static void run_handler (MetaEffect *effect);
00140 static void effect_free (MetaEffect *effect);
00141 
00142 static MetaEffect *
00143 create_effect (MetaEffectType      type,
00144                MetaWindow         *window,
00145                MetaEffectFinished  finished,
00146                gpointer            finished_data);
00147 
00148 static void
00149 draw_box_animation (MetaScreen     *screen,
00150                     MetaRectangle  *initial_rect,
00151                     MetaRectangle  *destination_rect,
00152                     double          seconds_duration);
00153 
00158 static MetaEffect*
00159 create_effect (MetaEffectType      type,
00160                MetaWindow         *window,
00161                MetaEffectFinished  finished,
00162                gpointer            finished_data)
00163 {
00164     MetaEffect *effect = g_new (MetaEffect, 1);
00165 
00166     effect->type = type;
00167     effect->window = window;
00168     effect->priv = g_new (MetaEffectPriv, 1);
00169     effect->priv->finished = finished;
00170     effect->priv->finished_data = finished_data;
00171 
00172     return effect;
00173 }
00174 
00181 static void
00182 effect_free (MetaEffect *effect)
00183 {
00184   if (effect->priv->finished)
00185     effect->priv->finished (effect->priv->finished_data);
00186     
00187   g_free (effect->priv);
00188   g_free (effect);
00189 }
00190 
00191 void
00192 meta_effect_run_focus (MetaWindow           *window,
00193                        MetaEffectFinished   finished,
00194                        gpointer             data)
00195 {
00196     MetaEffect *effect;
00197 
00198     g_return_if_fail (window != NULL);
00199 
00200     effect = create_effect (META_EFFECT_FOCUS, window, finished, data);
00201     
00202     run_handler (effect);
00203 }
00204 
00205 void
00206 meta_effect_run_minimize (MetaWindow         *window,
00207                           MetaRectangle      *window_rect,
00208                           MetaRectangle      *icon_rect,
00209                           MetaEffectFinished  finished,
00210                           gpointer            data)
00211 {
00212     MetaEffect *effect;
00213 
00214     g_return_if_fail (window != NULL);
00215     g_return_if_fail (icon_rect != NULL);
00216     
00217     effect = create_effect (META_EFFECT_MINIMIZE, window, finished, data);
00218 
00219     effect->u.minimize.window_rect = *window_rect;
00220     effect->u.minimize.icon_rect = *icon_rect;
00221 
00222     run_handler (effect);
00223 }
00224 
00225 void
00226 meta_effect_run_unminimize (MetaWindow         *window,
00227                             MetaRectangle      *window_rect,
00228                             MetaRectangle      *icon_rect,
00229                             MetaEffectFinished  finished,
00230                             gpointer            data)
00231 {
00232     MetaEffect *effect;
00233 
00234     g_return_if_fail (window != NULL);
00235     g_return_if_fail (icon_rect != NULL);
00236     
00237     effect = create_effect (META_EFFECT_UNMINIMIZE, window, finished, data);
00238 
00239     effect->u.minimize.window_rect = *window_rect;
00240     effect->u.minimize.icon_rect = *icon_rect;
00241 
00242     run_handler (effect);
00243 }
00244 
00245 void
00246 meta_effect_run_close (MetaWindow         *window,
00247                        MetaEffectFinished  finished,
00248                        gpointer            data)
00249 {
00250     MetaEffect *effect;
00251     
00252     g_return_if_fail (window != NULL);
00253 
00254     effect = create_effect (META_EFFECT_CLOSE, window,
00255                             finished, data);
00256 
00257     run_handler (effect);
00258 }
00259 
00260 
00261 /* old ugly minimization effect */
00262 
00263 #ifdef HAVE_SHAPE  
00264 static void
00265 update_wireframe_window (MetaDisplay         *display,
00266                          Window               xwindow,
00267                          const MetaRectangle *rect)
00268 {
00269   XMoveResizeWindow (display->xdisplay,
00270                      xwindow,
00271                      rect->x, rect->y,
00272                      rect->width, rect->height);
00273 
00274 #define OUTLINE_WIDTH 3
00275   
00276   if (rect->width > OUTLINE_WIDTH * 2 &&
00277       rect->height > OUTLINE_WIDTH * 2)
00278     {
00279       XRectangle xrect;
00280       Region inner_xregion;
00281       Region outer_xregion;
00282       
00283       inner_xregion = XCreateRegion ();
00284       outer_xregion = XCreateRegion ();
00285 
00286       xrect.x = 0;
00287       xrect.y = 0;
00288       xrect.width = rect->width;
00289       xrect.height = rect->height;
00290   
00291       XUnionRectWithRegion (&xrect, outer_xregion, outer_xregion);
00292   
00293       xrect.x += OUTLINE_WIDTH;
00294       xrect.y += OUTLINE_WIDTH;
00295       xrect.width -= OUTLINE_WIDTH * 2;
00296       xrect.height -= OUTLINE_WIDTH * 2;  
00297   
00298       XUnionRectWithRegion (&xrect, inner_xregion, inner_xregion);
00299 
00300       XSubtractRegion (outer_xregion, inner_xregion, outer_xregion);
00301 
00302       XShapeCombineRegion (display->xdisplay, xwindow,
00303                            ShapeBounding, 0, 0, outer_xregion, ShapeSet);
00304   
00305       XDestroyRegion (outer_xregion);
00306       XDestroyRegion (inner_xregion);
00307     }
00308   else
00309     {
00310       /* Unset the shape */
00311       XShapeCombineMask (display->xdisplay, xwindow,
00312                          ShapeBounding, 0, 0, None, ShapeSet);
00313     }
00314 }
00315 #endif
00316 
00321 static void
00322 graphics_sync (BoxAnimationContext *context)
00323 {
00324   XImage *image;
00325   
00326   image = XGetImage (context->screen->display->xdisplay,
00327                      context->screen->xroot,
00328                      0, 0, 1, 1,
00329                      AllPlanes, ZPixmap);
00330 
00331   XDestroyImage (image);
00332 }
00333 
00334 static gboolean
00335 effects_draw_box_animation_timeout (BoxAnimationContext *context)
00336 {
00337   double elapsed;
00338   GTimeVal current_time;
00339   MetaRectangle draw_rect;
00340   double fraction;
00341   
00342 #ifndef HAVE_SHAPE
00343   if (!context->first_time)
00344     {
00345        /* Restore the previously drawn background */
00346        XDrawRectangle (context->screen->display->xdisplay,
00347                        context->screen->xroot,
00348                        context->gc,
00349                        context->last_rect.x, context->last_rect.y,
00350                        context->last_rect.width, context->last_rect.height);
00351     }
00352   else
00353     context->first_time = FALSE;
00354 
00355 #endif /* !HAVE_SHAPE */
00356 
00357   g_get_current_time (&current_time);
00358   
00359   /* We use milliseconds for all times */
00360   elapsed =
00361     ((((double)current_time.tv_sec - context->start_time.tv_sec) * G_USEC_PER_SEC +
00362       (current_time.tv_usec - context->start_time.tv_usec))) / 1000.0;
00363   
00364   if (elapsed < 0)
00365     {
00366       /* Probably the system clock was set backwards? */
00367       meta_warning ("System clock seemed to go backwards?\n");
00368       elapsed = G_MAXDOUBLE; /* definitely done. */
00369     }
00370 
00371   if (elapsed > context->millisecs_duration)
00372     {
00373       /* All done */
00374 #ifdef HAVE_SHAPE
00375         XDestroyWindow (context->screen->display->xdisplay,
00376                           context->wireframe_xwindow);
00377 #else
00378         meta_display_ungrab (context->screen->display);
00379         meta_ui_pop_delay_exposes (context->screen->ui);
00380         XFreeGC (context->screen->display->xdisplay,
00381                  context->gc);
00382 #endif /* !HAVE_SHAPE */
00383 
00384       graphics_sync (context);
00385       
00386       g_free (context);
00387       return FALSE;
00388     }
00389 
00390   g_assert (context->millisecs_duration > 0.0);
00391   fraction = elapsed / context->millisecs_duration;
00392   
00393   draw_rect = context->start_rect;
00394   
00395   /* Now add a delta proportional to elapsed time. */
00396   draw_rect.x += (context->end_rect.x - context->start_rect.x) * fraction;
00397   draw_rect.y += (context->end_rect.y - context->start_rect.y) * fraction;
00398   draw_rect.width += (context->end_rect.width - context->start_rect.width) * fraction;
00399   draw_rect.height += (context->end_rect.height - context->start_rect.height) * fraction;
00400   
00401   /* don't confuse X or gdk-pixbuf with bogus rectangles */
00402   if (draw_rect.width < 1)
00403     draw_rect.width = 1;
00404   if (draw_rect.height < 1)
00405     draw_rect.height = 1;
00406 
00407 #ifdef HAVE_SHAPE
00408   update_wireframe_window (context->screen->display,
00409                            context->wireframe_xwindow,
00410                            &draw_rect);
00411 #else
00412   context->last_rect = draw_rect;
00413 
00414   /* Draw the rectangle */
00415   XDrawRectangle (context->screen->display->xdisplay,
00416                   context->screen->xroot,
00417                   context->gc,
00418                   draw_rect.x, draw_rect.y,
00419                   draw_rect.width, draw_rect.height);
00420     
00421 #endif /* !HAVE_SHAPE */
00422 
00423   /* kick changes onto the server */
00424   graphics_sync (context);
00425   
00426   return TRUE;
00427 }
00428  
00429 void
00430 draw_box_animation (MetaScreen     *screen,
00431                     MetaRectangle  *initial_rect,
00432                     MetaRectangle  *destination_rect,
00433                     double          seconds_duration)
00434 {
00435   BoxAnimationContext *context;
00436 
00437 #ifdef HAVE_SHAPE
00438   XSetWindowAttributes attrs;
00439 #else
00440   XGCValues gc_values;
00441 #endif
00442     
00443   g_return_if_fail (seconds_duration > 0.0);
00444 
00445   if (g_getenv ("METACITY_DEBUG_EFFECTS"))
00446     seconds_duration *= 10; /* slow things down */
00447   
00448   /* Create the animation context */
00449   context = g_new0 (BoxAnimationContext, 1);
00450 
00451   context->screen = screen;
00452 
00453   context->millisecs_duration = seconds_duration * 1000.0;
00454 
00455   context->start_rect = *initial_rect;
00456   context->end_rect = *destination_rect;
00457 
00458 #ifdef HAVE_SHAPE
00459 
00460   attrs.override_redirect = True;
00461   attrs.background_pixel = BlackPixel (screen->display->xdisplay,
00462                                        screen->number);
00463 
00464   context->wireframe_xwindow = XCreateWindow (screen->display->xdisplay,
00465                                               screen->xroot,
00466                                               initial_rect->x,
00467                                               initial_rect->y,
00468                                               initial_rect->width,
00469                                               initial_rect->height,
00470                                               0,
00471                                               CopyFromParent,
00472                                               CopyFromParent,
00473                                               (Visual *)CopyFromParent,
00474                                               CWOverrideRedirect | CWBackPixel,
00475                                               &attrs);
00476 
00477   update_wireframe_window (screen->display,
00478                            context->wireframe_xwindow,
00479                            initial_rect);
00480 
00481   XMapWindow (screen->display->xdisplay,
00482               context->wireframe_xwindow);
00483 
00484 #else /* !HAVE_SHAPE */
00485 
00486   context->first_time = TRUE;
00487   gc_values.subwindow_mode = IncludeInferiors;
00488   gc_values.function = GXinvert;
00489 
00490   context->gc = XCreateGC (screen->display->xdisplay,
00491                            screen->xroot,
00492                            GCSubwindowMode | GCFunction,
00493                            &gc_values);
00494       
00495   /* Grab the X server to avoid screen dirt */
00496   meta_display_grab (context->screen->display);
00497   meta_ui_push_delay_exposes (context->screen->ui);
00498 #endif
00499 
00500   /* Do this only after we get the pixbuf from the server,
00501    * so that the animation doesn't get truncated.
00502    */
00503   g_get_current_time (&context->start_time);
00504   
00505   /* Add the timeout - a short one, could even use an idle,
00506    * but this is maybe more CPU-friendly.
00507    */
00508   g_timeout_add (15,
00509                  (GSourceFunc)effects_draw_box_animation_timeout,
00510                  context);
00511 
00512   /* kick changes onto the server */
00513   XFlush (context->screen->display->xdisplay);  
00514 }
00515 
00516 void
00517 meta_effects_begin_wireframe (MetaScreen          *screen,
00518                               const MetaRectangle *rect,
00519                               int                  width,
00520                               int                  height)
00521 {
00522   /* Grab the X server to avoid screen dirt */
00523   meta_display_grab (screen->display);
00524   meta_ui_push_delay_exposes (screen->ui);  
00525 
00526   meta_effects_update_wireframe (screen, 
00527                                  NULL, -1, -1,
00528                                  rect, width, height);
00529 }
00530 
00531 static void
00532 draw_xor_rect (MetaScreen          *screen,
00533                const MetaRectangle *rect,
00534                int                  width,
00535                int                  height)
00536 {
00537   /* The lines in the center can't overlap the rectangle or each
00538    * other, or the XOR gets reversed. So we have to draw things
00539    * a bit oddly.
00540    */
00541   XSegment segments[8];
00542   MetaRectangle shrunk_rect;
00543   int i;
00544   
00545 #define LINE_WIDTH META_WIREFRAME_XOR_LINE_WIDTH
00546 
00547   /* We don't want the wireframe going outside the window area.
00548    * It makes it harder for the user to position windows and it exposes other
00549    * annoying bugs.
00550    */
00551   shrunk_rect = *rect;
00552 
00553   shrunk_rect.x += LINE_WIDTH / 2 + LINE_WIDTH % 2;
00554   shrunk_rect.y += LINE_WIDTH / 2 + LINE_WIDTH % 2;
00555   shrunk_rect.width -= LINE_WIDTH + 2 * (LINE_WIDTH % 2);
00556   shrunk_rect.height -= LINE_WIDTH + 2 * (LINE_WIDTH % 2);
00557 
00558   XDrawRectangle (screen->display->xdisplay,
00559                   screen->xroot,
00560                   screen->root_xor_gc,
00561                   shrunk_rect.x, shrunk_rect.y,
00562                   shrunk_rect.width, shrunk_rect.height);
00563 
00564   /* Don't put lines inside small rectangles where they won't fit */
00565   if (shrunk_rect.width < (LINE_WIDTH * 4) ||
00566       shrunk_rect.height < (LINE_WIDTH * 4))
00567     return;
00568 
00569   if ((width >= 0) && (height >= 0))
00570     {
00571       XGCValues gc_values = { 0 };
00572 
00573       if (XGetGCValues (screen->display->xdisplay,
00574                         screen->root_xor_gc, 
00575                         GCFont, &gc_values))
00576         {
00577           char *text;
00578           int text_length;
00579 
00580           XFontStruct *font_struct;
00581           int text_width, text_height; 
00582           int box_x, box_y;
00583           int box_width, box_height;
00584 
00585           font_struct = XQueryFont (screen->display->xdisplay,
00586                                     gc_values.font);
00587 
00588           if (font_struct != NULL)
00589             {
00590               text = g_strdup_printf ("%d x %d", width, height);
00591               text_length = strlen (text);
00592 
00593               text_width = text_length * font_struct->max_bounds.width;
00594               text_height = font_struct->max_bounds.descent + 
00595                             font_struct->max_bounds.ascent;
00596 
00597               box_width = text_width + 2 * LINE_WIDTH;
00598               box_height = text_height + 2 * LINE_WIDTH;
00599 
00600 
00601               box_x = shrunk_rect.x + (shrunk_rect.width - box_width) / 2;
00602               box_y = shrunk_rect.y + (shrunk_rect.height - box_height) / 2;
00603 
00604               if ((box_width < shrunk_rect.width) &&
00605                   (box_height < shrunk_rect.height))
00606                 {
00607                   XFillRectangle (screen->display->xdisplay,
00608                                   screen->xroot,
00609                                   screen->root_xor_gc,
00610                                   box_x, box_y,
00611                                   box_width, box_height);
00612                   XDrawString (screen->display->xdisplay, 
00613                                screen->xroot,
00614                                screen->root_xor_gc,
00615                                box_x + LINE_WIDTH,
00616                                box_y + LINE_WIDTH + font_struct->max_bounds.ascent,
00617                                text, text_length);
00618                 }
00619 
00620               g_free (text);
00621 
00622               XFreeFontInfo (NULL, font_struct, 1);
00623 
00624               if ((box_width + LINE_WIDTH) >= (shrunk_rect.width / 3))
00625                 return;
00626 
00627               if ((box_height + LINE_WIDTH) >= (shrunk_rect.height / 3))
00628                 return;
00629             }
00630         }
00631     }
00632 
00633   /* Two vertical lines at 1/3 and 2/3 */
00634   segments[0].x1 = shrunk_rect.x + shrunk_rect.width / 3;
00635   segments[0].y1 = shrunk_rect.y + LINE_WIDTH / 2 + LINE_WIDTH % 2;
00636   segments[0].x2 = segments[0].x1;
00637   segments[0].y2 = shrunk_rect.y + shrunk_rect.height - LINE_WIDTH / 2;  
00638 
00639   segments[1] = segments[0];
00640   segments[1].x1 = shrunk_rect.x + (shrunk_rect.width / 3) * 2;
00641   segments[1].x2 = segments[1].x1;
00642 
00643   /* Now make two horizontal lines at 1/3 and 2/3, but not
00644    * overlapping the verticals
00645    */
00646 
00647   segments[2].x1 = shrunk_rect.x + LINE_WIDTH / 2 + LINE_WIDTH % 2;
00648   segments[2].x2 = segments[0].x1 - LINE_WIDTH / 2;
00649   segments[2].y1 = shrunk_rect.y + shrunk_rect.height / 3;
00650   segments[2].y2 = segments[2].y1;
00651 
00652   segments[3] = segments[2];
00653   segments[3].x1 = segments[2].x2 + LINE_WIDTH;
00654   segments[3].x2 = segments[1].x1 - LINE_WIDTH / 2;
00655   
00656   segments[4] = segments[3];
00657   segments[4].x1 = segments[3].x2 + LINE_WIDTH;
00658   segments[4].x2 = shrunk_rect.x + shrunk_rect.width - LINE_WIDTH / 2;
00659 
00660   /* Second horizontal line is just like the first, but
00661    * shifted down
00662    */
00663   i = 5;
00664   while (i < 8)
00665     {
00666       segments[i] = segments[i - 3];
00667       segments[i].y1 = shrunk_rect.y + (shrunk_rect.height / 3) * 2;
00668       segments[i].y2 = segments[i].y1;
00669       ++i;
00670     }
00671   
00672   XDrawSegments (screen->display->xdisplay,
00673                  screen->xroot,
00674                  screen->root_xor_gc,
00675                  segments,
00676                  G_N_ELEMENTS (segments));
00677 }
00678 
00679 void
00680 meta_effects_update_wireframe (MetaScreen          *screen,
00681                                const MetaRectangle *old_rect,
00682                                int                  old_width,
00683                                int                  old_height,
00684                                const MetaRectangle *new_rect,
00685                                int                  new_width,
00686                                int                  new_height)
00687 {
00688   if (old_rect)
00689     draw_xor_rect (screen, old_rect, old_width, old_height);
00690     
00691   if (new_rect)
00692     draw_xor_rect (screen, new_rect, new_width, new_height);
00693     
00694   XFlush (screen->display->xdisplay);
00695 }
00696 
00697 void
00698 meta_effects_end_wireframe (MetaScreen          *screen,
00699                             const MetaRectangle *old_rect,
00700                             int                  old_width,
00701                             int                  old_height)
00702 {
00703   meta_effects_update_wireframe (screen, 
00704                                  old_rect, old_width, old_height,
00705                                  NULL, -1, -1);
00706   
00707   meta_display_ungrab (screen->display);
00708   meta_ui_pop_delay_exposes (screen->ui);
00709 }
00710 
00711 static void
00712 run_default_effect_handler (MetaEffect *effect)
00713 {
00714     switch (effect->type)
00715     {
00716     case META_EFFECT_MINIMIZE:
00717        draw_box_animation (effect->window->screen,
00718                      &(effect->u.minimize.window_rect),
00719                      &(effect->u.minimize.icon_rect),
00720                      META_MINIMIZE_ANIMATION_LENGTH);
00721        break;
00722 
00723     default:
00724        break;
00725     }
00726 }
00727 
00728 static void
00729 run_handler (MetaEffect *effect)
00730 {
00731   if (meta_prefs_get_gnome_animations ())
00732     run_default_effect_handler (effect);
00733 
00734   effect_free (effect);
00735 }

Generated on Sat Aug 23 22:04:16 2008 for metacity by  doxygen 1.5.5