/*
 *  Window Maker window manager
 *
 *  Copyright (c) 1997-2003 Alfredo K. Kojima
 *
 *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
 *  USA.
 */

#include "wconfig.h"

#include <X11/Xlib.h>
#include <X11/Xutil.h>

#ifdef TEXTURE_PLUGIN
# ifdef HAVE_DLFCN_H
#  include <dlfcn.h>
# endif
#endif

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

#include <wraster.h>

#include "WindowMaker.h"
#include "wcore.h"
#include "texture.h"
#include "funcs.h"

extern WPreferences wPreferences;


static void bevelImage(RImage *image, int relief);



WTexSolid*
wTextureMakeSolid(WScreen *scr, XColor *color)
{
    WTexSolid *texture;
    int gcm;
    XGCValues gcv;

    texture = wmalloc(sizeof(WTexture));

    texture->type = WTEX_SOLID;
    texture->subtype = 0;

    XAllocColor(dpy, scr->w_colormap, color);
    texture->normal = *color;
    if (color->red==0 && color->blue==0 && color->green == 0) {
        texture->light.red = 0xb6da;
        texture->light.green = 0xb6da;
        texture->light.blue = 0xb6da;
        texture->dim.red = 0x6185;
        texture->dim.green = 0x6185;
        texture->dim.blue = 0x6185;
    } else {
        RColor rgb;
        RHSVColor hsv, hsv2;
        int v;

        rgb.red = color->red >> 8;
        rgb.green = color->green >> 8;
        rgb.blue = color->blue >> 8;
        RRGBtoHSV(&rgb, &hsv);
        RHSVtoRGB(&hsv, &rgb);
        hsv2 = hsv;

        v = hsv.value*16/10;
        hsv.value = (v > 255 ? 255 : v);
        RHSVtoRGB(&hsv, &rgb);
        texture->light.red = rgb.red << 8;
        texture->light.green = rgb.green << 8;
        texture->light.blue = rgb.blue << 8;

        hsv2.value = hsv2.value/2;
        RHSVtoRGB(&hsv2, &rgb);
        texture->dim.red = rgb.red << 8;
        texture->dim.green = rgb.green << 8;
        texture->dim.blue = rgb.blue << 8;
    }
    texture->dark.red = 0;
    texture->dark.green = 0;
    texture->dark.blue = 0;
    XAllocColor(dpy, scr->w_colormap, &texture->light);
    XAllocColor(dpy, scr->w_colormap, &texture->dim);
    XAllocColor(dpy, scr->w_colormap, &texture->dark);

    gcm = GCForeground|GCBackground|GCGraphicsExposures;
    gcv.graphics_exposures = False;

    gcv.background = gcv.foreground = texture->light.pixel;
    texture->light_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);

    gcv.background = gcv.foreground = texture->dim.pixel;
    texture->dim_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);

    gcv.background = gcv.foreground = texture->dark.pixel;
    texture->dark_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);

    gcv.background = gcv.foreground = color->pixel;
    texture->normal_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);

    return texture;
}


static int
dummyErrorHandler(Display *foo, XErrorEvent *bar)
{
#ifdef DEBUG
    wwarning("your server is buggy. Tell the author if some error related to color occurs");
#endif
    return 0;
}


