#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

/* Copyright (C) 1997, Kenneth Albanowski.
   This code may be distributed under the same terms as Perl itself. */
   
#include <gdk/gdk.h>
#include <gtk/gtk.h>
#include "MiscTypes.h"
#include "GdkTypes.h"
#include "GtkTypes.h"
#include "GtkDefs.h"

/*SV * newSVGdkWindowRef(GdkWindow * w) { return newSVMiscRef(w, "Gtk::Gdk::Window",0); }
GdkWindow * SvGdkWindowRef(SV * data) { return SvMiscRef(data, "Gtk::Gdk::Window"); }

SV * newSVGdkPixmapRef(GdkPixmap * w) { return newSVMiscRef(w, "Gtk::Gdk::Pixmap",0); }
GdkPixmap * SvGdkPixmapRef(SV * data) { return SvMiscRef(data, "Gtk::Gdk::Pixmap"); }

SV * newSVGdkBitmapRef(GdkBitmap * w) { return newSVMiscRef(w, "Gtk::Gdk::Bitmap",0); }
GdkBitmap * SvGdkBitmapRef(SV * data) { return SvMiscRef(data, "Gtk::Gdk::Bitmap"); }

SV * newSVGdkColormapRef(GdkColormap * w) { return newSVMiscRef(w, "Gtk::Gdk::Colormap",0); }
GdkColormap * SvGdkColormapRef(SV * data) { return SvMiscRef(data, "Gtk::Gdk::Colormap"); }*/

SV * newSVGdkColor(GdkColor * c) { return newSVMiscRef(c, "Gtk::Gdk::Color",0); }
GdkColor * SvGdkColor(SV * data) { return SvMiscRef(data, "Gtk::Gdk::Color"); }

SV * newSVGdkCursorRef(GdkCursor * w) { return newSVMiscRef(w, "Gtk::Gdk::Cursor",0); }
GdkCursor * SvGdkCursorRef(SV * data) { return SvMiscRef(data, "Gtk::Gdk::Cursor"); }

SV * newSVGdkVisualRef(GdkVisual * w) { return newSVMiscRef(w, "Gtk::Gdk::Visual",0); }
GdkVisual * SvGdkVisualRef(SV * data) { return SvMiscRef(data, "Gtk::Gdk::Visual"); }

SV * newSVGdkGCRef(GdkGC * g) { return newSVMiscRef(g, "Gtk::Gdk::GC",0); }
GdkGC * SvGdkGCRef(SV * data) { return SvMiscRef(data, "Gtk::Gdk::GC"); }

SV * newSVGdkFontRef(GdkFont * f) { return newSVMiscRef(f, "Gtk::Gdk::Font",0); }
GdkFont * SvGdkFontRef(SV * data) { return SvMiscRef(data, "Gtk::Gdk::Font"); }

/*SV * newSVGdkImageRef(GdkImage * i) { return newSVMiscRef(i, "Gtk::Gdk::Image",0); }
GdkImage * SvGdkImageRef(SV * data) { return SvMiscRef(data, "Gtk::Gdk::Image"); }*/

SV * newSVGdkRectangle(GdkRectangle * rect)
{
	AV * a;
	SV * r;
	
	if (!rect)
		return newSVsv(&sv_undef);
		
	a = newAV();
	r = newRV((SV*)a);
	SvREFCNT_dec(a);
	
	av_push(a, newSViv(rect->x));
	av_push(a, newSViv(rect->y));
	av_push(a, newSViv(rect->width));
	av_push(a, newSViv(rect->height));
	
	return r;
}

GdkRectangle * SvGdkRectangle(SV * data, GdkRectangle * rect)
{
	AV * a;
	SV ** s;

	if ((!data) || (!SvOK(data)) || (!SvRV(data)) || (SvTYPE(SvRV(data)) != SVt_PVAV))
		return 0;
		
	a = (AV*)SvRV(data);

	if (av_len(a) != 3)
		croak("rectangle must have four elements");

	if (!rect)
		rect = alloc_temp(sizeof(GdkRectangle));
	
	rect->x = SvIV(*av_fetch(a, 0, 0));
	rect->y = SvIV(*av_fetch(a, 1, 0));
	rect->width = SvIV(*av_fetch(a, 2, 0));
	rect->height = SvIV(*av_fetch(a, 3, 0));
	
	return rect;
}

