#include <u.h>
#include "x11-inc.h"
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include "x11-memdraw.h"

AUTOLIB(X11);

/*
 * Allocate a Memimage with an optional pixmap backing on the X server.
 */
Memimage*
_xallocmemimage(Rectangle r, u32int chan, int pixmap)
{
	int d, offset;
	Memimage *m;
	Xmem *xm;
	XImage *xi;

	m = _allocmemimage(r, chan);
	if(chan != GREY1 && chan != _x.chan)
		return m;
	if(_x.display == 0)
		return m;

	/*
	 * For bootstrapping, don't bother storing 1x1 images
	 * on the X server.  Memimageinit needs to allocate these
	 * and we memimageinit before we do the rest of the X stuff.
	 * Of course, 1x1 images on the server are useless anyway.
	 */
	if(Dx(r)==1 && Dy(r)==1)
		return m;

	xm = mallocz(sizeof(Xmem), 1);
	if(xm == nil){
		freememimage(m);
		return nil;
	}

	/*
	 * Allocate backing store.
	 */
	if(chan == GREY1)
		d = 1;
	else
		d = _x.depth;
	if(pixmap != PMundef)
		xm->pixmap = pixmap;
	else
		xm->pixmap = XCreatePixmap(_x.display, _x.drawable, Dx(r), Dy(r), d);

	/*
	 * We want to align pixels on word boundaries.
	 */
	if(m->depth == 24)
		offset = r.min.x&3;
	else
		offset = r.min.x&(31/m->depth);
	r.min.x -= offset;
	assert(wordsperline(r, m->depth) <= m->width);

	/*
	 * Wrap our data in an XImage structure.
	 */
	xi = XCreateImage(_x.display, _x.vis, d,
		ZPixmap, 0, (char*)m->data->bdata, Dx(r), Dy(r),
		32, m->width*sizeof(u32int));
	if(xi == nil){
		freememimage(m);
		if(xm->pixmap != pixmap)
			XFreePixmap(_x.display, xm->pixmap);
		return nil;
	}

	xm->xi = xi;
	xm->r = r;

	/*
	 * Set the XImage parameters so that it looks exactly like
	 * a Memimage -- we're using the same data.
	 */
	if(m->depth < 8 || m->depth == 24)
		xi->bitmap_unit = 8;
	else
		xi->bitmap_unit = m->depth;
	xi->byte_order = LSBFirst;
	xi->bitmap_bit_order = MSBFirst;
	xi->bitmap_pad = 32;
	XInitImage(xi);
	XFlush(_x.display);

	m->X = xm;
	return m;
}

Memimage*
allocmemimage(Rectangle r, u32int chan)
{
	return _xallocmemimage(r, chan, PMundef);
}

void
freememimage(Memimage *m)
{
	Xmem *xm;

	if(m == nil)
		return;

	xm = m->X;
	if(xm && m->data->ref == 1){
		if(xm->xi){
			xm->xi->data = nil;
			XFree(xm->xi);
		}
		XFreePixmap(_x.display, xm->pixmap);
		free(xm);
		m->X = nil;
	}
	_freememimage(m);
}