void
wTextureDestroy(WScreen *scr, WTexture *texture)
{
    int i;
    int count=0;
    unsigned long colors[8];

#ifdef DEBUG
    if (texture==NULL) {
        printf("BUG: trying to free NULL texture\n");
        return;
    }
#endif

    /*
     * some stupid servers don't like white or black being freed...
     */
#define CANFREE(c) (c!=scr->black_pixel && c!=scr->white_pixel && c!=0)
    switch (texture->any.type) {
    case WTEX_SOLID:
        XFreeGC(dpy, texture->solid.light_gc);
        XFreeGC(dpy, texture->solid.dark_gc);
        XFreeGC(dpy, texture->solid.dim_gc);
        if (CANFREE(texture->solid.light.pixel))
            colors[count++] = texture->solid.light.pixel;
        if (CANFREE(texture->solid.dim.pixel))
            colors[count++] = texture->solid.dim.pixel;
        if (CANFREE(texture->solid.dark.pixel))
            colors[count++] = texture->solid.dark.pixel;
        break;

    case WTEX_PIXMAP:
        RReleaseImage(texture->pixmap.pixmap);
        break;

    case WTEX_MHGRADIENT:
    case WTEX_MVGRADIENT:
    case WTEX_MDGRADIENT:
        for (i=0; texture->mgradient.colors[i]!=NULL; i++) {
            wfree(texture->mgradient.colors[i]);
        }
        wfree(texture->mgradient.colors);
        break;

    case WTEX_THGRADIENT:
    case WTEX_TVGRADIENT:
    case WTEX_TDGRADIENT:
        RReleaseImage(texture->tgradient.pixmap);
        break;

#ifdef TEXTURE_PLUGIN
    case WTEX_FUNCTION:
#ifdef HAVE_DLFCN_H
        if (texture->function.handle) {
            dlclose(texture->function.handle);
        }
#endif
        for (i = 0; i < texture->function.argc; i++) {
            wfree(texture->function.argv[i]);
        }
        wfree(texture->function.argv);
        break;
#endif /* TEXTURE_PLUGIN */
    }
    if (CANFREE(texture->any.color.pixel))
        colors[count++] = texture->any.color.pixel;
    if (count > 0) {
        XErrorHandler oldhandler;

        /* ignore error from buggy servers that don't know how
         * to do reference counting for colors. */
        XSync(dpy,0);
        oldhandler = XSetErrorHandler(dummyErrorHandler);
        XFreeColors(dpy, scr->w_colormap, colors, count, 0);
        XSync(dpy,0);
        XSetErrorHandler(oldhandler);
    }
    XFreeGC(dpy, texture->any.gc);
    wfree(texture);
#undef CANFREE
}



WTexGradient*
wTextureMakeGradient(WScreen *scr, int style, RColor *from, RColor *to)
{
    WTexGradient *texture;
    XGCValues gcv;


    texture = wmalloc(sizeof(WTexture));
    memset(texture, 0, sizeof(WTexture));
    texture->type = style;
    texture->subtype = 0;

    texture->color1 = *from;
    texture->color2 = *to;

    texture->normal.red = (from->red + to->red)<<7;
    texture->normal.green = (from->green + to->green)<<7;
    texture->normal.blue = (from->blue + to->blue)<<7;

    XAllocColor(dpy, scr->w_colormap, &texture->normal);
    gcv.background = gcv.foreground = texture->normal.pixel;
    gcv.graphics_exposures = False;
    texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground|GCBackground
                                   |GCGraphicsExposures, &gcv);

    return texture;
}




WTexIGradient*
wTextureMakeIGradient(WScreen *scr, int thickness1, RColor colors1[2],
                      int thickness2, RColor colors2[2])
{
    WTexIGradient *texture;
    XGCValues gcv;
    int i;


    texture = wmalloc(sizeof(WTexture));
    memset(texture, 0, sizeof(WTexture));
    texture->type = WTEX_IGRADIENT;
    for (i = 0; i < 2; i++) {
        texture->colors1[i] = colors1[i];
        texture->colors2[i] = colors2[i];
    }
    texture->thickness1 = thickness1;
    texture->thickness2 = thickness2;
    if (thickness1 >= thickness2) {
        texture->normal.red = (colors1[0].red + colors1[1].red)<<7;
        texture->normal.green = (colors1[0].green + colors1[1].green)<<7;
        texture->normal.blue = (colors1[0].blue + colors1[1].blue)<<7;
    } else {
        texture->normal.red = (colors2[0].red + colors2[1].red)<<7;
        texture->normal.green = (colors2[0].green + colors2[1].green)<<7;
        texture->normal.blue = (colors2[0].blue + colors2[1].blue)<<7;
    }
    XAllocColor(dpy, scr->w_colormap, &texture->normal);
    gcv.background = gcv.foreground = texture->normal.pixel;
    gcv.graphics_exposures = False;
    texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground|GCBackground
                                   |GCGraphicsExposures, &gcv);

    return texture;
}



WTexMGradient*
wTextureMakeMGradient(WScreen *scr, int style, RColor **colors)
{
    WTexMGradient *texture;
    XGCValues gcv;
    int i;


    texture = wmalloc(sizeof(WTexture));
    memset(texture, 0, sizeof(WTexture));
    texture->type = style;
    texture->subtype = 0;

    i=0;
    while (colors[i]!=NULL) i++;
    i--;
    texture->normal.red = (colors[0]->red<<8);
    texture->normal.green = (colors[0]->green<<8);
    texture->normal.blue =  (colors[0]->blue<<8);

    texture->colors = colors;

    XAllocColor(dpy, scr->w_colormap, &texture->normal);
    gcv.background = gcv.foreground = texture->normal.pixel;
    gcv.graphics_exposures = False;
    texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground|GCBackground
                                   |GCGraphicsExposures, &gcv);

    return texture;
}



