/*--------------------------------*-C-*---------------------------------*
 * File:	pixmap.c 
 *----------------------------------------------------------------------*
 * Copyright (c) 1999 Ethan Fischer <allanon@crystaltokyo.com>
 * Copyright (c) 1999 Sasha Vasko   <sashav@sprintmail.com>
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *---------------------------------------------------------------------*/
/*---------------------------------------------------------------------*
 * Originally written:
 *    1999	Sasha Vasko <sashav@sprintmail.com>
 *----------------------------------------------------------------------*/

#include <stdio.h>

#include <X11/Intrinsic.h>
#include <X11/Xlibint.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>

#include "../configure.h"
#include "../include/aftersteplib.h"
#include "../include/afterstep.h"
#include "../include/style.h"
#include "../include/screen.h"
#include "../include/XImage_utils.h"

#define NO_PIXMAP_PROTOS
#include "../include/pixmap.h"

/*#define DO_CLOCKING       */
/*#define DEBUG_IMAGING     */

#ifdef DO_CLOCKING      
 #include <time.h>
#endif


/* Xdisplay, Xroot needs to be defined if code is moved to some other prog */
#define Xdisplay dpy
#define Xroot    RootWindow(dpy, screen)

#define CREATE_TRG_PIXMAP(w,h) XCreatePixmap(Xdisplay, Xroot, w, h, DefaultDepth(dpy, screen))

void CopyAndShadeArea( Drawable src, Pixmap trg, 
                       int x, int y, int w, int h, 
		       int trg_x, int trg_y, 
		       GC gc, ShadingInfo* shading )
{
    if( shading )
    {
      XImage* img ;
        if( x <0 || y <0  ) return ;
        if((img = XGetImage (Xdisplay, src, x, y, w, h, AllPlanes, ZPixmap))!= NULL )
        {
    	    ShadeXImage( img, shading, gc );
    	    XPutImage(Xdisplay, trg, gc, img, 0, 0, trg_x, trg_y, w, h);
	    XDestroyImage( img );
	    return ;
	}
    }
    XCopyArea (Xdisplay, src, trg, gc, x, y, w, h, trg_x, trg_y);			
}    

void ShadeTiledPixmap(Pixmap src, Pixmap trg, int src_w, int src_h, int x, int y, int w, int h, GC gc, ShadingInfo* shading)
{
  int tile_x, tile_y, left_w, bott_h;
    
    tile_x = x%src_w ;
    tile_y = y%src_h ;
    left_w = min(src_w-tile_x,w);
    bott_h = min(src_h-tile_y,h);    
/*fprintf( stderr, "\nShadeTiledPixmap(): tile_x = %d, tile_y = %d, left_w = %d, bott_h = %d, SRC = %dx%d TRG=%dx%d", tile_x, tile_y, left_w, bott_h, src_w, src_h, w, h);*/
    CopyAndShadeArea( src, trg, tile_x, tile_y, left_w, bott_h, 0, 0, gc, shading );
    if( bott_h < h )
    {  /* right-top parts */
	CopyAndShadeArea( src, trg, tile_x, 0, left_w, h-bott_h, 0, bott_h, gc, shading );
    }
    if( left_w < w )
    {  /* left-bott parts */
	CopyAndShadeArea( src, trg, 0, tile_y, w-left_w, bott_h, left_w, 0, gc, shading );
        if( bott_h < h  )      /* left-top parts */
	    CopyAndShadeArea( src, trg, 0, 0, w-left_w, h-bott_h, left_w, bott_h, gc, shading );
    }
}

/****************************************************************************
 *
 * fill part of a pixmap with the root pixmap, offset properly to look 
 * "transparent"
 *
 ***************************************************************************/
int FillPixmapWithTile(Pixmap pixmap, Pixmap tile, int x, int y, int width, int height, int tile_x, int tile_y)
{
    if (tile != None && pixmap != None )
    {
      GC gc;
      XGCValues gcv;
      
	gcv.tile = tile;
        gcv.fill_style = FillTiled;
        gcv.ts_x_origin = -tile_x;
        gcv.ts_y_origin = -tile_y;
        gc = XCreateGC (dpy, tile, GCFillStyle | GCTile | GCTileStipXOrigin | GCTileStipYOrigin, &gcv);
        XFillRectangle (dpy, pixmap, gc, x, y, width, height);
        XFreeGC (dpy, gc);
	return 1 ;
    }
    return 0 ;
}


