Logo Search packages:      
Sourcecode: beryl-plugins version File versions

decoration.c

/*
 * Copyright © 2005 Novell, Inc.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * Author: David Reveman <davidr@novell.com>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>

#include <X11/Xatom.h>
#include <X11/extensions/shape.h>

#include <beryl.h>
#include <beryl-decoration.h>

#define BERYL_ADVANCED_DECORATION_VERSION 10121980

typedef struct _Vector
{
      int dx;
      int dy;
      int x0;
      int y0;
} Vector;

#define DECOR_BARE   0
#define DECOR_NORMAL 1
#define DECOR_ACTIVE 2
#define DECOR_NUM    3

typedef struct _DecorTexture
{
      struct _DecorTexture *next;
      int refCount;
      Pixmap pixmap;
      Damage damage;
      CompTexture texture;
} DecorTexture;

typedef struct _Decoration
{
      int refCount;
      DecorTexture *texture;
      CompWindowExtents output;
      CompWindowExtents input;
      CompWindowExtents maxInput;
      int minWidth;
      int minHeight;
      long flags;
      decor_quad_t *quad;
      int nQuad;
} Decoration;

typedef struct _ScaledQuad
{
      CompMatrix matrix;
      double sx, sy;                      // stretch scales
      BoxRec box;
} ScaledQuad;

typedef struct _WindowDecoration
{
      Decoration *decor;
      ScaledQuad *quad;
      int nQuad;
} WindowDecoration;

#define DECOR_APPLY_PAINT_MODIFIERS_DEFAULT     TRUE
#define DECOR_SHADOWS_ON_DOCKS_DEFAULT          FALSE
#define DECOR_MIPMAP_DEFAULT              FALSE

#define DECOR_DISPLAY_OPTION_APPLY_PAINT_MODIFIERS 0
#define DECOR_DISPLAY_OPTION_SHADOWS_ON_DOCKS 1
#define DECOR_DISPLAY_OPTION_COMMAND         2
#define DECOR_DISPLAY_OPTION_MIPMAP          3
#define DECOR_DISPLAY_OPTION_NUM             4

static int displayPrivateIndex;

typedef struct _DecorDisplay
{
      int screenPrivateIndex;
      HandleEventProc handleEvent;
      DecorTexture *textures;
      Atom supportingDmCheckAtom;
      Atom winDecorAtom;
      Atom decorAtom[DECOR_NUM];

      CompOption opt[DECOR_DISPLAY_OPTION_NUM];
} DecorDisplay;

typedef struct _DecorScreen
{
      int windowPrivateIndex;

      Window dmWin;

      Decoration *decor[DECOR_NUM];

      DrawWindowProc drawWindow;
      DamageWindowRectProc damageWindowRect;
      GetOutputExtentsForWindowProc getOutputExtentsForWindow;

      WindowMoveNotifyProc windowMoveNotify;
      WindowResizeNotifyProc windowResizeNotify;

      WindowStateChangeNotifyProc windowStateChangeNotify;
} DecorScreen;

typedef struct _DecorWindow
{
      WindowDecoration *wd;
      Decoration *decor;
      Bool needsUpdate;
} DecorWindow;

#define GET_DECOR_DISPLAY(d)                          \
    ((DecorDisplay *) (d)->privates[displayPrivateIndex].ptr)

#define DECOR_DISPLAY(d)                 \
    DecorDisplay *dd = GET_DECOR_DISPLAY (d)

#define GET_DECOR_SCREEN(s, dd)                                 \
    ((DecorScreen *) (s)->privates[(dd)->screenPrivateIndex].ptr)

#define DECOR_SCREEN(s)                                        \
    DecorScreen *ds = GET_DECOR_SCREEN (s, GET_DECOR_DISPLAY (s->display))

#define GET_DECOR_WINDOW(w, ds)                               \
    ((DecorWindow *) (w)->privates[(ds)->windowPrivateIndex].ptr)

#define DECOR_WINDOW(w)                                \
    DecorWindow *dw = GET_DECOR_WINDOW  (w,                  \
            GET_DECOR_SCREEN  (w->screen,        \
                GET_DECOR_DISPLAY (w->screen->display)))

#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption))
static void decorDisplayInitOptions(DecorDisplay * dd)
{
      CompOption *o;

      o = &dd->opt[DECOR_DISPLAY_OPTION_APPLY_PAINT_MODIFIERS];
      o->advanced = False;
      o->name = "apply_paint_modifiers";
      o->group = N_("Misc. options");
      o->subGroup = N_("Appearance");
      o->displayHints = "";
      o->shortDesc = N_("Apply Transparency/Brightness/Saturation");
      o->longDesc =
                  N_
                  ("Apply the transparency/brightness/saturation of the Window to the Decoration.");
      o->type = CompOptionTypeBool;
      o->value.b = DECOR_APPLY_PAINT_MODIFIERS_DEFAULT;

      o = &dd->opt[DECOR_DISPLAY_OPTION_COMMAND];
      o->advanced = False;
      o->name = "command";
      o->group = N_("Misc. options");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Command Line");
      o->longDesc = N_("Decorator Command Line that is executed if no "
                               "decorator is already running.");
      o->type = CompOptionTypeString;
      o->value.s = strdup("");
      o->rest.s.string = NULL;
      o->rest.s.nString = 0;

      o = &dd->opt[DECOR_DISPLAY_OPTION_SHADOWS_ON_DOCKS];
      o->advanced = False;
      o->name = "shadows_on_docks";
      o->group = N_("Misc. options");
      o->subGroup = N_("Appearance");
      o->displayHints = "";
      o->shortDesc = N_("Draw Shadows on Panel Type Windows");
      o->longDesc = N_("Draw basic Shadow Decoration on panel windows.");
      o->type = CompOptionTypeBool;
      o->value.b = DECOR_SHADOWS_ON_DOCKS_DEFAULT;

      /*o = &dd->opt[DECOR_DISPLAY_OPTION_DROP_SHADOWS];
         o->advanced=False;
         o->name = "drop_shadows";
         o->group=N_("");
         o->subGroup=N_("");
         o->displayHints="";
         o->shortDesc = N_("Draw simple shaped drop-shadows");
         o->longDesc =
         N_("Draw a simple shaped drop-shadow on windows, separate from "
         "decoration manager.");
         o->type = CompOptionTypeBool;
         o->value.b = FALSE; */

      o = &dd->opt[DECOR_DISPLAY_OPTION_MIPMAP];
      o->advanced = False;
      o->name = "mipmap";
      o->group = N_("Main");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Mipmap");
      o->longDesc = N_("Allow Mipmaps to be generated for Decoration textures.");
      o->type = CompOptionTypeBool;
      o->value.b = DECOR_MIPMAP_DEFAULT;
}


