Exemplo n.º 1
0
static gint window_compare(gconstpointer a, gconstpointer b)
{
    ClutterActor* aa = *(ClutterActor**)a;
    ClutterActor* bb = *(ClutterActor**)b;

    MetaWindowActor* a1 = META_WINDOW_ACTOR(clutter_clone_get_source(CLUTTER_CLONE(aa)));
    MetaWindowActor* b1 = META_WINDOW_ACTOR(clutter_clone_get_source(CLUTTER_CLONE(bb)));

    MetaWindow* w1 = meta_window_actor_get_meta_window(a1);
    MetaWindow* w2 = meta_window_actor_get_meta_window(b1);
    return meta_window_get_stable_sequence(w1) - meta_window_get_stable_sequence(w2);
}
Exemplo n.º 2
0
/*
 * Simple TV-out like effect.
 */
static void
destroy (MetaPlugin *plugin, MetaWindowActor *window_actor)
{
  MetaWindowType type;
  ClutterActor *actor = CLUTTER_ACTOR (window_actor);
  MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor);

  type = meta_window_get_window_type (meta_window);

  if (type == META_WINDOW_NORMAL)
    {
      ClutterAnimation *animation;
      EffectCompleteData *data = g_new0 (EffectCompleteData, 1);
      ActorPrivate *apriv = get_actor_private (window_actor);

      animation = clutter_actor_animate (actor,
                                         CLUTTER_EASE_OUT_QUAD,
                                         DESTROY_TIMEOUT,
                                         "opacity", 0,
                                         "scale-x", 0.8,
                                         "scale-y", 0.8,
                                         NULL);
      apriv->tml_destroy = clutter_animation_get_timeline (animation);
      data->plugin = plugin;
      data->actor = actor;
      g_signal_connect (apriv->tml_destroy, "completed",
                        G_CALLBACK (on_destroy_effect_complete),
                        data);
    }
  else
    meta_plugin_destroy_completed (plugin, window_actor);
}
Exemplo n.º 3
0
void
mnb_zones_preview_add_window (MnbZonesPreview *preview,
                              MetaWindowActor *window)
{
  ClutterActor *clone;
  ClutterActor *group;
  MetaRectangle rect;
  gint workspace;

  /* TODO: Determine if we need to add a weak reference on the window
   *       in case it gets destroyed during the animation.
   *       I'd have thought that the clone's reference on the texture
   *       would be enough that this wouldn't be necessary.
   *
   * We do; while the clone's reference is enough to keep the texture about,
   * it is not enough to make it possible to map the clone once the texture
   * has been unparented.
   */
  workspace = meta_window_actor_get_workspace (window);
  group = mnb_zones_preview_get_workspace_group (preview, workspace);

  clone = clutter_clone_new (meta_window_actor_get_texture (window));

  g_signal_connect (window, "destroy",
                    G_CALLBACK (mnb_zones_preview_mcw_destroy_cb),
                    clone);
  g_signal_connect (clone, "destroy",
                    G_CALLBACK (mnb_zones_preview_clone_destroy_cb),
                    window);

  meta_window_get_input_rect (meta_window_actor_get_meta_window (window), &rect);
  clutter_actor_set_position (clone, rect.x, rect.y);

  clutter_container_add_actor (CLUTTER_CONTAINER (group), clone);
}
Exemplo n.º 4
0
/*
 * The Nature of Maximize operation is such that it is difficult to do a visual
 * effect that would work well. Scaling, the obvious effect, does not work that
 * well, because at the end of the effect we end up with window content bigger
 * and differently laid out than in the real window; this is a proof concept.
 *
 * (Something like a sound would be more appropriate.)
 */
