/*
* Copyright (C) 2001 Havoc Pennington
* Copyright (C) 2002, 2003 Red Hat, Inc.
* Copyright (C) 2003 Rob Adams
* Copyright (C) 2005 Novell, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <beryl.h>
#include <glib.h>
#define PLACE_WORKAROUND_DEFAULT TRUE
#define RECTANGLE_FROM_WINDOW(w) {w->attrib.x,w->attrib.y,w->attrib.width,w->attrib.height}
#define GET_WINDOW_HEIGHT(w) (window->serverHeight + window->serverBorderWidth * 2)
#define GET_WINDOW_WIDTH(w) (window->serverWidth + window->serverBorderWidth * 2)
static int displayPrivateIndex;
typedef struct _PlaceDisplay
{
int screenPrivateIndex;
// HandleEventProc handleEvent;
} PlaceDisplay;
#define PLACE_SCREEN_OPTION_WORKAROUND 0
#define PLACE_SCREEN_OPTION_MODE 1
#define PLACE_SCREEN_OPTION_NUM 2
typedef struct _PlaceScreen
{
CompOption opt[PLACE_SCREEN_OPTION_NUM];
DamageWindowRectProc damageWindowRect;
int placeMode;
} PlaceScreen;
typedef enum _PlaceMode
{
PlaceModeCascade = 0,
PlaceModeCentered,
PlaceModeRandom,
PlaceModeIntelligent,
} PlaceMode;
char *placeModes[] =
{ N_("Cascade"), N_("Centered"), N_("Random"), N_("Intelligent") };
#define PLACE_MODE_DEFAULT PlaceModeIntelligent
#define NUM_PLACE_MODES 4
#define GET_PLACE_DISPLAY(d) \
((PlaceDisplay *) (d)->privates[displayPrivateIndex].ptr)
#define PLACE_DISPLAY(d) \
PlaceDisplay *pd = GET_PLACE_DISPLAY (d)
#define GET_PLACE_SCREEN(s, pd) \
((PlaceScreen *) (s)->privates[(pd)->screenPrivateIndex].ptr)
#define PLACE_SCREEN(s) \
PlaceScreen *ps = GET_PLACE_SCREEN (s, GET_PLACE_DISPLAY (s->display))
#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
static Bool
placeSetScreenOption(CompScreen * screen, char *name, CompOptionValue * value)
{
CompOption *o;
int index;
PLACE_SCREEN(screen);
o = compFindOption(ps->opt, NUM_OPTIONS(ps), name, &index);
if (!o)
return FALSE;
switch (index)
{
case PLACE_SCREEN_OPTION_WORKAROUND:
if (compSetBoolOption(o, value))
return TRUE;
break;
case PLACE_SCREEN_OPTION_MODE:
if (compSetStringOption(o, value))
{
int i;
for (i = 0; i < NUM_PLACE_MODES; i++)
if (strcmp(placeModes[i], o->value.s) == 0)
ps->placeMode = (PlaceMode) i;
}
default:
break;
}
return FALSE;
}
static void placeScreenInitOptions(PlaceScreen * ps)
{
CompOption *o;
int i;
o = &ps->opt[PLACE_SCREEN_OPTION_WORKAROUND];
o->advanced = False;
o->name = "workarounds";
o->group = N_("Misc. options");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc = N_("Workarounds");
o->longDesc = N_("Window placement Workarounds.");
o->type = CompOptionTypeBool;
o->value.b = PLACE_WORKAROUND_DEFAULT;
o = &ps->opt[PLACE_SCREEN_OPTION_MODE];
o->advanced = False;
o->name = "place_mode";
o->group = N_("Misc. options");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc = N_("Placement Mode");
o->longDesc = N_("Window Placement technique.");
o->type = CompOptionTypeString;
o->value.s = strdup(placeModes[PLACE_MODE_DEFAULT]);
o->rest.s.string = placeModes;
o->rest.s.nString = NUM_PLACE_MODES;
for (i = 0; i < NUM_PLACE_MODES; i++)
if (strcmp(placeModes[i], o->value.s) == 0)
ps->placeMode = (PlaceMode) i;
}
static CompOption *placeGetScreenOptions(CompScreen * screen, int *count)
{
if (screen)
{
PLACE_SCREEN(screen);
*count = NUM_OPTIONS(ps);
return ps->opt;
}
else
{
PlaceScreen *ps = malloc(sizeof(PlaceScreen));
placeScreenInitOptions(ps);
*count = NUM_OPTIONS(ps);
return ps->opt;
}
}
typedef enum
{
PlaceLeft,
PlaceRight,
PlaceTop,
PlaceBottom
} PlaceWindowDirection;
static Bool
rectangleIntersect(XRectangle * src1, XRectangle * src2, XRectangle * dest)
{
int dest_x, dest_y;
int dest_w, dest_h;
int return_val;
g_return_val_if_fail(src1 != NULL, FALSE);
g_return_val_if_fail(src2 != NULL, FALSE);
g_return_val_if_fail(dest != NULL, FALSE);
return_val = FALSE;
dest_x = MAX(src1->x, src2->x);
dest_y = MAX(src1->y, src2->y);
dest_w = MIN(src1->x + src1->width, src2->x + src2->width) - dest_x;
dest_h = MIN(src1->y + src1->height, src2->y + src2->height) - dest_y;
if (dest_w > 0 && dest_h > 0)
{
dest->x = dest_x;
dest->y = dest_y;
dest->width = dest_w;
dest->height = dest_h;
return_val = TRUE;
}
else
{
dest->width = 0;
dest->height = 0;
}
return return_val;
}
static gint northwestcmp(gconstpointer a, gconstpointer b)
{
CompWindow *aw = (gpointer) a;
CompWindow *bw = (gpointer) b;
int from_origin_a;
int from_origin_b;
int ax, ay, bx, by;
ax = aw->attrib.x - aw->input.left;
ay = aw->attrib.y - aw->input.top;
bx = bw->attrib.x - bw->input.left;
by = bw->attrib.y - bw->input.top;
/* 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
get_workarea_of_current_output_device(CompScreen * s, XRectangle * area)
{
int head;
head = screenGetCurrentOutputDev(s);
screenGetOutputDevWorkArea(s, head, area);
}
static void placeCentered(CompWindow * window, int *x, int *y)
{
*x = window->screen->workArea.x + (window->screen->workArea.width
- GET_WINDOW_WIDTH(window)) / 2;
*y = window->screen->workArea.y + (window->screen->workArea.height
- GET_WINDOW_HEIGHT(window)) / 2;
}
static void placeRandom(CompWindow * window, int *x, int *y)
{
*x = rand() % (window->screen->workArea.width -
window->screen->workArea.x);
*y = rand() % (window->screen->workArea.height -
window->screen->workArea.y);
}
static void
find_next_cascade(CompWindow * window,
GList * windows, int x, int y, int *new_x, int *new_y)
{
GList *tmp;
GList *sorted;
int cascade_x, cascade_y;
int x_threshold, y_threshold;
int window_width, window_height;
int cascade_stage;
XRectangle work_area;
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
x_threshold = MAX(window->input.left, CASCADE_FUZZ);
y_threshold = MAX(window->input.top, CASCADE_FUZZ);
/* Find furthest-SE origin of all workspaces.
* cascade_x, cascade_y are the target position
* of NW corner of window frame.
*/
get_workarea_of_current_output_device(window->screen, &work_area);
cascade_x = MAX(0, work_area.x);
cascade_y = MAX(0, work_area.y);
/* Find first cascade position that's not used. */
window_width = GET_WINDOW_WIDTH(window) + window->input.left +
window->input.right;
window_height = GET_WINDOW_HEIGHT(window) + window->input.top +
window->input.bottom;
cascade_stage = 0;
tmp = sorted;
while (tmp != NULL)
{
CompWindow *w;
int wx, wy;
w = tmp->data;
/* we want frame position, not window position */
wx = w->attrib.x - w->input.left;
wy = w->attrib.y - w->input.top;
if (ABS(wx - cascade_x) < x_threshold &&
ABS(wy - cascade_y) < y_threshold)
{
/* This window is "in the way", move to next cascade
* point. The new window frame should go at the origin
* of the client window we're stacking above.
*/
wx = w->attrib.x;
wy = w->attrib.y;
cascade_x = wx;
cascade_y = wy;
/* 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);
/* Convert coords to position of window, not position of frame. */
*new_x = cascade_x + window->input.left;
*new_y = cascade_y + window->input.top;
}
static void
find_most_freespace(CompWindow * window,
CompWindow * focus_window,
int x, int y, int *new_x, int *new_y)
{
PlaceWindowDirection side;
int max_area;
int max_width, max_height, left, right, top, bottom;
int left_space, right_space, top_space, bottom_space;
int frame_size_left, frame_size_top;
XRectangle work_area;
XRectangle avoid;
XRectangle outer;
frame_size_left = window->input.left;
frame_size_top = window->input.top;
get_workarea_of_current_output_device(window->screen, &work_area);
getOuterRectOfWindow(focus_window, &avoid);
getOuterRectOfWindow(window, &outer);
/* Find the areas of choosing the various sides of the focus window */
max_width = MIN(avoid.width, outer.width);
max_height = MIN(avoid.height, outer.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, outer.width);
right = MIN(right_space, outer.width);
top = MIN(top_space, outer.height);
bottom = MIN(bottom_space, outer.height);
/* Find out which side of the focus_window can show the most of the
* window
*/
side = PlaceLeft;
max_area = left * max_height;
if (right * max_height > max_area)
{
side = PlaceRight;
max_area = right * max_height;
}
if (top * max_width > max_area)
{
side = PlaceTop;
max_area = top * max_width;
}
if (bottom * max_width > max_area)
{
side = PlaceBottom;
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 PlaceLeft:
*new_y = avoid.y + frame_size_top;
if (left_space > outer.width)
*new_x = avoid.x - outer.width + frame_size_left;
else
*new_x = work_area.x + frame_size_left;
break;
case PlaceRight:
*new_y = avoid.y + frame_size_top;
if (right_space > outer.width)
*new_x = avoid.x + avoid.width + frame_size_left;
else
*new_x = work_area.x + work_area.width - outer.width +
frame_size_left;
break;
case PlaceTop:
*new_x = avoid.x + frame_size_left;
if (top_space > outer.height)
*new_y = avoid.y - outer.height + frame_size_top;
else
*new_y = work_area.y + frame_size_top;
break;
case PlaceBottom:
*new_x = avoid.x + frame_size_left;
if (bottom_space > outer.height)
*new_y = avoid.y + avoid.height + frame_size_top;
else
*new_y = work_area.y + work_area.height - outer.height +
frame_size_top;
break;
}
}
static void
avoid_being_obscured_as_second_modal_dialog(CompWindow * window,
int *x, int *y)
{
/* We can't center this dialog if it was denied focus and it
* overlaps with the focus window and this dialog is modal and this
* dialog is in the same app as the focus window (*phew*...please
* don't make me say that ten times fast). See bug 307875 comment 11
* and 12 for details, but basically it means this is probably a
* second modal dialog for some app while the focus window is the
* first modal dialog. We should probably make them simultaneously
* visible in general, but it becomes mandatory to do so due to
* buggy apps (e.g. those using gtk+ *sigh*) because in those cases
* this second modal dialog also happens to be modal to the first
* dialog in addition to the main window, while it has only let us
* know about the modal-to-the-main-window part.
*/
CompWindow *focus_window;
focus_window =
findWindowAtDisplay(window->screen->display,
window->screen->display->activeWindow);
}
static gboolean
rectangle_overlaps_some_window(XRectangle * rect, GList * windows)
{
GList *tmp;
XRectangle dest;
tmp = windows;
while (tmp != NULL)
{
CompWindow *other = tmp->data;
XRectangle other_rect;
switch (other->type)
{
case CompWindowTypeSplashMask:
case CompWindowTypeDesktopMask:
case CompWindowTypeDialogMask:
case CompWindowTypeModalDialogMask:
case CompWindowTypeFullscreenMask:
case CompWindowTypeUnknownMask:
break;
case CompWindowTypeNormalMask:
case CompWindowTypeUtilMask:
case CompWindowTypeToolbarMask:
case CompWindowTypeMenuMask:
getOuterRectOfWindow(other, &other_rect);
if (rectangleIntersect(rect, &other_rect, &dest))
return TRUE;
break;
}
tmp = tmp->next;
}
return FALSE;
}
static void placeIntelligent(CompWindow * w, int *x, int *y, GList * windows)
{
int tries = 0;
XRectangle wRect = RECTANGLE_FROM_WINDOW(w);
XRectangle sRect = { 0, 0, 0, 0 };
wRect.height += w->serverBorderWidth;
int xi;
int yi;
CompScreen *screen = w->screen;
int maxTries = ( ((wRect.height + wRect.width ) / 100) > 1 ? ((wRect.height + wRect.width) / 100) : 1 );
int wIter = wRect.width / maxTries;
int hIter = wRect.height / maxTries;
get_workarea_of_current_output_device(screen, &sRect);
placeCentered(w, x, y);
XRectangle cRect = { *x, *y, wRect.width, wRect.height };
if (!rectangle_overlaps_some_window(&cRect, windows))
{
return; /*If it fits place it in the center */
}
/* Recursing would be ineffecient, a goto really is the best solution */
thisisdisgusting:
for (xi = sRect.x; xi < (sRect.width+sRect.x); xi += 10)
{
wRect.x = xi;
for (yi = sRect.y; yi < (sRect.height+sRect.y); yi += 10)
{
wRect.y = yi;
if (!rectangle_overlaps_some_window(&wRect, windows))
{
if (!
((wRect.width > sRect.width / 2)
&& ((wRect.width + w->serverX > sRect.width)
|| (wRect.height + w->serverY > sRect.height))))
{
*x = xi;
}
*y = yi;
return;
}
}
}
/* No fit and big window? Center it, that tends to be what people expect */
if (tries == 0 && wRect.width > (sRect.width / 2))
{
placeCentered(w, x, y);
return;
}
/* Would the window fit if it was smaller ? Reduce width of the window rectangle and go through the check loop again */
if (tries < maxTries && wRect.width > 0 && wRect.height > 0)
{
tries++;
wRect.width -= wIter;
wRect.height -= hIter;
goto thisisdisgusting;
}
/* Fallback, we shouldn't get here? */
placeCentered(w, x, y);
}
static gint leftmost_cmp(gconstpointer a, gconstpointer b)
{
CompWindow *aw = (gpointer) a;
CompWindow *bw = (gpointer) b;
int ax, bx;
ax = aw->attrib.x - aw->input.left;
bx = bw->attrib.x - bw->input.left;
if (ax < bx)
return -1;
else if (ax > bx)
return 1;
else
return 0;
}
static gint topmost_cmp(gconstpointer a, gconstpointer b)
{
CompWindow *aw = (gpointer) a;
CompWindow *bw = (gpointer) b;
int ay, by;
ay = aw->attrib.y - aw->input.top;
by = bw->attrib.y - bw->input.top;
if (ay < by)
return -1;
else if (ay > by)
return 1;
else
return 0;
}
static void
center_tile_rect_in_area(XRectangle * rect, XRectangle * work_area)
{
int fluff;
/* The point here is to tile a window such that "extra"
* space is equal on either side (i.e. so a full screen
* of windows tiled this way would center the windows
* as a group)
*/
fluff = (work_area->width % (rect->width + 1)) / 2;
rect->x = work_area->x + fluff;
fluff = (work_area->height % (rect->height + 1)) / 3;
rect->y = work_area->y + fluff;
}
static gboolean
rect_fits_in_work_area(XRectangle * work_area, XRectangle * rect)
{
return ((rect->x >= work_area->x) &&
(rect->y >= work_area->y) &&
(rect->x + rect->width <= work_area->x + work_area->width)
&& (rect->y + rect->height <= work_area->y + work_area->height));
}
/* 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(CompWindow * window,
GList * windows, 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;
XRectangle rect;
XRectangle 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);
getOuterRectOfWindow(window, &rect);
get_workarea_of_current_output_device(window->screen, &work_area);
work_area.x += (window->initialViewportX - window->screen->x) *
window->screen->width;
work_area.y += (window->initialViewportY - window->screen->y) *
window->screen->height;
center_tile_rect_in_area(&rect, &work_area);
if (rect_fits_in_work_area(&work_area, &rect) &&
!rectangle_overlaps_some_window(&rect, windows))
{
*new_x = rect.x + window->input.left;
*new_y = rect.y + window->input.top;
retval = TRUE;
goto out;
}
/* try below each window */
tmp = below_sorted;
while (tmp != NULL)
{
CompWindow *w = tmp->data;
XRectangle outer_rect;
getOuterRectOfWindow(w, &outer_rect);
rect.x = outer_rect.x;
rect.y = outer_rect.y + outer_rect.height;
if (rect_fits_in_work_area(&work_area, &rect) &&
!rectangle_overlaps_some_window(&rect, below_sorted))
{
*new_x = rect.x + window->input.left;
*new_y = rect.y + window->input.top;
retval = TRUE;
goto out;
}
tmp = tmp->next;
}
/* try to the right of each window */
tmp = right_sorted;
while (tmp != NULL)
{
CompWindow *w = tmp->data;
XRectangle outer_rect;
getOuterRectOfWindow(w, &outer_rect);
rect.x = outer_rect.x + outer_rect.width;
rect.y = outer_rect.y;
if (rect_fits_in_work_area(&work_area, &rect) &&
!rectangle_overlaps_some_window(&rect, right_sorted))
{
*new_x = rect.x + window->input.left;
*new_y = rect.y + window->input.top;
retval = TRUE;
goto out;
}
tmp = tmp->next;
}
out:
g_list_free(below_sorted);
g_list_free(right_sorted);
return retval;
}
static void
placeWindow(CompWindow * window, int x, int y, int *new_x, int *new_y)
{
CompWindow *wi;
GList *windows;
XRectangle work_area;
int x0 = (window->initialViewportX - window->screen->x) *
window->screen->width;
int y0 = (window->initialViewportY - window->screen->y) *
window->screen->height;
int window_width, window_height;
PLACE_SCREEN(window->screen);
window_width = GET_WINDOW_WIDTH(window);
window_height = GET_WINDOW_HEIGHT(window);
get_workarea_of_current_output_device(window->screen, &work_area);
work_area.x += x0;
work_area.y += y0;
windows = NULL;
switch (window->type)
{
case CompWindowTypeSplashMask:
case CompWindowTypeDialogMask:
case CompWindowTypeModalDialogMask:
case CompWindowTypeNormalMask:
/* Run placement algorithm on these. */
break;
case CompWindowTypeDockMask:
case CompWindowTypeDesktopMask:
case CompWindowTypeUtilMask:
case CompWindowTypeToolbarMask:
case CompWindowTypeMenuMask:
case CompWindowTypeFullscreenMask:
case CompWindowTypeUnknownMask:
/* Assume the app knows best how to place these, no placement
* algorithm ever (other than "leave them as-is")
*/
goto done_no_constraints;
break;
}
/* don't run placement algorithm on windows that can't be moved */
if (!(window->actions & CompWindowActionMoveMask))
{
goto done_no_constraints;
}
if (window->type & CompWindowTypeFullscreenMask)
{
x = x0;
y = y0;
goto done_no_constraints;
}
if (window->state & (CompWindowStateMaximizedVertMask |
CompWindowStateMaximizedHorzMask))
{
if (window->screen->nOutputDev == 1)
{
if (window->state & CompWindowStateMaximizedVertMask)
y = work_area.y + window->input.top;
if (window->state & CompWindowStateMaximizedHorzMask)
x = work_area.x + window->input.left;
}
else
{
int head = screenGetOutputDevForWindow(window);
XRectangle workArea;
screenGetOutputDevWorkArea(window->screen, head, &workArea);
if (window->state & CompWindowStateMaximizedVertMask)
y = workArea.y + window->input.top;
if (window->state & CompWindowStateMaximizedHorzMask)
x = workArea.x + window->input.left;
}
goto done;
}
if (ps->opt[PLACE_SCREEN_OPTION_WORKAROUND].value.b)
{
/* workarounds enabled */
if ((window->sizeHints.flags & PPosition) ||
(window->sizeHints.flags & USPosition))
{
avoid_being_obscured_as_second_modal_dialog(window, &x, &y);
goto done;
}
}
else
{
switch (window->type)
{
case CompWindowTypeNormalMask:
/* Only accept USPosition on normal windows because the app is full
* of shit claiming the user set -geometry for a dialog or dock
*/
if (window->sizeHints.flags & USPosition)
{
/* don't constrain with placement algorithm */
goto done;
}
break;
case CompWindowTypeSplashMask:
case CompWindowTypeDialogMask:
case CompWindowTypeModalDialogMask:
/* Ignore even USPosition on dialogs, splashscreen */
break;
case CompWindowTypeDockMask:
case CompWindowTypeDesktopMask:
case CompWindowTypeUtilMask:
case CompWindowTypeToolbarMask:
case CompWindowTypeMenuMask:
case CompWindowTypeFullscreenMask:
case CompWindowTypeUnknownMask:
/* Assume the app knows best how to place these. */
if (window->sizeHints.flags & PPosition)
{
goto done_no_constraints;
}
break;
}
}
if ((window->type == CompWindowTypeDialogMask ||
window->type == CompWindowTypeModalDialogMask) &&
window->transientFor != None)
{
/* Center horizontally, at top of parent vertically */
CompWindow *parent;
parent = findWindowAtDisplay(window->screen->display,
window->transientFor);
if (parent)
{
int w;
x = parent->attrib.x;
y = parent->attrib.y;
w = GET_WINDOW_WIDTH(parent);
/* center of parent */
x = x + w / 2 - window_width / 2;
/* "visually" center window over parent, leaving twice as
* much space below as on top.
*/
y += (GET_WINDOW_HEIGHT(parent) -
window_height) / 3 + window->input.top;
/* clip to screen if parent is visible in current viewport */
if (parent->attrib.x < parent->screen->width &&
parent->attrib.x + parent->screen->width > 0)
{
XRectangle area;
get_workarea_of_current_output_device(window->screen, &area);
if (x + window_width > area.x + area.width)
x = area.x + area.width - window_width;
if (y + window_height > area.y + area.height)
y = area.y + area.height - window_height;
if (x < area.x)
x = area.x;
if (y < area.y)
y = area.y;
}
avoid_being_obscured_as_second_modal_dialog(window, &x, &y);
goto done_no_x_constraints;
}
}
/* FIXME UTILITY with transient set should be stacked up
* on the sides of the parent window or something.
*/
if (window->type == CompWindowTypeDialogMask ||
window->type == CompWindowTypeModalDialogMask ||
window->type == CompWindowTypeSplashMask)
{
/* Center on screen */
int head;
XRectangle workArea;
/* w = window->screen->width;
h = window->screen->height;
x = (w - window->width) / 2;
y = (h - window->height) / 2;
*/
head = screenGetCurrentOutputDev(window->screen);
screenGetOutputDevWorkArea(window->screen, head, &workArea);
x = (workArea.width - window_width) / 2;
y = (workArea.height - window_height) / 2;
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)
*/
for (wi = window->screen->windows; wi; wi = wi->next)
{
if (!wi->shaded && wi->attrib.map_state != IsViewable)
continue;
if (wi->attrib.x >= work_area.x + work_area.width ||
wi->attrib.x + GET_WINDOW_WIDTH(wi) <= work_area.x ||
wi->attrib.y >= work_area.y + work_area.height ||
wi->attrib.y + GET_WINDOW_HEIGHT(wi) <= work_area.y)
continue;
if (wi->attrib.override_redirect)
continue;
if (wi->state & CompWindowStateOffscreenMask)
continue;
if (wi->type & (CompWindowTypeDesktopMask |
CompWindowTypeDockMask |
CompWindowTypeFullscreenMask |
CompWindowTypeUnknownMask))
continue;
if (wi != window)
windows = g_list_prepend(windows, wi);
}
/* "Origin" placement algorithm */
x = x0;
y = y0;
if (ps->placeMode == PlaceModeCascade)
{
if (find_first_fit(window, windows, x, y, &x, &y))
goto done_check_denied_focus;
/* if the window wasn't placed at the origin of screen,
* cascade it onto the current screen
*/
find_next_cascade(window, windows, x, y, &x, &y);
}
else if (ps->placeMode == PlaceModeCentered)
{
placeCentered(window, &x, &y);
}
else if (ps->placeMode == PlaceModeRandom)
{
placeRandom(window, &x, &y);
}
else
{
placeIntelligent(window, &x, &y, windows);
}
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 (0 /* window->denied_focus_and_not_transient */ )
{
gboolean found_fit = FALSE;
CompWindow *focus_window;
focus_window =
findWindowAtDisplay(window->screen->display,
window->screen->display->activeWindow);
if (focus_window)
{
XRectangle wr, fwr, overlap;
getOuterRectOfWindow(window, &wr);
getOuterRectOfWindow(focus_window, &fwr);
/* No need to do anything if the window doesn't overlap at all */
found_fit = !rectangleIntersect(&wr, &fwr, &overlap);
/* 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 = 0;
y = 0;
found_fit =
find_first_fit(window, focus_window_list, 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);
}
g_list_free(windows);
done:
if (((x + window_width + window->input.right < work_area.x +
work_area.width) &&
(x + window_width + window->input.right > work_area.x))
||
((x - window->input.left > work_area.x) &&
(x - window->input.left < work_area.x + work_area.width)))
{
if (x + window_width + window->input.right >
work_area.x + work_area.width)
x = work_area.x + work_area.width
- window_width - window->input.right;
if (x - window->input.left < work_area.x)
x = work_area.x + window->input.left;
}
else
{
//only use THIS code if the window is TOTALLY outside the viewport.
while (x + window_width + window->input.right >
work_area.x + work_area.width)
x -= work_area.width;
if (x - window->input.left < work_area.x)
x = work_area.x + window->input.left;
}
done_no_x_constraints:
if (window->screen->nOutputDev == 1)
{
if (y + window_height + window->input.bottom >
work_area.y + work_area.height)
y = work_area.y + work_area.height
- window_height - window->input.bottom;
if (y - window->input.top < work_area.y)
y = work_area.y + window->input.top;
}
else
{
int head = screenGetOutputDevForWindow(window);
XRectangle workArea;
screenGetOutputDevWorkArea(window->screen, head, &workArea);
if (y + window_height + window->input.bottom >
workArea.y + workArea.height)
y = workArea.y + workArea.height - window_height -
window->input.bottom;
if (y - window->input.top < workArea.y)
y = workArea.y + window->input.top;
}
done_no_constraints:
*new_x = x;
*new_y = y;
}
static Bool placeDamageWindowRect(CompWindow * w, Bool initial, BoxPtr rect)
{
Bool status;
PLACE_SCREEN(w->screen);
UNWRAP(ps, w->screen, damageWindowRect);
status = (*w->screen->damageWindowRect) (w, initial, rect);
WRAP(ps, w->screen, damageWindowRect, placeDamageWindowRect);
if (initial && !w->attrib.override_redirect && !w->placed)
{
int newX, newY;
placeWindow(w, w->attrib.x, w->attrib.y, &newX, &newY);
w->placed = TRUE;
if (newX != w->attrib.x || newY != w->attrib.y)
{
moveWindow(w, newX - w->attrib.x,
newY - w->attrib.y, FALSE, TRUE);
syncWindowPosition(w);
}
}
return status;
}
static Bool placeInitDisplay(CompPlugin * p, CompDisplay * d)
{
PlaceDisplay *pd;
pd = malloc(sizeof(PlaceDisplay));
if (!pd)
return FALSE;
pd->screenPrivateIndex = allocateScreenPrivateIndex(d);
if (pd->screenPrivateIndex < 0)
{
free(pd);
return FALSE;
}
d->privates[displayPrivateIndex].ptr = pd;
// WRAP (pd, d, handleEvent, placeHandleEvent);
return TRUE;
}
static void placeFiniDisplay(CompPlugin * p, CompDisplay * d)
{
PLACE_DISPLAY(d);
// UNWRAP (pd, d, handleEvent);
freeScreenPrivateIndex(d, pd->screenPrivateIndex);
free(pd);
}
static Bool placeInitScreen(CompPlugin * p, CompScreen * s)
{
PlaceScreen *ps;
PLACE_DISPLAY(s->display);
ps = malloc(sizeof(PlaceScreen));
if (!ps)
return FALSE;
placeScreenInitOptions(ps);
// addScreenBinding (s, &ps->opt[PLACE_SCREEN_OPTION_CENTER_INITIATE].value.bind);
WRAP(ps, s, damageWindowRect, placeDamageWindowRect);
s->privates[pd->screenPrivateIndex].ptr = ps;
return TRUE;
}
static void placeFiniScreen(CompPlugin * p, CompScreen * s)
{
PLACE_SCREEN(s);
UNWRAP(ps, s, damageWindowRect);
free(ps);
}
static Bool placeInit(CompPlugin * p)
{
displayPrivateIndex = allocateDisplayPrivateIndex();
if (displayPrivateIndex < 0)
return FALSE;
return TRUE;
}
static void placeFini(CompPlugin * p)
{
if (displayPrivateIndex >= 0)
freeDisplayPrivateIndex(displayPrivateIndex);
}
static CompPluginVTable placeVTable = {
"place",
N_("Place Windows"),
N_("Place windows at appropriate positions when mapped"),
placeInit,
placeFini,
placeInitDisplay,
placeFiniDisplay,
placeInitScreen,
placeFiniScreen,
0, /* InitWindow */
0, /* FiniWindow */
0, /* GetDisplayOptions */
0, /* SetDisplayOption */
placeGetScreenOptions,
placeSetScreenOption,
0,
0,
0,
0,
BERYL_ABI_INFO,
"beryl-plugins",
"wm",
0,
0,
True,
};
CompPluginVTable *getCompPluginInfo(void)
{
return &placeVTable;
}
syntax highlighted by Code2HTML, v. 0.9.1