static CompOption *decorGetDisplayOptions(CompDisplay * display, int *count)
{
      if (display)
      {
            DECOR_DISPLAY(display);

            *count = NUM_OPTIONS(dd);
            return dd->opt;
      }
      else
      {
            DecorDisplay *dd = malloc(sizeof(DecorDisplay));

            decorDisplayInitOptions(dd);
            *count = NUM_OPTIONS(dd);
            return dd->opt;
      }
}

static Bool decorWindowUpdate(CompWindow * w, Bool move);

static Bool
decorSetDisplayOption(CompDisplay * display,
                                char *name, CompOptionValue * value)
{
      CompOption *o;
      int index;

      DECOR_DISPLAY(display);

      o = compFindOption(dd->opt, NUM_OPTIONS(dd), name, &index);
      if (!o)
            return FALSE;

      switch (index)
      {
      case DECOR_DISPLAY_OPTION_APPLY_PAINT_MODIFIERS:
            //case DECOR_DISPLAY_OPTION_DROP_SHADOWS:
            if (compSetBoolOption(o, value))
            {
                  damageScreen(display->screens);
                  return TRUE;
            }
            break;
      case DECOR_DISPLAY_OPTION_COMMAND:
            if (compSetStringOption(o, value))
            {
                  if (display->screens && *o->value.s != '\0')
                  {
                        DECOR_SCREEN(display->screens);

                        /* run decorator command if no decorator is present on
                           first screen */
                        if (!ds->dmWin)
                        {
                              if (fork() == 0)
                              {
                                    putenv(display->displayString);
                                    execl("/bin/sh", "/bin/sh", "-c", o->value.s, NULL);
                                    exit(0);
                              }
                        }
                  }

                  return TRUE;
            }
            break;
      case DECOR_DISPLAY_OPTION_SHADOWS_ON_DOCKS:
            if (compSetBoolOption(o, value))
            {
                  CompScreen *s;
                  CompWindow *w;

                  for (s = display->screens; s; s = s->next)
                        for (w = s->windows; w; w = w->next)
                              decorWindowUpdate(w, TRUE);
                  return TRUE;
            }
      case DECOR_DISPLAY_OPTION_MIPMAP:
            if (compSetBoolOption(o, value))
                  return TRUE;
      default:
            break;
      }

      return FALSE;
}

static Bool
decorDrawWindow(CompWindow * w,
                        const WindowPaintAttrib * attrib,
                        Region region, unsigned int mask)
{
      Bool status;

      DECOR_WINDOW(w);
      if (dw->needsUpdate)
            decorWindowUpdate(w, FALSE);

      DECOR_SCREEN(w->screen);
      DECOR_DISPLAY(w->screen->display);

      if (!(mask & PAINT_WINDOW_SOLID_MASK))
      {
            DECOR_WINDOW(w);

            if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
                  region = getInfiniteRegion();

            if (dw->wd && region->numRects)
            {
                  WindowDecoration *wd = dw->wd;
                  WindowPaintAttrib sAttrib = *attrib;
                  REGION box;
                  int i;

                  box.rects = &box.extents;
                  box.numRects = 1;

                  w->vCount = 0;

                  for (i = 0; i < wd->nQuad; i++)
                  {
                        box.extents = wd->quad[i].box;

                        if (box.extents.x1 < box.extents.x2 &&
                              box.extents.y1 < box.extents.y2)
                        {
                              (*w->screen->addWindowGeometry) (w,
                                                                               &wd->
                                                                               quad
                                                                               [i].
                                                                               matrix, 1, &box, region);
                        }
                  }

                  if (!dd->opt[DECOR_DISPLAY_OPTION_APPLY_PAINT_MODIFIERS].value.b)
                  {
                        sAttrib.opacity = OPAQUE;
                        sAttrib.saturation = COLOR;
                        sAttrib.brightness = BRIGHT;
                  }

                  if (w->vCount)
                  {
                        (*w->screen->drawWindowTexture) (w,
                                                                         &wd->decor->texture->texture,
                                                                         &sAttrib,
                                                                         mask |
                                                                         PAINT_WINDOW_TRANSLUCENT_MASK
                                                                         |
                                                                         PAINT_WINDOW_DECORATION_MASK);
                  }
            }
      }

      UNWRAP(ds, w->screen, drawWindow);
      status = (*w->screen->drawWindow) (w, attrib, region, mask);
      WRAP(ds, w->screen, drawWindow, decorDrawWindow);

      return status;
}

