Example #1
0
static gint
northwestcmp (gconstpointer a, gconstpointer b)
{
  MetaWindow *aw = (gpointer) a;
  MetaWindow *bw = (gpointer) b;
  MetaRectangle a_frame;
  MetaRectangle b_frame;
  int from_origin_a;
  int from_origin_b;
  int ax, ay, bx, by;

  meta_window_get_frame_rect (aw, &a_frame);
  meta_window_get_frame_rect (bw, &b_frame);
  ax = a_frame.x;
  ay = a_frame.y;
  bx = b_frame.x;
  by = b_frame.y;

  /* probably there's a fast good-enough-guess we could use here. */
  from_origin_a = sqrt (ax * ax + ay * ay);
  from_origin_b = sqrt (bx * bx + by * by);

  if (from_origin_a < from_origin_b)
    return -1;
  else if (from_origin_a > from_origin_b)
    return 1;
  else
    return 0;
}
static void
meta_surface_actor_x11_process_damage (MetaSurfaceActor *actor,
                                       int x, int y, int width, int height)
{
  MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor);
  MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self);

  priv->received_damage = TRUE;

  if (meta_window_is_fullscreen (priv->window) && !priv->unredirected && !priv->does_full_damage)
    {
      MetaRectangle window_rect;
      meta_window_get_frame_rect (priv->window, &window_rect);

      if (x == 0 &&
          y == 0 &&
          window_rect.width == width &&
          window_rect.height == height)
        priv->full_damage_frames_count++;
      else
        priv->full_damage_frames_count = 0;

      if (priv->full_damage_frames_count >= 100)
        priv->does_full_damage = TRUE;
    }

  if (!is_visible (self))
    return;

  cogl_texture_pixmap_x11_update_area (priv->texture, x, y, width, height);
}
Example #3
0
static gboolean
window_overlaps_focus_window (MetaWindow *window)
{
  MetaWindow *focus_window;
  MetaRectangle window_frame, focus_frame, overlap;

  focus_window = window->display->focus_window;
  if (focus_window == NULL)
    return FALSE;

  meta_window_get_frame_rect (window, &window_frame);
  meta_window_get_frame_rect (focus_window, &focus_frame);

  return meta_rectangle_intersect (&window_frame,
                                   &focus_frame,
                                   &overlap);
}
Example #4
0
static gboolean
window_contains_point (MetaWindow *window,
                       int         root_x,
                       int         root_y)
{
  MetaRectangle rect;

  meta_window_get_frame_rect (window, &rect);

  return POINT_IN_RECT (root_x, root_y, rect);
}
Example #5
0
static gint
topmost_cmp (gconstpointer a, gconstpointer b)
{
  MetaWindow *aw = (gpointer) a;
  MetaWindow *bw = (gpointer) b;
  MetaRectangle a_frame;
  MetaRectangle b_frame;
  int ay, by;

  meta_window_get_frame_rect (aw, &a_frame);
  meta_window_get_frame_rect (bw, &b_frame);
  ay = a_frame.y;
  by = b_frame.y;

  if (ay < by)
    return -1;
  else if (ay > by)
    return 1;
  else
    return 0;
}
Example #6
0
static gint
leftmost_cmp (gconstpointer a, gconstpointer b)
{
  MetaWindow *aw = (gpointer) a;
  MetaWindow *bw = (gpointer) b;
  MetaRectangle a_frame;
  MetaRectangle b_frame;
  int ax, bx;

  meta_window_get_frame_rect (aw, &a_frame);
  meta_window_get_frame_rect (bw, &b_frame);
  ax = a_frame.x;
  bx = b_frame.x;

  if (ax < bx)
    return -1;
  else if (ax > bx)
    return 1;
  else
    return 0;
}
Example #7
0
static gboolean
rectangle_overlaps_some_window (MetaRectangle *rect,
                                GList         *windows)
{
  GList *tmp;
  MetaRectangle dest;

  tmp = windows;
  while (tmp != NULL)
    {
      MetaWindow *other = tmp->data;
      MetaRectangle other_rect;

      switch (other->type)
        {
        case META_WINDOW_DOCK:
        case META_WINDOW_SPLASHSCREEN:
        case META_WINDOW_DESKTOP:
        case META_WINDOW_DIALOG:
        case META_WINDOW_MODAL_DIALOG:
	/* override redirect window types: */
	case META_WINDOW_DROPDOWN_MENU:
	case META_WINDOW_POPUP_MENU:
	case META_WINDOW_TOOLTIP:
	case META_WINDOW_NOTIFICATION:
	case META_WINDOW_COMBO:
	case META_WINDOW_DND:
	case META_WINDOW_OVERRIDE_OTHER:
          break;

        case META_WINDOW_NORMAL:
        case META_WINDOW_UTILITY:
        case META_WINDOW_TOOLBAR:
        case META_WINDOW_MENU:
          meta_window_get_frame_rect (other, &other_rect);

          if (meta_rectangle_intersect (rect, &other_rect, &dest))
            return TRUE;
          break;
        }

      tmp = tmp->next;
    }

  return FALSE;
}
Example #8
0
/**
 * meta_shape_cow_for_window:
 * @compositor: A #MetaCompositor
 * @window: (nullable): A #MetaWindow to shape the COW for
 *
 * Sets an bounding shape on the COW so that the given window
 * is exposed. If @window is %NULL it clears the shape again.
 *
 * Used so we can unredirect windows, by shaping away the part
 * of the COW, letting the raw window be seen through below.
 */
