You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2279 lines
55 KiB
2279 lines
55 KiB
/*
|
|
* 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 <compiz-core.h>
|
|
|
|
static CompMetadata placeMetadata;
|
|
|
|
static int displayPrivateIndex;
|
|
|
|
typedef struct _PlaceDisplay {
|
|
int screenPrivateIndex;
|
|
|
|
Atom fullPlacementAtom;
|
|
|
|
HandleEventProc handleEvent;
|
|
} PlaceDisplay;
|
|
|
|
#define PLACE_MODE_CASCADE 0
|
|
#define PLACE_MODE_CENTERED 1
|
|
#define PLACE_MODE_SMART 2
|
|
#define PLACE_MODE_MAXIMIZE 3
|
|
#define PLACE_MODE_RANDOM 4
|
|
#define PLACE_MODE_POINTER 5
|
|
#define PLACE_MODE_LAST PLACE_MODE_POINTER
|
|
|
|
#define PLACE_MOMODE_CURRENT 0
|
|
#define PLACE_MOMODE_POINTER 1
|
|
#define PLACE_MOMODE_ACTIVEWIN 2
|
|
#define PLACE_MOMODE_FULLSCREEN 3
|
|
#define PLACE_MOMODE_LAST PLACE_MOMODE_FULLSCREEN
|
|
|
|
#define PLACE_SCREEN_OPTION_WORKAROUND 0
|
|
#define PLACE_SCREEN_OPTION_MODE 1
|
|
#define PLACE_SCREEN_OPTION_MULTIOUTPUT_MODE 2
|
|
#define PLACE_SCREEN_OPTION_FORCE_PLACEMENT 3
|
|
#define PLACE_SCREEN_OPTION_POSITION_MATCHES 4
|
|
#define PLACE_SCREEN_OPTION_POSITION_X_VALUES 5
|
|
#define PLACE_SCREEN_OPTION_POSITION_Y_VALUES 6
|
|
#define PLACE_SCREEN_OPTION_POSITION_CONSTRAIN 7
|
|
#define PLACE_SCREEN_OPTION_VIEWPORT_MATCHES 8
|
|
#define PLACE_SCREEN_OPTION_VIEWPORT_X_VALUES 9
|
|
#define PLACE_SCREEN_OPTION_VIEWPORT_Y_VALUES 10
|
|
#define PLACE_SCREEN_OPTION_MODE_MATCHES 11
|
|
#define PLACE_SCREEN_OPTION_MODE_MODES 12
|
|
#define PLACE_SCREEN_OPTION_NUM 13
|
|
|
|
typedef struct _PlaceScreen {
|
|
int windowPrivateIndex;
|
|
|
|
CompOption opt[PLACE_SCREEN_OPTION_NUM];
|
|
|
|
AddSupportedAtomsProc addSupportedAtoms;
|
|
PlaceWindowProc placeWindow;
|
|
ValidateWindowResizeRequestProc validateWindowResizeRequest;
|
|
WindowGrabNotifyProc windowGrabNotify;
|
|
|
|
int prevWidth;
|
|
int prevHeight;
|
|
int strutWindowCount;
|
|
CompTimeoutHandle resChangeFallbackHandle;
|
|
} PlaceScreen;
|
|
|
|
typedef struct _PlaceWindow {
|
|
Bool savedOriginal;
|
|
XRectangle origVpRelRect; /* saved original window rectangle with position
|
|
relative to viewport */
|
|
int prevServerX;
|
|
int prevServerY;
|
|
} PlaceWindow;
|
|
|
|
#define GET_PLACE_DISPLAY(d) \
|
|
((PlaceDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
|
|
|
|
#define PLACE_DISPLAY(d) \
|
|
PlaceDisplay *pd = GET_PLACE_DISPLAY (d)
|
|
|
|
#define GET_PLACE_SCREEN(s, pd) \
|
|
((PlaceScreen *) (s)->base.privates[(pd)->screenPrivateIndex].ptr)
|
|
|
|
#define PLACE_SCREEN(s) \
|
|
PlaceScreen *ps = GET_PLACE_SCREEN (s, GET_PLACE_DISPLAY (s->display))
|
|
|
|
#define GET_PLACE_WINDOW(w, ps) \
|
|
((PlaceWindow *) (w)->base.privates[(ps)->windowPrivateIndex].ptr)
|
|
|
|
#define PLACE_WINDOW(w) \
|
|
PlaceWindow *pw = GET_PLACE_WINDOW (w, \
|
|
GET_PLACE_SCREEN (w->screen, \
|
|
GET_PLACE_DISPLAY (w->screen->display)))
|
|
|
|
#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
|
|
|
|
typedef enum {
|
|
NoPlacement = 0,
|
|
PlaceOnly,
|
|
ConstrainOnly,
|
|
PlaceAndConstrain,
|
|
PlaceOverParent,
|
|
PlaceCenteredOnScreen
|
|
} PlacementStrategy;
|
|
|
|
/* helper macro that filters out windows irrelevant for placement */
|
|
#define IS_PLACE_RELEVANT(wi, w) \
|
|
((w != wi) && \
|
|
(wi->attrib.map_state == IsViewable || wi->shaded) && \
|
|
(!wi->attrib.override_redirect) && \
|
|
(!(wi->wmType & (CompWindowTypeDockMask | CompWindowTypeDesktopMask))))
|
|
|
|
/* helper macros to get the full dimensions of a window,
|
|
including decorations */
|
|
#define WIN_FULL_X(w) ((w)->serverX - (w)->input.left)
|
|
#define WIN_FULL_Y(w) ((w)->serverY - (w)->input.top)
|
|
#define WIN_FULL_W(w) ((w)->serverWidth + 2 * (w)->serverBorderWidth + \
|
|
(w)->input.left + (w)->input.right)
|
|
#define WIN_FULL_H(w) ((w)->serverHeight + 2 * (w)->serverBorderWidth + \
|
|
(w)->input.top + (w)->input.bottom)
|
|
|
|
static Bool
|
|
placeMatchXYValue (CompWindow *w,
|
|
CompOption *matches,
|
|
CompOption *xValues,
|
|
CompOption *yValues,
|
|
CompOption *constrain,
|
|
int *x,
|
|
int *y,
|
|
Bool *keepInWorkarea)
|
|
{
|
|
int i, min;
|
|
|
|
if (w->type & CompWindowTypeDesktopMask)
|
|
return FALSE;
|
|
|
|
min = MIN (matches->value.list.nValue, xValues->value.list.nValue);
|
|
min = MIN (min, yValues->value.list.nValue);
|
|
|
|
for (i = 0; i < min; i++)
|
|
{
|
|
if (matchEval (&matches->value.list.value[i].match, w))
|
|
{
|
|
*x = xValues->value.list.value[i].i;
|
|
*y = yValues->value.list.value[i].i;
|
|
|
|
if (keepInWorkarea)
|
|
{
|
|
if (constrain && constrain->value.list.nValue > i)
|
|
*keepInWorkarea = constrain->value.list.value[i].b;
|
|
else
|
|
*keepInWorkarea = TRUE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static Bool
|
|
placeMatchPosition (CompWindow *w,
|
|
int *x,
|
|
int *y,
|
|
Bool *keepInWorkarea)
|
|
{
|
|
PLACE_SCREEN (w->screen);
|
|
|
|
return placeMatchXYValue (w,
|
|
&ps->opt[PLACE_SCREEN_OPTION_POSITION_MATCHES],
|
|
&ps->opt[PLACE_SCREEN_OPTION_POSITION_X_VALUES],
|
|
&ps->opt[PLACE_SCREEN_OPTION_POSITION_Y_VALUES],
|
|
&ps->opt[PLACE_SCREEN_OPTION_POSITION_CONSTRAIN],
|
|
x,
|
|
y,
|
|
keepInWorkarea);
|
|
}
|
|
|
|
static Bool
|
|
placeMatchViewport (CompWindow *w,
|
|
int *x,
|
|
int *y)
|
|
{
|
|
PLACE_SCREEN (w->screen);
|
|
|
|
if (placeMatchXYValue (w,
|
|
&ps->opt[PLACE_SCREEN_OPTION_VIEWPORT_MATCHES],
|
|
&ps->opt[PLACE_SCREEN_OPTION_VIEWPORT_X_VALUES],
|
|
&ps->opt[PLACE_SCREEN_OPTION_VIEWPORT_Y_VALUES],
|
|
NULL,
|
|
x,
|
|
y,
|
|
NULL))
|
|
{
|
|
/* Viewport matches are given 1-based, so we need to adjust that */
|
|
*x -= 1;
|
|
*y -= 1;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
placeWindowGrabNotify (CompWindow *w,
|
|
int x,
|
|
int y,
|
|
unsigned int state,
|
|
unsigned int mask)
|
|
{
|
|
CompScreen *s = w->screen;
|
|
|
|
PLACE_SCREEN (s);
|
|
PLACE_WINDOW (w);
|
|
|
|
if (pw->savedOriginal)
|
|
{
|
|
int i;
|
|
|
|
/* look for move or resize grab */
|
|
for (i = 0; i < s->maxGrab; i++)
|
|
if (s->grabs[i].active &&
|
|
(strcmp ("move", s->grabs[i].name) == 0 ||
|
|
strcmp ("resize", s->grabs[i].name) == 0))
|
|
break;
|
|
|
|
/* only reset savedOriginal if move or resize is active */
|
|
if (i < s->maxGrab)
|
|
pw->savedOriginal = FALSE;
|
|
}
|
|
|
|
UNWRAP (ps, w->screen, windowGrabNotify);
|
|
(*w->screen->windowGrabNotify) (w, x, y, state, mask);
|
|
WRAP (ps, w->screen, windowGrabNotify, placeWindowGrabNotify);
|
|
}
|
|
|
|
static CompOption *
|
|
placeGetScreenOptions (CompPlugin *plugin,
|
|
CompScreen *screen,
|
|
int *count)
|
|
{
|
|
PLACE_SCREEN (screen);
|
|
|
|
*count = NUM_OPTIONS (ps);
|
|
return ps->opt;
|
|
}
|
|
|
|
static Bool
|
|
placeSetScreenOption (CompPlugin *plugin,
|
|
CompScreen *screen,
|
|
const 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_POSITION_MATCHES:
|
|
case PLACE_SCREEN_OPTION_VIEWPORT_MATCHES:
|
|
case PLACE_SCREEN_OPTION_MODE_MATCHES:
|
|
if (compSetOptionList (o, value))
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < o->value.list.nValue; i++)
|
|
matchUpdate (screen->display, &o->value.list.value[i].match);
|
|
|
|
return TRUE;
|
|
}
|
|
break;
|
|
default:
|
|
if (compSetOption (o, value))
|
|
return TRUE;
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
placeSendWindowMaximizationRequest (CompWindow *w)
|
|
{
|
|
XEvent xev;
|
|
CompDisplay *d = w->screen->display;
|
|
|
|
xev.xclient.type = ClientMessage;
|
|
xev.xclient.display = d->display;
|
|
xev.xclient.format = 32;
|
|
|
|
xev.xclient.message_type = d->winStateAtom;
|
|
xev.xclient.window = w->id;
|
|
|
|
xev.xclient.data.l[0] = 1;
|
|
xev.xclient.data.l[1] = d->winStateMaximizedHorzAtom;
|
|
xev.xclient.data.l[2] = d->winStateMaximizedVertAtom;
|
|
xev.xclient.data.l[3] = 0;
|
|
xev.xclient.data.l[4] = 0;
|
|
|
|
XSendEvent (d->display, w->screen->root, FALSE,
|
|
SubstructureRedirectMask | SubstructureNotifyMask, &xev);
|
|
}
|
|
|
|
static Bool
|
|
placeGetPointerPosition (CompScreen *s,
|
|
int *x,
|
|
int *y)
|
|
{
|
|
Window wDummy;
|
|
int iDummy;
|
|
unsigned int uiDummy;
|
|
|
|
/* this means a server roundtrip, which kind of sucks; thus
|
|
this code should be removed as soon as we have software
|
|
cursor rendering and thus have a cached pointer coordinate */
|
|
return XQueryPointer (s->display->display, s->root,
|
|
&wDummy, &wDummy, x, y,
|
|
&iDummy, &iDummy, &uiDummy);
|
|
}
|
|
|
|
static Bool
|
|
rectangleIntersect (XRectangle *src1,
|
|
XRectangle *src2,
|
|
XRectangle *dest)
|
|
{
|
|
int destX, destY;
|
|
int destW, destH;
|
|
|
|
destX = MAX (src1->x, src2->x);
|
|
destY = MAX (src1->y, src2->y);
|
|
destW = MIN (src1->x + src1->width, src2->x + src2->width) - destX;
|
|
destH = MIN (src1->y + src1->height, src2->y + src2->height) - destY;
|
|
|
|
if (destW <= 0 || destH <= 0)
|
|
{
|
|
dest->width = 0;
|
|
dest->height = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
dest->x = destX;
|
|
dest->y = destY;
|
|
dest->width = destW;
|
|
dest->height = destH;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
getWindowExtentsRect (CompWindow *w,
|
|
XRectangle *rect)
|
|
{
|
|
rect->x = WIN_FULL_X (w);
|
|
rect->y = WIN_FULL_Y (w);
|
|
rect->width = WIN_FULL_W (w);
|
|
rect->height = WIN_FULL_H (w);
|
|
}
|
|
|
|
static Bool
|
|
rectOverlapsWindow (XRectangle *rect,
|
|
CompWindow **windows,
|
|
unsigned int winCount)
|
|
{
|
|
unsigned int i;
|
|
XRectangle dest;
|
|
|
|
for (i = 0; i < winCount; i++)
|
|
{
|
|
CompWindow *other = windows[i];
|
|
XRectangle otherRect;
|
|
|
|
switch (other->type) {
|
|
case CompWindowTypeDockMask:
|
|
case CompWindowTypeSplashMask:
|
|
case CompWindowTypeDesktopMask:
|
|
case CompWindowTypeDialogMask:
|
|
case CompWindowTypeModalDialogMask:
|
|
case CompWindowTypeFullscreenMask:
|
|
case CompWindowTypeUnknownMask:
|
|
break;
|
|
case CompWindowTypeNormalMask:
|
|
case CompWindowTypeUtilMask:
|
|
case CompWindowTypeToolbarMask:
|
|
case CompWindowTypeMenuMask:
|
|
getWindowExtentsRect (other, &otherRect);
|
|
|
|
if (rectangleIntersect (rect, &otherRect, &dest))
|
|
return TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static int
|
|
compareLeftmost (const void *a,
|
|
const void *b)
|
|
{
|
|
CompWindow *aw = *((CompWindow **) a);
|
|
CompWindow *bw = *((CompWindow **) b);
|
|
int ax, bx;
|
|
|
|
ax = WIN_FULL_X (aw);
|
|
bx = WIN_FULL_X (bw);
|
|
|
|
if (ax < bx)
|
|
return -1;
|
|
else if (ax > bx)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
compareTopmost (const void *a,
|
|
const void *b)
|
|
{
|
|
CompWindow *aw = *((CompWindow **) a);
|
|
CompWindow *bw = *((CompWindow **) b);
|
|
int ay, by;
|
|
|
|
ay = WIN_FULL_Y (aw);
|
|
by = WIN_FULL_Y (bw);
|
|
|
|
if (ay < by)
|
|
return -1;
|
|
else if (ay > by)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
compareNorthWestCorner (const void *a,
|
|
const void *b)
|
|
{
|
|
CompWindow *aw = *((CompWindow **) a);
|
|
CompWindow *bw = *((CompWindow **) b);
|
|
int fromOriginA;
|
|
int fromOriginB;
|
|
int ax, ay, bx, by;
|
|
|
|
ax = WIN_FULL_X (aw);
|
|
ay = WIN_FULL_Y (aw);
|
|
|
|
bx = WIN_FULL_X (bw);
|
|
by = WIN_FULL_Y (bw);
|
|
|
|
/* probably there's a fast good-enough-guess we could use here. */
|
|
fromOriginA = sqrt (ax * ax + ay * ay);
|
|
fromOriginB = sqrt (bx * bx + by * by);
|
|
|
|
if (fromOriginA < fromOriginB)
|
|
return -1;
|
|
else if (fromOriginA > fromOriginB)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
centerTileRectInArea (XRectangle *rect,
|
|
XRectangle *workArea)
|
|
{
|
|
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 = (workArea->width % (rect->width + 1)) / 2;
|
|
rect->x = workArea->x + fluff;
|
|
|
|
fluff = (workArea->height % (rect->height + 1)) / 3;
|
|
rect->y = workArea->y + fluff;
|
|
}
|
|
|
|
static Bool
|
|
rectFitsInWorkarea (XRectangle *workArea,
|
|
XRectangle *rect)
|
|
{
|
|
if (rect->x < workArea->x)
|
|
return FALSE;
|
|
|
|
if (rect->y < workArea->y)
|
|
return FALSE;
|
|
|
|
if (rect->x + rect->width > workArea->x + workArea->width)
|
|
return FALSE;
|
|
|
|
if (rect->y + rect->height > workArea->y + workArea->height)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* 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 Bool
|
|
placeCascadeFindFirstFit (CompWindow *w,
|
|
CompWindow **windows,
|
|
unsigned int winCount,
|
|
XRectangle *workArea,
|
|
int x,
|
|
int y,
|
|
int *newX,
|
|
int *newY)
|
|
{
|
|
/* 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.
|
|
*/
|
|
Bool retval = FALSE;
|
|
unsigned int i, allocSize = winCount * sizeof (CompWindow *);
|
|
CompWindow **belowSorted, **rightSorted;
|
|
XRectangle rect;
|
|
|
|
belowSorted = malloc (allocSize);
|
|
if (!belowSorted)
|
|
return FALSE;
|
|
|
|
rightSorted = malloc (allocSize);
|
|
if (!rightSorted)
|
|
{
|
|
free (belowSorted);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Below each window */
|
|
memcpy (belowSorted, windows, allocSize);
|
|
qsort (belowSorted, winCount, sizeof (CompWindow *), compareLeftmost);
|
|
qsort (belowSorted, winCount, sizeof (CompWindow *), compareTopmost);
|
|
|
|
/* To the right of each window */
|
|
memcpy (rightSorted, windows, allocSize);
|
|
qsort (rightSorted, winCount, sizeof (CompWindow *), compareTopmost);
|
|
qsort (rightSorted, winCount, sizeof (CompWindow *), compareLeftmost);
|
|
|
|
getWindowExtentsRect (w, &rect);
|
|
|
|
centerTileRectInArea (&rect, workArea);
|
|
|
|
if (rectFitsInWorkarea (workArea, &rect) &&
|
|
!rectOverlapsWindow (&rect, windows, winCount))
|
|
{
|
|
*newX = rect.x + w->input.left;
|
|
*newY = rect.y + w->input.top;
|
|
|
|
retval = TRUE;
|
|
}
|
|
|
|
if (!retval)
|
|
{
|
|
/* try below each window */
|
|
for (i = 0; i < winCount && !retval; i++)
|
|
{
|
|
XRectangle outerRect;
|
|
|
|
getWindowExtentsRect (belowSorted[i], &outerRect);
|
|
|
|
rect.x = outerRect.x;
|
|
rect.y = outerRect.y + outerRect.height;
|
|
|
|
if (rectFitsInWorkarea (workArea, &rect) &&
|
|
!rectOverlapsWindow (&rect, belowSorted, winCount))
|
|
{
|
|
*newX = rect.x + w->input.left;
|
|
*newY = rect.y + w->input.top;
|
|
retval = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!retval)
|
|
{
|
|
/* try to the right of each window */
|
|
for (i = 0; i < winCount && !retval; i++)
|
|
{
|
|
XRectangle outerRect;
|
|
|
|
getWindowExtentsRect (rightSorted[i], &outerRect);
|
|
|
|
rect.x = outerRect.x + outerRect.width;
|
|
rect.y = outerRect.y;
|
|
|
|
if (rectFitsInWorkarea (workArea, &rect) &&
|
|
!rectOverlapsWindow (&rect, rightSorted, winCount))
|
|
{
|
|
*newX = rect.x + w->input.left;
|
|
*newY = rect.y + w->input.top;
|
|
retval = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
free (belowSorted);
|
|
free (rightSorted);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static void
|
|
placeCascadeFindNext (CompWindow *w,
|
|
CompWindow **windows,
|
|
unsigned int winCount,
|
|
XRectangle *workArea,
|
|
int x,
|
|
int y,
|
|
int *newX,
|
|
int *newY)
|
|
{
|
|
CompWindow **sorted;
|
|
unsigned int allocSize = winCount * sizeof (CompWindow *);
|
|
int cascadeX, cascadeY;
|
|
int xThreshold, yThreshold;
|
|
int winWidth, winHeight;
|
|
int i, cascadeStage;
|
|
|
|
sorted = malloc (allocSize);
|
|
if (!sorted)
|
|
return;
|
|
|
|
memcpy (sorted, windows, allocSize);
|
|
qsort (sorted, winCount, sizeof (CompWindow *), compareNorthWestCorner);
|
|
|
|
/* 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
|
|
|
|
xThreshold = MAX (w->input.left, CASCADE_FUZZ);
|
|
yThreshold = MAX (w->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.
|
|
*/
|
|
|
|
cascadeX = MAX (0, workArea->x);
|
|
cascadeY = MAX (0, workArea->y);
|
|
|
|
/* Find first cascade position that's not used. */
|
|
|
|
winWidth = WIN_FULL_W (w);
|
|
winHeight = WIN_FULL_H (w);
|
|
|
|
cascadeStage = 0;
|
|
for (i = 0; i < winCount; i++)
|
|
{
|
|
CompWindow *wi = sorted[i];
|
|
int wx, wy;
|
|
|
|
/* we want frame position, not window position */
|
|
wx = WIN_FULL_X (wi);
|
|
wy = WIN_FULL_Y (wi);
|
|
|
|
if (abs (wx - cascadeX) < xThreshold &&
|
|
abs (wy - cascadeY) < yThreshold)
|
|
{
|
|
/* 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 = cascadeX = wi->serverX;
|
|
wy = cascadeY = wi->serverY;
|
|
|
|
/* If we go off the screen, start over with a new cascade */
|
|
if ((cascadeX + winWidth > workArea->x + workArea->width) ||
|
|
(cascadeY + winHeight > workArea->y + workArea->height))
|
|
{
|
|
cascadeX = MAX (0, workArea->x);
|
|
cascadeY = MAX (0, workArea->y);
|
|
|
|
#define CASCADE_INTERVAL 50 /* space between top-left corners of cascades */
|
|
|
|
cascadeStage += 1;
|
|
cascadeX += CASCADE_INTERVAL * cascadeStage;
|
|
|
|
/* start over with a new cascade translated to the right,
|
|
* unless we are out of space
|
|
*/
|
|
if (cascadeX + winWidth < workArea->x + workArea->width)
|
|
{
|
|
i = 0;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
/* All out of space, this cascade_x won't work */
|
|
cascadeX = MAX (0, workArea->x);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Keep searching for a further-down-the-diagonal window. */
|
|
}
|
|
}
|
|
|
|
/* cascade_x and cascade_y will match the last window in the list
|
|
* that was "in the way" (in the approximate cascade diagonal)
|
|
*/
|
|
|
|
free (sorted);
|
|
|
|
/* Convert coords to position of window, not position of frame. */
|
|
*newX = cascadeX + w->input.left;
|
|
*newY = cascadeY + w->input.top;
|
|
}
|
|
|
|
static void
|
|
placeCascade (CompWindow *w,
|
|
XRectangle *workArea,
|
|
int *x,
|
|
int *y)
|
|
{
|
|
CompWindow **windows;
|
|
CompWindow *wi;
|
|
unsigned int count = 0;
|
|
|
|
/* get the total window count */
|
|
for (wi = w->screen->windows; wi; wi = wi->next)
|
|
count++;
|
|
|
|
windows = malloc (sizeof (CompWindow *) * count);
|
|
if (!windows)
|
|
return;
|
|
|
|
/* 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 = w->screen->windows, count = 0; wi; wi = wi->next)
|
|
{
|
|
if (!IS_PLACE_RELEVANT (wi, w))
|
|
continue;
|
|
|
|
if (wi->type & (CompWindowTypeFullscreenMask |
|
|
CompWindowTypeUnknownMask))
|
|
continue;
|
|
|
|
if (wi->serverX >= workArea->x + workArea->width ||
|
|
wi->serverX + wi->serverWidth <= workArea->x ||
|
|
wi->serverY >= workArea->y + workArea->height ||
|
|
wi->serverY + wi->serverHeight <= workArea->y)
|
|
continue;
|
|
|
|
windows[count++] = wi;
|
|
}
|
|
|
|
if (!placeCascadeFindFirstFit (w, windows, count, workArea, *x, *y, x, y))
|
|
{
|
|
/* if the window wasn't placed at the origin of screen,
|
|
* cascade it onto the current screen
|
|
*/
|
|
placeCascadeFindNext (w, windows, count, workArea, *x, *y, x, y);
|
|
}
|
|
|
|
free (windows);
|
|
}
|
|
|
|
static void
|
|
placeCentered (CompWindow *w,
|
|
XRectangle *workArea,
|
|
int *x,
|
|
int *y)
|
|
{
|
|
*x = workArea->x + (workArea->width - w->serverWidth) / 2;
|
|
*y = workArea->y + (workArea->height - w->serverHeight) / 2;
|
|
}
|
|
|
|
static void
|
|
placeRandom (CompWindow *w,
|
|
XRectangle *workArea,
|
|
int *x,
|
|
int *y)
|
|
{
|
|
int remainX, remainY;
|
|
|
|
*x = workArea->x;
|
|
*y = workArea->y;
|
|
|
|
remainX = workArea->width - w->serverWidth;
|
|
if (remainX > 0)
|
|
*x += rand () % remainX;
|
|
|
|
remainY = workArea->height - w->serverHeight;
|
|
if (remainY > 0)
|
|
*y += rand () % remainY;
|
|
}
|
|
|
|
static void
|
|
placePointer (CompWindow *w,
|
|
XRectangle *workArea,
|
|
int *x,
|
|
int *y)
|
|
{
|
|
int xPointer, yPointer;
|
|
|
|
if (placeGetPointerPosition (w->screen, &xPointer, &yPointer))
|
|
{
|
|
*x = xPointer - (w->serverWidth / 2);
|
|
*y = yPointer - (w->serverHeight / 2);
|
|
}
|
|
else
|
|
{
|
|
/* use centered as fallback */
|
|
placeCentered (w, workArea, x, y);
|
|
}
|
|
}
|
|
|
|
/* overlap types */
|
|
#define NONE 0
|
|
#define H_WRONG -1
|
|
#define W_WRONG -2
|
|
|
|
static void
|
|
placeSmart (CompWindow *w,
|
|
XRectangle *workArea,
|
|
int *x,
|
|
int *y)
|
|
{
|
|
/*
|
|
* SmartPlacement by Cristian Tibirna (tibirna@kde.org)
|
|
* adapted for kwm (16-19jan98) and for kwin (16Nov1999) using (with
|
|
* permission) ideas from fvwm, authored by
|
|
* Anthony Martin (amartin@engr.csulb.edu).
|
|
* Xinerama supported added by Balaji Ramani (balaji@yablibli.com)
|
|
* with ideas from xfce.
|
|
* adapted for Compiz by Bellegarde Cedric (gnumdk(at)gmail.com)
|
|
*/
|
|
CompWindow *wi;
|
|
int overlap, minOverlap = 0;
|
|
int xOptimal, yOptimal;
|
|
int possible;
|
|
|
|
/* temp coords */
|
|
int cxl, cxr, cyt, cyb;
|
|
/* temp coords */
|
|
int xl, xr, yt, yb;
|
|
/* temp holder */
|
|
int basket;
|
|
/* CT lame flag. Don't like it. What else would do? */
|
|
Bool firstPass = TRUE;
|
|
|
|
/* get the maximum allowed windows space */
|
|
int xTmp = workArea->x;
|
|
int yTmp = workArea->y;
|
|
|
|
/* client gabarit */
|
|
int cw = WIN_FULL_W (w) - 1;
|
|
int ch = WIN_FULL_H (w) - 1;
|
|
|
|
xOptimal = xTmp;
|
|
yOptimal = yTmp;
|
|
|
|
/* loop over possible positions */
|
|
do
|
|
{
|
|
/* test if enough room in x and y directions */
|
|
if (yTmp + ch > workArea->y + workArea->height && ch < workArea->height)
|
|
overlap = H_WRONG; /* this throws the algorithm to an exit */
|
|
else if (xTmp + cw > workArea->x + workArea->width)
|
|
overlap = W_WRONG;
|
|
else
|
|
{
|
|
overlap = NONE; /* initialize */
|
|
|
|
cxl = xTmp;
|
|
cxr = xTmp + cw;
|
|
cyt = yTmp;
|
|
cyb = yTmp + ch;
|
|
|
|
for (wi = w->screen->windows; wi; wi = wi->next)
|
|
{
|
|
if (!IS_PLACE_RELEVANT (wi, w))
|
|
continue;
|
|
|
|
xl = WIN_FULL_X (wi);
|
|
yt = WIN_FULL_Y (wi);
|
|
xr = WIN_FULL_X (wi) + WIN_FULL_W (wi);
|
|
yb = WIN_FULL_Y (wi) + WIN_FULL_H (wi);
|
|
|
|
/* if windows overlap, calc the overall overlapping */
|
|
if (cxl < xr && cxr > xl && cyt < yb && cyb > yt)
|
|
{
|
|
xl = MAX (cxl, xl);
|
|
xr = MIN (cxr, xr);
|
|
yt = MAX (cyt, yt);
|
|
yb = MIN (cyb, yb);
|
|
|
|
if (wi->state & CompWindowStateAboveMask)
|
|
overlap += 16 * (xr - xl) * (yb - yt);
|
|
else if (wi->state & CompWindowStateBelowMask)
|
|
overlap += 0;
|
|
else
|
|
overlap += (xr - xl) * (yb - yt);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* CT first time we get no overlap we stop */
|
|
if (overlap == NONE)
|
|
{
|
|
xOptimal = xTmp;
|
|
yOptimal = yTmp;
|
|
break;
|
|
}
|
|
|
|
if (firstPass)
|
|
{
|
|
firstPass = FALSE;
|
|
minOverlap = overlap;
|
|
}
|
|
/* CT save the best position and the minimum overlap up to now */
|
|
else if (overlap >= NONE && overlap < minOverlap)
|
|
{
|
|
minOverlap = overlap;
|
|
xOptimal = xTmp;
|
|
yOptimal = yTmp;
|
|
}
|
|
|
|
/* really need to loop? test if there's any overlap */
|
|
if (overlap > NONE)
|
|
{
|
|
possible = workArea->x + workArea->width;
|
|
|
|
if (possible - cw > xTmp)
|
|
possible -= cw;
|
|
|
|
/* compare to the position of each client on the same desk */
|
|
for (wi = w->screen->windows; wi; wi = wi->next)
|
|
{
|
|
if (!IS_PLACE_RELEVANT (wi, w))
|
|
continue;
|
|
|
|
xl = WIN_FULL_X (wi);
|
|
yt = WIN_FULL_Y (wi);
|
|
xr = WIN_FULL_X (wi) + WIN_FULL_W (wi);
|
|
yb = WIN_FULL_X (wi) + WIN_FULL_H (wi);
|
|
|
|
/* if not enough room above or under the current
|
|
* client determine the first non-overlapped x position
|
|
*/
|
|
if (yTmp < yb && yt < ch + yTmp)
|
|
{
|
|
if (xr > xTmp && possible > xr)
|
|
possible = xr;
|
|
|
|
basket = xl - cw;
|
|
if (basket > xTmp && possible > basket)
|
|
possible = basket;
|
|
}
|
|
}
|
|
xTmp = possible;
|
|
}
|
|
/* else ==> not enough x dimension (overlap was wrong on horizontal) */
|
|
else if (overlap == W_WRONG)
|
|
{
|
|
xTmp = workArea->x;
|
|
possible = workArea->y + workArea->height;
|
|
|
|
if (possible - ch > yTmp)
|
|
possible -= ch;
|
|
|
|
/* test the position of each window on the desk */
|
|
for (wi = w->screen->windows; wi; wi = wi->next)
|
|
{
|
|
if (!IS_PLACE_RELEVANT (wi, w))
|
|
continue;
|
|
|
|
xl = WIN_FULL_X (wi);
|
|
yt = WIN_FULL_Y (wi);
|
|
xr = WIN_FULL_X (wi) + WIN_FULL_W (wi);
|
|
yb = WIN_FULL_X (wi) + WIN_FULL_H (wi);
|
|
|
|
/* if not enough room to the left or right of the current
|
|
* client determine the first non-overlapped y position
|
|
*/
|
|
if (yb > yTmp && possible > yb)
|
|
possible = yb;
|
|
|
|
basket = yt - ch;
|
|
if (basket > yTmp && possible > basket)
|
|
possible = basket;
|
|
}
|
|
yTmp = possible;
|
|
}
|
|
}
|
|
while (overlap != NONE && overlap != H_WRONG &&
|
|
yTmp < workArea->y + workArea->height);
|
|
|
|
if (ch >= workArea->height)
|
|
yOptimal = workArea->y;
|
|
|
|
*x = xOptimal + w->input.left;
|
|
*y = yOptimal + w->input.top;
|
|
}
|
|
|
|
static PlacementStrategy
|
|
placeGetStrategyForWindow (CompWindow *w)
|
|
{
|
|
CompMatch *match;
|
|
|
|
PLACE_SCREEN (w->screen);
|
|
|
|
if (w->type & (CompWindowTypeDockMask | CompWindowTypeDesktopMask |
|
|
CompWindowTypeUtilMask | CompWindowTypeToolbarMask |
|
|
CompWindowTypeMenuMask | CompWindowTypeFullscreenMask |
|
|
CompWindowTypeUnknownMask))
|
|
{
|
|
/* assume the app knows best how to place these */
|
|
return NoPlacement;
|
|
}
|
|
|
|
if (w->wmType & (CompWindowTypeDockMask | CompWindowTypeDesktopMask))
|
|
{
|
|
/* see above */
|
|
return NoPlacement;
|
|
}
|
|
|
|
/* no placement for unmovable windows */
|
|
if (!(w->actions & CompWindowActionMoveMask))
|
|
return NoPlacement;
|
|
|
|
match = &ps->opt[PLACE_SCREEN_OPTION_FORCE_PLACEMENT].value.match;
|
|
if (!matchEval (match, w))
|
|
{
|
|
if ((w->type & CompWindowTypeNormalMask) ||
|
|
ps->opt[PLACE_SCREEN_OPTION_WORKAROUND].value.b)
|
|
{
|
|
/* Only accept USPosition on non-normal windows if workarounds are
|
|
* enabled because apps claiming the user set -geometry for a
|
|
* dialog or dock are most likely wrong
|
|
*/
|
|
if (w->sizeHints.flags & USPosition)
|
|
return ConstrainOnly;
|
|
}
|
|
|
|
if (w->sizeHints.flags & PPosition)
|
|
return ConstrainOnly;
|
|
}
|
|
|
|
if (w->transientFor &&
|
|
(w->type & (CompWindowTypeDialogMask |
|
|
CompWindowTypeModalDialogMask)))
|
|
{
|
|
CompWindow *parent = findWindowAtScreen (w->screen, w->transientFor);
|
|
if (parent && parent->managed)
|
|
return PlaceOverParent;
|
|
}
|
|
|
|
if (w->type & (CompWindowTypeDialogMask |
|
|
CompWindowTypeModalDialogMask |
|
|
CompWindowTypeSplashMask))
|
|
{
|
|
return PlaceCenteredOnScreen;
|
|
}
|
|
|
|
return PlaceAndConstrain;
|
|
}
|
|
|
|
static CompOutput *
|
|
placeGetPlacementOutput (CompWindow *w,
|
|
int mode,
|
|
PlacementStrategy strategy,
|
|
int x,
|
|
int y)
|
|
{
|
|
CompScreen *s = w->screen;
|
|
int output = -1;
|
|
int multiMode;
|
|
|
|
PLACE_SCREEN (s);
|
|
|
|
/* short cut: it makes no sense to determine a placement
|
|
output if there is only one output */
|
|
if (s->nOutputDev == 1)
|
|
return &s->outputDev[0];
|
|
|
|
switch (strategy) {
|
|
case PlaceOverParent:
|
|
{
|
|
CompWindow *parent;
|
|
|
|
parent = findWindowAtScreen (s, w->transientFor);
|
|
if (parent)
|
|
output = outputDeviceForWindow (parent);
|
|
}
|
|
break;
|
|
case ConstrainOnly:
|
|
output = outputDeviceForGeometry (s, x, y,
|
|
w->serverWidth,
|
|
w->serverHeight,
|
|
w->serverBorderWidth);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (output >= 0)
|
|
return &s->outputDev[output];
|
|
|
|
multiMode = ps->opt[PLACE_SCREEN_OPTION_MULTIOUTPUT_MODE].value.i;
|
|
/* force 'output with pointer' mode for placement under pointer */
|
|
if (mode == PLACE_MODE_POINTER)
|
|
multiMode = PLACE_MOMODE_POINTER;
|
|
|
|
switch (multiMode) {
|
|
case PLACE_MOMODE_CURRENT:
|
|
output = s->currentOutputDev;
|
|
break;
|
|
case PLACE_MOMODE_POINTER:
|
|
{
|
|
int xPointer, yPointer;
|
|
|
|
if (placeGetPointerPosition (s, &xPointer, &yPointer))
|
|
output = outputDeviceForPoint (s, xPointer, yPointer);
|
|
}
|
|
break;
|
|
case PLACE_MOMODE_ACTIVEWIN:
|
|
{
|
|
CompWindow *active;
|
|
|
|
active = findWindowAtScreen (s, s->display->activeWindow);
|
|
if (active)
|
|
output = outputDeviceForWindow (active);
|
|
}
|
|
break;
|
|
case PLACE_MOMODE_FULLSCREEN:
|
|
/* only place on fullscreen output if not placing centered, as the
|
|
constraining will move the window away from the center otherwise */
|
|
if (strategy != PlaceCenteredOnScreen)
|
|
return &s->fullscreenOutput;
|
|
break;
|
|
}
|
|
|
|
if (output < 0)
|
|
output = s->currentOutputDev;
|
|
|
|
return &s->outputDev[output];
|
|
}
|
|
|
|
static int
|
|
placeGetPlacementMode (CompWindow *w)
|
|
{
|
|
CompListValue *matches, *modes;
|
|
int i, min;
|
|
|
|
PLACE_SCREEN (w->screen);
|
|
|
|
matches = &ps->opt[PLACE_SCREEN_OPTION_MODE_MATCHES].value.list;
|
|
modes = &ps->opt[PLACE_SCREEN_OPTION_MODE_MODES].value.list;
|
|
min = MIN (matches->nValue, modes->nValue);
|
|
|
|
for (i = 0; i < min; i++)
|
|
if (matchEval (&matches->value[i].match, w))
|
|
return modes->value[i].i;
|
|
|
|
return ps->opt[PLACE_SCREEN_OPTION_MODE].value.i;
|
|
}
|
|
|
|
static void
|
|
placeConstrainToWorkarea (CompWindow *w,
|
|
XRectangle *workArea,
|
|
int *x,
|
|
int *y)
|
|
{
|
|
CompWindowExtents extents;
|
|
int delta;
|
|
|
|
extents.left = *x - w->input.left;
|
|
extents.top = *y - w->input.top;
|
|
extents.right = *x + w->serverWidth + w->input.right;
|
|
extents.bottom = *y + w->serverHeight + w->input.bottom;
|
|
|
|
delta = workArea->x + workArea->width - extents.right;
|
|
if (delta < 0)
|
|
extents.left += delta;
|
|
|
|
delta = workArea->x - extents.left;
|
|
if (delta > 0)
|
|
extents.left += delta;
|
|
|
|
delta = workArea->y + workArea->height - extents.bottom;
|
|
if (delta < 0)
|
|
extents.top += delta;
|
|
|
|
delta = workArea->y - extents.top;
|
|
if (delta > 0)
|
|
extents.top += delta;
|
|
|
|
*x = extents.left + w->input.left;
|
|
*y = extents.top + w->input.top;
|
|
}
|
|
|
|
static Bool
|
|
placeDoWindowPlacement (CompWindow *w,
|
|
int x,
|
|
int y,
|
|
int *newX,
|
|
int *newY)
|
|
{
|
|
CompScreen *s = w->screen;
|
|
XRectangle workArea;
|
|
int targetVpX, targetVpY;
|
|
CompOutput *output;
|
|
PlacementStrategy strategy;
|
|
Bool keepInWorkarea;
|
|
int mode;
|
|
|
|
if (placeMatchPosition (w, &x, &y, &keepInWorkarea))
|
|
{
|
|
strategy = keepInWorkarea ? ConstrainOnly : NoPlacement;
|
|
}
|
|
else
|
|
{
|
|
strategy = placeGetStrategyForWindow (w);
|
|
if (strategy == NoPlacement)
|
|
return FALSE;
|
|
}
|
|
|
|
mode = placeGetPlacementMode (w);
|
|
output = placeGetPlacementOutput (w, mode, strategy, x, y);
|
|
workArea = output->workArea;
|
|
|
|
targetVpX = w->initialViewportX;
|
|
targetVpY = w->initialViewportY;
|
|
|
|
if (strategy == PlaceOverParent)
|
|
{
|
|
CompWindow *parent;
|
|
|
|
parent = findWindowAtScreen (s, w->transientFor);
|
|
if (parent)
|
|
{
|
|
/* center over parent horizontally */
|
|
x = parent->serverX + (parent->serverWidth / 2) -
|
|
(w->serverWidth / 2);
|
|
|
|
/* "visually" center vertically, leaving twice as much space below
|
|
as on top */
|
|
y = parent->serverY + (parent->serverHeight - w->serverHeight) / 3;
|
|
|
|
/* put top of child's frame, not top of child's client */
|
|
y += w->input.top;
|
|
|
|
/* if parent is visible on current viewport, clip to work area;
|
|
don't constrain further otherwise */
|
|
if (parent->serverX < parent->screen->width &&
|
|
parent->serverX + parent->serverWidth > 0 &&
|
|
parent->serverY < parent->screen->height &&
|
|
parent->serverY + parent->serverHeight > 0)
|
|
{
|
|
defaultViewportForWindow (parent, &targetVpX, &targetVpY);
|
|
strategy = ConstrainOnly;
|
|
}
|
|
else
|
|
{
|
|
strategy = NoPlacement;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (strategy == PlaceCenteredOnScreen)
|
|
{
|
|
/* center window on current output device */
|
|
|
|
x = output->region.extents.x1;
|
|
y = output->region.extents.y1;
|
|
|
|
x += (output->width - w->serverWidth) / 2;
|
|
y += (output->height - w->serverHeight) / 2;
|
|
|
|
strategy = ConstrainOnly;
|
|
}
|
|
|
|
workArea.x += (targetVpX - s->x) * s->width;
|
|
workArea.y += (targetVpY - s->y) * s->height;
|
|
|
|
if (strategy == PlaceOnly || strategy == PlaceAndConstrain)
|
|
{
|
|
switch (mode) {
|
|
case PLACE_MODE_CASCADE:
|
|
placeCascade (w, &workArea, &x, &y);
|
|
break;
|
|
case PLACE_MODE_CENTERED:
|
|
placeCentered (w, &workArea, &x, &y);
|
|
break;
|
|
case PLACE_MODE_RANDOM:
|
|
placeRandom (w, &workArea, &x, &y);
|
|
break;
|
|
case PLACE_MODE_POINTER:
|
|
placePointer (w, &workArea, &x, &y);
|
|
break;
|
|
case PLACE_MODE_MAXIMIZE:
|
|
placeSendWindowMaximizationRequest (w);
|
|
break;
|
|
case PLACE_MODE_SMART:
|
|
placeSmart (w, &workArea, &x, &y);
|
|
break;
|
|
}
|
|
|
|
/* When placing to the fullscreen output, constrain to one
|
|
output nevertheless */
|
|
if (output->id == ~0)
|
|
{
|
|
int id;
|
|
|
|
id = outputDeviceForGeometry (s, x, y,
|
|
w->serverWidth,
|
|
w->serverHeight,
|
|
w->serverBorderWidth);
|
|
getWorkareaForOutput (s, id, &workArea);
|
|
|
|
workArea.x += (targetVpX - s->x) * s->width;
|
|
workArea.y += (targetVpY - s->y) * s->height;
|
|
}
|
|
|
|
/* 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 ((w->actions & MAXIMIZE_STATE) == MAXIMIZE_STATE &&
|
|
(w->mwmDecor & (MwmDecorAll | MwmDecorTitle)) &&
|
|
!(w->state & CompWindowStateFullscreenMask))
|
|
{
|
|
if (WIN_FULL_W (w) >= workArea.width &&
|
|
WIN_FULL_H (w) >= workArea.height)
|
|
{
|
|
placeSendWindowMaximizationRequest (w);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (strategy == ConstrainOnly || strategy == PlaceAndConstrain)
|
|
placeConstrainToWorkarea (w, &workArea, &x, &y);
|
|
|
|
*newX = x;
|
|
*newY = y;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static XRectangle
|
|
placeDoValidateWindowResizeRequest (CompWindow *w,
|
|
unsigned int *mask,
|
|
XWindowChanges *xwc,
|
|
Bool sizeOnly,
|
|
Bool clampToViewport)
|
|
{
|
|
CompScreen *s = w->screen;
|
|
XRectangle workArea;
|
|
int x, y, left, right, top, bottom;
|
|
int output;
|
|
|
|
if (clampToViewport)
|
|
{
|
|
/* left, right, top, bottom target coordinates, clamped to viewport
|
|
sizes as we don't need to validate movements to other viewports;
|
|
we are only interested in inner-viewport movements */
|
|
x = xwc->x % s->width;
|
|
if ((x + xwc->width) < 0)
|
|
x += s->width;
|
|
|
|
y = xwc->y % s->height;
|
|
if ((y + xwc->height) < 0)
|
|
y += s->height;
|
|
}
|
|
else
|
|
{
|
|
x = xwc->x;
|
|
y = xwc->y;
|
|
}
|
|
|
|
left = x - w->input.left;
|
|
right = x + xwc->width + w->input.right;
|
|
top = y - w->input.top;
|
|
bottom = y + xwc->height + w->input.bottom;
|
|
|
|
output = outputDeviceForGeometry (s,
|
|
xwc->x, xwc->y,
|
|
xwc->width, xwc->height,
|
|
w->serverBorderWidth);
|
|
|
|
getWorkareaForOutput (s, output, &workArea);
|
|
|
|
if (clampToViewport &&
|
|
xwc->width >= workArea.width &&
|
|
xwc->height >= workArea.height)
|
|
{
|
|
if ((w->actions & MAXIMIZE_STATE) == MAXIMIZE_STATE &&
|
|
(w->mwmDecor & (MwmDecorAll | MwmDecorTitle)) &&
|
|
!(w->state & CompWindowStateFullscreenMask))
|
|
{
|
|
placeSendWindowMaximizationRequest (w);
|
|
}
|
|
}
|
|
|
|
if ((right - left) > workArea.width)
|
|
{
|
|
left = workArea.x;
|
|
right = left + workArea.width;
|
|
}
|
|
else
|
|
{
|
|
if (left < workArea.x)
|
|
{
|
|
right += workArea.x - left;
|
|
left = workArea.x;
|
|
}
|
|
|
|
if (right > (workArea.x + workArea.width))
|
|
{
|
|
left -= right - (workArea.x + workArea.width);
|
|
right = workArea.x + workArea.width;
|
|
}
|
|
}
|
|
|
|
if ((bottom - top) > workArea.height)
|
|
{
|
|
top = workArea.y;
|
|
bottom = top + workArea.height;
|
|
}
|
|
else
|
|
{
|
|
if (top < workArea.y)
|
|
{
|
|
bottom += workArea.y - top;
|
|
top = workArea.y;
|
|
}
|
|
|
|
if (bottom > (workArea.y + workArea.height))
|
|
{
|
|
top -= bottom - (workArea.y + workArea.height);
|
|
bottom = workArea.y + workArea.height;
|
|
}
|
|
}
|
|
|
|
/* bring left/right/top/bottom to actual window coordinates */
|
|
left += w->input.left;
|
|
right -= w->input.right;
|
|
top += w->input.top;
|
|
bottom -= w->input.bottom;
|
|
|
|
if ((right - left) != xwc->width)
|
|
{
|
|
xwc->width = right - left;
|
|
*mask |= CWWidth;
|
|
sizeOnly = FALSE;
|
|
}
|
|
|
|
if ((bottom - top) != xwc->height)
|
|
{
|
|
xwc->height = bottom - top;
|
|
*mask |= CWHeight;
|
|
sizeOnly = FALSE;
|
|
}
|
|
|
|
if (!sizeOnly)
|
|
{
|
|
if (left != x)
|
|
{
|
|
xwc->x += left - x;
|
|
*mask |= CWX;
|
|
}
|
|
|
|
if (top != y)
|
|
{
|
|
xwc->y += top - y;
|
|
*mask |= CWY;
|
|
}
|
|
}
|
|
|
|
return workArea;
|
|
}
|
|
|
|
static void
|
|
placeValidateWindowResizeRequest (CompWindow *w,
|
|
unsigned int *mask,
|
|
XWindowChanges *xwc,
|
|
unsigned int source)
|
|
{
|
|
CompScreen *s = w->screen;
|
|
Bool sizeOnly = FALSE;
|
|
|
|
PLACE_SCREEN (s);
|
|
|
|
UNWRAP (ps, s, validateWindowResizeRequest);
|
|
(*s->validateWindowResizeRequest) (w, mask, xwc, source);
|
|
WRAP (ps, s, validateWindowResizeRequest,
|
|
placeValidateWindowResizeRequest);
|
|
|
|
if (*mask == 0)
|
|
return;
|
|
|
|
if (source == ClientTypePager)
|
|
return;
|
|
|
|
if (w->state & CompWindowStateFullscreenMask)
|
|
return;
|
|
|
|
if (w->wmType & (CompWindowTypeDockMask |
|
|
CompWindowTypeDesktopMask))
|
|
return;
|
|
|
|
if (w->sizeHints.flags & USPosition)
|
|
{
|
|
/* only respect USPosition on normal windows if
|
|
workarounds are disabled, reason see above */
|
|
if (ps->opt[PLACE_SCREEN_OPTION_WORKAROUND].value.b ||
|
|
(w->type & CompWindowTypeNormalMask))
|
|
{
|
|
/* try to keep the window position intact for USPosition -
|
|
obviously we can't do that if we need to change the size */
|
|
sizeOnly = TRUE;
|
|
}
|
|
}
|
|
|
|
placeDoValidateWindowResizeRequest (w, mask, xwc, sizeOnly, TRUE);
|
|
}
|
|
|
|
static unsigned int
|
|
placeAddSupportedAtoms (CompScreen *s,
|
|
Atom *atoms,
|
|
unsigned int size)
|
|
{
|
|
unsigned int count;
|
|
|
|
PLACE_DISPLAY (s->display);
|
|
PLACE_SCREEN (s);
|
|
|
|
UNWRAP (ps, s, addSupportedAtoms);
|
|
count = (*s->addSupportedAtoms) (s, atoms, size);
|
|
WRAP (ps, s, addSupportedAtoms, placeAddSupportedAtoms);
|
|
|
|
if (count < size)
|
|
atoms[count++] = pd->fullPlacementAtom;
|
|
|
|
return count;
|
|
}
|
|
|
|
static Bool
|
|
placePlaceWindow (CompWindow *w,
|
|
int x,
|
|
int y,
|
|
int *newX,
|
|
int *newY)
|
|
{
|
|
CompScreen *s = w->screen;
|
|
Bool status;
|
|
|
|
PLACE_SCREEN (s);
|
|
|
|
UNWRAP (ps, s, placeWindow);
|
|
status = (*s->placeWindow) (w, x, y, newX, newY);
|
|
WRAP (ps, s, placeWindow, placePlaceWindow);
|
|
|
|
if (!status)
|
|
{
|
|
int viewportX, viewportY;
|
|
|
|
if (!placeDoWindowPlacement (w, x, y, newX, newY))
|
|
{
|
|
*newX = x;
|
|
*newY = y;
|
|
}
|
|
|
|
if (placeMatchViewport (w, &viewportX, &viewportY))
|
|
{
|
|
viewportX = MAX (MIN (viewportX, s->hsize - 1), 0);
|
|
viewportY = MAX (MIN (viewportY, s->vsize - 1), 0);
|
|
|
|
x = *newX % s->width;
|
|
if (x < 0)
|
|
x += s->width;
|
|
y = *newY % s->height;
|
|
if (y < 0)
|
|
y += s->height;
|
|
|
|
*newX = x + (viewportX - s->x) * s->width;
|
|
*newY = y + (viewportY - s->y) * s->height;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
placeDoHandleScreenSizeChange (CompScreen *s,
|
|
Bool firstPass)
|
|
{
|
|
CompWindow *w;
|
|
int vpX, vpY; /* holds x and y index of a window's target vp */
|
|
int shiftX, shiftY;
|
|
XRectangle vpRelRect;
|
|
XRectangle winRect;
|
|
XRectangle workArea;
|
|
int pivotX, pivotY;
|
|
unsigned int mask;
|
|
XWindowChanges xwc;
|
|
int curVpOffsetX = s->x * s->width;
|
|
int curVpOffsetY = s->y * s->height;
|
|
|
|
PLACE_SCREEN (s);
|
|
|
|
if (firstPass)
|
|
ps->strutWindowCount = 0;
|
|
else
|
|
{
|
|
if (ps->resChangeFallbackHandle)
|
|
{
|
|
compRemoveTimeout (ps->resChangeFallbackHandle);
|
|
ps->resChangeFallbackHandle = 0;
|
|
}
|
|
}
|
|
|
|
for (w = s->windows; w; w = w->next)
|
|
{
|
|
if (!w->managed)
|
|
continue;
|
|
|
|
PLACE_WINDOW (w);
|
|
|
|
if (firstPass)
|
|
{
|
|
/* count windows that have struts */
|
|
if (w->struts)
|
|
ps->strutWindowCount++;
|
|
|
|
/* for maximized/fullscreen windows, keep window coords before
|
|
screen resize, as they are sometimes automatically changed
|
|
before the 2nd pass */
|
|
if (w->type & CompWindowTypeFullscreenMask ||
|
|
(w->state & (CompWindowStateMaximizedVertMask |
|
|
CompWindowStateMaximizedHorzMask)))
|
|
{
|
|
pw->prevServerX = w->serverX;
|
|
pw->prevServerY = w->serverY;
|
|
}
|
|
}
|
|
|
|
if (w->wmType & (CompWindowTypeDockMask |
|
|
CompWindowTypeDesktopMask))
|
|
continue;
|
|
|
|
/* Also in the first pass, we save the rectangle of those windows
|
|
that don't already have a saved one. So, skip those that do. */
|
|
if (firstPass && pw->savedOriginal)
|
|
continue;
|
|
|
|
winRect.x = w->serverX;
|
|
winRect.y = w->serverY;
|
|
winRect.width = w->serverWidth;
|
|
winRect.height = w->serverHeight;
|
|
|
|
pivotX = winRect.x;
|
|
pivotY = winRect.y;
|
|
|
|
if (w->type & CompWindowTypeFullscreenMask ||
|
|
(w->state & (CompWindowStateMaximizedVertMask |
|
|
CompWindowStateMaximizedHorzMask)))
|
|
{
|
|
if (w->saveMask & CWX)
|
|
winRect.x = w->saveWc.x;
|
|
if (w->saveMask & CWY)
|
|
winRect.y = w->saveWc.y;
|
|
if (w->saveMask & CWWidth)
|
|
winRect.width = w->saveWc.width;
|
|
if (w->saveMask & CWHeight)
|
|
winRect.height = w->saveWc.height;
|
|
|
|
pivotX = pw->prevServerX;
|
|
pivotY = pw->prevServerY;
|
|
}
|
|
|
|
/* calculate target vp x, y index for window's pivot point */
|
|
vpX = pivotX / ps->prevWidth;
|
|
if (pivotX < 0)
|
|
vpX -= 1;
|
|
vpY = pivotY / ps->prevHeight;
|
|
if (pivotY < 0)
|
|
vpY -= 1;
|
|
|
|
/* if window's target vp is to the left of the leftmost viewport on that
|
|
row, assign its target vp column as 0 (-s->x rel. to current vp) */
|
|
if (s->x + vpX < 0)
|
|
vpX = -s->x;
|
|
|
|
/* if window's target vp is above the topmost viewport on that column,
|
|
assign its target vp row as 0 (-s->y rel. to current vp) */
|
|
if (s->y + vpY < 0)
|
|
vpY = -s->y;
|
|
|
|
if (pw->savedOriginal)
|
|
{
|
|
/* set position/size to saved original rectangle */
|
|
vpRelRect = pw->origVpRelRect;
|
|
|
|
xwc.x = pw->origVpRelRect.x + vpX * s->width;
|
|
xwc.y = pw->origVpRelRect.y + vpY * s->height;
|
|
}
|
|
else
|
|
{
|
|
/* set position/size to window's current rectangle
|
|
(with position relative to target viewport) */
|
|
vpRelRect.x = winRect.x - vpX * ps->prevWidth;
|
|
vpRelRect.y = winRect.y - vpY * ps->prevHeight;
|
|
vpRelRect.width = winRect.width;
|
|
vpRelRect.height = winRect.height;
|
|
|
|
xwc.x = winRect.x;
|
|
xwc.y = winRect.y;
|
|
|
|
shiftX = vpX * (s->width - ps->prevWidth);
|
|
shiftY = vpY * (s->height - ps->prevHeight);
|
|
|
|
/* if coords. relative to viewport are outside new viewport area,
|
|
shift window left/up so that it falls inside */
|
|
if (vpRelRect.x >= s->width)
|
|
shiftX -= vpRelRect.x - (s->width - 1);
|
|
if (vpRelRect.y >= s->height)
|
|
shiftY -= vpRelRect.y - (s->height - 1);
|
|
|
|
if (shiftX)
|
|
xwc.x += shiftX;
|
|
|
|
if (shiftY)
|
|
xwc.y += shiftY;
|
|
}
|
|
|
|
mask = CWX | CWY | CWWidth | CWHeight;
|
|
xwc.width = vpRelRect.width;
|
|
xwc.height = vpRelRect.height;
|
|
|
|
/* Handle non-(0,0) current viewport by shifting by curVpOffsetX,Y,
|
|
and bring window to (0,0) by shifting by minus its vp offset */
|
|
|
|
xwc.x += curVpOffsetX - (s->x + vpX) * s->width;
|
|
xwc.y += curVpOffsetY - (s->y + vpY) * s->height;
|
|
|
|
workArea =
|
|
placeDoValidateWindowResizeRequest (w, &mask, &xwc, FALSE, FALSE);
|
|
|
|
xwc.x -= curVpOffsetX - (s->x + vpX) * s->width;
|
|
xwc.y -= curVpOffsetY - (s->y + vpY) * s->height;
|
|
|
|
/* Check if the new coordinates are different than current position and
|
|
size. If not, we can clear the corresponding mask bits. */
|
|
if (xwc.x == winRect.x)
|
|
mask &= ~CWX;
|
|
|
|
if (xwc.y == winRect.y)
|
|
mask &= ~CWY;
|
|
|
|
if (xwc.width == winRect.width)
|
|
mask &= ~CWWidth;
|
|
|
|
if (xwc.height == winRect.height)
|
|
mask &= ~CWHeight;
|
|
|
|
if (!pw->savedOriginal)
|
|
{
|
|
if (mask)
|
|
{
|
|
/* save window geometry (relative to viewport) so that it
|
|
can be restored later */
|
|
pw->savedOriginal = TRUE;
|
|
pw->origVpRelRect = vpRelRect;
|
|
|
|
if (firstPass)
|
|
{
|
|
/* If first pass, store updated pos. */
|
|
pw->origVpRelRect.x = xwc.x % s->width;
|
|
if (pw->origVpRelRect.x < 0)
|
|
pw->origVpRelRect.x += s->width;
|
|
pw->origVpRelRect.y = xwc.y % s->height;
|
|
if (pw->origVpRelRect.y < 0)
|
|
pw->origVpRelRect.y += s->height;
|
|
}
|
|
}
|
|
}
|
|
else if (pw->origVpRelRect.x + vpX * s->width == xwc.x &&
|
|
pw->origVpRelRect.y + vpY * s->height == xwc.y &&
|
|
pw->origVpRelRect.width == xwc.width &&
|
|
pw->origVpRelRect.height == xwc.height)
|
|
{
|
|
/* if size and position is back to original, clear saved rect */
|
|
pw->savedOriginal = FALSE;
|
|
}
|
|
|
|
if (firstPass) /* if first pass, don't actually move the window */
|
|
continue;
|
|
|
|
/* for maximized/fullscreen windows, update saved pos/size */
|
|
if (w->type & CompWindowTypeFullscreenMask ||
|
|
(w->state & (CompWindowStateMaximizedVertMask |
|
|
CompWindowStateMaximizedHorzMask)))
|
|
{
|
|
if (mask & CWX)
|
|
{
|
|
w->saveWc.x = xwc.x;
|
|
w->saveMask |= CWX;
|
|
}
|
|
if (mask & CWY)
|
|
{
|
|
w->saveWc.y = xwc.y;
|
|
w->saveMask |= CWY;
|
|
}
|
|
if (mask & CWWidth)
|
|
{
|
|
w->saveWc.width = xwc.width;
|
|
w->saveMask |= CWWidth;
|
|
}
|
|
if (mask & CWHeight)
|
|
{
|
|
w->saveWc.height = xwc.height;
|
|
w->saveMask |= CWHeight;
|
|
}
|
|
|
|
if (w->type & CompWindowTypeFullscreenMask)
|
|
{
|
|
mask |= CWX | CWY | CWWidth | CWHeight;
|
|
xwc.x = vpX * s->width;
|
|
xwc.y = vpY * s->height;
|
|
xwc.width = s->width;
|
|
xwc.height = s->height;
|
|
}
|
|
else
|
|
{
|
|
if (w->state & CompWindowStateMaximizedHorzMask)
|
|
{
|
|
mask |= CWX | CWWidth;
|
|
xwc.x = vpX * s->width + workArea.x + w->input.left;
|
|
xwc.width = workArea.width -
|
|
(2 * w->serverBorderWidth +
|
|
w->input.left + w->input.right);
|
|
}
|
|
if (w->state & CompWindowStateMaximizedVertMask)
|
|
{
|
|
mask |= CWY | CWHeight;
|
|
xwc.y = vpY * s->height + workArea.y + w->input.top;
|
|
xwc.height = workArea.height -
|
|
(2 * w->serverBorderWidth +
|
|
w->input.top + w->input.bottom);
|
|
}
|
|
}
|
|
}
|
|
if (mask)
|
|
{
|
|
/* actually move/resize window in directions given by mask */
|
|
configureXWindow (w, mask, &xwc);
|
|
}
|
|
}
|
|
}
|
|
|
|
static CompBool
|
|
placeHandleScreenSizeChangeFallback (void *closure)
|
|
{
|
|
CompScreen *s = (CompScreen *) closure;
|
|
|
|
PLACE_SCREEN (s);
|
|
|
|
ps->resChangeFallbackHandle = 0;
|
|
|
|
/* If countdown is not finished yet (i.e. at least one strut-window didn't
|
|
update its struts), reset the countdown and do the 2nd pass here. */
|
|
if (ps->strutWindowCount > 0) /* no windows with struts found */
|
|
{
|
|
ps->strutWindowCount = 0;
|
|
placeDoHandleScreenSizeChange (s, FALSE);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
placeHandleScreenSizeChange (CompScreen *s,
|
|
int width,
|
|
int height)
|
|
{
|
|
PLACE_SCREEN (s);
|
|
|
|
if (s->width == width && s->height == height) /* No change in screen size */
|
|
return;
|
|
|
|
ps->prevWidth = s->width;
|
|
ps->prevHeight = s->height;
|
|
|
|
if (ps->resChangeFallbackHandle)
|
|
compRemoveTimeout (ps->resChangeFallbackHandle);
|
|
|
|
placeDoHandleScreenSizeChange (s, TRUE); /* 1st pass */
|
|
|
|
if (ps->strutWindowCount == 0) /* no windows with struts found */
|
|
{
|
|
ps->resChangeFallbackHandle = 0;
|
|
|
|
/* do the 2nd pass right here instead of in placeHandleEvent */
|
|
placeDoHandleScreenSizeChange (s, FALSE);
|
|
}
|
|
else
|
|
{
|
|
/* Start a 4 second fallback timeout, just in case. */
|
|
ps->resChangeFallbackHandle =
|
|
compAddTimeout (4000, 4500, placeHandleScreenSizeChangeFallback, s);
|
|
}
|
|
}
|
|
|
|
static void
|
|
placeHandleEvent (CompDisplay *d,
|
|
XEvent *event)
|
|
{
|
|
PLACE_DISPLAY (d);
|
|
|
|
switch (event->type) {
|
|
case ConfigureNotify:
|
|
{
|
|
CompScreen *s;
|
|
|
|
s = findScreenAtDisplay (d, event->xconfigure.window);
|
|
if (s)
|
|
placeHandleScreenSizeChange (s,
|
|
event->xconfigure.width,
|
|
event->xconfigure.height);
|
|
}
|
|
break;
|
|
case PropertyNotify:
|
|
if (event->xproperty.atom == d->wmStrutAtom ||
|
|
event->xproperty.atom == d->wmStrutPartialAtom)
|
|
{
|
|
CompWindow *w;
|
|
|
|
w = findWindowAtDisplay (d, event->xproperty.window);
|
|
if (w)
|
|
{
|
|
PLACE_SCREEN (w->screen);
|
|
|
|
/* Only do when handling screen size change.
|
|
ps->strutWindowCount is 0 at any other time */
|
|
if (ps->strutWindowCount > 0 &&
|
|
updateWindowStruts (w))
|
|
{
|
|
ps->strutWindowCount--;
|
|
updateWorkareaForScreen (w->screen);
|
|
|
|
/* if this was the last window with struts */
|
|
if (!ps->strutWindowCount)
|
|
placeDoHandleScreenSizeChange (w->screen,
|
|
FALSE); /* 2nd pass */
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
UNWRAP (pd, d, handleEvent);
|
|
(*d->handleEvent) (d, event);
|
|
WRAP (pd, d, handleEvent, placeHandleEvent);
|
|
}
|
|
|
|
static Bool
|
|
placeInitDisplay (CompPlugin *p,
|
|
CompDisplay *d)
|
|
{
|
|
PlaceDisplay *pd;
|
|
|
|
if (!checkPluginABI ("core", CORE_ABIVERSION))
|
|
return FALSE;
|
|
|
|
pd = malloc (sizeof (PlaceDisplay));
|
|
if (!pd)
|
|
return FALSE;
|
|
|
|
pd->screenPrivateIndex = allocateScreenPrivateIndex (d);
|
|
if (pd->screenPrivateIndex < 0)
|
|
{
|
|
free (pd);
|
|
return FALSE;
|
|
}
|
|
|
|
pd->fullPlacementAtom = XInternAtom (d->display,
|
|
"_NET_WM_FULL_PLACEMENT", 0);
|
|
|
|
d->base.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 const CompMetadataOptionInfo placeScreenOptionInfo[] = {
|
|
{ "workarounds", "bool", 0, 0, 0 },
|
|
{ "mode", "int", RESTOSTRING (0, PLACE_MODE_LAST), 0, 0 },
|
|
{ "multioutput_mode", "int", RESTOSTRING (0, PLACE_MOMODE_LAST), 0, 0 },
|
|
{ "force_placement_match", "match", 0, 0, 0 },
|
|
{ "position_matches", "list", "<type>match</type>", 0, 0 },
|
|
{ "position_x_values", "list", "<type>int</type>", 0, 0 },
|
|
{ "position_y_values", "list", "<type>int</type>", 0, 0 },
|
|
{ "position_constrain_workarea", "list", "<type>bool</type>", 0, 0 },
|
|
{ "viewport_matches", "list", "<type>match</type>", 0, 0 },
|
|
{ "viewport_x_values", "list",
|
|
"<type>int</type><min>1</min><max>32</max>", 0, 0 },
|
|
{ "viewport_y_values", "list",
|
|
"<type>int</type><min>1</min><max>32</max>", 0, 0 },
|
|
{ "mode_matches", "list", "<type>match</type>", 0, 0 },
|
|
{ "mode_modes", "list",
|
|
"<type>int</type>" RESTOSTRING (0, PLACE_MODE_LAST), 0, 0 }
|
|
};
|
|
|
|
static Bool
|
|
placeInitScreen (CompPlugin *p,
|
|
CompScreen *s)
|
|
{
|
|
PlaceScreen *ps;
|
|
|
|
PLACE_DISPLAY (s->display);
|
|
|
|
ps = malloc (sizeof (PlaceScreen));
|
|
if (!ps)
|
|
return FALSE;
|
|
|
|
if (!compInitScreenOptionsFromMetadata (s,
|
|
&placeMetadata,
|
|
placeScreenOptionInfo,
|
|
ps->opt,
|
|
PLACE_SCREEN_OPTION_NUM))
|
|
{
|
|
free (ps);
|
|
return FALSE;
|
|
}
|
|
|
|
ps->windowPrivateIndex = allocateWindowPrivateIndex (s);
|
|
if (ps->windowPrivateIndex < 0)
|
|
{
|
|
compFiniScreenOptions (s, ps->opt, PLACE_SCREEN_OPTION_NUM);
|
|
free (ps);
|
|
return FALSE;
|
|
}
|
|
|
|
ps->prevWidth = s->width;
|
|
ps->prevHeight = s->height;
|
|
|
|
ps->strutWindowCount = 0;
|
|
ps->resChangeFallbackHandle = 0;
|
|
|
|
WRAP (ps, s, placeWindow, placePlaceWindow);
|
|
WRAP (ps, s, validateWindowResizeRequest,
|
|
placeValidateWindowResizeRequest);
|
|
WRAP (ps, s, addSupportedAtoms, placeAddSupportedAtoms);
|
|
WRAP (ps, s, windowGrabNotify, placeWindowGrabNotify);
|
|
|
|
s->base.privates[pd->screenPrivateIndex].ptr = ps;
|
|
|
|
setSupportedWmHints (s);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
placeFiniScreen (CompPlugin *p,
|
|
CompScreen *s)
|
|
{
|
|
PLACE_SCREEN (s);
|
|
|
|
if (ps->resChangeFallbackHandle)
|
|
compRemoveTimeout (ps->resChangeFallbackHandle);
|
|
|
|
UNWRAP (ps, s, placeWindow);
|
|
UNWRAP (ps, s, validateWindowResizeRequest);
|
|
UNWRAP (ps, s, addSupportedAtoms);
|
|
UNWRAP (ps, s, windowGrabNotify);
|
|
|
|
setSupportedWmHints (s);
|
|
|
|
compFiniScreenOptions (s, ps->opt, PLACE_SCREEN_OPTION_NUM);
|
|
|
|
free (ps);
|
|
}
|
|
|
|
static Bool
|
|
placeInitWindow (CompPlugin *p,
|
|
CompWindow *w)
|
|
{
|
|
PlaceWindow *pw;
|
|
|
|
PLACE_SCREEN (w->screen);
|
|
|
|
pw = malloc (sizeof (PlaceWindow));
|
|
if (!pw)
|
|
return FALSE;
|
|
|
|
pw->savedOriginal = FALSE;
|
|
|
|
w->base.privates[ps->windowPrivateIndex].ptr = pw;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
placeFiniWindow (CompPlugin *p,
|
|
CompWindow *w)
|
|
{
|
|
PLACE_WINDOW (w);
|
|
|
|
free (pw);
|
|
}
|
|
|
|
static CompBool
|
|
placeInitObject (CompPlugin *p,
|
|
CompObject *o)
|
|
{
|
|
static InitPluginObjectProc dispTab[] = {
|
|
(InitPluginObjectProc) 0, /* InitCore */
|
|
(InitPluginObjectProc) placeInitDisplay,
|
|
(InitPluginObjectProc) placeInitScreen,
|
|
(InitPluginObjectProc) placeInitWindow
|
|
};
|
|
|
|
RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
|
|
}
|
|
|
|
static void
|
|
placeFiniObject (CompPlugin *p,
|
|
CompObject *o)
|
|
{
|
|
static FiniPluginObjectProc dispTab[] = {
|
|
(FiniPluginObjectProc) 0, /* FiniCore */
|
|
(FiniPluginObjectProc) placeFiniDisplay,
|
|
(FiniPluginObjectProc) placeFiniScreen,
|
|
(FiniPluginObjectProc) placeFiniWindow
|
|
};
|
|
|
|
DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
|
|
}
|
|
|
|
static CompOption *
|
|
placeGetObjectOptions (CompPlugin *plugin,
|
|
CompObject *object,
|
|
int *count)
|
|
{
|
|
static GetPluginObjectOptionsProc dispTab[] = {
|
|
(GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
|
|
(GetPluginObjectOptionsProc) 0, /* GetDisplayOptions */
|
|
(GetPluginObjectOptionsProc) placeGetScreenOptions
|
|
};
|
|
|
|
*count = 0;
|
|
RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
|
|
(void *) count, (plugin, object, count));
|
|
}
|
|
|
|
static CompBool
|
|
placeSetObjectOption (CompPlugin *plugin,
|
|
CompObject *object,
|
|
const char *name,
|
|
CompOptionValue *value)
|
|
{
|
|
static SetPluginObjectOptionProc dispTab[] = {
|
|
(SetPluginObjectOptionProc) 0, /* SetCoreOption */
|
|
(SetPluginObjectOptionProc) 0, /* SetDisplayOption */
|
|
(SetPluginObjectOptionProc) placeSetScreenOption
|
|
};
|
|
|
|
RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
|
|
(plugin, object, name, value));
|
|
}
|
|
|
|
static Bool
|
|
placeInit (CompPlugin *p)
|
|
{
|
|
if (!compInitPluginMetadataFromInfo (&placeMetadata,
|
|
p->vTable->name, 0, 0,
|
|
placeScreenOptionInfo,
|
|
PLACE_SCREEN_OPTION_NUM))
|
|
return FALSE;
|
|
|
|
displayPrivateIndex = allocateDisplayPrivateIndex ();
|
|
if (displayPrivateIndex < 0)
|
|
{
|
|
compFiniMetadata (&placeMetadata);
|
|
return FALSE;
|
|
}
|
|
|
|
compAddMetadataFromFile (&placeMetadata, p->vTable->name);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
placeFini (CompPlugin *p)
|
|
{
|
|
freeDisplayPrivateIndex (displayPrivateIndex);
|
|
compFiniMetadata (&placeMetadata);
|
|
}
|
|
|
|
static CompMetadata *
|
|
placeGetMetadata (CompPlugin *plugin)
|
|
{
|
|
return &placeMetadata;
|
|
}
|
|
|
|
static CompPluginVTable placeVTable = {
|
|
"place",
|
|
placeGetMetadata,
|
|
placeInit,
|
|
placeFini,
|
|
placeInitObject,
|
|
placeFiniObject,
|
|
placeGetObjectOptions,
|
|
placeSetObjectOption
|
|
};
|
|
|
|
CompPluginVTable *
|
|
getCompPluginInfo20070830 (void)
|
|
{
|
|
return &placeVTable;
|
|
}
|