WTexPixmap*
wTextureMakePixmap(WScreen *scr, int style, char *pixmap_file, XColor *color)
{
    WTexPixmap *texture;
    XGCValues gcv;
    RImage *image;
    char *file;

    file = FindImage(wPreferences.pixmap_path, pixmap_file);
    if (!file) {
        wwarning(_("image file \"%s\" used as texture could not be found."),
                 pixmap_file);
        return NULL;
    }
    image = RLoadImage(scr->rcontext, file, 0);
    if (!image) {
        wwarning(_("could not load texture pixmap \"%s\":%s"), file,
                 RMessageForError(RErrorCode));
        wfree(file);
        return NULL;
    }
    wfree(file);

    texture = wmalloc(sizeof(WTexture));
    memset(texture, 0, sizeof(WTexture));
    texture->type = WTEX_PIXMAP;
    texture->subtype = style;

    texture->normal = *color;

    XAllocColor(dpy, scr->w_colormap, &texture->normal);
    gcv.background = gcv.foreground = texture->normal.pixel;
    gcv.graphics_exposures = False;
    texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground|GCBackground
                                   |GCGraphicsExposures, &gcv);

    texture->pixmap = image;

    return texture;
}

WTexTGradient*
wTextureMakeTGradient(WScreen *scr, int style, RColor *from, RColor *to,
                      char *pixmap_file, int opacity)
{
    WTexTGradient *texture;
    XGCValues gcv;
    RImage *image;
    char *file;

    file = FindImage(wPreferences.pixmap_path, pixmap_file);
    if (!file) {
        wwarning(_("image file \"%s\" used as texture could not be found."),
                 pixmap_file);
        return NULL;
    }
    image = RLoadImage(scr->rcontext, file, 0);
    if (!image) {
        wwarning(_("could not load texture pixmap \"%s\":%s"), file,
                 RMessageForError(RErrorCode));
        wfree(file);
        return NULL;
    }
    wfree(file);

    texture = wmalloc(sizeof(WTexture));
    memset(texture, 0, sizeof(WTexture));
    texture->type = style;

    texture->opacity = opacity;

    texture->color1 = *from;
    texture->color2 = *to;

    texture->normal.red = (from->red + to->red)<<7;
    texture->normal.green = (from->green + to->green)<<7;
    texture->normal.blue = (from->blue + to->blue)<<7;

    XAllocColor(dpy, scr->w_colormap, &texture->normal);
    gcv.background = gcv.foreground = texture->normal.pixel;
    gcv.graphics_exposures = False;
    texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground|GCBackground
                                   |GCGraphicsExposures, &gcv);

    texture->pixmap = image;

    return texture;
}


#ifdef TEXTURE_PLUGIN
WTexFunction*
wTextureMakeFunction(WScreen *scr, char *lib, char *func, int argc, char **argv)
{
    XColor fallbackColor;
    XGCValues gcv;
    WTexFunction *texture;

    texture = wmalloc(sizeof(WTexture));
    texture->type = WTEX_FUNCTION;
    texture->handle = NULL;
    texture->render = 0;
    texture->argc = argc;
    texture->argv = argv;

    fallbackColor.red = 0x8000;
    fallbackColor.green = 0x8000;
    fallbackColor.blue = 0x8000;

    gcv.background = gcv.foreground = fallbackColor.pixel;
    gcv.graphics_exposures = False;
    texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground|GCBackground
                                   |GCGraphicsExposures, &gcv);

# ifdef HAVE_DLFCN_H
    /* open the library */
    texture->handle = dlopen(lib, RTLD_LAZY);
    if (!texture->handle) {
        wwarning(_("library \"%s\" cound not be opened."), lib);
        wfree(argv);
        wfree(texture);
        return NULL;
    }

    /* find the function */
    texture->render = dlsym(texture->handle, func);
    if (!texture->render) {
        wwarning(_("function \"%s\" not found in library \"%s\""), func, lib);
        wfree(argv);
        dlclose(texture->handle);
        wfree(texture);
        return NULL;
    }