static void
meta_shape_cow_for_window (MetaCompositor *compositor,
                           MetaWindow *window)
{
  MetaDisplay *display = compositor->display;
  Display *xdisplay = meta_display_get_xdisplay (display);

  if (window == NULL)
    XFixesSetWindowShapeRegion (xdisplay, compositor->output, ShapeBounding, 0, 0, None);
  else
    {
      XserverRegion output_region;
      XRectangle screen_rect, window_bounds;
      int width, height;
      MetaRectangle rect;

      meta_window_get_frame_rect (window, &rect);

      window_bounds.x = rect.x;
      window_bounds.y = rect.y;
      window_bounds.width = rect.width;
      window_bounds.height = rect.height;

      meta_screen_get_size (display->screen, &width, &height);
      screen_rect.x = 0;
      screen_rect.y = 0;
      screen_rect.width = width;
      screen_rect.height = height;

      output_region = XFixesCreateRegion (xdisplay, &window_bounds, 1);

      XFixesInvertRegion (xdisplay, output_region, &screen_rect, output_region);
      XFixesSetWindowShapeRegion (xdisplay, compositor->output, ShapeBounding, 0, 0, output_region);
      XFixesDestroyRegion (xdisplay, output_region);
    }
}
Example #9
0
static void
find_next_cascade (MetaWindow *window,
                   /* visible windows on relevant workspaces */
                   GList      *windows,
                   int         x,
                   int         y,
                   int        *new_x,
                   int        *new_y)
{
  GList *tmp;
  GList *sorted;
  int cascade_x, cascade_y;
  MetaRectangle titlebar_rect;
  int x_threshold, y_threshold;
  MetaRectangle frame_rect;
  int window_width, window_height;
  int cascade_stage;
  MetaRectangle work_area;
  int current;

  sorted = g_list_copy (windows);
  sorted = g_list_sort (sorted, northwestcmp);

  /* This is a "fuzzy" cascade algorithm.
   * For each window in the list, we find where we'd cascade a
   * new window after it. If a window is already nearly at that
   * position, we move on.
   */

  /* arbitrary-ish threshold, honors user attempts to
   * manually cascade.
   */
#define CASCADE_FUZZ 15
  meta_window_get_titlebar_rect (window, &titlebar_rect);
  x_threshold = MAX (titlebar_rect.x, CASCADE_FUZZ);
  y_threshold = MAX (titlebar_rect.y, CASCADE_FUZZ);

  /* Find furthest-SE origin of all workspaces.
   * cascade_x, cascade_y are the target position
   * of NW corner of window frame.
   */

  current = meta_screen_get_current_monitor (window->screen);
  meta_window_get_work_area_for_monitor (window, current, &work_area);

  cascade_x = MAX (0, work_area.x);
  cascade_y = MAX (0, work_area.y);

  /* Find first cascade position that's not used. */

  meta_window_get_frame_rect (window, &frame_rect);
  window_width = frame_rect.width;
  window_height = frame_rect.height;

  cascade_stage = 0;
  tmp = sorted;
  while (tmp != NULL)
    {
      MetaWindow *w;
      MetaRectangle w_frame_rect;
      int wx, wy;

      w = tmp->data;

      /* we want frame position, not window position */
      meta_window_get_frame_rect (w, &w_frame_rect);
      wx = w_frame_rect.x;
      wy = w_frame_rect.y;

      if (ABS (wx - cascade_x) < x_threshold &&
          ABS (wy - cascade_y) < y_threshold)
        {
          meta_window_get_titlebar_rect (w, &titlebar_rect);

          /* Cascade the window evenly by the titlebar height; this isn't a typo. */
          cascade_x = wx + titlebar_rect.height;
          cascade_y = wy + titlebar_rect.height;

          /* If we go off the screen, start over with a new cascade */
	  if (((cascade_x + window_width) >
               (work_area.x + work_area.width)) ||
              ((cascade_y + window_height) >
	       (work_area.y + work_area.height)))
	    {
	      cascade_x = MAX (0, work_area.x);
	      cascade_y = MAX (0, work_area.y);

#define CASCADE_INTERVAL 50 /* space between top-left corners of cascades */
              cascade_stage += 1;
	      cascade_x += CASCADE_INTERVAL * cascade_stage;

	      /* start over with a new cascade translated to the right, unless
               * we are out of space
               */
              if ((cascade_x + window_width) <
                  (work_area.x + work_area.width))
                {
                  tmp = sorted;
                  continue;
                }
              else
                {
                  /* All out of space, this cascade_x won't work */
                  cascade_x = MAX (0, work_area.x);
                  break;
                }
	    }
        }
      else
        {
          /* Keep searching for a further-down-the-diagonal window. */
        }

      tmp = tmp->next;
    }

  /* cascade_x and cascade_y will match the last window in the list
   * that was "in the way" (in the approximate cascade diagonal)
   */

  g_list_free (sorted);

  *new_x = cascade_x;
  *new_y = cascade_y;
}
Example #10
0
void
meta_window_place (MetaWindow        *window,
                   int                x,
                   int                y,
                   int               *new_x,
                   int               *new_y)
{
  GList *windows = NULL;
  const MetaMonitorInfo *xi;

  meta_topic (META_DEBUG_PLACEMENT, "Placing window %s\n", window->desc);

  switch (window->type)
    {
      /* Run placement algorithm on these. */
    case META_WINDOW_NORMAL:
    case META_WINDOW_DIALOG:
    case META_WINDOW_MODAL_DIALOG:
    case META_WINDOW_SPLASHSCREEN:
      break;

      /* Assume the app knows best how to place these, no placement
       * algorithm ever (other than "leave them as-is")
       */
    case META_WINDOW_DESKTOP:
    case META_WINDOW_DOCK:
    case META_WINDOW_TOOLBAR:
    case META_WINDOW_MENU:
    case META_WINDOW_UTILITY:
    /* override redirect window types: */
    case META_WINDOW_DROPDOWN_MENU:
    case META_WINDOW_POPUP_MENU:
    case META_WINDOW_TOOLTIP:
    case META_WINDOW_NOTIFICATION:
    case META_WINDOW_COMBO:
    case META_WINDOW_DND:
    case META_WINDOW_OVERRIDE_OTHER:
      goto done;
    }

  if (meta_prefs_get_disable_workarounds ())
    {
      switch (window->type)
        {
          /* Only accept USPosition on normal windows because the app is full
           * of shit claiming the user set -geometry for a dialog or dock
           */
        case META_WINDOW_NORMAL:
          if (window->size_hints.flags & USPosition)
            {
              /* don't constrain with placement algorithm */
              meta_topic (META_DEBUG_PLACEMENT,
                          "Honoring USPosition for %s instead of using placement algorithm\n", window->desc);

              goto done;
            }
          break;

          /* Ignore even USPosition on dialogs, splashscreen */
        case META_WINDOW_DIALOG:
        case META_WINDOW_MODAL_DIALOG:
        case META_WINDOW_SPLASHSCREEN:
          break;

          /* Assume the app knows best how to place these. */
        case META_WINDOW_DESKTOP:
        case META_WINDOW_DOCK:
        case META_WINDOW_TOOLBAR:
        case META_WINDOW_MENU:
        case META_WINDOW_UTILITY:
	/* override redirect window types: */
	case META_WINDOW_DROPDOWN_MENU:
	case META_WINDOW_POPUP_MENU:
	case META_WINDOW_TOOLTIP:
	case META_WINDOW_NOTIFICATION:
	case META_WINDOW_COMBO:
	case META_WINDOW_DND:
	case META_WINDOW_OVERRIDE_OTHER:
          if (window->size_hints.flags & PPosition)
            {
              meta_topic (META_DEBUG_PLACEMENT,
                          "Not placing non-normal non-dialog window with PPosition set\n");
              goto done;
            }
          break;
        }
    }
  else
    {
      /* workarounds enabled */

      if ((window->size_hints.flags & PPosition) ||
          (window->size_hints.flags & USPosition))
        {
          meta_topic (META_DEBUG_PLACEMENT,
                      "Not placing window with PPosition or USPosition set\n");
          avoid_being_obscured_as_second_modal_dialog (window, &x, &y);
          goto done;
        }
    }

  if (window->type == META_WINDOW_DIALOG ||
      window->type == META_WINDOW_MODAL_DIALOG)
    {
      MetaWindow *parent = meta_window_get_transient_for (window);

      if (parent)
        {
          MetaRectangle frame_rect, parent_frame_rect;

          meta_window_get_frame_rect (window, &frame_rect);
          meta_window_get_frame_rect (parent, &parent_frame_rect);

          y = parent_frame_rect.y;

          /* center of parent */
          x = parent_frame_rect.x + parent_frame_rect.width / 2;
          /* center of child over center of parent */
          x -= frame_rect.width / 2;

          /* "visually" center window over parent, leaving twice as
           * much space below as on top.
           */
          y += (parent_frame_rect.height - frame_rect.height)/3;

          meta_topic (META_DEBUG_PLACEMENT, "Centered window %s over transient parent\n",
                      window->desc);

          avoid_being_obscured_as_second_modal_dialog (window, &x, &y);

          goto done;
        }
    }

  /* FIXME UTILITY with transient set should be stacked up
   * on the sides of the parent window or something.
   */

  if (window_place_centered (window))
    {
      /* Center on current monitor */
      int w, h;
      MetaRectangle frame_rect;

      meta_window_get_frame_rect (window, &frame_rect);

      /* Warning, this function is a round trip! */
      xi = meta_screen_get_current_monitor_info (window->screen);

      w = xi->rect.width;
      h = xi->rect.height;

      x = (w - frame_rect.width) / 2;
      y = (h - frame_rect.height) / 2;

      x += xi->rect.x;
      y += xi->rect.y;

      meta_topic (META_DEBUG_PLACEMENT, "Centered window %s on screen %d monitor %d\n",
                  window->desc, window->screen->number, xi->number);

      goto done_check_denied_focus;
    }

  /* Find windows that matter (not minimized, on same workspace
   * as placed window, may be shaded - if shaded we pretend it isn't
   * for placement purposes)
   */
  {
    GSList *all_windows;
    GSList *tmp;

    all_windows = meta_display_list_windows (window->display, META_LIST_DEFAULT);

    tmp = all_windows;
    while (tmp != NULL)
      {
        MetaWindow *w = tmp->data;

        if (w != window &&
            meta_window_showing_on_its_workspace (w) &&
            meta_window_located_on_workspace (w, window->workspace))
          windows = g_list_prepend (windows, w);

        tmp = tmp->next;
      }

    g_slist_free (all_windows);
  }

  /* Warning, this is a round trip! */
  xi = meta_screen_get_current_monitor_info (window->screen);

  /* Maximize windows if they are too big for their work area (bit of
   * a hack here). Assume undecorated windows probably don't intend to
   * be maximized.
   */
  if (window->has_maximize_func && window->decorated &&
      !window->fullscreen)
    {
      MetaRectangle workarea;
      MetaRectangle frame_rect;

      meta_window_get_work_area_for_monitor (window,
                                             xi->number,
                                             &workarea);
      meta_window_get_frame_rect (window, &frame_rect);

      /* If the window is bigger than the screen, then automaximize.  Do NOT
       * auto-maximize the directions independently.  See #419810.
       */
      if (frame_rect.width >= workarea.width && frame_rect.height >= workarea.height)
        {
          window->maximize_horizontally_after_placement = TRUE;
          window->maximize_vertically_after_placement = TRUE;
        }
    }

  /* "Origin" placement algorithm */
  x = xi->rect.x;
  y = xi->rect.y;

  if (find_first_fit (window, windows,
                      xi->number,
                      x, y, &x, &y))
    goto done_check_denied_focus;

  /* No good fit? Fall back to cascading... */
  find_next_cascade (window, windows, x, y, &x, &y);

 done_check_denied_focus:
  /* If the window is being denied focus and isn't a transient of the
   * focus window, we do NOT want it to overlap with the focus window
   * if at all possible.  This is guaranteed to only be called if the
   * focus_window is non-NULL, and we try to avoid that window.
   */
  if (window->denied_focus_and_not_transient)
    {
      MetaWindow    *focus_window;
      gboolean       found_fit;

      focus_window = window->display->focus_window;
      g_assert (focus_window != NULL);

      /* No need to do anything if the window doesn't overlap at all */
      found_fit = !window_overlaps_focus_window (window);

      /* Try to do a first fit again, this time only taking into account the
       * focus window.
       */
      if (!found_fit)
        {
          GList *focus_window_list;
          focus_window_list = g_list_prepend (NULL, focus_window);

          /* Reset x and y ("origin" placement algorithm) */
          x = xi->rect.x;
          y = xi->rect.y;

          found_fit = find_first_fit (window, focus_window_list,
                                      xi->number,
                                      x, y, &x, &y);
          g_list_free (focus_window_list);
	}

      /* If that still didn't work, just place it where we can see as much
       * as possible.
       */
      if (!found_fit)
        find_most_freespace (window, focus_window, x, y, &x, &y);
    }

 done:
  if (windows)
    g_list_free (windows);

  *new_x = x;
  *new_y = y;
}
Example #11
0
/* Find the leftmost, then topmost, empty area on the workspace
 * that can contain the new window.
 *
 * Cool feature to have: if we can't fit the current window size,
 * try shrinking the window (within geometry constraints). But
 * beware windows such as Emacs with no sane minimum size, we
 * don't want to create a 1x1 Emacs.
 */