SV * newSVGdkGCValues(GdkGCValues * v)
{
	HV * h;
	SV * r;
	
	if (!v)
		return newSVsv(&sv_undef);
		
	h = newHV();
	r = newRV((SV*)h);
	SvREFCNT_dec(h);

	hv_store(h, "foreground", 10, newSVMiscRef(&v->foreground, "Gtk::Gdk::Color",0), 0);
	hv_store(h, "background", 10, newSVMiscRef(&v->background, "Gtk::Gdk::Color",0), 0);
	hv_store(h, "font", 4, newSVMiscRef(v->font, "Gtk::Gdk::Font",0), 0);
	hv_store(h, "function", 8, newSVGdkFunction(v->function), 0);
	hv_store(h, "fill", 4, newSVGdkFill(v->fill), 0);
	hv_store(h, "tile", 4, newSVMiscRef(v->tile, "Gtk::Gdk::Pixmap",0), 0);
	hv_store(h, "stipple", 7, newSVMiscRef(v->stipple, "Gtk::Gdk::Pixmap",0), 0);
	hv_store(h, "clip_mask", 9, newSVMiscRef(v->clip_mask, "Gtk::Gdk::Pixmap",0), 0);
	hv_store(h, "subwindow_mode", 14, newSVGdkSubwindowMode(v->subwindow_mode), 0);
	hv_store(h, "ts_x_origin", 11, newSViv(v->ts_x_origin), 0);
	hv_store(h, "ts_y_origin", 11, newSViv(v->ts_y_origin), 0);
	hv_store(h, "clip_x_origin", 13, newSViv(v->clip_x_origin), 0);
	hv_store(h, "clip_x_origin", 13, newSViv(v->clip_y_origin), 0);
	hv_store(h, "graphics_exposures", 18, newSViv(v->graphics_exposures), 0);
	hv_store(h, "line_width", 10, newSViv(v->line_width), 0);
	hv_store(h, "line_style", 10, newSVGdkLineStyle(v->line_style), 0);
	hv_store(h, "cap_style", 9, newSVGdkCapStyle(v->cap_style), 0);
	hv_store(h, "join_style", 10, newSVGdkJoinStyle(v->join_style), 0);
	
	return r;
}

