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

screenshot.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 <stdlib.h>
#include <string.h>
#include <dirent.h>

#include <beryl.h>

#define SHOT_INITIATE_BUTTON_DEFAULT             Button1
#define SHOT_INITIATE_BUTTON_MODIFIERS_DEFAULT CompSuperMask | ControlMask

#define SHOT_DIR_DEFAULT "Desktop"

static int displayPrivateIndex;

#define SHOT_DISPLAY_OPTION_INITIATE 0
#define SHOT_DISPLAY_OPTION_DIR      1
#define SHOT_DISPLAY_OPTION_NUM          2

typedef struct _ShotDisplay
{
      int screenPrivateIndex;
      HandleEventProc handleEvent;

      CompOption opt[SHOT_DISPLAY_OPTION_NUM];
} ShotDisplay;

typedef struct _ShotScreen
{
      PaintScreenProc paintScreen;
      PaintTransformedScreenProc paintTransformedScreen;
      int grabIndex;

      Bool painted;
      int x, y;

      int x1, y1, x2, y2;
      Bool grab;
} ShotScreen;

#define GET_SHOT_DISPLAY(d)                          \
    ((ShotDisplay *) (d)->privates[displayPrivateIndex].ptr)

#define SHOT_DISPLAY(d)                \
    ShotDisplay *sd = GET_SHOT_DISPLAY (d)

#define GET_SHOT_SCREEN(s, sd)                               \
    ((ShotScreen *) (s)->privates[(sd)->screenPrivateIndex].ptr)

#define SHOT_SCREEN(s)                                      \
    ShotScreen *ss = GET_SHOT_SCREEN (s, GET_SHOT_DISPLAY (s->display))

#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))


static Bool
shotInitiate(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)
      {
            SHOT_SCREEN(s);

            if (otherScreenGrabExist(s, "screenshot", 0))
                  return FALSE;

            if (!ss->grabIndex)
                  ss->grabIndex = pushScreenGrab(s, None, "screenshot");

            if (state & CompActionStateInitButton)
                  action->state |= CompActionStateTermButton;

            /* start selection screenshot rectangle */

            ss->x1 = ss->x2 = d->pointerX;
            ss->y1 = ss->y2 = d->pointerY;

            ss->grab = TRUE;
      }

      return TRUE;
}

static Bool
shotTerminate(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)
      {
            SHOT_SCREEN(s);

            if (xid && s->root != xid)
                  continue;

            if (ss->grabIndex)
            {
                  removeScreenGrab(s, ss->grabIndex, NULL);
                  ss->grabIndex = 0;

                  if (ss->x1 != ss->x2 && ss->y1 != ss->y2)
                  {
                        REGION reg;

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

                        reg.extents.x1 = MIN(ss->x1, ss->x2) - 1;
                        reg.extents.y1 = MIN(ss->y1, ss->y2) - 1;
                        reg.extents.x2 = MAX(ss->x1, ss->x2) + 1;
                        reg.extents.y2 = MAX(ss->y1, ss->y2) + 1;

                        damageScreenRegion(s, &reg);
                  }
            }
      }

      action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);

      return FALSE;
}

static int shotFilter(const struct dirent *d)
{
      int number;

      if (sscanf(d->d_name, "screenshot%d.png", &number))
            return 1;

      return 0;
}

static int shotSort(const void *_a, const void *_b)
{
      struct dirent **a = (struct dirent **)_a;
      struct dirent **b = (struct dirent **)_b;
      int al = strlen((*a)->d_name);
      int bl = strlen((*b)->d_name);

      if (al == bl)
            return strcoll((*a)->d_name, (*b)->d_name);
      else
            return al - bl;
}