static DecorTexture *decorGetTexture(CompScreen * screen, Pixmap pixmap)
{
      DecorTexture *texture;
      unsigned int width, height, depth, ui;
      Window root;
      int i;

      DECOR_DISPLAY(screen->display);

      for (texture = dd->textures; texture; texture = texture->next)
      {
            if (texture->pixmap == pixmap)
            {
                  texture->refCount++;
                  return texture;
            }
      }

      texture = malloc(sizeof(DecorTexture));
      if (!texture)
            return NULL;

      initTexture(screen, &texture->texture);
      // we get damage events for decorations
      if (texture->texture.mode == TEXTURE_MODE_COPY)
      {
            texture->texture.mode = TEXTURE_MODE_COPY_DAMAGE;
            texture->texture.cmd.damaged = TRUE;
            texture->texture.cmd.fullDamage = TRUE;
      }

      if (!XGetGeometry(screen->display->display, pixmap, &root,
                                &i, &i, &width, &height, &ui, &depth))
      {
            finiTexture(screen, &texture->texture);
            free(texture);
            return NULL;
      }

      if (!bindPixmapToTexture(screen, &texture->texture, pixmap,
                                           width, height, depth))
      {
            finiTexture(screen, &texture->texture);
            free(texture);
            return NULL;
      }

      if (!dd->opt[DECOR_DISPLAY_OPTION_MIPMAP].value.b)
            texture->texture.mipmap = FALSE;

      texture->damage = XDamageCreate(screen->display->display, pixmap,
                                                      XDamageReportRawRectangles);

      texture->refCount = 1;
      texture->pixmap = pixmap;
      texture->next = dd->textures;

      dd->textures = texture;

      return texture;
}

static void decorReleaseTexture(CompScreen * screen, DecorTexture * texture)
{
      DECOR_DISPLAY(screen->display);

      texture->refCount--;
      if (texture->refCount)
            return;

      if (texture == dd->textures)
      {
            dd->textures = texture->next;
      }
      else
      {
            DecorTexture *t;

            for (t = dd->textures; t; t = t->next)
            {
                  if (t->next == texture)
                  {
                        t->next = texture->next;
                        break;
                  }
            }
      }

      finiTexture(screen, &texture->texture);
      free(texture);
}

static void
computeQuadBox(decor_quad_t * q,
                     int width,
                     int height,
                     int *return_x1,
                     int *return_y1,
                     int *return_x2,
                     int *return_y2, double *return_sx, double *return_sy)
{
      int x1, y1, x2, y2;
      double sx, sy;

      sx = 1;
      sy = 1;

      decor_apply_gravity(q->p1.gravity, q->p1.x, q->p1.y, width, height, &x1, &y1);
      decor_apply_gravity(q->p2.gravity, q->p2.x, q->p2.y, width, height, &x2, &y2);

      if (q->clamp & CLAMP_HORZ)
      {
            if (x2 - x1 > 0 && (q->clamp & CLAMP_VERT) &&
                  q->p1.x == 0 && q->p2.x == 0)
                  sx = (double)q->max_width / ((double)x2 - (double)x1);
            if ((q->p1.x == 0 && q->p2.x == 0) || !(q->clamp & CLAMP_VERT))
            {
                  if (x1 < 0)
                        x1 = 0;
                  if (x2 > width)
                        x2 = width;
            }
      }

      if (q->clamp & CLAMP_VERT)
      {
            if (y2 - y1 > 0 && (q->clamp & CLAMP_HORZ) &&
                  q->p1.y == 0 && q->p2.y == 0)
                  sy = (double)q->max_height / ((double)y2 - (double)y1);
            if ((q->p1.y == 0 && q->p2.y == 0) || !(q->clamp & CLAMP_HORZ))
            {
                  if (y1 < 0)
                        y1 = 0;
                  if (y2 > height)
                        y2 = height;
            }
      }

      if (q->max_width < x2 - x1
            && !(q->clamp & CLAMP_VERT && q->clamp & CLAMP_HORZ))
      {
            if (q->align & ALIGN_RIGHT)
                  x1 = x2 - q->max_width;
            else
                  x2 = x1 + q->max_width;
      }

      if (q->max_height < y2 - y1
            && !(q->clamp & CLAMP_VERT && q->clamp & CLAMP_HORZ))
      {
            if (q->align & ALIGN_BOTTOM)
                  y1 = y2 - q->max_height;
            else
                  y2 = y1 + q->max_height;
      }

      *return_x1 = x1;
      *return_y1 = y1;
      *return_x2 = x2;
      *return_y2 = y2;
      if (return_sx)
            *return_sx = sx;
      if (return_sy)
            *return_sy = sy;
}