Pixmap 
ShadePixmap (Pixmap src, int x, int y, int width, int height, GC gc, ShadingInfo* shading )
{
  Pixmap trg = CREATE_TRG_PIXMAP(width, height);
    if( trg != None )
    {
	CopyAndShadeArea( src, trg, x, y, width, height, 0, 0, gc, shading );
    }
    return trg ;
}
int MyXDestroyImage (XImage* ximage);

Pixmap 
ScalePixmap (Pixmap src, int src_w, int src_h, int width, int height, GC gc, ShadingInfo*  shading )
{
  XImage *srcImage, *trgImage;
  Pixmap trg = None;

    if (src != None)
    {
	if ((srcImage = XGetImage (Xdisplay, src, 0, 0, src_w, src_h, AllPlanes, ZPixmap)) != NULL)
	{
	    if ((trgImage = ScaleXImageToSize (srcImage, (Dimension) width, (Dimension) height)) != NULL)
	    {
		if ((trg = CREATE_TRG_PIXMAP(width, height)) != 0)
		{
		    if( shading )     ShadeXImage( trgImage, shading, gc );
		    XPutImage (Xdisplay, trg, gc, trgImage, 0, 0, 0, 0, width, height);
		}
		XDestroyImage (trgImage);
	    }
	    XDestroyImage (srcImage);
	}
    }
    return trg;
}

Pixmap 
CenterPixmap( Pixmap src, int src_w, int src_h, int width, int height, GC gc, ShadingInfo*  shading)
{
  int x, y, w, h, src_x = 0, src_y = 0 ;
  Pixmap trg ;
    /* create target pixmap of the size of the window */
    trg = CREATE_TRG_PIXMAP(width, height);
    if( trg != None )
    {
	/* fill it with background color */
        XFillRectangle(Xdisplay, trg, gc, 0, 0, width, height);
	/* place image at the center of it */
        x = (width-src_w)>>1 ;		    
	y = (height-src_h)>>1 ;
        if( x < 0 ) 
	{ 
	    src_x -= x ; 
	    w = min(width,src_w+x) ; 
	    x = 0 ; 
	}else w = min(width, src_w) ; 
        if( y < 0 ) 
	{ 
	    src_y -= y ; 
    	    h = min(height, src_h+y);
	    y = 0 ; 
	}else h = min(height, src_h);

	CopyAndShadeArea( src, trg, src_x, src_y, w, h, x, y, gc, shading);			
    }

    return trg ;
}

Pixmap 
GrowPixmap( Pixmap src, int src_w, int src_h, int width, int height, GC gc, ShadingInfo*  shading)
{
  int w, h;
  Pixmap trg ;
    /* create target pixmap of the size of the window */
    trg = CREATE_TRG_PIXMAP(width, height);
    if( trg != None )
    {
	/* fill it with background color */
        XFillRectangle(Xdisplay, trg, gc, 0, 0, width, height);
	/* place image at the center of it */
        w = min(width, src_w) ; 
        h = min(height, src_h);

	CopyAndShadeArea( src, trg, 0, 0, w, h, 0, 0, gc, shading);			
    }

    return trg ;
}
/* PROTO */
int 
GetRootDimensions( int* width, int* height )
{
  Window root ;
  int w_x, w_y ;
  unsigned int junk ; 
    if( !XGetGeometry( Xdisplay, Xroot, &root, 
	                  &w_x, &w_y, width, height, &junk, &junk ))	
    {
	*width = 0 ;
	*height = 0 ;
    }
    return (*width>0 && *height>0)?1:0 ;
}