# else
    wwarning(_("function textures not supported on this system, sorry."));
# endif

    /* success! */
    return texture;
}
#endif /* TEXTURE_PLUGIN */


RImage*
wTextureRenderImage(WTexture *texture, int width, int height,
                    int relief)
{
    RImage *image = NULL;
    RColor color1;
    int d;
    int subtype;

    switch (texture->any.type) {
    case WTEX_SOLID:
        image = RCreateImage(width, height, False);

        color1.red = texture->solid.normal.red >> 8;
        color1.green = texture->solid.normal.green >> 8;
        color1.blue = texture->solid.normal.blue >> 8;
        color1.alpha = 255;

        RClearImage(image, &color1);
        break;

    case WTEX_PIXMAP:
        if (texture->pixmap.subtype == WTP_TILE) {
            image = RMakeTiledImage(texture->pixmap.pixmap, width, height);
        } else if (texture->pixmap.subtype == WTP_CENTER) {
            color1.red = texture->pixmap.normal.red>>8;
            color1.green = texture->pixmap.normal.green>>8;
            color1.blue = texture->pixmap.normal.blue>>8;
            color1.alpha = 255;
            image = RMakeCenteredImage(texture->pixmap.pixmap, width, height,
                                       &color1);
        } else {
            image = RScaleImage(texture->pixmap.pixmap, width, height);
        }
        break;

    case WTEX_IGRADIENT:
        image = RRenderInterwovenGradient(width, height,
                                          texture->igradient.colors1,
                                          texture->igradient.thickness1,
                                          texture->igradient.colors2,
                                          texture->igradient.thickness2);
        break;

    case WTEX_HGRADIENT:
        subtype = RGRD_HORIZONTAL;
        goto render_gradient;

    case WTEX_VGRADIENT:
        subtype = RGRD_VERTICAL;
        goto render_gradient;

    case WTEX_DGRADIENT:
        subtype = RGRD_DIAGONAL;
    render_gradient:

        image = RRenderGradient(width, height, &texture->gradient.color1,
                                &texture->gradient.color2, subtype);
        break;

    case WTEX_MHGRADIENT:
        subtype = RGRD_HORIZONTAL;
        goto render_mgradient;

    case WTEX_MVGRADIENT:
        subtype = RGRD_VERTICAL;
        goto render_mgradient;

    case WTEX_MDGRADIENT:
        subtype = RGRD_DIAGONAL;
    render_mgradient:
        image = RRenderMultiGradient(width, height,
                                     &(texture->mgradient.colors[1]),
                                     subtype);
        break;

    case WTEX_THGRADIENT:
        subtype = RGRD_HORIZONTAL;
        goto render_tgradient;

    case WTEX_TVGRADIENT:
        subtype = RGRD_VERTICAL;
        goto render_tgradient;

    case WTEX_TDGRADIENT:
        subtype = RGRD_DIAGONAL;
    render_tgradient:
        {
            RImage *grad;

            image = RMakeTiledImage(texture->tgradient.pixmap, width, height);
            if (!image)
                break;

            grad = RRenderGradient(width, height, &texture->tgradient.color1,
                                   &texture->tgradient.color2, subtype);
            if (!grad) {
                RReleaseImage(image);
                image = NULL;
                break;
            }

            RCombineImagesWithOpaqueness(image, grad,
                                         texture->tgradient.opacity);
            RReleaseImage(grad);
        }
        break;

#ifdef TEXTURE_PLUGIN
    case WTEX_FUNCTION:
#ifdef HAVE_DLFCN_H
        if (texture->function.render) {
            image = texture->function.render (
                                              texture->function.argc, texture->function.argv,
                                              width, height, relief);
        }
#endif
        if (!image) {
            RErrorCode = RERR_INTERNAL;
        }
        break;
#endif /* TEXTURE_PLUGIN */

    default:
        puts("ERROR in wTextureRenderImage()");
        image = NULL;
        break;
    }

    if (!image) {
        RColor gray;

        wwarning(_("could not render texture: %s"), RMessageForError(RErrorCode));

        image = RCreateImage(width, height, False);
        if (image == NULL) {
            wwarning(_("could not allocate image buffer"));
            return NULL;
        }

        gray.red = 190;
        gray.green = 190;
        gray.blue = 190;
        gray.alpha = 255;
        RClearImage(image, &gray);
    }


    /* render bevel */

    switch (relief) {
    case WREL_ICON:
        d = RBEV_RAISED3;
        break;

    case WREL_RAISED:
        d = RBEV_RAISED2;
        break;

    case WREL_SUNKEN:
        d = RBEV_SUNKEN;
        break;

    case WREL_FLAT:
        d = 0;
        break;

    case WREL_MENUENTRY:
        d = -WREL_MENUENTRY;
        break;

    default:
        d = 0;
    }

    if (d > 0) {
        RBevelImage(image, d);
    } else if (d < 0) {
        bevelImage(image, -d);
    }

    return image;
}