static Decoration *decorCreateDecoration(CompScreen * screen, Window id,
                                                             Atom decorAtom)
{
      Decoration *decoration;
      Atom actual;
      int result, format;
      unsigned long n, nleft;
      unsigned char *data;
      long *prop;
      Pixmap pixmap;
      decor_extents_t input;
      decor_extents_t maxInput;
      decor_quad_t *quad;
      int nQuad;
      int minWidth, minHeight;
      int left, right, top, bottom;
      int x1, y1, x2, y2;
      double sx, sy;

      result = XGetWindowProperty(screen->display->display, id,
                                                decorAtom, 0L, 1024L, FALSE,
                                                XA_INTEGER, &actual, &format,
                                                &n, &nleft, &data);

      if (result != Success || !n || !data)
            return NULL;

      prop = (long*) data;

      if (decor_property_get_version(prop) != decor_version())
      {
            fprintf (stderr, "%s: decoration: property ignored because "
                                     "version is %d and decoration plugin version is %d\n",
                         getProgramName(), decor_property_get_version (prop),
                         decor_version ());

            XFree(data);
            return NULL;
      }

      nQuad = (n - BASE_PROP_SIZE) / QUAD_PROP_SIZE;

      quad = malloc(sizeof(decor_quad_t) * nQuad);
      if (!quad)
      {
            XFree(data);
            return NULL;
      }

      nQuad = decor_property_to_quads(prop, n, &pixmap, &input, &maxInput,
                              &minWidth, &minHeight, quad);

      XFree(data);

      if (!nQuad)
      {
            free(quad);
            return NULL;
      }

      decoration = malloc(sizeof(Decoration));
      if (!decoration)
      {
            free(quad);
            return NULL;
      }

      decoration->texture = decorGetTexture(screen, pixmap);
      if (!decoration->texture)
      {
            free(decoration);
            free(quad);
            return NULL;
      }

      decoration->minWidth = minWidth;
      decoration->minHeight = minHeight;
      decoration->quad = quad;
      decoration->nQuad = nQuad;

      left = 0;
      right = decoration->minWidth;
      top = 0;
      bottom = decoration->minHeight;

      while (nQuad--)
      {
            computeQuadBox(quad, minWidth, minHeight, &x1, &y1, &x2, &y2, &sx, &sy);

            if (x1 < left)
                  left = x1;
            if (y1 < top)
                  top = y1;
            if (x2 > right)
                  right = x2;
            if (y2 > bottom)
                  bottom = y2;

            quad++;
      }

      decoration->input.left   = input.left;
      decoration->input.right  = input.right;
      decoration->input.top    = input.top;
      decoration->input.bottom = input.bottom;

      decoration->maxInput.left   = maxInput.left;
      decoration->maxInput.right  = maxInput.right;
      decoration->maxInput.top    = maxInput.top;
      decoration->maxInput.bottom = maxInput.bottom;

      decoration->output.left = -left;
      decoration->output.right = right - decoration->minWidth;
      decoration->output.top = -top;
      decoration->output.bottom = bottom - decoration->minHeight;

      decoration->refCount = 1;

      return decoration;
}

static void
decorReleaseDecoration(CompScreen * screen, Decoration * decoration)
{
      decoration->refCount--;
      if (decoration->refCount)
            return;

      decorReleaseTexture(screen, decoration->texture);

      free(decoration->quad);
      free(decoration);
}

static void decorWindowUpdateDecoration(CompWindow * w)
{
      Decoration *decoration;

      DECOR_DISPLAY(w->screen->display);
      DECOR_WINDOW(w);
      dw->needsUpdate = False;

      decoration = decorCreateDecoration(w->screen, w->id, dd->winDecorAtom);

      if (dw->decor)
            decorReleaseDecoration(w->screen, dw->decor);

      dw->decor = decoration;
}

static WindowDecoration *createWindowDecoration(Decoration * d)
{
      WindowDecoration *wd;

      wd = malloc(sizeof(WindowDecoration) + sizeof(ScaledQuad) * d->nQuad);
      if (!wd)
            return NULL;

      d->refCount++;

      wd->decor = d;
      wd->quad = (ScaledQuad *) (wd + 1);
      wd->nQuad = d->nQuad;

      return wd;
}

static void
destroyWindowDecoration(CompScreen * screen, WindowDecoration * wd)
{
      decorReleaseDecoration(screen, wd->decor);
      free(wd);
}