GdkGCValues * SvGdkGCValues(SV * data, GdkGCValues * v, GdkGCValuesMask * m)
{
	HV * h;
	SV ** s;

	if ((!data) || (!SvOK(data)) || (!SvRV(data)) || (SvTYPE(SvRV(data)) != SVt_PVHV))
		return 0;
		
	h = (HV*)SvRV(data);

	if (!v)
		v = alloc_temp(sizeof(GdkGCValues));
	
	memset(v,0,sizeof(GdkGCValues));

	if ((s=hv_fetch(h, "foreground", 10, 0)) && SvOK(*s)) {
		GdkColor * c = SvMiscRef(*s, "Gtk::Gdk::Color");
		if (c) {
			v->foreground = *c;
			*m |= GDK_GC_FOREGROUND;
		}
	}
	if ((s=hv_fetch(h, "background", 10, 0)) && SvOK(*s)) {
		GdkColor * c = SvMiscRef(*s, "Gtk::Gdk::Color");
		if (c) {
			v->background = *c;
			*m |= GDK_GC_BACKGROUND;
		}
	}
	if ((s=hv_fetch(h, "font", 4, 0)) && SvOK(*s)) {
		v->font = SvMiscRef(*s, "Gtk::Gdk::Font");
		*m |= GDK_GC_FONT;
	}
	if ((s=hv_fetch(h, "function", 8, 0)) && SvOK(*s)) {
		v->function = SvGdkFunction(*s);
		*m |= GDK_GC_FUNCTION;
	}
	if ((s=hv_fetch(h, "fill", 4, 0)) && SvOK(*s)) {
		v->function = SvGdkFill(*s);
		*m |= GDK_GC_FILL;
	}
	if ((s=hv_fetch(h, "tile", 4, 0)) && SvOK(*s)) {
		v->tile = SvMiscRef(*s, "Gtk::Gdk::Pixmap");
		*m |= GDK_GC_TILE;
	}
	if ((s=hv_fetch(h, "stipple", 7, 0)) && SvOK(*s)) {
		v->stipple = SvMiscRef(*s, "Gtk::Gdk::Pixmap");
		*m |= GDK_GC_STIPPLE;
	}
	if ((s=hv_fetch(h, "clip_mask", 9, 0)) && SvOK(*s)) {
		v->clip_mask = SvMiscRef(*s, "Gtk::Gdk::Pixmap");
		*m |= GDK_GC_CLIP_MASK;
	}
	if ((s=hv_fetch(h, "subwindow_mode", 14, 0)) && SvOK(*s)) {
		v->subwindow_mode = SvGdkSubwindowMode(*s);
		*m |= GDK_GC_SUBWINDOW;
	}
	if ((s=hv_fetch(h, "ts_x_origin", 11, 0)) && SvOK(*s)) {
		v->ts_x_origin = SvIV(*s);
		*m |= GDK_GC_TS_X_ORIGIN;
	}
	if ((s=hv_fetch(h, "ts_y_origin", 11, 0)) && SvOK(*s)) {
		v->ts_y_origin = SvIV(*s);
		*m |= GDK_GC_TS_Y_ORIGIN;
	}
	if ((s=hv_fetch(h, "clip_x_origin", 13, 0)) && SvOK(*s)) {
		v->clip_x_origin = SvIV(*s);
		*m |= GDK_GC_CLIP_X_ORIGIN;
	}
	if ((s=hv_fetch(h, "clip_y_origin", 13, 0)) && SvOK(*s)) {
		v->clip_y_origin = SvIV(*s);
		*m |= GDK_GC_CLIP_Y_ORIGIN;
	}
	if ((s=hv_fetch(h, "graphics_exposures", 18, 0)) && SvOK(*s)) {
		v->graphics_exposures = SvIV(*s);
		*m |= GDK_GC_EXPOSURES;
	}
	if ((s=hv_fetch(h, "line_width", 10, 0)) && SvOK(*s)) {
		v->line_width= SvIV(*s);
		*m |= GDK_GC_LINE_WIDTH;
	}
	if ((s=hv_fetch(h, "line_style", 10, 0)) && SvOK(*s)) {
		v->line_style= SvGdkLineStyle(*s);
		*m |= GDK_GC_LINE_STYLE;
	}
	if ((s=hv_fetch(h, "cap_style", 9, 0)) && SvOK(*s)) {
		v->cap_style = SvGdkCapStyle(*s);
		*m |= GDK_GC_CAP_STYLE;
	}
	if ((s=hv_fetch(h, "join_style", 10, 0)) && SvOK(*s)) {
		v->join_style = SvGdkJoinStyle(*s);
		*m |= GDK_GC_JOIN_STYLE;
	}
	return v;
}

SV * newSVGdkDeviceInfo(GdkDeviceInfo * v)
{
	HV * h;
	SV * r;
	
	if (!v)
		return newSVsv(&sv_undef);
		
	h = newHV();
	r = newRV((SV*)h);
	SvREFCNT_dec(h);

	hv_store(h, "deviceid", 8, newSViv(v->deviceid), 0);
	hv_store(h, "name", 4, newSVpv(v->name, 0), 0);
	hv_store(h, "source", 6, newSVGdkInputSource(v->source), 0);
	hv_store(h, "mode", 4, newSVGdkInputMode(v->mode), 0);
	hv_store(h, "has_cursor", 10, newSViv(v->has_cursor), 0);
	hv_store(h, "num_axes", 8, newSViv(v->num_axes), 0);
	if (v->axes) {
		int i;
		AV * a = newAV();
		for(i=0;i<v->num_axes;i++) {
			av_push(a, newSVGdkAxisUse(v->axes[i]));
		}
		hv_store(h, "axes", 4, newRV((SV*)a), 0);
		SvREFCNT_dec(a);
	}

	return r;
}

