The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#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. */

#if !defined(PERLIO_IS_STDIO) && defined(HASATTRIBUTE)
# undef printf
#endif
    
#include <gdk/gdk.h>
#include <gtk/gtk.h>

#if !defined(PERLIO_IS_STDIO) && defined(HASATTRIBUTE)
# define printf PerlIO_stdoutf
#endif

#include "PerlGtkInt.h"
#include "MiscTypes.h"
#include "GdkTypes.h"
#include "GtkTypes.h"
#include "GtkDefs.h"

SV * newSVGdkBitmap(GdkBitmap * x) { return newSVGdkWindow(x); }
GdkBitmap * SvGdkBitmap(SV * x) { return SvGdkWindow(x); }
SV * newSVGdkPixmap(GdkPixmap * x) { return newSVGdkWindow(x); }
GdkPixmap * SvGdkPixmap(SV * x) { return SvGdkWindow(x); }

/*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 * newSVGdkRegion(GdkRegion * c) { return newSVMiscRef(c, "Gtk::Gdk::Region",0); }
GdkRegion * SvGdkRegion(SV * data) { return SvMiscRef(data, "Gtk::Gdk::Region"); }

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 * newSVGdkWindow(GdkWindow * value) {
	int n = 0;
	SV * result;
	result = newSVMiscRef(value, 
		(value && (gdk_window_get_type(value) == GDK_WINDOW_PIXMAP)) ? 
			"Gtk::Gdk::Pixmap" :
			"Gtk::Gdk::Window"
		, &n);
	if (n && value)
		gdk_window_ref(value);
	return result;
}

GdkWindow * SvGdkWindow(SV * value) { return (GdkWindow*)SvMiscRef(value, "Gtk::Gdk::Pixmap"); }


SV * newSVGdkRectangle(GdkRectangle * rect)
{
	AV * a;
	SV * r;
	
	if (!rect)
		return newSVsv(&PL_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 = pgtk_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(&PL_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 = pgtk_alloc_temp(sizeof(GdkGCValues));
	
	memset(v,0,sizeof(GdkGCValues));

	if ((s=hv_fetch(h, "foreground", 10, 0)) && SvOK(*s)) {
		SvSetGdkColor(*s, &v->foreground);
		*m |= GDK_GC_FOREGROUND;
	}
	if ((s=hv_fetch(h, "background", 10, 0)) && SvOK(*s)) {
		SvSetGdkColor(*s, &v->background);
		*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(&PL_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(&PL_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)
{
	SV *s = newSViv(0);
	sv_setuv(s, a);
	return s;
}

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

SV * newSVGdkEvent(GdkEvent * e)
{
	HV * h;
	GdkEvent * e2;
	SV * r;
	int n;
	
	if (!e)
		return newSVsv(&PL_sv_undef);
	/* gdk_event_copy() will segfault otherwise */
	if (!e->any.window)
		return newSVsv(&PL_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((long)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);
	hv_store(h, "send_event", 10, newSViv(e->any.send_event), 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_VISIBILITY_NOTIFY:
		hv_store(h, "state", 5, newSVGdkVisibilityState(e->visibility.state), 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);
		hv_store(h, "x_root", 6, newSVnv(e->motion.x_root), 0);
		hv_store(h, "y_root", 6, newSVnv(e->motion.y_root), 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->button.pressure), 0);
		hv_store(h, "xtilt", 5, newSVnv(e->button.xtilt), 0);
		hv_store(h, "ytilt", 5, newSVnv(e->button.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->button.source), 0);
		hv_store(h, "deviceid", 8, newSViv(e->button.deviceid), 0);
		hv_store(h, "x_root", 6, newSVnv(e->button.x_root), 0);
		hv_store(h, "y_root", 6, newSVnv(e->button.y_root), 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, newSVnv(e->key.state), 0);
		hv_store(h, "keyval", 6, newSViv(e->key.keyval), 0);
		hv_store(h, "string", 6, newSVpvn(e->key.string, e->key.length), 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, "time", 4, newSViv(e->crossing.time), 0);
		hv_store(h, "x", 1, newSVnv(e->crossing.x), 0);
		hv_store(h, "y", 1, newSVnv(e->crossing.y), 0);
		hv_store(h, "x_root", 6, newSVnv(e->crossing.x_root), 0);
		hv_store(h, "y_root", 6, newSVnv(e->crossing.y_root), 0);
		hv_store(h, "detail", 6, newSVGdkNotifyType(e->crossing.detail), 0);
		hv_store(h, "mode", 4, newSVGdkCrossingMode(e->crossing.mode), 0);
		hv_store(h, "focus", 5, newSVnv(e->crossing.focus), 0);
		hv_store(h, "state", 5, newSVnv(e->crossing.state), 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, newSVnv(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);
		hv_store(h, "target", 6, newSVGdkAtom(e->selection.target), 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->proximity.source), 0);
		hv_store(h, "deviceid", 8, newSViv(e->proximity.deviceid), 0);
		break;
	case GDK_CLIENT_EVENT:
		hv_store(h, "message_type", 12, newSVGdkAtom(e->client.message_type), 0);
		hv_store(h, "data_format", 11, newSViv(e->client.data_format), 0);
		hv_store(h, "data", 4, newSVpvn(e->client.data.b, 20), 0);
		break;
	case GDK_DRAG_ENTER:
	case GDK_DRAG_LEAVE:
	case GDK_DRAG_MOTION:
	case GDK_DRAG_STATUS:
	case GDK_DROP_START:
	case GDK_DROP_FINISHED:
		hv_store(h, "time", 4, newSVnv(e->dnd.time), 0);
		hv_store(h, "x_root", 6, newSViv(e->dnd.x_root), 0);
		hv_store(h, "y_root", 6, newSViv(e->dnd.y_root), 0);
		hv_store(h, "context", 7, newSVGdkDragContext(e->dnd.context), 0);
		break;
	default:
		/*g_message("event type %d not handled", e->type);*/
		break;
	}
	
	return r;
}

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

      	if (!data || !SvOK(data) || !(h=(HV*)SvRV(data)) || (SvTYPE(h) != SVt_PVHV))
                return 0;
        
        if (!e)
        	e = pgtk_alloc_temp(sizeof(GdkEvent));
        
        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");
	if ((s=hv_fetch(h, "send_event", 10, 0)))
		e->any.send_event = SvIV(*s);
	
	switch (e->type) {
	case GDK_MAP:
	case GDK_UNMAP:
	case GDK_DELETE:
	case GDK_DESTROY:
	case GDK_NO_EXPOSE:
		break;
	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_VISIBILITY_NOTIFY:
		if ((s=hv_fetch(h, "state", 5, 0)))
			e->visibility.state = SvGdkVisibilityState(*s);
		else
			croak("event must contain state");
		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->motion.pressure = SvNV(*s);
		else
			e->motion.pressure = 0;
		if ((s=hv_fetch(h, "xtilt", 5, 0)))
			e->motion.xtilt = SvNV(*s);
		else
			e->motion.xtilt = 0;
		if ((s=hv_fetch(h, "ytilt", 5, 0)))
			e->motion.ytilt = SvNV(*s);
		else
			e->motion.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->motion.source = SvGdkInputSource(*s);
		else
			e->motion.source = 0;
		if ((s=hv_fetch(h, "deviceid", 8, 0)) && SvOK(*s))
			e->motion.deviceid = SvIV(*s);
		else
			e->motion.deviceid = GDK_CORE_POINTER;
		if ((s=hv_fetch(h, "x_root", 6, 0)))
			e->motion.x_root = SvIV(*s);
		else
			croak("event must contain x_root");
		if ((s=hv_fetch(h, "y_root", 6, 0)))
			e->motion.y_root = SvIV(*s);
		else
			croak("event must contain y_root");
		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, "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;
		if ((s=hv_fetch(h, "x_root", 6, 0)))
			e->button.x_root = SvIV(*s);
		else
			croak("event must contain x_root");
		if ((s=hv_fetch(h, "y_root", 6, 0)))
			e->button.y_root = SvIV(*s);
		else
			croak("event must contain y_root");
		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");
		if ((s=hv_fetch(h, "string", 6, 0))) {
			STRLEN len;
			/* FIXME: need to free e->key.string? */
			e->key.string = g_strdup(SvPV(*s, len));
			e->key.length = len;
		} else
			croak("event must contain string");
		break;
	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, "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");
		if ((s=hv_fetch(h, "x", 1, 0)))
			e->crossing.x = SvIV(*s);
		else
			croak("event must contain x ordinate");
		if ((s=hv_fetch(h, "y", 1, 0)))
			e->crossing.y = SvIV(*s);
		else
			croak("event must contain y ordinate");
		if ((s=hv_fetch(h, "x_root", 6, 0)))
			e->crossing.x_root = SvIV(*s);
		else
			croak("event must contain x_root ordinate");
		if ((s=hv_fetch(h, "y_root", 6, 0)))
			e->crossing.y_root = SvIV(*s);
		else
			croak("event must contain y_root ordinate");
		if ((s=hv_fetch(h, "time", 4, 0)))
			e->crossing.time = SvIV(*s);
		else
			croak("event must contain time");
		if ((s=hv_fetch(h, "state", 5, 0)))
			e->crossing.state = SvIV(*s);
		else
			croak("event must contain state");
		if ((s=hv_fetch(h, "mode", 4, 0)))
			e->crossing.time = SvGdkCrossingMode(*s);
		else
			croak("event must contain time");
		if ((s=hv_fetch(h, "focus", 5, 0)))
			e->crossing.focus = SvIV(*s);
		else
			croak("event must contain focus");
		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");
		if ((s=hv_fetch(h, "target", 6, 0)))
			e->selection.target = SvGdkAtom(*s);
		else
			croak("event must contain target");
		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");
		if ((s=hv_fetch(h, "source", 6, 0)))
			e->proximity.source = SvGdkInputSource(*s);
		else
			croak("event must contain source");
		break;
	case GDK_CLIENT_EVENT:
		if ((s=hv_fetch(h, "message_type", 12, 0)))
			e->client.message_type = SvGdkAtom(*s);
		else
			croak("event must contain message_type");
		if ((s=hv_fetch(h, "data_format", 11, 0)))
			e->client.data_format = SvIV(*s);
		else
			croak("event must contain data_format");
		if ((s=hv_fetch(h, "data", 4, 0))) {
			STRLEN len;
			char *p = SvPV(*s, len);
			memcpy(e->client.data.b, p, len>20?20:len);
		} else
			croak("event must contain data");
		break;
	case GDK_DRAG_ENTER:
	case GDK_DRAG_LEAVE:
	case GDK_DRAG_MOTION:
	case GDK_DRAG_STATUS:
	case GDK_DROP_START:
	case GDK_DROP_FINISHED:
		if ((s=hv_fetch(h, "time", 4, 0)))
			e->dnd.time = SvIV(*s);
		else
			croak("event must contain time");
		if ((s=hv_fetch(h, "x_root", 6, 0)))
			e->dnd.x_root = SvIV(*s);
		else
			croak("event must contain x_root");
		if ((s=hv_fetch(h, "y_root", 6, 0)))
			e->dnd.y_root = SvIV(*s);
		else
			croak("event must contain y_root");
		if ((s=hv_fetch(h, "context", 7, 0)))
			e->dnd.context = SvGdkDragContext(*s);
		else
			croak("event must contain context");
		break;
	}
	
	return e;
}

GdkWindowAttr * SvGdkWindowAttr(SV * data, GdkWindowAttr * attr, gint * mask)
{
	dTHR;	

	HV * h;
	SV ** s;

	if ((!data) || (!SvOK(data)) || (!SvRV(data)) || (SvTYPE(SvRV(data)) != SVt_PVHV))
		return 0;
	
	if (!attr)
		attr = pgtk_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,PL_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;
}

#if GTK_HVER >= 0x010200

SV * newSVGdkDragContextRef(GdkDragContext* f) { return newSVMiscRef(f, "Gtk::Gdk::DragContext", 0); }
GdkDragContext* SvGdkDragContextRef(SV * data) { return SvMiscRef(data, "Gtk::Gdk::DragContext"); }

#endif