int 
GetWinPosition( Window w, int* x, int* y )
{
  Window root, parent, *children ;
  unsigned int nchildren ;
  static int rootWidth = 0, rootHeight = 0;
  XWindowAttributes attr ;
  int my_x, my_y ;

    XGetWindowAttributes( Xdisplay, w, &attr );
    if( attr.map_state != IsViewable ) return 0;

    if( !x ) x = &my_x ;
    if( !y ) y = &my_y ;

    *x = 0 ; 
    *y = 0 ;
    
    if( !rootWidth || !rootHeight ) 
        if( !GetRootDimensions(&rootWidth, &rootHeight)) return 0;
          
    while( XQueryTree( Xdisplay, w, &root, &parent, &children, &nchildren ) )
    {
      int w_x, w_y ;
      unsigned int w_w, w_h, border_w, w_depth ; 
	if( children ) XFree( children );
	if( !XGetGeometry( Xdisplay, w, &root, 
	                  &w_x, &w_y, &w_w, &w_h, &border_w, &w_depth ))	 
	    break ;
	(*x)+=w_x+border_w ;
	(*y)+=w_y+border_w ;	
	
	if( parent == root )
	{/* taking in to consideration virtual desktopping */
	  int bRes = 1 ;
	    if( *x<0 || *x>=rootWidth || *y<0 || *y>=rootHeight ) bRes = 0 ;
    /* don't want to return position outside the screen even if we fail */
	    while( *x < 0 ) *x += rootWidth ;
	    while( *y < 0 ) *y += rootHeight ;
	    if( *x > rootWidth )   *x %= rootWidth ;
	    if( *y > rootHeight )  *y %= rootHeight ; 
	    return bRes; 
	}
	w = parent ;
    }
    *x = 0 ;
    *y = 0 ;
    return 0 ;
}


Pixmap 
CutWinPixmap( Window win, Drawable src, int src_w, int src_h, int width, int height, GC gc, ShadingInfo*  shading)
{
  unsigned int x = 0, y = 0, w, h;
  Pixmap trg ; int bNeedToRemap = 0 ;

    if( src == None )
    {
      Window root, parent, *children ;
      unsigned int border, depth, nchildren ;
	  
    	if( !XGetGeometry(Xdisplay, win, &root, &x, &y, &w, &h, &border, &depth)) return None;
	if( !GetWinPosition(win, &x, &y)) return None ;
	w-=border*2+1;
	h-=border*2+1;
	src_w = w+x ;
	src_h = h+y ;
        /* of course we can do withou it, but to make code more generic */
        while( XQueryTree( Xdisplay, win, &src, &parent, &children, &nchildren ) )
	{
	    if( children ) XFree( children );
	    if( parent == src ) break ;
	    win = parent ;
	}
	    
	if( src == None || win == None) 
	return None ;/* it aint gonna happen, coz we always 
	                have root window but just in case */
	XUnmapWindow( Xdisplay, win );
        bNeedToRemap = 1 ;
  
    }else
    {
	/* find out our coordinates relative to the root window */
	if( !GetWinPosition( win, &x, &y ) ) return None ;
        w = width;
	h = height;

        if( x+w > src_w || y+h > src_h )
	{ /* tiled pixmap processing here */
	
	    if((trg = CREATE_TRG_PIXMAP(min(w,src_w), min(h, src_h))))
		ShadeTiledPixmap(src, trg, src_w, src_h, x, y, min(w,src_w), min(h, src_h), gc, shading); 
	    return trg ;
	}
    }

    if( x < 0 || y < 0 || w < 2 || h < 2 ) return None ;		    

    /* create target pixmap of the size of the window */
    trg = CREATE_TRG_PIXMAP(width, height);
    if( trg != None )
    {
        /* cut area */
	CopyAndShadeArea( src, trg, x, y, w, h, 0, 0, gc, shading);			

    }
    if( bNeedToRemap )
        XMapWindow( Xdisplay, win );

    return trg ;
}

