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

clone.c

/*
 * Copyright © 2006 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 <sys/time.h>

#include <beryl.h>

#define CLONE_INITIATE_BUTTON_DEFAULT           Button1
#define CLONE_INITIATE_BUTTON_MODIFIERS_DEFAULT (CompSuperMask | ShiftMask)

static int displayPrivateIndex;

#define CLONE_DISPLAY_OPTION_INITIATE 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;
      PaintScreenProc paintScreen;
      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)->privates[displayPrivateIndex].ptr)

#define CLONE_DISPLAY(d)                 \
    CloneDisplay *cd = GET_CLONE_DISPLAY (d)

#define GET_CLONE_SCREEN(s, cd)                                 \
    ((CloneScreen *) (s)->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;
                        cloneFinish(s);
                  }
            }

            damageScreen(s);
      }

      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,
                                                getEmptyRegion(), cs->clone[i].region);
                        XUnionRegion(s->damage, cs->clone[i].region, s->damage);
                        XSubtractRegion(&src->region,
                                                getEmptyRegion(), 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, getEmptyRegion(),
                                          cs->clone[i].region);
            }
      }
}

static void cloneDonePaintScreen(CompScreen * s)
{
      CLONE_SCREEN(s);

      if (cs->grab && cs->offset)
            damageScreen(s);

      UNWRAP(cs, s, donePaintScreen);
      (*s->donePaintScreen) (s);
      WRAP(cs, s, donePaintScreen, cloneDonePaintScreen);
}

static Bool
clonePaintScreen(CompScreen * s,
                         const ScreenPaintAttrib * sAttrib,
                         Region region, int output, unsigned int mask)
{
      Bool status;
      int i, dst = output;

      CLONE_SCREEN(s);

      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, paintScreen);
      status = (*s->paintScreen) (s, sAttrib, region, dst, mask);
      WRAP(cs, s, paintScreen, clonePaintScreen);

      if (cs->grab)
      {
            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;

            glPushMatrix();

            glTranslatef(-0.5f, -0.5f, -DEFAULT_Z_CAMERA);
            glScalef(1.0f / s->outputDev[output].width,
                         -1.0f / s->outputDev[output].height, 1.0f);
            glTranslatef(dx - s->outputDev[output].region.extents.x1,
                               dy - s->outputDev[output].region.extents.y2, 0.0f);
            glScalef(zoomX, zoomY, 1.0f);

            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,
                                             &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,
                         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, 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", 0))
                  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, d->pointerX, d->pointerY);
            break;
      case EnterNotify:
      case LeaveNotify:
            s = findScreenAtDisplay(d, event->xcrossing.root);
            if (s)
                  cloneHandleMotionEvent(s, d->pointerX, d->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 void cloneDisplayInitOptions(CloneDisplay * cd)
{
      CompOption *o;

      o = &cd->opt[CLONE_DISPLAY_OPTION_INITIATE];
      o->advanced = False;
      o->name = "initiate";
      o->group = N_("Misc. settings");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Initiate Clone Selection");
      o->longDesc = N_("Initiate Clone selection.");
      o->type = CompOptionTypeAction;
      o->value.action.initiate = cloneInitiate;
      o->value.action.terminate = cloneTerminate;
      o->value.action.bell = FALSE;
      o->value.action.edgeMask = 0;
      o->value.action.state = CompActionStateInitButton;
      o->value.action.type = CompBindingTypeButton;
      o->value.action.button.modifiers =
                  CLONE_INITIATE_BUTTON_MODIFIERS_DEFAULT;
      o->value.action.button.button = CLONE_INITIATE_BUTTON_DEFAULT;
}

static CompOption *cloneGetDisplayOptions(CompDisplay * display, int *count)
{
      if (display)
      {
            CLONE_DISPLAY(display);

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

            cloneDisplayInitOptions(cd);
            *count = NUM_OPTIONS(cd);
            return cd->opt;
      }
}

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

      CLONE_DISPLAY(display);

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

      switch (index)
      {
      case CLONE_DISPLAY_OPTION_INITIATE:
            if (setDisplayAction(display, o, value))
                  return TRUE;
      default:
            break;
      }

      return FALSE;
}

static Bool cloneInitDisplay(CompPlugin * p, CompDisplay * d)
{
      CloneDisplay *cd;

      cd = malloc(sizeof(CloneDisplay));
      if (!cd)
            return FALSE;

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

      cloneDisplayInitOptions(cd);

      WRAP(cd, d, handleEvent, cloneHandleEvent);

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

      return TRUE;
}

static void cloneFiniDisplay(CompPlugin * p, CompDisplay * d)
{
      CLONE_DISPLAY(d);

      freeScreenPrivateIndex(d, cd->screenPrivateIndex);

      UNWRAP(cd, d, handleEvent);

      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;

      addScreenAction(s, &cd->opt[CLONE_DISPLAY_OPTION_INITIATE].value.action);

      WRAP(cs, s, preparePaintScreen, clonePreparePaintScreen);
      WRAP(cs, s, donePaintScreen, cloneDonePaintScreen);
      WRAP(cs, s, paintScreen, clonePaintScreen);
      WRAP(cs, s, paintWindow, clonePaintWindow);
      WRAP(cs, s, outputChangeNotify, cloneOutputChangeNotify);

      s->privates[cd->screenPrivateIndex].ptr = cs;

      return TRUE;
}

static void cloneFiniScreen(CompPlugin * p, CompScreen * s)
{
      CLONE_SCREEN(s);


      UNWRAP(cs, s, preparePaintScreen);
      UNWRAP(cs, s, donePaintScreen);
      UNWRAP(cs, s, paintScreen);
      UNWRAP(cs, s, paintWindow);
      UNWRAP(cs, s, outputChangeNotify);

      free(cs);
}

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

      return TRUE;
}

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

CompPluginVTable cloneVTable = {
      "clone",
      N_("Clone Output"),
      N_("Output clone handler"),
      cloneInit,
      cloneFini,
      cloneInitDisplay,
      cloneFiniDisplay,
      cloneInitScreen,
      cloneFiniScreen,
      0,                                        /* InitWindow */
      0,                                        /* FiniWindow */
      cloneGetDisplayOptions,
      cloneSetDisplayOption,
      0,                                        /* GetScreenOptions */
      0,                                        /* SetScreenOption */
      0,                                        /* Deps */
      0,                                        /* nDeps */
      0,                                        /* Features */
      0,                                        /* nFeatures */
      BERYL_ABI_INFO,
      "beryl-plugins",
      "desktop",
      0,
      0,
      False,
};

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

Generated by  Doxygen 1.6.0   Back to index