static void
maximize (MetaPlugin *plugin,
          MetaWindowActor *window_actor,
          gint end_x, gint end_y, gint end_width, gint end_height)
{
  MetaWindowType type;
  ClutterActor *actor = CLUTTER_ACTOR (window_actor);
  MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor);

  gdouble  scale_x    = 1.0;
  gdouble  scale_y    = 1.0;
  gfloat   anchor_x   = 0;
  gfloat   anchor_y   = 0;

  type = meta_window_get_window_type (meta_window);

  if (type == META_WINDOW_NORMAL)
    {
      ClutterAnimation *animation;
      EffectCompleteData *data = g_new0 (EffectCompleteData, 1);
      ActorPrivate *apriv = get_actor_private (window_actor);
      gfloat width, height;
      gfloat x, y;

      apriv->is_maximized = TRUE;

      clutter_actor_get_size (actor, &width, &height);
      clutter_actor_get_position (actor, &x, &y);

      /*
       * Work out the scale and anchor point so that the window is expanding
       * smoothly into the target size.
       */
      scale_x = (gdouble)end_width / (gdouble) width;
      scale_y = (gdouble)end_height / (gdouble) height;

      anchor_x = (gdouble)(x - end_x)*(gdouble)width /
        ((gdouble)(end_width - width));
      anchor_y = (gdouble)(y - end_y)*(gdouble)height /
        ((gdouble)(end_height - height));

      clutter_actor_move_anchor_point (actor, anchor_x, anchor_y);

      animation = clutter_actor_animate (actor,
                                         CLUTTER_EASE_IN_SINE,
                                         MAXIMIZE_TIMEOUT,
                                         "scale-x", scale_x,
                                         "scale-y", scale_y,
                                         NULL);
      apriv->tml_maximize = clutter_animation_get_timeline (animation);
      data->plugin = plugin;
      data->actor = actor;
      g_signal_connect (apriv->tml_maximize, "completed",
                        G_CALLBACK (on_maximize_effect_complete),
                        data);
      return;
    }

  meta_plugin_maximize_completed (plugin, window_actor);
}
Exemplo n.º 5
0
void
meta_compositor_remove_window (MetaCompositor *compositor,
                               MetaWindow     *window)
{
    MetaWindowActor         *window_actor     = NULL;
    MetaScreen *screen;
    MetaCompScreen *info;

    DEBUG_TRACE ("meta_compositor_remove_window\n");
    window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
    if (!window_actor)
        return;

    screen = meta_window_get_screen (window);
    info = meta_screen_get_compositor_data (screen);

    if (window_actor == info->unredirected_window)
    {
        meta_window_actor_set_redirected (window_actor, TRUE);
        meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window (info->unredirected_window)),
                                   NULL);
        info->unredirected_window = NULL;
    }

    meta_window_actor_destroy (window_actor);
}
Exemplo n.º 6
0
static void
pre_paint_windows (MetaCompositor *compositor)
{
  GList *l;
  MetaWindowActor *top_window;

  if (compositor->onscreen == NULL)
    {
      compositor->onscreen = COGL_ONSCREEN (cogl_get_draw_framebuffer ());
      compositor->frame_closure = cogl_onscreen_add_frame_callback (compositor->onscreen,
                                                                    frame_callback,
                                                                    compositor,
                                                                    NULL);
    }

  if (compositor->windows == NULL)
    return;

  top_window = g_list_last (compositor->windows)->data;

  if (meta_window_actor_should_unredirect (top_window) &&
      compositor->disable_unredirect_count == 0)
    set_unredirected_window (compositor, meta_window_actor_get_meta_window (top_window));
  else
    set_unredirected_window (compositor, NULL);

  for (l = compositor->windows; l; l = l->next)
    meta_window_actor_pre_paint (l->data);

  if (compositor->frame_has_updated_xsurfaces)
    {
      /* We need to make sure that any X drawing that happens before
       * the XDamageSubtract() for each window above is visible to
       * subsequent GL rendering; the only standardized way to do this
       * is EXT_x11_sync_object, which isn't yet widely available. For
       * now, we count on details of Xorg and the open source drivers,
       * and hope for the best otherwise.
       *
       * Xorg and open source driver specifics:
       *
       * The X server makes sure to flush drawing to the kernel before
       * sending out damage events, but since we use
       * DamageReportBoundingBox there may be drawing between the last
       * damage event and the XDamageSubtract() that needs to be
       * flushed as well.
       *
       * Xorg always makes sure that drawing is flushed to the kernel
       * before writing events or responses to the client, so any
       * round trip request at this point is sufficient to flush the
       * GLX buffers.
       */
      XSync (compositor->display->xdisplay, False);

      compositor->frame_has_updated_xsurfaces = FALSE;
    }
}
Exemplo n.º 7
0
static void
pre_paint_windows (MetaCompScreen *info)
{
    GList *l;
    MetaWindowActor *top_window;
    MetaWindowActor *expected_unredirected_window = NULL;

    if (info->windows == NULL)
        return;

    top_window = g_list_last (info->windows)->data;

    if (meta_window_actor_should_unredirect (top_window) &&
            info->disable_unredirect_count == 0)
        expected_unredirected_window = top_window;

    if (info->unredirected_window != expected_unredirected_window)
    {
        if (info->unredirected_window != NULL)
        {
            meta_window_actor_set_redirected (info->unredirected_window, TRUE);
            meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window (info->unredirected_window)),
                                       NULL);
        }

        if (expected_unredirected_window != NULL)
        {
            meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window (top_window)),
                                       meta_window_actor_get_meta_window (top_window));
            meta_window_actor_set_redirected (top_window, FALSE);
        }

        info->unredirected_window = expected_unredirected_window;
    }

    for (l = info->windows; l; l = l->next)
        meta_window_actor_pre_paint (l->data);
}
Exemplo n.º 8
0
/*
 * Simple minimize handler: it applies a scale effect (which must be reversed on
 * completion).
 */
static void
minimize (MetaPlugin *plugin, MetaWindowActor *window_actor)
{
  MetaWindowType type;
  MetaRectangle icon_geometry;
  MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor);
  ClutterActor *actor  = CLUTTER_ACTOR (window_actor);


  type = meta_window_get_window_type (meta_window);

  if (!meta_window_get_icon_geometry(meta_window, &icon_geometry))
    {
      icon_geometry.x = 0;
      icon_geometry.y = 0;
    }

  if (type == META_WINDOW_NORMAL)
    {
      ClutterAnimation *animation;
      EffectCompleteData *data = g_new0 (EffectCompleteData, 1);
      ActorPrivate *apriv = get_actor_private (window_actor);

      apriv->is_minimized = TRUE;

      clutter_actor_move_anchor_point_from_gravity (actor,
                                                    CLUTTER_GRAVITY_CENTER);

      animation = clutter_actor_animate (actor,
                                         CLUTTER_EASE_IN_SINE,
                                         MINIMIZE_TIMEOUT,
                                         "scale-x", 0.0,
                                         "scale-y", 0.0,
                                         "x", (double)icon_geometry.x,
                                         "y", (double)icon_geometry.y,
                                         NULL);
      apriv->tml_minimize = clutter_animation_get_timeline (animation);
      data->plugin = plugin;
      data->actor = actor;
      g_signal_connect (apriv->tml_minimize, "completed",
                        G_CALLBACK (on_minimize_effect_complete),
                        data);

    }
  else
    meta_plugin_minimize_completed (plugin, window_actor);
}
Exemplo n.º 9
0
/*
 * See comments on the maximize() function.
 *
 * (Just a skeleton code.)
 */
static void
unmaximize (MetaPlugin *plugin,
            MetaWindowActor *window_actor,
            gint end_x, gint end_y, gint end_width, gint end_height)
{
  MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor);
  MetaWindowType type = meta_window_get_window_type (meta_window);

  if (type == META_WINDOW_NORMAL)
    {
      ActorPrivate *apriv = get_actor_private (window_actor);

      apriv->is_maximized = FALSE;
    }

  /* Do this conditionally, if the effect requires completion callback. */
  meta_plugin_unmaximize_completed (plugin, window_actor);
}
Exemplo n.º 10
0
/*
 * Simple map handler: it applies a scale effect which must be reversed on
 * completion).
 */