static void
shotPaintOutline(CompScreen * s, const ScreenPaintAttrib * sa, 
                         int output, Bool transformed)
{
      int x1 = 0, x2 = 0, y1 = 0, y2 = 0;

      SHOT_SCREEN(s);

      x1 = MIN(ss->x1, ss->x2);
      y1 = MIN(ss->y1, ss->y2);
      x2 = MAX(ss->x1, ss->x2);
      y2 = MAX(ss->y1, ss->y2);

      glPushMatrix();

      if (transformed)
      {
            glLoadIdentity();
            (s->applyScreenTransform) (s, sa, output);
      }

      prepareXCoords(s, output,
                           transformed ? -sa->zTranslate : -DEFAULT_Z_CAMERA);
      glDisableClientState(GL_TEXTURE_COORD_ARRAY);
      glEnable(GL_BLEND);

      glColor4us(0x2fff, 0x2fff, 0x4fff, 0x4fff);
      glRecti(x1, y2, x2, y1);

      /* This section draws the outline */
      glColor4us(0x2fff, 0x2fff, 0x4fff, 0x9fff);
      glLineWidth(2.0);
      glBegin(GL_LINE_LOOP);
      glVertex2i(x1, y1);
      glVertex2i(x2, y1);
      glVertex2i(x2, y2);
      glVertex2i(x1, y2);
      glEnd();

      /* Clean up */
      glColor4usv(defaultColor);
      glDisable(GL_BLEND);
      glEnableClientState(GL_TEXTURE_COORD_ARRAY);
      glPopMatrix();
}

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

      SHOT_SCREEN(s);

      ss->painted = FALSE;
      ss->x = s->x;
      ss->y = s->y;

      UNWRAP(ss, s, paintScreen);
      status = (*s->paintScreen) (s, sAttrib, region, output, mask);
      WRAP(ss, s, paintScreen, shotPaintScreen);

      if (status && ss->grab && !ss->painted)
      {
            if (ss->grabIndex)
            {
                  shotPaintOutline(s, sAttrib, output, FALSE);
            }
            else
            {
                  int x1, x2, y1, y2;

                  x1 = MIN(ss->x1, ss->x2);
                  y1 = MIN(ss->y1, ss->y2);
                  x2 = MAX(ss->x1, ss->x2);
                  y2 = MAX(ss->y1, ss->y2);

                  int w = x2 - x1;
                  int h = y2 - y1;

                  SHOT_DISPLAY(s->display);

                  if (w && h)
                  {
                        GLubyte *buffer;
                        char *dir = sd->opt[SHOT_DISPLAY_OPTION_DIR].value.s;

                        buffer = malloc(sizeof(GLubyte) * w * h * 4);
                        if (buffer)
                        {
                              struct dirent **namelist;
                              int n;

                              glReadPixels(x1, s->height - y2, w,
                                                 h, GL_RGBA,
                                                 GL_UNSIGNED_BYTE, (GLvoid *) buffer);

                              n = scandir(dir, &namelist, shotFilter, shotSort);
                              if (n >= 0)
                              {
                                    char name[256];
                                    int number = 0;

                                    if (n > 0)
                                          sscanf(namelist
                                                   [n -
                                                      1]->d_name, "screenshot%d.png", &number);

                                    number++;

                                    if (n)
                                          free(namelist);
                                    sprintf(name, "screenshot%d.png", number);

                                    if (!writeImageToFile(s->display, dir, name, "png",
                                                                    w, h, buffer))
                                    {
                                          fprintf(stderr, "%s: failed to write "
                                                      "screenshot image", getProgramName());
                                    }
                              }
                              else
                              {
                                    perror(dir);
                              }

                              free(buffer);
                        }
                  }

                  ss->grab = FALSE;
            }
      }

      return status;
}

static void
shotPaintTransformedScreen(CompScreen * s, const ScreenPaintAttrib * sa,
                                       Region region, int output, unsigned int mask)
{
      SHOT_SCREEN(s);

      UNWRAP(ss, s, paintTransformedScreen);
      (*s->paintTransformedScreen) (s, sa, region, output, mask);
      WRAP(ss, s, paintTransformedScreen, shotPaintTransformedScreen);

      if (ss->grab && ss->grabIndex && (ss->x == s->x) && (ss->y == s->y)) {
            ss->painted = TRUE;
            shotPaintOutline(s, sa, output, TRUE);
      }
}

static void shotHandleMotionEvent(CompScreen * s, int xRoot, int yRoot)
{
      SHOT_SCREEN(s);

      /* update screenshot rectangle size */

      if (ss->grabIndex)
      {
            REGION reg;

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

            reg.extents.x1 = MIN(ss->x1, ss->x2) - 1;
            reg.extents.y1 = MIN(ss->y1, ss->y2) - 1;
            reg.extents.x2 = MAX(ss->x1, ss->x2) + 1;
            reg.extents.y2 = MAX(ss->y1, ss->y2) + 1;

            damageScreenRegion(s, &reg);

            ss->x2 = xRoot;
            ss->y2 = yRoot;

            reg.extents.x1 = MIN(ss->x1, ss->x2) - 1;
            reg.extents.y1 = MIN(ss->y1, ss->y2) - 1;
            reg.extents.x2 = MAX(ss->x1, ss->x2) + 1;
            reg.extents.y2 = MAX(ss->y1, ss->y2) + 1;

            damageScreenRegion(s, &reg);

            damageScreen(s);
      }
}

static void shotHandleEvent(CompDisplay * d, XEvent * event)
{
      CompScreen *s;

      SHOT_DISPLAY(d);

      switch (event->type)
      {
      case MotionNotify:
            s = findScreenAtDisplay(d, event->xmotion.root);
            if (s)
                  shotHandleMotionEvent(s, d->pointerX, d->pointerY);
            break;
      case EnterNotify:
      case LeaveNotify:
            s = findScreenAtDisplay(d, event->xcrossing.root);
            if (s)
                  shotHandleMotionEvent(s, d->pointerX, d->pointerY);
      default:
            break;
      }

      UNWRAP(sd, d, handleEvent);
      (*d->handleEvent) (d, event);
      WRAP(sd, d, handleEvent, shotHandleEvent);
}