static void setDecorationMatrices(CompWindow * w)
{
      WindowDecoration *wd;
      int i;
      float x0, y0;

      DECOR_WINDOW(w);

      wd = dw->wd;
      if (!wd)
            return;

      for (i = 0; i < wd->nQuad; i++)
      {
            decor_matrix_t a;
            CompMatrix b;

            wd->quad[i].matrix = wd->decor->texture->texture.matrix;

            x0 = wd->decor->quad[i].m.x0;
            y0 = wd->decor->quad[i].m.y0;

            a = wd->decor->quad[i].m;
            b = wd->quad[i].matrix;

            wd->quad[i].matrix.xx = a.xx * b.xx + a.yx * b.xy;
            wd->quad[i].matrix.yx = a.xx * b.yx + a.yx * b.yy;
            wd->quad[i].matrix.xy = a.xy * b.xx + a.yy * b.xy;
            wd->quad[i].matrix.yy = a.xy * b.yx + a.yy * b.yy;
            wd->quad[i].matrix.x0 = x0 * b.xx + y0 * b.xy + b.x0;
            wd->quad[i].matrix.y0 = x0 * b.yx + y0 * b.yy + b.y0;

            if (wd->decor->quad[i].clamp & CLAMP_VERT
                  && wd->decor->quad[i].clamp & CLAMP_HORZ)
            {
                  wd->quad[i].matrix.xx *= wd->quad[i].sx;
                  wd->quad[i].matrix.yx *= wd->quad[i].sx;
                  wd->quad[i].matrix.xy *= wd->quad[i].sy;
                  wd->quad[i].matrix.yy *= wd->quad[i].sy;
            }

            if (wd->decor->quad[i].align & ALIGN_RIGHT)
                  x0 = wd->quad[i].box.x2 - wd->quad[i].box.x1;
            else
                  x0 = 0.0f;

            if (wd->decor->quad[i].align & ALIGN_BOTTOM)
                  y0 = wd->quad[i].box.y2 - wd->quad[i].box.y1;
            else
                  y0 = 0.0f;

            wd->quad[i].matrix.x0 -=
                        x0 * wd->quad[i].matrix.xx + y0 * wd->quad[i].matrix.xy;

            wd->quad[i].matrix.y0 -=
                        y0 * wd->quad[i].matrix.yy + x0 * wd->quad[i].matrix.yx;


            wd->quad[i].matrix.x0 -=
                        wd->quad[i].box.x1 * wd->quad[i].matrix.xx +
                        wd->quad[i].box.y1 * wd->quad[i].matrix.xy;

            wd->quad[i].matrix.y0 -=
                        wd->quad[i].box.y1 * wd->quad[i].matrix.yy +
                        wd->quad[i].box.x1 * wd->quad[i].matrix.yx;

      }
}

static void updateWindowDecorationScale(CompWindow * w)
{
      WindowDecoration *wd;
      int x1, y1, x2, y2;
      double sx, sy;
      int i;

      DECOR_WINDOW(w);

      wd = dw->wd;
      if (!wd)
            return;

      for (i = 0; i < wd->nQuad; i++)
      {
            computeQuadBox(&wd->decor->quad[i], w->width, w->height,
                                 &x1, &y1, &x2, &y2, &sx, &sy);

            wd->quad[i].box.x1 = x1 + w->attrib.x;
            wd->quad[i].box.y1 = y1 + w->attrib.y;
            wd->quad[i].box.x2 = x2 + w->attrib.x;
            wd->quad[i].box.y2 = y2 + w->attrib.y;
            wd->quad[i].sx = sx;
            wd->quad[i].sy = sy;
      }

      setDecorationMatrices(w);
}

static Bool decorCheckSize(CompWindow * w, Decoration * decor)
{
      return (decor->minWidth <= w->width && decor->minHeight <= w->height);
}

static Bool decorWindowUpdate(CompWindow * w, Bool move)
{
      WindowDecoration *wd;
      Decoration *old, *decor = NULL;

      DECOR_SCREEN(w->screen);
      DECOR_WINDOW(w);
      DECOR_DISPLAY(w->screen->display);

      wd = dw->wd;
      old = (wd) ? wd->decor : NULL;

      if (dw->decor && decorCheckSize(w, dw->decor))
      {
            if (w->type != CompWindowTypeFullscreenMask)
                  decor = dw->decor;
      }
      else
      {
            if (w->attrib.override_redirect)
            {
                  if (w->region->numRects == 1 && !w->alpha)
                        decor = ds->decor[DECOR_BARE];
            }
            else
            {
                  switch (w->type)
                  {
                  case CompWindowTypeDialogMask:
                  case CompWindowTypeModalDialogMask:
                  case CompWindowTypeUtilMask:
                  case CompWindowTypeNormalMask:
                        if (w->mwmDecor & (MwmDecorAll | MwmDecorTitle | MwmDecorBorder))
                        {
                              if (w->id == w->screen->display->activeWindow)
                                    decor = ds->decor[DECOR_ACTIVE];
                              else
                                    decor = ds->decor[DECOR_NORMAL];

                              break;
                        }
                        /* fall-through */
                  default:
                        if (w->region->numRects == 1 && !w->alpha)
                              decor = ds->decor[DECOR_BARE];

                        /* no decoration on windows with below state */
                        if (w->state & CompWindowStateBelowMask)
                              decor = NULL;

                        if (w->type == CompWindowTypeDockMask &&
                              !dd->opt[DECOR_DISPLAY_OPTION_SHADOWS_ON_DOCKS].value.b)
                              decor = NULL;
                        if (w->type & CompWindowTypeDesktopMask)
                              decor = NULL;

                        break;
                  }
            }

            if (decor)
            {
                  if (!decorCheckSize(w, decor))
                        decor = NULL;
            }
      }

      if (!ds->dmWin)
            decor = NULL;

      if (decor == old)
            return FALSE;

      damageWindowOutputExtents(w);

      if (old)
            destroyWindowDecoration(w->screen, wd);

      if (decor)
      {
            dw->wd = createWindowDecoration(decor);
            if (!dw->wd)
                  return FALSE;

            if ((w->state & MAXIMIZE_STATE) == MAXIMIZE_STATE)
                  setWindowFrameExtents(w, &decor->maxInput);
            else
                  setWindowFrameExtents(w, &decor->input);

            updateWindowOutputExtents(w);
            damageWindowOutputExtents(w);
            updateWindowDecorationScale(w);
      }
      else
      {
            dw->wd = NULL;
      }

      return TRUE;
}