static void
map (MetaPlugin *plugin, MetaWindowActor *window_actor)
{
  MetaWindowType type;
  ClutterActor *actor = CLUTTER_ACTOR (window_actor);
  MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor);

  type = meta_window_get_window_type (meta_window);

  if (type == META_WINDOW_NORMAL)
    {
      ClutterAnimation *animation;
      EffectCompleteData *data = g_new0 (EffectCompleteData, 1);
      ActorPrivate *apriv = get_actor_private (window_actor);

      clutter_actor_set_pivot_point (actor, 0.5, 0.5);
      clutter_actor_set_opacity (actor, 0);
      clutter_actor_set_scale (actor, 0.5, 0.5);
      clutter_actor_show (actor);

      animation = clutter_actor_animate (actor,
                                         CLUTTER_EASE_OUT_QUAD,
                                         MAP_TIMEOUT,
                                         "opacity", 255,
                                         "scale-x", 1.0,
                                         "scale-y", 1.0,
                                         NULL);
      apriv->tml_map = clutter_animation_get_timeline (animation);
      data->actor = actor;
      data->plugin = plugin;
      g_signal_connect (apriv->tml_map, "completed",
                        G_CALLBACK (on_map_effect_complete),
                        data);
    }
  else
    meta_plugin_map_completed (plugin, window_actor);
}
Exemplo n.º 11
0
/*
 * Simple TV-out like effect.
 */
static void
destroy (MetaPlugin *plugin, MetaWindowActor *window_actor)
{
  MetaWindowType type;
  ClutterActor *actor = CLUTTER_ACTOR (window_actor);
  MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor);

  type = meta_window_get_window_type (meta_window);

  if (type == META_WINDOW_NORMAL || type == META_WINDOW_DIALOG ||
      type == META_WINDOW_MODAL_DIALOG)
  {
      ClutterAnimation *animation;
      EffectCompleteData *data = g_new0 (EffectCompleteData, 1);
      ActorPrivate *apriv = get_actor_private (window_actor);

      clutter_actor_move_anchor_point_from_gravity (actor,
                                                    CLUTTER_GRAVITY_CENTER);

      animation = clutter_actor_animate (actor,
                                         CLUTTER_EASE_IN_SINE,
                                         DESTROY_TIMEOUT,
                                         "scale-x", DESTROY_SCALE,
                                         "scale-y", DESTROY_SCALE,
                                         "opacity", 0,
                                         NULL);
      apriv->tml_destroy = clutter_animation_get_timeline (animation);
      data->plugin = plugin;
      data->actor = actor;
      g_signal_connect (apriv->tml_destroy, "completed",
                        G_CALLBACK (on_destroy_effect_complete),
                        data);
    }
  else
    meta_plugin_destroy_completed (plugin, window_actor);
}
Exemplo n.º 12
0
/*
 * Simple map handler: it applies a scale effect which must be reversed on
 * completion).
 */
static void
map (MetaPlugin *plugin, MetaWindowActor *window_actor)
{
  MetaWindowType type;
  ClutterActor *actor = CLUTTER_ACTOR (window_actor);
  MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor);

  type = meta_window_get_window_type (meta_window);

  if (type == META_WINDOW_NORMAL || type == META_WINDOW_DIALOG ||
      type == META_WINDOW_MODAL_DIALOG)
  {
      ClutterAnimation *animation;
      EffectCompleteData *data = g_new0 (EffectCompleteData, 1);
      ActorPrivate *apriv = get_actor_private (window_actor);

      clutter_actor_move_anchor_point_from_gravity (actor,
                                                    CLUTTER_GRAVITY_CENTER);
      clutter_actor_set_scale (actor, MAP_SCALE, MAP_SCALE);
      clutter_actor_set_opacity (actor, 0);
      clutter_actor_show (actor);

      animation = clutter_actor_animate (actor,
                                         CLUTTER_EASE_IN_SINE,
                                         MAP_TIMEOUT,
                                         "scale-x", 1.0,
                                         "scale-y", 1.0,
                                         "opacity", 255,
                                         NULL);
      apriv->tml_map = clutter_animation_get_timeline (animation);
      data->actor = actor;
      data->plugin = plugin;
      g_signal_connect (apriv->tml_map, "completed",
                        G_CALLBACK (on_map_effect_complete),
                        data);

      apriv->is_minimized = FALSE;

  } else if (type == META_WINDOW_DOCK)
  {
      /* For context menus (popup/dropdown) we fade the menu in */
      ClutterAnimation *animation;
      EffectCompleteData *data = g_new0 (EffectCompleteData, 1);
      ActorPrivate *apriv = get_actor_private (window_actor);

      clutter_actor_set_opacity (actor, 0);
      clutter_actor_show (actor);

      animation = clutter_actor_animate (actor,
                                         CLUTTER_EASE_IN_SINE,
                                         MAP_TIMEOUT,
                                         "opacity", 255,
                                         NULL);
      apriv->tml_map = clutter_animation_get_timeline (animation);
      data->actor = actor;
      data->plugin = plugin;
      g_signal_connect (apriv->tml_map, "completed",
                        G_CALLBACK (on_map_effect_complete),
                        data);

      apriv->is_minimized = FALSE;
  } else
    meta_plugin_map_completed (plugin, window_actor);
}
Exemplo n.º 13
0
static void moses_overview_dispose(GObject *object)
{
    MosesOverview *overview = MOSES_OVERVIEW(object);
    MosesOverviewPrivate* priv = overview->priv;

    if (priv->disposed) return;
    priv->disposed = TRUE;

    g_clear_pointer(&priv->ov_head, g_object_unref);

    MetaScreen* screen = meta_plugin_get_screen(priv->plugin);
    ClutterActor* stage = meta_get_stage_for_screen(screen);

    ClutterActor* to_focus = NULL;
    if (priv->selected_actor) {
        to_focus = clutter_clone_get_source(CLUTTER_CLONE(priv->selected_actor));
    }

    for (int i = 0; priv->clones && i < priv->clones->len; i++) {
        ClutterActor* clone = g_ptr_array_index(priv->clones, i);
        ClutterActor* orig = clutter_clone_get_source(CLUTTER_CLONE(clone));
        clutter_actor_show(orig); // FIXME: maybe some actors had not been shown.
        clutter_actor_destroy(clone);
    }

    for (int i = 0; priv->badges && i < priv->badges->len; i++) {
        clutter_actor_destroy(CLUTTER_ACTOR(g_ptr_array_index(priv->badges, i)));
    }

    if (priv->background_actor) {
        clutter_actor_show(clutter_clone_get_source(CLUTTER_CLONE(priv->background_actor)));
        g_clear_pointer(&priv->background_actor, clutter_actor_destroy);
    }

    if (priv->modaled) {
        meta_plugin_end_modal(priv->plugin, clutter_get_current_event_time());
        meta_enable_unredirect_for_screen(screen);

        if (priv->selected_workspace) {
            meta_workspace_activate(priv->selected_workspace, CLUTTER_CURRENT_TIME);
            MetaDisplay* display = meta_screen_get_display(screen);
            meta_compositor_switch_workspace(meta_display_get_compositor(display),
                    meta_screen_get_active_workspace(screen),
                    priv->selected_workspace, META_MOTION_DOWN);

        } else if (to_focus) {
            clutter_stage_set_key_focus(CLUTTER_STAGE(stage), to_focus);
            MetaWindowActor* actor = META_WINDOW_ACTOR(to_focus);
            MetaWindow* win = meta_window_actor_get_meta_window(actor);
            meta_window_raise(win);
            meta_window_focus(win, CLUTTER_CURRENT_TIME);

        } else if (priv->previous_focused) {
            if (!CLUTTER_IS_STAGE(priv->previous_focused)) {
                clutter_stage_set_key_focus(CLUTTER_STAGE(stage), priv->previous_focused);
            }
        }
    }

    G_OBJECT_CLASS(moses_overview_parent_class)->dispose(object);
}
/*
 * This is the Metacity entry point for the effect.
 */