static void
bevelImage(RImage *image, int relief)
{
    int width = image->width;
    int height = image->height;
    RColor color;

    switch (relief) {
    case WREL_MENUENTRY:
        color.red = color.green = color.blue = 80;
        color.alpha = 0;
        /**/
        ROperateLine(image, RAddOperation, 1, 0, width-2, 0, &color);
        /**/

        ROperateLine(image, RAddOperation, 0, 0, 0, height-1, &color);

        color.red = color.green = color.blue = 40;
        color.alpha = 0;
        ROperateLine(image, RSubtractOperation, width-1, 0, width-1,
                     height-1, &color);

        /**/
        ROperateLine(image, RSubtractOperation, 1, height-2, width-2,
                     height-2, &color);

        color.red = color.green = color.blue = 0;
        color.alpha = 255;
        RDrawLine(image, 0, height-1, width-1, height-1, &color);
        /**/
        break;

    }
}



void
wDrawBevel(Drawable d, unsigned width, unsigned height,
           WTexSolid *texture, int relief)
{
    GC light, dim, dark;
    XSegment segs[4];

    if (relief==WREL_FLAT) return;

    light = texture->light_gc;
    dim = texture->dim_gc;
    dark = texture->dark_gc;
    switch (relief) {
    case WREL_FLAT:
        return;
    case WREL_MENUENTRY:
    case WREL_RAISED:
    case WREL_ICON:
        segs[0].x1 = 1;
        segs[0].x2 = width - 2;
        segs[0].y2 = segs[0].y1 = height - 2;
        segs[1].x1 = width - 2;
        segs[1].y1 = 1;
        segs[1].x2 = width - 2;
        segs[1].y2 = height - 2;
        XDrawSegments(dpy, d, dim, segs, 2);
        segs[0].x1 = 0;
        segs[0].x2 = width - 1;
        segs[0].y2 = segs[0].y1 = height - 1;
        segs[1].x1 = segs[1].x2 = width - 1;
        segs[1].y1 = 0;
        segs[1].y2 = height - 1;
        XDrawSegments(dpy, d, dark, segs, 2);
        segs[0].x1 = segs[0].y1 = segs[0].y2 = 0;
        segs[0].x2 = width - 2;
        segs[1].x1 = segs[1].y1 = 0;
        segs[1].x2 = 0;
        segs[1].y2 = height - 2;
        XDrawSegments(dpy, d, light, segs, 2);
        if (relief==WREL_ICON) {
            segs[0].x1 = segs[0].y1 = segs[0].y2 = 1;
            segs[0].x2 = width - 2;
            segs[1].x1 = segs[1].y1 = 1;
            segs[1].x2 = 1;
            segs[1].y2 = height - 2;
            XDrawSegments(dpy, d, light, segs, 2);
        }
        break;
#ifdef unused
    case WREL_SUNKEN:
        segs[0].x1 = 0;
        segs[0].x2 = width - 1;
        segs[0].y2 = segs[0].y1 = 0;
        segs[1].x1 = segs[1].x2 = 0;
        segs[1].y1 = 0;
        segs[1].y2 = height - 1;
        XDrawSegments(dpy, d, dark, segs, 2);

        segs[0].x1 = 0;
        segs[0].y1 = segs[0].y2 = height - 1;
        segs[0].x2 = width - 1;
        segs[1].x2 = segs[1].x1 = width - 1;
        segs[1].y1 = 1;
        segs[1].y2 = height - 1;
        XDrawSegments(dpy, d, light, segs, 2);
        break;
#endif
    }
}



syntax highlighted by Code2HTML, v. 0.9.1