static void decorCheckForDmOnScreen(CompScreen * s, Bool updateWindows)
{
      CompDisplay *d = s->display;
      Atom actual;
      int result, format;
      unsigned long n, left;
      unsigned char *data;
      Window dmWin = None;

      DECOR_DISPLAY(s->display);
      DECOR_SCREEN(s);

      result = XGetWindowProperty(d->display, s->root,
                                                dd->supportingDmCheckAtom, 0L, 1L,
                                                FALSE, XA_WINDOW, &actual, &format, &n,
                                                &left, &data);

      if (result == Success && n && data)
      {
            XWindowAttributes attr;

            memcpy(&dmWin, data, sizeof(Window));
            XFree(data);

            compCheckForError(d->display);

            XGetWindowAttributes(d->display, dmWin, &attr);

            if (compCheckForError(d->display))
                  dmWin = None;
      }

      if (dmWin != ds->dmWin)
      {
            CompWindow *w;
            int i;

            if (dmWin)
            {
                  for (i = 0; i < DECOR_NUM; i++)
                        ds->decor[i] =
                                    decorCreateDecoration(s, s->root, dd->decorAtom[i]);
            }
            else
            {
                  for (i = 0; i < DECOR_NUM; i++)
                  {
                        if (ds->decor[i])
                        {
                              decorReleaseDecoration(s, ds->decor[i]);
                              ds->decor[i] = 0;
                        }
                  }

                  for (w = s->windows; w; w = w->next)
                  {
                        DECOR_WINDOW(w);

                        if (dw->decor)
                        {
                              decorReleaseDecoration(s, dw->decor);
                              dw->decor = 0;
                        }
                  }
            }

            ds->dmWin = dmWin;

            if (updateWindows)
            {
                  for (w = s->windows; w; w = w->next)
                        decorWindowUpdate(w, TRUE);
            }
      }
}

static void decorHandleEvent(CompDisplay * d, XEvent * event)
{
      Window activeWindow = 0;
      CompWindow *w;

      DECOR_DISPLAY(d);

      switch (event->type)
      {
      case PropertyNotify:
            if (event->xproperty.atom == d->winActiveAtom)
                  activeWindow = d->activeWindow;
            break;
      case DestroyNotify:
            w = findWindowAtDisplay(d, event->xdestroywindow.window);
            if (w)
            {
                  DECOR_SCREEN(w->screen);

                  if (w->id == ds->dmWin)
                        decorCheckForDmOnScreen(w->screen, TRUE);
            }
      default:
            if (event->type == d->damageEvent + XDamageNotify)
            {
                  XDamageNotifyEvent *de = (XDamageNotifyEvent *) event;
                  DecorTexture *t;

                  for (t = dd->textures; t; t = t->next)
                  {
                        if (t->pixmap == de->drawable)
                        {
                              DecorWindow *dw;
                              DecorScreen *ds;
                              CompScreen *s;

                              t->texture.oldMipmaps = TRUE;

                              for (s = d->screens; s; s = s->next)
                              {
                                    ds = GET_DECOR_SCREEN(s, dd);

                                    for (w = s->windows; w; w = w->next)
                                    {

                                          if (w->shaded || w->mapNum)
                                          {
                                                dw = GET_DECOR_WINDOW(w, ds);

                                                if (dw->wd && dw->wd->decor->texture == t)
                                                {
                                                      if (dw->wd->decor->texture->texture.
                                                            mode == TEXTURE_MODE_COPY_DAMAGE)
                                                      {
                                                            dw->wd->decor->texture->texture.cmd.
                                                                        damaged = TRUE;
                                                            dw->wd->decor->texture->texture.cmd.
                                                                        fullDamage = TRUE;
                                                      }
                                                      damageWindowOutputExtents(w);
                                                }
                                          }
                                    }
                              }
                              return;
                        }
                  }
            }
            break;
      }

      UNWRAP(dd, d, handleEvent);
      (*d->handleEvent) (d, event);
      WRAP(dd, d, handleEvent, decorHandleEvent);

      switch (event->type)
      {
      case PropertyNotify:
            if (event->xproperty.atom == d->winActiveAtom)
            {
                  if (d->activeWindow != activeWindow)
                  {
                        w = findWindowAtDisplay(d, activeWindow);
                        if (w)
                              decorWindowUpdate(w, FALSE);

                        w = findWindowAtDisplay(d, d->activeWindow);
                        if (w)
                              decorWindowUpdate(w, FALSE);
                  }
            }
            else if (event->xproperty.atom == dd->winDecorAtom)
            {
                  w = findWindowAtDisplay(d, event->xproperty.window);
                  if (w)
                  {
                        decorWindowUpdateDecoration(w);
                        decorWindowUpdate(w, TRUE);
                  }
            }
            else if (event->xproperty.atom == d->mwmHintsAtom)
            {
                  w = findWindowAtDisplay(d, event->xproperty.window);
                  if (w)
                        decorWindowUpdate(w, TRUE);
            }
            else
            {
                  CompScreen *s;

                  s = findScreenAtDisplay(d, event->xproperty.window);
                  if (s)
                  {
                        if (event->xproperty.atom == dd->supportingDmCheckAtom)
                        {
                              decorCheckForDmOnScreen(s, TRUE);
                        }
                        else
                        {
                              int i;

                              for (i = 0; i < DECOR_NUM; i++)
                              {
                                    if (event->xproperty.atom == dd->decorAtom[i])
                                    {
                                          DECOR_SCREEN(s);

                                          if (ds->decor[i])
                                                decorReleaseDecoration(s, ds->decor[i]);

                                          ds->decor[i] =
                                                      decorCreateDecoration
                                                      (s, s->root, dd->decorAtom[i]);

                                          for (w = s->windows; w; w = w->next)
                                                decorWindowUpdate(w, TRUE);
                                    }
                              }
                        }
                  }
            }
            break;
      case MapRequest:
            w = findWindowAtDisplay(d, event->xmaprequest.window);
            if (w)
                  decorWindowUpdate(w, TRUE);
            break;
      default:
            if (d->shapeExtension && event->type == d->shapeEvent + ShapeNotify)
            {
                  w = findWindowAtDisplay(d, ((XShapeEvent *) event)->window);
                  if (w)
                        decorWindowUpdate(w, TRUE);
            }
            break;
      }
}

