/*
*
* Compiz widget handling plugin
*
* widget.c
*
* Copyright : (C) 2007 by Danny Baumann
* E-mail : maniac@opencompositing.org
*
* Idea based on widget.c:
* Copyright : (C) 2006 Quinn Storm
* E-mail : livinglatexkali@gmail.com
*
* Copyright : (C) 2007 Mike Dransfield
* E-mail : mike@blueroot.co.uk
*
* 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.
*
*/
#include <string.h>
#include <compiz.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include "widget_options.h"
static int displayPrivateIndex;
typedef enum _WidgetState
{
StateOff = 0,
StateFadeIn,
StateOn,
StateFadeOut
} WidgetState;
typedef enum _WidgetPropertyState
{
PropertyNotSet = 0,
PropertyWidget,
PropertyNoWidget
} WidgetPropertyState;
typedef struct _WidgetDisplay
{
int screenPrivateIndex;
HandleEventProc handleEvent;
MatchPropertyChangedProc matchPropertyChanged;
MatchExpHandlerChangedProc matchExpHandlerChanged;
MatchInitExpProc matchInitExp;
Atom compizWidgetAtom;
} WidgetDisplay;
typedef struct _WidgetScreen
{
int windowPrivateIndex;
PreparePaintScreenProc preparePaintScreen;
DonePaintScreenProc donePaintScreen;
PaintWindowProc paintWindow;
WidgetState state;
int fadeTime;
int grabIndex;
Cursor cursor;
} WidgetScreen;
typedef struct _WidgetWindow
{
Bool isWidget;
Bool wasUnmapped;
CompWindow *parentWidget;
CompTimeoutHandle matchUpdateHandle;
CompTimeoutHandle widgetStatusUpdateHandle;
WidgetPropertyState propertyState;
} WidgetWindow;
#define GET_WIDGET_DISPLAY(d) \
((WidgetDisplay *) (d)->privates[displayPrivateIndex].ptr)
#define WIDGET_DISPLAY(d) \
WidgetDisplay *wd = GET_WIDGET_DISPLAY (d)
#define GET_WIDGET_SCREEN(s, wd) \
((WidgetScreen *) (s)->privates[(wd)->screenPrivateIndex].ptr)
#define WIDGET_SCREEN(s) \
WidgetScreen *ws = GET_WIDGET_SCREEN (s, GET_WIDGET_DISPLAY (s->display))
#define GET_WIDGET_WINDOW(w, ws) \
((WidgetWindow *) (w)->privates[(ws)->windowPrivateIndex].ptr)
#define WIDGET_WINDOW(w) \
WidgetWindow *ww = GET_WIDGET_WINDOW (w, \
GET_WIDGET_SCREEN (w->screen, \
GET_WIDGET_DISPLAY (w->screen->display)))
static void
widgetUpdateTreeStatus (CompWindow *w)
{
CompWindow *p;
WidgetWindow *pww;
WIDGET_SCREEN (w->screen);
/* first clear out every reference to our window */
for (p = w->screen->windows; p; p = p->next)
{
pww = GET_WIDGET_WINDOW (p, ws);
if (pww->parentWidget == w)
pww->parentWidget = NULL;
}
for (p = w->screen->windows; p; p = p->next)
{
Window clientLeader;
if (p->attrib.override_redirect)
clientLeader = getClientLeader (p);
else
clientLeader = p->clientLeader;
if ((clientLeader == w->clientLeader) && (w->id != p->id))
{
WIDGET_SCREEN (w->screen);
pww = GET_WIDGET_WINDOW (p, ws);
pww->parentWidget = w;
}
}
}
static Bool
widgetUpdateWidgetStatus (CompWindow *w)
{
Bool isWidget, retval;
WIDGET_WINDOW (w);
switch (ww->propertyState) {
case PropertyWidget:
isWidget = TRUE;
break;
case PropertyNoWidget:
isWidget = FALSE;
break;
default:
isWidget = matchEval (widgetGetMatch (w->screen), w);
break;
}
retval = (!isWidget && ww->isWidget) || (isWidget && !ww->isWidget);
ww->isWidget = isWidget;
return retval;
}
static Bool
widgetUpdateWidgetPropertyState (CompWindow *w)
{
CompDisplay *d = w->screen->display;
Atom retType;
int format, result;
unsigned long nitems, remain;
unsigned char *data = NULL;
WIDGET_DISPLAY (d);
WIDGET_WINDOW (w);
result = XGetWindowProperty (d->display, w->id, wd->compizWidgetAtom,
0, 1L, FALSE, AnyPropertyType, &retType,
&format, &nitems, &remain, &data);
if (result == Success && nitems && data && format == 32)
{
unsigned long int *retData = (unsigned long int *) data;
if (*retData)
ww->propertyState = PropertyWidget;
else
ww->propertyState = PropertyNoWidget;
XFree (data);
}
else
ww->propertyState = PropertyNotSet;
return widgetUpdateWidgetStatus (w);
}
static void
widgetUpdateWidgetMapState (CompWindow *w,
Bool map)
{
WIDGET_WINDOW (w);
if (map && ww->wasUnmapped)
{
XMapWindow (w->screen->display->display, w->id);
raiseWindow (w);
ww->wasUnmapped = FALSE;
}
else if (!map && !ww->wasUnmapped)
{
/* never set ww->wasUnmapped on previously unmapped windows -
it might happen that we map windows when entering the
widget mode which aren't supposed to be unmapped */
if (w->attrib.map_state == IsViewable)
{
XUnmapWindow (w->screen->display->display, w->id);
ww->wasUnmapped = TRUE;
}
}
}
static void
widgetSetWidgetLayerMapState (CompScreen *s,
Bool map)
{
CompWindow *w;
for (w = s->windows; w; w = w->next)
{
WIDGET_WINDOW (w);
if (!ww->isWidget)
continue;
widgetUpdateWidgetMapState (w, map);
}
}
static Bool
widgetRegisterExpHandler (void *closure)
{
CompDisplay *d = (CompDisplay *) closure;
(*d->matchExpHandlerChanged) (d);
return FALSE;
}
static Bool
widgetMatchExpEval (CompDisplay *d,
CompWindow *w,
CompPrivate private)
{
WIDGET_WINDOW (w);
return ((private.val && ww->isWidget) || (!private.val && !ww->isWidget));
}
static void
widgetMatchInitExp (CompDisplay *d,
CompMatchExp *exp,
const char *value)
{
WIDGET_DISPLAY (d);
if (strncmp (value, "widget=", 7) == 0)
{
exp->fini = NULL;
exp->eval = widgetMatchExpEval;
exp->priv.val = strtol (value + 7, NULL, 0);
}
else
{
UNWRAP (wd, d, matchInitExp);
(*d->matchInitExp) (d, exp, value);
WRAP (wd, d, matchInitExp, widgetMatchInitExp);
}
}
static void
widgetMatchExpHandlerChanged (CompDisplay *d)
{
CompScreen *s;
CompWindow *w;
WIDGET_DISPLAY (d);
UNWRAP (wd, d, matchExpHandlerChanged);
(*d->matchExpHandlerChanged) (d);
WRAP (wd, d, matchExpHandlerChanged, widgetMatchExpHandlerChanged);
/* match options are up to date after the call to matchExpHandlerChanged */
for (s = d->screens; s; s = s->next)
{
for (w = s->windows; w; w = w->next)
if (widgetUpdateWidgetStatus (w))
{
Bool map;
WIDGET_SCREEN (s);
WIDGET_WINDOW (w);
map = !ww->isWidget || (ws->state != StateOff);
widgetUpdateWidgetMapState (w, map);
widgetUpdateTreeStatus (w);
(*d->matchPropertyChanged) (d, w);
}
}
}
static Bool
widgetToggle (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
Window xid;
CompScreen *s;
xid = getIntOptionNamed (option, nOption, "root", 0);
s = findScreenAtDisplay (d, xid);
if (s)
{
WIDGET_SCREEN (s);
switch (ws->state) {
case StateOff:
case StateFadeOut:
widgetSetWidgetLayerMapState (s, TRUE);
ws->fadeTime = 1000.0f * widgetGetFadeTime (s);
ws->state = StateFadeIn;
break;
case StateOn:
case StateFadeIn:
widgetSetWidgetLayerMapState (s, FALSE);
ws->fadeTime = 1000.0f * widgetGetFadeTime (s);
ws->state = StateFadeOut;
break;
}
if (!ws->grabIndex)
ws->grabIndex = pushScreenGrab (s, ws->cursor, "widget");
damageScreen (s);
return TRUE;
}
return FALSE;
}
static void
widgetHandleEvent (CompDisplay *d,
XEvent *event)
{
WIDGET_DISPLAY (d);
UNWRAP (wd, d, handleEvent);
(*d->handleEvent) (d, event);
WRAP (wd, d, handleEvent, widgetHandleEvent);
switch (event->type)
{
case PropertyNotify:
if (event->xproperty.atom == wd->compizWidgetAtom)
{
CompWindow *w;
w = findWindowAtDisplay (d, event->xproperty.window);
if (w)
{
if (widgetUpdateWidgetPropertyState (w))
{
Bool map;
WIDGET_SCREEN (w->screen);
WIDGET_WINDOW (w);
map = !ww->isWidget || (ws->state != StateOff);
widgetUpdateWidgetMapState (w, map);
widgetUpdateTreeStatus (w);
(*d->matchPropertyChanged) (d, w);
}
}
}
else if (event->xproperty.atom == d->wmClientLeaderAtom)
{
CompWindow *w;
w = findWindowAtDisplay (d, event->xproperty.window);
if (w)
{
WIDGET_WINDOW (w);
if (ww->isWidget)
widgetUpdateTreeStatus (w);
else if (ww->parentWidget)
widgetUpdateTreeStatus (ww->parentWidget);
}
}
break;
case ButtonPress:
{
CompScreen *s;
/* terminate widget mode if a non-widget window
was clicked */
s = findScreenAtDisplay (d, event->xbutton.root);
if (s && widgetGetEndOnClick (s))
{
WIDGET_SCREEN (s);
if (ws->state == StateOn)
{
CompWindow *w;
w = findWindowAtScreen (s, event->xbutton.window);
if (w && w->managed)
{
WIDGET_WINDOW (w);
if (!ww->isWidget && !ww->parentWidget)
{
CompOption o;
o.type = CompOptionTypeInt;
o.name = "root";
o.value.i = s->root;
widgetToggle (d, NULL, 0, &o, 1);
}
}
}
}
}
break;
case MapNotify:
{
CompWindow *w;
w = findWindowAtDisplay (d, event->xmap.window);
if (w)
{
WIDGET_WINDOW (w);
WIDGET_SCREEN (w->screen);
if (ww->isWidget)
widgetUpdateWidgetMapState (w, ws->state != StateOff);
}
}
break;
}
}
static Bool
widgetUpdateMatch (void *closure)
{
CompWindow *w = (CompWindow *) closure;
WIDGET_WINDOW (w);
if (widgetUpdateWidgetStatus (w))
{
widgetUpdateTreeStatus (w);
(*w->screen->display->matchPropertyChanged) (w->screen->display, w);
}
ww->matchUpdateHandle = 0;
return FALSE;
}
static Bool
widgetUpdateStatus (void *closure)
{
CompWindow *w = (CompWindow *) closure;
Window clientLeader;
WIDGET_WINDOW (w);
WIDGET_SCREEN (w->screen);
if (widgetUpdateWidgetPropertyState (w))
widgetUpdateWidgetMapState (w, (ws->state != StateOff));
if (w->attrib.override_redirect)
clientLeader = getClientLeader (w);
else
clientLeader = w->clientLeader;
if (ww->isWidget)
{
widgetUpdateTreeStatus (w);
}
else if (clientLeader)
{
CompWindow *lw;
lw = findWindowAtScreen (w->screen, clientLeader);
if (lw)
{
WidgetWindow *lww;
lww = GET_WIDGET_WINDOW (lw, ws);
if (lww->isWidget)
ww->parentWidget = lw;
else if (lww->parentWidget)
ww->parentWidget = lww->parentWidget;
}
}
ww->widgetStatusUpdateHandle = 0;
return FALSE;
}
static void
widgetMatchPropertyChanged (CompDisplay *d,
CompWindow *w)
{
WIDGET_DISPLAY (d);
WIDGET_WINDOW (w);
/* one shot timeout which will update the widget status (timer
is needed because we don't want to call wrapped functions
recursively) */
if (!ww->matchUpdateHandle)
ww->matchUpdateHandle = compAddTimeout (0, widgetUpdateMatch,
(void *) w);
UNWRAP (wd, d, matchPropertyChanged);
(*d->matchPropertyChanged) (d, w);
WRAP (wd, d, matchPropertyChanged, widgetMatchPropertyChanged);
}
static Bool
widgetPaintWindow (CompWindow *w,
const WindowPaintAttrib *attrib,
const CompTransform *transform,
Region region,
unsigned int mask)
{
Bool status;
CompScreen *s = w->screen;
WIDGET_SCREEN (s);
if (ws->state != StateOff)
{
WindowPaintAttrib wAttrib = *attrib;
float fadeProgress;
WIDGET_WINDOW (w);
if (ws->state == StateOn)
fadeProgress = 1.0f;
else
{
fadeProgress = widgetGetFadeTime (s);
if (fadeProgress)
fadeProgress = (float) ws->fadeTime / (1000.0f * fadeProgress);
fadeProgress = 1.0f - fadeProgress;
}
if (!ww->isWidget && !ww->parentWidget)
{
float progress;
if ((ws->state == StateFadeIn) || (ws->state == StateOn))
fadeProgress = 1.0f - fadeProgress;
progress = widgetGetBgSaturation (s) / 100.0f;
progress += (1.0f - progress) * fadeProgress;
wAttrib.saturation = (float) wAttrib.saturation * progress;
progress = widgetGetBgBrightness (s) / 100.0f;
progress += (1.0f - progress) * fadeProgress;
wAttrib.brightness = (float) wAttrib.brightness * progress;
}
UNWRAP (ws, s, paintWindow);
status = (*s->paintWindow) (w, &wAttrib, transform, region, mask);
WRAP (ws, s, paintWindow, widgetPaintWindow);
}
else
{
UNWRAP (ws, s, paintWindow);
status = (*s->paintWindow) (w, attrib, transform, region, mask);
WRAP (ws, s, paintWindow, widgetPaintWindow);
}
return status;
}
static void
widgetPreparePaintScreen (CompScreen *s,
int msSinceLastPaint)
{
WIDGET_SCREEN (s);
if ((ws->state == StateFadeIn) || (ws->state == StateFadeOut))
{
ws->fadeTime -= msSinceLastPaint;
ws->fadeTime = MAX (ws->fadeTime, 0);
}
UNWRAP (ws, s, preparePaintScreen);
(*s->preparePaintScreen) (s, msSinceLastPaint);
WRAP (ws, s, preparePaintScreen, widgetPreparePaintScreen);
}
static void
widgetDonePaintScreen (CompScreen *s)
{
WIDGET_SCREEN (s);
if ((ws->state == StateFadeIn) || (ws->state == StateFadeOut))
{
if (ws->fadeTime)
damageScreen (s);
else
{
removeScreenGrab (s, ws->grabIndex, NULL);
ws->grabIndex = 0;
if (ws->state == StateFadeIn)
ws->state = StateOn;
else
ws->state = StateOff;
}
}
UNWRAP (ws, s, donePaintScreen);
(*s->donePaintScreen) (s);
WRAP (ws, s, donePaintScreen, widgetDonePaintScreen);
}
static void
widgetScreenOptionChanged (CompScreen *s,
CompOption *opt,
WidgetScreenOptions num)
{
switch (num) {
case WidgetScreenOptionMatch:
{
CompWindow *w;
for (w = s->windows; w; w = w->next)
if (widgetUpdateWidgetStatus (w))
{
Bool map;
WIDGET_SCREEN (s);
WIDGET_WINDOW (w);
map = !ww->isWidget || (ws->state != StateOff);
widgetUpdateWidgetMapState (w, map);
widgetUpdateTreeStatus (w);
(*s->display->matchPropertyChanged) (s->display, w);
}
}
break;
default:
break;
}
}
static Bool
widgetInitDisplay (CompPlugin *p,
CompDisplay *d)
{
WidgetDisplay *wd;
wd = malloc (sizeof (WidgetDisplay));
if (!wd)
return FALSE;
wd->screenPrivateIndex = allocateScreenPrivateIndex (d);
if (wd->screenPrivateIndex < 0)
{
free (wd);
return FALSE;
}
wd->compizWidgetAtom = XInternAtom(d->display, "_COMPIZ_WIDGET", FALSE);
d->privates[displayPrivateIndex].ptr = wd;
widgetSetToggleInitiate (d, widgetToggle);
WRAP (wd, d, handleEvent, widgetHandleEvent);
WRAP (wd, d, matchPropertyChanged, widgetMatchPropertyChanged);
WRAP (wd, d, matchExpHandlerChanged, widgetMatchExpHandlerChanged);
WRAP (wd, d, matchInitExp, widgetMatchInitExp);
/* one shot timeout to which will register the expression handler
after all screens and windows have been initialized */
compAddTimeout (0, widgetRegisterExpHandler, (void *) d);
return TRUE;
}
static void
widgetFiniDisplay (CompPlugin *p,
CompDisplay *d)
{
WIDGET_DISPLAY (d);
freeScreenPrivateIndex (d, wd->screenPrivateIndex);
UNWRAP (wd, d, handleEvent);
UNWRAP (wd, d, matchPropertyChanged);
UNWRAP (wd, d, matchExpHandlerChanged);
UNWRAP (wd, d, matchInitExp);
(*d->matchExpHandlerChanged) (d);
free (wd);
}
static Bool
widgetInitScreen (CompPlugin *p,
CompScreen *s)
{
WidgetScreen *ws;
WIDGET_DISPLAY (s->display);
ws = malloc (sizeof (WidgetScreen));
if (!ws)
return FALSE;
ws->windowPrivateIndex = allocateWindowPrivateIndex (s);
if (ws->windowPrivateIndex < 0)
{
free (ws);
return FALSE;
}
ws->state = StateOff;
ws->cursor = XCreateFontCursor (s->display->display, XC_watch);
ws->grabIndex = 0;
ws->fadeTime = 0;
widgetSetMatchNotify (s, widgetScreenOptionChanged);
s->privates[wd->screenPrivateIndex].ptr = ws;
WRAP (ws, s, paintWindow, widgetPaintWindow);
WRAP (ws, s, preparePaintScreen, widgetPreparePaintScreen);
WRAP (ws, s, donePaintScreen, widgetDonePaintScreen);
return TRUE;
}
static void
widgetFiniScreen (CompPlugin *p,
CompScreen *s)
{
WIDGET_SCREEN (s);
UNWRAP (ws, s, paintWindow);
UNWRAP (ws, s, preparePaintScreen);
UNWRAP (ws, s, donePaintScreen);
freeWindowPrivateIndex (s, ws->windowPrivateIndex);
if (ws->cursor)
XFreeCursor (s->display->display, ws->cursor);
free (ws);
}
static Bool
widgetInitWindow (CompPlugin *p,
CompWindow *w)
{
WidgetWindow *ww;
WIDGET_SCREEN (w->screen);
ww = malloc (sizeof (WidgetWindow));
if (!ww)
return FALSE;
ww->isWidget = FALSE;
ww->propertyState = PropertyNotSet;
ww->parentWidget = NULL;
ww->wasUnmapped = FALSE;
ww->matchUpdateHandle = 0;
w->privates[ws->windowPrivateIndex].ptr = ww;
ww->widgetStatusUpdateHandle = compAddTimeout (0, widgetUpdateStatus,
(void *) w);
return TRUE;
}
static void
widgetFiniWindow (CompPlugin *p,
CompWindow *w)
{
WIDGET_WINDOW (w);
if (ww->wasUnmapped)
widgetUpdateWidgetMapState (w, TRUE);
if (ww->matchUpdateHandle)
compRemoveTimeout (ww->matchUpdateHandle);
if (ww->widgetStatusUpdateHandle)
compRemoveTimeout (ww->widgetStatusUpdateHandle);
free (ww);
}
static Bool
widgetInit (CompPlugin *p)
{
displayPrivateIndex = allocateDisplayPrivateIndex ();
if (displayPrivateIndex < 0)
return FALSE;
return TRUE;
}
static void
widgetFini (CompPlugin *p)
{
freeDisplayPrivateIndex (displayPrivateIndex);
}
static int
widgetGetVersion (CompPlugin *plugin,
int version)
{
return ABIVERSION;
}
static CompPluginVTable widgetVTable = {
"widget",
widgetGetVersion,
NULL,
widgetInit,
widgetFini,
widgetInitDisplay,
widgetFiniDisplay,
widgetInitScreen,
widgetFiniScreen,
widgetInitWindow,
widgetFiniWindow,
NULL,
NULL,
NULL,
NULL
};
CompPluginVTable*
getCompPluginInfo (void)
{
return &widgetVTable;
}
syntax highlighted by Code2HTML, v. 0.9.1