SV * newSVGdkTimeCoord(GdkTimeCoord * v)
{
	HV * h;
	SV * r;
	
	if (!v)
		return newSVsv(&sv_undef);
		
	h = newHV();
	r = newRV((SV*)h);
	SvREFCNT_dec(h);

	hv_store(h, "time", 4, newSViv(v->time), 0);
	hv_store(h, "x", 1, newSVnv(v->x), 0);
	hv_store(h, "y", 1, newSVnv(v->y), 0);
	hv_store(h, "pressure", 8, newSVnv(v->pressure), 0);
	hv_store(h, "xtilt", 5, newSVnv(v->xtilt), 0);
	hv_store(h, "ytilt", 5, newSVnv(v->ytilt), 0);

	return r;
}

SV * newSVGdkAtom(GdkAtom a)
{
	return newSViv(a);
}

GdkAtom SvGdkAtom(SV * data)
{
	return SvIV(data);
}

SV * newSVGdkEvent(GdkEvent * e)
{
	HV * h;
	GdkEvent * e2;
	SV * r;
	int n;
	
	if (!e)
		return newSVsv(&sv_undef);
 
 
        h = newHV();
        /*r = newSVMiscRef(e, "Gtk::Gdk::Event", &n);*/

	/*h = (HV*)SvRV(r);*/
	r = newRV((SV*)h);
	SvREFCNT_dec(h);

	sv_bless(r, gv_stashpv("Gtk::Gdk::Event", FALSE));
 	
	e2 = gdk_event_copy(e);
	
	hv_store(h, "_ptr", 4, newSViv((int)e2), 0);
	
	/*printf("Turning GdkEvent %d, type %d, into SV %d, ptr %d\n", e, e->type, r, e2);*/
	
	hv_store(h, "type", 4, newSVGdkEventType(e->type), 0);
	hv_store(h, "window", 6, newSVGdkWindow(e->any.window), 0);
	switch (e->type) {
	case GDK_EXPOSE:
		hv_store(h, "area", 4, newSVGdkRectangle(&e->expose.area), 0);
		hv_store(h, "count", 5, newSViv(e->expose.count), 0);
		break;
	case GDK_MOTION_NOTIFY:
		hv_store(h, "is_hint", 7, newSViv(e->motion.is_hint), 0);
		hv_store(h, "x", 1, newSVnv(e->motion.x), 0);
		hv_store(h, "y", 1, newSVnv(e->motion.y), 0);
		hv_store(h, "pressure", 8, newSVnv(e->motion.pressure), 0);
		hv_store(h, "xtilt", 5, newSVnv(e->motion.xtilt), 0);
		hv_store(h, "ytilt", 5, newSVnv(e->motion.ytilt), 0);
		hv_store(h, "time", 4, newSViv(e->motion.time), 0);
		hv_store(h, "state", 5, newSViv(e->motion.state), 0);
		hv_store(h, "source", 6, newSVGdkInputSource(e->motion.source), 0);
		hv_store(h, "deviceid", 8, newSViv(e->motion.deviceid), 0);
		break;
	case GDK_BUTTON_PRESS:
	case GDK_2BUTTON_PRESS:
	case GDK_3BUTTON_PRESS:
	case GDK_BUTTON_RELEASE:
		hv_store(h, "x", 1, newSViv(e->button.x), 0);
		hv_store(h, "y", 1, newSViv(e->button.y), 0);
		hv_store(h, "time", 4, newSViv(e->button.time), 0);
		hv_store(h, "pressure", 8, newSVnv(e->motion.pressure), 0);
		hv_store(h, "xtilt", 5, newSVnv(e->motion.xtilt), 0);
		hv_store(h, "ytilt", 5, newSVnv(e->motion.ytilt), 0);
		hv_store(h, "state", 5, newSViv(e->button.state), 0);
		hv_store(h, "button", 6, newSViv(e->button.button), 0);
		hv_store(h, "source", 6, newSVGdkInputSource(e->motion.source), 0);
		hv_store(h, "deviceid", 8, newSViv(e->motion.deviceid), 0);
		break;
	case GDK_KEY_PRESS:
	case GDK_KEY_RELEASE:
		hv_store(h, "time", 4, newSViv(e->key.time), 0);
		hv_store(h, "state", 5, newSViv(e->key.state), 0);
		hv_store(h, "keyval", 6, newSViv(e->key.keyval), 0);
		break;
	case GDK_FOCUS_CHANGE:
		hv_store(h, "in", 2, newSViv(e->focus_change.in), 0);
		break;
	case GDK_ENTER_NOTIFY:
	case GDK_LEAVE_NOTIFY:
		hv_store(h, "window", 6, newSVGdkWindow(e->crossing.window), 0);
		hv_store(h, "subwindow", 9, newSVGdkWindow(e->crossing.subwindow), 0);
		hv_store(h, "detail", 6, newSVGdkNotifyType(e->crossing.detail), 0);
		break;
	case GDK_CONFIGURE:
		hv_store(h, "x", 1, newSViv(e->configure.x), 0);
		hv_store(h, "y", 1, newSViv(e->configure.y), 0);
		hv_store(h, "width", 5, newSViv(e->configure.width), 0);
		hv_store(h, "height", 6, newSViv(e->configure.height), 0);
		break;
	case GDK_PROPERTY_NOTIFY:
		hv_store(h, "time", 4, newSViv(e->property.time), 0);
		hv_store(h, "state", 5, newSViv(e->property.state), 0);
		hv_store(h, "atom", 4, newSVGdkAtom(e->property.atom), 0);
		break;
	case GDK_SELECTION_CLEAR:
	case GDK_SELECTION_REQUEST:
	case GDK_SELECTION_NOTIFY:
		hv_store(h, "requestor", 9, newSViv(e->selection.requestor), 0);
		hv_store(h, "time", 4, newSViv(e->selection.time), 0);
		hv_store(h, "selection", 9, newSVGdkAtom(e->selection.selection), 0);
		hv_store(h, "property", 8, newSVGdkAtom(e->selection.property), 0);
		break;
	case GDK_PROXIMITY_IN:
	case GDK_PROXIMITY_OUT:
		hv_store(h, "time", 4, newSViv(e->proximity.time), 0);
		hv_store(h, "source", 6, newSVGdkInputSource(e->motion.source), 0);
		hv_store(h, "deviceid", 8, newSViv(e->motion.deviceid), 0);
		break;
		
	}
	
	return r;
}