static void shotDisplayInitOptions(ShotDisplay * sd)
{
      CompOption *o;

      o = &sd->opt[SHOT_DISPLAY_OPTION_INITIATE];
      o->advanced = False;
      o->name = "initiate";
      o->group = N_("Basic");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Initiate Screenshot");
      o->longDesc = N_("Initiate Rectangular Screenshot.");
      o->type = CompOptionTypeAction;
      o->value.action.initiate = shotInitiate;
      o->value.action.terminate = shotTerminate;
      o->value.action.bell = FALSE;
      o->value.action.edgeMask = 0;
      o->value.action.type = CompBindingTypeButton;
      o->value.action.state = CompActionStateInitButton;
      o->value.action.button.modifiers = SHOT_INITIATE_BUTTON_MODIFIERS_DEFAULT;
      o->value.action.button.button = SHOT_INITIATE_BUTTON_DEFAULT;

      o = &sd->opt[SHOT_DISPLAY_OPTION_DIR];
      o->advanced = False;
      o->name = "directory";
      o->group = N_("Basic");
      o->subGroup = N_("");
      o->displayHints = "file;directory;";
      o->shortDesc = N_("Saving Directory");
      o->longDesc = N_("Put Screenshot images in this Directory.");
      o->type = CompOptionTypeString;
      o->value.s = strdup(SHOT_DIR_DEFAULT);
      o->rest.s.string = 0;
      o->rest.s.nString = 0;
}

static CompOption *shotGetDisplayOptions(CompDisplay * display, int *count)
{
      if (display)
      {
            SHOT_DISPLAY(display);

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

            shotDisplayInitOptions(sd);
            *count = NUM_OPTIONS(sd);
            return sd->opt;
      }
}

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

      SHOT_DISPLAY(display);

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

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

      return FALSE;
}

static Bool shotInitDisplay(CompPlugin * p, CompDisplay * d)
{
      ShotDisplay *sd;

      sd = malloc(sizeof(ShotDisplay));
      if (!sd)
            return FALSE;

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

      WRAP(sd, d, handleEvent, shotHandleEvent);

      shotDisplayInitOptions(sd);

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

      return TRUE;
}

static void shotFiniDisplay(CompPlugin * p, CompDisplay * d)
{
      SHOT_DISPLAY(d);

      freeScreenPrivateIndex(d, sd->screenPrivateIndex);

      UNWRAP(sd, d, handleEvent);

      free(sd);
}

static Bool shotInitScreen(CompPlugin * p, CompScreen * s)
{
      ShotScreen *ss;

      SHOT_DISPLAY(s->display);

      ss = malloc(sizeof(ShotScreen));
      if (!ss)
            return FALSE;

      ss->grabIndex = 0;
      ss->grab = FALSE;

      addScreenAction(s, &sd->opt[SHOT_DISPLAY_OPTION_INITIATE].value.action);

      WRAP(ss, s, paintScreen, shotPaintScreen);
      WRAP(ss, s, paintTransformedScreen, shotPaintTransformedScreen);

      s->privates[sd->screenPrivateIndex].ptr = ss;

      return TRUE;
}

static void shotFiniScreen(CompPlugin * p, CompScreen * s)
{
      SHOT_SCREEN(s);
      SHOT_DISPLAY(s->display);

      UNWRAP(ss, s, paintScreen);
      UNWRAP(ss, s, paintTransformedScreen);

      removeScreenAction(s,
                                 &sd->opt[SHOT_DISPLAY_OPTION_INITIATE].value.action);

      free(ss);
}

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

      return TRUE;
}

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

CompPluginDep shotDeps[] = {
      {CompPluginRuleAfterCategory, "imageformat"}
      ,
};

static CompPluginVTable shotVTable = {
      "screenshot",
      N_("Screenshot"),
      N_("Screenshot plugin"),
      shotInit,
      shotFini,
      shotInitDisplay,
      shotFiniDisplay,
      shotInitScreen,
      shotFiniScreen,
      0,                                        /* InitWindow */
      0,                                        /* FiniWindow */
      shotGetDisplayOptions,
      shotSetDisplayOption,
      0,                                        /* GetScreenOptions */
      0,                                        /* SetScreenOption */
      shotDeps,
      sizeof(shotDeps) / sizeof(shotDeps[0]),
      0,
      0,
      BERYL_ABI_INFO,
      "beryl-plugins",
      "misc",
      0,
      0,
      False,
};

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

Generated by  Doxygen 1.6.0   Back to index