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.

938 lines
20 KiB

/*
* Copyright © 2006 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/time.h>
#include <compiz-core.h>
static CompMetadata cloneMetadata;
static int displayPrivateIndex;
#define CLONE_DISPLAY_OPTION_INITIATE_BUTTON 0
#define CLONE_DISPLAY_OPTION_NUM 1
typedef struct _CloneDisplay {
int screenPrivateIndex;
HandleEventProc handleEvent;
CompOption opt[CLONE_DISPLAY_OPTION_NUM];
} CloneDisplay;
typedef struct _CloneClone {
int src;
int dst;
Region region;
Window input;
} CloneClone;
typedef struct _CloneScreen {
PreparePaintScreenProc preparePaintScreen;
DonePaintScreenProc donePaintScreen;
PaintOutputProc paintOutput;
PaintWindowProc paintWindow;
OutputChangeNotifyProc outputChangeNotify;
int grabIndex;
Bool grab;
float offset;
Bool transformed;
CloneClone *clone;
int nClone;
int x, y;
int grabbedOutput;
int src, dst;
} CloneScreen;
#define GET_CLONE_DISPLAY(d) \
((CloneDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
#define CLONE_DISPLAY(d) \
CloneDisplay *cd = GET_CLONE_DISPLAY (d)
#define GET_CLONE_SCREEN(s, cd) \
((CloneScreen *) (s)->base.privates[(cd)->screenPrivateIndex].ptr)
#define CLONE_SCREEN(s) \
CloneScreen *cs = GET_CLONE_SCREEN (s, GET_CLONE_DISPLAY (s->display))
#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption))
static void
cloneRemove (CompScreen *s,
int i)
{
CloneClone *clone;
CLONE_SCREEN (s);
clone = malloc (sizeof (CloneClone) * (cs->nClone - 1));
if (clone)
{
int j, k = 0;
for (j = 0; j < cs->nClone; j++)
if (j != i)
memcpy (&clone[k++], &cs->clone[j],
sizeof (CloneClone));
XDestroyRegion (cs->clone[i].region);
XDestroyWindow (s->display->display, cs->clone[i].input);
free (cs->clone);
cs->clone = clone;
cs->nClone--;
}
}
static void
cloneFinish (CompScreen *s)
{
CloneClone *clone;
int i;
CLONE_SCREEN (s);
cs->grab = FALSE;
if (cs->src != cs->dst)
{
clone = NULL;
/* check if we should replace current clone */
for (i = 0; i < cs->nClone; i++)
{
if (cs->clone[i].dst == cs->dst)
{
clone = &cs->clone[i];
break;
}
}
/* no existing clone for this destination, we must allocate one */
if (!clone)
{
Region region;
region = XCreateRegion ();
if (region)
{
clone =
realloc (cs->clone,
sizeof (CloneClone) * (cs->nClone + 1));
if (clone)
{
XSetWindowAttributes attr;
int x, y;
cs->clone = clone;
clone = &cs->clone[cs->nClone++];
clone->region = region;
attr.override_redirect = TRUE;
x = s->outputDev[cs->dst].region.extents.x1;
y = s->outputDev[cs->dst].region.extents.y1;
clone->input =
XCreateWindow (s->display->display,
s->root, x, y,
s->outputDev[cs->dst].width,
s->outputDev[cs->dst].height,
0, 0, InputOnly, CopyFromParent,
CWOverrideRedirect, &attr);
XMapRaised (s->display->display, clone->input);
}
else
{
XDestroyRegion (region);
}
}
}
if (clone)
{
clone->src = cs->src;
clone->dst = cs->dst;
}
}
if (cs->grabbedOutput != cs->dst)
{
/* remove clone */
for (i = 0; i < cs->nClone; i++)
{
if (cs->clone[i].dst == cs->grabbedOutput)
{
cloneRemove (s, i);
break;
}
}
}
}
static void
clonePreparePaintScreen (CompScreen *s,
int msSinceLastPaint)
{
int i;
CLONE_SCREEN (s);
if (cs->grab)
{
if (cs->grabIndex)
{
cs->offset -= msSinceLastPaint * 0.005f;
if (cs->offset < 0.0f)
cs->offset = 0.0f;
}
else
{
cs->offset += msSinceLastPaint * 0.005f;
if (cs->offset >= 1.0f)
cs->offset = 1.0f;
}
}
UNWRAP (cs, s, preparePaintScreen);
(*s->preparePaintScreen) (s, msSinceLastPaint);
WRAP (cs, s, preparePaintScreen, clonePreparePaintScreen);
for (i = 0; i < cs->nClone; i++)
{
CompOutput *src = &s->outputDev[cs->clone[i].src];
CompOutput *dst = &s->outputDev[cs->clone[i].dst];
int dx, dy;
dx = dst->region.extents.x1 - src->region.extents.x1;
dy = dst->region.extents.y1 - src->region.extents.y1;
if (s->damageMask & COMP_SCREEN_DAMAGE_REGION_MASK)
{
if (src->width != dst->width || src->height != dst->height)
{
XSubtractRegion (&dst->region, &emptyRegion,
cs->clone[i].region);
XUnionRegion (s->damage, cs->clone[i].region, s->damage);
XSubtractRegion (&src->region, &emptyRegion,
cs->clone[i].region);
}
else
{
XSubtractRegion (s->damage, &dst->region, cs->clone[i].region);
XOffsetRegion (cs->clone[i].region, dx, dy);
XUnionRegion (s->damage, cs->clone[i].region, s->damage);
XSubtractRegion (s->damage, &src->region, cs->clone[i].region);
XOffsetRegion (cs->clone[i].region, -dx, -dy);
}
}
else
{
XSubtractRegion (&src->region, &emptyRegion, cs->clone[i].region);
}
}
}
static void
cloneDonePaintScreen (CompScreen *s)
{
CLONE_SCREEN (s);
if (cs->grab)
{
if (cs->offset == 1.0f)
cloneFinish (s);
damageScreen (s);
}
UNWRAP (cs, s, donePaintScreen);
(*s->donePaintScreen) (s);
WRAP (cs, s, donePaintScreen, cloneDonePaintScreen);
}
static Bool
clonePaintOutput (CompScreen *s,
const ScreenPaintAttrib *sAttrib,
const CompTransform *transform,
Region region,
CompOutput *outputPtr,
unsigned int mask)
{
Bool status;
int i, dst, output = 0;
CLONE_SCREEN (s);
dst = output = (outputPtr->id != ~0) ? outputPtr->id : 0;
if (!cs->grab || cs->grabbedOutput != output)
{
for (i = 0; i < cs->nClone; i++)
{
if (cs->clone[i].dst == output)
{
region = cs->clone[i].region;
dst = cs->clone[i].src;
if (s->outputDev[dst].width != s->outputDev[output].width ||
s->outputDev[dst].height != s->outputDev[output].height )
cs->transformed = TRUE;
else
cs->transformed = FALSE;
break;
}
}
}
UNWRAP (cs, s, paintOutput);
if (outputPtr->id != ~0)
status = (*s->paintOutput) (s, sAttrib, transform, region,
&s->outputDev[dst], mask);
else
status = (*s->paintOutput) (s, sAttrib, transform, region,
outputPtr, mask);
WRAP (cs, s, paintOutput, clonePaintOutput);
if (cs->grab)
{
CompTransform sTransform = *transform;
CompWindow *w;
GLenum filter;
float zoom1, zoom2x, zoom2y, x1, y1, x2, y2;
float zoomX, zoomY;
int dx, dy;
zoom1 = 160.0f / s->outputDev[cs->src].height;
x1 = cs->x - (s->outputDev[cs->src].region.extents.x1 * zoom1);
y1 = cs->y - (s->outputDev[cs->src].region.extents.y1 * zoom1);
x1 -= (s->outputDev[cs->src].width * zoom1) / 2;
y1 -= (s->outputDev[cs->src].height * zoom1) / 2;
if (cs->grabIndex)
{
x2 = s->outputDev[cs->grabbedOutput].region.extents.x1 -
s->outputDev[cs->src].region.extents.x1;
y2 = s->outputDev[cs->grabbedOutput].region.extents.y1 -
s->outputDev[cs->src].region.extents.y1;
zoom2x = (float) s->outputDev[cs->grabbedOutput].width /
s->outputDev[cs->src].width;
zoom2y = (float) s->outputDev[cs->grabbedOutput].height /
s->outputDev[cs->src].height;
}
else
{
x2 = s->outputDev[cs->dst].region.extents.x1 -
s->outputDev[cs->src].region.extents.x1;
y2 = s->outputDev[cs->dst].region.extents.y1 -
s->outputDev[cs->src].region.extents.y1;
zoom2x = (float) s->outputDev[cs->dst].width /
s->outputDev[cs->src].width;
zoom2y = (float) s->outputDev[cs->dst].height /
s->outputDev[cs->src].height;
}
/* XXX: hmm.. why do I need this.. */
if (x2 < 0.0f)
x2 *= zoom2x;
if (y2 < 0.0f)
y2 *= zoom2y;
dx = x1 * (1.0f - cs->offset) + x2 * cs->offset;
dy = y1 * (1.0f - cs->offset) + y2 * cs->offset;
zoomX = zoom1 * (1.0f - cs->offset) + zoom2x * cs->offset;
zoomY = zoom1 * (1.0f - cs->offset) + zoom2y * cs->offset;
matrixTranslate (&sTransform, -0.5f, -0.5f, -DEFAULT_Z_CAMERA);
matrixScale (&sTransform,
1.0f / s->outputDev[output].width,
-1.0f / s->outputDev[output].height,
1.0f);
matrixTranslate (&sTransform,
dx - s->outputDev[output].region.extents.x1,
dy - s->outputDev[output].region.extents.y2,
0.0f);
matrixScale (&sTransform, zoomX, zoomY, 1.0f);
glPushMatrix ();
glLoadMatrixf (sTransform.m);
filter = s->display->textureFilter;
if (cs->offset == 0.0f)
s->display->textureFilter = GL_LINEAR_MIPMAP_LINEAR;
for (w = s->windows; w; w = w->next)
{
if (w->destroyed)
continue;
if (!w->shaded)
{
if (w->attrib.map_state != IsViewable || !w->damaged)
continue;
}
(*s->paintWindow) (w, &w->paint, &sTransform,
&s->outputDev[cs->src].region,
PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK);
}
s->display->textureFilter = filter;
glPopMatrix ();
}
return status;
}
static Bool
clonePaintWindow (CompWindow *w,
const WindowPaintAttrib *attrib,
const CompTransform *transform,
Region region,
unsigned int mask)
{
Bool status;
CLONE_SCREEN (w->screen);
if (cs->nClone && cs->transformed)
mask |= PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK;
UNWRAP (cs, w->screen, paintWindow);
status = (*w->screen->paintWindow) (w, attrib, transform, region, mask);
WRAP (cs, w->screen, paintWindow, clonePaintWindow);
return status;
}
static Bool
cloneInitiate (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
CompScreen *s;
Window xid;
xid = getIntOptionNamed (option, nOption, "root", 0);
s = findScreenAtDisplay (d, xid);
if (s)
{
int i;
CLONE_SCREEN (s);
if (cs->grab || otherScreenGrabExist (s, "clone", NULL))
return FALSE;
if (!cs->grabIndex)
cs->grabIndex = pushScreenGrab (s, None, "clone");
cs->grab = TRUE;
cs->x = getIntOptionNamed (option, nOption, "x", 0);
cs->y = getIntOptionNamed (option, nOption, "y", 0);
cs->src = cs->grabbedOutput = outputDeviceForPoint (s, cs->x, cs->y);
/* trace source */
i = 0;
while (i < cs->nClone)
{
if (cs->clone[i].dst == cs->src)
{
cs->src = cs->clone[i].src;
i = 0;
}
else
{
i++;
}
}
if (state & CompActionStateInitButton)
action->state |= CompActionStateTermButton;
}
return FALSE;
}
static Bool
cloneTerminate (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
CompScreen *s;
Window xid;
xid = getIntOptionNamed (option, nOption, "root", 0);
for (s = d->screens; s; s = s->next)
{
CLONE_SCREEN (s);
if (xid && s->root != xid)
continue;
if (cs->grabIndex)
{
int x, y;
removeScreenGrab (s, cs->grabIndex, NULL);
cs->grabIndex = 0;
x = getIntOptionNamed (option, nOption, "x", 0);
y = getIntOptionNamed (option, nOption, "y", 0);
cs->dst = outputDeviceForPoint (s, x, y);
damageScreen (s);
}
}
action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
return FALSE;
}
static void
cloneSetStrutsForCloneWindow (CompScreen *s,
CloneClone *clone)
{
CompOutput *output = &s->outputDev[clone->dst];
XRectangle *rect = NULL;
CompStruts *struts;
CompWindow *w;
w = findWindowAtScreen (s, clone->input);
if (!w)
return;
struts = malloc (sizeof (CompStruts));
if (!struts)
return;
if (w->struts)
free (w->struts);
struts->left.x = 0;
struts->left.y = 0;
struts->left.width = 0;
struts->left.height = s->height;
struts->right.x = s->width;
struts->right.y = 0;
struts->right.width = 0;
struts->right.height = s->height;
struts->top.x = 0;
struts->top.y = 0;
struts->top.width = s->width;
struts->top.height = 0;
struts->bottom.x = 0;
struts->bottom.y = s->height;
struts->bottom.width = s->width;
struts->bottom.height = 0;
/* create struts relative to a screen edge that this output is next to */
if (output->region.extents.x1 == 0)
rect = &struts->left;
else if (output->region.extents.x2 == s->width)
rect = &struts->right;
else if (output->region.extents.y1 == 0)
rect = &struts->top;
else if (output->region.extents.y2 == s->height)
rect = &struts->bottom;
if (rect)
{
rect->x = output->region.extents.x1;
rect->y = output->region.extents.y1;
rect->width = output->width;
rect->height = output->height;
}
w->struts = struts;
}
static void
cloneHandleMotionEvent (CompScreen *s,
int xRoot,
int yRoot)
{
CLONE_SCREEN (s);
if (cs->grabIndex)
{
cs->x = xRoot;
cs->y = yRoot;
damageScreen (s);
}
}
static void
cloneHandleEvent (CompDisplay *d,
XEvent *event)
{
CompScreen *s;
CLONE_DISPLAY (d);
switch (event->type) {
case MotionNotify:
s = findScreenAtDisplay (d, event->xmotion.root);
if (s)
cloneHandleMotionEvent (s, pointerX, pointerY);
break;
case EnterNotify:
case LeaveNotify:
s = findScreenAtDisplay (d, event->xcrossing.root);
if (s)
cloneHandleMotionEvent (s, pointerX, pointerY);
default:
break;
}
UNWRAP (cd, d, handleEvent);
(*d->handleEvent) (d, event);
WRAP (cd, d, handleEvent, cloneHandleEvent);
switch (event->type) {
case CreateNotify:
s = findScreenAtDisplay (d, event->xcreatewindow.parent);
if (s)
{
int i;
CLONE_SCREEN (s);
for (i = 0; i < cs->nClone; i++)
if (event->xcreatewindow.window == cs->clone[i].input)
cloneSetStrutsForCloneWindow (s, &cs->clone[i]);
}
default:
break;
}
}
static void
cloneOutputChangeNotify (CompScreen *s)
{
int i;
CLONE_SCREEN (s);
/* remove clones with destination or source that doesn't exist */
for (i = 0; i < cs->nClone; i++)
{
if (cs->clone[i].dst >= s->nOutputDev ||
cs->clone[i].src >= s->nOutputDev)
{
cloneRemove (s, i);
i = 0;
continue;
}
}
UNWRAP (cs, s, outputChangeNotify);
(*s->outputChangeNotify) (s);
WRAP (cs, s, outputChangeNotify, cloneOutputChangeNotify);
}
static CompOption *
cloneGetDisplayOptions (CompPlugin *plugin,
CompDisplay *display,
int *count)
{
CLONE_DISPLAY (display);
*count = NUM_OPTIONS (cd);
return cd->opt;
}
static Bool
cloneSetDisplayOption (CompPlugin *plugin,
CompDisplay *display,
const char *name,
CompOptionValue *value)
{
CompOption *o;
CLONE_DISPLAY (display);
o = compFindOption (cd->opt, NUM_OPTIONS (cd), name, NULL);
if (!o)
return FALSE;
return compSetDisplayOption (display, o, value);
}
static const CompMetadataOptionInfo cloneDisplayOptionInfo[] = {
{ "initiate_button", "button", 0, cloneInitiate, cloneTerminate }
};
static Bool
cloneInitDisplay (CompPlugin *p,
CompDisplay *d)
{
CloneDisplay *cd;
if (!checkPluginABI ("core", CORE_ABIVERSION))
return FALSE;
cd = malloc (sizeof (CloneDisplay));
if (!cd)
return FALSE;
if (!compInitDisplayOptionsFromMetadata (d,
&cloneMetadata,
cloneDisplayOptionInfo,
cd->opt,
CLONE_DISPLAY_OPTION_NUM))
{
free (cd);
return FALSE;
}
cd->screenPrivateIndex = allocateScreenPrivateIndex (d);
if (cd->screenPrivateIndex < 0)
{
compFiniDisplayOptions (d, cd->opt, CLONE_DISPLAY_OPTION_NUM);
free (cd);
return FALSE;
}
WRAP (cd, d, handleEvent, cloneHandleEvent);
d->base.privates[displayPrivateIndex].ptr = cd;
return TRUE;
}
static void
cloneFiniDisplay (CompPlugin *p,
CompDisplay *d)
{
CLONE_DISPLAY (d);
freeScreenPrivateIndex (d, cd->screenPrivateIndex);
UNWRAP (cd, d, handleEvent);
compFiniDisplayOptions (d, cd->opt, CLONE_DISPLAY_OPTION_NUM);
free (cd);
}
static Bool
cloneInitScreen (CompPlugin *p,
CompScreen *s)
{
CloneScreen *cs;
CLONE_DISPLAY (s->display);
cs = malloc (sizeof (CloneScreen));
if (!cs)
return FALSE;
cs->grabIndex = 0;
cs->grab = FALSE;
cs->offset = 1.0f;
cs->transformed = FALSE;
cs->nClone = 0;
cs->clone = NULL;
cs->src = 0;
WRAP (cs, s, preparePaintScreen, clonePreparePaintScreen);
WRAP (cs, s, donePaintScreen, cloneDonePaintScreen);
WRAP (cs, s, paintOutput, clonePaintOutput);
WRAP (cs, s, paintWindow, clonePaintWindow);
WRAP (cs, s, outputChangeNotify, cloneOutputChangeNotify);
s->base.privates[cd->screenPrivateIndex].ptr = cs;
return TRUE;
}
static void
cloneFiniScreen (CompPlugin *p,
CompScreen *s)
{
int i;
CLONE_SCREEN (s);
for (i = 0; i < cs->nClone; i++)
cloneRemove (s, i);
if (cs->clone)
free (cs->clone);
UNWRAP (cs, s, preparePaintScreen);
UNWRAP (cs, s, donePaintScreen);
UNWRAP (cs, s, paintOutput);
UNWRAP (cs, s, paintWindow);
UNWRAP (cs, s, outputChangeNotify);
free (cs);
}
static CompBool
cloneInitObject (CompPlugin *p,
CompObject *o)
{
static InitPluginObjectProc dispTab[] = {
(InitPluginObjectProc) 0, /* InitCore */
(InitPluginObjectProc) cloneInitDisplay,
(InitPluginObjectProc) cloneInitScreen
};
RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
}
static void
cloneFiniObject (CompPlugin *p,
CompObject *o)
{
static FiniPluginObjectProc dispTab[] = {
(FiniPluginObjectProc) 0, /* FiniCore */
(FiniPluginObjectProc) cloneFiniDisplay,
(FiniPluginObjectProc) cloneFiniScreen
};
DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
}
static CompOption *
cloneGetObjectOptions (CompPlugin *plugin,
CompObject *object,
int *count)
{
static GetPluginObjectOptionsProc dispTab[] = {
(GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
(GetPluginObjectOptionsProc) cloneGetDisplayOptions
};
*count = 0;
RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
(void *) count, (plugin, object, count));
}
static CompBool
cloneSetObjectOption (CompPlugin *plugin,
CompObject *object,
const char *name,
CompOptionValue *value)
{
static SetPluginObjectOptionProc dispTab[] = {
(SetPluginObjectOptionProc) 0, /* SetCoreOption */
(SetPluginObjectOptionProc) cloneSetDisplayOption
};
RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
(plugin, object, name, value));
}
static Bool
cloneInit (CompPlugin *p)
{
if (!compInitPluginMetadataFromInfo (&cloneMetadata,
p->vTable->name,
cloneDisplayOptionInfo,
CLONE_DISPLAY_OPTION_NUM,
0, 0))
return FALSE;
displayPrivateIndex = allocateDisplayPrivateIndex ();
if (displayPrivateIndex < 0)
{
compFiniMetadata (&cloneMetadata);
return FALSE;
}
compAddMetadataFromFile (&cloneMetadata, p->vTable->name);
return TRUE;
}
static void
cloneFini (CompPlugin *p)
{
freeDisplayPrivateIndex (displayPrivateIndex);
compFiniMetadata (&cloneMetadata);
}
static CompMetadata *
cloneGetMetadata (CompPlugin *plugin)
{
return &cloneMetadata;
}
CompPluginVTable cloneVTable = {
"clone",
cloneGetMetadata,
cloneInit,
cloneFini,
cloneInitObject,
cloneFiniObject,
cloneGetObjectOptions,
cloneSetObjectOption
};
CompPluginVTable *
getCompPluginInfo20070830 (void)
{
return &cloneVTable;
}