GdkEvent * SvGdkEvent(SV * data)
{
	HV * h;
	SV ** s;
	GdkEvent * e;

      	if (!data || !SvOK(data) || !(h=(HV*)SvRV(data)) || (SvTYPE(h) != SVt_PVHV))
                return 0;
        s = hv_fetch(h, "_ptr", 4, 0);
        if (!s || !SvIV(*s))
                croak("event is damaged");
        
	e = (GdkEvent*)SvIV(*s);

        /*printf("Composing GdkEvent HV %d to pointer %d\n", h, e);*/
	
	if (s=hv_fetch(h, "type", 4, 0))
		e->type = SvGdkEventType(*s);
	else
		croak("event must contain type");
	if (s=hv_fetch(h, "window", 6, 0))
		e->any.window = SvGdkWindow(*s);
	else
		croak("event must contain window");
	
	switch (e->type) {
	case GDK_EXPOSE:
		if (s=hv_fetch(h, "area", 4, 0))
			SvGdkRectangle(*s, &e->expose.area);
		else
			croak("event must contain area");
		if (s=hv_fetch(h, "count", 5, 0))
			e->expose.count = SvIV(*s);
		else
			croak("event must contain count");
		break;
	case GDK_MOTION_NOTIFY:
		if (s=hv_fetch(h, "x", 1, 0))
			e->motion.x = SvNV(*s);
		else
			croak("event must contain x ordinate");
		if (s=hv_fetch(h, "y", 1, 0))
			e->motion.y = SvNV(*s);
		else
			croak("event must contain y ordinate");
		if (s=hv_fetch(h, "pressure", 8, 0))
			e->button.button = SvNV(*s);
		else
			e->button.pressure = 0;
		if (s=hv_fetch(h, "xtilt", 5, 0))
			e->button.xtilt = SvNV(*s);
		else
			e->button.xtilt = 0;
		if (s=hv_fetch(h, "ytilt", 5, 0))
			e->button.ytilt = SvNV(*s);
		else
			e->button.ytilt = 0;
		if (s=hv_fetch(h, "ytilt", 5, 0))
			e->button.ytilt = SvNV(*s);
		else
			e->button.ytilt = 0;
		if (s=hv_fetch(h, "time", 4, 0))
			e->motion.time = SvIV(*s);
		else
			croak("event must contain time");
		if (s=hv_fetch(h, "state", 5, 0))
			e->motion.state = SvIV(*s);
		else
			croak("event must contain state");
		if (s=hv_fetch(h, "is_hint", 7, 0))
			e->motion.is_hint = SvIV(*s);
		else
			croak("event must contain is_hint");
		if (s=hv_fetch(h, "source", 6, 0))
			e->button.source = SvGdkInputSource(*s);
		else
			e->button.source = 0;
		if ((s=hv_fetch(h, "deviceid", 8, 0)) && SvOK(*s))
			e->button.deviceid = SvIV(*s);
		else
			e->button.deviceid = GDK_CORE_POINTER;
		break;
	case GDK_BUTTON_PRESS:
	case GDK_2BUTTON_PRESS:
	case GDK_3BUTTON_PRESS:
	case GDK_BUTTON_RELEASE:
		if (s=hv_fetch(h, "x", 1, 0))
			e->button.x = SvNV(*s);
		else
			croak("event must contain x ordinate");
		if (s=hv_fetch(h, "y", 1, 0))
			e->button.y = SvNV(*s);
		else
			croak("event must contain y ordinate");
		if (s=hv_fetch(h, "pressure", 8, 0))
			e->button.button = SvNV(*s);
		else
			e->button.pressure = 0;
		if (s=hv_fetch(h, "xtilt", 5, 0))
			e->button.xtilt = SvNV(*s);
		else
			e->button.xtilt = 0;
		if (s=hv_fetch(h, "ytilt", 5, 0))
			e->button.ytilt = SvNV(*s);
		else
			e->button.ytilt = 0;
		if (s=hv_fetch(h, "ytilt", 5, 0))
			e->button.ytilt = SvNV(*s);
		else
			e->button.ytilt = 0;
		if (s=hv_fetch(h, "time", 4, 0))
			e->button.time = SvIV(*s);
		else
			croak("event must contain time");
		if (s=hv_fetch(h, "state", 5, 0))
			e->button.state = SvIV(*s);
		else
			croak("event must contain state");
		if (s=hv_fetch(h, "button", 6, 0))
			e->button.button = SvIV(*s);
		else
			croak("event must contain state");
		if (s=hv_fetch(h, "source", 6, 0))
			e->button.source = SvGdkInputSource(*s);
		else
			e->button.source = 0;
		if ((s=hv_fetch(h, "deviceid", 8, 0)) && SvOK(*s))
			e->button.deviceid = SvIV(*s);
		else
			e->button.deviceid = GDK_CORE_POINTER;
		break;
	case GDK_KEY_PRESS:
	case GDK_KEY_RELEASE:
		if (s=hv_fetch(h, "time", 4, 0))
			e->key.time = SvIV(*s);
		else
			croak("event must contain time");
		if (s=hv_fetch(h, "state", 5, 0))
			e->key.state = SvIV(*s);
		else
			croak("event must contain state");
		if (s=hv_fetch(h, "keyval", 6, 0))
			e->key.keyval = SvIV(*s);
		else
			croak("event must contain keyval");
	case GDK_FOCUS_CHANGE:
		if (s=hv_fetch(h, "in", 2, 0))
			e->focus_change.in = SvIV(*s);
		else
			croak("event must contain in");
		break;
	case GDK_ENTER_NOTIFY:
	case GDK_LEAVE_NOTIFY:
		if (s=hv_fetch(h, "window", 6, 0))
			e->crossing.window = SvGdkWindow(*s);
		else
			croak("event must contain window");
		if (s=hv_fetch(h, "subwindow", 9, 0))
			e->crossing.subwindow = SvGdkWindow(*s);
		else
			croak("event must contain subwindow");
		if (s=hv_fetch(h, "detail", 6, 0))
			e->crossing.detail = SvGdkNotifyType(*s);
		else
			croak("event must contain detail");
		break;
	case GDK_CONFIGURE:
		if (s=hv_fetch(h, "x", 1, 0))
			e->configure.x = SvIV(*s);
		else
			croak("event must contain x ordinate");
		if (s=hv_fetch(h, "y", 1, 0))
			e->configure.y = SvIV(*s);
		else
			croak("event must contain y ordinate");
		if (s=hv_fetch(h, "width", 5, 0))
			e->configure.width = SvIV(*s);
		else
			croak("event must contain width");
		if (s=hv_fetch(h, "height", 6, 0))
			e->configure.height = SvIV(*s);
		else
			croak("event must contain height");
		break;
	case GDK_PROPERTY_NOTIFY:
		if (s=hv_fetch(h, "time", 4, 0))
			e->property.time = SvIV(*s);
		else
			croak("event must contain time");
		if (s=hv_fetch(h, "state", 5, 0))
			e->property.state = SvIV(*s);
		else
			croak("event must contain state");
		if (s=hv_fetch(h, "atom", 4, 0))
			e->property.atom = SvGdkAtom(*s);
		else
			croak("event must contain atom");
		break;
	case GDK_SELECTION_CLEAR:
	case GDK_SELECTION_REQUEST:
	case GDK_SELECTION_NOTIFY:
		if (s=hv_fetch(h, "requestor", 9, 0))
			e->selection.requestor = SvIV(*s);
		else
			croak("event must contain requestor");
		if (s=hv_fetch(h, "time", 4, 0))
			e->selection.time = SvIV(*s);
		else
			croak("event must contain time");
		if (s=hv_fetch(h, "selection", 9, 0))
			e->selection.selection = SvGdkAtom(*s);
		else
			croak("event must contain selection");
		if (s=hv_fetch(h, "property", 8, 0))
			e->selection.property = SvGdkAtom(*s);
		else
			croak("event must contain property");
		break;
	case GDK_PROXIMITY_IN:
	case GDK_PROXIMITY_OUT:
		if (s=hv_fetch(h, "time", 4, 0))
			e->proximity.time = SvIV(*s);
		else
			croak("event must contain time");
		if (s=hv_fetch(h, "deviceid", 8, 0))
			e->proximity.deviceid = SvIV(*s);
		else
			croak("event must contain deviceid");
		break;
	}
	
	return e;
}

