예제 #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);
}
예제 #2
0
static void
clutter_clone_dispose (GObject *gobject)
{
  clutter_clone_set_source_internal (CLUTTER_CLONE (gobject), NULL);

  G_OBJECT_CLASS (clutter_clone_parent_class)->dispose (gobject);
}
예제 #3
0
static void
clutter_clone_allocate (ClutterActor           *self,
                        const ClutterActorBox  *box,
                        ClutterAllocationFlags  flags)
{
  ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv;
  ClutterActorClass *parent_class;

  /* chain up */
  parent_class = CLUTTER_ACTOR_CLASS (clutter_clone_parent_class);
  parent_class->allocate (self, box, flags);

  if (priv->clone_source == NULL)
    return;

#if 0
  /* XXX - this is wrong: ClutterClone cannot clone unparented
   * actors, as it will break all invariants
   */

  /* we act like a "foster parent" for the source we are cloning;
   * if the source has not been parented we have to force an
   * allocation on it, so that we can paint it correctly from
   * within our paint() implementation. since the actor does not
   * have a parent, and thus it won't be painted by the usual
   * paint cycle, we can safely give it as much size as it requires
   */
  if (clutter_actor_get_parent (priv->clone_source) == NULL)
    clutter_actor_allocate_preferred_size (priv->clone_source, flags);
#endif
}
예제 #4
0
static void
clutter_clone_apply_transform (ClutterActor *self, CoglMatrix *matrix)
{
  ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv;
  ClutterActorBox box, source_box;
  gfloat x_scale, y_scale;

  /* First chain up and apply all the standard ClutterActor
   * transformations... */
  CLUTTER_ACTOR_CLASS (clutter_clone_parent_class)->apply_transform (self,
                                                                     matrix);

  /* if we don't have a source, nothing else to do */
  if (priv->clone_source == NULL)
    return;

  /* get our allocated size */
  clutter_actor_get_allocation_box (self, &box);

  /* and get the allocated size of the source */
  clutter_actor_get_allocation_box (priv->clone_source, &source_box);

  /* We need to scale what the clone-source actor paints to fill our own
   * allocation...
   */
  x_scale = clutter_actor_box_get_width (&box)
          / clutter_actor_box_get_width (&source_box);
  y_scale = clutter_actor_box_get_height (&box)
          / clutter_actor_box_get_height (&source_box);

  cogl_matrix_scale (matrix, x_scale, y_scale, x_scale);
}
예제 #5
0
static void overview_animated_destroy(MosesOverview* self, MosesOverviewQuitReason reason, gboolean animate)
{
    MosesOverviewPrivate* priv = self->priv;

    gboolean just_destroy = !animate;
    if (reason == MOSES_OV_REASON_ACTIVATE_WINDOW && !priv->selected_actor) {
        just_destroy = TRUE;
    } else if (reason == MOSES_OV_REASON_ACTIVATE_WORKSPACE && !priv->selected_workspace) {
        just_destroy = TRUE;
    } else if (reason == MOSES_OV_REASON_NORMAL) {
        just_destroy = TRUE;
    }

    if (just_destroy) {
        clutter_actor_destroy(CLUTTER_ACTOR(self));
        return;
    }

    gfloat x, y, w, h;
    ClutterActor* target = NULL;

    if (reason == MOSES_OV_REASON_ACTIVATE_WINDOW) {
        target = self->priv->selected_actor;

        ClutterActor* orig = clutter_clone_get_source(CLUTTER_CLONE(target));
        clutter_actor_get_position(orig, &x, &y);
        clutter_actor_get_size(orig, &w, &h);
        g_signal_handlers_disconnect_by_func(target, on_effect_complete, self);

    } else if (reason == MOSES_OV_REASON_ACTIVATE_WORKSPACE) {
        g_assert(priv->selected_actor == NULL);

        MetaScreen* screen = meta_plugin_get_screen(priv->plugin);
        target = overview_head_get_actor_for_workspace(priv->ov_head, priv->selected_workspace);

        MetaRectangle geom;
        int focused_monitor = meta_screen_get_current_monitor(screen);
        meta_screen_get_monitor_geometry(screen, focused_monitor, &geom);
        x = geom.x, y = geom.y, w = geom.width, h = geom.height;
    }

    if (target) {
        clutter_actor_remove_all_transitions(target);
        clutter_actor_set_child_above_sibling(clutter_actor_get_parent(target), target, NULL);

        clutter_actor_save_easing_state(target);
        clutter_actor_set_easing_mode(target, CLUTTER_LINEAR);
        clutter_actor_set_easing_duration(target, 150);

        clutter_actor_set_position(target, x, y);
        clutter_actor_set_scale(target, w / clutter_actor_get_width(target),
                h / clutter_actor_get_height(target));
        clutter_actor_restore_easing_state(target);
        g_object_connect(target, "signal::transitions-completed",
                G_CALLBACK(on_restore_position_effect_complete), self, NULL);
    }
}
예제 #6
0
static void prepare_workspace_content(MosesOverview *self, MetaWorkspace *ws)
{
    MosesOverviewPrivate* priv = self->priv;
    GList* l = meta_workspace_list_windows(ws);
    if (!priv->clones) { priv->clones = g_ptr_array_new(); }

    while (l) {
        MetaWindow* win = l->data;
        MetaWindowActor* win_actor = META_WINDOW_ACTOR(meta_window_get_compositor_private(win));

        if (meta_window_get_window_type(win) == META_WINDOW_DESKTOP) {
            g_debug("%s: got desktop actor", __func__);
            priv->background_actor = clutter_clone_new(CLUTTER_ACTOR(win_actor));

        } else if (meta_window_get_window_type(win) == META_WINDOW_NORMAL &&
                !meta_window_is_hidden(win)) {
            ClutterActor* clone = clutter_clone_new(CLUTTER_ACTOR(win_actor));
            clutter_actor_set_reactive(clone, TRUE);

            float x = 0.0, y = 0.0;
            clutter_actor_get_position(CLUTTER_ACTOR(win_actor), &x, &y);
            clutter_actor_set_position(clone, x, y);

            clutter_actor_hide(CLUTTER_ACTOR(win_actor));

            g_ptr_array_add(priv->clones, clone);
            clutter_actor_add_child(CLUTTER_ACTOR(self), clone);

            g_object_connect(clone,
                    "signal::transitions-completed", G_CALLBACK(on_effect_complete), self,
                    "signal::button-press-event", on_thumb_button_press, self,
                    "signal::enter-event", on_thumb_enter, self,
                    "signal::leave-event", on_thumb_leave, self,
                    NULL);
        }

        l = l->next;
    }

    ClutterColor clr = CLUTTER_COLOR_INIT(0xff, 0xff, 0xff, 0xff);
    clutter_actor_set_background_color(CLUTTER_ACTOR(self), &clr);

    if (priv->background_actor) {
#if 0
        ClutterEffect* blur = moses_blur_effect_new();
        clutter_actor_add_effect_with_name(priv->background_actor, "blur", blur);
        clutter_actor_insert_child_below(CLUTTER_ACTOR(self), priv->background_actor, NULL);
        clutter_actor_hide(clutter_clone_get_source(CLUTTER_CLONE(priv->background_actor)));
        clutter_actor_set_reactive(priv->background_actor, TRUE);
#endif
    }

    g_object_connect(priv->background_actor ? priv->background_actor: CLUTTER_ACTOR(self),
            "signal::button-press-event", on_bg_button_press, self,
            NULL);
}
예제 #7
0
static void
clutter_clone_paint (ClutterActor *self)
{
  ClutterClone *clone = CLUTTER_CLONE (self);
  ClutterClonePrivate *priv = clone->priv;
  ClutterGeometry geom, clone_geom;
  gfloat x_scale, y_scale;
  gboolean was_unmapped = FALSE;

  if (G_UNLIKELY (priv->clone_source == NULL))
    return;

  CLUTTER_NOTE (PAINT,
                "painting clone actor '%s'",
		clutter_actor_get_name (self) ? clutter_actor_get_name (self)
                                              : "unknown");

  /* get our allocated size */
  clutter_actor_get_allocation_geometry (self, &geom);

  /* and get the allocated size of the source */
  clutter_actor_get_allocation_geometry (priv->clone_source, &clone_geom);

  /* We need to scale what the clone-source actor paints to fill our own
   * allocation...
   */
  x_scale = (gfloat) geom.width  / clone_geom.width;
  y_scale = (gfloat) geom.height / clone_geom.height;

  cogl_scale (x_scale, y_scale, 1.0);

  /* The final bits of magic:
   * - We need to make sure that when the clone-source actor's paint
   *   method calls clutter_actor_get_paint_opacity, it traverses to
   *   us and our parent not it's real parent.
   * - We need to stop clutter_actor_paint applying the model view matrix of
   *   the clone source actor.
   */
  _clutter_actor_set_opacity_parent (priv->clone_source, self);
  _clutter_actor_set_enable_model_view_transform (priv->clone_source, FALSE);

  if (!CLUTTER_ACTOR_IS_MAPPED (priv->clone_source))
    {
      _clutter_actor_set_enable_paint_unmapped (priv->clone_source, TRUE);
      was_unmapped = TRUE;
    }

  clutter_actor_paint (priv->clone_source);

  if (was_unmapped)
    _clutter_actor_set_enable_paint_unmapped (priv->clone_source, FALSE);

  _clutter_actor_set_enable_model_view_transform (priv->clone_source, TRUE);
  _clutter_actor_set_opacity_parent (priv->clone_source, NULL);
}
예제 #8
0
static gboolean
clutter_clone_has_overlaps (ClutterActor *actor)
{
  ClutterClonePrivate *priv = CLUTTER_CLONE (actor)->priv;

  /* The clone has overlaps iff the source has overlaps */

  if (priv->clone_source == NULL)
    return FALSE;

  return clutter_actor_has_overlaps (priv->clone_source);
}
void
mnb_fancy_bin_set_child (MnbFancyBin *bin, ClutterActor *child)
{
  MnbFancyBinPrivate *priv = bin->priv;

  if (priv->real_child)
    {
      clutter_clone_set_source (CLUTTER_CLONE (priv->child), NULL);
      clutter_clone_set_source (CLUTTER_CLONE (priv->clone), NULL);
      clutter_actor_remove_child (clutter_actor_get_parent(priv->real_child), priv->real_child);
    }

  priv->real_child = child;

  if (priv->real_child)
    {
	  clutter_actor_add_child(CLUTTER_ACTOR (bin), priv->real_child);
      clutter_clone_set_source (CLUTTER_CLONE (priv->child), priv->real_child);
      clutter_clone_set_source (CLUTTER_CLONE (priv->clone), priv->real_child);
    }

  clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
}
예제 #10
0
static void
clutter_clone_paint (ClutterActor *actor)
{
  ClutterClone *self = CLUTTER_CLONE (actor);
  ClutterClonePrivate *priv = self->priv;
  gboolean was_unmapped = FALSE;

  if (priv->clone_source == NULL)
    return;

  CLUTTER_NOTE (PAINT, "painting clone actor '%s'",
                _clutter_actor_get_debug_name (actor));

  /* The final bits of magic:
   * - We need to override the paint opacity of the actor with our own
   *   opacity.
   * - We need to inform the actor that it's in a clone paint (for the function
   *   clutter_actor_is_in_clone_paint())
   * - We need to stop clutter_actor_paint applying the model view matrix of
   *   the clone source actor.
   */
  _clutter_actor_set_in_clone_paint (priv->clone_source, TRUE);
  _clutter_actor_set_opacity_override (priv->clone_source,
                                       clutter_actor_get_paint_opacity (actor));
  _clutter_actor_set_enable_model_view_transform (priv->clone_source, FALSE);

  if (!CLUTTER_ACTOR_IS_MAPPED (priv->clone_source))
    {
      _clutter_actor_set_enable_paint_unmapped (priv->clone_source, TRUE);
      was_unmapped = TRUE;
    }

  _clutter_actor_push_clone_paint ();
  clutter_actor_paint (priv->clone_source);
  _clutter_actor_pop_clone_paint ();

  if (was_unmapped)
    _clutter_actor_set_enable_paint_unmapped (priv->clone_source, FALSE);

  _clutter_actor_set_enable_model_view_transform (priv->clone_source, TRUE);
  _clutter_actor_set_opacity_override (priv->clone_source, -1);
  _clutter_actor_set_in_clone_paint (priv->clone_source, FALSE);
}
예제 #11
0
static void
clutter_clone_set_property (GObject      *gobject,
                            guint         prop_id,
                            const GValue *value,
                            GParamSpec   *pspec)
{
  ClutterClone *self = CLUTTER_CLONE (gobject);

  switch (prop_id)
    {
    case PROP_SOURCE:
      clutter_clone_set_source (self, g_value_get_object (value));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
      break;
  }
}
예제 #12
0
static void
clutter_clone_get_property (GObject    *gobject,
                            guint       prop_id,
                            GValue     *value,
                            GParamSpec *pspec)
{
  ClutterClonePrivate *priv = CLUTTER_CLONE (gobject)->priv;

  switch (prop_id)
    {
    case PROP_SOURCE:
      g_value_set_object (value, priv->clone_source);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
      break;
    }
}
예제 #13
0
static gboolean
clutter_clone_get_paint_volume (ClutterActor       *actor,
                                ClutterPaintVolume *volume)
{
  ClutterClonePrivate *priv = CLUTTER_CLONE (actor)->priv;
  const ClutterPaintVolume *source_volume;

  /* if the source is not set the paint volume is defined to be empty */
  if (priv->clone_source == NULL)
    return TRUE;

  /* query the volume of the source actor and simply masquarade it as
   * the clones volume... */
  source_volume = clutter_actor_get_paint_volume (priv->clone_source);
  if (source_volume == NULL)
    return FALSE;

  _clutter_paint_volume_set_from_volume (volume, source_volume);
  _clutter_paint_volume_set_reference_actor (volume, actor);

  return TRUE;
}
예제 #14
0
static void
clutter_clone_get_preferred_height (ClutterActor *self,
                                    gfloat        for_width,
                                    gfloat       *min_height_p,
                                    gfloat       *natural_height_p)
{
  ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv;
  ClutterActor *clone_source = priv->clone_source;

  if (clone_source == NULL)
    {
      if (min_height_p)
        *min_height_p = 0;

      if (natural_height_p)
        *natural_height_p = 0;
    }
  else
    clutter_actor_get_preferred_height (clone_source,
                                        for_width,
                                        min_height_p,
                                        natural_height_p);
}
예제 #15
0
static void _clone_paint_cb (ClutterActor *actor) {
    ClutterActor *source;
    ClutterActorBox box;
    CoglHandle material;
    gfloat width, height;
    guint8 opacity;
    CoglColor color_1, color_2;
    CoglTextureVertex vertices[4];

    /* if we don't have a source actor, don't paint */
    source = clutter_clone_get_source (CLUTTER_CLONE (actor));
    if (source == NULL)
        goto out;

    /* if the source texture does not have any content, don't paint */
    material = clutter_texture_get_cogl_material (CLUTTER_TEXTURE (source));
    if (material == NULL)
        goto out;

    /* get the size of the reflection */
    clutter_actor_get_allocation_box (actor, &box);
    clutter_actor_box_get_size (&box, &width, &height);

    /* get the composite opacity of the actor */
    opacity = clutter_actor_get_paint_opacity (actor);

    /* figure out the two colors for the reflection: the first is
     * full color and the second is the same, but at 0 opacity
     */
    cogl_color_init_from_4f (&color_1, 1.0, 1.0, 1.0, opacity / 255.0);
    cogl_color_premultiply (&color_1);
    cogl_color_init_from_4f (&color_2, 1.0, 1.0, 1.0, 0.0);
    cogl_color_premultiply (&color_2);

    /* now describe the four vertices of the quad; since it has
     * to be a reflection, we need to invert it as well
     */
    vertices[0].x = 0; vertices[0].y = 0; vertices[0].z = 0;
    vertices[0].tx = 0.0; vertices[0].ty = 1.0;
    vertices[0].color = color_1;

    vertices[1].x = width; vertices[1].y = 0; vertices[1].z = 0;
    vertices[1].tx = 1.0; vertices[1].ty = 1.0;
    vertices[1].color = color_1;

    vertices[2].x = width; vertices[2].y = height; vertices[2].z = 0;
    vertices[2].tx = 1.0; vertices[2].ty = 0.0;
    vertices[2].color = color_2;

    vertices[3].x = 0; vertices[3].y = height; vertices[3].z = 0;
    vertices[3].tx = 0.0; vertices[3].ty = 0.0;
    vertices[3].color = color_2;

    /* paint the same texture but with a different geometry */
    cogl_set_source (material);
    cogl_polygon (vertices, 4, TRUE);

    out:
      /* prevent the default clone handler from running */
      g_signal_stop_emission_by_name (actor, "paint");
}
예제 #16
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);
}
예제 #17
0
static void
clutter_reflect_texture_paint (ClutterActor *actor)
{
  ClutterReflectTexturePrivate *priv;
  ClutterReflectTexture *texture;
  ClutterClone          *clone;
  ClutterTexture        *parent;
  guint                  width, height;
  gfloat                 fwidth, fheight;
  gint                   r_height;
  gint                   opacity;
  gint                   bottom;

  CoglHandle        cogl_texture;
  CoglTextureVertex tvert[4];
  CoglFixed      rty;

  texture = CLUTTER_REFLECT_TEXTURE (actor);
  clone = CLUTTER_CLONE (actor);

  parent = (ClutterTexture*) clutter_clone_get_source (clone);
  if (!parent) 
    return;
  
  if (!CLUTTER_ACTOR_IS_REALIZED (parent))
    clutter_actor_realize (CLUTTER_ACTOR (parent));

  cogl_texture = clutter_texture_get_cogl_texture (parent);
  if (cogl_texture == COGL_INVALID_HANDLE)
    return;

  priv = texture->priv;

  clutter_actor_get_size (CLUTTER_ACTOR(parent), &fwidth, &fheight);
  width = fwidth;
  height = fheight;
      
  if (!height)
      // probably won't happen, but just in case, to avoid divide by zero.
      return;

  r_height = priv->reflection_height;
  bottom = priv->reflect_bottom;
  opacity = clutter_actor_get_opacity(actor);

  if (r_height < 0 || r_height > height)
    r_height = height;

#define FX(x) COGL_FIXED_FROM_INT(x)

  rty = COGL_FIXED_FAST_DIV(FX(bottom ? height-r_height : r_height),FX(height));

  /* clockise vertices and tex coords and colors! */

  tvert[0].x = tvert[0].y = tvert[0].z = 0;
  tvert[0].tx = 0; tvert[0].ty = bottom ? COGL_FIXED_1 : rty;
  tvert[0].color.red = tvert[0].color.green = tvert[0].color.blue = 0xff;
  tvert[0].color.alpha = bottom ? opacity : 0;

  tvert[1].x = FX(width); tvert[1].y = tvert[1].z = 0;
  tvert[1].tx = COGL_FIXED_1; tvert[1].ty = bottom ? COGL_FIXED_1 : rty;
  tvert[1].color.red = tvert[1].color.green = tvert[1].color.blue = 0xff;
  tvert[1].color.alpha = bottom ? opacity : 0;

  tvert[2].x = FX(width); tvert[2].y = FX(r_height); tvert[2].z = 0;
  tvert[2].tx = COGL_FIXED_1; tvert[2].ty = bottom ? rty : 0;
  tvert[2].color.red = tvert[2].color.green = tvert[2].color.blue = 0xff;
  tvert[2].color.alpha = bottom ? 0 : opacity;

  tvert[3].x = 0; tvert[3].y = FX(r_height); tvert[3].z = 0;
  tvert[3].tx = 0; tvert[3].ty = bottom ? rty : 0;
  tvert[3].color.red = tvert[3].color.green = tvert[3].color.blue = 0xff;
  tvert[3].color.alpha = bottom ? 0 : opacity;

  cogl_push_matrix ();

  cogl_set_source_texture(cogl_texture);
  /* FIXME: this does not work as expected */
  /* cogl_polygon(tvert, 4, TRUE); */
  
  cogl_pop_matrix ();
}
예제 #18
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);
}