/*
* Copyright © 2005 Novell, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of
* Novell, Inc. not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior permission.
* Novell, Inc. makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*
* NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
* NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Author: David Reveman <davidr@novell.com>
*/
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xproto.h>
#include <X11/extensions/shape.h>
#include <X11/extensions/Xcomposite.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
#include <math.h>
#include <boost/bind.hpp>
#include <compiz-core.h>
#include <comptexture.h>
#include <compicon.h>
#include "privatewindow.h"
#include "privatescreen.h"
CompObject::indices windowPrivateIndices (0);
int
CompWindow::allocPrivateIndex ()
{
return CompObject::allocatePrivateIndex (COMP_OBJECT_TYPE_WINDOW,
&windowPrivateIndices);
}
void
CompWindow::freePrivateIndex (int index)
{
CompObject::freePrivateIndex (COMP_OBJECT_TYPE_WINDOW,
&windowPrivateIndices, index);
}
bool
PrivateWindow::isAncestorTo (CompWindow *transient,
CompWindow *ancestor)
{
if (transient->priv->transientFor)
{
if (transient->priv->transientFor == ancestor->priv->id)
return true;
transient =
transient->priv->screen->findWindow (transient->priv->transientFor);
if (transient)
return isAncestorTo (transient, ancestor);
}
return false;
}
void
PrivateWindow::recalcNormalHints ()
{
int maxSize;
maxSize = screen->maxTextureSize ();
maxSize -= serverGeometry.border () * 2;
sizeHints.x = serverGeometry.x ();
sizeHints.y = serverGeometry.y ();
sizeHints.width = serverGeometry.width ();
sizeHints.height = serverGeometry.height ();
if (!(sizeHints.flags & PBaseSize))
{
if (sizeHints.flags & PMinSize)
{
sizeHints.base_width = sizeHints.min_width;
sizeHints.base_height = sizeHints.min_height;
}
else
{
sizeHints.base_width = 0;
sizeHints.base_height = 0;
}
sizeHints.flags |= PBaseSize;
}
if (!(sizeHints.flags & PMinSize))
{
sizeHints.min_width = sizeHints.base_width;
sizeHints.min_height = sizeHints.base_height;
sizeHints.flags |= PMinSize;
}
if (!(sizeHints.flags & PMaxSize))
{
sizeHints.max_width = 65535;
sizeHints.max_height = 65535;
sizeHints.flags |= PMaxSize;
}
if (sizeHints.max_width < sizeHints.min_width)
sizeHints.max_width = sizeHints.min_width;
if (sizeHints.max_height < sizeHints.min_height)
sizeHints.max_height = sizeHints.min_height;
if (sizeHints.min_width < 1)
sizeHints.min_width = 1;
if (sizeHints.max_width < 1)
sizeHints.max_width = 1;
if (sizeHints.min_height < 1)
sizeHints.min_height = 1;
if (sizeHints.max_height < 1)
sizeHints.max_height = 1;
if (sizeHints.max_width > maxSize)
sizeHints.max_width = maxSize;
if (sizeHints.max_height > maxSize)
sizeHints.max_height = maxSize;
if (sizeHints.min_width > maxSize)
sizeHints.min_width = maxSize;
if (sizeHints.min_height > maxSize)
sizeHints.min_height = maxSize;
if (sizeHints.base_width > maxSize)
sizeHints.base_width = maxSize;
if (sizeHints.base_height > maxSize)
sizeHints.base_height = maxSize;
if (sizeHints.flags & PResizeInc)
{
if (sizeHints.width_inc == 0)
sizeHints.width_inc = 1;
if (sizeHints.height_inc == 0)
sizeHints.height_inc = 1;
}
else
{
sizeHints.width_inc = 1;
sizeHints.height_inc = 1;
sizeHints.flags |= PResizeInc;
}
if (sizeHints.flags & PAspect)
{
/* don't divide by 0 */
if (sizeHints.min_aspect.y < 1)
sizeHints.min_aspect.y = 1;
if (sizeHints.max_aspect.y < 1)
sizeHints.max_aspect.y = 1;
}
else
{
sizeHints.min_aspect.x = 1;
sizeHints.min_aspect.y = 65535;
sizeHints.max_aspect.x = 65535;
sizeHints.max_aspect.y = 1;
sizeHints.flags |= PAspect;
}
if (!(sizeHints.flags & PWinGravity))
{
sizeHints.win_gravity = NorthWestGravity;
sizeHints.flags |= PWinGravity;
}
}
void
CompWindow::updateNormalHints ()
{
Status status;
long supplied;
status = XGetWMNormalHints (priv->screen->display ()->dpy (), priv->id,
&priv->sizeHints, &supplied);
if (!status)
priv->sizeHints.flags = 0;
priv->recalcNormalHints ();
}
void
CompWindow::updateWmHints ()
{
XWMHints *hints;
hints = XGetWMHints (priv->screen->display ()->dpy (), priv->id);
if (hints)
{
if (hints->flags & InputHint)
priv->inputHint = hints->input;
XFree (hints);
}
}
void
CompWindow::updateClassHints ()
{
XClassHint classHint;
int status;
if (priv->resName)
{
free (priv->resName);
priv->resName = NULL;
}
if (priv->resClass)
{
free (priv->resClass);
priv->resClass = NULL;
}
status = XGetClassHint (priv->screen->display ()->dpy (),
priv->id, &classHint);
if (status)
{
if (classHint.res_name)
{
priv->resName = strdup (classHint.res_name);
XFree (classHint.res_name);
}
if (classHint.res_class)
{
priv->resClass = strdup (classHint.res_class);
XFree (classHint.res_class);
}
}
}
void
CompWindow::updateTransientHint ()
{
Window transientFor;
Status status;
priv->transientFor = None;
status = XGetTransientForHint (priv->screen->display ()->dpy (),
priv->id, &transientFor);
if (status)
{
CompWindow *ancestor;
ancestor = priv->screen->findWindow (transientFor);
if (!ancestor)
return;
/* protect against circular transient dependencies */
if (transientFor == priv->id ||
PrivateWindow::isAncestorTo (ancestor, this))
return;
priv->transientFor = transientFor;
}
}
void
CompWindow::updateIconGeometry ()
{
Atom actual;
int result, format;
unsigned long n, left;
unsigned char *data;
result = XGetWindowProperty (priv->screen->display ()->dpy (), priv->id,
priv->screen->display ()->atoms ().wmIconGeometry,
0L, 1024L, False, XA_CARDINAL,
&actual, &format, &n, &left, &data);
priv->iconGeometrySet = false;
if (result == Success && data)
{
if (n == 4)
{
unsigned long *geometry = (unsigned long *) data;
priv->iconGeometry.x = geometry[0];
priv->iconGeometry.y = geometry[1];
priv->iconGeometry.width = geometry[2];
priv->iconGeometry.height = geometry[3];
priv->iconGeometrySet = TRUE;
}
XFree (data);
}
}
Window
PrivateWindow::getClientLeaderOfAncestor ()
{
if (transientFor)
{
CompWindow *w = screen->findWindow (transientFor);
if (w)
{
if (w->priv->clientLeader)
return w->priv->clientLeader;
return w->priv->getClientLeaderOfAncestor ();
}
}
return None;
}
Window
CompWindow::getClientLeader ()
{
Atom actual;
int result, format;
unsigned long n, left;
unsigned char *data;
result = XGetWindowProperty (priv->screen->display ()->dpy (), priv->id,
priv->screen->display ()->atoms ().wmClientLeader,
0L, 1L, False, XA_WINDOW, &actual, &format,
&n, &left, &data);
if (result == Success && n && data)
{
Window win;
memcpy (&win, data, sizeof (Window));
XFree ((void *) data);
if (win)
return win;
}
return priv->getClientLeaderOfAncestor ();
}
char *
CompWindow::getStartupId ()
{
Atom actual;
int result, format;
unsigned long n, left;
unsigned char *data;
result = XGetWindowProperty (priv->screen->display ()->dpy (), priv->id,
priv->screen->display ()->atoms ().startupId,
0L, 1024L, False,
priv->screen->display ()->atoms ().utf8String,
&actual, &format,
&n, &left, &data);
if (result == Success && n && data)
{
char *id;
id = strdup ((char *) data);
XFree ((void *) data);
return id;
}
return NULL;
}
void
CompWindow::changeState (unsigned int newState)
{
CompDisplay *d = priv->screen->display ();
unsigned int oldState;
if (priv->state == newState)
return;
oldState = priv->state;
priv->state = newState;
recalcType ();
recalcActions ();
d->setWindowState (priv->state, priv->id);
stateChangeNotify (oldState);
d->matchPropertyChanged (this);
}
static void
setWindowActions (CompDisplay *display,
unsigned int actions,
Window id)
{
Atom data[32];
int i = 0;
if (actions & CompWindowActionMoveMask)
data[i++] = display->atoms ().winActionMove;
if (actions & CompWindowActionResizeMask)
data[i++] = display->atoms ().winActionResize;
if (actions & CompWindowActionStickMask)
data[i++] = display->atoms ().winActionStick;
if (actions & CompWindowActionMinimizeMask)
data[i++] = display->atoms ().winActionMinimize;
if (actions & CompWindowActionMaximizeHorzMask)
data[i++] = display->atoms ().winActionMaximizeHorz;
if (actions & CompWindowActionMaximizeVertMask)
data[i++] = display->atoms ().winActionMaximizeVert;
if (actions & CompWindowActionFullscreenMask)
data[i++] = display->atoms ().winActionFullscreen;
if (actions & CompWindowActionCloseMask)
data[i++] = display->atoms ().winActionClose;
if (actions & CompWindowActionShadeMask)
data[i++] = display->atoms ().winActionShade;
if (actions & CompWindowActionChangeDesktopMask)
data[i++] = display->atoms ().winActionChangeDesktop;
if (actions & CompWindowActionAboveMask)
data[i++] = display->atoms ().winActionAbove;
if (actions & CompWindowActionBelowMask)
data[i++] = display->atoms ().winActionBelow;
XChangeProperty (display->dpy (), id, display->atoms ().wmAllowedActions,
XA_ATOM, 32, PropModeReplace,
(unsigned char *) data, i);
}
void
CompWindow::recalcActions ()
{
unsigned int actions = 0;
unsigned int setActions, clearActions;
switch (priv->type) {
case CompWindowTypeFullscreenMask:
case CompWindowTypeNormalMask:
actions =
CompWindowActionMaximizeHorzMask |
CompWindowActionMaximizeVertMask |
CompWindowActionFullscreenMask |
CompWindowActionMoveMask |
CompWindowActionResizeMask |
CompWindowActionStickMask |
CompWindowActionMinimizeMask |
CompWindowActionCloseMask |
CompWindowActionChangeDesktopMask;
break;
case CompWindowTypeUtilMask:
case CompWindowTypeMenuMask:
case CompWindowTypeToolbarMask:
actions =
CompWindowActionMoveMask |
CompWindowActionResizeMask |
CompWindowActionStickMask |
CompWindowActionCloseMask |
CompWindowActionChangeDesktopMask;
break;
case CompWindowTypeDialogMask:
case CompWindowTypeModalDialogMask:
actions =
CompWindowActionMaximizeHorzMask |
CompWindowActionMaximizeVertMask |
CompWindowActionMoveMask |
CompWindowActionResizeMask |
CompWindowActionStickMask |
CompWindowActionCloseMask |
CompWindowActionChangeDesktopMask;
/* allow minimization for dialog windows if they
a) are not a transient (transients can be minimized
with their parent)
b) don't have the skip taskbar hint set (as those
have no target to be minimized to)
*/
if (!priv->transientFor &&
!(priv->state & CompWindowStateSkipTaskbarMask))
{
actions |= CompWindowActionMinimizeMask;
}
default:
break;
}
if (priv->input.top)
actions |= CompWindowActionShadeMask;
actions |= (CompWindowActionAboveMask | CompWindowActionBelowMask);
switch (priv->wmType) {
case CompWindowTypeNormalMask:
actions |= CompWindowActionFullscreenMask |
CompWindowActionMinimizeMask;
default:
break;
}
if (priv->sizeHints.min_width == priv->sizeHints.max_width &&
priv->sizeHints.min_height == priv->sizeHints.max_height)
actions &= ~(CompWindowActionResizeMask |
CompWindowActionMaximizeHorzMask |
CompWindowActionMaximizeVertMask |
CompWindowActionFullscreenMask);
if (!(priv->mwmFunc & MwmFuncAll))
{
if (!(priv->mwmFunc & MwmFuncResize))
actions &= ~(CompWindowActionResizeMask |
CompWindowActionMaximizeHorzMask |
CompWindowActionMaximizeVertMask |
CompWindowActionFullscreenMask);
if (!(priv->mwmFunc & MwmFuncMove))
actions &= ~(CompWindowActionMoveMask |
CompWindowActionMaximizeHorzMask |
CompWindowActionMaximizeVertMask |
CompWindowActionFullscreenMask);
if (!(priv->mwmFunc & MwmFuncIconify))
actions &= ~CompWindowActionMinimizeMask;
if (!(priv->mwmFunc & MwmFuncClose))
actions &= ~CompWindowActionCloseMask;
}
getAllowedActions (&setActions, &clearActions);
actions &= ~clearActions;
actions |= setActions;
if (actions != priv->actions)
{
priv->actions = actions;
setWindowActions (priv->screen->display (), actions, priv->id);
}
}
void
CompWindow::getAllowedActions (unsigned int *setActions,
unsigned int *clearActions)
{
WRAPABLE_HND_FUNC(getAllowedActions, setActions, clearActions)
*setActions = 0;
*clearActions = 0;
}
unsigned int
CompWindow::constrainWindowState (unsigned int state,
unsigned int actions)
{
if (!(actions & CompWindowActionMaximizeHorzMask))
state &= ~CompWindowStateMaximizedHorzMask;
if (!(actions & CompWindowActionMaximizeVertMask))
state &= ~CompWindowStateMaximizedVertMask;
if (!(actions & CompWindowActionShadeMask))
state &= ~CompWindowStateShadedMask;
if (!(actions & CompWindowActionFullscreenMask))
state &= ~CompWindowStateFullscreenMask;
return state;
}
unsigned int
CompWindow::windowTypeFromString (const char *str)
{
if (strcasecmp (str, "desktop") == 0)
return CompWindowTypeDesktopMask;
else if (strcasecmp (str, "dock") == 0)
return CompWindowTypeDockMask;
else if (strcasecmp (str, "toolbar") == 0)
return CompWindowTypeToolbarMask;
else if (strcasecmp (str, "menu") == 0)
return CompWindowTypeMenuMask;
else if (strcasecmp (str, "utility") == 0)
return CompWindowTypeUtilMask;
else if (strcasecmp (str, "splash") == 0)
return CompWindowTypeSplashMask;
else if (strcasecmp (str, "dialog") == 0)
return CompWindowTypeDialogMask;
else if (strcasecmp (str, "normal") == 0)
return CompWindowTypeNormalMask;
else if (strcasecmp (str, "dropdownmenu") == 0)
return CompWindowTypeDropdownMenuMask;
else if (strcasecmp (str, "popupmenu") == 0)
return CompWindowTypePopupMenuMask;
else if (strcasecmp (str, "tooltip") == 0)
return CompWindowTypeTooltipMask;
else if (strcasecmp (str, "notification") == 0)
return CompWindowTypeNotificationMask;
else if (strcasecmp (str, "combo") == 0)
return CompWindowTypeComboMask;
else if (strcasecmp (str, "dnd") == 0)
return CompWindowTypeDndMask;
else if (strcasecmp (str, "modaldialog") == 0)
return CompWindowTypeModalDialogMask;
else if (strcasecmp (str, "fullscreen") == 0)
return CompWindowTypeFullscreenMask;
else if (strcasecmp (str, "unknown") == 0)
return CompWindowTypeUnknownMask;
else if (strcasecmp (str, "any") == 0)
return ~0;
return 0;
}
void
CompWindow::recalcType ()
{
unsigned int type;
type = priv->wmType;
if (!priv->attrib.override_redirect && priv->wmType == CompWindowTypeUnknownMask)
type = CompWindowTypeNormalMask;
if (priv->state & CompWindowStateFullscreenMask)
type = CompWindowTypeFullscreenMask;
if (type == CompWindowTypeNormalMask)
{
if (priv->transientFor)
type = CompWindowTypeDialogMask;
}
if (type == CompWindowTypeDockMask && (priv->state & CompWindowStateBelowMask))
type = CompWindowTypeNormalMask;
if ((type & (CompWindowTypeNormalMask | CompWindowTypeDialogMask)) &&
(priv->state & CompWindowStateModalMask))
type = CompWindowTypeModalDialogMask;
if (type & CompWindowTypeDesktopMask)
priv->paint.opacity = OPAQUE;
priv->type = type;
}
void
PrivateWindow::updateFrameWindow ()
{
CompDisplay *d = screen->display ();
if (input.left || input.right || input.top || input.bottom)
{
XRectangle rects[4];
int x, y, width, height;
int i = 0;
int bw = serverGeometry.border () * 2;
x = serverGeometry.x () - input.left;
y = serverGeometry.y () - input.top;
width = serverGeometry.width () + input.left + input.right + bw;
height = serverGeometry.height () + input.top + input.bottom + bw;
if (shaded)
height = input.top + input.bottom;
if (!frame)
{
XSetWindowAttributes attr;
XWindowChanges xwc;
attr.event_mask = 0;
attr.override_redirect = TRUE;
frame = XCreateWindow (d->dpy (), screen->root (),
x, y, width, height, 0,
CopyFromParent,
InputOnly,
CopyFromParent,
CWOverrideRedirect | CWEventMask, &attr);
XGrabButton (d->dpy (), AnyButton, AnyModifier, frame, TRUE,
ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
GrabModeSync, GrabModeSync, None, None);
xwc.stack_mode = Below;
xwc.sibling = id;
XConfigureWindow (d->dpy (), frame,
CWSibling | CWStackMode, &xwc);
if (mapNum || shaded)
XMapWindow (d->dpy (), frame);
XChangeProperty (d->dpy (), id, d->atoms ().frameWindow,
XA_WINDOW, 32, PropModeReplace,
(unsigned char *) &frame, 1);
}
XMoveResizeWindow (d->dpy (), frame, x, y, width, height);
rects[i].x = 0;
rects[i].y = 0;
rects[i].width = width;
rects[i].height = input.top;
if (rects[i].width && rects[i].height)
i++;
rects[i].x = 0;
rects[i].y = input.top;
rects[i].width = input.left;
rects[i].height = height - input.top - input.bottom;
if (rects[i].width && rects[i].height)
i++;
rects[i].x = width - input.right;
rects[i].y = input.top;
rects[i].width = input.right;
rects[i].height = height - input.top - input.bottom;
if (rects[i].width && rects[i].height)
i++;
rects[i].x = 0;
rects[i].y = height - input.bottom;
rects[i].width = width;
rects[i].height = input.bottom;
if (rects[i].width && rects[i].height)
i++;
XShapeCombineRectangles (d->dpy (),
frame,
ShapeInput,
0,
0,
rects,
i,
ShapeSet,
YXBanded);
}
else
{
if (frame)
{
XDeleteProperty (d->dpy (), id, d->atoms ().frameWindow);
XDestroyWindow (d->dpy (), frame);
frame = None;
}
}
window->recalcActions ();
}
void
CompWindow::setWindowFrameExtents (CompWindowExtents *input)
{
if (input->left != priv->input.left ||
input->right != priv->input.right ||
input->top != priv->input.top ||
input->bottom != priv->input.bottom)
{
unsigned long data[4];
priv->input = *input;
data[0] = input->left;
data[1] = input->right;
data[2] = input->top;
data[3] = input->bottom;
updateSize ();
priv->updateFrameWindow ();
recalcActions ();
XChangeProperty (priv->screen->display ()->dpy (), priv->id,
priv->screen->display ()->atoms ().frameExtents,
XA_CARDINAL, 32, PropModeReplace,
(unsigned char *) data, 4);
}
}
void
CompWindow::updateWindowOutputExtents ()
{
CompWindowExtents output;
getOutputExtents (&output);
if (output.left != priv->output.left ||
output.right != priv->output.right ||
output.top != priv->output.top ||
output.bottom != priv->output.bottom)
{
priv->output = output;
resizeNotify (0, 0, 0, 0);
}
}
void
PrivateWindow::setWindowMatrix ()
{
matrix = texture.matrix ();
matrix.x0 -= (attrib.x * matrix.xx);
matrix.y0 -= (attrib.y * matrix.yy);
}
bool
CompWindow::bind ()
{
redirect ();
if (!priv->pixmap)
{
XWindowAttributes attr;
/* don't try to bind window again if it failed previously */
if (priv->bindFailed)
return false;
/* We have to grab the server here to make sure that window
is mapped when getting the window pixmap */
XGrabServer (priv->screen->display ()->dpy ());
XGetWindowAttributes (priv->screen->display ()->dpy (), priv->id, &attr);
if (attr.map_state != IsViewable)
{
XUngrabServer (priv->screen->display ()->dpy ());
priv->texture.reset ();
priv->bindFailed = true;
return false;
}
priv->pixmap = XCompositeNameWindowPixmap (priv->screen->display ()->dpy (),
priv->id);
XUngrabServer (priv->screen->display ()->dpy ());
}
if (!priv->texture.bindPixmap (priv->pixmap, priv->width, priv->height,
priv->attrib.depth))
{
compLogMessage (priv->screen->display (), "core", CompLogLevelInfo,
"Couldn't bind redirected window 0x%x to "
"texture\n", (int) priv->id);
}
priv->setWindowMatrix ();
return true;
}
void
CompWindow::release ()
{
if (priv->pixmap)
{
priv->texture = CompTexture (priv->screen);
XFreePixmap (priv->screen->display ()->dpy (), priv->pixmap);
priv->pixmap = None;
}
}
void
CompWindow::damageTransformedRect (float xScale,
float yScale,
float xTranslate,
float yTranslate,
BoxPtr rect)
{
REGION reg;
reg.rects = ®.extents;
reg.numRects = 1;
reg.extents.x1 = (short) (rect->x1 * xScale) - 1;
reg.extents.y1 = (short) (rect->y1 * yScale) - 1;
reg.extents.x2 = (short) (rect->x2 * xScale + 0.5f) + 1;
reg.extents.y2 = (short) (rect->y2 * yScale + 0.5f) + 1;
reg.extents.x1 += (short) xTranslate;
reg.extents.y1 += (short) yTranslate;
reg.extents.x2 += (short) (xTranslate + 0.5f);
reg.extents.y2 += (short) (yTranslate + 0.5f);
if (reg.extents.x2 > reg.extents.x1 && reg.extents.y2 > reg.extents.y1)
{
reg.extents.x1 += priv->attrib.x + priv->attrib.border_width;
reg.extents.y1 += priv->attrib.y + priv->attrib.border_width;
reg.extents.x2 += priv->attrib.x + priv->attrib.border_width;
reg.extents.y2 += priv->attrib.y + priv->attrib.border_width;
priv->screen->damageRegion (®);
}
}
void
CompWindow::damageOutputExtents ()
{
if (priv->screen->damageMask () & COMP_SCREEN_DAMAGE_ALL_MASK)
return;
if (priv->shaded || (priv->attrib.map_state == IsViewable && priv->damaged))
{
BoxRec box;
/* top */
box.x1 = -priv->output.left - priv->attrib.border_width;
box.y1 = -priv->output.top - priv->attrib.border_width;
box.x2 = priv->width + priv->output.right - priv->attrib.border_width;
box.y2 = -priv->attrib.border_width;
if (box.x1 < box.x2 && box.y1 < box.y2)
addDamageRect (&box);
/* bottom */
box.y1 = priv->height - priv->attrib.border_width;
box.y2 = box.y1 + priv->output.bottom - priv->attrib.border_width;
if (box.x1 < box.x2 && box.y1 < box.y2)
addDamageRect (&box);
/* left */
box.x1 = -priv->output.left - priv->attrib.border_width;
box.y1 = -priv->attrib.border_width;
box.x2 = -priv->attrib.border_width;
box.y2 = priv->height - priv->attrib.border_width;
if (box.x1 < box.x2 && box.y1 < box.y2)
addDamageRect (&box);
/* right */
box.x1 = priv->width - priv->attrib.border_width;
box.x2 = box.x1 + priv->output.right - priv->attrib.border_width;
if (box.x1 < box.x2 && box.y1 < box.y2)
addDamageRect (&box);
}
}
bool
CompWindow::damageRect (bool initial,
BoxPtr rect)
{
WRAPABLE_HND_FUNC_RETURN(bool, damageRect, initial, rect)
return false;
}
void
CompWindow::addDamageRect (BoxPtr rect)
{
REGION region;
if (priv->screen->damageMask () & COMP_SCREEN_DAMAGE_ALL_MASK)
return;
region.extents = *rect;
if (!damageRect (false, ®ion.extents))
{
region.extents.x1 += priv->attrib.x + priv->attrib.border_width;
region.extents.y1 += priv->attrib.y + priv->attrib.border_width;
region.extents.x2 += priv->attrib.x + priv->attrib.border_width;
region.extents.y2 += priv->attrib.y + priv->attrib.border_width;
region.rects = ®ion.extents;
region.numRects = region.size = 1;
priv->screen->damageRegion (®ion);
}
}
void
CompWindow::getOutputExtents (CompWindowExtents *output)
{
WRAPABLE_HND_FUNC(getOutputExtents, output)
output->left = 0;
output->right = 0;
output->top = 0;
output->bottom = 0;
}
void
CompWindow::addDamage ()
{
if (priv->screen->damageMask () & COMP_SCREEN_DAMAGE_ALL_MASK)
return;
if (priv->shaded || (priv->attrib.map_state == IsViewable && priv->damaged))
{
BoxRec box;
box.x1 = -priv->output.left - priv->attrib.border_width;
box.y1 = -priv->output.top - priv->attrib.border_width;
box.x2 = priv->width + priv->output.right;
box.y2 = priv->height + priv->output.bottom;
addDamageRect (&box);
}
}
void
CompWindow::updateRegion ()
{
REGION rect;
XRectangle r, *rects, *shapeRects = 0;
int i, n = 0;
EMPTY_REGION (priv->region);
if (priv->screen->display ()->XShape ())
{
int order;
shapeRects = XShapeGetRectangles (priv->screen->display ()->dpy (), priv->id,
ShapeBounding, &n, &order);
}
if (n < 2)
{
r.x = -priv->attrib.border_width;
r.y = -priv->attrib.border_width;
r.width = priv->width;
r.height = priv->height;
rects = &r;
n = 1;
}
else
{
rects = shapeRects;
}
rect.rects = &rect.extents;
rect.numRects = rect.size = 1;
for (i = 0; i < n; i++)
{
rect.extents.x1 = rects[i].x + priv->attrib.border_width;
rect.extents.y1 = rects[i].y + priv->attrib.border_width;
rect.extents.x2 = rect.extents.x1 + rects[i].width;
rect.extents.y2 = rect.extents.y1 + rects[i].height;
if (rect.extents.x1 < 0)
rect.extents.x1 = 0;
if (rect.extents.y1 < 0)
rect.extents.y1 = 0;
if (rect.extents.x2 > priv->width)
rect.extents.x2 = priv->width;
if (rect.extents.y2 > priv->height)
rect.extents.y2 = priv->height;
if (rect.extents.y1 < rect.extents.y2 &&
rect.extents.x1 < rect.extents.x2)
{
rect.extents.x1 += priv->attrib.x;
rect.extents.y1 += priv->attrib.y;
rect.extents.x2 += priv->attrib.x;
rect.extents.y2 += priv->attrib.y;
XUnionRegion (&rect, priv->region
|