All pastes #182728 Raw Edit

Stuff

public text v1 · immutable
#182728 ·published 2006-09-26 08:59 UTC
rendered paste body
/*
 * Copyright © 2006 Novell, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice
 * appear in supporting documentation, and that the name of
 * Novell, Inc. not be used in advertising or publicity pertaining to
 * distribution of the software without specific, written prior permission.
 * Novell, Inc. makes no representations about the suitability of this
 * software for any purpose. It is provided "as is" without express or
 * implied warranty.
 *
 * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
 * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Author: David Reveman <davidr@novell.com>
 */

#define _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <dirent.h>

#include <beryl.h>
#include<libintl.h>
#include<locale.h>
#define _(String) gettext (String)
#define gettext_noop(String) String
#define N_(String) gettext_noop (String)

#define SHOT_INITIATE_BUTTON_DEFAULT	       Button1
#define SHOT_INITIATE_BUTTON_MODIFIERS_DEFAULT CompSuperMask

#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;
    int grabIndex;

    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 = pointerX;
        ss->y1 = ss->y2 = pointerY;

        ss->grab = TRUE;
    }

    return FALSE;
}

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 Bool
shotPaintScreen (CompScreen * s,
                 const ScreenPaintAttrib * sAttrib,
                 Region region, unsigned int mask)
{
    Bool status;

    SHOT_SCREEN (s);

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

    if (status && ss->grab)
    {
        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);

        if (ss->grabIndex)
        {
            glPushMatrix ();
            glTranslatef (-0.5f, -0.5f, -DEFAULT_Z_CAMERA);
            glScalef (1.0f / s->width, -1.0f / s->height, 1.0f);
            glTranslatef (0.0f, -s->height, 0.0f);
            glDisableClientState (GL_TEXTURE_COORD_ARRAY);
            glEnable (GL_BLEND);
            glColor4us (0x2fff, 0x2fff, 0x4fff, 0x4fff);
            glRecti (x1, y2, x2, y1);
            glColor4us (0x2fff, 0x2fff, 0x4fff, 0x9fff);
            glBegin (GL_LINE_LOOP);
            glVertex2i (x1, y1);
            glVertex2i (x2, y1);
            glVertex2i (x2, y2);
            glVertex2i (x1, y2);
            glEnd ();
            glColor4usv (defaultColor);
            glDisable (GL_BLEND);
            glEnableClientState (GL_TEXTURE_COORD_ARRAY);
            glPopMatrix ();
        }
        else
        {
            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, versionsort);
                    if (n >= 0)
                    {
                        char *name;
                        int number = 0;

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

                        number++;

                        if (n)
                            free (namelist);

                        name = malloc (strlen (dir) + 256);
                        if (name)
                        {
                            sprintf (name, "%s/screenshot%d.png", dir,
                                     number);

                            if (!writePngToFile (buffer, name, w, h, w * 4))
                                fprintf (stderr, "%s: failed to write "
                                         "screenshot image", programName);

                            free (name);
                        }
                    }
                    else
                    {
                        perror (dir);
                    }

                    free (buffer);
                }
            }

            ss->grab = FALSE;
        }
    }

    return status;
}


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, pointerX, pointerY);
        break;
    case EnterNotify:
    case LeaveNotify:
        s = findScreenAtDisplay (d, event->xcrossing.root);
        if (s)
            shotHandleMotionEvent (s, pointerX, 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->name = "initiate";
    o->shortDesc = N_("Initiate");
    o->longDesc = N_("Initiate rectangle 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->name = "directory";
    o->shortDesc = N_("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);

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

    return TRUE;
}

static void
shotFiniScreen (CompPlugin * p, CompScreen * s)
{
    SHOT_SCREEN (s);

    UNWRAP (ss, s, paintScreen);

    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);
}

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 */
    NULL,
    0,
    BERYL_ABI_INFO
};

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