GdkWindowAttr * SvGdkWindowAttr(SV * data, GdkWindowAttr * attr, gint * mask)
{
	HV * h;
	SV ** s;

	if ((!data) || (!SvOK(data)) || (!SvRV(data)) || (SvTYPE(SvRV(data)) != SVt_PVHV))
		return 0;
	
	if (!attr)
		attr = alloc_temp(sizeof(GdkWindowAttr));
	
	memset(attr, 0, sizeof(GdkWindowAttr));
	
	*mask = 0;

	h = (HV*)SvRV(data);
	
	if (s=hv_fetch(h, "title", 5, 0)) {
		attr->title = SvPV(*s,na);
		*mask |= GDK_WA_TITLE;
	}
	
	if (s=hv_fetch(h, "x", 1, 0)) {
		attr->x = SvIV(*s);
		*mask |= GDK_WA_X;
	}
	
	if (s=hv_fetch(h, "y", 1, 0)) {
		attr->y = SvIV(*s);
		*mask |= GDK_WA_Y;
	}
	
	if (s=hv_fetch(h, "cursor", 6, 0)) {
		attr->cursor = SvGdkCursorRef(*s);
		*mask |= GDK_WA_CURSOR;
	}
	
	if (s=hv_fetch(h, "colormap", 8, 0)) {
		attr->colormap = SvGdkColormap(*s);
		*mask |= GDK_WA_COLORMAP;
	}
	
	if (s=hv_fetch(h, "visual", 6, 0)) {
		attr->visual = SvGdkVisual(*s);
		*mask |= GDK_WA_VISUAL;
	}

	if (s=hv_fetch(h, "window_type",11, 0))
		attr->window_type = SvGdkWindowType(*s);
	else
		croak("window attribute must have window_type");
	if (s=hv_fetch(h, "event_mask",10, 0))
		attr->event_mask = SvGdkEventMask(*s);
	else
		croak("window attribute must have event_mask");
	if (s=hv_fetch(h, "width",5, 0))
		attr->width = SvIV(*s);
	else
		croak("window attribute must have width");
	if (s=hv_fetch(h, "height",6, 0))
		attr->height = SvIV(*s);
	else
		croak("window attribute must have height");
	if (s=hv_fetch(h, "wclass",6, 0))
		attr->wclass = SvGdkWindowClass(*s);
	else
		attr->wclass = GDK_INPUT_OUTPUT;

	return attr;
}

