/* xutil.c - utility functions for X
 * 
 *  Raster graphics library
 *
 *  Copyright (c) 1997 Alfredo K. Kojima 
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *  
 *  This library 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
 *  Library General Public License for more details.
 *  
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <config.h>


#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <assert.h>

#ifdef XSHM
#include <sys/ipc.h>
#include <sys/shm.h>
#endif /* XSHM */

#include "wraster.h"


#ifdef XSHM

static int shmError;

static int (*oldErrorHandler)();

static int
errorHandler(Display *dpy, XErrorEvent *err)
{
    shmError=1;
    if(err->error_code!=BadAccess)
	(*oldErrorHandler)(dpy, err);

    return 0;
}


#endif


RXImage*
RCreateXImage(RContext *context, int depth, unsigned width, unsigned height)
{
    RXImage *rximg;
    Visual *visual = context->visual;
    
    rximg = malloc(sizeof(RXImage));
    if (!rximg) {
	RErrorCode = RERR_NOMEMORY;
	return NULL;
    }
    
#ifndef XSHM
    rximg->image = XCreateImage(context->dpy, visual, depth,
				ZPixmap, 0, NULL, width, height, 8, 0);
    if (!rximg->image) {
	free(rximg);
	RErrorCode = RERR_XERROR;
	return NULL;
    }
    rximg->image->data = malloc(rximg->image->bytes_per_line*height);
    if (!rximg->image->data) {
	XDestroyImage(rximg->image);
	free(rximg);
	RErrorCode = RERR_NOMEMORY;
	return NULL;
    }
    
#else /* XSHM */    
    if (!context->attribs->use_shared_memory) {
    retry_without_shm:

	context->attribs->use_shared_memory = 0;
	rximg->is_shared = 0;
	rximg->image = XCreateImage(context->dpy, visual, depth,
				    ZPixmap, 0, NULL, width, height, 8, 0);
	if (!rximg->image) {
	    free(rximg);
	    RErrorCode = RERR_XERROR;
	    return NULL;
	}
	rximg->image->data = malloc(rximg->image->bytes_per_line*height);
	if (!rximg->image->data) {
	    XDestroyImage(rximg->image);
	    free(rximg);
	    RErrorCode = RERR_NOMEMORY;
	    return NULL;
	}
    } else {
	rximg->is_shared = 1;

	rximg->info.readOnly = False;

	rximg->image = XShmCreateImage(context->dpy, visual, depth,
				       ZPixmap, NULL, &rximg->info, width, 
				       height);	

	rximg->info.shmid = shmget(IPC_PRIVATE, 
				   rximg->image->bytes_per_line*height,
				   IPC_CREAT|0777);
	if (rximg->info.shmid < 0) {
	    context->attribs->use_shared_memory = 0;
	    perror("wrlib:could not allocate shared memory segment");
	    XDestroyImage(rximg->image);
	    goto retry_without_shm;
	}
	
	rximg->info.shmaddr = shmat(rximg->info.shmid, 0, 0);
	if (rximg->info.shmaddr == (void*)-1) {
	    context->attribs->use_shared_memory = 0;
	    if (shmctl(rximg->info.shmid, IPC_RMID, 0) < 0)
		perror("wrlib:shmctl");
	    perror("wrlib:could not allocate shared memory");
	    XDestroyImage(rximg->image);
	    goto retry_without_shm;
	}
	
	shmError = 0;
	XSync(context->dpy, False);
	oldErrorHandler = XSetErrorHandler(errorHandler);
	XShmAttach(context->dpy, &rximg->info);	
	XSync(context->dpy, False);
	XSetErrorHandler(oldErrorHandler);

	rximg->image->data = rximg->info.shmaddr;
/*	rximg->image->obdata = &(rximg->info);*/

	if (shmError) {
	    context->attribs->use_shared_memory = 0;
	    XDestroyImage(rximg->image);
	    if (shmdt(rximg->info.shmaddr) < 0)
		perror("wrlib:shmdt");
	    if (shmctl(rximg->info.shmid, IPC_RMID, 0) < 0)
		perror("wrlib:shmctl");
/*	    printf("wrlib:error attaching shared memory segment to XImage\n");
 */
	    goto retry_without_shm;
	}
    }
#endif /* XSHM */
    
    return rximg;
}


void
RDestroyXImage(RContext *context, RXImage *rximage)
{
#ifndef XSHM
    XDestroyImage(rximage->image);
#else /* XSHM */
    if (rximage->is_shared) {
	XSync(context->dpy, False);
	XShmDetach(context->dpy, &rximage->info);
	XDestroyImage(rximage->image);
	if (shmdt(rximage->info.shmaddr) < 0)
	    perror("wrlib:shmdt");
	if (shmctl(rximage->info.shmid, IPC_RMID, 0) < 0)
	    perror("wrlib:shmctl");
    } else {
	XDestroyImage(rximage->image);
    }
#endif
    free(rximage);
}


static unsigned
getDepth(Display *dpy, Drawable d)
{
    Window w;
    int foo;
    unsigned bar;
    unsigned depth;
    
    XGetGeometry(dpy, d, &w, &foo, &foo, &bar, &bar, &bar, &depth);
    
    return depth;
}



RXImage*
RGetXImage(RContext *context, Drawable d, int x, int y,
	   unsigned width, unsigned height)
{
    RXImage *ximg = NULL;

#ifdef XSHM
    if (context->attribs->use_shared_memory && 0) {
	ximg = RCreateXImage(context, getDepth(context->dpy, d),
			     width, height);

	if (ximg && !ximg->is_shared) {
	    RDestroyXImage(context, ximg);
	    ximg = NULL;
	}
	if (ximg) {
	    XShmGetImage(context->dpy, d, ximg->image, x, y, AllPlanes);
	}
    }
    if (!ximg) {
	ximg = malloc(sizeof(RXImage));
	if (!ximg) {
	    RErrorCode = RERR_NOMEMORY;
	    return NULL;
	}
	ximg->is_shared = 0;
	ximg->image = XGetImage(context->dpy, d, x, y, width, height,
				AllPlanes, ZPixmap);
    }
    return ximg;
#else /* !XSHM */
    ximg = malloc(sizeof(RXImage));
    if (!ximg) {
	RErrorCode = RERR_NOMEMORY;
	return NULL;
    }
    
    ximg->image = XGetImage(context->dpy, d, x, y, width, height,
			    AllPlanes, ZPixmap);

    return ximg;
#endif /* !XSHM */
}


void
RPutXImage(RContext *context, Drawable d, GC gc, RXImage *ximage, int src_x,
	   int src_y, int dest_x, int dest_y, 
	   unsigned int width, unsigned int height)
{
#ifndef XSHM
    XPutImage(context->dpy, d, gc, ximage->image, src_x, src_y, dest_x,
	      dest_y, width, height);
#else
    if (ximage->is_shared) {
	XShmPutImage(context->dpy, d, gc, ximage->image, src_x, src_y,
		     dest_x, dest_y, width, height, False);
    } else {
	XPutImage(context->dpy, d, gc, ximage->image, src_x, src_y, dest_x,
		  dest_y, width, height);
    }
    XFlush(context->dpy);
#endif /* XSHM */
}


#ifdef XSHM
Pixmap
R_CreateXImageMappedPixmap(RContext *context, RXImage *rximage)
{
    Pixmap pix;

    pix = XShmCreatePixmap(context->dpy, context->drawable, 
			   rximage->image->data, &rximage->info,
			   rximage->image->width, rximage->image->height,
			   rximage->image->depth);

    return pix;
}

#endif /* XSHM */