static gboolean
find_first_fit (MetaWindow *window,
                /* visible windows on relevant workspaces */
                GList      *windows,
		int         monitor,
                int         x,
                int         y,
                int        *new_x,
                int        *new_y)
{
  /* This algorithm is limited - it just brute-force tries
   * to fit the window in a small number of locations that are aligned
   * with existing windows. It tries to place the window on
   * the bottom of each existing window, and then to the right
   * of each existing window, aligned with the left/top of the
   * existing window in each of those cases.
   */
  int retval;
  GList *below_sorted;
  GList *right_sorted;
  GList *tmp;
  MetaRectangle rect;
  MetaRectangle work_area;

  retval = FALSE;

  /* Below each window */
  below_sorted = g_list_copy (windows);
  below_sorted = g_list_sort (below_sorted, leftmost_cmp);
  below_sorted = g_list_sort (below_sorted, topmost_cmp);

  /* To the right of each window */
  right_sorted = g_list_copy (windows);
  right_sorted = g_list_sort (right_sorted, topmost_cmp);
  right_sorted = g_list_sort (right_sorted, leftmost_cmp);

  meta_window_get_frame_rect (window, &rect);

#ifdef WITH_VERBOSE_MODE
  {
    char monitor_location_string[RECT_LENGTH];
    meta_rectangle_to_string (&window->screen->monitor_infos[monitor].rect,
                              monitor_location_string);
    meta_topic (META_DEBUG_XINERAMA,
                "Natural monitor is %s\n",
                monitor_location_string);
  }
#endif

  meta_window_get_work_area_for_monitor (window, monitor, &work_area);

  center_tile_rect_in_area (&rect, &work_area);

  if (meta_rectangle_contains_rect (&work_area, &rect) &&
      !rectangle_overlaps_some_window (&rect, windows))
    {
      *new_x = rect.x;
      *new_y = rect.y;

      retval = TRUE;

      goto out;
    }

  /* try below each window */
  tmp = below_sorted;
  while (tmp != NULL)
    {
      MetaWindow *w = tmp->data;
      MetaRectangle frame_rect;

      meta_window_get_frame_rect (w, &frame_rect);

      rect.x = frame_rect.x;
      rect.y = frame_rect.y + frame_rect.height;

      if (meta_rectangle_contains_rect (&work_area, &rect) &&
          !rectangle_overlaps_some_window (&rect, below_sorted))
        {
          *new_x = rect.x;
          *new_y = rect.y;

          retval = TRUE;

          goto out;
        }

      tmp = tmp->next;
    }

  /* try to the right of each window */
  tmp = right_sorted;
  while (tmp != NULL)
    {
      MetaWindow *w = tmp->data;
      MetaRectangle frame_rect;

      meta_window_get_frame_rect (w, &frame_rect);

      rect.x = frame_rect.x + frame_rect.width;
      rect.y = frame_rect.y;

      if (meta_rectangle_contains_rect (&work_area, &rect) &&
          !rectangle_overlaps_some_window (&rect, right_sorted))
        {
          *new_x = rect.x;
          *new_y = rect.y;

          retval = TRUE;

          goto out;
        }

      tmp = tmp->next;
    }

 out:
  g_list_free (below_sorted);
  g_list_free (right_sorted);
  return retval;
}
Example #12
0
static void
find_most_freespace (MetaWindow *window,
                     /* visible windows on relevant workspaces */
                     MetaWindow *focus_window,
                     int         x,
                     int         y,
                     int        *new_x,
                     int        *new_y)
{
  MetaWindowDirection side;
  int max_area;
  int max_width, max_height, left, right, top, bottom;
  int left_space, right_space, top_space, bottom_space;
  MetaRectangle work_area;
  MetaRectangle avoid;
  MetaRectangle frame_rect;

  meta_window_get_work_area_current_monitor (focus_window, &work_area);
  meta_window_get_frame_rect (focus_window, &avoid);
  meta_window_get_frame_rect (window, &frame_rect);

  /* Find the areas of choosing the various sides of the focus window */
  max_width  = MIN (avoid.width, frame_rect.width);
  max_height = MIN (avoid.height, frame_rect.height);
  left_space   = avoid.x - work_area.x;
  right_space  = work_area.width - (avoid.x + avoid.width - work_area.x);
  top_space    = avoid.y - work_area.y;
  bottom_space = work_area.height - (avoid.y + avoid.height - work_area.y);
  left   = MIN (left_space,   frame_rect.width);
  right  = MIN (right_space,  frame_rect.width);
  top    = MIN (top_space,    frame_rect.height);
  bottom = MIN (bottom_space, frame_rect.height);

  /* Find out which side of the focus_window can show the most of the window */
  side = META_LEFT;
  max_area = left*max_height;
  if (right*max_height > max_area)
    {
      side = META_RIGHT;
      max_area = right*max_height;
    }
  if (top*max_width > max_area)
    {
      side = META_TOP;
      max_area = top*max_width;
    }
  if (bottom*max_width > max_area)
    {
      side = META_BOTTOM;
      max_area = bottom*max_width;
    }

  /* Give up if there's no where to put it (i.e. focus window is maximized) */
  if (max_area == 0)
    return;

  /* Place the window on the relevant side; if the whole window fits,
   * make it adjacent to the focus window; if not, make sure the
   * window doesn't go off the edge of the screen.
   */
  switch (side)
    {
    case META_LEFT:
      *new_y = avoid.y;
      if (left_space > frame_rect.width)
        *new_x = avoid.x - frame_rect.width;
      else
        *new_x = work_area.x;
      break;
    case META_RIGHT:
      *new_y = avoid.y;
      if (right_space > frame_rect.width)
        *new_x = avoid.x + avoid.width;
      else
        *new_x = work_area.x + work_area.width - frame_rect.width;
      break;
    case META_TOP:
      *new_x = avoid.x;
      if (top_space > frame_rect.height)
        *new_y = avoid.y - frame_rect.height;
      else
        *new_y = work_area.y;
      break;
    case META_BOTTOM:
      *new_x = avoid.x;
      if (bottom_space > frame_rect.height)
        *new_y = avoid.y + avoid.height;
      else
        *new_y = work_area.y + work_area.height - frame_rect.height;
      break;
    }
}
Example #13
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);
}
Example #14
0
/**
 * shell_screenshot_screenshot_window:
 * @screenshot: the #ShellScreenshot
 * @include_frame: Whether to include the frame or not
 * @include_cursor: Whether to include the cursor or not
 * @filename: The filename for the screenshot
 * @callback: (scope async): function to call returning success or failure
 * of the async grabbing
 *
 * Takes a screenshot of the focused window (optionally omitting the frame)
 * in @filename as png image.
 *
 */