void
mnb_switch_zones_effect (MetaPlugin         *plugin,
                         gint                from,
                         gint                to,
                         MetaMotionDirection direction)
{
  GList *w;
  gint width, height;
  MetaScreen *screen;
  ClutterActor *window_group;

  if (running++)
    {
      /*
       * We have been called while the effect is already in progress; we need to
       * mutter know that we completed the previous run.
       */
      if (--running < 0)
        {
          g_warning (G_STRLOC ": error in running effect accounting!");
          running = 0;
        }

      meta_plugin_switch_workspace_completed (plugin);
    }

  if ((from == to) && !zones_preview)
    {
      if (--running < 0)
        {
          g_warning (G_STRLOC ": error in running effect accounting!");
          running = 0;
        }

      meta_plugin_switch_workspace_completed (plugin);

      return;
    }

  screen = meta_plugin_get_screen (plugin);

  if (!zones_preview)
    {
      ClutterActor *stage;

      /* Construct the zones preview actor */
      zones_preview = mnb_zones_preview_new ();
      g_object_set (G_OBJECT (zones_preview),
                    "workspace", (gdouble)from,
                    NULL);

      /* Add it to the stage */
      stage = meta_get_stage_for_screen (screen);
      clutter_container_add_actor (CLUTTER_CONTAINER (stage), zones_preview);

      /* Attach to completed signal */
      g_signal_connect (zones_preview, "switch-completed",
                        G_CALLBACK (mnb_switch_zones_completed_cb), plugin);
    }

  meta_screen_get_size (screen, &width, &height);
  g_object_set (G_OBJECT (zones_preview),
                "workspace-width", (guint)width,
                "workspace-height", (guint)height,
                NULL);

  mnb_zones_preview_clear (MNB_ZONES_PREVIEW (zones_preview));
  mnb_zones_preview_set_n_workspaces (MNB_ZONES_PREVIEW (zones_preview),
                                      meta_screen_get_n_workspaces (screen));

  /* Add windows to zone preview actor */
  for (w = meta_get_window_actors (screen); w; w = w->next)
    {
      MetaWindowActor *window_actor = w->data;
      gint workspace = meta_window_actor_get_workspace (window_actor);
      MetaWindow *window = meta_window_actor_get_meta_window (window_actor);
      MetaWindowType type = meta_window_get_window_type (window);

      /*
       * Only show regular windows that are not sticky (getting stacking order
       * right for sticky windows would be really hard, and since they appear
       * on each workspace, they do not help in identifying which workspace
       * it is).
       */
      if ((workspace < 0) ||
          meta_window_actor_is_override_redirect (window_actor) ||
          (type != META_WINDOW_NORMAL))
        continue;

      mnb_zones_preview_add_window (MNB_ZONES_PREVIEW (zones_preview), window_actor);
    }

  /* Make sure it's on top */
  window_group = meta_get_window_group_for_screen (screen);
  clutter_actor_raise (zones_preview, window_group);

  /* Initiate animation */
  mnb_zones_preview_change_workspace (MNB_ZONES_PREVIEW (zones_preview), to);
}
Exemplo n.º 15
0
static gboolean
meta_pre_paint_func (gpointer data)
{
  GList *l;
  MetaWindowActor *top_window;
  MetaCompositor *compositor = data;

  if (compositor->onscreen == NULL)
    {
      compositor->onscreen = COGL_ONSCREEN (cogl_get_draw_framebuffer ());
      compositor->frame_closure = cogl_onscreen_add_frame_callback (compositor->onscreen,
                                                                    frame_callback,
                                                                    compositor,
                                                                    NULL);
    }

  if (compositor->windows == NULL)
    return TRUE;

  top_window = g_list_last (compositor->windows)->data;

  if (meta_window_actor_should_unredirect (top_window) &&
      compositor->disable_unredirect_count == 0)
    set_unredirected_window (compositor, meta_window_actor_get_meta_window (top_window));
  else
    set_unredirected_window (compositor, NULL);

  for (l = compositor->windows; l; l = l->next)
    meta_window_actor_pre_paint (l->data);

  if (compositor->frame_has_updated_xsurfaces)
    {
      /* We need to make sure that any X drawing that happens before
       * the XDamageSubtract() for each window above is visible to
       * subsequent GL rendering; the standardized way to do this is
       * GL_EXT_X11_sync_object. Since this isn't implemented yet in
       * mesa, we also have a path that relies on the implementation
       * of the open source drivers.
       *
       * Anything else, we just hope for the best.
       *
       * Xorg and open source driver specifics:
       *
       * The X server makes sure to flush drawing to the kernel before
       * sending out damage events, but since we use
       * DamageReportBoundingBox there may be drawing between the last
       * damage event and the XDamageSubtract() that needs to be
       * flushed as well.
       *
       * Xorg always makes sure that drawing is flushed to the kernel
       * before writing events or responses to the client, so any
       * round trip request at this point is sufficient to flush the
       * GLX buffers.
       */
      if (compositor->have_x11_sync_object)
        compositor->have_x11_sync_object = meta_sync_ring_insert_wait ();
      else
        XSync (compositor->display->xdisplay, False);
    }

  return TRUE;
}
Exemplo n.º 16
0
void
meta_compositor_sync_stack (MetaCompositor  *compositor,
			    GList	    *stack)
{
  GList *old_stack;

  /* This is painful because hidden windows that we are in the process
   * of animating out of existence. They'll be at the bottom of the
   * stack of X windows, but we want to leave them in their old position
   * until the animation effect finishes.
   */

  /* Sources: first window is the highest */
  stack = g_list_copy (stack); /* The new stack of MetaWindow */
  old_stack = g_list_reverse (compositor->windows); /* The old stack of MetaWindowActor */
  compositor->windows = NULL;

  while (TRUE)
    {
      MetaWindowActor *old_actor = NULL, *stack_actor = NULL, *actor;
      MetaWindow *old_window = NULL, *stack_window = NULL, *window;

      /* Find the remaining top actor in our existing stack (ignoring
       * windows that have been hidden and are no longer animating) */
      while (old_stack)
        {
          old_actor = old_stack->data;
          old_window = meta_window_actor_get_meta_window (old_actor);

          if ((old_window->hidden || old_window->unmanaging) &&
              !meta_window_actor_effect_in_progress (old_actor))
            {
              old_stack = g_list_delete_link (old_stack, old_stack);
              old_actor = NULL;
            }
          else
            break;
        }

      /* And the remaining top actor in the new stack */
      while (stack)
        {
          stack_window = stack->data;
          stack_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (stack_window));
          if (!stack_actor)
            {
              meta_verbose ("Failed to find corresponding MetaWindowActor "
                            "for window %s\n", meta_window_get_description (stack_window));
              stack = g_list_delete_link (stack, stack);
            }
          else
            break;
        }

      if (!old_actor && !stack_actor) /* Nothing more to stack */
        break;

      /* We usually prefer the window in the new stack, but if if we
       * found a hidden window in the process of being animated out
       * of existence in the old stack we use that instead. We've
       * filtered out non-animating hidden windows above.
       */
      if (old_actor &&
          (!stack_actor || old_window->hidden || old_window->unmanaging))
        {
          actor = old_actor;
          window = old_window;
        }
      else
        {
          actor = stack_actor;
          window = stack_window;
        }

      /* OK, we know what actor we want next. Add it to our window
       * list, and remove it from both source lists. (It will
       * be at the front of at least one, hopefully it will be
       * near the front of the other.)
       */
      compositor->windows = g_list_prepend (compositor->windows, actor);

      stack = g_list_remove (stack, window);
      old_stack = g_list_remove (old_stack, actor);
    }

  sync_actor_stacking (compositor);
}
Exemplo n.º 17
0
static void
switch_workspace (MetaPlugin *plugin,
                  gint from, gint to,
                  MetaMotionDirection direction)
{
  MetaScreen *screen;
  MetaDefaultPluginPrivate *priv = META_DEFAULT_PLUGIN (plugin)->priv;
  GList        *l;
  ClutterActor *workspace0  = clutter_group_new ();
  ClutterActor *workspace1  = clutter_group_new ();
  ClutterActor *stage;
  int           screen_width, screen_height;
  ClutterAnimation *animation;

  screen = meta_plugin_get_screen (plugin);
  stage = meta_get_stage_for_screen (screen);

  meta_screen_get_size (screen,
                        &screen_width,
                        &screen_height);

  clutter_actor_set_anchor_point (workspace1,
                                  screen_width,
                                  screen_height);
  clutter_actor_set_position (workspace1,
                              screen_width,
                              screen_height);

  clutter_actor_set_scale (workspace1, 0.0, 0.0);

  clutter_container_add_actor (CLUTTER_CONTAINER (stage), workspace1);
  clutter_container_add_actor (CLUTTER_CONTAINER (stage), workspace0);

  if (from == to)
    {
      meta_plugin_switch_workspace_completed (plugin);
      return;
    }

  l = g_list_last (meta_get_window_actors (screen));

  while (l)
    {
      MetaWindowActor *window_actor = l->data;
      MetaWindow *window = meta_window_actor_get_meta_window (window_actor);
      MetaWorkspace   *workspace;
      ActorPrivate    *apriv	    = get_actor_private (window_actor);
      ClutterActor    *actor	    = CLUTTER_ACTOR (window_actor);
      gint             win_workspace;

      workspace = meta_window_get_workspace (window);
      win_workspace = meta_workspace_index (workspace);

      if (win_workspace == to || win_workspace == from)
        {
          apriv->orig_parent = clutter_actor_get_parent (actor);

          clutter_actor_reparent (actor,
				  win_workspace == to ? workspace1 : workspace0);
          clutter_actor_show_all (actor);
          clutter_actor_raise_top (actor);
        }
      else if (win_workspace < 0)
        {
          /* Sticky window */
          apriv->orig_parent = NULL;
        }
      else
        {
          /* Window on some other desktop */
          clutter_actor_hide (actor);
          apriv->orig_parent = NULL;
        }

      l = l->prev;
    }

  priv->desktop1 = workspace0;
  priv->desktop2 = workspace1;

  animation = clutter_actor_animate (workspace0, CLUTTER_EASE_IN_SINE,
                                     SWITCH_TIMEOUT,
                                     "scale-x", 1.0,
                                     "scale-y", 1.0,
                                     NULL);
  priv->tml_switch_workspace1 = clutter_animation_get_timeline (animation);
  g_signal_connect (priv->tml_switch_workspace1,
                    "completed",
                    G_CALLBACK (on_switch_workspace_effect_complete),
                    plugin);

  animation = clutter_actor_animate (workspace1, CLUTTER_EASE_IN_SINE,
                                     SWITCH_TIMEOUT,
                                     "scale-x", 0.0,
                                     "scale-y", 0.0,
                                     NULL);
  priv->tml_switch_workspace2 = clutter_animation_get_timeline (animation);
}
Exemplo n.º 18
0
static void
meta_window_group_paint (ClutterActor *actor)
{
  cairo_region_t *visible_region;
  ClutterActor *stage;
  cairo_rectangle_int_t visible_rect;
  GList *children, *l;
  int paint_x_origin, paint_y_origin;
  int actor_x_origin, actor_y_origin;
  int paint_x_offset, paint_y_offset;

  MetaWindowGroup *window_group = META_WINDOW_GROUP (actor);
  MetaCompScreen *info = meta_screen_get_compositor_data (window_group->screen);

  /* Normally we expect an actor to be drawn at it's position on the screen.
   * However, if we're inside the paint of a ClutterClone, that won't be the
   * case and we need to compensate. We look at the position of the window
   * group under the current model-view matrix and the position of the actor.
   * If they are both simply integer translations, then we can compensate
   * easily, otherwise we give up.
   *
   * Possible cleanup: work entirely in paint space - we can compute the
   * combination of the model-view matrix with the local matrix for each child
   * actor and get a total transformation for that actor for how we are
   * painting currently, and never worry about how actors are positioned
   * on the stage.
   */
  if (!painting_untransformed (window_group, &paint_x_origin, &paint_y_origin) ||
      !meta_actor_is_untransformed (actor, &actor_x_origin, &actor_y_origin))
    {
      CLUTTER_ACTOR_CLASS (meta_window_group_parent_class)->paint (actor);
      return;
    }

  paint_x_offset = paint_x_origin - actor_x_origin;
  paint_y_offset = paint_y_origin - actor_y_origin;

  /* We walk the list from top to bottom (opposite of painting order),
   * and subtract the opaque area of each window out of the visible
   * region that we pass to the windows below.
   */
  children = clutter_actor_get_children (actor);
  children = g_list_reverse (children);

  /* Get the clipped redraw bounds from Clutter so that we can avoid
   * painting shadows on windows that don't need to be painted in this
   * frame. In the case of a multihead setup with mismatched monitor
   * sizes, we could intersect this with an accurate union of the
   * monitors to avoid painting shadows that are visible only in the
   * holes. */
  stage = clutter_actor_get_stage (actor);
  clutter_stage_get_redraw_clip_bounds (CLUTTER_STAGE (stage),
                                        &visible_rect);

  visible_region = cairo_region_create_rectangle (&visible_rect);

  if (info->unredirected_window != NULL)
    {
      cairo_rectangle_int_t unredirected_rect;
      MetaWindow *window = meta_window_actor_get_meta_window (info->unredirected_window);

      meta_window_get_outer_rect (window, (MetaRectangle *)&unredirected_rect);
      cairo_region_subtract_rectangle (visible_region, &unredirected_rect);
    }

  for (l = children; l; l = l->next)
    {
      if (!CLUTTER_ACTOR_IS_VISIBLE (l->data))
        continue;

      if (l->data == info->unredirected_window)
        continue;

      /* If an actor has effects applied, then that can change the area
       * it paints and the opacity, so we no longer can figure out what
       * portion of the actor is obscured and what portion of the screen
       * it obscures, so we skip the actor.
       *
       * This has a secondary beneficial effect: if a ClutterOffscreenEffect
       * is applied to an actor, then our clipped redraws interfere with the
       * caching of the FBO - even if we only need to draw a small portion
       * of the window right now, ClutterOffscreenEffect may use other portions
       * of the FBO later. So, skipping actors with effects applied also
       * prevents these bugs.
       *
       * Theoretically, we should check clutter_actor_get_offscreen_redirect()
       * as well for the same reason, but omitted for simplicity in the
       * hopes that no-one will do that.
       */
      if (clutter_actor_has_effects (l->data))
        continue;

      if (META_IS_WINDOW_ACTOR (l->data))
        {
          MetaWindowActor *window_actor = l->data;
          int x, y;

          if (!meta_actor_is_untransformed (CLUTTER_ACTOR (window_actor), &x, &y))
            continue;

          x += paint_x_offset;
          y += paint_y_offset;

          /* Temporarily move to the coordinate system of the actor */
          cairo_region_translate (visible_region, - x, - y);

          meta_window_actor_set_visible_region (window_actor, visible_region);

          if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff)
            {
              cairo_region_t *obscured_region = meta_window_actor_get_obscured_region (window_actor);
              if (obscured_region)
                cairo_region_subtract (visible_region, obscured_region);
            }

          meta_window_actor_set_visible_region_beneath (window_actor, visible_region);
          cairo_region_translate (visible_region, x, y);
        }
      else if (META_IS_BACKGROUND_ACTOR (l->data) ||
               META_IS_BACKGROUND_GROUP (l->data))
        {
          ClutterActor *background_actor = l->data;
          int x, y;

          if (!meta_actor_is_untransformed (CLUTTER_ACTOR (background_actor), &x, &y))
            continue;

          x += paint_x_offset;
          y += paint_y_offset;

          cairo_region_translate (visible_region, - x, - y);

          if (META_IS_BACKGROUND_GROUP (background_actor))
            meta_background_group_set_visible_region (META_BACKGROUND_GROUP (background_actor), visible_region);
          else
            meta_background_actor_set_visible_region (META_BACKGROUND_ACTOR (background_actor), visible_region);
          cairo_region_translate (visible_region, x, y);
        }
    }

  cairo_region_destroy (visible_region);

  CLUTTER_ACTOR_CLASS (meta_window_group_parent_class)->paint (actor);

  /* Now that we are done painting, unset the visible regions (they will
   * mess up painting clones of our actors)
   */
  for (l = children; l; l = l->next)
    {
      if (META_IS_WINDOW_ACTOR (l->data))
        {
          MetaWindowActor *window_actor = l->data;
          meta_window_actor_reset_visible_regions (window_actor);
        }
      else if (META_IS_BACKGROUND_ACTOR (l->data))
        {
          MetaBackgroundActor *background_actor = l->data;
          meta_background_actor_set_visible_region (background_actor, NULL);
        }
    }

  g_list_free (children);
}
Exemplo n.º 19
0
static void
meta_window_group_paint (ClutterActor *actor)
{
  cairo_region_t *clip_region;
  cairo_region_t *unobscured_region;
  ClutterActorIter iter;
  ClutterActor *child;
  cairo_rectangle_int_t visible_rect, clip_rect;
  int paint_x_offset, paint_y_offset;
  int paint_x_origin, paint_y_origin;
  int actor_x_origin, actor_y_origin;

  MetaWindowGroup *window_group = META_WINDOW_GROUP (actor);
  MetaCompositor *compositor = window_group->screen->display->compositor;
  ClutterActor *stage = CLUTTER_STAGE (compositor->stage);

  /* Start off by treating all windows as completely unobscured, so damage anywhere
   * in a window queues redraws, but confine it more below. */
  clutter_actor_iter_init (&iter, actor);
  while (clutter_actor_iter_next (&iter, &child))
    {
      if (META_IS_WINDOW_ACTOR (child))
        {
          MetaWindowActor *window_actor = META_WINDOW_ACTOR (child);
          meta_window_actor_set_unobscured_region (window_actor, NULL);
        }
    }

  /* Normally we expect an actor to be drawn at it's position on the screen.
   * However, if we're inside the paint of a ClutterClone, that won't be the
   * case and we need to compensate. We look at the position of the window
   * group under the current model-view matrix and the position of the actor.
   * If they are both simply integer translations, then we can compensate
   * easily, otherwise we give up.
   *
   * Possible cleanup: work entirely in paint space - we can compute the
   * combination of the model-view matrix with the local matrix for each child
   * actor and get a total transformation for that actor for how we are
   * painting currently, and never worry about how actors are positioned
   * on the stage.
   */
  if (!painting_untransformed (window_group, &paint_x_origin, &paint_y_origin) ||
      !meta_actor_is_untransformed (actor, &actor_x_origin, &actor_y_origin))
    {
      CLUTTER_ACTOR_CLASS (meta_window_group_parent_class)->paint (actor);
      return;
    }

  paint_x_offset = paint_x_origin - actor_x_origin;
  paint_y_offset = paint_y_origin - actor_y_origin;

  visible_rect.x = visible_rect.y = 0;
  visible_rect.width = clutter_actor_get_width (stage);
  visible_rect.height = clutter_actor_get_height (stage);

  unobscured_region = cairo_region_create_rectangle (&visible_rect);

  /* Get the clipped redraw bounds from Clutter so that we can avoid
   * painting shadows on windows that don't need to be painted in this
   * frame. In the case of a multihead setup with mismatched monitor
   * sizes, we could intersect this with an accurate union of the
   * monitors to avoid painting shadows that are visible only in the
   * holes. */
  clutter_stage_get_redraw_clip_bounds (stage, &clip_rect);

  clip_region = cairo_region_create_rectangle (&clip_rect);

  cairo_region_translate (clip_region, -paint_x_offset, -paint_y_offset);

  gboolean has_unredirected_window = compositor->unredirected_window != NULL;
  if (has_unredirected_window)
    {
      cairo_rectangle_int_t unredirected_rect;
      MetaWindow *window = meta_window_actor_get_meta_window (compositor->unredirected_window);

      meta_window_get_outer_rect (window, (MetaRectangle *)&unredirected_rect);
      cairo_region_subtract_rectangle (unobscured_region, &unredirected_rect);
      cairo_region_subtract_rectangle (clip_region, &unredirected_rect);
    }

  meta_window_group_cull_out (window_group,
                              CLUTTER_ACTOR (compositor->unredirected_window),
                              has_unredirected_window,
                              unobscured_region,
                              clip_region);

  cairo_region_destroy (unobscured_region);
  cairo_region_destroy (clip_region);

  CLUTTER_ACTOR_CLASS (meta_window_group_parent_class)->paint (actor);

  meta_window_group_reset_culling (window_group);
}
Exemplo n.º 20
0
static void natural_placement (MosesOverview* self, MetaRectangle area)
{
    g_debug("%s: geom: %d,%d,%d,%d", __func__, area.x, area.y, area.width, area.height);
    MosesOverviewPrivate* priv = self->priv;
    GPtrArray* clones = priv->clones;

    MetaRectangle bounds = {area.x, area.y, area.width, area.height};

    int direction = 0;
    int* directions = g_malloc(sizeof(int)*clones->len);
    MetaRectangle* rects = g_malloc(sizeof(MetaRectangle)*clones->len);

    for (int i = 0; i < clones->len; i++) {
        // save rectangles into 4-dimensional arrays representing two corners of the rectangular: [left_x, top_y, right_x, bottom_y]
        MetaRectangle rect;
        ClutterActor* clone = g_ptr_array_index(clones, i);
        MetaWindowActor* actor = META_WINDOW_ACTOR(clutter_clone_get_source(CLUTTER_CLONE(clone)));
        MetaWindow* win = meta_window_actor_get_meta_window(actor);

        meta_window_get_frame_rect(win, &rect);
        rect = rect_adjusted(rect, -GAPS, -GAPS, GAPS, GAPS);
        rects[i] = rect;
        g_debug("%s: frame: %d,%d,%d,%d", __func__, rect.x, rect.y, rect.width, rect.height);

        meta_rectangle_union(&bounds, &rect, &bounds);

        // This is used when the window is on the edge of the screen to try to use as much screen real estate as possible.
        directions[i] = direction;
        direction++;
        if (direction == 4)
            direction = 0;
    }

    int loop_counter = 0;
    gboolean overlap = FALSE;
    do {
        overlap = FALSE;
        for (int i = 0; i < clones->len; i++) {
            for (int j = 0; j < clones->len; j++) {
                if (i == j)
                    continue;

                MetaRectangle rect = rects[i];
                MetaRectangle comp = rects[j];

                if (!meta_rectangle_overlap(&rect, &comp))
                    continue;

                loop_counter ++;
                overlap = TRUE;

                // Determine pushing direction
                GdkPoint i_center = rect_center (rect);
                GdkPoint j_center = rect_center (comp);
                GdkPoint diff = {j_center.x - i_center.x, j_center.y - i_center.y};

                // Prevent dividing by zero and non-movement
                if (diff.x == 0 && diff.y == 0)
                    diff.x = 1;

                // Approximate a vector of between 10px and 20px in magnitude in the same direction
                float length = sqrtf (diff.x * diff.x + diff.y * diff.y);
                diff.x = (int)floorf (diff.x * ACCURACY / length);
                diff.y = (int)floorf (diff.y * ACCURACY / length);
                // Move both windows apart
                rect.x += -diff.x;
                rect.y += -diff.y;
                comp.x += diff.x;
                comp.y += diff.y;

                // Try to keep the bounding rect the same aspect as the screen so that more
                // screen real estate is utilised. We do this by splitting the screen into nine
                // equal sections, if the window center is in any of the corner sections pull the
                // window towards the outer corner. If it is in any of the other edge sections
                // alternate between each corner on that edge. We don't want to determine it
                // randomly as it will not produce consistant locations when using the filter.
                // Only move one window so we don't cause large amounts of unnecessary zooming
                // in some situations. We need to do this even when expanding later just in case
                // all windows are the same size.
                // (We are using an old bounding rect for this, hopefully it doesn't matter)
                int x_section = (int)roundf ((rect.x - bounds.x) / (bounds.width / 3.0f));
                int y_section = (int)roundf ((comp.y - bounds.y) / (bounds.height / 3.0f));

                i_center = rect_center (rect);
                diff.x = 0;
                diff.y = 0;
                if (x_section != 1 || y_section != 1) { // Remove this if you want the center to pull as well
                    if (x_section == 1)
                        x_section = (directions[i] / 2 == 1 ? 2 : 0);
                    if (y_section == 1)
                        y_section = (directions[i] % 2 == 1 ? 2 : 0);
                }
                if (x_section == 0 && y_section == 0) {
                    diff.x = bounds.x - i_center.x;
                    diff.y = bounds.y - i_center.y;
                }
                if (x_section == 2 && y_section == 0) {
                    diff.x = bounds.x + bounds.width - i_center.x;
                    diff.y = bounds.y - i_center.y;
                }
                if (x_section == 2 && y_section == 2) {
                    diff.x = bounds.x + bounds.width - i_center.x;
                    diff.y = bounds.y + bounds.height - i_center.y;
                }
                if (x_section == 0 && y_section == 2) {
                    diff.x = bounds.x - i_center.x;
                    diff.y = bounds.y + bounds.height - i_center.y;
                }
                if (diff.x != 0 || diff.y != 0) {
                    length = sqrtf (diff.x * diff.x + diff.y * diff.y);
                    diff.x *= (int)floorf (ACCURACY / length / 2.0f);
                    diff.y *= (int)floorf (ACCURACY / length / 2.0f);
                    rect.x += diff.x;
                    rect.y += diff.y;
                }

                // Update bounding rect
                meta_rectangle_union(&bounds, &rect, &bounds);
                meta_rectangle_union(&bounds, &comp, &bounds);

                //we took copies from the rects from our list so we need to reassign them
                rects[i] = rect;
                rects[j] = comp;
            }
        }
    } while (overlap && loop_counter < MAX_TRANSLATIONS);

    // Work out scaling by getting the most top-left and most bottom-right window coords.
    float scale = fminf (fminf (area.width / (float)bounds.width, area.height / (float)bounds.height), 1.0f);

    // Make bounding rect fill the screen size for later steps
    bounds.x = (int)floorf (bounds.x - (area.width - bounds.width * scale) / 2);
    bounds.y = (int)floorf (bounds.y - (area.height - bounds.height * scale) / 2);
    bounds.width = (int)floorf (area.width / scale);
    bounds.height = (int)floorf (area.height / scale);

    // Move all windows back onto the screen and set their scale
    int index = 0;
    for (; index < clones->len; index++) {
        MetaRectangle rect = rects[index];
        rects[index] = (MetaRectangle){
            (int)floorf ((rect.x - bounds.x) * scale + area.x),
            (int)floorf ((rect.y - bounds.y) * scale + area.y),
            (int)floorf (rect.width * scale),
            (int)floorf (rect.height * scale)
        };
    }

    // fill gaps by enlarging windows
    gboolean moved = FALSE;
    MetaRectangle border = area;
    do {
        moved = FALSE;

        index = 0;
        for (; index < clones->len; index++) {
            MetaRectangle rect = rects[index];

            int width_diff = ACCURACY;
            int height_diff = (int)floorf ((((rect.width + width_diff) - rect.height) /
                        (float)rect.width) * rect.height);
            int x_diff = width_diff / 2;
            int y_diff = height_diff / 2;

            //top right
            MetaRectangle old = rect;
            rect = (MetaRectangle){ rect.x + x_diff, rect.y - y_diff - height_diff, rect.width + width_diff, rect.height + width_diff };
            if (rect_is_overlapping_any (rect, rects, clones->len, border))
                rect = old;
            else moved = TRUE;

            //bottom right
            old = rect;
            rect = (MetaRectangle){rect.x + x_diff, rect.y + y_diff, rect.width + width_diff, rect.height + width_diff};
            if (rect_is_overlapping_any (rect, rects, clones->len, border))
                rect = old;
            else moved = TRUE;

            //bottom left
            old = rect;
            rect = (MetaRectangle){rect.x - x_diff, rect.y + y_diff, rect.width + width_diff, rect.height + width_diff};
            if (rect_is_overlapping_any (rect, rects, clones->len, border))
                rect = old;
            else moved = TRUE;

            //top left
            old = rect;
            rect = (MetaRectangle){rect.x - x_diff, rect.y - y_diff - height_diff, rect.width + width_diff, rect.height + width_diff};
            if (rect_is_overlapping_any (rect, rects, clones->len, border))
                rect = old;
            else moved = TRUE;

            rects[index] = rect;
        }
    } while (moved);

    index = 0;
    for (; index < clones->len; index++) {
        MetaRectangle rect = rects[index];

        ClutterActor* clone = g_ptr_array_index(clones, index);
        MetaWindowActor* actor = META_WINDOW_ACTOR(clutter_clone_get_source(CLUTTER_CLONE(clone)));
        MetaWindow* window = meta_window_actor_get_meta_window(actor);

        MetaRectangle window_rect;
        meta_window_get_frame_rect(window, &window_rect);


        rect = rect_adjusted(rect, GAPS, GAPS, -GAPS, -GAPS);
        scale = rect.width / (float)window_rect.width;

        if (scale > 2.0 || (scale > 1.0 && (window_rect.width > 300 || window_rect.height > 300))) {
            scale = (window_rect.width > 300 || window_rect.height > 300) ? 1.0f : 2.0f;
            rect = (MetaRectangle){rect_center (rect).x - (int)floorf (window_rect.width * scale) / 2,
                rect_center (rect).y - (int)floorf (window_rect.height * scale) / 2,
                (int)floorf (window_rect.width * scale),
                (int)floorf (window_rect.height * scale)};
        }

        place_window(self, clone, rect);
    }

    g_free(directions);
    g_free(rects);
}