/* * * 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 #include #include #include #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; }