void
shell_screenshot_screenshot_window (ShellScreenshot *screenshot,
                                    gboolean include_frame,
                                    gboolean include_cursor,
                                    const char *filename,
                                    ShellScreenshotCallback callback)
{
  GSimpleAsyncResult *result;
  GSettings *settings;

  _screenshot_data *screenshot_data = g_new0 (_screenshot_data, 1);

  MetaScreen *screen = shell_global_get_screen (screenshot->global);
  MetaCursorTracker *tracker;
  MetaDisplay *display = meta_screen_get_display (screen);
  MetaWindow *window = meta_display_get_focus_window (display);
  ClutterActor *window_actor;
  gfloat actor_x, actor_y;
  MetaShapedTexture *stex;
  MetaRectangle rect;
  cairo_rectangle_int_t clip;

  screenshot_data->screenshot = g_object_ref (screenshot);
  screenshot_data->filename = g_strdup (filename);
  screenshot_data->callback = callback;

  if (!window)
    {
      screenshot_data->filename_used = g_strdup ("");
      result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, shell_screenshot_screenshot_window);
      g_simple_async_result_set_op_res_gboolean (result, FALSE);
      g_simple_async_result_complete (result);
      g_object_unref (result);

      return;
    }

  window_actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window));
  clutter_actor_get_position (window_actor, &actor_x, &actor_y);

  meta_window_get_frame_rect (window, &rect);

  if (!include_frame)
    meta_window_frame_rect_to_client_rect (window, &rect, &rect);

  screenshot_data->screenshot_area.x = rect.x;
  screenshot_data->screenshot_area.y = rect.y;
  clip.x = rect.x - (gint) actor_x;
  clip.y = rect.y - (gint) actor_y;

  clip.width = screenshot_data->screenshot_area.width = rect.width;
  clip.height = screenshot_data->screenshot_area.height = rect.height;

  stex = META_SHAPED_TEXTURE (meta_window_actor_get_texture (META_WINDOW_ACTOR (window_actor)));
  screenshot_data->image = meta_shaped_texture_get_image (stex, &clip);

  settings = g_settings_new (A11Y_APPS_SCHEMA);
  if (include_cursor && !g_settings_get_boolean (settings, MAGNIFIER_ACTIVE_KEY))
    {
      tracker = meta_cursor_tracker_get_for_screen (screen);
      _draw_cursor_image (tracker, screenshot_data->image, screenshot_data->screenshot_area);
    }
  g_object_unref (settings);

  result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, shell_screenshot_screenshot_window);
  g_simple_async_result_run_in_thread (result, write_screenshot_thread, G_PRIORITY_DEFAULT, NULL);
  g_object_unref (result);
}