00001
00002
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
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
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
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
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
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
00356
00357 g_get_current_time (¤t_time);
00358
00359
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
00367 meta_warning ("System clock seemed to go backwards?\n");
00368 elapsed = G_MAXDOUBLE;
00369 }
00370
00371 if (elapsed > context->millisecs_duration)
00372 {
00373
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
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
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
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
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
00422
00423
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;
00447
00448
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
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
00496 meta_display_grab (context->screen->display);
00497 meta_ui_push_delay_exposes (context->screen->ui);
00498 #endif
00499
00500
00501
00502
00503 g_get_current_time (&context->start_time);
00504
00505
00506
00507
00508 g_timeout_add (15,
00509 (GSourceFunc)effects_draw_box_animation_timeout,
00510 context);
00511
00512
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
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
00538
00539
00540
00541 XSegment segments[8];
00542 MetaRectangle shrunk_rect;
00543 int i;
00544
00545 #define LINE_WIDTH META_WIREFRAME_XOR_LINE_WIDTH
00546
00547
00548
00549
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
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
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
00644
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
00661
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 }