/* PROTO */
Pixmap 
GetRootPixmap(Atom id)
{
  Pixmap currentRootPixmap = None;
    if( id == None ) id = XInternAtom (Xdisplay, "_XROOTPMAP_ID", True);
    
    if( id != None )
    {
      Atom act_type;
      int act_format;
      unsigned long nitems, bytes_after;
      unsigned char *prop = NULL;

/*fprintf(stderr, "\n aterm GetRootPixmap(): root pixmap is set");    		    */
	if (XGetWindowProperty (Xdisplay, Xroot, id, 0, 1, False, XA_PIXMAP,
			        &act_type, &act_format, &nitems, &bytes_after,
				&prop) == Success)
	{
	    if (prop)
	    {
		currentRootPixmap = *((Pixmap *) prop);
		XFree (prop);
/*fprintf(stderr, "\n aterm GetRootPixmap(): root pixmap is [%lu]", currentRootPixmap); */
	    }
	}
    }
    return currentRootPixmap ;
}    

static int
pixmap_error_handler (Display * dpy, XErrorEvent * error)
{
#ifdef DEBUG_IMAGING
  fprintf(stderr, "\n aterm caused XError # %u, in resource %lu, Request: %d.%d",
          error->error_code, error->resourceid, error->request_code, error->minor_code );
#endif	  
  return 0;
}


Pixmap
ValidatePixmap(Pixmap p, int bSetHandler, int bTransparent, unsigned int *pWidth, unsigned int *pHeight)
{
  int (*oldXErrorHandler) (Display *, XErrorEvent *) = NULL;
   /* we need to check if pixmap is still valid */
  Window root;
  int junk;
    if( bSetHandler )
        oldXErrorHandler = XSetErrorHandler (pixmap_error_handler);

    if(bTransparent)        p = GetRootPixmap( None );
    if( !pWidth ) pWidth = &junk ;
    if( !pHeight ) pHeight = &junk ;
	
    if( p != None )
    {
        if (!XGetGeometry (Xdisplay, p, &root, &junk, &junk, pWidth, pHeight, &junk, &junk))
    	    p = None ;
    }
    if( bSetHandler )
        XSetErrorHandler (oldXErrorHandler);
    
    return p ;
}

/****************************************************************************
 *
 * grab a section of the screen and darken it
 *
 ***************************************************************************/
int 
fill_with_darkened_background (Pixmap *pixmap, XColor color, int x, int y, int width, int height, int root_x, int root_y, int bDiscardOriginal)
{
  unsigned int root_w, root_h ;
  Pixmap root_pixmap ;
  ShadingInfo shading ;

    /* added by Sasha on 02/24/1999 to use transparency&shading provided by
       libasimage 1.1 */
    root_pixmap = ValidatePixmap(None, 1, 1, &root_w, &root_h);

    if( root_pixmap != None )
    {
      
	INIT_SHADING2( shading, color )
	if (*pixmap == None)
	  {
	    *pixmap = XCreatePixmap (dpy, RootWindow (dpy, screen), width, height, DefaultDepth (dpy, screen));
	    bDiscardOriginal = 1;
	  }
	FillPixmapWithTile( *pixmap, root_pixmap, x, y, width, height, root_x, root_y );
	if( !NO_NEED_TO_SHADE(shading) )
	{
	  Pixmap shaded_pixmap = ShadePixmap(*pixmap, x, y, width, height, DefaultGC( dpy, screen ), &shading );
	    if( shaded_pixmap )
	    {
		if( bDiscardOriginal ) XFreePixmap( dpy, *pixmap );
		*pixmap = shaded_pixmap ;
	    }
	}

	return 1;
    }
    return 0;
}

/****************************************************************************
 *
 * grab a section of the screen and combine it with an XImage
 *
 ***************************************************************************/
int 
fill_with_pixmapped_background (Pixmap *pixmap, XImage* image, int x, int y, int width, int height, int root_x, int root_y, int bDiscardOriginal)
{
  unsigned int root_w, root_h ;
  Pixmap root_pixmap ;

    root_pixmap = ValidatePixmap(None, 1, 1, &root_w, &root_h);
    if( root_pixmap != None )
    {
	if (*pixmap == None)
	  *pixmap = XCreatePixmap (dpy, RootWindow (dpy, screen), width, height, DefaultDepth (dpy, screen));
	FillPixmapWithTile( *pixmap, root_pixmap, x, y, width, height, root_x, root_y );
	CombinePixmapWithXImage( *pixmap, image, x, y, width, height );
	return 1;
    }
    return 0;
}
