/*
* 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 <compiz.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xproto.h>
#include <X11/extensions/shape.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 <core/core.h>
#include <core/icon.h>
#include <core/atoms.h>
#include "privatewindow.h"
#include "privatescreen.h"
PluginClassStorage::Indices windowPluginClassIndices (0);
unsigned int
CompWindow::allocPluginClassIndex ()
{
unsigned int i = PluginClassStorage::allocatePluginClassIndex (windowPluginClassIndices);
foreach (CompWindow *w, screen->windows ())
if (windowPluginClassIndices.size () != w->pluginClasses.size ())
w->pluginClasses.resize (windowPluginClassIndices.size ());
return i;
}
void
CompWindow::freePluginClassIndex (unsigned int index)
{
PluginClassStorage::freePluginClassIndex (windowPluginClassIndices, index);
foreach (CompWindow *w, ::screen->windows ())
if (windowPluginClassIndices.size () != w->pluginClasses.size ())
w->pluginClasses.resize (windowPluginClassIndices.size ());
}
bool
PrivateWindow::isAncestorTo (CompWindow *transient,
CompWindow *ancestor)
{
if (transient->priv->transientFor)
{
if (transient->priv->transientFor == ancestor->priv->id)
return true;
transient = screen->findWindow (transient->priv->transientFor);
if (transient)
return isAncestorTo (transient, ancestor);
}
return false;
}
void
PrivateWindow::recalcNormalHints ()
{
int maxSize;
#warning fixme to max Texture size
maxSize = MAXSHORT;
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
PrivateWindow::updateNormalHints ()
{
Status status;
long supplied;
status = XGetWMNormalHints (screen->dpy (), priv->id,
&priv->sizeHints, &supplied);
if (!status)
priv->sizeHints.flags = 0;
priv->recalcNormalHints ();
}
void
PrivateWindow::updateWmHints ()
{
XWMHints *newHints;
long dFlags = 0;
bool iconChanged = false;
if (hints)
dFlags = hints->flags;
inputHint = true;
newHints = XGetWMHints (screen->dpy (), id);
if (newHints)
{
dFlags ^= newHints->flags;
if (newHints->flags & InputHint)
inputHint = newHints->input;
if (hints)
{
if ((newHints->flags & IconPixmapHint) &&
(hints->icon_pixmap != newHints->icon_pixmap))
{
iconChanged = true;
}
else if ((newHints->flags & IconMaskHint) &&
(hints->icon_mask != newHints->icon_mask))
{
iconChanged = true;
}
}
}
iconChanged |= (dFlags & (IconPixmapHint | IconMaskHint));
if (iconChanged)
freeIcons ();
if (hints)
XFree (hints);
hints = newHints;
}
void
PrivateWindow::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 (screen->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
PrivateWindow::updateTransientHint ()
{
Window transientFor;
Status status;
priv->transientFor = None;
status = XGetTransientForHint (screen->dpy (),
priv->id, &transientFor);
if (status)
{
CompWindow *ancestor;
ancestor = screen->findWindow (transientFor);
if (!ancestor)
return;
/* protect against circular transient dependencies */
if (transientFor == priv->id ||
PrivateWindow::isAncestorTo (ancestor, window))
return;
priv->transientFor = transientFor;
}
}
void
PrivateWindow::updateIconGeometry ()
{
Atom actual;
int result, format;
unsigned long n, left;
unsigned char *data;
priv->iconGeometry.setGeometry (0, 0, 0, 0);
result = XGetWindowProperty (screen->dpy (), priv->id,
Atoms::wmIconGeometry,
0L, 1024L, False, XA_CARDINAL,
&actual, &format, &n, &left, &data);
if (result == Success && data)
{
if (n == 4)
{
unsigned long *geometry = (unsigned long *) data;
priv->iconGeometry.setX (geometry[0]);
priv->iconGeometry.setY (geometry[1]);
priv->iconGeometry.setWidth (geometry[2]);
priv->iconGeometry.setHeight (geometry[3]);
}
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
PrivateWindow::getClientLeader ()
{
Atom actual;
int result, format;
unsigned long n, left;
unsigned char *data;
result = XGetWindowProperty (screen->dpy (), priv->id,
Atoms::wmClientLeader,
0L, 1L, False, XA_WINDOW, &actual, &format,
&n, &left, &data);
if (result == Success && data)
{
Window win = None;
if (n)
memcpy (&win, data, sizeof (Window));
XFree ((void *) data);
if (win)
return win;
}
return priv->getClientLeaderOfAncestor ();
}
char *
PrivateWindow::getStartupId ()
{
Atom actual;
int result, format;
unsigned long n, left;
unsigned char *data;
result = XGetWindowProperty (screen->dpy (), priv->id,
Atoms::startupId,
0L, 1024L, False,
Atoms::utf8String,
&actual, &format,
&n, &left, &data);
if (result == Success && data)
{
char *id = NULL;
if (n)
id = strdup ((char *) data);
XFree ((void *) data);
return id;
}
return NULL;
}
void
PrivateWindow::setFullscreenMonitors (CompFullscreenMonitorSet *monitors)
{
bool hadFsMonitors = fullscreenMonitorsSet;
unsigned int outputs = screen->outputDevs ().size ();
fullscreenMonitorsSet = false;
if (monitors &&
(unsigned int) monitors->left < outputs &&
(unsigned int) monitors->right < outputs &&
(unsigned int) monitors->top < outputs &&
(unsigned int) monitors->bottom < outputs)
{
CompRect fsRect (screen->outputDevs ()[monitors->left].x1 (),
screen->outputDevs ()[monitors->top].y1 (),
screen->outputDevs ()[monitors->right].x2 (),
screen->outputDevs ()[monitors->bottom].y2 ());
if (fsRect.x1 () < fsRect.x2 () && fsRect.y1 () < fsRect.y2 ())
{
fullscreenMonitorsSet = true;
fullscreenMonitorRect = fsRect;
}
}
if (fullscreenMonitorsSet)
{
long data[4];
data[0] = monitors->top;
data[1] = monitors->bottom;
data[2] = monitors->left;
data[3] = monitors->right;
XChangeProperty (screen->dpy (), id, Atoms::wmFullscreenMonitors,
XA_CARDINAL, 32, PropModeReplace,
(unsigned char *) data, 4);
}
else if (hadFsMonitors)
{
XDeleteProperty (screen->dpy (), id, Atoms::wmFullscreenMonitors);
}
if (state & CompWindowStateFullscreenMask)
if (fullscreenMonitorsSet || hadFsMonitors)
window->updateAttributes (CompStackingUpdateModeNone);
}
void
CompWindow::changeState (unsigned int newState)
{
unsigned int oldState;
if (priv->state == newState)
return;
oldState = priv->state;
priv->state = newState;
recalcType ();
recalcActions ();
if (priv->managed)
screen->priv->setWindowState (priv->state, priv->id);
stateChangeNotify (oldState);
screen->matchPropertyChanged (this);
}
static void
setWindowActions (CompScreen *s,
unsigned int actions,
Window id)
{
Atom data[32];
int i = 0;
if (actions & CompWindowActionMoveMask)
data[i++] = Atoms::winActionMove;
if (actions & CompWindowActionResizeMask)
data[i++] = Atoms::winActionResize;
if (actions & CompWindowActionStickMask)
data[i++] = Atoms::winActionStick;
if (actions & CompWindowActionMinimizeMask)
data[i++] = Atoms::winActionMinimize;
if (actions & CompWindowActionMaximizeHorzMask)
data[i++] = Atoms::winActionMaximizeHorz;
if (actions & CompWindowActionMaximizeVertMask)
data[i++] = Atoms::winActionMaximizeVert;
if (actions & CompWindowActionFullscreenMask)
data[i++] = Atoms::winActionFullscreen;
if (actions & CompWindowActionCloseMask)
data[i++] = Atoms::winActionClose;
if (actions & CompWindowActionShadeMask)
data[i++] = Atoms::winActionShade;
if (actions & CompWindowActionChangeDesktopMask)
data[i++] = Atoms::winActionChangeDesktop;
if (actions & CompWindowActionAboveMask)
data[i++] = Atoms::winActionAbove;
if (actions & CompWindowActionBelowMask)
data[i++] = Atoms::winActionBelow;
XChangeProperty (s->dpy (), id, 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 (screen, actions, priv->id);
}
}
void
CompWindow::getAllowedActions (unsigned int &setActions,
unsigned int &clearActions)
{
WRAPABLE_HND_FUNC (1, 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
PrivateWindow::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 (!overrideRedirect () && 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;
}
priv->type = type;
}
void
PrivateWindow::updateFrameWindow ()
{
if (!frame)
return;
if (input.left || input.right || input.top || input.bottom)
{
int x, y, width, height;
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;
XMoveResizeWindow (screen->dpy (), frame, x, y, width, height);
if (shaded)
{
XUnmapWindow (screen->dpy (), wrapper);
}
else
{
XMapWindow (screen->dpy (), wrapper);
XMoveResizeWindow (screen->dpy (), wrapper, input.left, input.top,
serverGeometry.width (), serverGeometry.height ());
}
XMoveResizeWindow (screen->dpy (), id, 0, 0,
serverGeometry.width (), serverGeometry.height ());
window->sendConfigureNotify ();
window->updateFrameRegion ();
window->windowNotify (CompWindowNotifyFrameUpdate);
}
else
{
int x, y, width, height;
int bw = serverGeometry.border () * 2;
x = serverGeometry.x ();
y = serverGeometry.y ();
width = serverGeometry.width () + bw;
height = serverGeometry.height () + bw;
if (shaded)
height = 0;
XMoveResizeWindow (screen->dpy (), frame, x, y, width, height);
if (shaded)
{
XUnmapWindow (screen->dpy (), wrapper);
}
else
{
XMapWindow (screen->dpy (), wrapper);
XMoveResizeWindow (screen->dpy (), wrapper, 0, 0,
serverGeometry.width (), serverGeometry.height ());
}
XMoveResizeWindow (screen->dpy (), id, 0, 0,
serverGeometry.width (), serverGeometry.height ());
window->sendConfigureNotify ();
frameRegion = CompRegion ();
window->windowNotify (CompWindowNotifyFrameUpdate);
}
window->recalcActions ();
}
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
CompWindow::getOutputExtents (CompWindowExtents& output)
{
WRAPABLE_HND_FUNC (0, getOutputExtents, output)
output.left = 0;
output.right = 0;
output.top = 0;
output.bottom = 0;
}
void
PrivateWindow::updateRegion ()
{
int x1, x2, y1, y2;
XRectangle r, *rects, *shapeRects = 0;
int i, n = 0;
priv->region = CompRegion ();
if (screen->XShape ())
{
int order;
|