static Bool decorDamageWindowRect(CompWindow * w, Bool initial, BoxPtr rect)
{
      Bool status;

      DECOR_SCREEN(w->screen);

      if (initial)
            decorWindowUpdate(w, FALSE);

      UNWRAP(ds, w->screen, damageWindowRect);
      status = (*w->screen->damageWindowRect) (w, initial, rect);
      WRAP(ds, w->screen, damageWindowRect, decorDamageWindowRect);

      return status;
}

static void
decorGetOutputExtentsForWindow(CompWindow * w, CompWindowExtents * output)
{
      DECOR_SCREEN(w->screen);
      DECOR_WINDOW(w);

      if (dw->needsUpdate)
            decorWindowUpdate(w, FALSE);

      UNWRAP(ds, w->screen, getOutputExtentsForWindow);
      (*w->screen->getOutputExtentsForWindow) (w, output);
      WRAP(ds, w->screen, getOutputExtentsForWindow,
             decorGetOutputExtentsForWindow);

      if (dw->wd)
      {
            CompWindowExtents *e = &dw->wd->decor->output;

            if (e->left > output->left)
                  output->left = e->left;
            if (e->right > output->right)
                  output->right = e->right;
            if (e->top > output->top)
                  output->top = e->top;
            if (e->bottom > output->bottom)
                  output->bottom = e->bottom;
      }
}

static void
decorWindowMoveNotify(CompWindow * w, int dx, int dy, Bool immediate)
{
      DECOR_SCREEN(w->screen);
      DECOR_WINDOW(w);

      if (dw->wd)
      {
            WindowDecoration *wd = dw->wd;
            int i;

            for (i = 0; i < wd->nQuad; i++)
            {
                  wd->quad[i].box.x1 += dx;
                  wd->quad[i].box.y1 += dy;
                  wd->quad[i].box.x2 += dx;
                  wd->quad[i].box.y2 += dy;
            }

            setDecorationMatrices(w);
      }

      UNWRAP(ds, w->screen, windowMoveNotify);
      (*w->screen->windowMoveNotify) (w, dx, dy, immediate);
      WRAP(ds, w->screen, windowMoveNotify, decorWindowMoveNotify);
}

static void decorWindowResizeNotify(CompWindow * w, int dx, int dy,
                                                      int dwidth, int dheight, Bool preview)
{
      DECOR_SCREEN(w->screen);

      if ( !decorWindowUpdate(w, FALSE))
            updateWindowDecorationScale(w);

      UNWRAP(ds, w->screen, windowResizeNotify);
      (*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight, preview);
      WRAP(ds, w->screen, windowResizeNotify, decorWindowResizeNotify);
}

static void decorWindowStateChangeNotify(CompWindow * w)
{
      DECOR_SCREEN(w->screen);
      DECOR_WINDOW(w);

      if (dw->wd && dw->wd->decor)
      {
            Decoration *decor = dw->wd->decor;

            if ((w->state & MAXIMIZE_STATE) == MAXIMIZE_STATE)
                  setWindowFrameExtents(w, &decor->maxInput);
            else
                  setWindowFrameExtents(w, &decor->input);
      }

      UNWRAP(ds, w->screen, windowStateChangeNotify);
      (*w->screen->windowStateChangeNotify) (w);
      WRAP(ds, w->screen, windowStateChangeNotify,
             decorWindowStateChangeNotify);
}

static Bool decorInitDisplay(CompPlugin * p, CompDisplay * d)
{
      DecorDisplay *dd;

      dd = malloc(sizeof(DecorDisplay));
      if (!dd)
            return FALSE;

      dd->screenPrivateIndex = allocateScreenPrivateIndex(d);
      if (dd->screenPrivateIndex < 0)
      {
            free(dd);
            return FALSE;
      }

      dd->textures = 0;

      dd->supportingDmCheckAtom =
                  XInternAtom(d->display, "_NET_SUPPORTING_DM_CHECK", 0);
      dd->winDecorAtom = XInternAtom(d->display, "_NET_WINDOW_DECOR", 0);
      dd->decorAtom[DECOR_BARE] =
                  XInternAtom(d->display, "_NET_WINDOW_DECOR_BARE", 0);
      dd->decorAtom[DECOR_NORMAL] =
                  XInternAtom(d->display, "_NET_WINDOW_DECOR_NORMAL", 0);
      dd->decorAtom[DECOR_ACTIVE] =
                  XInternAtom(d->display, "_NET_WINDOW_DECOR_ACTIVE", 0);

      decorDisplayInitOptions(dd);

      WRAP(dd, d, handleEvent, decorHandleEvent);

      d->privates[displayPrivateIndex].ptr = dd;

      return TRUE;
}

static void decorFiniDisplay(CompPlugin * p, CompDisplay * d)
{
      DECOR_DISPLAY(d);

      freeScreenPrivateIndex(d, dd->screenPrivateIndex);

      UNWRAP(dd, d, handleEvent);

      free(dd);
}

static Bool decorInitScreen(CompPlugin * p, CompScreen * s)
{
      DecorScreen *ds;

      DECOR_DISPLAY(s->display);

      ds = malloc(sizeof(DecorScreen));
      if (!ds)
            return FALSE;

      ds->windowPrivateIndex = allocateWindowPrivateIndex(s);
      if (ds->windowPrivateIndex < 0)
      {
            free(ds);
            return FALSE;
      }

      memset(ds->decor, 0, sizeof(ds->decor));

      ds->dmWin = None;

      WRAP(ds, s, drawWindow, decorDrawWindow);
      WRAP(ds, s, damageWindowRect, decorDamageWindowRect);
      WRAP(ds, s, getOutputExtentsForWindow, decorGetOutputExtentsForWindow);
      WRAP(ds, s, windowMoveNotify, decorWindowMoveNotify);
      WRAP(ds, s, windowResizeNotify, decorWindowResizeNotify);
      WRAP(ds, s, windowStateChangeNotify, decorWindowStateChangeNotify);

      s->privates[dd->screenPrivateIndex].ptr = ds;

      decorCheckForDmOnScreen(s, FALSE);

      return TRUE;
}

static void decorFiniScreen(CompPlugin * p, CompScreen * s)
{
      int i;

      DECOR_SCREEN(s);

      for (i = 0; i < DECOR_NUM; i++)
            if (ds->decor[i])
                  decorReleaseDecoration(s, ds->decor[i]);

      UNWRAP(ds, s, drawWindow);
      UNWRAP(ds, s, damageWindowRect);
      UNWRAP(ds, s, getOutputExtentsForWindow);
      UNWRAP(ds, s, windowMoveNotify);
      UNWRAP(ds, s, windowResizeNotify);
      UNWRAP(ds, s, windowStateChangeNotify);

      free(ds);
}

static Bool decorInitWindow(CompPlugin * p, CompWindow * w)
{
      DecorWindow *dw;

      DECOR_SCREEN(w->screen);

      dw = malloc(sizeof(DecorWindow));
      if (!dw)
            return FALSE;

      dw->wd = NULL;
      dw->decor = NULL;

      w->privates[ds->windowPrivateIndex].ptr = dw;

      dw->needsUpdate = False;
      if (!w->attrib.override_redirect)
            decorWindowUpdateDecoration(w);

      if (w->shaded || w->attrib.map_state == IsViewable)
            dw->needsUpdate = True;

      return TRUE;
}

static void decorFiniWindow(CompPlugin * p, CompWindow * w)
{
      DECOR_WINDOW(w);

      if (dw->wd)
            destroyWindowDecoration(w->screen, dw->wd);

      if (dw->decor)
            decorReleaseDecoration(w->screen, dw->decor);

      

      free(dw);
}

static Bool decorInit(CompPlugin * p)
{
      displayPrivateIndex = allocateDisplayPrivateIndex();
      if (displayPrivateIndex < 0)
            return FALSE;

      return TRUE;
}

static void decorFini(CompPlugin * p)
{
      if (displayPrivateIndex >= 0)
            freeDisplayPrivateIndex(displayPrivateIndex);
}

static CompPluginVTable decorVTable = {
      "decoration",
      N_("Window Decoration"),
      N_("Window decorations"),
      decorInit,
      decorFini,
      decorInitDisplay,
      decorFiniDisplay,
      decorInitScreen,
      decorFiniScreen,
      decorInitWindow,
      decorFiniWindow,
      decorGetDisplayOptions,
      decorSetDisplayOption,
      0,                                        /* GetScreenOptions */
      0,                                        /* SetScreenOption */
      0,
      0,
      0,
      0,
      BERYL_ABI_INFO,
      "beryl-plugins",
      "effects",
      0,
      0,
      True,
};

CompPluginVTable *getCompPluginInfo(void)
{
      return &decorVTable;
}

Generated by  Doxygen 1.6.0   Back to index