changeset 0:aa4efef0f0b1

Initial commit of code.
author Jordan Miner <jminer7@gmail.com>
date Mon, 15 Jun 2009 22:10:48 -0500
parents
children 3ab1e9bfb88c
files dynamin/all.d dynamin/all_core.d dynamin/all_gui.d dynamin/all_painting.d dynamin/c/cairo.d dynamin/c/cairo_win32.d dynamin/c/cairo_xlib.d dynamin/c/curl.d dynamin/c/portaudio.d dynamin/c/uniscribe.d dynamin/c/vorbisfile.d dynamin/c/windows.d dynamin/c/windows_tmschema.d dynamin/c/windows_windef.d dynamin/c/x_composite.d dynamin/c/x_types.d dynamin/c/xlib.d dynamin/c/xmu.d dynamin/core/all.d dynamin/core/animated_value.d dynamin/core/benchmark.d dynamin/core/console.d dynamin/core/environment.d dynamin/core/event.d dynamin/core/file.d dynamin/core/global.d dynamin/core/list.d dynamin/core/math.d dynamin/core/settings.d dynamin/core/string.d dynamin/core/unix_console.d dynamin/core/unix_environment.d dynamin/core/windows_console.d dynamin/core/windows_environment.d dynamin/core_backend.d dynamin/gui/action.d dynamin/gui/all.d dynamin/gui/basic_theme.d dynamin/gui/button.d dynamin/gui/check_box.d dynamin/gui/clipboard.d dynamin/gui/container.d dynamin/gui/control.d dynamin/gui/cursor.d dynamin/gui/directory_dialog.d dynamin/gui/events.d dynamin/gui/file_dialog.d dynamin/gui/key.d dynamin/gui/label.d dynamin/gui/layout.d dynamin/gui/list_box.d dynamin/gui/notebook.d dynamin/gui/office_theme.d dynamin/gui/opengl_view.d dynamin/gui/radio_button.d dynamin/gui/screen.d dynamin/gui/scroll_bar.d dynamin/gui/scrollable.d dynamin/gui/text_box.d dynamin/gui/theme.d dynamin/gui/window.d dynamin/gui/windows_clipboard.d dynamin/gui/windows_cursor.d dynamin/gui/windows_theme.d dynamin/gui/windows_window.d dynamin/gui/x_clipboard.d dynamin/gui/x_cursor.d dynamin/gui/x_key.d dynamin/gui/x_window.d dynamin/gui_backend.d dynamin/lodepng/common.d dynamin/lodepng/decode.d dynamin/lodepng/encode.d dynamin/lodepng/util.d dynamin/lodepng/zlib_codec.d dynamin/painting/all.d dynamin/painting/color.d dynamin/painting/coordinates.d dynamin/painting/graphics.d
diffstat 79 files changed, 21903 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/all.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,10 @@
+module dynamin.all;
+
+//public import dynamin.core.all;
+//public import dynamin.painting.all;   //depends on core
+//public import dynamin.gui.all;        //depends on core and painting
+
+public import dynamin.all_core;
+public import dynamin.all_painting;   //depends on core
+public import dynamin.all_gui;        //depends on core and painting
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/all_core.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,38 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.all_core;
+
+public import dynamin.core.benchmark;
+public import dynamin.core.console;
+public import dynamin.core.environment;
+public import dynamin.core.event;
+public import dynamin.core.file;
+public import dynamin.core.global;
+public import dynamin.core.list;
+public import dynamin.core.math;
+public import dynamin.core.settings;
+public import dynamin.core.string;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/all_gui.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,58 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.all_gui;
+
+// listed somewhat in order of dependences
+public import dynamin.gui.key;
+public import dynamin.gui.control;
+public import dynamin.gui.container;
+public import dynamin.gui.events;
+public import dynamin.gui.window;
+public import dynamin.gui.layout;
+
+public import dynamin.gui.cursor;
+public import dynamin.gui.clipboard;
+public import dynamin.gui.screen;
+version(Windows) public import dynamin.gui.directory_dialog;
+version(Windows) public import dynamin.gui.file_dialog;
+
+public import dynamin.gui.label;
+public import dynamin.gui.button;
+public import dynamin.gui.check_box;
+public import dynamin.gui.radio_button;
+public import dynamin.gui.notebook;
+
+public import dynamin.gui.scroll_bar;
+public import dynamin.gui.scrollable;
+public import dynamin.gui.list_box;
+public import dynamin.gui.text_box;
+
+public import dynamin.gui.theme;
+public import dynamin.gui.basic_theme;
+public import dynamin.gui.windows_theme;
+
+void aHack() { Theme.current; }
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/all_painting.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,31 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.all_painting;
+
+public import dynamin.painting.color;
+public import dynamin.painting.coordinates;
+public import dynamin.painting.graphics;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/c/cairo.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,803 @@
+module dynamin.c.cairo;
+
+/*
+ * A complete binding to the core of
+ * the cairo graphics library version 1.3.16.
+ */
+
+version(build) { pragma(link, cairo); }
+
+extern(C):
+
+int CAIRO_VERSION_ENCODE(int major, int minor, int micro) {
+	return major * 10000 + minor * 100 + micro * 1;
+}
+
+int cairo_version();
+
+char* cairo_version_string();
+
+alias int cairo_bool_t;
+
+typedef void cairo_t;
+
+typedef void cairo_surface_t;
+
+struct cairo_matrix_t {
+	double xx; double yx;
+	double xy; double yy;
+	double x0; double y0;
+}
+
+typedef void cairo_pattern_t;
+
+typedef void function(void* data) cairo_destroy_func_t;
+
+struct cairo_user_data_key_t {
+	int unused;
+}
+
+alias int cairo_status_t;
+enum {
+	CAIRO_STATUS_SUCCESS = 0,
+	CAIRO_STATUS_NO_MEMORY,
+	CAIRO_STATUS_INVALID_RESTORE,
+	CAIRO_STATUS_INVALID_POP_GROUP,
+	CAIRO_STATUS_NO_CURRENT_POINT,
+	CAIRO_STATUS_INVALID_MATRIX,
+	CAIRO_STATUS_INVALID_STATUS,
+	CAIRO_STATUS_NULL_POINTER,
+	CAIRO_STATUS_INVALID_STRING,
+	CAIRO_STATUS_INVALID_PATH_DATA,
+	CAIRO_STATUS_READ_ERROR,
+	CAIRO_STATUS_WRITE_ERROR,
+	CAIRO_STATUS_SURFACE_FINISHED,
+	CAIRO_STATUS_SURFACE_TYPE_MISMATCH,
+	CAIRO_STATUS_PATTERN_TYPE_MISMATCH,
+	CAIRO_STATUS_INVALID_CONTENT,
+	CAIRO_STATUS_INVALID_FORMAT,
+	CAIRO_STATUS_INVALID_VISUAL,
+	CAIRO_STATUS_FILE_NOT_FOUND,
+	CAIRO_STATUS_INVALID_DASH,
+	CAIRO_STATUS_INVALID_DSC_COMMENT,
+	CAIRO_STATUS_INVALID_INDEX,
+	CAIRO_STATUS_CLIP_NOT_REPRESENTABLE
+}
+
+alias int cairo_content_t;
+enum {
+	CAIRO_CONTENT_COLOR       = 0x1000,
+	CAIRO_CONTENT_ALPHA       = 0x2000,
+	CAIRO_CONTENT_COLOR_ALPHA = 0x3000
+}
+
+typedef cairo_status_t function(void* closure, char* data, uint length) cairo_write_func_t;
+
+typedef cairo_status_t function(void* closure, char* data, uint length) cairo_read_func_t;
+
+cairo_t* cairo_create(cairo_surface_t* target);
+
+cairo_t* cairo_reference(cairo_t* cr);
+
+void cairo_destroy(cairo_t* cr);
+
+uint cairo_get_reference_count(cairo_t* cr);
+
+void* cairo_get_user_data(cairo_t* cr, cairo_user_data_key_t* key);
+
+cairo_status_t cairo_set_user_data(
+	cairo_t* cr,
+	cairo_user_data_key_t* key,
+	void* user_data,
+	cairo_destroy_func_t destroy);
+
+void cairo_save(cairo_t* cr);
+
+void cairo_restore(cairo_t* cr);
+
+void cairo_push_group(cairo_t* cr);
+
+void cairo_push_group_with_content(cairo_t* cr, cairo_content_t content);
+
+cairo_pattern_t* cairo_pop_group(cairo_t* cr);
+
+void cairo_pop_group_to_source(cairo_t* cr);
+
+alias int cairo_operator_t;
+enum {
+	CAIRO_OPERATOR_CLEAR,
+
+	CAIRO_OPERATOR_SOURCE,
+	CAIRO_OPERATOR_OVER,
+	CAIRO_OPERATOR_IN,
+	CAIRO_OPERATOR_OUT,
+	CAIRO_OPERATOR_ATOP,
+
+	CAIRO_OPERATOR_DEST,
+	CAIRO_OPERATOR_DEST_OVER,
+	CAIRO_OPERATOR_DEST_IN,
+	CAIRO_OPERATOR_DEST_OUT,
+	CAIRO_OPERATOR_DEST_ATOP,
+
+	CAIRO_OPERATOR_XOR,
+	CAIRO_OPERATOR_ADD,
+	CAIRO_OPERATOR_SATURATE
+}
+
+void cairo_set_operator(cairo_t* cr, cairo_operator_t op);
+
+void cairo_set_source(cairo_t* cr, cairo_pattern_t* source);
+
+void cairo_set_source_rgb(cairo_t* cr, double red, double green, double blue);
+
+void cairo_set_source_rgba(cairo_t* cr, double red, double green, double blue, double alpha);
+
+void cairo_set_source_surface(cairo_t* cr, cairo_surface_t* surface, double x, double y);
+
+void cairo_set_tolerance(cairo_t* cr, double tolerance);
+
+alias int cairo_antialias_t;
+enum {
+	CAIRO_ANTIALIAS_DEFAULT,
+	CAIRO_ANTIALIAS_NONE,
+	CAIRO_ANTIALIAS_GRAY,
+	CAIRO_ANTIALIAS_SUBPIXEL
+}
+
+void cairo_set_antialias(cairo_t* cr, cairo_antialias_t antialias);
+
+alias int cairo_fill_rule_t;
+enum {
+	CAIRO_FILL_RULE_WINDING,
+	CAIRO_FILL_RULE_EVEN_ODD
+}
+
+void cairo_set_fill_rule(cairo_t* cr, cairo_fill_rule_t fill_rule);
+
+void cairo_set_line_width(cairo_t* cr, double width);
+
+alias int cairo_line_cap_t;
+enum {
+	CAIRO_LINE_CAP_BUTT,
+	CAIRO_LINE_CAP_ROUND,
+	CAIRO_LINE_CAP_SQUARE
+}
+
+void cairo_set_line_cap(cairo_t* cr, cairo_line_cap_t line_cap);
+
+alias int cairo_line_join_t;
+enum {
+	CAIRO_LINE_JOIN_MITER,
+	CAIRO_LINE_JOIN_ROUND,
+	CAIRO_LINE_JOIN_BEVEL
+}
+
+void cairo_set_line_join(cairo_t* cr, cairo_line_join_t line_join);
+
+void cairo_set_dash(cairo_t* cr, double* dashes, int num_dashes, double offset);
+
+void cairo_set_miter_limit(cairo_t* cr, double limit);
+
+void cairo_translate(cairo_t* cr, double tx, double ty);
+
+void cairo_scale(cairo_t* cr, double sx, double sy);
+
+void cairo_rotate(cairo_t* cr, double angle);
+
+void cairo_transform(cairo_t* cr, cairo_matrix_t* matrix);
+
+void cairo_set_matrix(cairo_t* cr, cairo_matrix_t* matrix);
+
+void cairo_identity_matrix(cairo_t* cr);
+
+void cairo_user_to_device(cairo_t* cr, double* x, double* y);
+
+void cairo_user_to_device_distance(cairo_t* cr, double* dx, double* dy);
+
+void cairo_device_to_user(cairo_t* cr, double* x, double* y);
+
+void cairo_device_to_user_distance(cairo_t* cr, double* dx, double* dy);
+
+void cairo_new_path(cairo_t* cr);
+
+void cairo_move_to(cairo_t* cr, double x, double y);
+
+void cairo_new_sub_path(cairo_t* cr);
+
+void cairo_line_to(cairo_t* cr, double x, double y);
+
+void cairo_curve_to(cairo_t* cr, double x1, double y1, double x2, double y2, double x3, double y3);
+
+void cairo_arc(cairo_t* cr, double xc, double yc, double radius, double angle1, double angle2);
+
+void cairo_arc_negative(cairo_t* cr, double xc, double yc, double radius, double angle1, double angle2);
+
+void cairo_rel_move_to(cairo_t* cr, double dx, double dy);
+
+void cairo_rel_line_to(cairo_t* cr, double dx, double dy);
+
+void cairo_rel_curve_to(cairo_t* cr, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3);
+
+void cairo_rectangle(cairo_t* cr, double x, double y, double width, double height);
+
+void cairo_close_path(cairo_t* cr);
+
+void cairo_paint(cairo_t* cr);
+
+void cairo_paint_with_alpha(cairo_t* cr, double alpha);
+
+void cairo_mask(cairo_t* cr, cairo_pattern_t* pattern);
+
+void cairo_mask_surface(
+	cairo_t* cr,
+	cairo_surface_t* surface,
+	double surface_x, double surface_y);
+
+void cairo_stroke(cairo_t* cr);
+
+void cairo_stroke_preserve(cairo_t* cr);
+
+void cairo_fill(cairo_t* cr);
+
+void cairo_fill_preserve(cairo_t* cr);
+
+void cairo_copy_page(cairo_t* cr);
+
+void cairo_show_page(cairo_t* cr);
+
+cairo_bool_t cairo_in_stroke(cairo_t* cr, double x, double y);
+
+cairo_bool_t cairo_in_fill(cairo_t* cr, double x, double y);
+
+void cairo_stroke_extents(
+	cairo_t* cr,
+	double* x1, double* y1,
+	double* x2, double* y2);
+
+void cairo_fill_extents(
+	cairo_t* cr,
+	double* x1, double* y1,
+	double* x2, double* y2);
+
+void cairo_reset_clip(cairo_t* cr);
+
+void cairo_clip(cairo_t* cr);
+
+void cairo_clip_preserve(cairo_t* cr);
+
+void cairo_clip_extents(
+	cairo_t* cr,
+	double* x1, double* y1,
+	double* x2, double* y2);
+
+struct cairo_rectangle_t {
+	double x, y, width, height;
+}
+
+struct cairo_rectangle_list_t {
+	cairo_status_t     status;
+	cairo_rectangle_t* rectangles;
+	int                num_rectangles;
+}
+
+cairo_rectangle_list_t* cairo_copy_clip_rectangle_list(cairo_t* cr);
+
+void cairo_rectangle_list_destroy(cairo_rectangle_list_t* rectangle_list);
+
+typedef void cairo_scaled_font_t;
+
+typedef void cairo_font_face_t;
+
+struct cairo_glyph_t {
+	uint index;
+	double x;
+	double y;
+}
+
+struct cairo_text_extents_t {
+	double x_bearing;
+	double y_bearing;
+	double width;
+	double height;
+	double x_advance;
+	double y_advance;
+}
+
+struct cairo_font_extents_t {
+	double ascent;
+	double descent;
+	double height;
+	double max_x_advance;
+	double max_y_advance;
+}
+
+alias int cairo_font_slant_t;
+enum {
+	CAIRO_FONT_SLANT_NORMAL,
+	CAIRO_FONT_SLANT_ITALIC,
+	CAIRO_FONT_SLANT_OBLIQUE
+}
+
+alias int cairo_font_weight_t;
+enum {
+	CAIRO_FONT_WEIGHT_NORMAL,
+	CAIRO_FONT_WEIGHT_BOLD
+}
+
+alias int cairo_subpixel_order_t;
+enum {
+	CAIRO_SUBPIXEL_ORDER_DEFAULT,
+	CAIRO_SUBPIXEL_ORDER_RGB,
+	CAIRO_SUBPIXEL_ORDER_BGR,
+	CAIRO_SUBPIXEL_ORDER_VRGB,
+	CAIRO_SUBPIXEL_ORDER_VBGR
+}
+
+alias int cairo_hint_style_t;
+enum {
+	CAIRO_HINT_STYLE_DEFAULT,
+	CAIRO_HINT_STYLE_NONE,
+	CAIRO_HINT_STYLE_SLIGHT,
+	CAIRO_HINT_STYLE_MEDIUM,
+	CAIRO_HINT_STYLE_FULL
+}
+
+alias int cairo_hint_metrics_t;
+enum {
+	CAIRO_HINT_METRICS_DEFAULT,
+	CAIRO_HINT_METRICS_OFF,
+	CAIRO_HINT_METRICS_ON
+}
+
+typedef void cairo_font_options_t;
+
+cairo_font_options_t* cairo_font_options_create();
+
+cairo_font_options_t* cairo_font_options_copy(cairo_font_options_t* original);
+
+void cairo_font_options_destroy(cairo_font_options_t* options);
+
+cairo_status_t cairo_font_options_status(cairo_font_options_t* options);
+
+void cairo_font_options_merge(cairo_font_options_t* options, cairo_font_options_t* other);
+
+cairo_bool_t cairo_font_options_equal(
+	cairo_font_options_t* options,
+	cairo_font_options_t* other);
+
+uint cairo_font_options_hash(cairo_font_options_t* options);
+
+void cairo_font_options_set_antialias(cairo_font_options_t* options, cairo_antialias_t antialias);
+
+cairo_antialias_t cairo_font_options_get_antialias(cairo_font_options_t* options);
+
+void cairo_font_options_set_subpixel_order(cairo_font_options_t* options, cairo_subpixel_order_t subpixel_order);
+
+cairo_subpixel_order_t cairo_font_options_get_subpixel_order(cairo_font_options_t* options);
+
+void cairo_font_options_set_hint_style(cairo_font_options_t* options, cairo_hint_style_t hint_style);
+
+cairo_hint_style_t cairo_font_options_get_hint_style(cairo_font_options_t* options);
+
+void cairo_font_options_set_hint_metrics(cairo_font_options_t* options, cairo_hint_metrics_t hint_metrics);
+
+cairo_hint_metrics_t cairo_font_options_get_hint_metrics(cairo_font_options_t* options);
+
+void cairo_select_font_face(
+	cairo_t* cr,
+	char* family,
+	cairo_font_slant_t slant,
+	cairo_font_weight_t weight);
+
+void cairo_set_font_size(cairo_t* cr, double size);
+
+void cairo_set_font_matrix(cairo_t* cr, cairo_matrix_t* matrix);
+
+void cairo_get_font_matrix(cairo_t* cr, cairo_matrix_t* matrix);
+
+void cairo_set_font_options(cairo_t* cr, cairo_font_options_t* options);
+
+void cairo_get_font_options(cairo_t* cr, cairo_font_options_t* options);
+
+void cairo_set_font_face(cairo_t* cr, cairo_font_face_t* font_face);
+
+cairo_font_face_t* cairo_get_font_face(cairo_t* cr);
+
+void cairo_set_scaled_font(cairo_t* cr, cairo_scaled_font_t* scaled_font);
+
+cairo_scaled_font_t* cairo_get_scaled_font(cairo_t* cr);
+
+void cairo_show_text(cairo_t* cr, char* utf8);
+
+void cairo_show_glyphs(cairo_t* cr, cairo_glyph_t* glyphs, int num_glyphs);
+
+void cairo_text_path(cairo_t* cr, char* utf8);
+
+void cairo_glyph_path(cairo_t* cr, cairo_glyph_t* glyphs, int num_glyphs);
+
+void cairo_text_extents(cairo_t* cr, char* utf8, cairo_text_extents_t* extents);
+
+void cairo_glyph_extents(
+	cairo_t* cr,
+	cairo_glyph_t* glyphs, int num_glyphs,
+	cairo_text_extents_t* extents);
+
+void cairo_font_extents(cairo_t* cr, cairo_font_extents_t* extents);
+
+void cairo_text_path(cairo_t* cr, char* utf8);
+
+void cairo_glyph_path(cairo_t* cr, cairo_glyph_t* glyphs, int num_glyphs);
+
+
+cairo_font_face_t* cairo_font_face_reference(cairo_font_face_t* font_face);
+
+void cairo_font_face_destroy(cairo_font_face_t* font_face);
+
+uint cairo_font_face_get_reference_count(cairo_font_face_t* font_face);
+
+cairo_status_t cairo_font_face_status(cairo_font_face_t* font_face);
+
+alias int cairo_font_type_t;
+enum {
+	CAIRO_FONT_TYPE_TOY,
+	CAIRO_FONT_TYPE_FT,
+	CAIRO_FONT_TYPE_WIN32,
+	CAIRO_FONT_TYPE_ATSUI
+}
+
+cairo_font_type_t cairo_font_face_get_type(cairo_font_face_t* font_face);
+
+void* cairo_font_face_get_user_data(
+	cairo_font_face_t* font_face,
+	cairo_user_data_key_t* key);
+
+cairo_status_t cairo_font_face_set_user_data(
+	cairo_font_face_t* font_face,
+	cairo_user_data_key_t* key,
+	void* user_data,
+	cairo_destroy_func_t destroy);
+
+cairo_scaled_font_t* cairo_scaled_font_create(
+	cairo_font_face_t* font_face,
+	cairo_matrix_t* font_matrix,
+	cairo_matrix_t* ctm,
+	cairo_font_options_t* options);
+
+cairo_scaled_font_t* cairo_scaled_font_reference(
+	cairo_scaled_font_t* scaled_font);
+
+void cairo_scaled_font_destroy(cairo_scaled_font_t* scaled_font);
+
+uint cairo_scaled_font_get_reference_count(cairo_scaled_font_t* scaled_font);
+
+cairo_status_t cairo_scaled_font_status(cairo_scaled_font_t* scaled_font);
+
+cairo_font_type_t cairo_scaled_font_get_type(cairo_scaled_font_t* scaled_font);
+
+void* cairo_scaled_font_get_user_data(
+	cairo_scaled_font_t* scaled_font,
+	cairo_user_data_key_t* key);
+
+cairo_status_t cairo_scaled_font_set_user_data(
+	cairo_scaled_font_t* scaled_font,
+	cairo_user_data_key_t* key,
+	void* user_data,
+	cairo_destroy_func_t destroy);
+
+void cairo_scaled_font_extents(cairo_scaled_font_t* scaled_font, cairo_font_extents_t* extents);
+
+void cairo_scaled_font_text_extents(
+	cairo_scaled_font_t* scaled_font,
+	char* utf8,
+	cairo_text_extents_t* extents);
+
+void cairo_scaled_font_glyph_extents(
+	cairo_scaled_font_t* scaled_font,
+	cairo_glyph_t* glyphs, int num_glyphs,
+	cairo_text_extents_t* extents);
+
+cairo_font_face_t* cairo_scaled_font_get_font_face(cairo_scaled_font_t* scaled_font);
+
+void cairo_scaled_font_get_font_matrix(
+	cairo_scaled_font_t* scaled_font,
+	cairo_matrix_t* font_matrix);
+
+void cairo_scaled_font_get_ctm(
+	cairo_scaled_font_t* scaled_font,
+	cairo_matrix_t* ctm);
+
+void cairo_scaled_font_get_font_options(
+	cairo_scaled_font_t* scaled_font,
+	cairo_font_options_t* options);
+
+
+cairo_operator_t cairo_get_operator(cairo_t* cr);
+
+cairo_pattern_t* cairo_get_source(cairo_t* cr);
+
+double cairo_get_tolerance(cairo_t* cr);
+
+cairo_antialias_t cairo_get_antialias(cairo_t* cr);
+
+void cairo_get_current_point(cairo_t* cr, double* x, double* y);
+
+cairo_fill_rule_t cairo_get_fill_rule(cairo_t* cr);
+
+double cairo_get_line_width(cairo_t* cr);
+
+cairo_line_cap_t cairo_get_line_cap(cairo_t* cr);
+
+cairo_line_join_t cairo_get_line_join(cairo_t* cr);
+
+double cairo_get_miter_limit(cairo_t* cr);
+
+int cairo_get_dash_count(cairo_t* cr);
+
+void cairo_get_dash(cairo_t* cr, double* dashes, double* offset);
+
+void cairo_get_matrix(cairo_t* cr, cairo_matrix_t* matrix);
+
+cairo_surface_t* cairo_get_target(cairo_t* cr);
+
+cairo_surface_t* cairo_get_group_target(cairo_t* cr);
+
+alias int cairo_path_data_type_t;
+enum {
+	CAIRO_PATH_MOVE_TO,
+	CAIRO_PATH_LINE_TO,
+	CAIRO_PATH_CURVE_TO,
+	CAIRO_PATH_CLOSE_PATH
+}
+
+union cairo_path_data_t {
+	struct _header {
+		cairo_path_data_type_t type;
+		int length;
+	}
+	struct _point {
+		double x, y;
+	}
+	_header header;
+	_point point;
+}
+
+struct cairo_path_t {
+	cairo_status_t status;
+	cairo_path_data_t* data;
+	int num_data;
+}
+
+cairo_path_t* cairo_copy_path(cairo_t* cr);
+
+cairo_path_t* cairo_copy_path_flat(cairo_t* cr);
+
+void cairo_append_path(cairo_t* cr, cairo_path_t* path);
+
+void cairo_path_destroy(cairo_path_t* path);
+
+cairo_status_t cairo_status(cairo_t* cr);
+
+char* cairo_status_to_string(cairo_status_t status);
+
+
+cairo_surface_t* cairo_surface_create_similar(
+	cairo_surface_t* other,
+	cairo_content_t content,
+	int width, int height);
+
+cairo_surface_t* cairo_surface_reference(cairo_surface_t* surface);
+
+void cairo_surface_finish(cairo_surface_t* surface);
+
+void cairo_surface_destroy(cairo_surface_t* surface);
+
+uint cairo_surface_get_reference_count(cairo_surface_t* surface);
+
+cairo_status_t cairo_surface_status(cairo_surface_t* surface);
+
+alias int cairo_surface_type_t;
+enum {
+	CAIRO_SURFACE_TYPE_IMAGE,
+	CAIRO_SURFACE_TYPE_PDF,
+	CAIRO_SURFACE_TYPE_PS,
+	CAIRO_SURFACE_TYPE_XLIB,
+	CAIRO_SURFACE_TYPE_XCB,
+	CAIRO_SURFACE_TYPE_GLITZ,
+	CAIRO_SURFACE_TYPE_QUARTZ,
+	CAIRO_SURFACE_TYPE_WIN32,
+	CAIRO_SURFACE_TYPE_BEOS,
+	CAIRO_SURFACE_TYPE_DIRECTFB,
+	CAIRO_SURFACE_TYPE_SVG,
+	CAIRO_SURFACE_TYPE_OS2
+}
+
+cairo_surface_type_t cairo_surface_get_type(cairo_surface_t* surface);
+
+cairo_content_t cairo_surface_get_content(cairo_surface_t* surface);
+
+void* cairo_surface_get_user_data(
+	cairo_surface_t* surface,
+	cairo_user_data_key_t* key);
+
+cairo_status_t cairo_surface_set_user_data(
+	cairo_surface_t* surface,
+	cairo_user_data_key_t* key,
+	void* user_data,
+	cairo_destroy_func_t destroy);
+
+void cairo_surface_get_font_options(cairo_surface_t* surface, cairo_font_options_t* options);
+
+void cairo_surface_flush(cairo_surface_t* surface);
+
+void cairo_surface_mark_dirty(cairo_surface_t* surface);
+
+void cairo_surface_mark_dirty_rectangle(cairo_surface_t* surface, int x, int y, int width, int height);
+
+void cairo_surface_set_device_offset(cairo_surface_t* surface, double x_offset, double y_offset);
+
+void cairo_surface_get_device_offset(
+	cairo_surface_t* surface,
+	double* x_offset, double* y_offset);
+
+void cairo_surface_set_fallback_resolution(
+	cairo_surface_t* surface,
+	double x_pixels_per_inch, double y_pixels_per_inch);
+
+alias int cairo_format_t;
+enum {
+	CAIRO_FORMAT_ARGB32,
+	CAIRO_FORMAT_RGB24,
+	CAIRO_FORMAT_A8,
+	CAIRO_FORMAT_A1
+}
+
+cairo_surface_t* cairo_image_surface_create(cairo_format_t format, int width, int height);
+
+cairo_surface_t* cairo_image_surface_create_for_data(
+	char* data,
+	cairo_format_t format,
+	int width, int height, int stride);
+
+char* cairo_image_surface_get_data(cairo_surface_t* surface);
+
+cairo_format_t cairo_image_surface_get_format(cairo_surface_t* surface);
+
+int cairo_image_surface_get_width(cairo_surface_t* surface);
+
+int cairo_image_surface_get_height(cairo_surface_t* surface);
+
+int cairo_image_surface_get_stride(cairo_surface_t* surface);
+
+
+cairo_pattern_t* cairo_pattern_create_rgb(double red, double green, double blue);
+
+cairo_pattern_t* cairo_pattern_create_rgba(double red, double green, double blue, double alpha);
+
+cairo_pattern_t* cairo_pattern_create_for_surface(cairo_surface_t* surface);
+
+cairo_pattern_t* cairo_pattern_create_linear(double x0, double y0, double x1, double y1);
+
+cairo_pattern_t* cairo_pattern_create_radial(double cx0, double cy0, double radius0, double cx1, double cy1, double radius1);
+
+cairo_pattern_t* cairo_pattern_reference(cairo_pattern_t* pattern);
+
+void cairo_pattern_destroy(cairo_pattern_t* pattern);
+
+uint cairo_pattern_get_reference_count(cairo_pattern_t* pattern);
+
+cairo_status_t cairo_pattern_status(cairo_pattern_t* pattern);
+
+void* cairo_pattern_get_user_data(
+	cairo_pattern_t* pattern,
+	cairo_user_data_key_t* key);
+
+cairo_status_t cairo_pattern_set_user_data(
+	cairo_pattern_t* pattern,
+	cairo_user_data_key_t* key,
+	void* user_data,
+	cairo_destroy_func_t destroy);
+
+alias int cairo_pattern_type_t;
+enum {
+	CAIRO_PATTERN_TYPE_SOLID,
+	CAIRO_PATTERN_TYPE_SURFACE,
+	CAIRO_PATTERN_TYPE_LINEAR,
+	CAIRO_PATTERN_TYPE_RADIAL
+}
+
+cairo_pattern_type_t cairo_pattern_get_type(cairo_pattern_t* pattern);
+
+void cairo_pattern_add_color_stop_rgb(
+	cairo_pattern_t* pattern, double offset,
+	double red, double green, double blue);
+
+void cairo_pattern_add_color_stop_rgba(
+	cairo_pattern_t* pattern, double offset,
+	double red, double green, double blue, double alpha);
+
+void cairo_pattern_set_matrix(cairo_pattern_t* pattern, cairo_matrix_t* matrix);
+
+void cairo_pattern_get_matrix(cairo_pattern_t* pattern, cairo_matrix_t* matrix);
+
+alias int cairo_extend_t;
+enum {
+	CAIRO_EXTEND_NONE,
+	CAIRO_EXTEND_REPEAT,
+	CAIRO_EXTEND_REFLECT,
+	CAIRO_EXTEND_PAD
+}
+
+void cairo_pattern_set_extend(cairo_pattern_t* pattern, cairo_extend_t extend);
+
+cairo_extend_t cairo_pattern_get_extend(cairo_pattern_t* pattern);
+
+alias int cairo_filter_t;
+enum {
+	CAIRO_FILTER_FAST,
+	CAIRO_FILTER_GOOD,
+	CAIRO_FILTER_BEST,
+	CAIRO_FILTER_NEAREST,
+	CAIRO_FILTER_BILINEAR,
+	CAIRO_FILTER_GAUSSIAN
+}
+
+void cairo_pattern_set_filter(cairo_pattern_t* pattern, cairo_filter_t filter);
+
+cairo_filter_t cairo_pattern_get_filter(cairo_pattern_t* pattern);
+
+cairo_status_t cairo_pattern_get_rgba(
+	cairo_pattern_t* pattern,
+	double* red, double* green,
+	double* blue, double* alpha);
+
+cairo_status_t cairo_pattern_get_surface(
+	cairo_pattern_t* pattern,
+	cairo_surface_t** surface);
+
+cairo_status_t cairo_pattern_get_color_stop_rgba(
+	cairo_pattern_t* pattern,
+	int index, double* offset,
+	double* red, double* green,
+	double* blue, double* alpha);
+
+cairo_status_t cairo_pattern_get_color_stop_count(
+	cairo_pattern_t* pattern,
+	int* count);
+
+cairo_status_t cairo_pattern_get_linear_points(
+	cairo_pattern_t* pattern,
+	double* x0, double* y0,
+	double* x1, double* y1);
+
+cairo_status_t cairo_pattern_get_radial_circles(
+	cairo_pattern_t* pattern,
+	double* x0, double* y0, double* r0,
+	double* x1, double* y1, double* r1);
+
+
+void cairo_matrix_init(cairo_matrix_t* matrix, double xx, double yx, double xy, double yy, double x0, double y0);
+
+void cairo_matrix_init_identity(cairo_matrix_t* matrix);
+
+void cairo_matrix_init_translate(cairo_matrix_t* matrix, double tx, double ty);
+
+void cairo_matrix_init_scale(cairo_matrix_t* matrix, double sx, double sy);
+
+void cairo_matrix_init_rotate(cairo_matrix_t* matrix, double radians);
+
+void cairo_matrix_translate(cairo_matrix_t* matrix, double tx, double ty);
+
+void cairo_matrix_scale(cairo_matrix_t* matrix, double sx, double sy);
+
+void cairo_matrix_rotate(cairo_matrix_t* matrix, double radians);
+
+cairo_status_t cairo_matrix_invert(cairo_matrix_t* matrix);
+
+void cairo_matrix_multiply(cairo_matrix_t* result, cairo_matrix_t* a, cairo_matrix_t* b);
+
+void cairo_matrix_transform_distance(cairo_matrix_t* matrix, double* dx, double* dy);
+
+void cairo_matrix_transform_point(cairo_matrix_t* matrix, double* x, double* y);
+
+void cairo_debug_reset_static_data();
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/c/cairo_win32.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,48 @@
+module dynamin.c.cairo_win32;
+
+/*
+ * A complete binding to the win32 backend of
+ * the cairo graphics library version 1.3.16.
+ */
+
+import dynamin.c.cairo;
+import dynamin.c.windows;
+
+extern(C):
+
+cairo_surface_t* cairo_win32_surface_create(HDC hdc);
+
+cairo_surface_t* cairo_win32_surface_create_with_ddb(
+	HDC hdc,
+	cairo_format_t format,
+	int width, int height);
+
+cairo_surface_t* cairo_win32_surface_create_with_dib(
+	cairo_format_t format,
+	int width, int height);
+
+HDC cairo_win32_surface_get_dc(cairo_surface_t* surface);
+
+cairo_surface_t* cairo_win32_surface_get_image(cairo_surface_t* surface);
+
+cairo_font_face_t* cairo_win32_font_face_create_for_logfontw(LOGFONTW* logfont);
+
+cairo_font_face_t* cairo_win32_font_face_create_for_hfont(HFONT font);
+
+cairo_status_t cairo_win32_scaled_font_select_font(
+	cairo_scaled_font_t* scaled_font,
+	HDC hdc);
+
+void cairo_win32_scaled_font_done_font(cairo_scaled_font_t* scaled_font);
+
+double cairo_win32_scaled_font_get_metrics_factor(
+	cairo_scaled_font_t* scaled_font);
+
+void cairo_win32_scaled_font_get_logical_to_device(
+	cairo_scaled_font_t* scaled_font,
+	cairo_matrix_t* logical_to_device);
+
+void cairo_win32_scaled_font_get_device_to_logical(
+	cairo_scaled_font_t* scaled_font,
+	cairo_matrix_t* device_to_logical);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/c/cairo_xlib.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,49 @@
+module dynamin.c.cairo_xlib;
+
+/*
+ * A complete binding to the xlib backend of
+ * the cairo graphics library version 1.3.16.
+ */
+
+import dynamin.c.cairo;
+import dynamin.c.xlib;
+import dynamin.c.x_types;
+
+extern(C):
+
+cairo_surface_t* cairo_xlib_surface_create(
+	Display* dpy,
+	Drawable drawable,
+	Visual* visual,
+	int width,
+	int height);
+
+cairo_surface_t* cairo_xlib_surface_create_for_bitmap(
+	Display* dpy,
+	Pixmap bitmap,
+	Screen* screen,
+	int width, int height);
+
+void cairo_xlib_surface_set_size(
+	cairo_surface_t* surface,
+	int width, int height);
+
+void cairo_xlib_surface_set_drawable(
+	cairo_surface_t* surface,
+	Drawable drawable,
+	int width, int height);
+
+Display* cairo_xlib_surface_get_display(cairo_surface_t* surface);
+
+Drawable cairo_xlib_surface_get_drawable(cairo_surface_t* surface);
+
+Screen* cairo_xlib_surface_get_screen(cairo_surface_t* surface);
+
+Visual* cairo_xlib_surface_get_visual(cairo_surface_t* surface);
+
+int cairo_xlib_surface_get_depth(cairo_surface_t* surface);
+
+int cairo_xlib_surface_get_width(cairo_surface_t* surface);
+
+int cairo_xlib_surface_get_height(cairo_surface_t* surface);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/c/curl.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,707 @@
+module dynamin.c.curl;
+
+/*
+ * A largely complete binding to the cURL library version 7.15.
+ */
+
+version(build) { pragma(link, curl); }
+
+extern(C):
+
+alias void CURL;
+
+CURL* curl_easy_init();
+
+CURLcode curl_easy_setopt(CURL* curl, CURLoption option, ...);
+
+CURLcode curl_easy_perform(CURL* curl);
+
+void curl_easy_cleanup(CURL* curl);
+
+CURLcode curl_easy_getinfo(CURL* curl, CURLINFO info, ...);
+
+CURL* curl_easy_duphandle(CURL* curl);
+
+void curl_easy_reset(CURL* curl);
+
+const int HTTPPOST_FILENAME    = 1 << 0;
+const int HTTPPOST_READFILE    = 1 << 1;
+const int HTTPPOST_PTRNAME     = 1 << 2;
+const int HTTPPOST_PTRCONTENTS = 1 << 3;
+const int HTTPPOST_BUFFER      = 1 << 4;
+const int HTTPPOST_PTRBUFFER   = 1 << 5;
+
+struct curl_httppost {
+	curl_httppost* next;
+	char* name;
+	int namelength;
+	char* contents;
+	int contentslength;
+	char* buffer;
+	int bufferlength;
+	char* contenttype;
+	curl_slist* contentheader;
+	curl_httppost* more;
+	int flags;
+	char* showfilename;
+}
+
+alias int function(
+	void* clientp,
+	double dltotal,
+	double dlnow,
+	double ultotal,
+	double ulnow) curl_progress_callback;
+
+const int CURL_MAX_WRITE_SIZE = 16384;
+
+alias size_t function(
+	char* buffer,
+	size_t size,
+	size_t nitems,
+	void* outstream) curl_write_callback;
+
+const int CURL_READFUNC_ABORT = 0x10000000;
+
+alias size_t function(
+	char* buffer,
+	size_t size,
+	size_t nitems,
+	void* instream) curl_read_callback;
+
+alias int curlioerr;
+enum {
+	CURLIOE_OK,
+	CURLIOE_UNKNOWNCMD,
+	CURLIOE_FAILRESTART,
+
+	CURLIOE_LAST
+}
+
+alias int curliocmd;
+enum {
+	CURLIOCMD_NOP,
+	CURLIOCMD_RESTARTREAD,
+
+	CURLIOCMD_LAST
+}
+
+alias curlioerr function(
+	CURL* handle,
+	int cmd,
+	void* clientp) curl_ioctl_callback;
+
+alias void* function(size_t size) curl_malloc_callback;
+alias void function(void* ptr) curl_free_callback;
+alias void* function(void* ptr, size_t size) curl_realloc_callback;
+alias char* function(char* str) curl_strdup_callback;
+alias void* function(size_t nmemb, size_t size) curl_calloc_callback;
+
+alias int curl_infotype;
+enum {
+	CURLINFO_TEXT = 0,
+	CURLINFO_HEADER_IN,
+	CURLINFO_HEADER_OUT,
+	CURLINFO_DATA_IN,
+	CURLINFO_DATA_OUT,
+	CURLINFO_SSL_DATA_IN,
+	CURLINFO_SSL_DATA_OUT,
+
+	CURLINFO_END
+}
+
+alias int function(
+	CURL* handle,
+	curl_infotype type,
+	char* data,
+	size_t size,
+	void* userptr) curl_debug_callback;
+
+alias int CURLcode;
+enum {
+	CURLE_OK = 0,
+	CURLE_UNSUPPORTED_PROTOCOL,
+	CURLE_FAILED_INIT,
+	CURLE_URL_MALFORMAT,
+	CURLE_URL_MALFORMAT_USER,
+	CURLE_COULDNT_RESOLVE_PROXY,
+	CURLE_COULDNT_RESOLVE_HOST,
+	CURLE_COULDNT_CONNECT,
+	CURLE_FTP_WEIRD_SERVER_REPLY,
+	CURLE_FTP_ACCESS_DENIED,
+	CURLE_FTP_USER_PASSWORD_INCORRECT,
+	CURLE_FTP_WEIRD_PASS_REPLY,
+	CURLE_FTP_WEIRD_USER_REPLY,
+	CURLE_FTP_WEIRD_PASV_REPLY,
+	CURLE_FTP_WEIRD_227_FORMAT,
+	CURLE_FTP_CANT_GET_HOST,
+	CURLE_FTP_CANT_RECONNECT,
+	CURLE_FTP_COULDNT_SET_BINARY,
+	CURLE_PARTIAL_FILE,
+	CURLE_FTP_COULDNT_RETR_FILE,
+	CURLE_FTP_WRITE_ERROR,
+	CURLE_FTP_QUOTE_ERROR,
+	CURLE_HTTP_RETURNED_ERROR,
+	CURLE_WRITE_ERROR,
+	CURLE_MALFORMAT_USER,
+	CURLE_FTP_COULDNT_STOR_FILE,
+	CURLE_READ_ERROR,
+	CURLE_OUT_OF_MEMORY,
+	CURLE_OPERATION_TIMEOUTED,
+	CURLE_FTP_COULDNT_SET_ASCII,
+	CURLE_FTP_PORT_FAILED,
+	CURLE_FTP_COULDNT_USE_REST,
+	CURLE_FTP_COULDNT_GET_SIZE,
+	CURLE_HTTP_RANGE_ERROR,
+	CURLE_HTTP_POST_ERROR,
+	CURLE_SSL_CONNECT_ERROR,
+	CURLE_BAD_DOWNLOAD_RESUME,
+	CURLE_FILE_COULDNT_READ_FILE,
+	CURLE_LDAP_CANNOT_BIND,
+	CURLE_LDAP_SEARCH_FAILED,
+	CURLE_LIBRARY_NOT_FOUND,
+	CURLE_FUNCTION_NOT_FOUND,
+	CURLE_ABORTED_BY_CALLBACK,
+	CURLE_BAD_FUNCTION_ARGUMENT,
+	CURLE_BAD_CALLING_ORDER,
+	CURLE_INTERFACE_FAILED,
+	CURLE_BAD_PASSWORD_ENTERED,
+	CURLE_TOO_MANY_REDIRECTS ,
+	CURLE_UNKNOWN_TELNET_OPTION,
+	CURLE_TELNET_OPTION_SYNTAX ,
+	CURLE_OBSOLETE,
+	CURLE_SSL_PEER_CERTIFICATE,
+	CURLE_GOT_NOTHING,
+	CURLE_SSL_ENGINE_NOTFOUND,
+	CURLE_SSL_ENGINE_SETFAILED,
+	CURLE_SEND_ERROR,
+	CURLE_RECV_ERROR,
+	CURLE_SHARE_IN_USE,
+	CURLE_SSL_CERTPROBLEM,
+	CURLE_SSL_CIPHER,
+	CURLE_SSL_CACERT,
+	CURLE_BAD_CONTENT_ENCODING,
+	CURLE_LDAP_INVALID_URL,
+	CURLE_FILESIZE_EXCEEDED,
+	CURLE_FTP_SSL_FAILED,
+	CURLE_SEND_FAIL_REWIND,
+	CURLE_SSL_ENGINE_INITFAILED,
+	CURLE_LOGIN_DENIED,
+	CURLE_TFTP_NOTFOUND,
+	CURLE_TFTP_PERM,
+	CURLE_TFTP_DISKFULL,
+	CURLE_TFTP_ILLEGAL,
+	CURLE_TFTP_UNKNOWNID,
+	CURLE_TFTP_EXISTS,
+	CURLE_TFTP_NOSUCHUSER,
+	CURLE_CONV_FAILED,
+	CURLE_CONV_REQD,
+
+	CURL_LAST
+}
+
+alias CURLcode function(char* buffer, size_t length) curl_conv_callback;
+alias CURLcode function(
+	CURL* curl,
+	void* ssl_ctx,
+	void* userptr) curl_ssl_ctx_callback;
+
+alias int curl_proxytype;
+enum {
+	CURLPROXY_HTTP = 0,
+	CURLPROXY_SOCKS4 = 4,
+	CURLPROXY_SOCKS5 = 5
+}
+
+const int CURLAUTH_NONE         = 0;
+const int CURLAUTH_BASIC        = 1 << 0;
+const int CURLAUTH_DIGEST       = 1 << 1;
+const int CURLAUTH_GSSNEGOTIATE = 1 << 2;
+const int CURLAUTH_NTLM         = 1 << 3;
+const int CURLAUTH_ANY          = ~0;
+const int CURLAUTH_ANYSAFE      = ~CURLAUTH_BASIC;
+
+alias int curl_ftpssl;
+enum {
+	CURLFTPSSL_NONE,
+	CURLFTPSSL_TRY,
+	CURLFTPSSL_CONTROL,
+	CURLFTPSSL_ALL,
+
+	CURLFTPSSL_LAST
+}
+
+alias int curl_ftpauth;
+enum {
+	CURLFTPAUTH_DEFAULT,
+	CURLFTPAUTH_SSL,
+	CURLFTPAUTH_TLS,
+
+	CURLFTPAUTH_LAST
+}
+
+alias int curl_ftpmethod;
+enum {
+	CURLFTPMETHOD_DEFAULT,
+	CURLFTPMETHOD_MULTICWD,
+	CURLFTPMETHOD_NOCWD,
+	CURLFTPMETHOD_SINGLECWD,
+
+	CURLFTPMETHOD_LAST
+}
+
+const int CURLOPTTYPE_LONG =          0;     // usually 32 bits
+const int CURLOPTTYPE_OBJECTPOINT =   10000; // a pointer
+const int CURLOPTTYPE_FUNCTIONPOINT = 20000; // a function pointer
+const int CURLOPTTYPE_OFF_T =         30000; // usually 64 bits
+
+alias int CURLoption;
+enum {
+	CURLOPT_FILE             = CURLOPTTYPE_OBJECTPOINT + 1,
+	CURLOPT_URL              = CURLOPTTYPE_OBJECTPOINT + 2,
+	CURLOPT_PORT             = CURLOPTTYPE_LONG + 3,
+	CURLOPT_PROXY            = CURLOPTTYPE_OBJECTPOINT + 4,
+	CURLOPT_USERPWD          = CURLOPTTYPE_OBJECTPOINT + 5,
+	CURLOPT_PROXYUSERPWD     = CURLOPTTYPE_OBJECTPOINT + 6,
+	CURLOPT_RANGE            = CURLOPTTYPE_OBJECTPOINT + 7,
+	CURLOPT_INFILE           = CURLOPTTYPE_OBJECTPOINT + 9,
+	CURLOPT_ERRORBUFFER      = CURLOPTTYPE_OBJECTPOINT + 10,
+	CURLOPT_WRITEFUNCTION    = CURLOPTTYPE_FUNCTIONPOINT + 11,
+	CURLOPT_READFUNCTION     = CURLOPTTYPE_FUNCTIONPOINT + 12,
+	CURLOPT_TIMEOUT          = CURLOPTTYPE_LONG + 13,
+	CURLOPT_INFILESIZE       = CURLOPTTYPE_LONG + 14,
+	CURLOPT_POSTFIELDS       = CURLOPTTYPE_OBJECTPOINT + 15,
+	CURLOPT_REFERER          = CURLOPTTYPE_OBJECTPOINT + 16,
+	CURLOPT_FTPPORT          = CURLOPTTYPE_OBJECTPOINT + 17,
+	CURLOPT_USERAGENT        = CURLOPTTYPE_OBJECTPOINT + 18,
+	CURLOPT_LOW_SPEED_LIMIT  = CURLOPTTYPE_LONG + 19,
+	CURLOPT_LOW_SPEED_TIME   = CURLOPTTYPE_LONG + 20,
+	CURLOPT_RESUME_FROM      = CURLOPTTYPE_LONG + 21,
+	CURLOPT_COOKIE           = CURLOPTTYPE_OBJECTPOINT + 22,
+	CURLOPT_HTTPHEADER       = CURLOPTTYPE_OBJECTPOINT + 23,
+	CURLOPT_HTTPPOST         = CURLOPTTYPE_OBJECTPOINT + 24,
+	CURLOPT_SSLCERT          = CURLOPTTYPE_OBJECTPOINT + 25,
+	CURLOPT_SSLCERTPASSWD    = CURLOPTTYPE_OBJECTPOINT + 26,
+	CURLOPT_SSLKEYPASSWD     = CURLOPTTYPE_OBJECTPOINT + 26,
+	CURLOPT_CRLF             = CURLOPTTYPE_LONG + 27,
+	CURLOPT_QUOTE            = CURLOPTTYPE_OBJECTPOINT + 28,
+	CURLOPT_WRITEHEADER      = CURLOPTTYPE_OBJECTPOINT + 29,
+	CURLOPT_COOKIEFILE       = CURLOPTTYPE_OBJECTPOINT + 31,
+	CURLOPT_SSLVERSION       = CURLOPTTYPE_LONG + 32,
+	CURLOPT_TIMECONDITION    = CURLOPTTYPE_LONG + 33,
+	CURLOPT_TIMEVALUE        = CURLOPTTYPE_LONG + 34,
+	CURLOPT_CUSTOMREQUEST    = CURLOPTTYPE_OBJECTPOINT + 36,
+	CURLOPT_STDERR           = CURLOPTTYPE_OBJECTPOINT + 37,
+	CURLOPT_POSTQUOTE        = CURLOPTTYPE_OBJECTPOINT + 39,
+	CURLOPT_WRITEINFO        = CURLOPTTYPE_OBJECTPOINT + 40,
+	CURLOPT_VERBOSE          = CURLOPTTYPE_LONG + 41,
+	CURLOPT_HEADER           = CURLOPTTYPE_LONG + 42,
+	CURLOPT_NOPROGRESS       = CURLOPTTYPE_LONG + 43,
+	CURLOPT_NOBODY           = CURLOPTTYPE_LONG + 44,
+	CURLOPT_FAILONERROR      = CURLOPTTYPE_LONG + 45,
+	CURLOPT_UPLOAD           = CURLOPTTYPE_LONG + 46,
+	CURLOPT_POST             = CURLOPTTYPE_LONG + 47,
+	CURLOPT_FTPLISTONLY      = CURLOPTTYPE_LONG + 48,
+	CURLOPT_FTPAPPEND        = CURLOPTTYPE_LONG + 50,
+	CURLOPT_NETRC            = CURLOPTTYPE_LONG + 51,
+	CURLOPT_FOLLOWLOCATION   = CURLOPTTYPE_LONG + 52,
+	CURLOPT_TRANSFERTEXT     = CURLOPTTYPE_LONG + 53,
+	CURLOPT_PUT              = CURLOPTTYPE_LONG + 54,
+	CURLOPT_PROGRESSFUNCTION = CURLOPTTYPE_FUNCTIONPOINT + 56,
+	CURLOPT_PROGRESSDATA     = CURLOPTTYPE_OBJECTPOINT + 57,
+	CURLOPT_AUTOREFERER      = CURLOPTTYPE_LONG + 58,
+	CURLOPT_PROXYPORT        = CURLOPTTYPE_LONG + 59,
+	CURLOPT_POSTFIELDSIZE    = CURLOPTTYPE_LONG + 60,
+	CURLOPT_HTTPPROXYTUNNEL  = CURLOPTTYPE_LONG + 61,
+	CURLOPT_INTERFACE        = CURLOPTTYPE_OBJECTPOINT + 62,
+	CURLOPT_KRB4LEVEL        = CURLOPTTYPE_OBJECTPOINT + 63,
+	CURLOPT_SSL_VERIFYPEER   = CURLOPTTYPE_LONG + 64,
+	CURLOPT_CAINFO           = CURLOPTTYPE_OBJECTPOINT + 65,
+	CURLOPT_MAXREDIRS        = CURLOPTTYPE_LONG + 68,
+	CURLOPT_FILETIME         = CURLOPTTYPE_LONG + 69,
+	CURLOPT_TELNETOPTIONS    = CURLOPTTYPE_OBJECTPOINT + 70,
+	CURLOPT_MAXCONNECTS      = CURLOPTTYPE_LONG + 71,
+	CURLOPT_CLOSEPOLICY      = CURLOPTTYPE_LONG + 72,
+	CURLOPT_FRESH_CONNECT    = CURLOPTTYPE_LONG + 74,
+	CURLOPT_FORBID_REUSE     = CURLOPTTYPE_LONG + 75,
+	CURLOPT_RANDOM_FILE      = CURLOPTTYPE_OBJECTPOINT + 76,
+	CURLOPT_EGDSOCKET        = CURLOPTTYPE_OBJECTPOINT + 77,
+	CURLOPT_CONNECTTIMEOUT   = CURLOPTTYPE_LONG + 78,
+	CURLOPT_HEADERFUNCTION   = CURLOPTTYPE_FUNCTIONPOINT + 79,
+	CURLOPT_HTTPGET          = CURLOPTTYPE_LONG + 80,
+	CURLOPT_SSL_VERIFYHOST   = CURLOPTTYPE_LONG + 81,
+	CURLOPT_COOKIEJAR        = CURLOPTTYPE_OBJECTPOINT + 82,
+	CURLOPT_SSL_CIPHER_LIST  = CURLOPTTYPE_OBJECTPOINT + 83,
+	CURLOPT_HTTP_VERSION     = CURLOPTTYPE_LONG + 84,
+	CURLOPT_FTP_USE_EPSV     = CURLOPTTYPE_LONG + 85,
+	CURLOPT_SSLCERTTYPE      = CURLOPTTYPE_OBJECTPOINT + 86,
+	CURLOPT_SSLKEY           = CURLOPTTYPE_OBJECTPOINT + 87,
+	CURLOPT_SSLKEYTYPE       = CURLOPTTYPE_OBJECTPOINT + 88,
+	CURLOPT_SSLENGINE        = CURLOPTTYPE_OBJECTPOINT + 89,
+	CURLOPT_SSLENGINE_DEFAULT = CURLOPTTYPE_LONG + 90,
+	CURLOPT_DNS_USE_GLOBAL_CACHE = CURLOPTTYPE_LONG + 91,
+	CURLOPT_DNS_CACHE_TIMEOUT = CURLOPTTYPE_LONG + 92,
+	CURLOPT_PREQUOTE         = CURLOPTTYPE_OBJECTPOINT + 93,
+	CURLOPT_DEBUGFUNCTION    = CURLOPTTYPE_FUNCTIONPOINT + 94,
+	CURLOPT_DEBUGDATA        = CURLOPTTYPE_OBJECTPOINT + 95,
+	CURLOPT_COOKIESESSION    = CURLOPTTYPE_LONG + 96,
+	CURLOPT_CAPATH           = CURLOPTTYPE_OBJECTPOINT + 97,
+	CURLOPT_BUFFERSIZE       = CURLOPTTYPE_LONG + 98,
+	CURLOPT_NOSIGNAL         = CURLOPTTYPE_LONG + 99,
+	CURLOPT_SHARE            = CURLOPTTYPE_OBJECTPOINT + 100,
+	CURLOPT_PROXYTYPE        = CURLOPTTYPE_LONG + 101,
+	CURLOPT_ENCODING         = CURLOPTTYPE_OBJECTPOINT + 102,
+	CURLOPT_PRIVATE          = CURLOPTTYPE_OBJECTPOINT + 103,
+	CURLOPT_HTTP200ALIASES   = CURLOPTTYPE_OBJECTPOINT + 104,
+	CURLOPT_UNRESTRICTED_AUTH = CURLOPTTYPE_LONG + 105,
+	CURLOPT_FTP_USE_EPRT     = CURLOPTTYPE_LONG + 106,
+	CURLOPT_HTTPAUTH         = CURLOPTTYPE_LONG + 107,
+	CURLOPT_SSL_CTX_FUNCTION = CURLOPTTYPE_FUNCTIONPOINT + 108,
+	CURLOPT_SSL_CTX_DATA     = CURLOPTTYPE_OBJECTPOINT + 109,
+	CURLOPT_FTP_CREATE_MISSING_DIRS = CURLOPTTYPE_LONG + 110,
+	CURLOPT_PROXYAUTH        = CURLOPTTYPE_LONG + 111,
+	CURLOPT_FTP_RESPONSE_TIMEOUT = CURLOPTTYPE_LONG + 112,
+	CURLOPT_IPRESOLVE        = CURLOPTTYPE_LONG + 113,
+	CURLOPT_MAXFILESIZE      = CURLOPTTYPE_LONG + 114,
+	CURLOPT_INFILESIZE_LARGE = CURLOPTTYPE_OFF_T + 115,
+	CURLOPT_RESUME_FROM_LARGE = CURLOPTTYPE_OFF_T + 116,
+	CURLOPT_MAXFILESIZE_LARGE = CURLOPTTYPE_OFF_T + 117,
+	CURLOPT_NETRC_FILE       = CURLOPTTYPE_OBJECTPOINT + 118,
+	CURLOPT_FTP_SSL          = CURLOPTTYPE_LONG + 119,
+	CURLOPT_POSTFIELDSIZE_LARGE = CURLOPTTYPE_OFF_T + 120,
+	CURLOPT_TCP_NODELAY      = CURLOPTTYPE_LONG + 121,
+	CURLOPT_SOURCE_USERPWD   = CURLOPTTYPE_OBJECTPOINT + 123,
+	CURLOPT_SOURCE_PREQUOTE  = CURLOPTTYPE_OBJECTPOINT + 127,
+	CURLOPT_SOURCE_POSTQUOTE = CURLOPTTYPE_OBJECTPOINT + 128,
+	CURLOPT_FTPSSLAUTH       = CURLOPTTYPE_LONG + 129,
+	CURLOPT_IOCTLFUNCTION    = CURLOPTTYPE_FUNCTIONPOINT + 130,
+	CURLOPT_IOCTLDATA        = CURLOPTTYPE_OBJECTPOINT + 131,
+	CURLOPT_SOURCE_URL       = CURLOPTTYPE_OBJECTPOINT + 132,
+	CURLOPT_SOURCE_QUOTE     = CURLOPTTYPE_OBJECTPOINT + 133,
+	CURLOPT_FTP_ACCOUNT      = CURLOPTTYPE_OBJECTPOINT + 134,
+	CURLOPT_COOKIELIST       = CURLOPTTYPE_OBJECTPOINT + 135,
+	CURLOPT_IGNORE_CONTENT_LENGTH = CURLOPTTYPE_LONG + 136,
+	CURLOPT_FTP_SKIP_PASV_IP = CURLOPTTYPE_LONG + 137,
+	CURLOPT_FTP_FILEMETHOD   = CURLOPTTYPE_LONG + 138,
+	CURLOPT_LOCALPORT        = CURLOPTTYPE_LONG + 139,
+	CURLOPT_LOCALPORTRANGE   = CURLOPTTYPE_LONG + 140,
+	CURLOPT_CONNECT_ONLY     = CURLOPTTYPE_LONG + 141,
+	CURLOPT_CONV_FROM_NETWORK_FUNCTION = CURLOPTTYPE_FUNCTIONPOINT + 142,
+	CURLOPT_CONV_TO_NETWORK_FUNCTION = CURLOPTTYPE_FUNCTIONPOINT + 143,
+	CURLOPT_CONV_FROM_UTF8_FUNCTION = CURLOPTTYPE_FUNCTIONPOINT + 144,
+	CURLOPT_MAX_SEND_SPEED_LARGE = CURLOPTTYPE_OFF_T + 145,
+	CURLOPT_MAX_RECV_SPEED_LARGE = CURLOPTTYPE_OFF_T + 146,
+	CURLOPT_FTP_ALTERNATIVE_TO_USER = CURLOPTTYPE_OBJECTPOINT + 147,
+
+	CURLOPT_LASTENTRY
+}
+
+const int CURL_IPRESOLVE_WHATEVER = 0;
+const int CURL_IPRESOLVE_V4 = 1;
+const int CURL_IPRESOLVE_V6 = 2;
+const int CURLOPT_WRITEDATA = CURLOPT_FILE;
+const int CURLOPT_READDATA = CURLOPT_INFILE;
+const int CURLOPT_HEADERDATA = CURLOPT_WRITEHEADER;
+
+enum {
+	CURL_HTTP_VERSION_NONE,
+	CURL_HTTP_VERSION_1_0,
+	CURL_HTTP_VERSION_1_1,
+
+	CURL_HTTP_VERSION_LAST
+}
+
+enum {
+	CURL_NETRC_IGNORED,
+	CURL_NETRC_OPTIONAL,
+	CURL_NETRC_REQUIRED,
+
+	CURL_NETRC_LAST
+}
+
+enum {
+	CURL_SSLVERSION_DEFAULT,
+	CURL_SSLVERSION_TLSv1,
+	CURL_SSLVERSION_SSLv2,
+	CURL_SSLVERSION_SSLv3,
+
+	CURL_SSLVERSION_LAST
+}
+
+enum {
+	CURL_TIMECOND_NONE,
+	CURL_TIMECOND_IFMODSINCE,
+	CURL_TIMECOND_IFUNMODSINCE,
+	CURL_TIMECOND_LASTMOD,
+
+	CURL_TIMECOND_LAST
+}
+
+alias int CURLformoption;
+enum {
+	CURLFORM_NOTHING,
+	CURLFORM_COPYNAME,
+	CURLFORM_PTRNAME,
+	CURLFORM_NAMELENGTH,
+	CURLFORM_COPYCONTENTS,
+	CURLFORM_PTRCONTENTS,
+	CURLFORM_CONTENTSLENGTH,
+	CURLFORM_FILECONTENT,
+	CURLFORM_ARRAY,
+	CURLFORM_OBSOLETE,
+	CURLFORM_FILE,
+	CURLFORM_BUFFER,
+	CURLFORM_BUFFERPTR,
+	CURLFORM_BUFFERLENGTH,
+	CURLFORM_CONTENTTYPE,
+	CURLFORM_CONTENTHEADER,
+	CURLFORM_FILENAME,
+	CURLFORM_END,
+	CURLFORM_OBSOLETE2,
+	CURLFORM_LASTENTRY
+}
+
+struct curl_forms {
+	CURLformoption option;
+	char* value;
+}
+
+alias int CURLFORMcode;
+enum {
+	CURL_FORMADD_OK,
+	CURL_FORMADD_MEMORY,
+	CURL_FORMADD_OPTION_TWICE,
+	CURL_FORMADD_NULL,
+	CURL_FORMADD_UNKNOWN_OPTION,
+	CURL_FORMADD_INCOMPLETE,
+	CURL_FORMADD_ILLEGAL_ARRAY,
+	CURL_FORMADD_DISABLED,
+
+	CURL_FORMADD_LAST
+}
+
+CURLFORMcode curl_formadd(
+	curl_httppost** httppost,
+	curl_httppost** last_post,
+	...);
+
+alias size_t function(void* arg, char* buf, size_t len) curl_formget_callback;
+
+int curl_formget(curl_httppost* form, void* arg, curl_formget_callback append);
+
+void curl_formfree(curl_httppost* form);
+
+char* curl_getenv(char* variable);
+
+char* curl_version();
+
+char* curl_easy_escape(CURL* handle, char* string, int length);
+
+char* curl_easy_unescape(
+	CURL* handle,
+	char* string,
+	int length,
+	int* outlength);
+
+void curl_free(void* p);
+
+CURLcode curl_global_init(int flags);
+
+CURLcode curl_global_init_mem(
+	int flags,
+	curl_malloc_callback m,
+	curl_free_callback f,
+	curl_realloc_callback r,
+	curl_strdup_callback s,
+	curl_calloc_callback c);
+
+void curl_global_cleanup();
+
+struct curl_slist {
+	char* data;
+	curl_slist* next;
+}
+
+curl_slist* curl_slist_append(curl_slist*, char*);
+
+void curl_slist_free_all(curl_slist*);
+
+size_t curl_getdate(char* p, size_t* unused);
+
+const int CURLINFO_STRING   = 0x100000;
+const int CURLINFO_LONG     = 0x200000;
+const int CURLINFO_DOUBLE   = 0x300000;
+const int CURLINFO_SLIST    = 0x400000;
+const int CURLINFO_MASK     = 0x0fffff;
+const int CURLINFO_TYPEMASK = 0xf00000;
+
+alias int CURLINFO;
+enum {
+	CURLINFO_NONE,
+
+	CURLINFO_EFFECTIVE_URL    = CURLINFO_STRING + 1,
+	CURLINFO_RESPONSE_CODE    = CURLINFO_LONG   + 2,
+	CURLINFO_TOTAL_TIME       = CURLINFO_DOUBLE + 3,
+	CURLINFO_NAMELOOKUP_TIME  = CURLINFO_DOUBLE + 4,
+	CURLINFO_CONNECT_TIME     = CURLINFO_DOUBLE + 5,
+	CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6,
+	CURLINFO_SIZE_UPLOAD      = CURLINFO_DOUBLE + 7,
+	CURLINFO_SIZE_DOWNLOAD    = CURLINFO_DOUBLE + 8,
+	CURLINFO_SPEED_DOWNLOAD   = CURLINFO_DOUBLE + 9,
+	CURLINFO_SPEED_UPLOAD     = CURLINFO_DOUBLE + 10,
+	CURLINFO_HEADER_SIZE      = CURLINFO_LONG   + 11,
+	CURLINFO_REQUEST_SIZE     = CURLINFO_LONG   + 12,
+	CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG   + 13,
+	CURLINFO_FILETIME         = CURLINFO_LONG   + 14,
+	CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE + 15,
+	CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE + 16,
+	CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17,
+	CURLINFO_CONTENT_TYPE     = CURLINFO_STRING + 18,
+	CURLINFO_REDIRECT_TIME    = CURLINFO_DOUBLE + 19,
+	CURLINFO_REDIRECT_COUNT   = CURLINFO_LONG   + 20,
+	CURLINFO_PRIVATE          = CURLINFO_STRING + 21,
+	CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG   + 22,
+	CURLINFO_HTTPAUTH_AVAIL   = CURLINFO_LONG   + 23,
+	CURLINFO_PROXYAUTH_AVAIL  = CURLINFO_LONG   + 24,
+	CURLINFO_OS_ERRNO         = CURLINFO_LONG   + 25,
+	CURLINFO_NUM_CONNECTS     = CURLINFO_LONG   + 26,
+	CURLINFO_SSL_ENGINES      = CURLINFO_SLIST  + 27,
+	CURLINFO_COOKIELIST       = CURLINFO_SLIST  + 28,
+	CURLINFO_LASTSOCKET       = CURLINFO_LONG   + 29,
+	CURLINFO_FTP_ENTRY_PATH   = CURLINFO_STRING + 30,
+
+	CURLINFO_LASTONE          = 30
+}
+
+alias int curl_closepolicy;
+enum {
+	CURLCLOSEPOLICY_NONE,
+
+	CURLCLOSEPOLICY_OLDEST,
+	CURLCLOSEPOLICY_LEAST_RECENTLY_USED,
+	CURLCLOSEPOLICY_LEAST_TRAFFIC,
+	CURLCLOSEPOLICY_SLOWEST,
+	CURLCLOSEPOLICY_CALLBACK,
+
+	CURLCLOSEPOLICY_LAST
+}
+
+const int CURL_GLOBAL_SSL     = 1 << 0;
+const int CURL_GLOBAL_WIN32   = 1 << 1;
+const int CURL_GLOBAL_ALL     = CURL_GLOBAL_SSL | CURL_GLOBAL_WIN32;
+const int CURL_GLOBAL_NOTHING = 0;
+const int CURL_GLOBAL_DEFAULT = CURL_GLOBAL_ALL;
+
+//{{{ share
+alias int curl_lock_data;
+enum {
+	CURL_LOCK_DATA_NONE = 0,
+	CURL_LOCK_DATA_SHARE,
+	CURL_LOCK_DATA_COOKIE,
+	CURL_LOCK_DATA_DNS,
+	CURL_LOCK_DATA_SSL_SESSION,
+	CURL_LOCK_DATA_CONNECT,
+
+	CURL_LOCK_DATA_LAST
+}
+
+alias int curl_lock_access;
+enum {
+	CURL_LOCK_ACCESS_NONE = 0,
+	CURL_LOCK_ACCESS_SHARED = 1,
+	CURL_LOCK_ACCESS_SINGLE = 2,
+
+	CURL_LOCK_ACCESS_LAST
+}
+
+alias void function(
+	CURL* handle,
+	curl_lock_data data,
+	curl_lock_access locktype,
+	void* userptr) curl_lock_function;
+alias void function(
+	CURL* handle,
+	curl_lock_data data,
+	void* userptr) curl_unlock_function;
+
+alias void CURLSH;
+
+alias int CURLSHcode;
+enum {
+	CURLSHE_OK,
+	CURLSHE_BAD_OPTION,
+	CURLSHE_IN_USE,
+	CURLSHE_INVALID,
+	CURLSHE_NOMEM,
+
+	CURLSHE_LAST
+}
+
+alias int CURLSHoption;
+enum {
+	CURLSHOPT_NONE,
+	CURLSHOPT_SHARE,
+	CURLSHOPT_UNSHARE,
+	CURLSHOPT_LOCKFUNC,
+	CURLSHOPT_UNLOCKFUNC,
+	CURLSHOPT_USERDATA,
+
+	CURLSHOPT_LAST
+}
+
+CURLSH* curl_share_init();
+
+CURLSHcode curl_share_setopt(CURLSH*, CURLSHoption option, ...);
+
+CURLSHcode curl_share_cleanup(CURLSH*);
+
+char* curl_share_strerror(CURLSHcode);
+//}}}
+
+//{{{ version
+alias int CURLversion;
+enum {
+	CURLVERSION_FIRST,
+	CURLVERSION_SECOND,
+	CURLVERSION_THIRD,
+
+	CURLVERSION_LAST
+}
+
+const int CURLVERSION_NOW = CURLVERSION_THIRD;
+
+struct curl_version_info_data {
+	CURLversion age;
+	char* version_str;
+	uint version_num;
+	char* host;
+	int features;
+	char* ssl_version;
+	int ssl_version_num;
+	char* libz_version;
+	char** protocols;
+	char* ares;
+	int ares_num;
+	char* libidn;
+	int iconv_ver_num;
+}
+
+const int CURL_VERSION_IPV6         = 1 << 0;
+const int CURL_VERSION_KERBEROS4    = 1 << 1;
+const int CURL_VERSION_SSL          = 1 << 2;
+const int CURL_VERSION_LIBZ         = 1 << 3;
+const int CURL_VERSION_NTLM         = 1 << 4;
+const int CURL_VERSION_GSSNEGOTIATE = 1 << 5;
+const int CURL_VERSION_DEBUG        = 1 << 6;
+const int CURL_VERSION_ASYNCHDNS    = 1 << 7;
+const int CURL_VERSION_SPNEGO       = 1 << 8;
+const int CURL_VERSION_LARGEFILE    = 1 << 9;
+const int CURL_VERSION_IDN          = 1 << 10;
+const int CURL_VERSION_SSPI         = 1 << 11;
+const int CURL_VERSION_CONV         = 1 << 12;
+
+curl_version_info_data* curl_version_info(CURLversion);
+
+char* curl_easy_strerror(CURLcode);
+//}}}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/c/portaudio.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,292 @@
+module dynamin.c.portaudio;
+
+/*
+ * A complete binding to the core of the PortAudio library v19.
+ */
+
+version(build) { pragma(link, portaudio); }
+
+extern(C):
+
+int Pa_GetVersion();
+
+char* Pa_GetVersionText();
+
+alias int PaError;
+
+alias int PaErrorCode;
+enum : PaErrorCode {
+	paNoError = 0,
+
+	paNotInitialized = -10000,
+	paUnanticipatedHostError,
+	paInvalidChannelCount,
+	paInvalidSampleRate,
+	paInvalidDevice,
+	paInvalidFlag,
+	paSampleFormatNotSupported,
+	paBadIODeviceCombination,
+	paInsufficientMemory,
+	paBufferTooBig,
+	paBufferTooSmall,
+	paNullCallback,
+	paBadStreamPtr,
+	paTimedOut,
+	paInternalError,
+	paDeviceUnavailable,
+	paIncompatibleHostApiSpecificStreamInfo,
+	paStreamIsStopped,
+	paStreamIsNotStopped,
+	paInputOverflowed,
+	paOutputUnderflowed,
+	paHostApiNotFound,
+	paInvalidHostApi,
+	paCanNotReadFromACallbackStream,
+	paCanNotWriteToACallbackStream,
+	paCanNotReadFromAnOutputOnlyStream,
+	paCanNotWriteToAnInputOnlyStream,
+	paIncompatibleStreamHostApi,
+	paBadBufferPtr
+}
+
+char* Pa_GetErrorText(PaError errorCode);
+
+PaError Pa_Initialize();
+
+PaError Pa_Terminate();
+
+alias int PaDeviceIndex;
+
+enum : PaDeviceIndex {
+	paNoDevice = -1,
+	paUseHostApiSpecificDeviceSpecification  = -2
+}
+
+alias int PaHostApiIndex;
+
+PaHostApiIndex Pa_GetHostApiCount();
+
+PaHostApiIndex Pa_GetDefaultHostApi();
+
+alias int PaHostApiTypeId;
+enum : PaHostApiTypeId {
+	paInDevelopment = 0,
+	paDirectSound = 1,
+	paMME = 2,
+	paASIO = 3,
+	paSoundManager = 4,
+	paCoreAudio = 5,
+	paOSS = 7,
+	paALSA = 8,
+	paAL = 9,
+	paBeOS = 10,
+	paWDMKS = 11,
+	paJACK = 12,
+	paWASAPI = 13,
+	paAudioScienceHPI = 14
+}
+
+struct PaHostApiInfo {
+	int structVersion;
+	PaHostApiTypeId type;
+	char* name;
+
+	int deviceCount;
+
+	PaDeviceIndex defaultInputDevice;
+
+	PaDeviceIndex defaultOutputDevice;
+
+}
+
+PaHostApiInfo* Pa_GetHostApiInfo(PaHostApiIndex hostApi);
+
+PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex(PaHostApiTypeId type);
+
+PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex(
+	PaHostApiIndex hostApi,
+	int hostApiDeviceIndex);
+
+struct PaHostErrorInfo {
+	PaHostApiTypeId hostApiType;
+	int errorCode;
+	char* errorText;
+}
+
+
+PaHostErrorInfo* Pa_GetLastHostErrorInfo();
+
+PaDeviceIndex Pa_GetDeviceCount();
+
+PaDeviceIndex Pa_GetDefaultInputDevice();
+
+PaDeviceIndex Pa_GetDefaultOutputDevice();
+
+alias double PaTime;
+
+alias uint PaSampleFormat;
+
+enum : PaSampleFormat {
+	paFloat32        = 0x00000001,
+	paInt32          = 0x00000002,
+	paInt24          = 0x00000004,
+	paInt16          = 0x00000008,
+	paInt8           = 0x00000010,
+	paUInt8          = 0x00000020,
+	paCustomFormat   = 0x00010000,
+
+	paNonInterleaved = 0x80000000
+}
+
+struct PaDeviceInfo {
+	int structVersion;
+	char* name;
+	PaHostApiIndex hostApi;
+
+	int maxInputChannels;
+	int maxOutputChannels;
+
+	PaTime defaultLowInputLatency;
+	PaTime defaultLowOutputLatency;
+	PaTime defaultHighInputLatency;
+	PaTime defaultHighOutputLatency;
+
+	double defaultSampleRate;
+}
+
+PaDeviceInfo* Pa_GetDeviceInfo(PaDeviceIndex device);
+
+struct PaStreamParameters {
+	PaDeviceIndex device;
+
+	int channelCount;
+
+	PaSampleFormat sampleFormat;
+
+	PaTime suggestedLatency;
+
+	void* hostApiSpecificStreamInfo;
+}
+
+const paFormatIsSupported = 0;
+
+PaError Pa_IsFormatSupported(
+	PaStreamParameters* inputParameters,
+	PaStreamParameters* outputParameters,
+	double sampleRate);
+
+alias void PaStream;
+
+const paFramesPerBufferUnspecified = 0;
+
+alias uint PaStreamFlags;
+enum : PaStreamFlags {
+	paNoFlag                = 0,
+	paClipOff               = 0x00000001,
+	paDitherOff             = 0x00000002,
+	paNeverDropInput        = 0x00000004,
+	paPrimeOutputBuffersUsingStreamCallback = 0x00000008,
+	paPlatformSpecificFlags = 0xFFFF0000
+}
+
+struct PaStreamCallbackTimeInfo {
+	PaTime inputBufferAdcTime;
+	PaTime currentTime;
+	PaTime outputBufferDacTime;
+}
+
+alias uint PaStreamCallbackFlags;
+enum : PaStreamCallbackFlags {
+	paInputUnderflow  = 0x00000001,
+	paInputOverflow   = 0x00000002,
+	paOutputUnderflow = 0x00000004,
+	paOutputOverflow  = 0x00000008,
+	paPrimingOutput   = 0x00000010
+}
+
+alias uint PaStreamCallbackResult;
+enum : PaStreamCallbackResult {
+	paContinue = 0,
+	paComplete = 1,
+	paAbort = 2
+}
+
+alias int function(
+	void* input, void* output,
+	uint frameCount,
+	PaStreamCallbackTimeInfo* timeInfo,
+	PaStreamCallbackFlags statusFlags,
+	void* userData) PaStreamCallback;
+
+PaError Pa_OpenStream(
+	PaStream** stream,
+	PaStreamParameters* inputParameters,
+	PaStreamParameters* outputParameters,
+	double sampleRate,
+	uint framesPerBuffer,
+	PaStreamFlags streamFlags,
+	PaStreamCallback streamCallback,
+	void* userData);
+
+PaError Pa_OpenDefaultStream(
+	PaStream** stream,
+	int numInputChannels,
+	int numOutputChannels,
+	PaSampleFormat sampleFormat,
+	double sampleRate,
+	uint framesPerBuffer,
+	PaStreamCallback streamCallback,
+	void* userData);
+
+PaError Pa_CloseStream(PaStream* stream);
+
+alias void function(void* userData) PaStreamFinishedCallback;
+
+PaError Pa_SetStreamFinishedCallback(
+	PaStream* stream, PaStreamFinishedCallback streamFinishedCallback);
+
+PaError Pa_StartStream(PaStream* stream);
+
+PaError Pa_StopStream(PaStream* stream);
+
+PaError Pa_AbortStream(PaStream* stream);
+
+PaError Pa_IsStreamStopped(PaStream* stream);
+
+PaError Pa_IsStreamActive(PaStream* stream);
+
+struct PaStreamInfo {
+	int structVersion;
+
+	PaTime inputLatency;
+
+	PaTime outputLatency;
+
+	double sampleRate;
+}
+
+PaStreamInfo* Pa_GetStreamInfo(PaStream* stream);
+
+PaTime Pa_GetStreamTime(PaStream* stream);
+
+double Pa_GetStreamCpuLoad(PaStream* stream);
+
+PaError Pa_ReadStream(
+	PaStream* stream,
+	void* buffer,
+	uint frames);
+
+
+PaError Pa_WriteStream(
+	PaStream* stream,
+	void* buffer,
+	uint frames);
+
+int Pa_GetStreamReadAvailable(PaStream* stream);
+
+int Pa_GetStreamWriteAvailable(PaStream* stream);
+
+PaError Pa_GetSampleSize(PaSampleFormat format);
+
+void Pa_Sleep(long msec);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/c/uniscribe.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,505 @@
+module dynamin.c.uniscribe;
+
+/*
+ * A complete binding to Uniscribe.
+ */
+
+import dynamin.c.windows;
+
+version(build) { pragma(link, usp10); }
+
+// TODO: move to a general place?
+// http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D.learn&article_id=1507
+template BitField(uint start, uint count, alias data, type = uint) {
+	static assert((1L << (start + count)) - 1 <= data.max);
+
+	const typeof(data) mask = (1UL << count) - 1;
+
+	type get() {
+		return cast(type) ((data >> start) & mask);
+	}
+
+	type set(type value) {
+		data = (data & ~(mask << start)) |
+			((cast(typeof(data)) value & mask) << start);
+		return value;
+	}
+}
+
+extern(C):
+
+const int USPBUILD = 0400;
+
+enum {
+	SCRIPT_UNDEFINED = 0
+}
+
+//#define USP_E_SCRIPT_NOT_IN_FONT  MAKE_HRESULT(SEVERITY_ERROR,FACILITY_ITF,0x200)
+
+alias void* SCRIPT_CACHE;
+
+HRESULT ScriptFreeCache(SCRIPT_CACHE* psc);
+
+struct SCRIPT_CONTROL { // 32 bits
+	DWORD fields;
+	mixin BitField!( 0, 16, fields) uDefaultLanguage;
+	mixin BitField!(16,  1, fields) fContextDigits;
+	mixin BitField!(17,  1, fields) fInvertPreBoundDir;
+	mixin BitField!(18,  1, fields) fInvertPostBoundDir;
+	mixin BitField!(19,  1, fields) fLinkStringBefore;
+	mixin BitField!(20,  1, fields) fLinkStringAfter;
+	mixin BitField!(21,  1, fields) fNeutralOverride;
+	mixin BitField!(22,  1, fields) fNumericOverride;
+	mixin BitField!(23,  1, fields) fLegacyBidiClass;
+	mixin BitField!(24,  8, fields) fReserved;
+
+	/*
+	DWORD uDefaultLanguage    :16;
+	DWORD fContextDigits      :1;
+
+	DWORD fInvertPreBoundDir  :1;
+	DWORD fInvertPostBoundDir :1;
+	DWORD fLinkStringBefore   :1;
+	DWORD fLinkStringAfter    :1;
+	DWORD fNeutralOverride    :1;
+	DWORD fNumericOverride    :1;
+	DWORD fLegacyBidiClass    :1;
+	DWORD fReserved           :8;
+	*/
+}
+
+struct SCRIPT_STATE { // 16 bits
+	WORD fields;
+	mixin BitField!( 0, 5, fields, WORD) uBidiLevel;
+	mixin BitField!( 5, 1, fields, WORD) fOverrideDirection;
+	mixin BitField!( 6, 1, fields, WORD) fInhibitSymSwap;
+	mixin BitField!( 7, 1, fields, WORD) fCharShape;
+	mixin BitField!( 8, 1, fields, WORD) fDigitSubstitute;
+	mixin BitField!( 9, 1, fields, WORD) fInhibitLigate;
+	mixin BitField!(10, 1, fields, WORD) fDisplayZWG;
+	mixin BitField!(11, 1, fields, WORD) fArabicNumContext;
+	mixin BitField!(12, 1, fields, WORD) fGcpClusters;
+	mixin BitField!(13, 1, fields, WORD) fReserved;
+	mixin BitField!(14, 2, fields, WORD) fEngineReserved;
+
+	/*
+	WORD uBidiLevel         :5;
+	WORD fOverrideDirection :1;
+	WORD fInhibitSymSwap    :1;
+	WORD fCharShape         :1;
+	WORD fDigitSubstitute   :1;
+	WORD fInhibitLigate     :1;
+	WORD fDisplayZWG        :1;
+	WORD fArabicNumContext  :1;
+	WORD fGcpClusters       :1;
+	WORD fReserved          :1;
+	WORD fEngineReserved    :2;
+	*/
+}
+
+struct SCRIPT_ANALYSIS { // 16 bits +
+	WORD fields;
+	mixin BitField!( 0, 10, fields, WORD) eScript;
+	mixin BitField!(10,  1, fields, WORD) fRTL;
+	mixin BitField!(11,  1, fields, WORD) fLayoutRTL;
+	mixin BitField!(12,  1, fields, WORD) fLinkBefore;
+	mixin BitField!(13,  1, fields, WORD) fLinkAfter;
+	mixin BitField!(14,  1, fields, WORD) fLogicalOrder;
+	mixin BitField!(15,  1, fields, WORD) fNoGlyphIndex;
+
+	/*
+	WORD eScript         :10;
+	WORD fRTL            :1;
+	WORD fLayoutRTL      :1;
+	WORD fLinkBefore     :1;
+	WORD fLinkAfter      :1;
+	WORD fLogicalOrder   :1;
+	WORD fNoGlyphIndex   :1;
+	*/
+	SCRIPT_STATE s;
+}
+
+struct SCRIPT_ITEM {
+	int iCharPos;
+	SCRIPT_ANALYSIS a;
+}
+
+HRESULT ScriptItemize(
+	/*const*/ WCHAR* pwcInChars,
+	int cInChars,
+	int cMaxItems,
+	/*const*/ SCRIPT_CONTROL* psControl,
+	/*const*/ SCRIPT_STATE* psState,
+	SCRIPT_ITEM* pItems,
+	int* pcItems);
+
+HRESULT ScriptLayout(
+	int cRuns,
+	/*const*/ BYTE* pbLevel,
+	int* piVisualToLogical,
+	int* piLogicalToVisual);
+
+enum SCRIPT_JUSTIFY {
+	SCRIPT_JUSTIFY_NONE           = 0,
+	SCRIPT_JUSTIFY_ARABIC_BLANK   = 1,
+	SCRIPT_JUSTIFY_CHARACTER      = 2,
+	SCRIPT_JUSTIFY_RESERVED1      = 3,
+	SCRIPT_JUSTIFY_BLANK          = 4,
+	SCRIPT_JUSTIFY_RESERVED2      = 5,
+	SCRIPT_JUSTIFY_RESERVED3      = 6,
+	SCRIPT_JUSTIFY_ARABIC_NORMAL  = 7,
+	SCRIPT_JUSTIFY_ARABIC_KASHIDA = 8,
+	SCRIPT_JUSTIFY_ARABIC_ALEF    = 9,
+	SCRIPT_JUSTIFY_ARABIC_HA      = 10,
+	SCRIPT_JUSTIFY_ARABIC_RA      = 11,
+	SCRIPT_JUSTIFY_ARABIC_BA      = 12,
+	SCRIPT_JUSTIFY_ARABIC_BARA    = 13,
+	SCRIPT_JUSTIFY_ARABIC_SEEN    = 14,
+	SCRIPT_JUSTIFY_RESERVED4      = 15,
+}
+
+struct SCRIPT_VISATTR { // 16 bits
+	WORD fields;
+	mixin BitField!(0, 4, fields, WORD) uJustification;
+	mixin BitField!(4, 1, fields, WORD) fClusterStart;
+	mixin BitField!(5, 1, fields, WORD) fDiacritic;
+	mixin BitField!(6, 1, fields, WORD) fZeroWidth;
+	mixin BitField!(7, 1, fields, WORD) fReserved;
+	mixin BitField!(8, 8, fields, WORD) fShapeReserved;
+
+	/*
+	WORD uJustification :4;
+	WORD fClusterStart  :1;
+	WORD fDiacritic     :1;
+	WORD fZeroWidth     :1;
+	WORD fReserved      :1;
+	WORD fShapeReserved :8;
+	*/
+}
+
+HRESULT ScriptShape(
+	HDC hdc,
+	SCRIPT_CACHE* psc,
+	/*const*/ WCHAR* pwcChars,
+	int cChars,
+	int cMaxGlyphs,
+	SCRIPT_ANALYSIS* psa,
+	WORD* pwOutGlyphs,
+	WORD* pwLogClust,
+	SCRIPT_VISATTR* psva,
+	int* pcGlyphs);
+
+struct GOFFSET {
+	LONG du;
+	LONG dv;
+}
+
+HRESULT ScriptPlace(
+	HDC hdc,
+	SCRIPT_CACHE* psc,
+	/*const*/ WORD* pwGlyphs,
+	int cGlyphs,
+	/*const*/ SCRIPT_VISATTR* psva,
+	SCRIPT_ANALYSIS* psa,
+	int* piAdvance,
+	GOFFSET* pGoffset,
+	ABC* pABC);
+
+HRESULT ScriptTextOut(
+	/*const*/ HDC hdc,
+	SCRIPT_CACHE* psc,
+	int x,
+	int y,
+	UINT fuOptions,
+	/*const*/ RECT* lprc,
+	/*const*/ SCRIPT_ANALYSIS* psa,
+	/*const*/ WCHAR* pwcReserved,
+	int iReserved,
+	/*const*/ WORD* pwGlyphs,
+	int cGlyphs,
+	/*const*/ int* piAdvance,
+	/*const*/ int* piJustify,
+	/*const*/ GOFFSET* pGoffset);
+
+HRESULT ScriptJustify(
+	/*const*/ SCRIPT_VISATTR* psva,
+	/*const*/ int* piAdvance,
+	int cGlyphs,
+	int iDx,
+	int iMinKashida,
+	int* piJustify);
+
+struct SCRIPT_LOGATTR { // 8 bits
+	BYTE fields;
+	mixin BitField!(0, 1, fields, BYTE) fSoftBreak;
+	mixin BitField!(1, 1, fields, BYTE) fWhiteSpace;
+	mixin BitField!(2, 1, fields, BYTE) fCharStop;
+	mixin BitField!(3, 1, fields, BYTE) fWordStop;
+	mixin BitField!(4, 1, fields, BYTE) fInvalid;
+	mixin BitField!(5, 3, fields, BYTE) fReserved;
+
+	/*
+	BYTE fSoftBreak  :1;
+	BYTE fWhiteSpace :1;
+	BYTE fCharStop   :1;
+	BYTE fWordStop   :1;
+	BYTE fInvalid    :1;
+	BYTE fReserved   :3;
+	*/
+}
+
+HRESULT ScriptBreak(
+	/*const*/ WCHAR* pwcChars,
+	int cChars,
+	/*const*/ SCRIPT_ANALYSIS* psa,
+	SCRIPT_LOGATTR* psla);
+
+HRESULT ScriptCPtoX(
+	int iCP,
+	BOOL fTrailing,
+	int cChars,
+	int cGlyphs,
+	/*const*/ WORD* pwLogClust,
+	/*const*/ SCRIPT_VISATTR* psva,
+	/*const*/ int* piAdvance,
+	/*const*/ SCRIPT_ANALYSIS* psa,
+	int* piX);
+
+HRESULT ScriptXtoCP(
+	int iX,
+	int cChars,
+	int cGlyphs,
+	/*const*/ WORD* pwLogClust,
+	/*const*/ SCRIPT_VISATTR* psva,
+	/*const*/ int* piAdvance,
+	/*const*/ SCRIPT_ANALYSIS* psa,
+	int* piCP,
+	int* piTrailing);
+
+HRESULT ScriptGetLogicalWidths(
+	/*const*/ SCRIPT_ANALYSIS* psa,
+	int cChars,
+	int cGlyphs,
+	/*const*/ int* piGlyphWidth,
+	/*const*/ WORD* pwLogClust,
+	/*const*/ SCRIPT_VISATTR* psva,
+	int* piDx);
+
+HRESULT ScriptApplyLogicalWidth(
+	/*const*/ int* piDx,
+	int cChars,
+	int cGlyphs,
+	/*const*/ WORD* pwLogClust,
+	/*const*/ SCRIPT_VISATTR* psva,
+	/*const*/ int* piAdvance,
+	/*const*/ SCRIPT_ANALYSIS* psa,
+	ABC* pABC,
+	int* piJustify);
+
+enum {
+	SGCM_RTL = 0x00000001
+}
+
+HRESULT ScriptGetCMap(
+	HDC hdc,
+	SCRIPT_CACHE* psc,
+	/*const*/ WCHAR* pwcInChars,
+	int cChars,
+	DWORD dwFlags,
+	WORD* pwOutGlyphs);
+
+HRESULT ScriptGetGlyphABCWidth(
+	HDC hdc,
+	SCRIPT_CACHE* psc,
+	WORD wGlyph,
+	ABC* pABC);
+
+struct SCRIPT_PROPERTIES { // 37 bits
+	DWORD fields1;
+	DWORD fields2;
+	mixin BitField!( 0, 16, fields1) langid;
+	mixin BitField!(16,  1, fields1) fNumeric;
+	mixin BitField!(17,  1, fields1) fComplex;
+	mixin BitField!(18,  1, fields1) fNeedsWordBreaking;
+	mixin BitField!(19,  1, fields1) fNeedsCaretInfo;
+	mixin BitField!(20,  8, fields1) bCharSet;
+	mixin BitField!(28,  1, fields1) fControl;
+	mixin BitField!(29,  1, fields1) fPrivateUseArea;
+	mixin BitField!(30,  1, fields1) fNeedsCharacterJustify;
+	mixin BitField!(31,  1, fields1) fInvalidGlyph;
+	mixin BitField!( 0,  1, fields2) fInvalidLogAttr;
+	mixin BitField!( 1,  1, fields2) fCDM;
+	mixin BitField!( 2,  1, fields2) fAmbiguousCharSet;
+	mixin BitField!( 3,  1, fields2) fClusterSizeVaries;
+	mixin BitField!( 4,  1, fields2) fRejectInvalid;
+
+	/*
+	DWORD langid                 :16;
+	DWORD fNumeric               :1;
+	DWORD fComplex               :1;
+	DWORD fNeedsWordBreaking     :1;
+	DWORD fNeedsCaretInfo        :1;
+	DWORD bCharSet               :8;
+	DWORD fControl               :1;
+	DWORD fPrivateUseArea        :1;
+	DWORD fNeedsCharacterJustify :1;
+	DWORD fInvalidGlyph          :1;
+	DWORD fInvalidLogAttr        :1;
+	DWORD fCDM                   :1;
+	DWORD fAmbiguousCharSet      :1;
+	DWORD fClusterSizeVaries     :1;
+	DWORD fRejectInvalid         :1;
+	*/
+}
+
+HRESULT ScriptGetProperties(
+	/*const*/ SCRIPT_PROPERTIES*** ppSp,
+	int* piNumScripts);
+
+struct SCRIPT_FONTPROPERTIES {
+	int  cBytes;
+	WORD wgBlank;
+	WORD wgDefault;
+	WORD wgInvalid;
+	WORD wgKashida;
+	int  iKashidaWidth;
+}
+
+HRESULT ScriptGetFontProperties(
+	HDC hdc,
+	SCRIPT_CACHE* psc,
+	SCRIPT_FONTPROPERTIES* sfp);
+
+HRESULT ScriptCacheGetHeight(
+	HDC hdc,
+	SCRIPT_CACHE* psc,
+	int* tmHeight);
+
+enum {
+	SSA_PASSWORD        = 0x00000001,
+	SSA_TAB             = 0x00000002,
+	SSA_CLIP            = 0x00000004,
+	SSA_FIT             = 0x00000008,
+	SSA_DZWG            = 0x00000010,
+	SSA_FALLBACK        = 0x00000020,
+	SSA_BREAK           = 0x00000040,
+	SSA_GLYPHS          = 0x00000080,
+	SSA_RTL             = 0x00000100,
+	SSA_GCP             = 0x00000200,
+	SSA_HOTKEY          = 0x00000400,
+	SSA_METAFILE        = 0x00000800,
+	SSA_LINK            = 0x00001000,
+	SSA_HIDEHOTKEY      = 0x00002000,
+	SSA_HOTKEYONLY      = 0x00002400,
+
+	SSA_FULLMEASURE     = 0x04000000,
+	SSA_LPKANSIFALLBACK = 0x08000000,
+	SSA_PIDX            = 0x10000000,
+	SSA_LAYOUTRTL       = 0x20000000,
+	SSA_DONTGLYPH       = 0x40000000,
+	SSA_NOKASHIDA       = 0x80000000,
+}
+
+struct SCRIPT_TABDEF {
+	int cTabStops;
+	int iScale;
+	int* pTabStops;
+	int iTabOrigin;
+}
+
+alias void* SCRIPT_STRING_ANALYSIS;
+
+HRESULT ScriptStringAnalyse(
+	HDC hdc,
+	/*const*/ void* pString,
+	int cString,
+	int cGlyphs,
+	int iCharset,
+	DWORD dwFlags,
+	int iReqWidth,
+	SCRIPT_CONTROL* psControl,
+	SCRIPT_STATE* psState,
+	/*const*/ int* piDx,
+	SCRIPT_TABDEF* pTabdef,
+	/*const*/ BYTE* pbInClass,
+	SCRIPT_STRING_ANALYSIS* pssa);
+
+HRESULT ScriptStringFree(SCRIPT_STRING_ANALYSIS* pssa);
+
+/*const*/ SIZE* ScriptString_pSize(SCRIPT_STRING_ANALYSIS ssa);
+
+/*const*/ int* ScriptString_pcOutChars(SCRIPT_STRING_ANALYSIS ssa);
+
+/*const*/ SCRIPT_LOGATTR* ScriptString_pLogAttr(SCRIPT_STRING_ANALYSIS ssa);
+
+HRESULT ScriptStringGetOrder(SCRIPT_STRING_ANALYSIS ssa, UINT* puOrder);
+
+HRESULT ScriptStringCPtoX(
+	SCRIPT_STRING_ANALYSIS ssa,
+	int icp,
+	BOOL fTrailing,
+	int* pX);
+
+HRESULT ScriptStringXtoCP(
+	SCRIPT_STRING_ANALYSIS ssa,
+	int iX,
+	int* piCh,
+	int* piTrailing);
+
+HRESULT ScriptStringGetLogicalWidths(SCRIPT_STRING_ANALYSIS ssa, int* piDx);
+
+HRESULT ScriptStringValidate(SCRIPT_STRING_ANALYSIS ssa);
+
+HRESULT ScriptStringOut(
+	SCRIPT_STRING_ANALYSIS ssa,
+	int iX,
+	int iY,
+	UINT uOptions,
+	/*const*/ RECT* prc,
+	int iMinSel,
+	int iMaxSel,
+	BOOL fDisabled);
+
+enum {
+	SIC_COMPLEX    = 1,
+	SIC_ASCIIDIGIT = 2,
+	SIC_NEUTRAL    = 4,
+}
+
+HRESULT ScriptIsComplex(
+    /*const*/ WCHAR* pwcInChars,
+    int cInChars,
+    DWORD dwFlags);
+
+struct SCRIPT_DIGITSUBSTITUTE {
+	DWORD fields1;
+	DWORD fields2;
+	mixin BitField!( 0, 16, fields1) NationalDigitLanguage;
+	mixin BitField!(16, 16, fields1) TraditionalDigitLanguage;
+	mixin BitField!( 0,  8, fields2) DigitSubstitute;
+
+	/*
+	DWORD NationalDigitLanguage    :16;
+	DWORD TraditionalDigitLanguage :16;
+	DWORD DigitSubstitute          :8;
+	*/
+	DWORD dwReserved;
+}
+
+HRESULT ScriptRecordDigitSubstitution(
+	LCID Locale,
+	SCRIPT_DIGITSUBSTITUTE* psds);
+
+enum {
+	SCRIPT_DIGITSUBSTITUTE_CONTEXT     = 0,
+	SCRIPT_DIGITSUBSTITUTE_NONE        = 1,
+	SCRIPT_DIGITSUBSTITUTE_NATIONAL    = 2,
+	SCRIPT_DIGITSUBSTITUTE_TRADITIONAL = 3,
+}
+
+HRESULT ScriptApplyDigitSubstitution(
+	/*const*/ SCRIPT_DIGITSUBSTITUTE* psds,
+	SCRIPT_CONTROL* psc,
+	SCRIPT_STATE* pss);
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/c/vorbisfile.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,259 @@
+module dynamin.c.vorbisfile;
+
+/*
+ * A binding to the vorbisfile API.
+ */
+
+version(build) { pragma(link, vorbisfile); }
+
+extern(C):
+
+alias long ogg_int64_t;
+
+// ogg.h
+
+struct oggpack_buffer {
+	int  endbyte;
+	int  endbit;
+
+	byte* buffer;
+	byte* ptr;
+	int   storage;
+} ;
+
+struct ogg_stream_state {
+	byte   *body_data;
+	int    body_storage;
+	int    body_fill;
+	int    body_returned;
+
+
+	int     *lacing_vals;
+	ogg_int64_t *granule_vals;
+	int    lacing_storage;
+	int    lacing_fill;
+	int    lacing_packet;
+	int    lacing_returned;
+
+	byte    header[282];
+	int              header_fill;
+
+	int     e_o_s;
+	int     b_o_s;
+	int    serialno;
+	int    pageno;
+	ogg_int64_t  packetno;
+	ogg_int64_t   granulepos;
+}
+
+struct ogg_sync_state {
+	byte* data;
+	int   storage;
+	int   fill;
+	int   returned;
+
+	int   unsynced;
+	int   headerbytes;
+	int   bodybytes;
+}
+
+// codec.h
+
+struct vorbis_info {
+	int vers;
+	int channels;
+	int rate;
+
+	int bitrate_upper;
+	int bitrate_nominal;
+	int bitrate_lower;
+	int bitrate_window;
+
+	void* codec_setup;
+}
+
+struct vorbis_dsp_state {
+	int analysisp;
+	vorbis_info* vi;
+
+	float** pcm;
+	float** pcmret;
+	int     pcm_storage;
+	int     pcm_current;
+	int     pcm_returned;
+
+	int preextrapolate;
+	int eofflag;
+
+	int lW;
+	int W;
+	int nW;
+	int centerW;
+
+	ogg_int64_t granulepos;
+	ogg_int64_t sequence;
+
+	ogg_int64_t glue_bits;
+	ogg_int64_t time_bits;
+	ogg_int64_t floor_bits;
+	ogg_int64_t res_bits;
+
+	void* backend_state;
+}
+
+struct vorbis_block {
+  float** pcm;
+  oggpack_buffer opb;
+
+  int lW;
+  int W;
+  int nW;
+  int pcmend;
+  int mode;
+
+  int         eofflag;
+  ogg_int64_t granulepos;
+  ogg_int64_t sequence;
+  vorbis_dsp_state* vd;
+
+  void*        localstore;
+  int          localtop;
+  int          localalloc;
+  int          totaluse;
+  alloc_chain* reap;
+
+  int glue_bits;
+  int time_bits;
+  int floor_bits;
+  int res_bits;
+
+  void *internal;
+}
+
+struct alloc_chain {
+  void* ptr;
+  alloc_chain* next;
+}
+
+struct vorbis_comment {
+	char** user_comments;
+	int*   comment_lengths;
+	int    comments;
+	char*  vendor;
+}
+
+enum {
+	OV_FALSE = -1,
+	OV_EOF   = -2,
+	OV_HOLE  = -3
+}
+
+enum {
+	OV_EREAD      = -128,
+	OV_EFAULT     = -129,
+	OV_EIMPL      = -130,
+	OV_EINVAL     = -131,
+	OV_ENOTVORBIS = -132,
+	OV_EBADHEADER = -133,
+	OV_EVERSION   = -134,
+	OV_ENOTAUDIO  = -135,
+	OV_EBADPACKET = -136,
+	OV_EBADLINK   = -137,
+	OV_ENOSEEK    = -138
+}
+
+// vorbisfile.h
+
+struct ov_callbacks {
+	size_t function(void* ptr, size_t size, size_t nmemb, void* datasource) read_func;
+	int function(void* datasource, ogg_int64_t offset, int whence) seek_func;
+	int function(void* datasource) close_func;
+	int function(void* datasource) tell_func;
+}
+
+enum {
+	NOTOPEN   = 0,
+	PARTOPEN  = 1,
+	OPENED    = 2,
+	STREAMSET = 3,
+	INITSET   = 4
+}
+
+struct OggVorbis_File {
+	void*            datasource;
+	int              seekable;
+	ogg_int64_t      offset;
+	ogg_int64_t      end;
+	ogg_sync_state   oy;
+
+	int              links;
+	ogg_int64_t*     offsets;
+	ogg_int64_t*     dataoffsets;
+	int*             serialnos;
+	ogg_int64_t*     pcmlengths;
+	vorbis_info*     vi;
+	vorbis_comment*  vc;
+
+	ogg_int64_t      pcm_offset;
+	int              ready_state;
+	int              current_serialno;
+	int              current_link;
+
+	double           bittrack;
+	double           samptrack;
+
+	ogg_stream_state os;
+	vorbis_dsp_state vd;
+	vorbis_block     vb;
+
+	ov_callbacks callbacks;
+}
+import tango.stdc.stdio;
+int ov_clear(OggVorbis_File* vf);
+int ov_open(FILE* f,OggVorbis_File* vf,char* initial,int ibytes);
+int ov_open_callbacks(void* datasource, OggVorbis_File* vf,
+		char* initial, int ibytes, ov_callbacks callbacks);
+
+int ov_test(FILE* f,OggVorbis_File* vf,char* initial,int ibytes);
+int ov_test_callbacks(void* datasource, OggVorbis_File* vf,
+		char* initial, int ibytes, ov_callbacks callbacks);
+int ov_test_open(OggVorbis_File* vf);
+
+int ov_bitrate(OggVorbis_File* vf,int i);
+int ov_bitrate_instant(OggVorbis_File* vf);
+int ov_streams(OggVorbis_File* vf);
+int ov_seekable(OggVorbis_File* vf);
+int ov_serialnumber(OggVorbis_File* vf,int i);
+
+ogg_int64_t ov_raw_total(OggVorbis_File* vf,int i);
+ogg_int64_t ov_pcm_total(OggVorbis_File* vf,int i);
+double ov_time_total(OggVorbis_File* vf,int i);
+
+int ov_raw_seek(OggVorbis_File* vf,ogg_int64_t pos);
+int ov_pcm_seek(OggVorbis_File* vf,ogg_int64_t pos);
+int ov_pcm_seek_page(OggVorbis_File* vf,ogg_int64_t pos);
+int ov_time_seek(OggVorbis_File* vf,double pos);
+int ov_time_seek_page(OggVorbis_File* vf,double pos);
+
+int ov_raw_seek_lap(OggVorbis_File* vf,ogg_int64_t pos);
+int ov_pcm_seek_lap(OggVorbis_File* vf,ogg_int64_t pos);
+int ov_pcm_seek_page_lap(OggVorbis_File* vf,ogg_int64_t pos);
+int ov_time_seek_lap(OggVorbis_File* vf,double pos);
+int ov_time_seek_page_lap(OggVorbis_File* vf,double pos);
+
+ogg_int64_t ov_raw_tell(OggVorbis_File* vf);
+ogg_int64_t ov_pcm_tell(OggVorbis_File* vf);
+double ov_time_tell(OggVorbis_File* vf);
+
+vorbis_info* ov_info(OggVorbis_File* vf,int link);
+vorbis_comment* ov_comment(OggVorbis_File* vf,int link);
+
+int ov_read_float(OggVorbis_File* vf,float*** pcm_channels,int samples,
+			  int* bitstream);
+int ov_read(OggVorbis_File* vf,char* buffer,int length,
+		    int bigendianp,int word,int sgned,int* bitstream);
+int ov_crosslap(OggVorbis_File* vf1,OggVorbis_File* vf2);
+
+int ov_halfrate(OggVorbis_File* vf,int flag);
+int ov_halfrate_p(OggVorbis_File* vf);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/c/windows.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,1944 @@
+module dynamin.c.windows;
+
+/*
+ * A binding to at least the part of the Windows API that Dynamin uses. This
+ * binding is obviously very incomplete but does contain several functions
+ * that the standard Windows binding does not contain.
+ */
+
+version(Windows) {
+} else {
+	static assert(0);
+}
+
+version(build) { pragma(link, gdi32, comdlg32, shell32, ole32, winmm); }
+
+extern(Windows):
+
+version = UNICODE;
+alias char*	 LPSTR;
+alias wchar* LPWSTR;
+alias char*	 LPCSTR;
+alias wchar* LPCWSTR;
+version(UNICODE) {
+	alias wchar	  TCHAR;
+	alias LPWSTR  LPTSTR;
+	alias LPCWSTR LPCTSTR;
+} else {
+	alias char	 TCHAR;
+	alias LPSTR	 LPTSTR;
+	alias LPCSTR LPCTSTR;
+}
+
+alias VOID*		HANDLE;
+alias HANDLE	HBITMAP;
+alias HANDLE	HBRUSH;
+alias HANDLE	HICON;
+alias HICON		HCURSOR;
+alias HANDLE	HDC;
+alias HANDLE	HGDIOBJ;
+alias HANDLE	HGLOBAL;
+alias int		HFILE;
+alias HANDLE	HFONT;
+alias HANDLE	HINSTANCE;
+alias HANDLE	HKEY;
+alias HANDLE	HMENU;
+alias HINSTANCE HMODULE;
+alias int		HRESULT;
+alias HANDLE	HRGN;
+alias HANDLE	HTHEME;
+alias HANDLE	HWND;
+
+version(Win64) {
+	alias int	HALF_PTR;
+	alias uint	UHALF_PTR;
+	alias long	INT_PTR;
+	alias ulong UINT_PTR;
+	alias long	LONG_PTR;
+	alias ulong ULONG_PTR;
+} else {
+	alias short	 HALF_PTR;
+	alias ushort UHALF_PTR;
+	alias int	 INT_PTR;
+	alias uint	 UINT_PTR;
+	alias int	 LONG_PTR;
+	alias uint	 ULONG_PTR;
+}
+
+alias INT_PTR function() FARPROC;
+alias int		BOOL;
+alias ubyte		BYTE;
+alias char		CHAR;
+alias wchar     WCHAR;
+alias uint		DWORD;
+alias ulong		DWORDLONG;
+alias uint		DWORD32;
+alias ulong		DWORD64;
+alias float		FLOAT;
+alias int		INT;
+alias int		INT32;
+alias long		INT64;
+alias int		LONG;
+alias int		LONG32;
+alias long		LONG64;
+alias UINT_PTR	WPARAM;
+alias LONG_PTR	LPARAM;
+alias LONG_PTR	LRESULT;
+alias char		UCHAR;
+alias uint		UINT;
+alias uint		UINT32;
+alias ulong		UINT64;
+alias uint		ULONG;
+alias uint		ULONG32;
+alias ulong		ULONG64;
+alias short		SHORT;
+alias ushort	USHORT;
+alias void		VOID;
+alias ushort	WORD;
+alias WORD		ATOM;
+alias ULONG_PTR SIZE_T;
+alias DWORD		COLORREF;
+alias LONG      NTSTATUS;
+
+const HRESULT S_OK	  = 0;
+const HRESULT S_FALSE = 1;
+const HRESULT NOERROR = 0;
+
+const int MAX_PATH = 260;
+
+alias UINT_PTR function(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) LPOFNHOOKPROC;
+alias LRESULT function(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) WNDPROC;
+alias int function(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) BFFCALLBACK;
+
+public import tango.sys.win32.Macros;
+//public import tango.sys.win32.UserGdi : LOWORD, HIWORD, RGB;
+
+WORD MAKEWORD(BYTE a, BYTE b) { return cast(WORD)a | cast(WORD)b << 8; }
+LONG MAKELONG(WORD a, WORD b) { return cast(WORD)a | cast(WORD)b << 16; }
+BYTE LOBYTE(WORD w) { return cast(BYTE)(w & 0xff); }
+BYTE HIBYTE(WORD w) { return cast(BYTE)(w >> 8); }
+LPWSTR MAKEINTRESOURCE(int i) { return cast(LPWSTR)cast(WORD)i; }
+BYTE GetRValue(DWORD rgb) { return LOBYTE(rgb); }
+BYTE GetGValue(DWORD rgb) { return LOBYTE(cast(WORD)rgb >> 8); }
+BYTE GetBValue(DWORD rgb) { return LOBYTE(rgb >> 16); }
+
+int MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
+int MessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType);
+
+DWORD GetLastError();
+
+int MultiByteToWideChar(
+	UINT CodePage,
+	DWORD dwFlags,
+	LPCSTR lpMultiByteStr,
+	int cbMultiByte,
+	LPWSTR lpWideCharStr,
+	int cchWideChar);
+
+int WideCharToMultiByte(
+	UINT CodePage,
+	DWORD dwFlags,
+	LPCWSTR lpWideCharStr,
+	int cchWideChar,
+	LPSTR lpMultiByteStr,
+	int cbMultiByte,
+	LPCSTR lpDefaultChar,
+	BOOL* lpUsedDefaultChar);
+
+//{{{ memory functions
+HANDLE GetProcessHeap();
+
+VOID* HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);
+
+VOID* HeapReAlloc(HANDLE hHeap, DWORD dwFlags, VOID* lpMem, SIZE_T dwBytes);
+
+BOOL HeapFree(HANDLE hHeap, DWORD dwFlags, VOID* lpMem);
+
+SIZE_T HeapSize(HANDLE hHeap, DWORD dwFlags, /*const*/ VOID* lpMem);
+
+enum {
+	HEAP_ZERO_MEMORY = 0x0008,
+	HEAP_REALLOC_IN_PLACE_ONLY = 0x0010
+}
+
+HGLOBAL GlobalAlloc(UINT uFlags, SIZE_T dwBytes);
+
+HGLOBAL GlobalReAlloc(HGLOBAL hMem, SIZE_T dwBytes, UINT uFlags);
+
+HGLOBAL GlobalFree(HGLOBAL hMem);
+
+VOID* GlobalLock(HGLOBAL hMem);
+
+BOOL GlobalUnlock(HGLOBAL hMem);
+
+enum {
+	GMEM_FIXED = 0x0000,
+	GMEM_MOVEABLE = 0x0002,
+	GMEM_ZEROINIT = 0x0040,
+	GPTR = 0x0040,
+	GHND = 0x0042
+}
+//}}}
+
+//{{{ window functions
+enum {
+	CS_VREDRAW =	 0x0001,
+	CS_HREDRAW =	 0x0002,
+	CS_DBLCLKS =	 0x0008,
+	CS_OWNDC =		 0x0020,
+	CS_CLASSDC =	 0x0040,
+	CS_PARENTDC =	 0x0080,
+	CS_NOCLOSE =	 0x0200,
+	CS_SAVEBITS =	 0x0800,
+	CS_BYTEALIGNCLIENT = 0x1000,
+	CS_BYTEALIGNWINDOW = 0x2000,
+	CS_GLOBALCLASS = 0x4000,
+	//Windows XP required
+	CS_DROPSHADOW =	 0x00020000
+
+}
+ATOM RegisterClassExA(/*const*/ WNDCLASSEX* lpwcx);
+ATOM RegisterClassExW(/*const*/ WNDCLASSEX* lpwcx);
+
+LRESULT DefWindowProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+//{{{ window styles
+enum {
+	WS_OVERLAPPED =	  0x00000000,
+	WS_MAXIMIZEBOX =  0x00010000,
+	WS_TABSTOP =	  0x00010000,
+	WS_GROUP =		  0x00020000,
+	WS_MINIMIZEBOX =  0x00020000,
+	WS_THICKFRAME =	  0x00040000,
+	WS_SYSMENU =	  0x00080000,
+	WS_HSCROLL =	  0x00100000,
+	WS_VSCROLL =	  0x00200000,
+	WS_DLGFRAME =	  0x00400000,
+	WS_BORDER =		  0x00800000,
+	WS_CAPTION =	  0x00C00000,
+	WS_MAXIMIZE =	  0x01000000,
+	WS_CLIPCHILDREN = 0x02000000,
+	WS_CLIPSIBLINGS = 0x04000000,
+	WS_DISABLED =	  0x08000000,
+	WS_VISIBLE =	  0x10000000,
+	WS_MINIMIZE =	  0x20000000,
+	WS_CHILD =		  0x40000000,
+	WS_POPUP =		  0x80000000,
+	WS_OVERLAPPEDWINDOW = WS_OVERLAPPED |
+						  WS_CAPTION |
+						  WS_SYSMENU |
+						  WS_THICKFRAME |
+						  WS_MINIMIZEBOX |
+						  WS_MAXIMIZEBOX,
+	WS_POPUPWINDOW =  WS_POPUP |
+					  WS_BORDER |
+					  WS_SYSMENU,
+	WS_CHILDWINDOW =  WS_CHILD,
+	WS_TILED =		  WS_OVERLAPPED,
+	WS_ICONIC =		  WS_MINIMIZE,
+	WS_SIZEBOX =	  WS_THICKFRAME,
+	WS_TILEDWINDOW =  WS_OVERLAPPEDWINDOW
+}
+//}}}
+
+//{{{ extended window styles
+enum {
+	WS_EX_DLGMODALFRAME =	 0x00000001,
+	WS_EX_TOPMOST =			 0x00000008,
+	WS_EX_TOOLWINDOW =		 0x00000080,
+	WS_EX_WINDOWEDGE =		 0x00000100,
+	WS_EX_CLIENTEDGE =		 0x00000200,
+	WS_EX_CONTROLPARENT =	 0x00010000,
+	WS_EX_STATICEDGE =		 0x00020000,
+	WS_EX_APPWINDOW =		 0x00040000,
+	WS_EX_LAYERED =			 0x00080000,
+	WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
+	WS_EX_COMPOSITED =		 0x02000000
+}
+//}}}
+
+//{{{ CreateWindowEx()
+HWND CreateWindowExA(
+	DWORD dwExStyle,
+	LPCSTR lpClassName,
+	LPCSTR lpWindowName,
+	DWORD dwStyle,
+	int x,
+	int y,
+	int nWidth,
+	int nHeight,
+	HWND hWndParent,
+	HMENU hMenu,
+	HINSTANCE hInstance,
+	VOID* lpParam);
+HWND CreateWindowExW(
+	DWORD dwExStyle,
+	LPCWSTR lpClassName,
+	LPCWSTR lpWindowName,
+	DWORD dwStyle,
+	int x,
+	int y,
+	int nWidth,
+	int nHeight,
+	HWND hWndParent,
+	HMENU hMenu,
+	HINSTANCE hInstance,
+	VOID* lpParam);
+//}}}
+
+BOOL DestroyWindow(HWND hWnd);
+
+BOOL ClientToScreen(HWND hWnd, POINT* lpPoint);
+
+BOOL ScreenToClient(HWND hWnd, POINT* lpPoint);
+
+//{{{ messages
+enum {
+	WM_NULL					  = 0x0000,
+	WM_CREATE				  = 0x0001,
+	WM_DESTROY				  = 0x0002,
+	WM_MOVE					  = 0x0003,
+	WM_SIZE					  = 0x0005,
+	WM_ACTIVATE				  = 0x0006,
+	WM_SETFOCUS				  = 0x0007,
+	WM_KILLFOCUS			  = 0x0008,
+	WM_ENABLE				  = 0x000A,
+	WM_SETREDRAW			  = 0x000B,
+	WM_SETTEXT				  = 0x000C,
+	WM_GETTEXT				  = 0x000D,
+	WM_GETTEXTLENGTH		  = 0x000E,
+	WM_PAINT				  = 0x000F,
+	WM_CLOSE				  = 0x0010,
+	WM_QUERYENDSESSION		  = 0x0011,
+	WM_QUERYOPEN			  = 0x0013,
+	WM_ENDSESSION			  = 0x0016,
+	WM_QUIT					  = 0x0012,
+	WM_ERASEBKGND			  = 0x0014,
+	WM_SYSCOLORCHANGE		  = 0x0015,
+	WM_SHOWWINDOW			  = 0x0018,
+	WM_WININICHANGE			  = 0x001A,
+	WM_SETTINGCHANGE		  = WM_WININICHANGE,
+	WM_DEVMODECHANGE		  = 0x001B,
+	WM_ACTIVATEAPP			  = 0x001C,
+	WM_FONTCHANGE			  = 0x001D,
+	WM_TIMECHANGE			  = 0x001E,
+	WM_CANCELMODE			  = 0x001F,
+	WM_SETCURSOR			  = 0x0020,
+	WM_MOUSEACTIVATE		  = 0x0021,
+	WM_CHILDACTIVATE		  = 0x0022,
+	WM_QUEUESYNC			  = 0x0023,
+	WM_GETMINMAXINFO		  = 0x0024,
+	WM_PAINTICON			  = 0x0026,
+	WM_ICONERASEBKGND		  = 0x0027,
+	WM_NEXTDLGCTL			  = 0x0028,
+	WM_SPOOLERSTATUS		  = 0x002A,
+	WM_DRAWITEM				  = 0x002B,
+	WM_MEASUREITEM			  = 0x002C,
+	WM_DELETEITEM			  = 0x002D,
+	WM_VKEYTOITEM			  = 0x002E,
+	WM_CHARTOITEM			  = 0x002F,
+	WM_SETFONT				  = 0x0030,
+	WM_GETFONT				  = 0x0031,
+	WM_SETHOTKEY			  = 0x0032,
+	WM_GETHOTKEY			  = 0x0033,
+	WM_QUERYDRAGICON		  = 0x0037,
+	WM_COMPAREITEM			  = 0x0039,
+	WM_GETOBJECT			  = 0x003D,
+	WM_COMPACTING			  = 0x0041,
+	WM_WINDOWPOSCHANGING	  = 0x0046,
+	WM_WINDOWPOSCHANGED		  = 0x0047,
+	WM_POWER				  = 0x0048,
+	WM_COPYDATA				  = 0x004A,
+	WM_CANCELJOURNAL		  = 0x004B,
+	WM_NOTIFY				  = 0x004E,
+	WM_INPUTLANGCHANGEREQUEST = 0x0050,
+	WM_INPUTLANGCHANGE		  = 0x0051,
+	WM_TCARD				  = 0x0052,
+	WM_HELP					  = 0x0053,
+	WM_USERCHANGED			  = 0x0054,
+	WM_NOTIFYFORMAT			  = 0x0055,
+	WM_CONTEXTMENU			  = 0x007B,
+	WM_STYLECHANGING		  = 0x007C,
+	WM_STYLECHANGED			  = 0x007D,
+	WM_DISPLAYCHANGE		  = 0x007E,
+	WM_GETICON				  = 0x007F,
+	WM_SETICON				  = 0x0080,
+	WM_NCCREATE				  = 0x0081,
+	WM_NCDESTROY			  = 0x0082,
+	WM_NCCALCSIZE			  = 0x0083,
+	WM_NCHITTEST			  = 0x0084,
+	WM_NCPAINT				  = 0x0085,
+	WM_NCACTIVATE			  = 0x0086,
+	WM_GETDLGCODE			  = 0x0087,
+	WM_SYNCPAINT			  = 0x0088,
+	WM_NCMOUSEMOVE			  = 0x00A0,
+	WM_NCLBUTTONDOWN		  = 0x00A1,
+	WM_NCLBUTTONUP			  = 0x00A2,
+	WM_NCLBUTTONDBLCLK		  = 0x00A3,
+	WM_NCRBUTTONDOWN		  = 0x00A4,
+	WM_NCRBUTTONUP			  = 0x00A5,
+	WM_NCRBUTTONDBLCLK		  = 0x00A6,
+	WM_NCMBUTTONDOWN		  = 0x00A7,
+	WM_NCMBUTTONUP			  = 0x00A8,
+	WM_NCMBUTTONDBLCLK		  = 0x00A9,
+	WM_NCXBUTTONDOWN		  = 0x00AB,
+	WM_NCXBUTTONUP			  = 0x00AC,
+	WM_NCXBUTTONDBLCLK		  = 0x00AD,
+	WM_INPUT				  = 0x00FF,
+	WM_KEYDOWN				  = 0x0100,
+	WM_KEYUP				  = 0x0101,
+	WM_CHAR					  = 0x0102,
+	WM_DEADCHAR				  = 0x0103,
+	WM_SYSKEYDOWN			  = 0x0104,
+	WM_SYSKEYUP				  = 0x0105,
+	WM_SYSCHAR				  = 0x0106,
+	WM_SYSDEADCHAR			  = 0x0107,
+	WM_UNICHAR				  = 0x0109,
+	WM_IME_STARTCOMPOSITION	  = 0x010D,
+	WM_IME_ENDCOMPOSITION	  = 0x010E,
+	WM_IME_COMPOSITION		  = 0x010F,
+	WM_IME_KEYLAST			  = 0x010F,
+	WM_INITDIALOG			  = 0x0110,
+	WM_COMMAND				  = 0x0111,
+	WM_SYSCOMMAND			  = 0x0112,
+	WM_TIMER				  = 0x0113,
+	WM_HSCROLL				  = 0x0114,
+	WM_VSCROLL				  = 0x0115,
+	WM_INITMENU				  = 0x0116,
+	WM_INITMENUPOPUP		  = 0x0117,
+	WM_MENUSELECT			  = 0x011F,
+	WM_MENUCHAR				  = 0x0120,
+	WM_ENTERIDLE			  = 0x0121,
+	WM_MENURBUTTONUP		  = 0x0122,
+	WM_MENUDRAG				  = 0x0123,
+	WM_MENUGETOBJECT		  = 0x0124,
+	WM_UNINITMENUPOPUP		  = 0x0125,
+	WM_MENUCOMMAND			  = 0x0126,
+	WM_CHANGEUISTATE		  = 0x0127,
+	WM_UPDATEUISTATE		  = 0x0128,
+	WM_QUERYUISTATE			  = 0x0129,
+	WM_CTLCOLORMSGBOX		  = 0x0132,
+	WM_CTLCOLOREDIT			  = 0x0133,
+	WM_CTLCOLORLISTBOX		  = 0x0134,
+	WM_CTLCOLORBTN			  = 0x0135,
+	WM_CTLCOLORDLG			  = 0x0136,
+	WM_CTLCOLORSCROLLBAR	  = 0x0137,
+	WM_CTLCOLORSTATIC		  = 0x0138,
+	WM_MOUSEFIRST			  = 0x0200,
+	WM_MOUSEMOVE			  = 0x0200,
+	WM_LBUTTONDOWN			  = 0x0201,
+	WM_LBUTTONUP			  = 0x0202,
+	WM_LBUTTONDBLCLK		  = 0x0203,
+	WM_RBUTTONDOWN			  = 0x0204,
+	WM_RBUTTONUP			  = 0x0205,
+	WM_RBUTTONDBLCLK		  = 0x0206,
+	WM_MBUTTONDOWN			  = 0x0207,
+	WM_MBUTTONUP			  = 0x0208,
+	WM_MBUTTONDBLCLK		  = 0x0209,
+	WM_MOUSEWHEEL			  = 0x020A,
+	WM_XBUTTONDOWN			  = 0x020B,
+	WM_XBUTTONUP			  = 0x020C,
+	WM_XBUTTONDBLCLK		  = 0x020D,
+	WM_PARENTNOTIFY			  = 0x0210,
+	WM_ENTERMENULOOP		  = 0x0211,
+	WM_EXITMENULOOP			  = 0x0212,
+	WM_NEXTMENU				  = 0x0213,
+	WM_SIZING				  = 0x0214,
+	WM_CAPTURECHANGED		  = 0x0215,
+	WM_MOVING				  = 0x0216,
+	WM_POWERBROADCAST		  = 0x0218,
+	WM_DEVICECHANGE			  = 0x0219,
+	WM_MDICREATE			  = 0x0220,
+	WM_MDIDESTROY			  = 0x0221,
+	WM_MDIACTIVATE			  = 0x0222,
+	WM_MDIRESTORE			  = 0x0223,
+	WM_MDINEXT				  = 0x0224,
+	WM_MDIMAXIMIZE			  = 0x0225,
+	WM_MDITILE				  = 0x0226,
+	WM_MDICASCADE			  = 0x0227,
+	WM_MDIICONARRANGE		  = 0x0228,
+	WM_MDIGETACTIVE			  = 0x0229,
+	WM_MDISETMENU			  = 0x0230,
+	WM_ENTERSIZEMOVE		  = 0x0231,
+	WM_EXITSIZEMOVE			  = 0x0232,
+	WM_DROPFILES			  = 0x0233,
+	WM_MDIREFRESHMENU		  = 0x0234,
+	WM_IME_SETCONTEXT		  = 0x0281,
+	WM_IME_NOTIFY			  = 0x0282,
+	WM_IME_CONTROL			  = 0x0283,
+	WM_IME_COMPOSITIONFULL	  = 0x0284,
+	WM_IME_SELECT			  = 0x0285,
+	WM_IME_CHAR				  = 0x0286,
+	WM_IME_REQUEST			  = 0x0288,
+	WM_IME_KEYDOWN			  = 0x0290,
+	WM_IME_KEYUP			  = 0x0291,
+	WM_MOUSEHOVER			  = 0x02A1,
+	WM_MOUSELEAVE			  = 0x02A3,
+	WM_NCMOUSEHOVER			  = 0x02A0,
+	WM_NCMOUSELEAVE			  = 0x02A2,
+	WM_WTSSESSION_CHANGE	  = 0x02B1,
+	WM_TABLET_FIRST			  = 0x02C0,
+	WM_TABLET_LAST			  = 0x02DF,
+	WM_CUT					  = 0x0300,
+	WM_COPY					  = 0x0301,
+	WM_PASTE				  = 0x0302,
+	WM_CLEAR				  = 0x0303,
+	WM_UNDO					  = 0x0304,
+	WM_RENDERFORMAT			  = 0x0305,
+	WM_RENDERALLFORMATS		  = 0x0306,
+	WM_DESTROYCLIPBOARD		  = 0x0307,
+	WM_DRAWCLIPBOARD		  = 0x0308,
+	WM_PAINTCLIPBOARD		  = 0x0309,
+	WM_VSCROLLCLIPBOARD		  = 0x030A,
+	WM_SIZECLIPBOARD		  = 0x030B,
+	WM_ASKCBFORMATNAME		  = 0x030C,
+	WM_CHANGECBCHAIN		  = 0x030D,
+	WM_HSCROLLCLIPBOARD		  = 0x030E,
+	WM_QUERYNEWPALETTE		  = 0x030F,
+	WM_PALETTEISCHANGING	  = 0x0310,
+	WM_PALETTECHANGED		  = 0x0311,
+	WM_HOTKEY				  = 0x0312,
+	WM_PRINT				  = 0x0317,
+	WM_PRINTCLIENT			  = 0x0318,
+	WM_APPCOMMAND			  = 0x0319,
+	WM_THEMECHANGED			  = 0x031A,
+	WM_HANDHELDFIRST		  = 0x0358,
+	WM_HANDHELDLAST			  = 0x035F,
+	WM_AFXFIRST				  = 0x0360,
+	WM_AFXLAST				  = 0x037F,
+	WM_PENWINFIRST			  = 0x0380,
+	WM_PENWINLAST			  = 0x038F,
+	WM_APP					  = 0x8000,
+	WM_USER					  = 0x0400
+}
+enum {
+	PBT_APMQUERYSUSPEND       = 0x0000,
+	PBT_APMQUERYSTANDBY       = 0x0001,
+	PBT_APMQUERYSUSPENDFAILED = 0x0002,
+	PBT_APMQUERYSTANDBYFAILED = 0x0003,
+	PBT_APMSUSPEND            = 0x0004,
+	PBT_APMSTANDBY            = 0x0005,
+	PBT_APMRESUMECRITICAL     = 0x0006,
+	PBT_APMRESUMESUSPEND      = 0x0007,
+	PBT_APMRESUMESTANDBY      = 0x0008,
+	PBTF_APMRESUMEFROMFAILURE = 0x00000001,
+	PBT_APMBATTERYLOW         = 0x0009,
+	PBT_APMPOWERSTATUSCHANGE  = 0x000A,
+	PBT_APMOEMEVENT           = 0x000B,
+	PBT_APMRESUMEAUTOMATIC    = 0x0012
+}
+//}}}
+
+enum {
+	MK_LBUTTON =  0x0001,
+	MK_RBUTTON =  0x0002,
+	MK_SHIFT =	  0x0004,
+	MK_CONTROL =  0x0008,
+	MK_MBUTTON =  0x0010,
+	MK_XBUTTON1 = 0x0020,
+	MK_XBUTTON2 = 0x0040
+}
+
+enum {
+	WMSZ_LEFT		 = 1,
+	WMSZ_RIGHT		 = 2,
+	WMSZ_TOP		 = 3,
+	WMSZ_TOPLEFT	 = 4,
+	WMSZ_TOPRIGHT	 = 5,
+	WMSZ_BOTTOM		 = 6,
+	WMSZ_BOTTOMLEFT	 = 7,
+	WMSZ_BOTTOMRIGHT = 8
+}
+
+enum {
+	SIZE_RESTORED  = 0,
+	SIZE_MINIMIZED = 1,
+	SIZE_MAXIMIZED = 2,
+	SIZE_MAXSHOW   = 3,
+	SIZE_MAXHIDE   = 4
+}
+
+enum {
+	SW_HIDE = 0,
+	SW_SHOWNORMAL = 1,
+	SW_NORMAL = 1,
+	SW_SHOWMINIMIZED = 2,
+	SW_SHOWMAXIMIZED = 3,
+	SW_MAXIMIZE = 3,
+	SW_SHOWNOACTIVATE = 4,
+	SW_SHOW = 5,
+	SW_MINIMIZE = 6,
+	SW_SHOWMINNOACTIVE = 7,
+	SW_SHOWNA = 8,
+	SW_RESTORE = 9,
+	SW_SHOWDEFAULT = 10,
+	SW_FORCEMINIMIZE = 11,
+	SW_MAX = 11
+}
+
+enum {
+	GWL_STYLE =	  -16,
+	GWL_EXSTYLE = -20
+}
+
+DWORD GetClassLongA(HWND hWnd, int nIndex);
+DWORD GetClassLongW(HWND hWnd, int nIndex);
+
+DWORD SetClassLongA(HWND hWnd, int nIndex, LONG dwNewLong);
+DWORD SetClassLongW(HWND hWnd, int nIndex, LONG dwNewLong);
+
+ULONG_PTR GetClassLongPtrA(HWND hWnd, int nIndex);
+ULONG_PTR GetClassLongPtrW(HWND hWnd, int nIndex);
+
+ULONG_PTR SetClassLongPtrA(HWND hWnd, int nIndex, LONG_PTR dwNewLong);
+ULONG_PTR SetClassLongPtrW(HWND hWnd, int nIndex, LONG_PTR dwNewLong);
+
+LONG GetWindowLongA(HWND hWnd, int nIndex);
+LONG GetWindowLongW(HWND hWnd, int nIndex);
+
+LONG SetWindowLongA(HWND hWnd, int nIndex, LONG dwNewLong);
+LONG SetWindowLongW(HWND hWnd, int nIndex, LONG dwNewLong);
+
+LONG_PTR GetWindowLongPtrA(HWND hWnd, int nIndex);
+LONG_PTR GetWindowLongPtrW(HWND hWnd, int nIndex);
+
+LONG_PTR SetWindowLongPtrA(HWND hWnd, int nIndex, LONG_PTR dwNewLong);
+LONG_PTR SetWindowLongPtrW(HWND hWnd, int nIndex, LONG_PTR dwNewLong);
+
+BOOL ShowWindow(HWND hWnd, int nCmdShow);
+
+//Windows 98 required
+BOOL AnimateWindow(HWND hWnd, DWORD dwTime, DWORD dwFlags);
+
+BOOL IsWindowVisible(HWND hWnd);
+
+BOOL IsWindow(HWND hWnd);
+
+BOOL GetWindowRect(HWND hWnd, RECT* lpRect);
+
+BOOL GetClientRect(HWND hWnd, RECT* lpRect);
+
+enum {
+	SWP_NOSIZE =	   0x0001,
+	SWP_NOMOVE =	   0x0002,
+	SWP_NOZORDER =	   0x0004,
+	SWP_NOACTIVATE =   0x0010,
+	SWP_FRAMECHANGED = 0x0020
+}
+
+BOOL SetWindowPos(HWND hWnd, HWND hWndInsertAfter, int x, int y, int cx, int cy, UINT uFlags);
+
+BOOL MoveWindow(HWND hWnd, int x, int y, int nWidth, int nHeight, BOOL bRepaint);
+
+int SetWindowRgn(HWND hWnd, HRGN hRgn, BOOL bRedraw);
+
+enum {
+	ULW_COLORKEY = 0x00000001,
+	ULW_ALPHA =	   0x00000002,
+	ULW_OPAQUE =   0x00000004
+}
+
+BOOL UpdateLayeredWindow(
+	HWND hWnd,
+	HDC hdcDst,
+	POINT* pptDst,
+	SIZE* psize,
+	HDC hdcSrc,
+	POINT* pptSrc,
+	COLORREF crKey,
+	BLENDFUNCTION* pblend,
+	DWORD dwFlags);
+
+HWND SetParent(HWND hWndChild, HWND hWndNewParent);
+
+HWND GetParent(HWND hWnd);
+
+BOOL IsChild(HWND hWndParent, HWND hWnd);
+
+BOOL EnableWindow(HWND hWnd, BOOL bEnable);
+
+enum {
+	GW_HWNDFIRST = 0,
+	GW_HWNDLAST =  1,
+	GW_HWNDNEXT =  2,
+	GW_HWNDPREV =  3,
+	GW_OWNER =	   4,
+	GW_CHILD =	   5
+}
+
+HWND GetWindow(HWND hWnd, UINT uCmd);
+
+BOOL GetWindowInfo(HWND hwnd, WINDOWINFO* pwi);
+
+int GetWindowTextLength(HWND hWnd);
+
+int GetWindowTextA(HWND hWnd, LPSTR lpString, int nMaxCount);
+int GetWindowTextW(HWND hWnd, LPWSTR lpString, int nMaxCount);
+
+BOOL SetWindowTextA(HWND hWnd, LPCSTR lpString);
+BOOL SetWindowTextW(HWND hWnd, LPCWSTR lpString);
+
+UINT GetWindowModuleFileNameA(HWND hWnd, LPSTR lpszFileName, UINT cchFileNameMax);
+UINT GetWindowModuleFileNameW(HWND hWnd, LPWSTR lpszFileName, UINT cchFileNameMax);
+
+BOOL SetPropA(HWND hWnd, LPCSTR lpString, HANDLE hData);
+BOOL SetPropW(HWND hWnd, LPCWSTR lpString, HANDLE hData);
+
+HANDLE GetPropA(HWND hWnd, LPCSTR lpString);
+HANDLE GetPropW(HWND hWnd, LPCWSTR lpString);
+
+HANDLE RemovePropA(HWND hWnd, LPCSTR lpString);
+HANDLE RemovePropW(HWND hWnd, LPCWSTR lpString);
+
+DWORD CommDlgExtendedError();
+enum { FNERR_BUFFERTOOSMALL = 0x3003 }
+
+enum {
+	OFN_READONLY			 = 0x00000001,
+	OFN_OVERWRITEPROMPT		 = 0x00000002,
+	OFN_HIDEREADONLY		 = 0x00000004,
+	OFN_NOCHANGEDIR			 = 0x00000008,
+	OFN_SHOWHELP			 = 0x00000010,
+	OFN_ENABLEHOOK			 = 0x00000020,
+	OFN_ENABLETEMPLATE		 = 0x00000040,
+	OFN_ENABLETEMPLATEHANDLE = 0x00000080,
+	OFN_NOVALIDATE			 = 0x00000100,
+	OFN_ALLOWMULTISELECT	 = 0x00000200,
+	OFN_EXTENSIONDIFFERENT	 = 0x00000400,
+	OFN_PATHMUSTEXIST		 = 0x00000800,
+	OFN_FILEMUSTEXIST		 = 0x00001000,
+	OFN_CREATEPROMPT		 = 0x00002000,
+	OFN_SHAREAWARE			 = 0x00004000,
+	OFN_NOREADONLYRETURN	 = 0x00008000,
+	OFN_NOTESTFILECREATE	 = 0x00010000,
+	OFN_NONETWORKBUTTON		 = 0x00020000,
+	OFN_NOLONGNAMES			 = 0x00040000,
+	OFN_EXPLORER			 = 0x00080000,
+	OFN_NODEREFERENCELINKS	 = 0x00100000,
+	OFN_LONGNAMES			 = 0x00200000,
+	OFN_ENABLEINCLUDENOTIFY	 = 0x00400000,
+	OFN_ENABLESIZING		 = 0x00800000,
+	OFN_DONTADDTORECENT		 = 0x02000000,
+	OFN_FORCESHOWHIDDEN		 = 0x10000000
+}
+
+BOOL GetOpenFileNameA(OPENFILENAME* lpofn);
+BOOL GetOpenFileNameW(OPENFILENAME* lpofn);
+
+BOOL GetSaveFileNameA(OPENFILENAME* lpofn);
+BOOL GetSaveFileNameW(OPENFILENAME* lpofn);
+
+//}}}
+
+//{{{ painting functions
+BOOL InvalidateRect(HWND hWnd, RECT* lpRect, BOOL bErase);
+
+BOOL ValidateRect(HWND hWnd, RECT* lpRect);
+
+BOOL UpdateWindow(HWND hWnd);
+
+HWND WindowFromDC(HDC hdc);
+
+HDC BeginPaint(HWND hWnd, PAINTSTRUCT* lpPaint);
+
+BOOL EndPaint(HWND hWnd, PAINTSTRUCT* lpPaint);
+//}}}
+
+//{{{ device context functions
+HDC GetDC(HWND hWnd);
+
+int ReleaseDC(HWND hWnd, HDC hDC);
+
+HDC CreateCompatibleDC(HDC hdc);
+
+BOOL DeleteDC(HDC hdc);
+
+HGDIOBJ SelectObject(HDC hdc, HGDIOBJ hObject);
+
+BOOL DeleteObject(HGDIOBJ hObject);
+
+int GetObjectW(HANDLE h, int c, VOID* pv);
+
+HBITMAP CreateCompatibleBitmap(HDC hdc, int cx, int cy);
+
+HBITMAP CreateDIBSection(
+	HDC hdc,
+	BITMAPINFO* lpbmi,
+	UINT usage,
+	VOID** ppvBits,
+	HANDLE hSection,
+	DWORD offset);
+
+int GetDIBits(HDC hdc, HBITMAP hbm, UINT start, UINT cLines, VOID* lpvBits, BITMAPINFO* lpbmi, UINT usage);
+
+enum {
+	DIB_RGB_COLORS = 0
+}
+enum {
+	BI_RGB = 0
+}
+
+enum {
+	SRCCOPY = 0x00CC0020
+}
+
+BOOL BitBlt(HDC hdc, int x, int y, int cx, int cy, HDC hdcSrc, int x1, int y1, DWORD rop);
+
+enum {
+	AC_SRC_OVER = 0x00,
+	AC_SRC_ALPHA = 0x01
+}
+
+int EnumFontFamiliesExA(
+	HDC hdc, LOGFONT* lpLogfont,
+	FONTENUMPROCA lpProc, LPARAM lParam, DWORD dwFlags);
+int EnumFontFamiliesExW(
+	HDC hdc, LOGFONT* lpLogfont,
+	FONTENUMPROCW lpProc, LPARAM lParam, DWORD dwFlags);
+
+alias int function(ENUMLOGFONTEX*, TEXTMETRIC*, DWORD, LPARAM) FONTENUMPROCA;
+alias int function(ENUMLOGFONTEX*, TEXTMETRIC*, DWORD, LPARAM) FONTENUMPROCW;
+//}}}
+
+//{{{ message functions
+BOOL GetMessageA(MSG* lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);
+BOOL GetMessageW(MSG* lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);
+
+BOOL TranslateMessage(MSG* lpMsg);
+
+LRESULT DispatchMessageA(MSG* lpMsg);
+LRESULT DispatchMessageW(MSG* lpMsg);
+
+BOOL PostMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+void PostQuitMessage(int nExitCode);
+
+LRESULT SendMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+//}}}
+
+//{{{ clipboard functions
+enum {
+	CF_TEXT			= 1,
+	CF_BITMAP		= 2,
+	CF_METAFILEPICT = 3,
+	CF_SYLK			= 4,
+	CF_DIF			= 5,
+	CF_TIFF			= 6,
+	CF_OEMTEXT		= 7,
+	CF_DIB			= 8,
+	CF_PALETTE		= 9,
+	CF_PENDATA		= 10,
+	CF_RIFF			= 11,
+	CF_WAVE			= 12,
+	CF_UNICODETEXT	= 13,
+	CF_ENHMETAFILE	= 14,
+	CF_HDROP		= 15,
+	CF_LOCALE		= 16
+}
+
+BOOL OpenClipboard(HWND hWndNewOwner);
+
+BOOL CloseClipboard();
+
+BOOL IsClipboardFormatAvailable(UINT format);
+
+HANDLE GetClipboardData(UINT uFormat);
+
+HANDLE SetClipboardData(UINT uFormat, HANDLE hMem);
+
+BOOL EmptyClipboard();
+//}}}
+
+//{{{ mouse functions
+enum {
+	XBUTTON1 = 0x0001,
+	XBUTTON2 = 0x0002
+}
+
+HWND SetCapture(HWND hWnd);
+
+BOOL ReleaseCapture();
+
+enum {
+	TME_HOVER	  = 0x00000001,
+	TME_LEAVE	  = 0x00000002,
+	TME_NONCLIENT = 0x00000010
+}
+BOOL TrackMouseEvent(TRACKMOUSEEVENT* lpEventTrack);
+
+HCURSOR SetCursor(HCURSOR hCursor);
+
+BOOL GetCursorPos(POINT* lpPoint);
+//}}}
+
+//{{{ keyboard functions
+//{{{ keys
+enum {
+	VK_LBUTTON			   = 0x01,
+	VK_RBUTTON			   = 0x02,
+	VK_CANCEL			   = 0x03,
+	VK_MBUTTON			   = 0x04,
+	VK_XBUTTON1			   = 0x05,
+	VK_XBUTTON2			   = 0x06,
+	VK_BACK				   = 0x08,
+	VK_TAB				   = 0x09,
+	VK_CLEAR			   = 0x0C,
+	VK_RETURN			   = 0x0D,
+	VK_SHIFT			   = 0x10,
+	VK_CONTROL			   = 0x11,
+	VK_MENU				   = 0x12,
+	VK_PAUSE			   = 0x13,
+	VK_CAPITAL			   = 0x14,
+	VK_KANA				   = 0x15,
+	VK_HANGEUL			   = 0x15,
+	VK_HANGUL			   = 0x15,
+	VK_JUNJA			   = 0x17,
+	VK_FINAL			   = 0x18,
+	VK_HANJA			   = 0x19,
+	VK_KANJI			   = 0x19,
+	VK_ESCAPE			   = 0x1B,
+	VK_CONVERT			   = 0x1C,
+	VK_NONCONVERT		   = 0x1D,
+	VK_ACCEPT			   = 0x1E,
+	VK_MODECHANGE		   = 0x1F,
+	VK_SPACE			   = 0x20,
+	VK_PRIOR			   = 0x21,
+	VK_NEXT				   = 0x22,
+	VK_END				   = 0x23,
+	VK_HOME				   = 0x24,
+	VK_LEFT				   = 0x25,
+	VK_UP				   = 0x26,
+	VK_RIGHT			   = 0x27,
+	VK_DOWN				   = 0x28,
+	VK_SELECT			   = 0x29,
+	VK_PRINT			   = 0x2A,
+	VK_EXECUTE			   = 0x2B,
+	VK_SNAPSHOT			   = 0x2C,
+	VK_INSERT			   = 0x2D,
+	VK_DELETE			   = 0x2E,
+	VK_HELP				   = 0x2F,
+	VK_LWIN				   = 0x5B,
+	VK_RWIN				   = 0x5C,
+	VK_APPS				   = 0x5D,
+	VK_SLEEP			   = 0x5F,
+	VK_NUMPAD0			   = 0x60,
+	VK_NUMPAD1			   = 0x61,
+	VK_NUMPAD2			   = 0x62,
+	VK_NUMPAD3			   = 0x63,
+	VK_NUMPAD4			   = 0x64,
+	VK_NUMPAD5			   = 0x65,
+	VK_NUMPAD6			   = 0x66,
+	VK_NUMPAD7			   = 0x67,
+	VK_NUMPAD8			   = 0x68,
+	VK_NUMPAD9			   = 0x69,
+	VK_MULTIPLY			   = 0x6A,
+	VK_ADD				   = 0x6B,
+	VK_SEPARATOR		   = 0x6C,
+	VK_SUBTRACT			   = 0x6D,
+	VK_DECIMAL			   = 0x6E,
+	VK_DIVIDE			   = 0x6F,
+	VK_F1				   = 0x70,
+	VK_F2				   = 0x71,
+	VK_F3				   = 0x72,
+	VK_F4				   = 0x73,
+	VK_F5				   = 0x74,
+	VK_F6				   = 0x75,
+	VK_F7				   = 0x76,
+	VK_F8				   = 0x77,
+	VK_F9				   = 0x78,
+	VK_F10				   = 0x79,
+	VK_F11				   = 0x7A,
+	VK_F12				   = 0x7B,
+	VK_F13				   = 0x7C,
+	VK_F14				   = 0x7D,
+	VK_F15				   = 0x7E,
+	VK_F16				   = 0x7F,
+	VK_F17				   = 0x80,
+	VK_F18				   = 0x81,
+	VK_F19				   = 0x82,
+	VK_F20				   = 0x83,
+	VK_F21				   = 0x84,
+	VK_F22				   = 0x85,
+	VK_F23				   = 0x86,
+	VK_F24				   = 0x87,
+	VK_NUMLOCK			   = 0x90,
+	VK_SCROLL			   = 0x91,
+	VK_OEM_NEC_EQUAL	   = 0x92,
+	VK_OEM_FJ_JISHO		   = 0x92,
+	VK_OEM_FJ_MASSHOU	   = 0x93,
+	VK_OEM_FJ_TOUROKU	   = 0x94,
+	VK_OEM_FJ_LOYA		   = 0x95,
+	VK_OEM_FJ_ROYA		   = 0x96,
+	VK_LSHIFT			   = 0xA0,
+	VK_RSHIFT			   = 0xA1,
+	VK_LCONTROL			   = 0xA2,
+	VK_RCONTROL			   = 0xA3,
+	VK_LMENU			   = 0xA4,
+	VK_RMENU			   = 0xA5,
+	VK_BROWSER_BACK		   = 0xA6,
+	VK_BROWSER_FORWARD	   = 0xA7,
+	VK_BROWSER_REFRESH	   = 0xA8,
+	VK_BROWSER_STOP		   = 0xA9,
+	VK_BROWSER_SEARCH	   = 0xAA,
+	VK_BROWSER_FAVORITES   = 0xAB,
+	VK_BROWSER_HOME		   = 0xAC,
+	VK_VOLUME_MUTE		   = 0xAD,
+	VK_VOLUME_DOWN		   = 0xAE,
+	VK_VOLUME_UP		   = 0xAF,
+	VK_MEDIA_NEXT_TRACK	   = 0xB0,
+	VK_MEDIA_PREV_TRACK	   = 0xB1,
+	VK_MEDIA_STOP		   = 0xB2,
+	VK_MEDIA_PLAY_PAUSE	   = 0xB3,
+	VK_LAUNCH_MAIL		   = 0xB4,
+	VK_LAUNCH_MEDIA_SELECT = 0xB5,
+	VK_LAUNCH_APP1		   = 0xB6,
+	VK_LAUNCH_APP2		   = 0xB7,
+	VK_OEM_1			   = 0xBA,
+	VK_OEM_PLUS			   = 0xBB,
+	VK_OEM_COMMA		   = 0xBC,
+	VK_OEM_MINUS		   = 0xBD,
+	VK_OEM_PERIOD		   = 0xBE,
+	VK_OEM_2			   = 0xBF,
+	VK_OEM_3			   = 0xC0,
+	VK_OEM_4			   = 0xDB,
+	VK_OEM_5			   = 0xDC,
+	VK_OEM_6			   = 0xDD,
+	VK_OEM_7			   = 0xDE,
+	VK_OEM_8			   = 0xDF,
+	VK_OEM_AX			   = 0xE1,
+	VK_OEM_102			   = 0xE2,
+	VK_ICO_HELP			   = 0xE3,
+	VK_ICO_00			   = 0xE4,
+	VK_PROCESSKEY		   = 0xE5,
+	VK_ICO_CLEAR		   = 0xE6,
+	VK_PACKET			   = 0xE7,
+	VK_OEM_RESET		   = 0xE9,
+	VK_OEM_JUMP			   = 0xEA,
+	VK_OEM_PA1			   = 0xEB,
+	VK_OEM_PA2			   = 0xEC,
+	VK_OEM_PA3			   = 0xED,
+	VK_OEM_WSCTRL		   = 0xEE,
+	VK_OEM_CUSEL		   = 0xEF,
+	VK_OEM_ATTN			   = 0xF0,
+	VK_OEM_FINISH		   = 0xF1,
+	VK_OEM_COPY			   = 0xF2,
+	VK_OEM_AUTO			   = 0xF3,
+	VK_OEM_ENLW			   = 0xF4,
+	VK_OEM_BACKTAB		   = 0xF5,
+	VK_ATTN				   = 0xF6,
+	VK_CRSEL			   = 0xF7,
+	VK_EXSEL			   = 0xF8,
+	VK_EREOF			   = 0xF9,
+	VK_PLAY				   = 0xFA,
+	VK_ZOOM				   = 0xFB,
+	VK_NONAME			   = 0xFC,
+	VK_PA1				   = 0xFD,
+	VK_OEM_CLEAR		   = 0xFE
+}
+//}}}
+
+SHORT GetKeyState(int nVirtKey);
+//}}}
+
+//{{{ system functions
+//const LPTSTR IDC_ARROW		 = cast(LPTSTR)32512u;
+//const LPTSTR IDC_IBEAM		 = cast(LPTSTR)32513u;
+//const LPTSTR IDC_WAIT		 = cast(LPTSTR)32514u;
+//const LPTSTR IDC_CROSS		 = cast(LPTSTR)32515u;
+//const LPTSTR IDC_UPARROW	 = cast(LPTSTR)32516u;
+//const LPTSTR IDC_SIZE		 = cast(LPTSTR)32640u;
+//const LPTSTR IDC_ICON		 = cast(LPTSTR)32641u;
+//const LPTSTR IDC_SIZENWSE	 = cast(LPTSTR)32642u;
+//const LPTSTR IDC_SIZENESW	 = cast(LPTSTR)32643u;
+//const LPTSTR IDC_SIZEWE		 = cast(LPTSTR)32644u;
+//const LPTSTR IDC_SIZENS		 = cast(LPTSTR)32645u;
+//const LPTSTR IDC_SIZEALL	 = cast(LPTSTR)32646u;
+//const LPTSTR IDC_NO			 = cast(LPTSTR)32648u;
+//const LPTSTR IDC_HAND		 = cast(LPTSTR)32649u;
+//const LPTSTR IDC_APPSTARTING = cast(LPTSTR)32650u;
+//const LPTSTR IDC_HELP		 = cast(LPTSTR)32651u;
+
+//const LPTSTR IDI_APPLICATION = cast(LPTSTR)32512;
+//const LPTSTR IDI_HAND		 = cast(LPTSTR)32513;
+//const LPTSTR IDI_QUESTION	 = cast(LPTSTR)32514;
+//const LPTSTR IDI_EXCLAMATION = cast(LPTSTR)32515;
+//const LPTSTR IDI_ASTERISK	 = cast(LPTSTR)32516;
+//const LPTSTR IDI_WINLOGO	 = cast(LPTSTR)32517;
+//const LPTSTR IDI_WARNING	 = IDI_EXCLAMATION;
+//const LPTSTR IDI_ERROR		 = IDI_HAND;
+//const LPTSTR IDI_INFORMATION = IDI_ASTERISK;
+
+enum {
+	OIC_SAMPLE      = 32512,
+	OIC_HAND        = 32513,
+	OIC_QUES        = 32514,
+	OIC_BANG        = 32515,
+	OIC_NOTE        = 32516,
+	OIC_WINLOGO     = 32517,
+	OIC_WARNING     = OIC_BANG,
+	OIC_ERROR       = OIC_HAND,
+	OIC_INFORMATION = OIC_NOTE
+}
+
+enum {
+	OCR_NORMAL      = 32512,
+	OCR_IBEAM       = 32513,
+	OCR_WAIT        = 32514,
+	OCR_CROSS       = 32515,
+	OCR_UP          = 32516,
+	OCR_SIZENWSE    = 32642,
+	OCR_SIZENESW    = 32643,
+	OCR_SIZEWE      = 32644,
+	OCR_SIZENS      = 32645,
+	OCR_SIZEALL     = 32646,
+	OCR_NO          = 32648,
+	OCR_HAND        = 32649,
+	OCR_APPSTARTING = 32650
+}
+
+enum {
+	IMAGE_BITMAP = 0,
+	IMAGE_ICON	 = 1,
+	IMAGE_CURSOR = 2
+}
+
+enum {
+	LR_DEFAULTCOLOR		= 0x0000,
+	LR_MONOCHROME		= 0x0001,
+	LR_COLOR			= 0x0002,
+	LR_COPYRETURNORG	= 0x0004,
+	LR_COPYDELETEORG	= 0x0008,
+	LR_LOADFROMFILE		= 0x0010,
+	LR_LOADTRANSPARENT	= 0x0020,
+	LR_DEFAULTSIZE		= 0x0040,
+	LR_VGACOLOR			= 0x0080,
+	LR_LOADMAP3DCOLORS	= 0x1000,
+	LR_CREATEDIBSECTION = 0x2000,
+	LR_COPYFROMRESOURCE = 0x4000,
+	LR_SHARED			= 0x8000
+}
+
+HANDLE LoadImageA(
+	HINSTANCE hInst,
+	LPCSTR name,
+	UINT type,
+	int cx,
+	int cy,
+	UINT fuLoad);
+HANDLE LoadImageW(
+	HINSTANCE hInst,
+	LPCWSTR name,
+	UINT type,
+	int cx,
+	int cy,
+	UINT fuLoad);
+
+HICON CreateIconFromResource(
+	BYTE* presbits,
+	DWORD dwResSize,
+	BOOL fIcon,
+	DWORD dwVer);
+
+enum {
+	SPI_GETNONCLIENTMETRICS = 0x0029,
+	SPI_GETWORKAREA			= 0x0030,
+	SPI_GETWHEELSCROLLLINES = 0x0068
+}
+
+BOOL SystemParametersInfoA(UINT uiAction, UINT uiParam, VOID* pvParam, UINT fWinIni);
+BOOL SystemParametersInfoW(UINT uiAction, UINT uiParam, VOID* pvParam, UINT fWinIni);
+
+enum {
+	COLOR_SCROLLBAR       = 0,
+	COLOR_BACKGROUND      = 1,
+	COLOR_ACTIVECAPTION   = 2,
+	COLOR_INACTIVECAPTION = 3,
+	COLOR_MENU            = 4,
+	COLOR_WINDOW          = 5,
+	COLOR_WINDOWFRAME     = 6,
+	COLOR_MENUTEXT        = 7,
+	COLOR_WINDOWTEXT      = 8,
+	COLOR_CAPTIONTEXT     = 9,
+	COLOR_ACTIVEBORDER    = 10,
+	COLOR_INACTIVEBORDER  = 11,
+	COLOR_APPWORKSPACE    = 12,
+	COLOR_HIGHLIGHT       = 13,
+	COLOR_HIGHLIGHTTEXT   = 14,
+	COLOR_BTNFACE         = 15,
+	COLOR_BTNSHADOW       = 16,
+	COLOR_GRAYTEXT        = 17,
+	COLOR_BTNTEXT         = 18,
+	COLOR_INACTIVECAPTIONTEXT = 19,
+	COLOR_BTNHIGHLIGHT    = 20,
+
+	COLOR_3DDKSHADOW      = 21,
+	COLOR_3DLIGHT         = 22,
+	COLOR_INFOTEXT        = 23,
+	COLOR_INFOBK          = 24,
+
+	COLOR_HOTLIGHT        = 26,
+	COLOR_GRADIENTACTIVECAPTION = 27,
+	COLOR_GRADIENTINACTIVECAPTION = 28,
+	COLOR_MENUHILIGHT     = 29,
+	COLOR_MENUBAR         = 30,
+
+	COLOR_DESKTOP         = COLOR_BACKGROUND,
+	COLOR_3DFACE          = COLOR_BTNFACE,
+	COLOR_3DSHADOW        = COLOR_BTNSHADOW,
+	COLOR_3DHIGHLIGHT     = COLOR_BTNHIGHLIGHT,
+	COLOR_3DHILIGHT       = COLOR_BTNHIGHLIGHT,
+	COLOR_BTNHILIGHT      = COLOR_BTNHIGHLIGHT
+}
+
+DWORD GetSysColor(int nIndex);
+
+enum {
+	SM_CXSCREEN          = 0,
+	SM_CYSCREEN          = 1,
+	SM_CXVSCROLL         = 2,
+	SM_CYHSCROLL         = 3,
+	SM_CYCAPTION         = 4,
+	SM_CXBORDER          = 5,
+	SM_CYBORDER          = 6,
+	SM_CXDLGFRAME        = 7,
+	SM_CYDLGFRAME        = 8,
+	SM_CYVTHUMB          = 9,
+	SM_CXHTHUMB          = 10,
+	SM_CXICON            = 11,
+	SM_CYICON            = 12,
+	SM_CXCURSOR          = 13,
+	SM_CYCURSOR          = 14,
+	SM_CYMENU            = 15,
+	SM_CXFULLSCREEN      = 16,
+	SM_CYFULLSCREEN      = 17,
+	SM_CYKANJIWINDOW     = 18,
+	SM_MOUSEPRESENT      = 19,
+	SM_CYVSCROLL         = 20,
+	SM_CXHSCROLL         = 21,
+	SM_DEBUG             = 22,
+	SM_SWAPBUTTON        = 23,
+	SM_RESERVED1         = 24,
+	SM_RESERVED2         = 25,
+	SM_RESERVED3         = 26,
+	SM_RESERVED4         = 27,
+	SM_CXMIN             = 28,
+	SM_CYMIN             = 29,
+	SM_CXSIZE            = 30,
+	SM_CYSIZE            = 31,
+	SM_CXFRAME           = 32,
+	SM_CYFRAME           = 33,
+	SM_CXMINTRACK        = 34,
+	SM_CYMINTRACK        = 35,
+	SM_CXDOUBLECLK       = 36,
+	SM_CYDOUBLECLK       = 37,
+	SM_CXICONSPACING     = 38,
+	SM_CYICONSPACING     = 39,
+	SM_MENUDROPALIGNMENT = 40,
+	SM_PENWINDOWS        = 41,
+	SM_DBCSENABLED       = 42,
+	SM_CMOUSEBUTTONS     = 43,
+	SM_CXFIXEDFRAME      = SM_CXDLGFRAME,
+	SM_CYFIXEDFRAME      = SM_CYDLGFRAME,
+	SM_CXSIZEFRAME       = SM_CXFRAME,
+	SM_CYSIZEFRAME       = SM_CYFRAME,
+	SM_SECURE            = 44,
+	SM_CXEDGE            = 45,
+	SM_CYEDGE            = 46,
+	SM_CXMINSPACING      = 47,
+	SM_CYMINSPACING      = 48,
+	SM_CXSMICON          = 49,
+	SM_CYSMICON          = 50,
+	SM_CYSMCAPTION       = 51,
+	SM_CXSMSIZE          = 52,
+	SM_CYSMSIZE          = 53,
+	SM_CXMENUSIZE        = 54,
+	SM_CYMENUSIZE        = 55,
+	SM_ARRANGE           = 56,
+	SM_CXMINIMIZED       = 57,
+	SM_CYMINIMIZED       = 58,
+	SM_CXMAXTRACK        = 59,
+	SM_CYMAXTRACK        = 60,
+	SM_CXMAXIMIZED       = 61,
+	SM_CYMAXIMIZED       = 62,
+	SM_NETWORK           = 63,
+	SM_CLEANBOOT         = 67,
+	SM_CXDRAG            = 68,
+	SM_CYDRAG            = 69,
+	SM_SHOWSOUNDS        = 70,
+	SM_CXMENUCHECK       = 71,
+	SM_CYMENUCHECK       = 72,
+	SM_SLOWMACHINE       = 73,
+	SM_MIDEASTENABLED    = 74,
+	SM_MOUSEWHEELPRESENT = 75,
+	SM_XVIRTUALSCREEN    = 76,
+	SM_YVIRTUALSCREEN    = 77,
+	SM_CXVIRTUALSCREEN   = 78,
+	SM_CYVIRTUALSCREEN   = 79,
+	SM_CMONITORS         = 80,
+	SM_SAMEDISPLAYFORMAT = 81,
+	SM_IMMENABLED        = 82,
+	SM_CXFOCUSBORDER     = 83,
+	SM_CYFOCUSBORDER     = 84,
+	SM_TABLETPC          = 86,
+	SM_MEDIACENTER       = 87,
+	SM_STARTER           = 88,
+	SM_SERVERR2          = 89
+}
+
+int GetSystemMetrics(int nIndex);
+
+VOID GetSystemInfo(SYSTEM_INFO* lpSystemInfo);
+
+BOOL GetVersionExA(OSVERSIONINFO* lpVersionInformation);
+BOOL GetVersionExW(OSVERSIONINFO* lpVersionInformation);
+
+HMODULE GetModuleHandleA(LPCSTR lpModuleName);
+HMODULE GetModuleHandleW(LPCWSTR lpModuleName);
+
+HMODULE LoadLibraryA(LPCSTR lpLibFileName);
+HMODULE LoadLibraryW(LPCWSTR lpLibFileName);
+
+FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName);
+
+//Requires Internet Explorer 4.0 (but will work on Windows 95 with IE 4)
+BOOL SHGetSpecialFolderPathA(HWND hWndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate);
+BOOL SHGetSpecialFolderPathW(HWND hWndOwner, LPWSTR lpszPath, int nFolder, BOOL fCreate);
+
+BOOL QueryPerformanceFrequency(ulong* lpFrequency);
+
+BOOL QueryPerformanceCounter(ulong* lpPerformanceCount);
+
+VOID GetSystemTimeAsFileTime(FILETIME* lpSystemTimeAsFileTime);
+
+BOOL SystemTimeToFileTime(SYSTEMTIME* lpSystemTime, FILETIME* lpFileTime);
+
+HANDLE GetCurrentThread();
+
+VOID Sleep(DWORD dwMilliseconds);
+
+BOOL GetProcessAffinityMask(
+	HANDLE hProcess,
+	ULONG_PTR* lpProcessAffinityMask,
+	ULONG_PTR* lpSystemAffinityMask);
+
+ULONG_PTR SetThreadAffinityMask(HANDLE hThread, ULONG_PTR dwThreadAffinityMask);
+//}}}
+
+//{{{ file functions
+
+BOOL GetFileSizeEx(HANDLE hFile, long* lpFileSize);
+
+BOOL ReadFile(
+	HANDLE hFile,
+	VOID* lpBuffer,
+	DWORD nNumberOfBytesToRead,
+	DWORD* lpNumberOfBytesRead,
+	OVERLAPPED* lpOverlapped);
+
+BOOL WriteFile(
+	HANDLE hFile,
+	VOID* lpBuffer,
+	DWORD nNumberOfBytesToWrite,
+	DWORD* lpNumberOfBytesWritten,
+	OVERLAPPED* lpOverlapped);
+//}}}
+
+//{{{ console functions
+BOOL AllocConsole();
+
+BOOL FreeConsole();
+
+BOOL GetConsoleMode(HANDLE hConsoleHandle, DWORD* lpMode);
+
+BOOL SetConsoleMode(HANDLE hConsoleHandle, DWORD dwMode);
+
+DWORD GetConsoleTitleA(LPSTR lpConsoleTitle, DWORD nSize);
+DWORD GetConsoleTitleW(LPWSTR lpConsoleTitle, DWORD nSize);
+
+BOOL SetConsoleTitleA(LPCSTR lpConsoleTitle);
+BOOL SetConsoleTitleW(LPCWSTR lpConsoleTitle);
+
+BOOL ReadConsoleA(
+	HANDLE hConsoleInput,
+	LPSTR lpBuffer,
+	DWORD nNumberOfCharsToRead,
+	DWORD* lpNumberOfCharsRead,
+	VOID* lpReserved);
+BOOL ReadConsoleW(
+	HANDLE hConsoleInput,
+	LPWSTR lpBuffer,
+	DWORD nNumberOfCharsToRead,
+	DWORD* lpNumberOfCharsRead,
+	VOID* lpReserved);
+
+BOOL WriteConsoleA(
+	HANDLE hConsoleOutput,
+	LPCSTR lpBuffer,
+	DWORD nNumberOfCharsToWrite,
+	DWORD* lpNumberOfCharsWritten,
+	VOID* lpReserved);
+BOOL WriteConsoleW(
+	HANDLE hConsoleOutput,
+	LPCWSTR lpBuffer,
+	DWORD nNumberOfCharsToWrite,
+	DWORD* lpNumberOfCharsWritten,
+	VOID* lpReserved);
+
+enum {
+	FOREGROUND_BLUE		 = 0x0001,
+	FOREGROUND_GREEN	 = 0x0002,
+	FOREGROUND_RED		 = 0x0004,
+	FOREGROUND_INTENSITY = 0x0008,
+	BACKGROUND_BLUE		 = 0x0010,
+	BACKGROUND_GREEN	 = 0x0020,
+	BACKGROUND_RED		 = 0x0040,
+	BACKGROUND_INTENSITY = 0x0080
+}
+
+BOOL SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttributes);
+
+BOOL GetConsoleScreenBufferInfo(
+	HANDLE hConsoleOutput,
+	CONSOLE_SCREEN_BUFFER_INFO* lpConsoleScreenBufferInfo);
+
+BOOL SetConsoleActiveScreenBuffer(HANDLE hConsoleOutput);
+
+enum {
+	STD_INPUT_HANDLE  = -10,
+	STD_OUTPUT_HANDLE = -11,
+	STD_ERROR_HANDLE  = -12
+}
+
+HANDLE GetStdHandle(DWORD nStdHandle);
+
+UINT GetConsoleCP();
+
+UINT GetConsoleOutputCP();
+
+//}}}
+
+//{{{ COM functions
+enum {
+	COINIT_APARTMENTTHREADED = 0x2,
+}
+
+HRESULT CoInitializeEx(VOID* pvReserved, DWORD dwCoInit);
+void CoUninitialize();
+
+VOID* CoTaskMemAlloc(SIZE_T cb);
+VOID* CoTaskMemRealloc(VOID* pv, SIZE_T cb);
+void CoTaskMemFree(VOID* pv);
+
+enum {
+	BIF_RETURNONLYFSDIRS = 0x0001,
+	BIF_EDITBOX			 = 0x0010,
+	BIF_NEWDIALOGSTYLE	 = 0x0040,
+	BIF_USENEWUI		 = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX)
+}
+
+ITEMIDLIST* SHBrowseForFolderA(BROWSEINFO* lpbi);
+ITEMIDLIST* SHBrowseForFolderW(BROWSEINFO* lpbi);
+
+BOOL SHGetPathFromIDListA(ITEMIDLIST* pidl, LPSTR pszPath);
+BOOL SHGetPathFromIDListW(ITEMIDLIST* pidl, LPWSTR pszPath);
+
+HRESULT SHGetFolderPathA(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPSTR pszPath);
+HRESULT SHGetFolderPathW(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath);
+
+HRESULT SHGetDesktopFolder(IShellFolder** ppshf);
+
+struct GUID {
+align(1):
+	uint Data1;
+	ushort Data2;
+	ushort Data3;
+	ubyte[8] Data4;
+}
+alias GUID IID;
+alias GUID CLSID;
+
+// I can't get this #!@#! COM stuff working...
+interface IUnknown {
+extern(Windows):
+	HRESULT QueryInterface(IID* riid, void** ppvObject);
+	ULONG AddRef();
+	ULONG Release();
+}
+alias void* LPBC;
+interface IShellFolder : IUnknown {
+extern(Windows):
+	HRESULT ParseDisplayName(
+		HWND hwnd,
+		LPBC pbc,
+		wchar* pszDisplayName,
+		ULONG* pchEaten,
+		ITEMIDLIST** ppidl,
+		ULONG* pdwAttributes);
+
+	//other methods omitted
+}
+//}}}
+
+//{{{ Multimedia functions
+alias uint MMRESULT;
+alias HANDLE HWAVEOUT;
+struct TIMECAPS {
+	UINT wPeriodMin;
+	UINT wPeriodMax;
+}
+DWORD timeGetTime();
+MMRESULT timeGetDevCaps(TIMECAPS* ptc, UINT cbtc);
+MMRESULT timeBeginPeriod(UINT uPeriod);
+MMRESULT timeEndPeriod(UINT uPeriod);
+
+struct WAVEHDR {
+	LPSTR     lpData;
+	DWORD     dwBufferLength;
+	DWORD     dwBytesRecorded;
+	ULONG_PTR dwUser;
+	DWORD     dwFlags;
+	DWORD     dwLoops;
+	WAVEHDR*  lpNext;
+	ULONG_PTR reserved;
+}
+enum {
+	WHDR_DONE      = 0x00000001,
+	WHDR_PREPARED  = 0x00000002,
+	WHDR_BEGINLOOP = 0x00000004,
+	WHDR_ENDLOOP   = 0x00000008,
+	WHDR_INQUEUE   = 0x00000010
+}
+alias uint MMVERSION;
+struct WAVEOUTCAPSW {
+	WORD      wMid;
+	WORD      wPid;
+	MMVERSION vDriverVersion;
+	WCHAR[32] szPname;
+	DWORD     dwFormats;
+	WORD      wChannels;
+	WORD      wReserved1;
+	DWORD     dwSupport;
+}
+struct MMTIME {
+	UINT wType;
+	union _u {
+		DWORD ms;
+		DWORD sample;
+		DWORD cb;
+		DWORD ticks;
+
+		struct _smpte {
+			BYTE hour;
+			BYTE min;
+			BYTE sec;
+			BYTE frame;
+			BYTE fps;
+			BYTE dummy;
+			BYTE pad[2];
+		}
+		_smpte smpte;
+
+		struct _midi {
+			DWORD songptrpos;
+		}
+		_midi midi;
+	}
+	_u u;
+}
+enum {
+	TIME_MS      = 0x0001,
+	TIME_SAMPLES = 0x0002,
+	TIME_BYTES   = 0x0004,
+	TIME_SMPTE   = 0x0008,
+	TIME_MIDI    = 0x0010,
+	TIME_TICKS   = 0x0020
+}
+struct WAVEFORMATEX {
+	WORD  wFormatTag;
+	WORD  nChannels;
+	DWORD nSamplesPerSec;
+	DWORD nAvgBytesPerSec;
+	WORD  nBlockAlign;
+	WORD  wBitsPerSample;
+	WORD  cbSize;
+
+}
+
+MMRESULT waveOutGetVolume(HWAVEOUT hwo, DWORD* pdwVolume);
+MMRESULT waveOutSetVolume(HWAVEOUT hwo, DWORD dwVolume);
+MMRESULT waveOutOpen(
+	HWAVEOUT* phwo,
+	UINT uDeviceID,
+	WAVEFORMATEX* pwfx,
+	ULONG_PTR dwCallback,
+	ULONG_PTR dwInstance,
+	DWORD fdwOpen);
+MMRESULT waveOutClose(HWAVEOUT hwo);
+MMRESULT waveOutPrepareHeader(HWAVEOUT hwo, WAVEHDR* pwh, UINT cbwh);
+MMRESULT waveOutUnprepareHeader(HWAVEOUT hwo, WAVEHDR* pwh, UINT cbwh);
+MMRESULT waveOutWrite(HWAVEOUT hwo, WAVEHDR* pwh, UINT cbwh);
+MMRESULT waveOutPause(HWAVEOUT hwo);
+MMRESULT waveOutRestart(HWAVEOUT hwo);
+MMRESULT waveOutReset(HWAVEOUT hwo);
+MMRESULT waveOutBreakLoop(HWAVEOUT hwo);
+MMRESULT waveOutGetPosition(HWAVEOUT hwo, MMTIME* pmmt, UINT cbmmt);
+MMRESULT waveOutGetPitch(HWAVEOUT hwo, DWORD* pdwPitch);
+MMRESULT waveOutSetPitch(HWAVEOUT hwo, DWORD dwPitch);
+MMRESULT waveOutGetPlaybackRate(HWAVEOUT hwo, DWORD* pdwRate);
+MMRESULT waveOutSetPlaybackRate(HWAVEOUT hwo, DWORD dwRate);
+
+//}}}
+
+version(UNICODE) {
+	alias RegisterClassExW		   RegisterClassEx;
+	alias CreateWindowExW		   CreateWindowEx;
+	alias DefWindowProcW		   DefWindowProc;
+	alias GetWindowLongW		   GetWindowLong;
+	alias SetWindowLongW		   SetWindowLong;
+	alias GetWindowLongPtrW		   GetWindowLongPtr;
+	alias SetWindowLongPtrW		   SetWindowLongPtr;
+	alias GetWindowTextW		   GetWindowText;
+	alias SetWindowTextW		   SetWindowText;
+	alias GetWindowModuleFileNameW GetWindowModuleFileName;
+	alias SetPropW				   SetProp;
+	alias GetPropW				   GetProp;
+	alias RemovePropW			   RemoveProp;
+	alias GetOpenFileNameW		   GetOpenFileName;
+	alias GetSaveFileNameW		   GetSaveFileName;
+	alias GetMessageW			   GetMessage;
+	alias DispatchMessageW		   DispatchMessage;
+	alias LoadImageW			   LoadImage;
+	alias GetObjectW               GetObject;
+	alias EnumFontFamiliesExW	   EnumFontFamiliesEx;
+	alias SystemParametersInfoW	   SystemParametersInfo;
+	alias GetVersionExW			   GetVersionEx;
+	alias GetModuleHandleW		   GetModuleHandle;
+	alias LoadLibraryW			   LoadLibrary;
+	alias SHGetSpecialFolderPathW  SHGetSpecialFolderPath;
+	alias GetConsoleTitleW		   GetConsoleTitle;
+	alias SetConsoleTitleW		   SetConsoleTitle;
+	alias ReadConsoleW			   ReadConsole;
+	alias WriteConsoleW			   WriteConsole;
+	alias SHBrowseForFolderW	   SHBrowseForFolder;
+	alias SHGetPathFromIDListW	   SHGetPathFromIDList;
+	alias SHGetFolderPathW		   SHGetFolderPath;
+
+	alias LOGFONTW       LOGFONT;
+	alias NONCLIENTMETRICSW NONCLIENTMETRICS;
+	alias ENUMLOGFONTEXW ENUMLOGFONTEX;
+	alias TEXTMETRICW    TEXTMETRIC;
+	alias OSVERSIONINFOW OSVERSIONINFO;
+} else {
+	alias RegisterClassExA		   RegisterClassEx;
+	alias CreateWindowExA		   CreateWindowEx;
+	alias DefWindowProcA		   DefWindowProc;
+	alias GetWindowLongA		   GetWindowLong;
+	alias SetWindowLongA		   SetWindowLong;
+	alias GetWindowLongPtrA		   GetWindowLongPtr;
+	alias SetWindowLongPtrA		   SetWindowLongPtr;
+	alias GetWindowTextA		   GetWindowText;
+	alias SetWindowTextA		   SetWindowText;
+	alias GetWindowModuleFileNameA GetWindowModuleFileName;
+	alias SetPropA				   SetProp;
+	alias GetPropA				   GetProp;
+	alias RemovePropA			   RemoveProp;
+	alias GetOpenFileNameA		   GetOpenFileName;
+	alias GetSaveFileNameA		   GetSaveFileName;
+	alias GetMessageA			   GetMessage;
+	alias DispatchMessageA		   DispatchMessage;
+	alias LoadImageA			   LoadImage;
+	alias EnumFontFamiliesExA	   EnumFontFamiliesEx;
+	alias SystemParametersInfoA	   SystemParametersInfo;
+	alias GetVersionExA			   GetVersionEx;
+	alias GetModuleHandleA		   GetModuleHandle;
+	alias LoadLibraryA			   LoadLibrary;
+	alias SHGetSpecialFolderPathA  SHGetSpecialFolderPath;
+	alias GetConsoleTitleA		   GetConsoleTitle;
+	alias SetConsoleTitleA		   SetConsoleTitle;
+	alias ReadConsoleA			   ReadConsole;
+	alias WriteConsoleA			   WriteConsole;
+	alias SHBrowseForFolderA	   SHBrowseForFolder;
+	alias SHGetPathFromIDListA	   SHGetPathFromIDList;
+	alias SHGetFolderPathA		   SHGetFolderPath;
+}
+
+struct POINT {
+	LONG x;
+	LONG y;
+}
+struct SIZE {
+	LONG cx;
+	LONG cy;
+}
+struct RECT {
+	LONG left;
+	LONG top;
+	LONG right;
+	LONG bottom;
+}
+struct BLENDFUNCTION {
+	BYTE BlendOp;
+	BYTE BlendFlags;
+	BYTE SourceConstantAlpha;
+	BYTE AlphaFormat;
+}
+const int LF_FACESIZE = 32;
+struct LOGFONTW {
+	LONG			   lfHeight;
+	LONG			   lfWidth;
+	LONG			   lfEscapement;
+	LONG			   lfOrientation;
+	LONG			   lfWeight;
+	BYTE			   lfItalic;
+	BYTE			   lfUnderline;
+	BYTE			   lfStrikeOut;
+	BYTE			   lfCharSet;
+	BYTE			   lfOutPrecision;
+	BYTE			   lfClipPrecision;
+	BYTE			   lfQuality;
+	BYTE			   lfPitchAndFamily;
+	WCHAR[LF_FACESIZE] lfFaceName;
+}
+struct NONCLIENTMETRICSW {
+	UINT     cbSize;
+	int      iBorderWidth;
+	int      iScrollWidth;
+	int      iScrollHeight;
+	int      iCaptionWidth;
+	int      iCaptionHeight;
+	LOGFONTW lfCaptionFont;
+	int      iSmCaptionWidth;
+	int      iSmCaptionHeight;
+	LOGFONTW lfSmCaptionFont;
+	int      iMenuWidth;
+	int      iMenuHeight;
+	LOGFONTW lfMenuFont;
+	LOGFONTW lfStatusFont;
+	LOGFONTW lfMessageFont;
+}
+const int LF_FULLFACESIZE = 64;
+struct ENUMLOGFONTEXW {
+	LOGFONT				   elfLogFont;
+	WCHAR[LF_FULLFACESIZE] elfFullName;
+	WCHAR[LF_FACESIZE]	   elfStyle;
+	WCHAR[LF_FACESIZE]	   elfScript;
+}
+struct TEXTMETRICW {
+	LONG  tmHeight;
+	LONG  tmAscent;
+	LONG  tmDescent;
+	LONG  tmInternalLeading;
+	LONG  tmExternalLeading;
+	LONG  tmAveCharWidth;
+	LONG  tmMaxCharWidth;
+	LONG  tmWeight;
+	LONG  tmOverhang;
+	LONG  tmDigitizedAspectX;
+	LONG  tmDigitizedAspectY;
+	WCHAR tmFirstChar;
+	WCHAR tmLastChar;
+	WCHAR tmDefaultChar;
+	WCHAR tmBreakChar;
+	BYTE  tmItalic;
+	BYTE  tmUnderlined;
+	BYTE  tmStruckOut;
+	BYTE  tmPitchAndFamily;
+	BYTE  tmCharSet;
+}
+struct RGBQUAD {
+	BYTE rgbBlue;
+	BYTE rgbGreen;
+	BYTE rgbRed;
+	BYTE rgbReserved;
+}
+struct BITMAPINFO {
+	BITMAPINFOHEADER bmiHeader;
+	RGBQUAD[1]		 bmiColors;
+}
+struct BITMAPINFOHEADER {
+	DWORD biSize;
+	LONG  biWidth;
+	LONG  biHeight;
+	WORD  biPlanes;
+	WORD  biBitCount;
+	DWORD biCompression;
+	DWORD biSizeImage;
+	LONG  biXPelsPerMeter;
+	LONG  biYPelsPerMeter;
+	DWORD biClrUsed;
+	DWORD biClrImportant;
+}
+struct DIBSECTION {
+	BITMAP           dsBm;
+	BITMAPINFOHEADER dsBmih;
+	DWORD[3]         dsBitfields;
+	HANDLE           dshSection;
+	DWORD            dsOffset;
+}
+struct BITMAP {
+	LONG  bmType;
+	LONG  bmWidth;
+	LONG  bmHeight;
+	LONG  bmWidthBytes;
+	WORD  bmPlanes;
+	WORD  bmBitsPixel;
+	VOID* bmBits;
+}
+struct MSG {
+	HWND hWnd;
+	UINT message;
+	WPARAM wParam;
+	LPARAM lParam;
+	DWORD time;
+	POINT pt;
+}
+struct WNDCLASSEX {
+	UINT	  cbSize;
+	UINT	  style;
+	WNDPROC	  lpfnWndProc;
+	int		  cbClsExtra;
+	int		  cbWndExtra;
+	HINSTANCE hInstance;
+	HICON	  hIcon;
+	HCURSOR	  hCursor;
+	HBRUSH	  hbrBackground;
+	LPCTSTR	  lpszMenuName;
+	LPCTSTR	  lpszClassName;
+	HICON	  hIconSm;
+}
+struct WINDOWINFO {
+	DWORD cbSize;
+	RECT  rcWindow;
+	RECT  rcClient;
+	DWORD dwStyle;
+	DWORD dwExStyle;
+	DWORD dwWindowStatus;
+	UINT  cxWindowBorders;
+	UINT  cyWindowBorders;
+	ATOM  atomWindowType;
+	WORD  wCreatorVersion;
+}
+struct PAINTSTRUCT {
+	HDC		 hdc;
+	BOOL	 fErase;
+	RECT	 rcPaint;
+	BOOL	 fRestore;
+	BOOL	 fIncUpdate;
+	BYTE[32] rgbReserved;
+}
+struct TRACKMOUSEEVENT {
+	DWORD cbSize;
+	DWORD dwFlags;
+	HWND  hWndTrack;
+	DWORD dwHoverTime;
+}
+struct OPENFILENAME {
+	DWORD		  lStructSize;
+	HWND		  hWndOwner;
+	HINSTANCE	  hInstance;
+	LPCTSTR		  lpstrFilter;
+	LPTSTR		  lpstrCustomFilter;
+	DWORD		  nMaxCustFilter;
+	DWORD		  nFilterIndex;
+	LPTSTR		  lpstrFile;
+	DWORD		  nMaxFile;
+	LPTSTR		  lpstrFileTitle;
+	DWORD		  nMaxFileTitle;
+	LPCTSTR		  lpstrInitialDir;
+	LPCTSTR		  lpstrTitle;
+	DWORD		  Flags;
+	WORD		  nFileOffset;
+	WORD		  nFileExtension;
+	LPCTSTR		  lpstrDefExt;
+	LPARAM		  lCustData;
+	LPOFNHOOKPROC lpfnHook;
+	LPCTSTR		  lpTemplateName;
+	//	if(_WIN32_WINNT >= 0x0500)
+	//void*		  pvReserved;
+	//DWORD		  dwReserved;
+	//DWORD		  FlagsEx;
+}
+struct SHITEMID {
+	USHORT	cb;
+	BYTE[1] abID;
+}
+struct ITEMIDLIST {
+	SHITEMID mkid;
+}
+struct BROWSEINFO {
+	HWND		hwndOwner;
+	ITEMIDLIST* pidlRoot;
+	LPTSTR		pszDisplayName;
+	LPCTSTR		lpszTitle;
+	UINT		ulFlags;
+	BFFCALLBACK lpfn;
+	LPARAM		lParam;
+	int			iImage;
+}
+struct OVERLAPPED {
+	ULONG_PTR Internal;
+	ULONG_PTR InternalHigh;
+	union {
+		struct {
+			DWORD Offset;
+			DWORD OffsetHigh;
+		}
+
+		VOID* Pointer;
+	}
+
+	HANDLE hEvent;
+}
+struct OSVERSIONINFOW {
+	DWORD dwOSVersionInfoSize;
+	DWORD dwMajorVersion;
+	DWORD dwMinorVersion;
+	DWORD dwBuildNumber;
+	DWORD dwPlatformId;
+	WCHAR[128] szCSDVersion;
+}
+struct SYSTEM_INFO {
+	WORD wProcessorArchitecture;
+	WORD wReserved;
+	DWORD dwPageSize;
+	VOID* lpMinimumApplicationAddress;
+	VOID* lpMaximumApplicationAddress;
+	ULONG_PTR dwActiveProcessorMask;
+	DWORD dwNumberOfProcessors;
+	DWORD dwProcessorType;
+	DWORD dwAllocationGranularity;
+	WORD wProcessorLevel;
+	WORD wProcessorRevision;
+}
+struct CONSOLE_SCREEN_BUFFER_INFO {
+	COORD dwSize;
+	COORD dwCursorPosition;
+	WORD wAttributes;
+	SMALL_RECT srWindow;
+	COORD dwMaximumWindowSize;
+}
+struct FILETIME {
+	DWORD dwLowDateTime;
+	DWORD dwHighDateTime;
+}
+struct SYSTEMTIME {
+	WORD wYear;
+	WORD wMonth;
+	WORD wDayOfWeek;
+	WORD wDay;
+	WORD wHour;
+	WORD wMinute;
+	WORD wSecond;
+	WORD wMilliseconds;
+}
+struct COORD {
+	SHORT X;
+	SHORT Y;
+}
+struct SMALL_RECT {
+	SHORT Left;
+	SHORT Top;
+	SHORT Right;
+	SHORT Bottom;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/c/windows_tmschema.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,759 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * Translated from MinGW Windows headers as part of the WindowsAPI project:
+ * http://www.dsource.org/projects/bindings/wiki/WindowsApi
+ *
+ * Placed into public domain
+ */
+
+module dynamin.c.windows_tmschema;
+
+/* BUTTON parts */
+enum {
+	BP_PUSHBUTTON = 1,
+	BP_RADIOBUTTON = 2,
+	BP_CHECKBOX = 3,
+	BP_GROUPBOX = 4,
+	BP_USERBUTTON = 5
+}
+
+enum {
+	CBS_UNCHECKEDNORMAL = 1,
+	CBS_UNCHECKEDHOT = 2,
+	CBS_UNCHECKEDPRESSED = 3,
+	CBS_UNCHECKEDDISABLED = 4,
+	CBS_CHECKEDNORMAL = 5,
+	CBS_CHECKEDHOT = 6,
+	CBS_CHECKEDPRESSED = 7,
+	CBS_CHECKEDDISABLED = 8,
+	CBS_MIXEDNORMAL = 9,
+	CBS_MIXEDHOT = 10,
+	CBS_MIXEDPRESSED = 11,
+	CBS_MIXEDDISABLED = 12
+}
+
+enum {
+	GBS_NORMAL = 1,
+	GBS_DISABLED = 2
+}
+
+enum {
+	PBS_NORMAL = 1,
+	PBS_HOT = 2,
+	PBS_PRESSED = 3,
+	PBS_DISABLED = 4,
+	PBS_DEFAULTED = 5
+}
+
+enum {
+	RBS_UNCHECKEDNORMAL = 1,
+	RBS_UNCHECKEDHOT = 2,
+	RBS_UNCHECKEDPRESSED = 3,
+	RBS_UNCHECKEDDISABLED = 4,
+	RBS_CHECKEDNORMAL = 5,
+	RBS_CHECKEDHOT = 6,
+	RBS_CHECKEDPRESSED = 7,
+	RBS_CHECKEDDISABLED = 8
+}
+
+/* CLOCK parts */
+enum {
+	CLP_TIME = 1
+}
+
+enum {
+	CLS_NORMAL = 1
+}
+
+/* COMBOBOX parts */
+enum {
+	CP_DROPDOWNBUTTON = 1
+}
+
+enum {
+	CBXS_NORMAL = 1,
+	CBXS_HOT = 2,
+	CBXS_PRESSED = 3,
+	CBXS_DISABLED = 4
+}
+
+/* EDIT parts */
+enum {
+	EP_EDITTEXT = 1,
+	EP_CARET = 2
+}
+
+enum {
+	ETS_NORMAL = 1,
+	ETS_HOT = 2,
+	ETS_SELECTED = 3,
+	ETS_DISABLED = 4,
+	ETS_FOCUSED = 5,
+	ETS_READONLY = 6,
+	ETS_ASSIST = 7
+}
+/* EXPLORERBAR parts */
+enum {
+	EBP_HEADERBACKGROUND = 1,
+	EBP_HEADERCLOSE = 2,
+	EBP_HEADERPIN = 3,
+	EBP_IEBARMENU = 4,
+	EBP_NORMALGROUPBACKGROUND = 5,
+	EBP_NORMALGROUPCOLLAPSE = 6,
+	EBP_NORMALGROUPEXPAND = 7,
+	EBP_NORMALGROUPHEAD = 8,
+	EBP_SPECIALGROUPBACKGROUND = 9,
+	EBP_SPECIALGROUPCOLLAPSE = 10,
+	EBP_SPECIALGROUPEXPAND = 11,
+	EBP_SPECIALGROUPHEAD = 12
+}
+
+enum {
+	EBHC_NORMAL = 1,
+	EBHC_HOT = 2,
+	EBHC_PRESSED = 3
+}
+
+enum {
+	EBHP_NORMAL = 1,
+	EBHP_HOT = 2,
+	EBHP_PRESSED = 3,
+	EBHP_SELECTEDNORMAL = 4,
+	EBHP_SELECTEDHOT = 5,
+	EBHP_SELECTEDPRESSED = 6
+}
+
+enum {
+	EBM_NORMAL = 1,
+	EBM_HOT = 2,
+	EBM_PRESSED = 3
+}
+
+enum {
+	EBNGC_NORMAL = 1,
+	EBNGC_HOT = 2,
+	EBNGC_PRESSED = 3
+}
+
+enum {
+	EBNGE_NORMAL = 1,
+	EBNGE_HOT = 2,
+	EBNGE_PRESSED = 3
+}
+
+enum {
+	EBSGC_NORMAL = 1,
+	EBSGC_HOT = 2,
+	EBSGC_PRESSED = 3
+}
+
+enum {
+	EBSGE_NORMAL = 1,
+	EBSGE_HOT = 2,
+	EBSGE_PRESSED = 3
+}
+
+/* HEADER parts */
+enum {
+	HP_HEADERITEM = 1,
+	HP_HEADERITEMLEFT = 2,
+	HP_HEADERITEMRIGHT = 3,
+	HP_HEADERSORTARROW = 4
+}
+
+enum {
+	HIS_NORMAL = 1,
+	HIS_HOT = 2,
+	HIS_PRESSED = 3
+}
+
+enum {
+	HILS_NORMAL = 1,
+	HILS_HOT = 2,
+	HILS_PRESSED = 3
+}
+
+enum {
+	HIRS_NORMAL = 1,
+	HIRS_HOT = 2,
+	HIRS_PRESSED = 3
+}
+
+enum {
+	HSAS_SORTEDUP = 1,
+	HSAS_SORTEDDOWN = 2
+}
+
+/* LISTVIEW parts */
+enum {
+	LVP_LISTITEM = 1,
+	LVP_LISTGROUP = 2,
+	LVP_LISTDETAIL = 3,
+	LVP_LISTSORTEDDETAIL = 4,
+	LVP_EMPTYTEXT = 5
+}
+
+enum {
+	LIS_NORMAL = 1,
+	LIS_HOT = 2,
+	LIS_SELECTED = 3,
+	LIS_DISABLED = 4,
+	LIS_SELECTEDNOTFOCUS = 5
+}
+
+/* MENU parts */
+enum {
+	MP_MENUITEM = 1,
+	MP_MENUDROPDOWN = 2,
+	MP_MENUBARITEM = 3,
+	MP_MENUBARDROPDOWN = 4,
+	MP_CHEVRON = 5,
+	MP_SEPARATOR = 6
+}
+
+enum {
+	MS_NORMAL = 1,
+	MS_SELECTED = 2,
+	MS_DEMOTED = 3
+}
+/* MENUBAND parts */
+enum {
+	MDP_NEWAPPBUTTON = 1,
+	MDP_SEPERATOR = 2
+}
+
+enum {
+	MDS_NORMAL = 1,
+	MDS_HOT = 2,
+	MDS_PRESSED = 3,
+	MDS_DISABLED = 4,
+	MDS_CHECKED = 5,
+	MDS_HOTCHECKED = 6
+}
+
+/* PAGE parts */
+enum {
+	PGRP_UP = 1,
+	PGRP_DOWN = 2,
+	PGRP_UPHORZ = 3,
+	PGRP_DOWNHORZ = 4
+}
+
+enum {
+	DNS_NORMAL = 1,
+	DNS_HOT = 2,
+	DNS_PRESSED = 3,
+	DNS_DISABLED = 4
+}
+
+enum {
+	DNHZS_NORMAL = 1,
+	DNHZS_HOT = 2,
+	DNHZS_PRESSED = 3,
+	DNHZS_DISABLED = 4
+}
+
+enum {
+	UPS_NORMAL = 1,
+	UPS_HOT = 2,
+	UPS_PRESSED = 3,
+	UPS_DISABLED = 4
+}
+
+enum {
+	UPHZS_NORMAL = 1,
+	UPHZS_HOT = 2,
+	UPHZS_PRESSED = 3,
+	UPHZS_DISABLED = 4
+}
+
+/* PROGRESS parts */
+enum {
+	PP_BAR = 1,
+	PP_BARVERT = 2,
+	PP_CHUNK = 3,
+	PP_CHUNKVERT = 4
+}
+
+/* REBAR parts */
+enum {
+	RP_GRIPPER = 1,
+	RP_GRIPPERVERT = 2,
+	RP_BAND = 3,
+	RP_CHEVRON = 4,
+	RP_CHEVRONVERT = 5
+}
+
+enum {
+	CHEVS_NORMAL = 1,
+	CHEVS_HOT = 2,
+	CHEVS_PRESSED = 3
+}
+
+/* SCROLLBAR parts */
+enum {
+	SBP_ARROWBTN = 1,
+	SBP_THUMBBTNHORZ = 2,
+	SBP_THUMBBTNVERT = 3,
+	SBP_LOWERTRACKHORZ = 4,
+	SBP_UPPERTRACKHORZ = 5,
+	SBP_LOWERTRACKVERT = 6,
+	SBP_UPPERTRACKVERT = 7,
+	SBP_GRIPPERHORZ = 8,
+	SBP_GRIPPERVERT = 9,
+	SBP_SIZEBOX = 10
+}
+
+enum {
+	ABS_UPNORMAL = 1,
+	ABS_UPHOT = 2,
+	ABS_UPPRESSED = 3,
+	ABS_UPDISABLED = 4,
+	ABS_DOWNNORMAL = 5,
+	ABS_DOWNHOT = 6,
+	ABS_DOWNPRESSED = 7,
+	ABS_DOWNDISABLED = 8,
+	ABS_LEFTNORMAL = 9,
+	ABS_LEFTHOT = 10,
+	ABS_LEFTPRESSED = 11,
+	ABS_LEFTDISABLED = 12,
+	ABS_RIGHTNORMAL = 13,
+	ABS_RIGHTHOT = 14,
+	ABS_RIGHTPRESSED = 15,
+	ABS_RIGHTDISABLED = 16
+}
+
+enum {
+	SCRBS_NORMAL = 1,
+	SCRBS_HOT = 2,
+	SCRBS_PRESSED = 3,
+	SCRBS_DISABLED = 4
+}
+
+enum {
+	SZB_RIGHTALIGN = 1,
+	SZB_LEFTALIGN = 2
+}
+
+/* SPIN parts */
+enum {
+	SPNP_UP = 1,
+	SPNP_DOWN = 2,
+	SPNP_UPHORZ = 3,
+	SPNP_DOWNHORZ = 4
+}
+
+/* STARTPANEL parts */
+enum {
+	SPP_USERPANE = 1,
+	SPP_MOREPROGRAMS = 2,
+	SPP_MOREPROGRAMSARROW = 3,
+	SPP_PROGLIST = 4,
+	SPP_PROGLISTSEPARATOR = 5,
+	SPP_PLACESLIST = 6,
+	SPP_PLACESLISTSEPARATOR = 7,
+	SPP_LOGOFF = 8,
+	SPP_LOGOFFBUTTONS = 9,
+	SPP_USERPICTURE = 10,
+	SPP_PREVIEW = 11
+}
+
+enum {
+	SPLS_NORMAL = 1,
+	SPLS_HOT = 2,
+	SPLS_PRESSED = 3
+}
+
+enum {
+	SPS_NORMAL = 1,
+	SPS_HOT = 2,
+	SPS_PRESSED = 3
+}
+
+/* STATUS parts */
+enum {
+	SP_PANE = 1,
+	SP_GRIPPERPANE = 2,
+	SP_GRIPPER = 3
+}
+
+/* TAB parts */
+enum {
+	TABP_TABITEM = 1,
+	TABP_TABITEMLEFTEDGE = 2,
+	TABP_TABITEMRIGHTEDGE = 3,
+	TABP_TABITEMBOTHEDGE = 4,
+	TABP_TOPTABITEM = 5,
+	TABP_TOPTABITEMLEFTEDGE = 6,
+	TABP_TOPTABITEMRIGHTEDGE = 7,
+	TABP_TOPTABITEMBOTHEDGE = 8,
+	TABP_PANE = 9,
+	TABP_BODY = 10
+}
+
+enum {
+	TIS_NORMAL = 1,
+	TIS_HOT = 2,
+	TIS_SELECTED = 3,
+	TIS_DISABLED = 4,
+	TIS_FOCUSED = 5
+}
+
+enum {
+	TIBES_NORMAL = 1,
+	TIBES_HOT = 2,
+	TIBES_SELECTED = 3,
+	TIBES_DISABLED = 4,
+	TIBES_FOCUSED = 5
+}
+
+enum {
+	TILES_NORMAL = 1,
+	TILES_HOT = 2,
+	TILES_SELECTED = 3,
+	TILES_DISABLED = 4,
+	TILES_FOCUSED = 5
+}
+
+enum {
+	TIRES_NORMAL = 1,
+	TIRES_HOT = 2,
+	TIRES_SELECTED = 3,
+	TIRES_DISABLED = 4,
+	TIRES_FOCUSED = 5
+}
+
+enum {
+	TTIS_NORMAL = 1,
+	TTIS_HOT = 2,
+	TTIS_SELECTED = 3,
+	TTIS_DISABLED = 4,
+	TTIS_FOCUSED = 5
+}
+
+enum {
+	TTIBES_NORMAL = 1,
+	TTIBES_HOT = 2,
+	TTIBES_SELECTED = 3,
+	TTIBES_DISABLED = 4,
+	TTIBES_FOCUSED = 5
+}
+
+enum {
+	TTILES_NORMAL = 1,
+	TTILES_HOT = 2,
+	TTILES_SELECTED = 3,
+	TTILES_DISABLED = 4,
+	TTILES_FOCUSED = 5
+}
+
+enum {
+	TTIRES_NORMAL = 1,
+	TTIRES_HOT = 2,
+	TTIRES_SELECTED = 3,
+	TTIRES_DISABLED = 4,
+	TTIRES_FOCUSED = 5
+}
+
+/* TASKBAND parts */
+enum {
+	TDP_GROUPCOUNT = 1,
+	TDP_FLASHBUTTON = 2,
+	TDP_FLASHBUTTONGROUPMENU = 3
+}
+
+/* TASKBAR parts */
+enum {
+	TBP_BACKGROUNDBOTTOM = 1,
+	TBP_BACKGROUNDRIGHT = 2,
+	TBP_BACKGROUNDTOP = 3,
+	TBP_BACKGROUNDLEFT = 4,
+	TBP_SIZINGBARBOTTOM = 5,
+	TBP_SIZINGBARRIGHT = 6,
+	TBP_SIZINGBARTOP = 7,
+	TBP_SIZINGBARLEFT = 8
+}
+
+/* TOOLBAR parts */
+enum {
+	TP_BUTTON = 1,
+	TP_DROPDOWNBUTTON = 2,
+	TP_SPLITBUTTON = 3,
+	TP_SPLITBUTTONDROPDOWN = 4,
+	TP_SEPARATOR = 5,
+	TP_SEPARATORVERT = 6
+}
+
+enum {
+	TS_NORMAL = 1,
+	TS_HOT = 2,
+	TS_PRESSED = 3,
+	TS_DISABLED = 4,
+	TS_CHECKED = 5,
+	TS_HOTCHECKED = 6
+}
+
+/* TOOLTIP parts */
+enum {
+	TTP_STANDARD = 1,
+	TTP_STANDARDTITLE = 2,
+	TTP_BALLOON = 3,
+	TTP_BALLOONTITLE = 4,
+	TTP_CLOSE = 5
+}
+
+enum {
+	TTBS_NORMAL = 1,
+	TTBS_LINK = 2
+}
+
+enum {
+	TTCS_NORMAL = 1,
+	TTCS_HOT = 2,
+	TTCS_PRESSED = 3
+}
+
+enum {
+	TTSS_NORMAL = 1,
+	TTSS_LINK = 2
+}
+
+/* TRACKBAR parts */
+enum {
+	TKP_TRACK = 1,
+	TKP_TRACKVERT = 2,
+	TKP_THUMB = 3,
+	TKP_THUMBBOTTOM = 4,
+	TKP_THUMBTOP = 5,
+	TKP_THUMBVERT = 6,
+	TKP_THUMBLEFT = 7,
+	TKP_THUMBRIGHT = 8,
+	TKP_TICS = 9,
+	TKP_TICSVERT = 10
+}
+
+enum {
+	TUS_NORMAL = 1,
+	TUS_HOT = 2,
+	TUS_PRESSED = 3,
+	TUS_FOCUSED = 4,
+	TUS_DISABLED = 5
+}
+
+enum {
+	TUBS_NORMAL = 1,
+	TUBS_HOT = 2,
+	TUBS_PRESSED = 3,
+	TUBS_FOCUSED = 4,
+	TUBS_DISABLED = 5
+}
+
+enum {
+	TUVLS_NORMAL = 1,
+	TUVLS_HOT = 2,
+	TUVLS_PRESSED = 3,
+	TUVLS_FOCUSED = 4,
+	TUVLS_DISABLED = 5
+}
+
+enum {
+	TUVRS_NORMAL = 1,
+	TUVRS_HOT = 2,
+	TUVRS_PRESSED = 3,
+	TUVRS_FOCUSED = 4,
+	TUVRS_DISABLED = 5
+}
+
+enum {
+	TUTS_NORMAL = 1,
+	TUTS_HOT = 2,
+	TUTS_PRESSED = 3,
+	TUTS_FOCUSED = 4,
+	TUTS_DISABLED = 5
+}
+
+enum {
+	TUVS_NORMAL = 1,
+	TUVS_HOT = 2,
+	TUVS_PRESSED = 3,
+	TUVS_FOCUSED = 4,
+	TUVS_DISABLED = 5
+}
+
+enum {
+	TSS_NORMAL = 1
+}
+
+enum {
+	TSVS_NORMAL = 1
+}
+
+enum {
+	TRS_NORMAL = 1
+}
+
+enum {
+	TRVS_NORMAL = 1
+}
+
+/* TRAYNOTIFY parts */
+enum {
+	TNP_BACKGROUND = 1,
+	TNP_ANIMBACKGROUND = 2
+}
+
+/* TREEVIEW parts */
+enum {
+	TVP_TREEITEM = 1,
+	TVP_GLYPH = 2,
+	TVP_BRANCH = 3
+}
+
+enum {
+	GLPS_CLOSED = 1,
+	GLPS_OPENED = 2
+}
+
+enum {
+	TREIS_NORMAL = 1,
+	TREIS_HOT = 2,
+	TREIS_SELECTED = 3,
+	TREIS_DISABLED = 4,
+	TREIS_SELECTEDNOTFOCUS = 5
+}
+
+/* WINDOW parts */
+enum {
+	WP_CAPTION = 1,
+	WP_SMALLCAPTION = 2,
+	WP_MINCAPTION = 3,
+	WP_SMALLMINCAPTION = 4,
+	WP_MAXCAPTION = 5,
+	WP_SMALLMAXCAPTION = 6,
+	WP_FRAMELEFT = 7,
+	WP_FRAMERIGHT = 8,
+	WP_FRAMEBOTTOM = 9,
+	WP_SMALLFRAMELEFT = 10,
+	WP_SMALLFRAMERIGHT = 11,
+	WP_SMALLFRAMEBOTTOM = 12,
+	WP_SYSBUTTON = 13,
+	WP_MDISYSBUTTON = 14,
+	WP_MINBUTTON = 15,
+	WP_MDIMINBUTTON = 16,
+	WP_MAXBUTTON = 17,
+	WP_CLOSEBUTTON = 18,
+	WP_SMALLCLOSEBUTTON = 19,
+	WP_MDICLOSEBUTTON = 20,
+	WP_RESTOREBUTTON = 21,
+	WP_MDIRESTOREBUTTON = 22,
+	WP_HELPBUTTON = 23,
+	WP_MDIHELPBUTTON = 24,
+	WP_HORZSCROLL = 25,
+	WP_HORZTHUMB = 26,
+	WP_VERTSCROLL = 27,
+	WP_VERTTHUMB = 28,
+	WP_DIALOG = 29,
+	WP_CAPTIONSIZINGTEMPLATE = 30,
+	WP_SMALLCAPTIONSIZINGTEMPLATE = 31,
+	WP_FRAMELEFTSIZINGTEMPLATE = 32,
+	WP_SMALLFRAMELEFTSIZINGTEMPLATE = 33,
+	WP_FRAMERIGHTSIZINGTEMPLATE = 34,
+	WP_SMALLFRAMERIGHTSIZINGTEMPLATE = 35,
+	WP_FRAMEBOTTOMSIZINGTEMPLATE = 36,
+	WP_SMALLFRAMEBOTTOMSIZINGTEMPLATE = 37
+}
+
+enum {
+	CS_ACTIVE = 1,
+	CS_INACTIVE = 2,
+	CS_DISABLED = 3
+}
+
+enum {
+	CBS_NORMAL = 1,
+	CBS_HOT = 2,
+	CBS_PUSHED = 3,
+	CBS_DISABLED = 4
+}
+
+enum {
+	FS_ACTIVE = 1,
+	FS_INACTIVE = 2
+}
+
+enum {
+	HBS_NORMAL = 1,
+	HBS_HOT = 2,
+	HBS_PUSHED = 3,
+	HBS_DISABLED = 4
+}
+
+enum {
+	HSS_NORMAL = 1,
+	HSS_HOT = 2,
+	HSS_PUSHED = 3,
+	HSS_DISABLED = 4
+}
+
+enum {
+	HTS_NORMAL = 1,
+	HTS_HOT = 2,
+	HTS_PUSHED = 3,
+	HTS_DISABLED = 4
+}
+
+enum {
+	MAXBS_NORMAL = 1,
+	MAXBS_HOT = 2,
+	MAXBS_PUSHED = 3,
+	MAXBS_DISABLED = 4
+}
+
+enum {
+	MXCS_ACTIVE = 1,
+	MXCS_INACTIVE = 2,
+	MXCS_DISABLED = 3
+}
+
+enum {
+	MINBS_NORMAL = 1,
+	MINBS_HOT = 2,
+	MINBS_PUSHED = 3,
+	MINBS_DISABLED = 4
+}
+
+enum {
+	RBS_NORMAL = 1,
+	RBS_HOT = 2,
+	RBS_PUSHED = 3,
+	RBS_DISABLED = 4
+}
+
+enum {
+	SBS_NORMAL = 1,
+	SBS_HOT = 2,
+	SBS_PUSHED = 3,
+	SBS_DISABLED = 4
+}
+
+enum {
+	MNCS_ACTIVE = 1,
+	MNCS_INACTIVE = 2,
+	MNCS_DISABLED = 3
+}
+
+enum {
+	VSS_NORMAL = 1,
+	VSS_HOT = 2,
+	VSS_PUSHED = 3,
+	VSS_DISABLED = 4
+}
+
+enum {
+	VTS_NORMAL = 1,
+	VTS_HOT = 2,
+	VTS_PUSHED = 3,
+	VTS_DISABLED = 4
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/c/windows_windef.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,114 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * Translated from MinGW Windows headers as part of the WindowsAPI project:
+ * http://www.dsource.org/projects/bindings/wiki/WindowsApi
+ *
+ * by Stewart Gordon
+ * Placed into public domain
+ */
+
+module dynamin.c.windows_windef;
+
+public import dynamin.c.windows_winnt;
+private import win32.w32api;
+
+const size_t MAX_PATH = 260;
+
+ushort MAKEWORD(ubyte a, ubyte b) {
+	return cast(ushort) ((b << 8) | a);
+}
+
+uint MAKELONG(ushort a, ushort b) {
+	return cast(uint) ((b << 16) | a);
+}
+
+ushort LOWORD(uint l) {
+	return cast(ushort) l;
+}
+
+ushort HIWORD(uint l) {
+	return cast(ushort) (l >>> 16);
+}
+
+ubyte LOBYTE(ushort w) {
+	return cast(ubyte) w;
+}
+
+ubyte HIBYTE(ushort w) {
+	return cast(ubyte) (w >>> 8);
+}
+
+template max(T) {
+	T max(T a, T b) {
+		return a > b ? a : b;
+	}
+}
+
+template min(T) {
+	T min(T a, T b) {
+		return a < b ? a : b;
+	}
+}
+
+alias ubyte   BYTE;
+alias ubyte*  PBYTE, LPBYTE;
+alias ushort  USHORT, WORD, ATOM;
+alias ushort* PUSHORT, PWORD, LPWORD;
+alias uint    ULONG, DWORD, UINT, COLORREF;
+alias uint*   PULONG, PDWORD, LPDWORD, PUINT, LPUINT;
+alias int     WINBOOL, BOOL, INT, LONG, HFILE, HRESULT;
+alias int*    PWINBOOL, LPWINBOOL, PBOOL, LPBOOL, PINT, LPINT, LPLONG;
+alias float   FLOAT;
+alias float*  PFLOAT;
+alias void*   PCVOID, LPCVOID;
+
+alias UINT_PTR WPARAM;
+alias LONG_PTR LPARAM, LRESULT;
+
+alias HANDLE HGLOBAL, HLOCAL, GLOBALHANDLE, LOCALHANDLE, HGDIOBJ, HACCEL,
+  HBITMAP, HBRUSH, HCOLORSPACE, HDC, HGLRC, HDESK, HENHMETAFILE, HFONT,
+  HICON, HINSTANCE, HKEY, HMENU, HMETAFILE, HMODULE, HMONITOR, HPALETTE, HPEN,
+  HRGN, HRSRC, HSTR, HTASK, HWND, HWINSTA, HKL, HCURSOR;
+alias HANDLE* PHKEY;
+
+static if (WINVER >= 0x500) {
+	alias HANDLE HTERMINAL, HWINEVENTHOOK;
+}
+
+alias extern (Windows) int function() FARPROC, NEARPROC, PROC;
+
+struct RECT {
+	LONG left;
+	LONG top;
+	LONG right;
+	LONG bottom;
+}
+alias RECT RECTL;
+alias RECT* PRECT, LPRECT, LPCRECT, PRECTL, LPRECTL, LPCRECTL;
+
+struct POINT {
+	LONG x;
+	LONG y;
+}
+alias POINT POINTL;
+alias POINT* PPOINT, LPPOINT, PPOINTL, LPPOINTL;
+
+struct SIZE {
+	LONG cx;
+	LONG cy;
+}
+alias SIZE SIZEL;
+alias SIZE* PSIZE, LPSIZE, PSIZEL, LPSIZEL;
+
+struct POINTS {
+	SHORT x;
+	SHORT y;
+}
+alias POINTS* PPOINTS, LPPOINTS;
+
+enum : BOOL {
+	FALSE = 0,
+	TRUE  = 1
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/c/x_composite.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,1 @@
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/c/x_types.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,25 @@
+module dynamin.c.x_types;
+
+extern(C):
+
+// Most of these types were foolishly not prefixed with X
+
+alias ubyte* XPointer;
+alias uint XID;
+alias uint Mask;
+alias uint Atom;
+alias uint VisualID;
+alias uint Time;
+alias XID Window;
+alias XID Drawable;
+alias XID Pixmap;
+alias XID Cursor;
+alias XID Colormap;
+alias XID KeySym;
+alias ubyte KeyCode;
+alias int Bool;
+alias int Status;
+
+alias void Screen;
+alias void Display;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/c/xlib.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,1522 @@
+module dynamin.c.xlib;
+
+/*
+ * A binding to at least the part of the X Window System that Dynamin uses. This
+ * binding is incomplete as it is made only for Dynamin's use.
+ */
+
+import dynamin.c.x_types;
+
+version(build) { pragma(link, X11); }
+
+extern(C):
+
+alias ubyte* XPointer;
+alias uint XID;
+// export these with an X prefix so they won't as easily conflict
+alias uint XMask;
+alias uint XAtom;
+alias uint XVisualID;
+alias uint XTime;
+alias XID XWindow;
+alias XID XDrawable;
+alias XID XPixmap;
+alias XID XCursor;
+alias XID XColormap;
+alias XID XKeySym;
+alias ubyte XKeyCode;
+alias int XBool;
+alias int XStatus;
+
+alias void XScreen;
+alias void XDisplay;
+
+const None            = 0;
+const ParentRelative  = 1;
+const CopyFromParent  = 0;
+const PointerWindow   = 0;
+const InputFocus      = 1;
+const PointerRoot     = 1;
+const AnyPropertyType = 0;
+const AnyKey          = 0;
+const AnyButton       = 0;
+const AllTemporary    = 0;
+const CurrentTime     = 0;
+const NoSymbol        = 0;
+
+//{{{ display functions
+Display* XOpenDisplay(char* display_name);
+
+int XNextEvent(Display* display, XEvent* event_return);
+
+int XEventsQueued(Display* display, int mode);
+
+int XPending(Display* display);
+
+int XPutBackEvent(Display* display, XEvent* event);
+
+Status XSendEvent(
+	Display* display,
+	Window w,
+	Bool propagate,
+	int event_mask,
+	XEvent* event_send);
+
+int XCloseDisplay(Display* display);
+
+char* XDisplayString(Display* display);
+
+int XFlush(Display* display);
+
+int XSync(Display* display, Bool discard);
+
+int XDisplayWidth(Display* display, int screen_number);
+
+int XDisplayHeight(Display* display, int screen_number);
+
+int XProtocolRevision(Display* display);
+
+int XProtocolVersion(Display* display);
+
+Colormap XDefaultColormap(Display* display, int screen_number);
+
+Status XAllocColor(Display* display, Colormap colormap, XColor* screen_in_out);
+//}}}
+
+//{{{ window functions
+Window XCreateSimpleWindow(
+	Display* display,
+	Window parent,
+	int x,
+	int y,
+	uint width,
+	uint height,
+	uint border_width,
+	uint border,
+	uint background);
+
+Window XCreateWindow(
+	Display* display,
+	Window parent,
+	int x,
+	int y,
+	uint width,
+	uint height,
+	uint border_width,
+	int depth,
+	uint c_class,
+	Visual* visual,
+	uint valuemask,
+	XSetWindowAttributes* attributes);
+
+int XDestroyWindow(Display* display, Window w);
+
+int XClearArea(
+	Display* display,
+	Window w,
+	int x,
+	int y,
+	uint width,
+	uint height,
+	Bool exposures);
+
+Window XRootWindow(Display* display, int screen_number);
+
+Window XDefaultRootWindow(Display* display);
+
+Status XQueryTree(
+	Display* display,
+	Window w,
+	Window* root_return,
+	Window* parent_return,
+	Window** children_return,
+	uint* nchildren_return);
+
+Bool XTranslateCoordinates(
+	Display* display,
+	Window src_w,
+	Window dest_w,
+	int src_x,
+	int src_y,
+	int* dest_x_return,
+	int* dest_y_return,
+	Window* child_return);
+
+Window XRootWindowOfScreen(Screen* screen);
+
+Status XIconifyWindow(Display* display, Window w, int screen_number);
+
+Status XWithdrawWindow(Display* display, Window w, int screen_number);
+
+int XChangeProperty(
+	Display* display,
+	Window w,
+	Atom property,
+	Atom type,
+	int format,
+	int mode,
+	void* data,
+	int nelements);
+
+Bool XCheckIfEvent(
+	Display* display,
+	XEvent* event_return,
+	Bool function(Display* display,
+		XEvent* event,
+		XPointer arg) predicate,
+	XPointer arg);
+
+int XGetWindowProperty(
+	Display* display,
+	Window w,
+	Atom property,
+	int long_offset,
+	int long_length,
+	Bool should_delete,
+	Atom req_type,
+	Atom* actual_type_return,
+	int* actual_format_return,
+	uint* nitems_return,
+	uint* bytes_after_return,
+	void** prop_return);
+
+int XDeleteProperty(Display* display, Window w, Atom property);
+
+int XMapWindow(Display* display, Window w);
+
+int XUnmapWindow(Display* display, Window w);
+
+int XMoveWindow(Display* display, Window w, int x, int y);
+
+int XResizeWindow(Display* display, Window w, uint width, uint height);
+
+int XMoveResizeWindow(Display* display, Window w,
+	int x, int y, uint width, uint height);
+
+int XSelectInput(Display* display, Window w, int event_mask);
+
+int XReparentWindow(Display* display, Window w, Window parent, int x, int y);
+
+int XRestackWindows(Display* display, Window* windows, int nwindows);
+
+int XChangeWindowAttributes(
+	Display* display,
+	Window w,
+	uint valuemask,
+	XSetWindowAttributes* attributes);
+
+Status XGetWindowAttributes(
+	Display* display,
+	Window w,
+	XWindowAttributes* window_attributes_return);
+//}}}
+
+//{{{ screen functions
+int XDefaultScreen(Display* display);
+
+Screen* XDefaultScreenOfDisplay(Display* display);
+
+int XScreenCount(Display* display);
+
+Screen* XScreenOfDisplay(Display* display, int screen_number);
+
+Display* XDisplayOfScreen(Screen* screen);
+
+int XScreenNumberOfScreen(Screen* screen);
+
+int XWidthOfScreen(Screen* screen);
+
+int XHeightOfScreen(Screen* screen);
+
+int XDefaultDepthOfScreen(Screen* screen);
+
+//}}}
+
+//{{{ pixmap functions
+Pixmap XCreatePixmap(Display* display, Drawable d, uint width, uint height, uint depth);
+
+Pixmap XCreatePixmapFromBitmapData(
+	Display* display,
+	Drawable d,
+	ubyte* data,
+	uint width,
+	uint height,
+	uint fg,
+	uint bg,
+	uint depth);
+
+int XFreePixmap(Display* display, Pixmap pixmap);
+//}}}
+
+//{{{ cursor functions
+Cursor XCreatePixmapCursor(
+	Display* display,
+	Pixmap source,
+	Pixmap mask,
+	XColor* foreground_color,
+	XColor* background_color,
+	uint x,
+	uint y);
+
+int XDefineCursor(Display* display, Window w, Cursor cursor);
+
+int XUndefineCursor(Display* display, Window w);
+
+int XFreeCursor(Display* display, Cursor cursor);
+
+//}}}
+
+//{{{ keyboard functions
+KeyCode XKeysymToKeycode(Display* display, KeySym keysym);
+
+KeySym XKeycodeToKeysym(
+	Display* display,
+	// if NeedWidePrototypes
+	uint keycode,
+	// else
+	//KeyCode keycode,
+	int index);
+
+char* XKeysymToString(KeySym keysym);
+
+KeySym XStringToKeysym(char* string);
+//}}}
+
+char** XListExtensions(Display* display, int* nextensions_return);
+
+int XFreeExtensionList(char** list);
+
+XExtCodes* XInitExtension(Display* display, char* name);
+
+uint XBlackPixel(Display* display, int screen_number);
+
+uint XWhitePixel(Display* display, int screen_number);
+
+int XDefaultDepth(Display* display, int screen_number);
+
+int XFree(void* data);
+
+int XNoOp(Display* display);
+
+Visual* XDefaultVisual(Display* display, int screen_number);
+
+Visual* XDefaultVisualOfScreen(Screen* screen);
+
+Window XGetSelectionOwner(Display* display, Atom selection);
+
+int XSetSelectionOwner(Display* display, Atom selection, Window owner, Time time);
+
+void XSetWMName(Display* display, Window w, XTextProperty* text_prop);
+
+Status XSetWMProtocols(Display* display, Window w, Atom* protocols, int count);
+
+int XConvertSelection(
+	Display* display,
+	Atom selection,
+	Atom target,
+	Atom property,
+	Window requestor,
+	Time time);
+
+//{{{ atoms
+Atom XInternAtom(Display* display, char* atom_name, Bool only_if_exists);
+
+Status XInternAtoms(
+	Display* dpy,
+	char** names,
+	int count,
+	Bool onlyIfExists,
+	Atom* atoms_return);
+
+enum : Atom {
+	XA_PRIMARY             = 1,
+	XA_SECONDARY           = 2,
+	XA_ARC                 = 3,
+	XA_ATOM                = 4,
+	XA_BITMAP              = 5,
+	XA_CARDINAL            = 6,
+	XA_COLORMAP            = 7,
+	XA_CURSOR              = 8,
+	XA_CUT_BUFFER0         = 9,
+	XA_CUT_BUFFER1         = 10,
+	XA_CUT_BUFFER2         = 11,
+	XA_CUT_BUFFER3         = 12,
+	XA_CUT_BUFFER4         = 13,
+	XA_CUT_BUFFER5         = 14,
+	XA_CUT_BUFFER6         = 15,
+	XA_CUT_BUFFER7         = 16,
+	XA_DRAWABLE            = 17,
+	XA_FONT                = 18,
+	XA_INTEGER             = 19,
+	XA_PIXMAP              = 20,
+	XA_POINT               = 21,
+	XA_RECTANGLE           = 22,
+	XA_RESOURCE_MANAGER    = 23,
+	XA_RGB_COLOR_MAP       = 24,
+	XA_RGB_BEST_MAP        = 25,
+	XA_RGB_BLUE_MAP        = 26,
+	XA_RGB_DEFAULT_MAP     = 27,
+	XA_RGB_GRAY_MAP        = 28,
+	XA_RGB_GREEN_MAP       = 29,
+	XA_RGB_RED_MAP         = 30,
+	XA_STRING              = 31,
+	XA_VISUALID            = 32,
+	XA_WINDOW              = 33,
+	XA_WM_COMMAND          = 34,
+	XA_WM_HINTS            = 35,
+	XA_WM_CLIENT_MACHINE   = 36,
+	XA_WM_ICON_NAME        = 37,
+	XA_WM_ICON_SIZE        = 38,
+	XA_WM_NAME             = 39,
+	XA_WM_NORMAL_HINTS     = 40,
+	XA_WM_SIZE_HINTS       = 41,
+	XA_WM_ZOOM_HINTS       = 42,
+	XA_MIN_SPACE           = 43,
+	XA_NORM_SPACE          = 44,
+	XA_MAX_SPACE           = 45,
+	XA_END_SPACE           = 46,
+	XA_SUPERSCRIPT_X       = 47,
+	XA_SUPERSCRIPT_Y       = 48,
+	XA_SUBSCRIPT_X         = 49,
+	XA_SUBSCRIPT_Y         = 50,
+	XA_UNDERLINE_POSITION  = 51,
+	XA_UNDERLINE_THICKNESS = 52,
+	XA_STRIKEOUT_ASCENT    = 53,
+	XA_STRIKEOUT_DESCENT   = 54,
+	XA_ITALIC_ANGLE        = 55,
+	XA_X_HEIGHT            = 56,
+	XA_QUAD_WIDTH          = 57,
+	XA_WEIGHT              = 58,
+	XA_POINT_SIZE          = 59,
+	XA_RESOLUTION          = 60,
+	XA_COPYRIGHT           = 61,
+	XA_NOTICE              = 62,
+	XA_FONT_NAME           = 63,
+	XA_FAMILY_NAME         = 64,
+	XA_FULL_NAME           = 65,
+	XA_CAP_HEIGHT          = 66,
+	XA_WM_CLASS            = 67,
+	XA_WM_TRANSIENT_FOR    = 68,
+
+	XA_LAST_PREDEFINED     = 68
+}
+//}}}
+
+Status XStringListToTextProperty(char** list, int count, XTextProperty* text_prop_return);
+
+XSizeHints* XAllocSizeHints();
+
+void XSetWMNormalHints(Display* display, Window w, XSizeHints* hints);
+
+Status XGetWMNormalHints(
+	Display* display,
+	Window w,
+	XSizeHints* hints_return,
+	long* supplied_return);
+
+XWMHints* XAllocWMHints();
+
+int XSetWMHints(Display* display, Window w, XWMHints* wm_hints);
+
+XWMHints *XGetWMHints(Display* display, Window w);
+
+//{{{ enums
+enum {
+	InputHint        = 1 << 0,
+	StateHint        = 1 << 1,
+	IconPixmapHint   = 1 << 2,
+	IconWindowHint   = 1 << 3,
+	IconPositionHint = 1 << 4,
+	IconMaskHint     = 1 << 5,
+	WindowGroupHint  = 1 << 6,
+	AllHints         = InputHint | StateHint | IconPixmapHint | IconWindowHint |
+	                   IconPositionHint | IconMaskHint | WindowGroupHint,
+	XUrgencyHint     = 1 << 8
+}
+
+enum {
+	WithdrawnState = 0,
+	NormalState = 1,
+	IconicState = 3
+}
+
+enum {
+	QueuedAlready      = 0,
+	QueuedAfterReading = 1,
+	QueuedAfterFlush   = 2
+}
+
+enum {
+	USPosition	= 1 << 0,
+	USSize		= 1 << 1,
+	PPosition	= 1 << 2,
+	PSize		= 1 << 3,
+	PMinSize	= 1 << 4,
+	PMaxSize	= 1 << 5,
+	PResizeInc	= 1 << 6,
+	PAspect		= 1 << 7,
+	PBaseSize	= 1 << 8,
+	PWinGravity	= 1 << 9
+}
+
+enum {
+	ShiftMask   = 1 << 0,
+	LockMask    = 1 << 1,
+	ControlMask = 1 << 2,
+	Mod1Mask    = 1 << 3,
+	Mod2Mask    = 1 << 4,
+	Mod3Mask    = 1 << 5,
+	Mod4Mask    = 1 << 6,
+	Mod5Mask    = 1 << 7
+}
+
+enum {
+	ShiftMapIndex   = 0,
+	LockMapIndex    = 1,
+	ControlMapIndex = 2,
+	Mod1MapIndex    = 3,
+	Mod2MapIndex    = 4,
+	Mod3MapIndex    = 5,
+	Mod4MapIndex    = 6,
+	Mod5MapIndex    = 7
+}
+
+enum {
+	Button1Mask = 1 << 8,
+	Button2Mask = 1 << 9,
+	Button3Mask = 1 << 10,
+	Button4Mask = 1 << 11,
+	Button5Mask = 1 << 12
+}
+
+enum {
+	AnyModifier = 1 << 15
+}
+
+enum {
+	Button1 = 1,
+	Button2 = 2,
+	Button3 = 3,
+	Button4 = 4,
+	Button5 = 5
+}
+
+enum {
+	NotifyNormal       = 0,
+	NotifyGrab         = 1,
+	NotifyUngrab       = 2,
+	NotifyWhileGrabbed = 3
+}
+
+enum {
+	NotifyHint = 1
+}
+
+enum {
+	NotifyAncestor         = 0,
+	NotifyVirtual          = 1,
+	NotifyInferior         = 2,
+	NotifyNonlinear        = 3,
+	NotifyNonlinearVirtual = 4,
+	NotifyPointer          = 5,
+	NotifyPointerRoot      = 6,
+	NotifyDetailNone       = 7
+}
+
+enum {
+	VisibilityUnobscured        = 0,
+	VisibilityPartiallyObscured = 1,
+	VisibilityFullyObscured     = 2
+}
+
+enum {
+	PlaceOnTop    = 0,
+	PlaceOnBottom = 1
+}
+
+enum {
+	FamilyInternet  = 0,
+	FamilyDECnet    = 1,
+	FamilyChaos     = 2,
+	FamilyInternet6 = 6
+}
+
+enum {
+	FamilyServerInterpreted = 5
+}
+
+enum {
+	PropertyNewValue = 0,
+	PropertyDelete   = 1
+}
+
+enum {
+	ColormapUninstalled = 0,
+	ColormapInstalled   = 1
+}
+
+enum {
+	GrabModeSync  = 0,
+	GrabModeAsync = 1
+}
+
+enum {
+	GrabSuccess     = 0,
+	AlreadyGrabbed  = 1,
+	GrabInvalidTime = 2,
+	GrabNotViewable = 3,
+	GrabFrozen      = 4
+}
+
+enum {
+	AsyncPointer   = 0,
+	SyncPointer    = 1,
+	ReplayPointer  = 2,
+	AsyncKeyboard  = 3,
+	SyncKeyboard   = 4,
+	ReplayKeyboard = 5,
+	AsyncBoth      = 6,
+	SyncBoth       = 7
+}
+
+enum {
+	RevertToNone        = None,
+	RevertToPointerRoot = PointerRoot,
+	RevertToParent      = 2
+}
+
+enum {
+	InputOutput = 1,
+	InputOnly   = 2
+}
+
+enum {
+	CWBackPixmap       = 1 << 0,
+	CWBackPixel        = 1 << 1,
+	CWBorderPixmap     = 1 << 2,
+	CWBorderPixel      = 1 << 3,
+	CWBitGravity       = 1 << 4,
+	CWWinGravity       = 1 << 5,
+	CWBackingStore     = 1 << 6,
+	CWBackingPlanes    = 1 << 7,
+	CWBackingPixel     = 1 << 8,
+	CWOverrideRedirect = 1 << 9,
+	CWSaveUnder        = 1 << 10,
+	CWEventMask        = 1 << 11,
+	CWDontPropagate    = 1 << 12,
+	CWColormap         = 1 << 13,
+	CWCursor           = 1 << 14,
+}
+
+enum {
+	CWX           = 1 << 0,
+	CWY           = 1 << 1,
+	CWWidth       = 1 << 2,
+	CWHeight      = 1 << 3,
+	CWBorderWidth = 1 << 4,
+	CWSibling     = 1 << 5,
+	CWStackMode   = 1 << 6
+}
+
+enum {
+	ForgetGravity    = 0,
+	NorthWestGravity = 1,
+	NorthGravity     = 2,
+	NorthEastGravity = 3,
+	WestGravity      = 4,
+	CenterGravity    = 5,
+	EastGravity      = 6,
+	SouthWestGravity = 7,
+	SouthGravity     = 8,
+	SouthEastGravity = 9,
+	StaticGravity    = 10
+}
+
+enum {
+	UnmapGravity = 0
+}
+
+enum {
+	NotUseful  = 0,
+	WhenMapped = 1,
+	Always     = 2
+}
+
+enum {
+	IsUnmapped   = 0,
+	IsUnviewable = 1,
+	IsViewable   = 2
+}
+
+enum {
+	SetModeInsert = 0,
+	SetModeDelete = 1
+}
+
+enum {
+	DestroyAll      = 0,
+	RetainPermanent = 1,
+	RetainTemporary = 2
+}
+
+enum {
+	Above    = 0,
+	Below    = 1,
+	TopIf    = 2,
+	BottomIf = 3,
+	Opposite = 4
+}
+
+enum {
+	RaiseLowest  = 0,
+	LowerHighest = 1
+}
+
+enum {
+	PropModeReplace = 0,
+	PropModePrepend = 1,
+	PropModeAppend  = 2
+}
+//}}}
+
+struct XSizeHints {
+	int flags;
+	int x, y;
+	int width, height;
+	int min_width, min_height;
+	int max_width, max_height;
+	int width_inc, height_inc;
+	struct _aspect {
+		int x;
+		int y;
+	}
+	_aspect min_aspect, max_aspect;
+	int base_width, base_height;
+	int win_gravity;
+}
+
+struct XWMHints {
+	int flags;
+	Bool input;
+	int initial_state;
+	Pixmap icon_pixmap;
+	Window icon_window;
+	int icon_x, icon_y;
+	Pixmap icon_mask;
+	XID window_group;
+}
+
+struct XTextProperty {
+	char* value;
+	Atom encoding;
+	int format;
+	uint nitems;
+}
+
+struct XColor {
+	uint pixel;
+	ushort red, green, blue;
+	ubyte flags;
+	ubyte pad;
+}
+
+struct Visual {
+	XExtData* ext_data;
+	VisualID visualid;
+	int c_class;
+	uint red_mask, green_mask, blue_mask;
+	int bits_per_rgb;
+	int map_entries;
+}
+
+struct XExtCodes {
+	int extension;
+	int major_opcode;
+	int first_event;
+	int first_error;
+}
+
+struct XExtData {
+	int number;
+	XExtData* next;
+	int function(XExtData* extension) free_private;
+	XPointer private_data;
+}
+
+struct XSetWindowAttributes {
+	Pixmap background_pixmap;
+	uint background_pixel;
+	Pixmap border_pixmap;
+	uint border_pixel;
+	int bit_gravity;
+	int win_gravity;
+	int backing_store;
+	uint backing_planes;
+	uint backing_pixel;
+	Bool save_under;
+	int event_mask;
+	int do_not_propagate_mask;
+	Bool override_redirect;
+	Colormap colormap;
+	Cursor cursor;
+}
+
+struct XWindowAttributes {
+	int x, y;
+	int width, height;
+	int border_width;
+	int depth;
+	Visual* visual;
+	Window root;
+	int c_class;
+	int bit_gravity;
+	int win_gravity;
+	int backing_store;
+	uint backing_planes;
+	uint backing_pixel;
+	Bool save_under;
+	Colormap colormap;
+	Bool map_installed;
+	int map_state;
+	int all_event_masks;
+	int your_event_mask;
+	int do_not_propagate_mask;
+	Bool override_redirect;
+	Screen* screen;
+}
+
+//{{{ events
+struct XKeyEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window window;
+	Window root;
+	Window subwindow;
+	Time time;
+	int x, y;
+	int x_root, y_root;
+	uint state;
+	uint keycode;
+	Bool same_screen;
+}
+alias XKeyEvent XKeyPressedEvent;
+alias XKeyEvent XKeyReleasedEvent;
+
+struct XButtonEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window window;
+	Window root;
+	Window subwindow;
+	Time time;
+	int x, y;
+	int x_root, y_root;
+	uint state;
+	uint button;
+	Bool same_screen;
+}
+alias XButtonEvent XButtonPressedEvent;
+alias XButtonEvent XButtonReleasedEvent;
+
+
+struct XMotionEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window window;
+	Window root;
+	Window subwindow;
+	Time time;
+	int x, y;
+	int x_root, y_root;
+	uint state;
+	char is_hint;
+	Bool same_screen;
+}
+alias XMotionEvent XPointerMovedEvent;
+
+struct XCrossingEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window window;
+	Window root;
+	Window subwindow;
+	Time time;
+	int x, y;
+	int x_root, y_root;
+	int mode;
+	int detail;
+	Bool same_screen;
+	Bool focus;
+	uint state;
+}
+alias XCrossingEvent XEnterWindowEvent;
+alias XCrossingEvent XLeaveWindowEvent;
+
+struct XFocusChangeEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window window;
+	int mode;
+	int detail;
+}
+alias XFocusChangeEvent XFocusInEvent;
+alias XFocusChangeEvent XFocusOutEvent;
+
+struct XKeymapEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window window;
+	char key_vector[32];
+}
+
+struct XExposeEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window window;
+	int x, y;
+	int width, height;
+	int count;
+}
+
+struct XGraphicsExposeEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Drawable drawable;
+	int x, y;
+	int width, height;
+	int count;
+	int major_code;
+	int minor_code;
+}
+
+struct XNoExposeEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Drawable drawable;
+	int major_code;
+	int minor_code;
+}
+
+struct XVisibilityEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window window;
+	int state;
+}
+
+struct XCreateWindowEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window parent;
+	Window window;
+	int x, y;
+	int width, height;
+	int border_width;
+	Bool override_redirect;
+}
+
+struct XDestroyWindowEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window event;
+	Window window;
+}
+
+struct XUnmapEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window event;
+	Window window;
+	Bool from_configure;
+}
+
+struct XMapEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window event;
+	Window window;
+	Bool override_redirect;
+}
+
+struct XMapRequestEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window parent;
+	Window window;
+}
+
+struct XReparentEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window event;
+	Window window;
+	Window parent;
+	int x, y;
+	Bool override_redirect;
+}
+
+struct XConfigureEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window event;
+	Window window;
+	int x, y;
+	int width, height;
+	int border_width;
+	Window above;
+	Bool override_redirect;
+}
+
+struct XGravityEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window event;
+	Window window;
+	int x, y;
+}
+
+struct XResizeRequestEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window window;
+	int width, height;
+}
+
+struct XConfigureRequestEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window parent;
+	Window window;
+	int x, y;
+	int width, height;
+	int border_width;
+	Window above;
+	int detail;
+	uint value_mask;
+}
+
+struct XCirculateEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window event;
+	Window window;
+	int place;
+}
+
+struct XCirculateRequestEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window parent;
+	Window window;
+	int place;
+}
+
+struct XPropertyEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window window;
+	Atom atom;
+	Time time;
+	int state;
+}
+
+struct XSelectionClearEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window window;
+	Atom selection;
+	Time time;
+}
+
+struct XSelectionRequestEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window owner;
+	Window requestor;
+	Atom selection;
+	Atom target;
+	Atom property;
+	Time time;
+}
+
+struct XSelectionEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window requestor;
+	Atom selection;
+	Atom target;
+	Atom property;
+	Time time;
+}
+
+struct XColormapEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window window;
+	Colormap colormap;
+	Bool is_new;
+	int state;
+}
+
+struct XClientMessageEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window window;
+	Atom message_type;
+	int format;
+	union _data {
+		char[20] b;
+		short[10] s;
+		int[5] l;
+	}
+	_data data;
+}
+
+struct XMappingEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window window;
+	int request;
+	int first_keycode;
+	int count;
+}
+
+struct XErrorEvent {
+	int type;
+	Display* display;
+	XID resourceid;
+	uint serial;
+	ubyte error_code;
+	ubyte request_code;
+	ubyte minor_code;
+}
+
+struct XAnyEvent {
+	int type;
+	uint serial;
+	Bool send_event;
+	Display* display;
+	Window window;
+}
+
+union XEvent {
+		int type;
+	XAnyEvent xany;
+	XKeyEvent xkey;
+	XButtonEvent xbutton;
+	XMotionEvent xmotion;
+	XCrossingEvent xcrossing;
+	XFocusChangeEvent xfocus;
+	XExposeEvent xexpose;
+	XGraphicsExposeEvent xgraphicsexpose;
+	XNoExposeEvent xnoexpose;
+	XVisibilityEvent xvisibility;
+	XCreateWindowEvent xcreatewindow;
+	XDestroyWindowEvent xdestroywindow;
+	XUnmapEvent xunmap;
+	XMapEvent xmap;
+	XMapRequestEvent xmaprequest;
+	XReparentEvent xreparent;
+	XConfigureEvent xconfigure;
+	XGravityEvent xgravity;
+	XResizeRequestEvent xresizerequest;
+	XConfigureRequestEvent xconfigurerequest;
+	XCirculateEvent xcirculate;
+	XCirculateRequestEvent xcirculaterequest;
+	XPropertyEvent xproperty;
+	XSelectionClearEvent xselectionclear;
+	XSelectionRequestEvent xselectionrequest;
+	XSelectionEvent xselection;
+	XColormapEvent xcolormap;
+	XClientMessageEvent xclient;
+	XMappingEvent xmapping;
+	XErrorEvent xerror;
+	XKeymapEvent xkeymap;
+	int pad[24];
+}
+//}}}
+
+//{{{ event types
+enum {
+	KeyPress         = 2,
+	KeyRelease       = 3,
+	ButtonPress      = 4,
+	ButtonRelease    = 5,
+	MotionNotify     = 6,
+	EnterNotify      = 7,
+	LeaveNotify      = 8,
+	FocusIn          = 9,
+	FocusOut         = 10,
+	KeymapNotify     = 11,
+	Expose           = 12,
+	GraphicsExpose   = 13,
+	NoExpose         = 14,
+	VisibilityNotify = 15,
+	CreateNotify     = 16,
+	DestroyNotify    = 17,
+	UnmapNotify      = 18,
+	MapNotify        = 19,
+	MapRequest       = 20,
+	ReparentNotify   = 21,
+	ConfigureNotify  = 22,
+	ConfigureRequest = 23,
+	GravityNotify    = 24,
+	ResizeRequest    = 25,
+	CirculateNotify  = 26,
+	CirculateRequest = 27,
+	PropertyNotify   = 28,
+	SelectionClear   = 29,
+	SelectionRequest = 30,
+	SelectionNotify  = 31,
+	ColormapNotify   = 32,
+	ClientMessage    = 33,
+	MappingNotify    = 34,
+	LASTEvent        = 35 // must be bigger than any event #
+}
+//}}}
+
+//{{{ event masks
+enum {
+	NoEventMask              = 0,
+	KeyPressMask             = 1 << 0,
+	KeyReleaseMask           = 1 << 1,
+	ButtonPressMask          = 1 << 2,
+	ButtonReleaseMask        = 1 << 3,
+	EnterWindowMask          = 1 << 4,
+	LeaveWindowMask          = 1 << 5,
+	PointerMotionMask        = 1 << 6,
+	PointerMotionHintMask    = 1 << 7,
+	Button1MotionMask        = 1 << 8,
+	Button2MotionMask        = 1 << 9,
+	Button3MotionMask        = 1 << 10,
+	Button4MotionMask        = 1 << 11,
+	Button5MotionMask        = 1 << 12,
+	ButtonMotionMask         = 1 << 13,
+	KeymapStateMask          = 1 << 14,
+	ExposureMask             = 1 << 15,
+	VisibilityChangeMask     = 1 << 16,
+	StructureNotifyMask      = 1 << 17,
+	ResizeRedirectMask       = 1 << 18,
+	SubstructureNotifyMask   = 1 << 19,
+	SubstructureRedirectMask = 1 << 20,
+	FocusChangeMask          = 1 << 21,
+	PropertyChangeMask       = 1 << 22,
+	ColormapChangeMask       = 1 << 23,
+	OwnerGrabButtonMask      = 1 << 24
+}
+//}}}
+
+//{{{ keys
+enum {
+	XK_BackSpace     = 0xFF08,
+	XK_Tab           = 0xFF09,
+	XK_Linefeed      = 0xFF0A,
+	XK_Clear         = 0xFF0B,
+	XK_Return        = 0xFF0D,
+	XK_Pause         = 0xFF13,
+	XK_Scroll_Lock   = 0xFF14,
+	XK_Sys_Req       = 0xFF15,
+	XK_Escape        = 0xFF1B,
+	XK_Delete        = 0xFFFF,
+
+	XK_Home          = 0xFF50,
+	XK_Left          = 0xFF51,
+	XK_Up            = 0xFF52,
+	XK_Right         = 0xFF53,
+	XK_Down          = 0xFF54,
+	XK_Prior         = 0xFF55,
+	XK_Page_Up       = 0xFF55,
+	XK_Next          = 0xFF56,
+	XK_Page_Down     = 0xFF56,
+	XK_End           = 0xFF57,
+	XK_Begin         = 0xFF58,
+
+	XK_Select        = 0xFF60,
+	XK_Print         = 0xFF61,
+	XK_Execute       = 0xFF62,
+	XK_Insert        = 0xFF63,
+	XK_Undo          = 0xFF65,
+	XK_Redo          = 0xFF66,
+	XK_Menu          = 0xFF67,
+	XK_Find          = 0xFF68,
+	XK_Cancel        = 0xFF69,
+	XK_Help          = 0xFF6A,
+	XK_Break         = 0xFF6B,
+	XK_Mode_switch   = 0xFF7E,
+	XK_script_switch = 0xFF7E,
+	XK_Num_Lock      = 0xFF7F,
+
+	XK_KP_Space      = 0xFF80,
+	XK_KP_Tab        = 0xFF89,
+	XK_KP_Enter      = 0xFF8D,
+	XK_KP_F1         = 0xFF91,
+	XK_KP_F2         = 0xFF92,
+	XK_KP_F3         = 0xFF93,
+	XK_KP_F4         = 0xFF94,
+	XK_KP_Home       = 0xFF95,
+	XK_KP_Left       = 0xFF96,
+	XK_KP_Up         = 0xFF97,
+	XK_KP_Right      = 0xFF98,
+	XK_KP_Down       = 0xFF99,
+	XK_KP_Prior      = 0xFF9A,
+	XK_KP_Page_Up    = 0xFF9A,
+	XK_KP_Next       = 0xFF9B,
+	XK_KP_Page_Down  = 0xFF9B,
+	XK_KP_End        = 0xFF9C,
+	XK_KP_Begin      = 0xFF9D,
+	XK_KP_Insert     = 0xFF9E,
+	XK_KP_Delete     = 0xFF9F,
+	XK_KP_Equal      = 0xFFBD,
+	XK_KP_Multiply   = 0xFFAA,
+	XK_KP_Add        = 0xFFAB,
+	XK_KP_Separator  = 0xFFAC,
+	XK_KP_Subtract   = 0xFFAD,
+	XK_KP_Decimal    = 0xFFAE,
+	XK_KP_Divide     = 0xFFAF,
+
+	XK_KP_0          = 0xFFB0,
+	XK_KP_1          = 0xFFB1,
+	XK_KP_2          = 0xFFB2,
+	XK_KP_3          = 0xFFB3,
+	XK_KP_4          = 0xFFB4,
+	XK_KP_5          = 0xFFB5,
+	XK_KP_6          = 0xFFB6,
+	XK_KP_7          = 0xFFB7,
+	XK_KP_8          = 0xFFB8,
+	XK_KP_9          = 0xFFB9,
+
+	XK_F1            = 0xFFBE,
+	XK_F2            = 0xFFBF,
+	XK_F3            = 0xFFC0,
+	XK_F4            = 0xFFC1,
+	XK_F5            = 0xFFC2,
+	XK_F6            = 0xFFC3,
+	XK_F7            = 0xFFC4,
+	XK_F8            = 0xFFC5,
+	XK_F9            = 0xFFC6,
+	XK_F10           = 0xFFC7,
+	XK_F11           = 0xFFC8,
+	XK_L1            = 0xFFC8,
+	XK_F12           = 0xFFC9,
+	XK_L2            = 0xFFC9,
+	XK_F13           = 0xFFCA,
+	XK_L3            = 0xFFCA,
+	XK_F14           = 0xFFCB,
+	XK_L4            = 0xFFCB,
+	XK_F15           = 0xFFCC,
+	XK_L5            = 0xFFCC,
+	XK_F16           = 0xFFCD,
+	XK_L6            = 0xFFCD,
+	XK_F17           = 0xFFCE,
+	XK_L7            = 0xFFCE,
+	XK_F18           = 0xFFCF,
+	XK_L8            = 0xFFCF,
+	XK_F19           = 0xFFD0,
+	XK_L9            = 0xFFD0,
+	XK_F20           = 0xFFD1,
+	XK_L10           = 0xFFD1,
+	XK_F21           = 0xFFD2,
+	XK_R1            = 0xFFD2,
+	XK_F22           = 0xFFD3,
+	XK_R2            = 0xFFD3,
+	XK_F23           = 0xFFD4,
+	XK_R3            = 0xFFD4,
+	XK_F24           = 0xFFD5,
+	XK_R4            = 0xFFD5,
+	XK_F25           = 0xFFD6,
+	XK_R5            = 0xFFD6,
+	XK_F26           = 0xFFD7,
+	XK_R6            = 0xFFD7,
+	XK_F27           = 0xFFD8,
+	XK_R7            = 0xFFD8,
+	XK_F28           = 0xFFD9,
+	XK_R8            = 0xFFD9,
+	XK_F29           = 0xFFDA,
+	XK_R9            = 0xFFDA,
+	XK_F30           = 0xFFDB,
+	XK_R10           = 0xFFDB,
+	XK_F31           = 0xFFDC,
+	XK_R11           = 0xFFDC,
+	XK_F32           = 0xFFDD,
+	XK_R12           = 0xFFDD,
+	XK_F33           = 0xFFDE,
+	XK_R13           = 0xFFDE,
+	XK_F34           = 0xFFDF,
+	XK_R14           = 0xFFDF,
+	XK_F35           = 0xFFE0,
+	XK_R15           = 0xFFE0,
+
+	XK_Shift_L       = 0xFFE1,
+	XK_Shift_R       = 0xFFE2,
+	XK_Control_L     = 0xFFE3,
+	XK_Control_R     = 0xFFE4,
+	XK_Caps_Lock     = 0xFFE5,
+	XK_Shift_Lock    = 0xFFE6,
+
+	XK_Meta_L        = 0xFFE7,
+	XK_Meta_R        = 0xFFE8,
+	XK_Alt_L         = 0xFFE9,
+	XK_Alt_R         = 0xFFEA,
+	XK_Super_L       = 0xFFEB,
+	XK_Super_R       = 0xFFEC,
+	XK_Hyper_L       = 0xFFED,
+	XK_Hyper_R       = 0xFFEE,
+
+	XK_space         = 0x020,
+	XK_exclam        = 0x021,
+	XK_quotedbl      = 0x022,
+	XK_numbersign    = 0x023,
+	XK_dollar        = 0x024,
+	XK_percent       = 0x025,
+	XK_ampersand     = 0x026,
+	XK_apostrophe    = 0x027,
+	XK_quoteright    = 0x027,
+	XK_parenleft     = 0x028,
+	XK_parenright    = 0x029,
+	XK_asterisk      = 0x02A,
+	XK_plus          = 0x02B,
+	XK_comma         = 0x02C,
+	XK_minus         = 0x02D,
+	XK_period        = 0x02E,
+	XK_slash         = 0x02F,
+	XK_0             = 0x030,
+	XK_1             = 0x031,
+	XK_2             = 0x032,
+	XK_3             = 0x033,
+	XK_4             = 0x034,
+	XK_5             = 0x035,
+	XK_6             = 0x036,
+	XK_7             = 0x037,
+	XK_8             = 0x038,
+	XK_9             = 0x039,
+	XK_colon         = 0x03A,
+	XK_semicolon     = 0x03B,
+	XK_less          = 0x03C,
+	XK_equal         = 0x03D,
+	XK_greater       = 0x03E,
+	XK_question      = 0x03F,
+	XK_at            = 0x040,
+	XK_A             = 0x041,
+	XK_B             = 0x042,
+	XK_C             = 0x043,
+	XK_D             = 0x044,
+	XK_E             = 0x045,
+	XK_F             = 0x046,
+	XK_G             = 0x047,
+	XK_H             = 0x048,
+	XK_I             = 0x049,
+	XK_J             = 0x04A,
+	XK_K             = 0x04B,
+	XK_L             = 0x04C,
+	XK_M             = 0x04D,
+	XK_N             = 0x04E,
+	XK_O             = 0x04F,
+	XK_P             = 0x050,
+	XK_Q             = 0x051,
+	XK_R             = 0x052,
+	XK_S             = 0x053,
+	XK_T             = 0x054,
+	XK_U             = 0x055,
+	XK_V             = 0x056,
+	XK_W             = 0x057,
+	XK_X             = 0x058,
+	XK_Y             = 0x059,
+	XK_Z             = 0x05A,
+	XK_bracketleft   = 0x05B,
+	XK_backslash     = 0x05C,
+	XK_bracketright  = 0x05D,
+	XK_asciicircum   = 0x05E,
+	XK_underscore    = 0x05F,
+	XK_grave         = 0x060,
+	XK_quoteleft     = 0x060,
+	XK_a             = 0x061,
+	XK_b             = 0x062,
+	XK_c             = 0x063,
+	XK_d             = 0x064,
+	XK_e             = 0x065,
+	XK_f             = 0x066,
+	XK_g             = 0x067,
+	XK_h             = 0x068,
+	XK_i             = 0x069,
+	XK_j             = 0x06A,
+	XK_k             = 0x06B,
+	XK_l             = 0x06C,
+	XK_m             = 0x06D,
+	XK_n             = 0x06E,
+	XK_o             = 0x06F,
+	XK_p             = 0x070,
+	XK_q             = 0x071,
+	XK_r             = 0x072,
+	XK_s             = 0x073,
+	XK_t             = 0x074,
+	XK_u             = 0x075,
+	XK_v             = 0x076,
+	XK_w             = 0x077,
+	XK_x             = 0x078,
+	XK_y             = 0x079,
+	XK_z             = 0x07A,
+	XK_braceleft     = 0x07B,
+	XK_bar           = 0x07C,
+	XK_braceright    = 0x07D,
+	XK_asciitilde    = 0x07E
+}
+//}}}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/c/xmu.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,87 @@
+module dynamin.c.xmu;
+
+/*
+ * A binding to at least the part of Xmu that Dynamin uses. This
+ * binding is incomplete as it is made only for Dynamin's use.
+ */
+
+import dynamin.c.xlib;
+import dynamin.c.x_types;
+
+version(build) { pragma(link, Xmu); }
+
+extern(C):
+
+/***************************** Atoms.h *****************************/
+alias void* AtomPtr;
+
+extern AtomPtr
+	_XA_ATOM_PAIR,
+	_XA_CHARACTER_POSITION,
+	_XA_CLASS,
+	_XA_CLIENT_WINDOW,
+	_XA_CLIPBOARD,
+	_XA_COMPOUND_TEXT,
+	_XA_DECNET_ADDRESS,
+	_XA_DELETE,
+	_XA_FILENAME,
+	_XA_HOSTNAME,
+	_XA_IP_ADDRESS,
+	_XA_LENGTH,
+	_XA_LIST_LENGTH,
+	_XA_NAME,
+	_XA_NET_ADDRESS,
+	_XA_NULL,
+	_XA_OWNER_OS,
+	_XA_SPAN,
+	_XA_TARGETS,
+	_XA_TEXT,
+	_XA_TIMESTAMP,
+	_XA_USER,
+	_XA_UTF8_STRING;
+
+Atom XA_ATOM_PAIR(Display* d)      { return XmuInternAtom(d, _XA_ATOM_PAIR); }
+Atom XA_CHARACTER_POSITION(Display* d) {
+	return XmuInternAtom(d, _XA_CHARACTER_POSITION);
+}
+Atom XA_CLASS(Display* d)          { return XmuInternAtom(d, _XA_CLASS); }
+Atom XA_CLIENT_WINDOW(Display* d)  {
+	return XmuInternAtom(d, _XA_CLIENT_WINDOW);
+}
+Atom XA_CLIPBOARD(Display* d)      { return XmuInternAtom(d, _XA_CLIPBOARD); }
+Atom XA_COMPOUND_TEXT(Display* d)  {
+	return XmuInternAtom(d, _XA_COMPOUND_TEXT);
+}
+Atom XA_DECNET_ADDRESS(Display* d) {
+	return XmuInternAtom(d, _XA_DECNET_ADDRESS);
+}
+Atom XA_DELETE(Display* d)         { return XmuInternAtom(d, _XA_DELETE); }
+Atom XA_FILENAME(Display* d)       { return XmuInternAtom(d, _XA_FILENAME); }
+Atom XA_HOSTNAME(Display* d)       { return XmuInternAtom(d, _XA_HOSTNAME); }
+Atom XA_IP_ADDRESS(Display* d)     { return XmuInternAtom(d, _XA_IP_ADDRESS); }
+Atom XA_LENGTH(Display* d)         { return XmuInternAtom(d, _XA_LENGTH); }
+Atom XA_LIST_LENGTH(Display* d)    { return XmuInternAtom(d, _XA_LIST_LENGTH); }
+Atom XA_NAME(Display* d)           { return XmuInternAtom(d, _XA_NAME); }
+Atom XA_NET_ADDRESS(Display* d)    { return XmuInternAtom(d, _XA_NET_ADDRESS); }
+Atom XA_NULL(Display* d)           { return XmuInternAtom(d, _XA_NULL); }
+Atom XA_OWNER_OS(Display* d)       { return XmuInternAtom(d, _XA_OWNER_OS); }
+Atom XA_SPAN(Display* d)           { return XmuInternAtom(d, _XA_SPAN); }
+Atom XA_TARGETS(Display* d)        { return XmuInternAtom(d, _XA_TARGETS); }
+Atom XA_TEXT(Display* d)           { return XmuInternAtom(d, _XA_TEXT); }
+Atom XA_TIMESTAMP(Display* d)      { return XmuInternAtom(d, _XA_TIMESTAMP); }
+Atom XA_USER(Display* d)           { return XmuInternAtom(d, _XA_USER); }
+Atom XA_UTF8_STRING(Display* d)    { return XmuInternAtom(d, _XA_UTF8_STRING); }
+
+char* XmuGetAtomName(Display* dpy, Atom atom);
+
+Atom XmuInternAtom(Display* dpy, AtomPtr atom_ptr);
+
+//void XmuInternStrings(
+//	Display* dpy, String* names, Cardinal count, Atom* atoms_return);
+
+AtomPtr XmuMakeAtom(char* name);
+
+char* XmuNameOfAtom(AtomPtr atom_ptr);
+
+/*******************************************************************/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core/all.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,38 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.core.all;
+
+public import dynamin.core.benchmark;
+public import dynamin.core.console;
+public import dynamin.core.environment;
+public import dynamin.core.event;
+public import dynamin.core.file;
+public import dynamin.core.global;
+public import dynamin.core.list;
+public import dynamin.core.math;
+public import dynamin.core.settings;
+public import dynamin.core.string;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core/animated_value.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,92 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.core.animated_value;
+
+/// AnimatedValue!(int)
+alias AnimatedValue!(int) AnimatedInt;
+/// AnimatedValue!(float)
+alias AnimatedValue!(float) AnimatedFloat;
+/// AnimatedValue!(double)
+alias AnimatedValue!(double) AnimatedDouble;
+/// AnimatedValue!(real)
+alias AnimatedValue!(real) AnimatedReal;
+
+// TODO: change to a struct?
+/**
+ * Holds a value that changes over time. The value can be changed by calling
+ * animate(), then calling advance() with an amount of time.
+ * Example:
+ * -----
+ * AnimatedInt x = new AnimatedInt;
+ * x.set(100);   /+ start the animation at 100 +/
+ * x.animate(900, 1000);   /+ animate to 900, over a period of 1000 ms +/
+ * x.advance(250);   /+ advance 250 ms, one-fourth of the way to 1000 +/
+ * x.get(); /+ returns 300, one-fourth of the way between 100 and 900 +/
+ * x.advance(2000); /+ advance past the end of the animation +/
+ * x.get(); /+ returns 900 +/
+ * -----
+ */
+class AnimatedValue(T) {
+	T _value = 0;
+	T _startValue, _endValue;
+	int _elapsed, _duration;
+	bool _animating;
+	T get() {
+		return _value;
+	}
+	T end() {
+		return _endValue;
+	}
+	AnimatedValue set(T newVal) {
+		_value = newVal;
+		_endValue = _value;
+		_animating = false;
+		return this;
+	}
+	void animate(T endVal, int dur) {
+		_animating = true;
+		_elapsed = 0;
+		_duration = dur;
+		_startValue = _value;
+		_endValue = endVal;
+	}
+	bool animating() { return _animating; }
+	void advance(int time) {
+		_elapsed += time;
+		if(!_animating)
+			return;
+		if(_elapsed > _duration)
+			set(_endValue);
+		else
+			_value = _startValue+(_endValue-_startValue)*_elapsed/_duration;
+	}
+	int elapsed() {
+		return _elapsed;
+	}
+	int duration() {
+		return _duration;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core/benchmark.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,88 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.core.benchmark;
+
+import dynamin.all_core;
+import dynamin.core.string;
+import tango.io.Stdout;
+
+/**
+ * Returns: The average number of milliseconds one call of the specified
+ *          delegate took.
+ */
+real benchmark(int repetitions, void delegate() dg) { // use static opCall()?
+	long time = Environment.runningTime;
+	for(int i = 0; i < repetitions; ++i)
+		dg();
+	return (Environment.runningTime-time)/cast(real)repetitions;
+}
+real benchmark(void delegate() dg) {
+	return benchmark(1, dg);
+}
+/**
+ * name can be null
+ */
+real benchmarkAndWrite(string name, int repetitions, void delegate() dg) {
+	real time = benchmark(repetitions, dg);
+	Stdout.format("{} took {:.2}ms.", name, time).newline; // TODO: verify :.2
+	return time;
+}
+real benchmarkAndWrite(string name, void delegate() dg) {
+	return benchmarkAndWrite(name, 1, dg);
+}
+
+/**
+ * As the constructor calls the Start() method, the only time one would need
+ * to is when reusing a Benchmark object.
+ */
+class Benchmark {
+	long _startTime;
+	this() {
+		start();
+	}
+	void start() {
+		_startTime = Environment.runningTime;
+	}
+	long time() {
+		return _startTime-Environment.runningTime;
+	}
+	void writeTime(string opName) {
+		if(opName is null)
+			opName = "Benchmark";
+		Stdout.format("{} took {}ms.", opName, time).newline;
+	}
+
+	/**
+	 * calls the specified delegate the specified number of times and
+	 * returns the average time one call took
+	 */
+	static long measure(int times, void delegate() d) {
+		long time = Environment.runningTime;
+		for(int i = 0; i < times; ++i)
+			d();
+		return (Environment.runningTime-time)/times;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core/console.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,263 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.core.console;
+
+import dynamin.core.string;
+import dynamin.core_backend;
+import tango.io.Stdout;
+import tango.io.Console;
+
+///
+enum ConsoleColor {
+// Black, Silver, Maroon, DarkBlue, Green,      Purple, DarkYellow, Teal,
+// Gray,  White,  Red,    Blue,     LightGreen, Pink,   Yellow,     Cyan
+	Black,      ///
+	Silver,     ///
+	Maroon,     ///
+	DarkBlue,   ///
+	Green,      ///
+	Purple,     ///
+	DarkYellow, ///
+	Teal,       ///
+	Gray,       ///
+	White,      ///
+	Red,        ///
+    Blue,       ///
+	LightGreen, ///
+	Pink,       ///
+	Yellow,     ///
+	Cyan        ///
+}
+
+/**
+ * This class allows programs to read from and write to the console.
+ *
+ * Programs can also take advantage of some more advanced console features,
+ * such as reading input without echoing it to the screen, clearing the screen,
+ * setting text foreground and background color, and setting text style.
+ * However, the more advanced features will not be available if the program
+ * is not actually writing to a console window, such as if its output has be
+ * redirected to a file or if another program is receiving its output.
+ * The only functions that will work corectly when output or input have been
+ * redirected are the following:
+ * $(UL
+	 $(LI Write())
+	 $(LI WriteLine())
+	 $(LI ReadLine())
+ * )
+ * On Windows, when writing to a console window, all functionality is
+ * available except Bold, Italic, Underline, Strikethrough. Windows does
+ * not support these.
+ *
+ * On other systems, colors and styles are supported by writing
+ * out control codes.
+ */
+class Console {
+static:
+private:
+	mixin ConsoleBackend;
+public:
+//required features:
+//- Easily reading a line at once...input is returned when user presses ENTER
+//- Easily reading a key press from the user...input is returned as soon as it is available
+//- Allow for typing a password and replacing the characters entered with *
+//   considering this would actually be fairly hard, I may not implement it...
+//   Instead could do as linux and not show anything...
+//wished features:
+//- Getting the screen size
+//- Turning off echoing the key press that the user types when returning
+//  input without ENTER
+//- Writing a string, such as "23%", and then changing it to like "24%".
+//  Console.WriteBuffer(40) writes 40 spaces and returns a ConsoleBuffer object
+	/**
+	 * Sets whether or not writing to the standard output will be
+	 * buffered. By default, this is false, meaning that no buffering will
+	 * ever be done. Setting this to true will buffer output when the standard
+	 * output is going to a file or stream, such as a text editor, but will
+	 * not buffer output to a console window.
+	 * TODO: not implemented on Windows
+	 */
+	void buffered(bool b) {
+		backend_buffered(b);
+	}
+	/**
+	 * Writes the specified text string to the console.
+	 */
+	void write(string s, ...) {
+		Stdout.layout.convert(&Stdout.emit, _arguments, _argptr, s);
+	}
+	/**
+	 * Writes the specified text string to the console, followed by a newline.
+	 */
+	void writeLine() { Stdout.newline; }
+	/// ditto
+	void writeLine(string s, ...) {
+		Stdout.layout.convert(&Stdout.emit, _arguments, _argptr, s);
+		Stdout.newline;
+	}
+	/**
+	 * Reads a line of text from the console. The returned returned string
+	 * will end in a newline, unless it was read from the last line in a text
+	 * file.
+	 */
+	string readLineRaw() { return Cin.copyln(true); }
+	/**
+	 * Reads a line of text from the console. The returned string does not
+	 * contain a newline.
+	 */
+	string readLine() { return Cin.copyln(false); }
+	/// ditto
+	string readLine(string prompt, ...) {
+		Stdout.layout.convert(&Stdout.emit, _arguments, _argptr, prompt);
+		return readLine();
+	}
+	/**
+	 * reads a character, echoing it to the screen
+	 * TODO: not implemented
+	 */
+	string read() { return backend_read(); }
+	/// ditto
+	string read(string prompt) {
+		write(prompt);
+		return backend_read();
+	}
+	/**
+	 * reads a line without showing that line
+	 * TODO: not implemented
+	 */
+	string readLineHidden() { return backend_readLineHidden(); }
+	/// ditto
+	string readLineHidden(string prompt) {
+		write(prompt);
+		return backend_readLineHidden();
+	}
+	/**
+	 * reads a character without showing it
+	 * TODO: not implemented
+	 */
+	string readHidden() { return backend_readHidden(); }
+	/// ditto
+	string readHidden(string prompt) {
+		write(prompt);
+		return backend_readHidden();
+	}
+	/**
+	 * Clears the text that has been written to the console.
+	 */
+	void clear() {
+		backend_clear();
+	}
+	/**
+	 * Sets the foreground color of text written to the console.
+	 */
+	void foreColor(ConsoleColor color) {
+		backend_foreColor = color;
+	}
+	/**
+	 * Sets the background color of text written to the console.
+	 */
+	void backColor(ConsoleColor color) {
+		backend_backColor = color;
+	}
+	/**
+	 * Resets the foreground and background colors of text written to the
+	 * console to the defaults.
+	 */
+	void resetColors() {
+		backend_resetColors();
+	}
+	/**
+	 * Sets whether text written to the console is bold.
+	 * Has no effect on Windows.
+	 */
+	void bold(bool b) {
+		backend_bold = b;
+	}
+	/**
+	 * Sets whether text written to the console is italic.
+	 * Has no effect on Windows.
+	 */
+	void italic(bool b) {
+		backend_italic = b;
+	}
+	/**
+	 * Sets whether text written to the console is underlined.
+	 * Has no effect on Windows.
+	 */
+	void underline(bool b) {
+		backend_underline = b;
+	}
+	/**
+	 * Sets whether text written to the console is strikethrough.
+	 * Has no effect on Windows.
+	 */
+	void strikethrough(bool b) {
+		backend_strikethrough = b;
+	}
+}
+/*  "\x1b[9;31mThis is red and strikethrough\x1b[0m"
+*/
+
+///
+alias Console.readLine readLine;
+///
+alias Console.writeLine writeLine;
+
+/* unittest {
+	Console.foreColor = ConsoleColor.Black;
+	writeLine("Black");
+	Console.foreColor = ConsoleColor.Gray;
+	writeLine("Gray");
+	Console.foreColor = ConsoleColor.Silver;
+	writeLine("Silver");
+	Console.foreColor = ConsoleColor.Red;
+	writeLine("Red");
+	Console.foreColor = ConsoleColor.Blue;
+	writeLine("Blue");
+	Console.foreColor = ConsoleColor.LightGreen;
+	writeLine("LightGreen");
+	Console.foreColor = ConsoleColor.Green;
+	writeLine("Green");
+	Console.foreColor = ConsoleColor.Teal;
+	writeLine("Teal");
+	Console.foreColor = ConsoleColor.Yellow;
+	writeLine("Yellow");
+	Console.foreColor = ConsoleColor.Purple;
+	writeLine("Purple");
+	Console.foreColor = ConsoleColor.Pink;
+	writeLine("Pink");
+	Console.foreColor = ConsoleColor.DarkYellow;
+	writeLine("DarkYellow");
+	Console.foreColor = ConsoleColor.Maroon;
+	writeLine("Maroon");
+	Console.foreColor = ConsoleColor.DarkBlue;
+	writeLine("DarkBlue");
+	Console.foreColor = ConsoleColor.Cyan;
+	writeLine("Cyan");
+	Console.foreColor = ConsoleColor.White;
+	writeLine("White");
+} */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core/environment.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,132 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.core.environment;
+
+import dynamin.all_core;
+import dynamin.core_backend;
+
+/**
+ * Contains static methods to access information about the computer the
+ * application is running on.
+ */
+static class Environment {
+static:
+private:
+	mixin EnvironmentBackend;
+public:
+	/**
+	 * Returns the time in milliseconds since the program was started.
+	 * On Windows XP, this time is updated every millisecond.
+	 * On Linux, this time is usually updated every millisecond, but
+	 * occasionally may take 5 to 10 milliseconds.
+	 * This is the author's dream time function because
+	 *
+	 * $(OL
+	 * $(LI It is accurate to 1 millisecond.)
+	 * $(LI It works correctly on multiple core computers.)
+	 * $(LI It is unaffected by changes to the system time.)
+	 * $(LI It never wraps to zero.)
+	 * )
+	 *
+	 * On my 1.3 GHz celeron, this function can be called about 480 times
+	 * in one millisecond under Windows and about 380 times in one millisecond
+	 * under Linux.
+	 *
+	 * TODO: make sure it works with multiple cores, although I'm sure it does
+	 */
+	long runningTime() {
+		return backend_runningTime;
+	}
+	/**
+	 * Returns the system time in milliseconds since January 1, 1970 UTC.
+	 * On Windows XP, this time is only updated every 15.625 milliseconds.
+	 *
+	 * On my 1.3 GHz celeron, this function can be called about 12,000 times
+	 * in one millisecond under Windows and about 460 times in one millisecond
+	 * under Linux.
+	 */
+	long systemTime() {
+		return backend_systemTime;
+	}
+	/**
+	 * Gets the number of logical processors on this computer. A logical
+	 * processor can either be a different physical processor or simply
+	 * another core in the same processor. Even a single core hyper-threaded
+	 * processor is considered to have two logical processors.
+	 * Returns: the number of logical processors
+	 */
+	int processorCount() {
+		return backend_processorCount;
+	}
+	/**
+	 * The number returned by this method can be used to measure the
+	 * time between two calls. This method uses the highest resolution
+	 * timer available.
+	 *
+	 * On my 1.3 GHz celeron, this function can be called about 500 times
+	 * in one millisecond under Windows.
+	 *
+	 * Returns: the current time in milliseconds
+	 *
+	 * Note: Under Windows, this is implemented using QueryPerformanceCounter().
+	 * QueryPerformanceCounter() gets the time counter from the processor.
+	 * On processors with multiple cores (such as an Althon X2 or a Core 2 Duo),
+	 * the time counter for each core may be a few milliseconds different.
+	 * (Microsoft's documentation says this is due to bugs in the BIOS or HAL.)
+	 * Since QueryPerformanceCounter() can get the time from either core,
+	 * the time between two calls made within the same millisecond can be off.
+	 * For example, on my Althon X2 computer, the difference between cores
+	 * is usually 60 ms. If two calls to QueryPerformanceCounter() are made
+	 * in the same millisecond, there is a possiblity that the second one
+	 * will return a time 60 ms smaller than the first.
+	 * Under Linux, this is implemented using gettimeofday(), which has no
+	 * problems with multiple cores and is accurate.
+	 * One way to fix this inaccuracy is by only allowing the thread to
+	 * use one processor. Another problem is that this time will run slightly
+	 * faster or slower than the system time.
+	 */
+	private long processorTime() {
+		return backend_processorTime;
+	}
+}
+
+unittest {
+	auto startTime = Environment.runningTime;
+	assert(startTime > 0);
+	auto time = startTime;
+	const SAMPLE = 50;
+	// makes sure that RunningTime does not go backwards
+	for(int i = 0; i < SAMPLE;) {
+		auto time2 = Environment.runningTime;
+		assert(time2 >= time);
+		if(time2 > time) {
+			time = time2;
+			++i;
+		}
+	}
+	//printf("avg accuracy: %.1f ms\n", (time-startTime)/cast(float)SAMPLE);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core/event.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,243 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.core.event;
+
+import tango.io.Stdout;
+import dynamin.core.global;
+import tango.core.Exception;
+import tango.core.Traits;
+
+// TODO: mixin Event!(WhenMoved) Moved;
+// TODO: mixin Event!(WhenMoved, DispatchMoved) Moved;
+
+/**
+ * A class used to notify handlers of an event. It is similar to .NET's events.
+ * Here is an example of its usage:
+ * -----
+ * class Control {
+ * public {
+ *     Event!(PaintingEventArgs) painting;
+ *     protected void whenPainting(PaintingEventArgs e) {
+ *         /+ painting code goes here +/
+ *     }
+ *     this() {
+ *         painting = new Event!(PaintingEventArgs)(&whenPainting);
+ *     }
+ * }
+ * }
+ * -----
+ * Then to add a handler to it:
+ * -----
+ * Control control = new Control();
+ * control.painting += (PaintingEventArgs e) {
+ *     /+ painting code goes here +/
+ * };
+ * -----
+ * And fire it in the Control like this:
+ * -----
+ * painting(new PaintingEventArgs());
+ * -----
+ * Not as easy to use as I would like, but still much better than Java's
+ * event handling.
+ * When the event is fired, all the handlers are called first, followed
+ * by the delegate passed into the constructor.
+ */
+class Event(ArgsT = EventArgs) {
+protected:
+	/// void delegate(ArgsT e)
+	public alias void delegate(ArgsT e) EventHandler;
+	/// void delegate(ArgsT e)
+	public alias void delegate(ArgsT e) EventDispatcher;
+
+	EventHandler[] handlers;
+	EventHandler mainHandler;
+	EventDispatcher dispatcher;
+public:
+
+	/**
+	 * Creates an event object. mainHandler is called after all
+	 * the other handlers.
+	 */
+	this(EventHandler mainHandler) {
+		this.mainHandler = mainHandler;
+		dispatcher = &defaultDispatch;
+	}
+	/**
+	 * Creates an event object. mainHandler is called after all
+	 * the other handlers.
+	 */
+	this(EventHandler mainHandler, EventDispatcher dispatcher) {
+		this.mainHandler = mainHandler;
+		if(!dispatcher.funcptr) throw new Exception("dispatcher cannot be null");
+		this.dispatcher = dispatcher;
+	}
+	/**
+	 * Calls all the handlers added to this event, passing e to them.
+	 */
+	void opCall(ArgsT e) {
+		if(e is null)
+			Stdout("Warning: EventArgs null").newline;
+		dispatcher(e);
+	}
+	/**
+	 * Adds handler to this event. handler will be called when the event is fired.
+	 */
+	void opAddAssign(EventHandler handler) {
+		if(!handler.funcptr)
+			throw new IllegalArgumentException("handler is null");
+		handlers.length = handlers.length + 1;
+		handlers[length-1] = handler;
+		// TODO: use a list?
+		//handlers.Add(handler);
+	}
+	/// ditto
+	void opAddAssign(void delegate() handler) {
+		struct Foo {
+			void delegate() handler;
+			void wrapper(ArgsT e) { handler(); }
+		}
+		Foo* f = new Foo;
+		f.handler = handler;
+		this += &f.wrapper;
+		// I really wish D could do this:
+		//this += (ArgsT e) { handler(); };
+	}
+	/// TODO: implement this method
+	void opSubAssign(EventHandler handler) {
+		throw new Exception("Removing handlers not yet implemented");
+	}
+	/**
+	 * Calls the handlers (not including the main handler) added to this event.
+	 * Only use this method from a method that does custom dispatching.
+	 */
+	void callHandlers(ArgsT e) {
+		foreach(handler; handlers)
+			handler(e);
+	}
+	/**
+	 * Calls the main handler unless the StopEventArgs.stopped has been
+	 * set to true.
+	 * Only use this method from a method that does custom dispatching.
+	 */
+	void callMainHandler(ArgsT e) {
+		auto stopEventArgs = cast(StopEventArgs)e;
+		// if e is an instance of StopEventArgs, then check if it is canceled
+		if(stopEventArgs is null || !stopEventArgs.stopped)
+			mainHandler(e);
+	}
+	/**
+	 *
+	 */
+	void defaultDispatch(ArgsT e) {
+		callHandlers(e);
+		callMainHandler(e);
+	}
+}
+
+
+public import tango.core.Traits;
+template Event2(alias mainHandler) {
+protected:
+	alias ParameterTupleOf!(mainHandler)[0] ArgsType;
+	/// void delegate(ArgsType e)
+	public alias void delegate(ArgsType e) Handler;
+	/// void delegate(ArgsType e)
+	public alias void delegate(ArgsType e) Dispatcher;
+
+	// TODO: use a list-like struct?
+	Handler[] handlers;
+public:
+
+	/**
+	 * Calls all the handlers added to this event, passing e to them.
+	 */
+	void opCall(ArgsType e) {
+		if(e is null)
+			Stdout("Warning: EventArgs null").newline;
+		callHandlers(e);
+		callMainHandler(e);
+	}
+	/**
+	 * Adds the specified handler to this event. The handler will be called
+	 * when the event is fired.
+	 */
+	void opAddAssign(Handler handler) {
+		if(!handler.funcptr) throw new Exception("handler cannot be null");
+		handlers.length = handlers.length + 1;
+		handlers[length-1] = handler;
+		// TODO: use a list?
+		//handlers.Add(handler);
+	}
+	/// ditto
+	void opAddAssign(void delegate() handler) {
+		struct Foo {
+			void delegate() handler;
+			void wrapper(ArgsType e) { handler(); }
+		}
+		Foo* f = new Foo;
+		f.handler = handler;
+		this += &f.wrapper;
+		// I really wish D could do this:
+		//this += (ArgsType e) { handler(); };
+	}
+	/// TODO: implement this method
+	void opSubAssign(Handler handler) {
+		throw new Exception("removing handlers not yet implemented");
+	}
+	/**
+	 * Calls the handlers (not including the main handler) added to this event.
+	 * Only use this method from a method that does custom dispatching.
+	 */
+	void callHandlers(ArgsType e) {
+		foreach(handler; handlers)
+			handler(e);
+	}
+	/**
+	 * Calls the main handler unless the StopEventArgs.Stopped has been
+	 * set to true.
+	 * Only use this method from a method that does custom dispatching.
+	 */
+	void callMainHandler(ArgsType e) {
+		auto stopEventArgs = cast(StopEventArgs)e;
+		// if e is an instance of StopEventArgs, then check if it is stopped
+		if(stopEventArgs is null || !stopEventArgs.stopped)
+			mainHandler(e);
+	}
+}
+
+/// The base class for passing arguments to event handlers.
+// TODO: shorter name?
+class EventArgs {
+}
+class StopEventArgs : EventArgs {
+	/**
+	 * If Stopped is set to true, then the Control will not respond to
+	 * the event. Like if a key is typed while a text box is focused,
+	 * but a handler sets Stopped to true, the text box will not
+	 * respond to the key.
+	 */
+	bool stopped = false;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core/file.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,41 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.core.file;
+
+import dynamin.core.string;
+import tango.io.device.File;
+import tango.io.UnicodeFile;
+
+ubyte[] readFileBytes(string file) {
+	return cast(ubyte[])File.get(file);
+	//scope f = new File(file);
+	//return cast(ubyte[])f.read();
+}
+string readFileText(string file) {
+	scope f = new UnicodeFile!(char)(file, Encoding.UTF_8);
+	return f.read();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core/global.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,328 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.core.global;
+
+import dynamin.core.string;
+import tango.math.Math;
+import tango.io.model.IFile;
+import tango.core.Exception;
+
+public import tango.util.Convert;
+
+static if((void*).sizeof == 4) {
+	/**
+	 * Defined as an int on 32-bit platforms and as a long on 64-bit platforms.
+	 */
+	alias int word;
+	/**
+	 * Defined as a uint on 32-bit platforms and
+	 * as a ulong on 64-bit platforms.
+	 */
+	alias uint uword;
+} else static if((void*).sizeof == 8) {
+	/**
+	 * Defined as an int on 32-bit platforms and as a long on 64-bit platforms.
+	 */
+	alias long word;
+	/**
+	 * Defined as a uint on 32-bit platforms and
+	 * as a ulong on 64-bit platforms.
+	 */
+	alias ulong uword;
+}
+
+/**
+ * The string used to separate lines.
+ * This is "\r\n" under Windows and "\n" under Linux.
+ */
+const string LineSeparator = FileConst.NewlineString;
+/**
+ * The string used to separate directories in a path.
+ * This is "\\" under Windows and "/" under Linux.
+ */
+const string DirSeparator = FileConst.PathSeparatorString;
+///
+const char DirSeparatorChar = FileConst.PathSeparatorChar;
+/**
+ * The string used to separate paths.
+ * This is ";" under Windows and ":" under Linux
+ */
+const string PathSeparator = FileConst.SystemPathString;
+///
+const char PathSeparatorChar = FileConst.SystemPathChar;
+
+/**
+ * Tests whether num1 and num2 are equal. They are considered equal
+ * if the difference between them is less than epsilon.
+ * Examples:
+ * -----
+ * floatsEqual(3.14, 3.2, 0.1) == true
+ * floatsEqual(3.14, 3.3, 0.1) == false
+ * floatsEqual(3.14, 3.151, 0.01) == false
+ * -----
+ */
+bool floatsEqual(real num1, real num2, real epsilon) {
+	return abs(num1 - num2) <= epsilon;
+}
+unittest {
+	assert(floatsEqual(3.14, 3.2, 0.1) == true);
+	assert(floatsEqual(3.14, 3.3, 0.1) == false);
+	assert(floatsEqual(3.14, 3.151, 0.01) == false);
+}
+
+/**
+ * Copies length elements starting at srcStart in srcData to destStart
+ * in destData. Data is copied as if srcData and destData are two separate
+ * arrays, even if they are the same.
+ */
+void arrayCopy(T)(T[] srcData, uint srcStart, T[] destData, uint destStart, uint length) {
+	if((srcData is destData && srcStart == destStart) || length == 0)
+		return;
+	if(srcStart > destStart) {
+		//copy forward
+		for(int i = 0; i < length; ++i)
+		  destData[destStart + i] = srcData[srcStart + i];
+	} else {
+		//copy reverse
+		for(int i = length-1; i >= 0; --i)
+			destData[destStart + i] = srcData[srcStart + i];
+	}
+}
+unittest {
+	char[] c = "Computer".dup;
+	arrayCopy!(char)(c, 3, c, 2, 4);
+	assert(c == "Coputeer");
+	c = "Computer".dup;
+	arrayCopy!(char)(c, 2, c, 3, 4);
+	assert(c == "Commputr");
+	c = "hi".dup;
+	arrayCopy!(char)(c, 1, c, 0, 1);
+	assert(c == "ii");
+}
+
+/**
+ * Sets every byte of the specified memory block to value.
+ */
+void memoryFill(void* mem, uword count, ubyte value) {
+	ubyte* memB = cast(ubyte*)mem;
+	while(count != 0) {
+		*memB++ = value;
+		--count;
+	}
+}
+unittest {
+	char[] buff = "jEdit".dup;
+	memoryFill(buff.ptr+1, 3, 0x23);
+	assert(buff == "j\x23\x23\x23t");
+}
+
+/**
+ * Sets every byte of the specified memory block to zero.
+ */
+void memoryZero(void* mem, uword count) {
+	memoryFill(mem, count, 0);
+}
+unittest {
+	char[] buff = "jEdit".dup;
+	memoryZero(buff.ptr+1, 3);
+	assert(buff == "j\0\0\0t");
+}
+
+/**
+ * Copies the specified number of bytes from srcMem to destMem. The source
+ * and destination should not overlap, or the results will be undefined.
+ * Note that the source and destination parameters are opposite in
+ * order from the C function memcpy(). If count is a multiple of the
+ * native pointer size, the copy will be done in blocks of that size.
+ */
+void memoryCopy(void* srcMem, void* destMem, uword count) {
+	// copy in blocks of the pointer size, if possible
+	if(count % word.sizeof == 0) {
+		count /= word.sizeof;
+		uword* src = cast(uword*)srcMem;
+		uword* dest = cast(uword*)destMem;
+		while(count != 0) {
+			*dest++ = *src++;
+			--count;
+		}
+	} else {
+		ubyte* src = cast(ubyte*)srcMem;
+		ubyte* dest = cast(ubyte*)destMem;
+		while(count != 0) {
+			*dest++ = *src++;
+			--count;
+		}
+	}
+}
+unittest {
+	char[] buff = "Hello".dup;
+	memoryCopy(buff.ptr, buff.ptr+3, 2);
+	assert(buff == "HelHe");
+	buff = "Longer text here".dup;
+	memoryCopy(buff.ptr+7, buff.ptr+12, 4);
+	assert(buff == "Longer text text");
+}
+
+/**
+ * Converts a number into its roman numeral form. The number must
+ * be between 0 and 3,999, inclusive.
+ * Examples:
+ * -----
+ * toRomanNumerals(2) == "II"
+ * toRomanNumerals(58) == "LVIII"
+ * toRomanNumerals(194) == "CXCIV"
+ * -----
+ */
+string toRomanNumerals(int num) {
+	if(num > 3999 || num < 0)
+		throw new IllegalArgumentException("ToRomanNumerals():" ~
+			"highest convertable roman numeral is 3999");
+	auto combos = [new int[0], [0], [0,0], [0,0,0], [0,1],
+	[1], [1,0], [1,0,0], [1,0,0,0], [0,2]];
+	auto letters = ['I', 'V', 'X', 'L', 'C', 'D', 'M'];
+	string str = "";
+	int letterOffset = 0;
+	while(num > 0) {
+		foreach_reverse(int c; combos[num % 10])
+			str = letters[c+letterOffset] ~ str;
+		num /= 10;
+		letterOffset += 2;
+	}
+	return str;
+}
+unittest {
+	assert(toRomanNumerals(2) == "II");
+	assert(toRomanNumerals(58) == "LVIII");
+	assert(toRomanNumerals(194) == "CXCIV");
+
+	assert(toRomanNumerals(0) == "");
+	assert(toRomanNumerals(1) == "I");
+	assert(toRomanNumerals(10) == "X");
+	assert(toRomanNumerals(500) == "D");
+	assert(toRomanNumerals(18) == "XVIII");
+	assert(toRomanNumerals(3949) == "MMMCMXLIX");
+}
+
+int numeralToValue(char c) {
+	switch(c) {
+	case 'I': case 'i': return 1;
+	case 'V': case 'v': return 5;
+	case 'X': case 'x': return 10;
+	case 'L': case 'l': return 50;
+	case 'C': case 'c': return 100;
+	case 'D': case 'd': return 500;
+	case 'M': case 'm': return 1000;
+	default: return -1;
+	}
+}
+/**
+ * Parses the specified string of roman numerals and returns the value.
+ * The value must be less than or equal to 3,999. The string may be uppercase,
+ * lowercase, or a mixture of both cases.
+ * Examples:
+ * -----
+ * parseRomanNumerals("II") == 2
+ * parseRomanNumerals("LVIII") == 58
+ * parseRomanNumerals("CXCIV") == 194
+ * parseRomanNumerals("xxxxviiii") == 49
+ * -----
+ */
+int parseRomanNumerals(string str) {
+	int num = 0;
+	int largestSoFar = 1;
+	foreach_reverse(c; str) {
+		int value = numeralToValue(c);
+		if(value == -1)
+			throw new IllegalArgumentException("Invalid roman numeral: " ~ c);
+		if(value < largestSoFar) {
+			num -= value;
+		} else {
+			num += value;
+			largestSoFar = value;
+		}
+	}
+	if(num > 3999 || num < 0)
+		throw new IllegalArgumentException("ParseRomanNumerals():" ~
+			"highest convertable roman numeral is 3999");
+	return num;
+}
+unittest {
+	assert(parseRomanNumerals("II") == 2);
+	assert(parseRomanNumerals("LVIII") == 58);
+	assert(parseRomanNumerals("CXCIV") == 194);
+	assert(parseRomanNumerals("xxxxviiii") == 49);
+
+	assert(parseRomanNumerals("") == 0);
+	assert(parseRomanNumerals("I") == 1);
+	assert(parseRomanNumerals("X") == 10);
+	assert(parseRomanNumerals("D") == 500);
+	assert(parseRomanNumerals("XVIII") == 18);
+	assert(parseRomanNumerals("MMMCMXLIX") == 3949);
+}
+unittest {
+	for(int i = 0; i < 4000; ++i)
+		assert(toRomanNumerals(i).parseRomanNumerals() == i);
+}
+
+/**
+ * Converts a number of bytes into a human friendly string. The units
+ * supported are bytes, KB, MB, GB, TB, PB, EB, ZB, and YB.
+ * Examples:
+ * -----
+ * byteCountToString(202) == "202 bytes"
+ * byteCountToString(1021) == "1021 bytes"
+ * byteCountToString(106_496) == "104 KB"
+ * byteCountToString(620_705_792) == "591 MB"
+ * -----
+ */
+string byteCountToString(ulong num) {
+	const factor = 1024;
+	//kilo, mega, giga, tera, peta, exa, zetta, yotta
+	char[][] units = [
+	" bytes", " KB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB"];
+	uint unitIndex = 0;
+	ulong div = factor;
+	uint rem;
+	while(num > factor-1 && unitIndex < units.length-1) {
+		rem = num % factor;
+		num /= factor;
+		++unitIndex;
+	}
+	//rem/1024 equals the fraction of unit
+	string str = to!(string)(num);
+	if(str.length < 3) {
+		str ~= "." ~ to!(string)(rem*10/factor);
+	}
+	str ~= units[unitIndex];
+	return str;
+}
+unittest {
+	assert(byteCountToString(202) == "202 bytes");
+	assert(byteCountToString(1021) == "1021 bytes");
+	assert(byteCountToString(106_496) == "104 KB");
+	assert(byteCountToString(620_705_792) == "591 MB");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core/list.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,190 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.core.list;
+
+import dynamin.core.global;
+import dynamin.core.string;
+import dynamin.core.math;
+import tango.io.Stdout;
+
+// TODO: QuickSearch()
+// TODO: BinarySearch()
+//       MUST use (high + low) >>> 1 to find the average
+// TODO: Search()
+// TODO: QuickSort()
+// TODO: HeapSort()
+// TODO: Sort() - calls HeapSort() so stable sort is default
+// TODO: when D has template inheritance, have separate const_List and List
+class List(T) {
+protected:
+	T[] _data;
+	uint _count;
+	void delegate() whenChanged; // TODO: have an index and length...
+public:
+	this() {
+		this(16, {});
+	}
+	this(uint capacity) {
+		this(capacity, {});
+	}
+	this(void delegate() whenChanged) {
+		this(16, whenChanged);
+	}
+	this(uint capacity, void delegate() whenChanged) {
+		_data = new T[capacity];
+		this.whenChanged = whenChanged;
+	}
+	static List fromArray(T[] arr...) {
+		List list = new List!(T)();
+		list._data = arr.dup;
+		list._count = arr.length;
+		return list;
+	}
+	uint count() {
+		return _count;
+	}
+	uint capacity() {
+		return _data.length;
+	}
+	T[] toArray() {
+		return _data[0.._count].dup;
+	}
+	T[] data() {
+		return _data[0.._count];
+	}
+	/*string toString() {
+		string str = "[";
+		if(Count > 0)
+			str ~= ToString(this[0]);
+		foreach(item; this) {
+			str ~= ", ";
+			str ~= ToString(item);
+		}
+		str ~= "]";
+		return str;
+	}*/
+	protected void maybeEnlarge(uint neededCap) {
+		if(neededCap <= capacity)
+			return;
+		_data.length = max(neededCap, (capacity+1)*2);
+	}
+	T opIndex(uint index) {
+		return _data[0.._count][index];
+	}
+	void push(T item) {
+		add(item);
+	}
+	T pop() {
+		if(_count < 1)
+			throw new Exception("List.Pop() - List is empty");
+		T item = _data[_count-1];
+		// must null out to allow to be collected
+		static if(is(T == class) || is(T == interface))
+			_data[_count-1] = cast(T)null;
+		--_count;
+		whenChanged();
+		return item;
+	}
+	void add(T item) {
+		insert(_count, item);
+	}
+	// TODO: AddRange?
+	void remove(T item) {
+		uint i = find(item);
+		if(i == -1)
+			return;
+		removeRange(i);
+	}
+	void removeRange(uint index, uint length = 1) {
+		arrayCopy!(T)(_data, index+length, _data, index, _count - (index+length));
+		// must null out to allow to be collected
+		static if(is(T == class) || is(T == interface))
+			for(uint i = _count-length; i < _count; ++i)
+				_data[i] = cast(T)null;
+		_count -= length;
+		whenChanged();
+	}
+	void insert(uint index, T item) {
+		maybeEnlarge(_count+1);
+		arrayCopy!(T)(_data, index, _data, index+1, _count - index);
+		_data[index] = item;
+		++_count;
+		whenChanged();
+	}
+	// TODO: InsertRange?
+	void clear() {
+		// must null out to allow to be collected
+		static if(is(T == class) || is(T == interface))
+			for(uint i = 0; i < count; ++i)
+				data[i] = cast(T)null;
+		_count = 0;
+		whenChanged();
+	}
+	uint find(T item) {
+		foreach(i, item2; _data)
+			if(item == item2) // if(item2 == item) would crash on a null item
+				return i;
+		return -1;
+	}
+	//trimCapacity()
+	//opIndex
+	//opIndexAssign
+	//opConcat
+	//opEquals
+	//opSlice
+	//opSliceAssign
+	int opApply(int delegate(inout T item) dg) {
+		for(uint i = 0; i < _count; ++i) {
+			if(int result = dg(_data[i]))
+				return result;
+		}
+		return 0;
+	}
+	//so you can do:
+	//foreach(i, item; list)
+	int opApply(int delegate(inout uint index, inout T item) dg) {
+		for(uint i = 0; i < _count; ++i) {
+			if(int result = dg(i, _data[i]))
+				return result;
+		}
+		return 0;
+	}
+
+}
+unittest {
+	auto list = List!(char).fromArray("Hello, Mat");
+	list.add('t');
+	assert(list.data == "Hello, Matt");
+	assert(list.pop() == 't');
+	list.removeRange(1, 7);
+	assert(list.data == "Hat");
+	list.clear();
+	assert(list.data == "");
+	auto list2 = new List!(string);
+	list2.add("hello");
+	assert(list2.pop() == "hello");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core/math.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,35 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.core.math;
+
+public import tango.math.Math;
+
+///
+alias PI Pi; // about 3
+
+///
+bool isOdd(T)(T x) { return cast(bool)(x & 1); }
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core/settings.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,261 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.core.settings;
+
+import dynamin.core.string;
+import tango.io.device.Conduit;
+import tango.io.device.File;
+import tango.io.device.Array;
+import tango.io.stream.Text;
+import tango.io.Stdout;
+import tango.util.Convert;
+import tango.core.Exception;
+
+// TODO:
+align(1)
+struct Pixel32 {
+version(BigEndian) {
+	ubyte A;
+	ubyte R;
+	ubyte G;
+	ubyte B;
+} else {
+	ubyte B;
+	ubyte G;
+	ubyte R;
+	ubyte A;
+}
+}
+class Color2 {
+	Pixel32 ToPixel32() {
+		Pixel32 px;
+		return px;
+	}
+	string ToSetting() {
+		auto px = ToPixel32();
+		return format("{}, {}, {}", px.R, px.G, px.B);
+	}
+	static Color2 FromSetting(string str) {
+		// allow "#AB00F2", "171, 0, 242"
+		return null;
+	}
+}
+
+string test = r"
+; Here is a comment
+[Main]
+UndoLevels=500
+# Here is another comment
+CaretColor=255, 0, 0
+LineNumsVisible=true
+
+[RubyMode]
+TabSize=4
+";
+unittest {
+	auto settings = new Settings;
+	settings.loadFromString(test);
+	assert(settings.get("UndoLevels") == "500");
+	assert(settings.get("TabSize", "RubyMode") == "4");
+	assert(getSetting!(int)(settings, "UndoLevels") == 500);
+	assert(getSetting!(bool)(settings, "LineNumsVisible") == true);
+	Stdout(settings.saveToString()).newline;
+}
+
+/**
+ * This class will read from and write to what is basically a Windows INI file.
+ * Here is what a file would look like:
+ * Example:
+ * -----
+ * ; Here is a comment
+ * [Main]
+ * UndoLevels=500
+ * # Here is another comment
+ * CaretColor=255, 0, 0
+ * LineNumsVisible=true
+ *
+ * [RubyMode]
+ * TabSize=4
+ * -----
+ */
+class Settings {
+protected:
+	string[string][string] sections;
+	string[] comment;
+	void loadFromStream(InputStream stream) {
+		auto input = new TextInput(stream);
+		string section = MainSectionName;
+		bool inStartComment = true;
+		int lineNum = 0;
+		foreach(string line; input) {
+			lineNum++;
+			// check for a line with just whitespace
+			if(line.trim().length == 0)
+				continue;
+
+			// check for a comment
+			if(line.trimLeft().startsWith(";") ||
+					line.trimLeft().startsWith("#")) {
+				if(inStartComment) {
+					comment.length = comment.length + 1;
+					comment[$-1] = line.trimLeft()[1..$].dup;
+				}
+				continue;
+			}
+			inStartComment = false;
+
+			// check for a section header
+			if(line.startsWith("[")) {
+				if(!line.endsWith("]") || line.length < 3)
+					throw new Exception("Invalid section on line " ~ to!(string)(lineNum));
+				section = line[1..$-1].dup;
+				continue;
+			}
+
+			// parse key=value line (quickly)
+			int eqIndex;
+			for(eqIndex = 0; eqIndex < line.length; ++eqIndex)
+				if(line[eqIndex] == '=')
+					break;
+			if(eqIndex == line.length)
+				throw new Exception("Invalid format on line " ~ to!(string)(lineNum));
+			string value = line[eqIndex+1..$].unescape();
+			sections[section][line[0..eqIndex].dup] = value;
+		}
+	}
+	void saveToStream(OutputStream stream) {
+		auto output = new TextOutput(stream);
+		foreach(line; comment)
+			output(';')(line).newline;
+		foreach(string sectionName, string[string] sectionData; sections) {
+			output('[')(sectionName)(']').newline;// TODO: newline before each section
+			foreach(key, value; sectionData)
+				output(key)('=')(value).newline; // TODO: escape?
+		}
+	}
+public:
+	const string MainSectionName = "Main";
+	/**
+	 * Parses the specified file.
+	 */
+	void load(string file) {
+		scope f = new File(file);
+		loadFromStream(f);
+	}
+	void loadFromString(string str) {
+		scope a = new Array(str);
+		loadFromStream(a);
+	}
+	string saveToString() {
+		scope a = new Array(256, 80);
+		saveToStream(a);
+		return cast(string)a.slice(a.readable);
+	}
+	/**
+	 *
+	 * Examples:
+	 * -----
+	 * settings.get("UndoLevels");
+	 * settings.get("TabSize", "RubyMode");
+	 * -----
+	 */
+	string get(string name, string section = MainSectionName) {
+		auto sect = section in sections;
+		if(sect) {
+			auto val = name in *sect;
+			if(val)
+				return *val;
+		}
+		return "";
+	}
+
+	/**
+	 * Examples:
+	 * -----
+	 * settings.set("UndoLevels", "500")
+	 * settings.set("TabSize", "4", "RubyMode");
+	 * -----
+	 */
+	void set(string name, string value, string section = MainSectionName) {
+		if(section.contains('[') || section.contains(']'))
+			throw new IllegalArgumentException(
+				"Section name cannot contain a bracket");
+		if(name.contains('='))
+			throw new IllegalArgumentException(
+				"name cannot contain an equal sign");
+		sections[section][name] = value;
+	}
+	/**
+	 * Gets or sets the comment that appears at the beginning of the settings
+	 * file. This is set when a file is read in.
+	 */
+	string[] commentLines() {
+		return comment;
+	}
+	/// ditto
+	void commentLines(string[] lines) {
+		comment = lines;
+	}
+}
+
+/**
+ * Examples:
+ * -----
+ * getSetting!(int)(settings, "UndoLevels");
+ * getSetting!(int)(settings, "TabSize", "RubyMode");
+ * -----
+ */
+T getSetting(T)(Settings settings,
+		string name, string section = Settings.MainSectionName) {
+	string str = settings.get(name, section);
+	static if(is(T == bool))
+		return str == "true" || str == "yes" || str == "on";
+	else static if(is(T : long))
+		return cast(T)to!(T)(str);
+	else
+		return T.FromSetting(str);
+}
+
+/**
+ * Examples:
+ * -----
+ * setSetting!(int)(settings, "UndoLevels", 500);
+ * setSetting!(int)(settings, "TabSize", 4, "RubyMode");
+ * -----
+ */
+void setSetting(T)(Settings settings,
+		string name, T value, string section = Settings.MainSectionName) {
+	string str;
+	static if(is(T == bool))
+		str = value ? "true" : "false";
+	else static if(is(T : long))
+		str = to!(string)(value);
+	else
+		str = T.ToSetting();
+
+	settings.Set(name, str, section);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core/string.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,747 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+/**
+ * These functions should all return a new string if it is possible for them
+ * to ever modify their input. If they will never modify their input, they
+ * should always return a slice.
+ */
+module dynamin.core.string;
+
+import tango.core.Exception;
+import tango.text.convert.Utf;
+import tango.text.convert.Layout;
+import tango.text.Unicode;
+import tango.text.Util;
+import dynamin.core.global;
+import dynamin.core.math;
+
+/// Defined as a char[]
+alias char[] string;
+
+///
+char* toCharPtr(char[] str) {
+	return (str~'\0').ptr;
+}
+///
+wchar* toWcharPtr(char[] str) {
+	return toString16(str~'\0').ptr;
+}
+
+/*
+string ToString(ulong num, uint base = 10) {
+	if(base > 16)
+		throw new Exception("ToString() - radix more than 16");
+	char[] digits = "0123456789abcdef";
+	string str;
+	ulong div = base;
+	ulong prevDiv = 1;
+	do {
+		uint rem = num % div;
+		str ~= digits[rem/prevDiv];
+		prevDiv = div;
+		div *= base;
+		num -= rem;
+	} while(num > 0);
+	str.reverse;
+	return str;
+}
+*/
+Layout!(char) formatter;
+static this() {
+	formatter = new Layout!(char);
+}
+string format(char[] str, ...) {
+	return formatter.convert(_arguments, _argptr, str);
+}
+unittest {
+	assert(format("I am {}", 20) == "I am 20");
+}
+
+/**
+ * Converts all lowercase characters in the specified string to uppercase.
+ * Examples:
+ * -----
+ * "Bounce the ball.".upcase() == "BOUNCE THE BALL."
+ * "Mañana".upcase() == "MAÑANA"
+ * "æóëø".upcase() == "ÆÓËØ"
+ * -----
+ */
+string upcase(string str) {
+	return toUpper(str);
+}
+unittest {
+	assert("Bounce the ball.".upcase() == "BOUNCE THE BALL.");
+	assert("Mañana".upcase() == "MAÑANA");
+	assert("æóëø".upcase() == "ÆÓËØ");
+}
+/**
+ * Converts all uppercase characters in the specified string to lowercase.
+ * Examples:
+ * -----
+ * "BoUnCe ThE BALL.".downcase() == "bounce the ball."
+ * "MAÑANA".downcase() == "mañana"
+ * "ÆÓËØ".downcase() == "æóëø"
+ * -----
+ */
+string downcase(string str) {
+	return toLower(str);
+}
+unittest {
+	assert("BoUnCe ThE BALL.".downcase() == "bounce the ball.");
+	assert("MAÑANA".downcase() == "mañana");
+	assert("ÆÓËØ".downcase() == "æóëø");
+}
+
+// TODO: make more use of delegates in these?
+// TODO; use templates so that these work with wchar and dchar?
+bool startsWith(string str, string subStr, int start = 0) {
+	if(start+subStr.length > str.length)
+		return false;
+	return str[start..start+subStr.length] == subStr;
+}
+bool endsWith(string str, string subStr) {
+	return endsWith(str, subStr, str.length);
+}
+bool endsWith(string str, string subStr, int start) {
+	if(start-subStr.length < 0)
+		return false;
+	return str[str.length-subStr.length..str.length] == subStr;
+}
+int findLast(string str, string subStr) {
+	return findLast(str, subStr, str.length);
+}
+int findLast(string str, string subStr, int start) {
+	for(int i = start-subStr.length; i >= 0; --i)
+		if(str[i..i+subStr.length] == subStr)
+			return i;
+	return -1;
+}
+int find(string str, string subStr, int start = 0) {
+	for(int i = start; i < str.length-subStr.length; ++i)
+		if(str[i..i+subStr.length] == subStr)
+			return i;
+	return -1;
+}
+/**
+ * Tests whether or not the specified char is in the specified string.
+ * Returns: true if the specified char is in the string and false otherwise
+ * Examples:
+ * -----
+ * "Hello".contains('e') == true
+ * "Hello".contains('a') == false
+ * "".contains('e') == false
+ * -----
+ */
+bool contains(string str, char c) {
+	foreach(char c2; str) {
+		if(c == c2)
+			return true;
+	}
+	return false;
+}
+/// ditto
+bool contains(string str, dchar c) {
+	foreach(dchar c2; str) {
+		if(c == c2)
+			return true;
+	}
+	return false;
+}
+unittest {
+	assert("Hello".contains('e') == true);
+	assert("Hello".contains('a') == false);
+	assert("".contains('e') == false);
+}
+string remove(string str, int start, int count = 1) {
+	return str[0..start] ~ str[start+count..str.length];
+}
+// TODO: ?
+// split(string str, int delegate(string s) func)
+//string[] split(string str, string subStr) {
+//	return split(str, (string s) { return s.startsWith(subStr) ? subStr.length, : -1; };
+//}
+// TODO: return slices to string
+//split1("50=20=10", "=") -> ["50", "20=10"]
+string[] split1(string str, string subStr) {
+	if(subStr.length == 0)
+		return [str];
+	int index = find(str, subStr);
+	if(index == -1)
+		return [str];
+	string[] strs = new string[2];
+	strs[0] = str[0..index].dup;
+	strs[1] = str[index+subStr.length..str.length].dup;
+	return strs;
+}
+// TODO: return slices to string
+//split("50=20=10", "=") -> ["50", "20", "10"]
+string[] split(string str, string subStr) {
+	if(subStr.length == 0)
+		return [str];
+	string[] strs;
+	int index, searchFrom;
+	int i = 0;
+	while(searchFrom < str.length) {
+		index = find(str, subStr, searchFrom);
+		if(index == -1) index = str.length;
+		strs.length = strs.length+1;
+		strs[i] = str[searchFrom..index].dup;
+		++i;
+		searchFrom = index+subStr.length;
+	}
+	return strs;
+}
+///
+enum Newline {
+	///
+	Cr = 0,
+	///
+	Lf = 1,
+	///
+	Crlf = 2,
+	///
+	Macintosh = 0,
+	///
+	Linux = 1,
+	///
+	Windows = 2
+}
+/**
+ * Changes every occurrence of a newline in the specified string to the specified newline.
+ * Examples:
+ * -----
+ * "\r\n\n\r".convertNewlines(Newline.Lf) == "\n\n\n"
+ * "\r\n\n\r".convertNewlines(Newline.Windows) == "\r\n\r\n\r\n"
+ * "\n\r\n".convertNewlines(Newline.Macintosh) == "\r\r"
+ * -----
+ */
+string convertNewlines(string str, Newline nl) {
+	string lineSep;
+	switch(nl) {
+	case Newline.Cr:   lineSep = "\r";   break;
+	case Newline.Lf:   lineSep = "\n";   break;
+	case Newline.Crlf: lineSep = "\r\n"; break;
+	}
+	return str.replace([cast(string)"\r\n", "\r", "\n"], lineSep);
+}
+unittest {
+	assert("\r\n\n\r".convertNewlines(Newline.Lf) == "\n\n\n");
+	assert("\r\n\n\r".convertNewlines(Newline.Windows) == "\r\n\r\n\r\n");
+	assert("\n\r\n".convertNewlines(Newline.Macintosh) == "\r\r");
+}
+
+/**
+ * Joins all the strings in the specified array together into one string, putting
+ * the specified separator between them.
+ * Examples:
+ * -----
+ * join(["10", "15", "17"], " - ") == "10 - 15 - 17"
+ * join(["789", "672", "484"], ",") == "789,672,484"
+ * join(["aol.com", "join", "intro.html"], "/") == "aol.com/join/intro.html"
+ * -----
+ */
+string join(string[] strs, string sep) {
+	if(strs.length == 0)
+		return "";
+	int len;
+	foreach(string s; strs)
+		len += s.length;
+	len += sep.length*(strs.length-1);
+
+	string newStr = new char[len];
+	newStr[0..strs[0].length] = strs[0];
+	int start = strs[0].length;
+	for(int i = 1; i < strs.length; ++i) {
+		auto str = strs[i];
+		newStr[start..start+sep.length] = sep;
+		start += sep.length;
+		newStr[start..start+str.length] = str;
+		start += str.length;
+	}
+	return newStr;
+}
+unittest {
+	// TODO: remove cast(string) when D has bugs fixed
+	assert(join(["10", "15", "17"], " - ") == "10 - 15 - 17");
+	assert(join(["789", "672", "484"], ",") == "789,672,484");
+	assert(join([cast(string)"aol.com", "join", "intro.html"], "/") == "aol.com/join/intro.html");
+}
+
+/**
+ * Multiplies the given string the specified number of times.
+ * Returns: a string that is the result of adding the specified string onto
+ *          an empty string the specified number of times
+ * Examples:
+ * -----
+ * "Hi...".times(3) == "Hi...Hi...Hi..."
+ * "0".times(20) == "00000000000000000000"
+ * "Hi".times(0) == ""
+ * -----
+ */
+string times(string str, int n) {
+	string newStr = new char[n * str.length];
+	for(int i = 0; i < newStr.length; i += str.length)
+		newStr[i..i+str.length] = str;
+	return newStr;
+}
+unittest {
+	assert("0".times(4) == "0000");
+	assert("Hello! ".times(2) == "Hello! Hello! ");
+	assert("".times(50) == "");
+	assert("Hi".times(0) == "");
+}
+
+// TODO: flesh out and make public
+struct sbuilder {
+	int Count;
+	string Data;
+	void Add(char c) {
+		if(Count + 1 > Data.length)
+			Data.length = (Data.length + 1) * 2;
+		Data[Count] = c;
+		++Count;
+	}
+	void Add(string str) {
+		if(Count + str.length > Data.length)
+			Data.length = max((Data.length + 1) * 2, Count + str.length);
+		Data[Count..Count+str.length] = str;
+		Count += str.length;
+	}
+	string ToString() {
+		return Data[0..Count].dup;
+	}
+}
+/**
+ * Replaces any occurrence of a specified search string in the specified string
+ * with corresponding replacement string. The length of the searchStrs array
+ * must equal the length of the replacements array.
+ * Examples:
+ * -----
+ * "Mississippi".replace(["is", "i"], ["..", "*"]) == "M..s..s*pp*"
+ * "Mississippi".replace("ss", "...") == "Mi...i...ippi"
+ * "Hello".replace("ll", "y") == "Heyo"
+ * "Hi".replace([], []) == "Hi"
+ * -----
+ * Note: If multiple search strings have the same prefix, the longer search
+ *       strings must be given first. Otherwise, any occurrence will match a
+ *       shorter one and will not have a chance to match any longer one.
+ * Examples:
+ * -----
+ * "Speaker".replace(["ea", "e"], ":") == "Sp:k:r"
+ * "Speaker".replace(["e", "ea"], ":") == "Sp:ak:r"
+ * -----
+ * Bug: If a search string has a length of zero, this method will go into an infinite loop.
+ */
+string replace(string str, string[] searchStrs, string[] replacements) {
+	if(replacements.length == 1 && searchStrs.length > 1) {
+		string tmp = replacements[0];
+		replacements = new string[searchStrs.length];
+			foreach(i, dummy; searchStrs)
+				replacements[i] = tmp;
+	}
+	if(searchStrs.length != replacements.length)
+		throw new IllegalArgumentException(
+			"Replace(): searchStrs and replacements must be same length");
+	sbuilder builder;
+	loop:
+	for(int i = 0; i < str.length; ) {
+		foreach(j, subStr; searchStrs) {
+			if(i+subStr.length <= str.length && str[i..i+subStr.length] == subStr) {
+				// skip the part of string that matched
+				i += subStr.length;
+				builder.Add(replacements[j]);
+				continue loop;
+			}
+		}
+		builder.Add(str[i]);
+		++i;
+	}
+	return builder.ToString();
+}
+/// ditto
+string replace(string str, string[] searchStrs, string replacement) {
+	return str.replace(searchStrs, [replacement]);
+}
+/// ditto
+string replace(string str, string searchStr, string replacement) {
+	return str.replace([searchStr], [replacement]);
+}
+unittest {
+	assert("Mississippi".replace([cast(string)"is", "i"], [cast(string)"..", "*"]) == "M..s..s*pp*");
+	assert("Mississippi".replace("ss", "...") == "Mi...i...ippi");
+	assert("Hello".replace("ll", "y") == "Heyo");
+	//assert("Hi".Replace(cast(string[])[], cast(string[])[]) == "Hi");
+	assert("Speaker".replace([cast(string)"ea", "e"], ":") == "Sp:k:r");
+	assert("Speaker".replace([cast(string)"e", "ea"], ":") == "Sp:ak:r");
+}
+
+/**
+ * Changes every occurrence of a specified character in chars to the
+ * corresponding character in escChars.
+ * Examples:
+ * -----
+ * "Line1\r\nLine2\\".escape() == "Line1\\r\\nLine2\\\\"
+ * "Line1\tLine2".escape() == "Line1\\tLine2"
+ * "Part1|Part2\r\n".escape("|\r\n", "|rn") == "Part1\\|Part2\\r\\n"
+ * -----
+ */
+string escape(string str, char[] chars, char[] escChars) {
+	if(chars.length != escChars.length)
+		throw new IllegalArgumentException("Escape(): chars and escChars must be same length");
+	sbuilder builder;
+	loop:
+	foreach(i, c; str) {
+		foreach(j, c2; chars) {
+			if(c == '\\') {   // always escape backslash
+				builder.Add('\\');
+				builder.Add('\\');
+				continue loop;
+			}
+			if(c == c2) {
+				builder.Add('\\');
+				builder.Add(escChars[j]);
+				continue loop;
+			}
+		}
+		builder.Add(c);
+	}
+	return builder.ToString();
+}
+/// ditto
+string escape(string str) {
+	return str.escape("\t\r\n", "trn");
+}
+unittest {
+	assert("Line1\r\nLine2\\".escape() == "Line1\\r\\nLine2\\\\");
+	assert("Line1\tLine2".escape() == "Line1\\tLine2");
+	assert("Part1|Part2\r\n".escape("|\r\n", "|rn") == "Part1\\|Part2\\r\\n");
+}
+/**
+ * Changes every occurrence of a specified character in escChars to the
+ * corresponding character in chars.
+ * Examples:
+ * -----
+ * "Line1\\r\\nLine2".unescape() == "Line1\r\nLine2"
+ * "Line1\\tLine2".unescape() == "Line1\tLine2"
+ * "Part1\\|Part2\\r\\n".unescape("|rn", "|\r\n") == "Part1|Part2\r\n"
+ * // error:
+ * "test\\".unescape()
+ * -----
+ */
+string unescape(string str, char[] escChars, char[] chars) {
+	if(escChars.length != chars.length)
+		throw new IllegalArgumentException("Unescape(): escChars and chars must be same length");
+	sbuilder builder;
+	loop:
+	foreach(i, c; str) {
+		if(c == '\\') {
+			if(i == str.length-1)
+				throw new IllegalArgumentException("Unescape(): partial escape sequence at end of string");
+			if(str[i+1] == '\\') {
+				builder.Add('\\');
+				++i;
+				continue loop;
+			}
+			foreach(j, c2; escChars) {
+				if(str[i+1] == c2) {
+					builder.Add(chars[j]);
+					++i;
+					continue loop;
+				}
+			}
+			throw new IllegalArgumentException("Unescape(): invalid escape sequence");
+		}
+		builder.Add(str[i]);
+	}
+	return builder.ToString();
+}
+/// ditto
+string unescape(string str) {
+	return str.unescape("trn", "\t\r\n");
+}
+unittest {
+	assert("Line1\\r\\nLine2\\\\".unescape() == "Line1\r\nLine2\\");
+	assert("Line1\\tLine2".unescape() == "Line1\tLine2");
+	assert("Part1\\|Part2\\r\\n".unescape("|rn", "|\r\n") == "Part1|Part2\r\n");
+}
+unittest {
+	string str = r"C:\\n";
+	assert(str.escape().unescape() == str);
+}
+/**
+ * Removes all whitespace characters from the specified string.
+ * Examples:
+ * -----
+ * "4a d2  7c 3f".removeWhitespace() == "4ad27c3f"
+ * " Hello \r\n".removeWhitespace() == "Hello"
+ * "How are you?".removeWhitespace() == "Howareyou?"
+ * "\t \n\r\f\v".removeWhitespace() == ""
+ * -----
+ */
+string removeWhitespace(string str) {
+	sbuilder builder;
+	foreach(c; str)
+		if(!" \t\n\r\v\f".contains(c))
+			builder.Add(c);
+	return builder.ToString();
+}
+unittest {
+	assert("4a d2  7c 3f".removeWhitespace() == "4ad27c3f");
+	assert(" Hello \r\n".removeWhitespace() == "Hello");
+	assert("How are you?".removeWhitespace() == "Howareyou?");
+	assert("\t \n\r\f\v".removeWhitespace() == "");
+}
+/**
+ * Removes all the whitespace characters from the start and from the end
+ * of the specified string. Returns a slice.
+ * Examples:
+ * -----
+ * " Hello \r\n".trim() == "Hello"
+ * "How are you?".trim() == "How are you?"
+ * "\n la di da ".trim() == "la di da"
+ * " \n".trim() == ""
+ * "".trim() == ""
+ * -----
+ */
+string trim(string str) {
+	int start = -1, end = str.length;
+	while( --end >= 0 && " \t\n\r\v\f".contains(str[end]) ) { }
+	end++;
+	if(end == 0) // means all whitespace
+		return "";
+	while(" \t\n\r\v\f".contains(str[++start])) { }
+	return str[start..end];
+}
+unittest {
+	assert(" Hello \r\n".trim() == "Hello");
+	assert("How are you?".trim() == "How are you?");
+	assert("\n la di da ".trim() == "la di da");
+	assert(" \n".trim() == "");
+	assert("".trim() == "");
+}
+/**
+ * Removes all the whitespace characters from the start
+ * of the specified string. Returns a slice.
+ * Examples:
+ * -----
+ * " Hello \r\n".trimLeft() == "Hello \r\n"
+ * "How are you?".trimLeft() == "How are you?"
+ * "\n la di da ".trimLeft() == "la di da "
+ * " \n".trimLeft() == ""
+ * "".trimLeft() == ""
+ * -----
+ */
+string trimLeft(string str) {
+	int start = -1;
+	while(++start < str.length && " \t\n\r\v\f".contains(str[start])) { }
+	return str[start..$];
+}
+unittest {
+	assert(" Hello \r\n".trimLeft() == "Hello \r\n");
+	assert("How are you?".trimLeft() == "How are you?");
+	assert("\n la di da ".trimLeft() == "la di da ");
+	assert(" \n".trimLeft() == "");
+	assert("".trimLeft() == "");
+}
+/**
+ * Removes all the whitespace characters from the start
+ * of the specified string. Returns a slice.
+ * Examples:
+ * -----
+ * " Hello \r\n".trimRight() == " Hello"
+ * "How are you?".trimRight() == "How are you?"
+ * "\n la di da ".trimRight() == "\n la di da"
+ * " \n".trimRight() == ""
+ * "".trimRight() == ""
+ * -----
+ */
+string trimRight(string str) {
+	int end = str.length;
+	while( --end >= 0 && " \t\n\r\v\f".contains(str[end]) ) { }
+	end++;
+	return str[0..end];
+}
+unittest {
+	assert(" Hello \r\n".trimRight() == " Hello");
+	assert("How are you?".trimRight() == "How are you?");
+	assert("\n la di da ".trimRight() == "\n la di da");
+	assert(" \n".trimRight() == "");
+	assert("".trimRight() == "");
+}
+
+
+unittest {
+	assert(".NET Framework".startsWith(".N"));
+	assert(".NET Framework".startsWith("Frame", 5));
+	assert(!".NET Framework".startsWith(".NEW"));
+	assert(".NET Framework".find("NET") == 1);
+	assert(".NET Framework".find("NET", 2) == -1);
+	assert(".NET Framework".find("") == 0);
+	assert("Mississippi".findLast("ss") == 5);
+	assert("Mississippi".findLast("ss", 4) == 2);
+	assert("Jordan=20".split("=") == [cast(string)"Jordan", "20"]);
+	assert("Jordan".split("") == [cast(string)"Jordan"]);
+	assert("Jordan".split1("=") == [cast(string)"Jordan"]);
+}
+
+/*class Encoding {
+private:
+	//TODO: remove dependency on std.utf
+	static Encoding[] encodings = [
+		//new Encoding("windows-1252".Str(), "Western European (Windows)".Str(), encodeTableWindows1252)
+	];
+	String name;
+	String desc;
+public:
+	//property
+	static Encoding[] Encodings() {
+		return encodings;
+	}
+	static Encoding GetEncoding(String name) {
+	}
+	static byte[] Convert(Encoding src, Encoding dst, byte[] bytes) {}
+
+	this(String name, String description, wchar[] table) {
+		this.name = name;
+	}
+	// example: utf-8
+	String Name() {
+		return name;
+	}
+	// example: Unicode (UTF-8)
+	String Description() {
+		return desc;
+	}
+	int GetEncodedCount() {}
+	byte[] Encode(String str) {
+	}
+	//Returns the number of characters
+	int GetDecodedLength() {
+	}
+	String Decode(byte[] bytes) {
+	}
+}*/
+//class Utf8Encoding : Encoding {
+//public:
+//}
+
+//characters that cannot be mapped to unicode are 0xFFFD in the tables
+wchar[] encodeTableWindows1252 = [
+	0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008,
+	0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011,
+	0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A,
+	0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023,
+	0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C,
+	0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,
+	0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E,
+	0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+	0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
+	0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059,
+	0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061, 0x0062,
+	0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B,
+	0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074,
+	0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D,
+	0x007E, 0x007F, 0x20AC, 0xFFFD, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020,
+	0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0xFFFD, 0x017D, 0xFFFD,
+	0xFFFD, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC,
+	0x2122, 0x0161, 0x203A, 0x0153, 0xFFFD, 0x017E, 0x0178, 0x00A0, 0x00A1,
+	0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA,
+	0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3,
+	0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC,
+	0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5,
+	0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE,
+	0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+	0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0,
+	0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9,
+	0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2,
+	0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB,
+	0x00FC, 0x00FD, 0x00FE, 0x00FF
+];
+
+//iso8859-1 does not need a conversion table, as its values are all the same as Unicode's
+
+wchar[] encodeTableIso8859_2 = [
+	0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008,
+	0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011,
+	0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A,
+	0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023,
+	0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C,
+	0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,
+	0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E,
+	0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+	0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
+	0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059,
+	0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061, 0x0062,
+	0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B,
+	0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074,
+	0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D,
+	0x007E, 0x007F, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086,
+	0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+	0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098,
+	0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, 0x00A0, 0x0104,
+	0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7, 0x00A8, 0x0160, 0x015E,
+	0x0164, 0x0179, 0x00AD, 0x017D, 0x017B, 0x00B0, 0x0105, 0x02DB, 0x0142,
+	0x00B4, 0x013E, 0x015B, 0x02C7, 0x00B8, 0x0161, 0x015F, 0x0165, 0x017A,
+	0x02DD, 0x017E, 0x017C, 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139,
+	0x0106, 0x00C7, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE,
+	0x010E, 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7,
+	0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, 0x0155,
+	0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9,
+	0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, 0x0111, 0x0144, 0x0148,
+	0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171,
+	0x00FC, 0x00FD, 0x0163, 0x02D9
+];
+
+wchar[] encodeTableIso8859_3 = [
+	0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008,
+	0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011,
+	0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A,
+	0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023,
+	0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C,
+	0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,
+	0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E,
+	0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+	0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
+	0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059,
+	0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061, 0x0062,
+	0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B,
+	0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074,
+	0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D,
+	0x007E, 0x007F, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086,
+	0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+	0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098,
+	0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, 0x00A0, 0x0126,
+	0x02D8, 0x00A3, 0x00A4, 0x0124, 0x00A7, 0x00A8, 0x0130, 0x015E, 0x011E,
+	0x0134, 0x00AD, 0x017B, 0x00B0, 0x0127, 0x00B2, 0x00B3, 0x00B4, 0x00B5,
+	0x0125, 0x00B7, 0x00B8, 0x0131, 0x015F, 0x011F, 0x0135, 0x00BD, 0x017C,
+	0x00C0, 0x00C1, 0x00C2, 0x00C4, 0x010A, 0x0108, 0x00C7, 0x00C8, 0x00C9,
+	0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D1, 0x00D2, 0x00D3,
+	0x00D4, 0x0120, 0x00D6, 0x00D7, 0x011C, 0x00D9, 0x00DA, 0x00DB, 0x00DC,
+	0x016C, 0x015C, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E4, 0x010B, 0x0109,
+	0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+	0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x0121, 0x00F6, 0x00F7, 0x011D, 0x00F9,
+	0x00FA, 0x00FB, 0x00FC, 0x016D, 0x015D, 0x02D9
+];
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core/unix_console.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,127 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.core.unix_console;
+
+public import tango.stdc.stdlib;
+//public import tango.stdc.string;
+public import dynamin.core.string;
+
+/*
+Can get width of console with $COLUMNS
+Can get height of console with $LINES
+*/
+template ConsoleBackend() {
+	static colorsUsed = false;
+	static ~this() {
+		if(colorsUsed)
+			backend_resetColors();
+	}
+	bool buffered = false;
+	void backend_buffered(bool b) {
+		buffered = b;
+	}
+	void backend_write(string s) {
+		/*fwrite(s.ptr, 1, s.length, stdout);
+		if(!buffered)
+			fflush(stdout);*/
+	}
+	string backend_readLineRaw() {
+		/*uint size;
+		char* line;
+		auto numRead = getline(&line, &size, stdin);
+		scope(exit) free(line);
+		string str = new char[numRead];
+		str[] = line[0..numRead];
+		return str;*/
+		return null;
+	}
+	string backend_read() {
+		return null;
+	}
+	// TODO: use this
+	//termios ts;
+	//tcgetattr(filedes, &ts);
+	//ts.c_lflag |= ICANON; // turns on waiting for a whole lines
+	//ts.c_lflag |= ECHO;   // turns on echoing
+	//tcsetattr(filedes, TCSAFLUSH, &ts);
+	string backend_readLineHidden() {
+		system("stty -echo");
+		auto line = readLine();
+		system("stty echo");
+		return line;
+	}
+	string backend_readHidden() {
+		return null;
+	}
+	void backend_clear() {
+		system("clear");
+	}
+	string backend_getColorStr(ConsoleColor c, bool fore) {
+		switch(c) {
+		case c.Black:      return fore ? "\x1b[30m" : "\x1b[40m";
+		case c.Silver:     return fore ? "\x1b[37m" : "\x1b[47m";
+		case c.Maroon:     return fore ? "\x1b[31m" : "\x1b[41m";
+		case c.DarkBlue:   return fore ? "\x1b[34m" : "\x1b[44m";
+		case c.Green:      return fore ? "\x1b[32m" : "\x1b[42m";
+		case c.Purple:     return fore ? "\x1b[35m" : "\x1b[45m";
+		case c.DarkYellow: return fore ? "\x1b[33m" : "\x1b[43m";
+		case c.Teal:       return fore ? "\x1b[36m" : "\x1b[46m";
+		case c.Gray:       return fore ? "\x1b[90m" : "\x1b[100m";
+		case c.White:      return fore ? "\x1b[97m" : "\x1b[107m";
+		case c.Red:        return fore ? "\x1b[91m" : "\x1b[101m";
+		case c.Blue:       return fore ? "\x1b[94m" : "\x1b[104m";
+		case c.LightGreen: return fore ? "\x1b[92m" : "\x1b[102m";
+		case c.Pink:       return fore ? "\x1b[95m" : "\x1b[105m";
+		case c.Yellow:     return fore ? "\x1b[93m" : "\x1b[103m";
+		case c.Cyan:       return fore ? "\x1b[96m" : "\x1b[106m";
+		default: assert(0);
+		}
+	}
+	void backend_foreColor(ConsoleColor color) {
+		colorsUsed = true;
+		backend_write(backend_getColorStr(color, true));
+	}
+	void backend_backColor(ConsoleColor color) {
+		colorsUsed = true;
+		backend_write(backend_getColorStr(color, false));
+	}
+	void backend_resetColors() {
+		backend_write("\x1b[39;49m");
+	}
+	void backend_bold(bool b) {
+		backend_write(b ? "\x1b[1m" : "\x1b[22m");
+	}
+	void backend_italic(bool b) {
+		backend_write(b ? "\x1b[3m" : "\x1b[23m");
+	}
+	void backend_underline(bool b) {
+		backend_write(b ? "\x1b[4m" : "\x1b[24m");
+	}
+	void backend_strikethrough(bool b) {
+		backend_write(b ? "\x1b[9m" : "\x1b[29m");
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core/unix_environment.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,80 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.core.unix_environment;
+
+public import tango.stdc.posix.sys.time;
+public import tango.io.Stdout;
+
+// TODO: v1.0 make a binding to these
+extern(C) {
+	int get_nprocs_conf();
+	int get_nprocs();
+	int getitimer(int which, itimerval* value);
+	int setitimer(int which, itimerval* value,
+		itimerval* ovalue);
+}
+
+enum {
+	ITIMER_REAL    = 0,
+	ITIMER_VIRTUAL = 1,
+	ITIMER_PROF    = 2
+}
+
+struct itimerval {
+	timeval it_interval;
+	timeval it_value;
+}
+
+template EnvironmentBackend() {
+	long backend_timevalToMs(timeval* tv) {
+		return tv.tv_sec*1000L+tv.tv_usec/1000;
+	}
+	const long timerSec = 31_536_000*5; // 31,536,000 seconds in 365 days
+	static this() {
+		itimerval itv;
+		itv.it_value.tv_sec = timerSec;
+		if(setitimer(ITIMER_REAL, &itv, null))
+			Stdout("setitimer() failed").newline;
+	}
+	long backend_runningTime() {
+		itimerval itv;
+		getitimer(ITIMER_REAL, &itv);
+		return timerSec*1000-backend_timevalToMs(&itv.it_value);
+	}
+	long backend_systemTime() {
+		timeval tv;
+		if(gettimeofday(&tv, null))
+			Stdout("gettimeofday() failed!").newline;
+		return backend_timevalToMs(&tv);
+	}
+	int backend_processorCount() {
+		return get_nprocs();
+	}
+	long backend_processorTime() {
+		return 0;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core/windows_console.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,147 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.core.windows_console;
+
+public import dynamin.c.windows;
+public import dynamin.core.string;
+
+public import tango.io.Stdout;
+public import tango.stdc.stdlib;
+
+template ConsoleBackend() {
+	uint inputCP;
+	uint outputCP;
+	static this() {
+		// calling GetConsoleOutputCP() takes twice as long as
+		// the entire backend_Write() function...it is slow!
+		//inputCP = GetConsoleCP();
+		//outputCP = GetConsoleOutputCP();
+	}
+	bool buffered = false;
+	void backend_buffered(bool b) {
+		buffered = b;
+	}
+	void backend_write(string s) {
+		// the reasons for this function being slower than writef():
+		//  - partly the conversion overhead (UTF-8 -> UTF16 -> CP)
+		//  - partly because it is not buffered
+/+
+		// make sure printf/writef output appears in right order
+		fflush(stdout);
+
+		auto wbuffer = ToUtf16(s);
+		scope(exit) delete wbuffer;
+		auto needed = WideCharToMultiByte(outputCP, 0, wbuffer.ptr, wbuffer.length, null, 0, null, null);
+		// faster than: auto buffer = new char[needed];
+		auto buffer = (cast(char*)alloca(needed))[0..needed];
+		scope(exit) delete buffer;
+		WideCharToMultiByte(outputCP, 0, wbuffer.ptr, wbuffer.length, buffer.ptr, buffer.length, null, null);
+
+		auto stdOut = GetStdHandle(-11);
+		DWORD numWritten;
+		if(!WriteFile(stdOut, buffer.ptr, buffer.length, &numWritten, null))
+			printf("WriteFile() failed, error %d\n", GetLastError());+/
+
+	}
+	string backend_readLineRaw() {/+
+		auto stdIn = GetStdHandle(-10);
+		// TODO: does not work if input is from a file!
+		// if reading from a file, a line can be very long...
+		// and you do not want to read farther than that.
+		char[4096] buffer = void;
+		DWORD numRead;
+		if(!ReadFile(stdIn, buffer.ptr, buffer.length, &numRead, null))
+			printf("ReadFile() failed, error %d\n", GetLastError());
+
+		auto needed = MultiByteToWideChar(inputCP, 0, buffer.ptr, numRead, null, 0);
+		//faster than:  auto wbuffer = new wchar[needed];
+		auto wbuffer = (cast(wchar*)alloca(needed*2))[0..needed];
+		scope(exit) delete wbuffer;
+		auto numUsed = MultiByteToWideChar(inputCP, 0, buffer.ptr, numRead, wbuffer.ptr, wbuffer.length);
+		return ToUtf8(wbuffer[0..numUsed]);+/
+		return null;
+	}
+	string backend_read() {
+		return null;
+	}
+	string backend_readLineHidden() {
+		return null;
+	}
+	string backend_readHidden() {
+		return null;
+	}
+	void backend_clear() {
+		system("cls");
+	}
+	uint backend_getColorFlags(ConsoleColor c, bool fore) {
+		uint i = fore ? FOREGROUND_INTENSITY : BACKGROUND_INTENSITY;
+		uint r = fore ? FOREGROUND_RED : BACKGROUND_RED;
+		uint g = fore ? FOREGROUND_GREEN : BACKGROUND_GREEN;
+		uint b = fore ? FOREGROUND_BLUE : BACKGROUND_BLUE;
+		switch(c) {
+		case c.Black:      return 0;
+		case c.Silver:     return r | g | b;
+		case c.Maroon:     return r;
+		case c.DarkBlue:   return b;
+		case c.Green:      return g;
+		case c.Purple:     return r | b;
+		case c.DarkYellow: return r | g;
+		case c.Teal:       return g | b;
+		case c.Gray:       return i;
+		case c.White:      return r | g | b | i;
+		case c.Red:        return r | i;
+		case c.Blue:       return b | i;
+		case c.LightGreen: return g | i;
+		case c.Pink:       return r | b | i;
+		case c.Yellow:     return r | g | i;
+		case c.Cyan:       return g | b | i;
+		default: assert(0);
+		}
+	}
+	void backend_foreColor(ConsoleColor color) {
+		CONSOLE_SCREEN_BUFFER_INFO bufferInfo;
+		if(!GetConsoleScreenBufferInfo(GetStdHandle(-11), &bufferInfo))
+			return;
+		bufferInfo.wAttributes &= ~0b1111; // clear the 4 foreground bits
+		SetConsoleTextAttribute(GetStdHandle(-11),
+			bufferInfo.wAttributes | backend_getColorFlags(color, true));
+	}
+	void backend_backColor(ConsoleColor color) {
+		CONSOLE_SCREEN_BUFFER_INFO bufferInfo;
+		if(!GetConsoleScreenBufferInfo(GetStdHandle(-11), &bufferInfo))
+			return;
+		bufferInfo.wAttributes &= ~0b11110000; // clear the 4 background bits
+		SetConsoleTextAttribute(GetStdHandle(-11),
+			bufferInfo.wAttributes | backend_getColorFlags(color, false));
+	}
+	void backend_resetColors() {
+	}
+	void backend_bold(bool b) { }
+	void backend_italic(bool b) { }
+	void backend_underline(bool b) { }
+	void backend_strikethrough(bool b) { }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core/windows_environment.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,91 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.core.windows_environment;
+
+import dynamin.c.windows;
+
+template EnvironmentBackend() {
+	long pStart;    // processor time in milliseconds
+	long tStart;    // computer uptime in milliseconds
+	long startDiff; // pStart-tStart
+	static this() {
+		backend_increaseTimerRes();
+		QueryPerformanceFrequency(&freq);
+		freq /= 1000;
+		pStart = processorTime;
+		tStart = timeGetTime();
+		startDiff = pStart-tStart;
+	}
+	public void backend_increaseTimerRes() {
+		static int period = -1;
+		if(period >= 0)
+			timeEndPeriod(period);
+		TIMECAPS tc;
+		timeGetDevCaps(&tc, TIMECAPS.sizeof);
+		period = tc.wPeriodMin > 0 ? tc.wPeriodMin : 1;
+		timeBeginPeriod(period);
+	}
+	long backend_runningTime() {
+		// NOTE: might be a faster way to do this...ProcessorTime is slow
+		// Use ProcessorTime to fix when timeGetTime() rolls over
+		const strayMs = 18_000_000; // 5 hours
+		long pNow = processorTime;
+		long tNow = timeGetTime();
+		// pNow-startDiff would equal tNow except that:
+		// - tNow has possibly rolled over
+		// - pNow has strayed because it runs at a slightly different speed
+		while(pNow-startDiff > tNow+strayMs)
+			tNow += 0xFFFF_FFFF;   // tNow has rolled over, so fix it
+		return tNow-tStart;
+	}
+	long backend_systemTime() {
+		long t;
+		// gets time as 100 ns since Jan 1, 1601
+		GetSystemTimeAsFileTime(cast(FILETIME*)&t);
+		// This code gets the difference between 1601 and 1970
+		//SYSTEMTIME st;
+		//st.wYear = 1970;
+		//st.wMonth = 1;
+		//st.wDay = 1;
+		//long diff;
+		//SystemTimeToFileTime(&st, cast(FILETIME*)&diff);
+		t -= 116444736000000000; // change to Jan 1, 1970
+		t /= 10000; // change to milliseconds
+		return t;
+	}
+	int backend_processorCount() {
+		SYSTEM_INFO si;
+		GetSystemInfo(&si);
+		return si.dwNumberOfProcessors;
+	}
+	ulong freq = 0;
+	long backend_processorTime() {
+		ulong count;
+		QueryPerformanceCounter(&count);
+		return count/freq;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core_backend.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,35 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.core_backend;
+
+version(Windows) {
+	public import dynamin.core.windows_console;
+	public import dynamin.core.windows_environment;
+} else {
+	public import dynamin.core.unix_console;
+	public import dynamin.core.unix_environment;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/action.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,97 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.action;
+
+import dynamin.all_core;
+import dynamin.all_gui;
+import tango.util.collection.HashMap;
+
+/**
+ * TODO: change to struct with D 2.0 and use ref return on ActionMap.addAction
+ *       might not be possible...probably have to keep class
+ */
+class Action {
+	/// The non-visible name of the action
+	string name; // no setter for this ...set with constructor
+	/// The text displayed on the menu item, tool bar button, etc.
+	string text;
+	///
+	string shortcut; // TODO: how to store string and key/modifiers?
+	/**
+	 * The icon displayed on the menu item, tool bar button, etc.
+	 * Icons for disabled and hot controls are created automatically
+	 * using the current theme.
+	 */
+	string icon; // TODO: allow setting hot and disabled icons to override
+	///
+	void delegate() invoke;
+	/**
+	 * If toggle is true, then this action can be selected and deselected.
+	 */
+	bool toggle = false;
+	/**
+	 * Has no effect unless toggle is set to true.
+	 * If the group is the default, the action can be selected/deselected
+	 * regardless of other actions. If the group is set to another value, then
+	 * selecting this action will deselect other actions with the same group.
+	 */
+	// TODO: the deselecting of other actions w/same group should look through
+	//       the ActionMap...will need ref
+	int group = -1; // TODO: a string is a lot easier to avoid conflicts
+	///
+	bool selected = false;
+	///
+	bool enabled;
+	// TODO: need to make getters and setters...have to fire event when changed
+}
+
+/**
+ * Example:
+ * -----
+ * with(actionMap.addAction("data-viewer")) {
+ *     text = "Data Viewer";
+ *     shortcut = "Ctrl+D";
+ *     icon = "data-viewer.png";
+ *     toggle = true;
+ *     group = 2;
+ *     invoke = {
+ *         new DataViewerDialog().show();
+ *     };
+ * }
+ * -----
+ */
+class ActionMap : HashMap!(string, Action) {
+	Action addAction(string name) {
+		assert(!name.contains(' '), "action name cannot contain space");
+		Action a = new Action(name);
+		add(name, a);
+	}
+}
+
+unittest {
+	//
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/all.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,58 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.all;
+
+// listed somewhat in order of dependences
+public import dynamin.gui.key;
+public import dynamin.gui.control;
+public import dynamin.gui.container;
+public import dynamin.gui.events;
+public import dynamin.gui.window;
+public import dynamin.gui.layout;
+
+public import dynamin.gui.cursor;
+public import dynamin.gui.clipboard;
+public import dynamin.gui.screen;
+version(Windows) public import dynamin.gui.directory_dialog;
+version(Windows) public import dynamin.gui.file_dialog;
+
+public import dynamin.gui.label;
+public import dynamin.gui.button;
+public import dynamin.gui.check_box;
+public import dynamin.gui.radio_button;
+public import dynamin.gui.notebook;
+
+public import dynamin.gui.scroll_bar;
+public import dynamin.gui.scrollable;
+public import dynamin.gui.list_box;
+public import dynamin.gui.text_box;
+
+public import dynamin.gui.theme;
+public import dynamin.gui.basic_theme;
+public import dynamin.gui.windows_theme;
+
+void aHack() { Theme.current; }
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/basic_theme.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,183 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.basic_theme;
+
+import dynamin.core.string;
+import dynamin.all_painting;
+import dynamin.all_gui;
+import dynamin.core.math;
+
+static this() {
+	Theme.add(new BasicTheme());
+}
+
+/*
+ * Four colors used in this theme:
+ * - white (255, 255, 255)
+ * - black (0, 0, 0)
+ * - light gray (220, 220, 220)
+ * - dark gray (70, 70, 70)
+ */
+class BasicTheme : Theme {
+	string name() {
+		return "Basic";
+	}
+
+	void Window_paint(Window c, Graphics g) {
+		g.source = Color.White;
+		g.paint();
+	}
+	Size Button_bestSize(Button c) {
+		return Size(60, 25);
+	}
+
+	void Button_paint(Button c, Graphics g) {
+		with(g) {
+			if(c.state == ButtonState.Pressed)
+				g.source = Color.Black;
+			else
+				g.source = Color.White;
+			paint();
+
+			source = Color.Black;
+			rectangle(0.5, 0.5, c.width-1, c.height-1);
+			stroke();
+			if(c.state == ButtonState.Hot) {
+				rectangle(1.5, 1.5, c.width-3, c.height-3);
+				stroke();
+			}
+			if(c.state == ButtonState.Pressed)
+				g.source = Color.White;
+			else
+				g.source = c.foreColor;
+			c.paintFore(g);
+		}
+	}
+
+	void CheckBox_paint(CheckBox c, Graphics g) {
+		with(g) {
+			Point box = Point(2, cast(int)(c.height/2-6));
+			fontSize = 13;
+			drawText(c.text, box.x+18, 2);
+
+			source = c.state == ButtonState.Pressed ? Color.Black : Color.White;
+			rectangle(box.x, box.y, 13, 13);
+			fill();
+			source = Color.Black;
+			rectangle(box.x+0.5, box.y+0.5, 12, 12);
+			stroke();
+			if(c.state == ButtonState.Hot) {
+				rectangle(box.x+1.5, box.y+1.5, 10, 10);
+				stroke();
+			}
+
+			source = c.state == ButtonState.Pressed ? Color.White : Color.Black;
+			if(c.checked) {
+				moveTo(box.x+2.5, box.y+7.5);
+				relLineTo(2, 3);
+				relLineTo(6, -8);
+				stroke();
+			}
+		}
+	}
+	void RadioButton_paint(CheckBox c, Graphics g) {
+		with(g) {
+			int radius = 6;
+			Point circle = Point(2, cast(int)(c.height/2-radius));
+			fontSize = 13;
+			drawText(c.text, circle.x+18, 2);
+
+			source = c.state == ButtonState.Pressed ? Color.Black : Color.White;
+			ellipse(circle.x+radius, circle.y+radius, radius);
+			fill();
+			source = Color.Black;
+			if(c.state == ButtonState.Hot) {
+				lineWidth = 2;
+				ellipse(circle.x+radius, circle.y+radius, radius-1);
+			} else {
+				ellipse(circle.x+radius, circle.y+radius, radius-0.5);
+			}
+			stroke();
+			lineWidth = 1;
+
+			source = c.state == ButtonState.Pressed ? Color.White : Color.Black;
+			if(c.checked) {
+				ellipse(circle.x+radius, circle.y+radius, radius-4);
+				fill();
+			}
+		}
+	}
+
+	void ScrollBarTrack_paint(ScrollBarTrack c, Graphics g) {
+		if(c.state == ButtonState.Pressed)
+			g.paint();
+		else if(isOdd(cast(int)round(c.x)) || isOdd(cast(int)round(c.y)))
+			drawCheckerboard(g, 0, 0, c.width, c.height,
+				Color.White, Color.Black);
+		else
+			drawCheckerboard(g, 0, 0, c.width, c.height,
+				Color.Black, Color.White);
+	}
+
+	void ScrollBarThumb_paint(ScrollBarThumb c, Graphics g) {
+		with(g) {
+			source = Color.White;
+			paint();
+			source = Color.Black;
+			rectangle(0.5, 0.5, c.width-1, c.height-1);
+			stroke();
+		}
+	}
+
+	real ScrollBar_size() {
+		// TODO: all themes should get this from SystemGui.ScrollBarSize
+		return 18;
+	}
+
+	void ArrowButton_paint(ArrowButton c, Graphics g) {
+		Button_paint(c, g);
+	}
+
+	BorderSize Scrollable_borderSize(Scrollable c) {
+		return BorderSize(1, 1, 1, 1);
+	}
+
+	void Scrollable_paint(Scrollable c, Graphics g) {
+		g.source = Color.White;
+		g.paint();
+		g.source = Color.Black;
+		g.rectangle(0.5, 0.5, c.width-0.5, c.height-0.5);
+		g.stroke();
+	}
+
+	BorderSize Notebook_borderSize(Notebook c) {
+		return BorderSize(1, 1, 1, 1);
+	}
+	void Tab_paint(TabPage page, Notebook c, Graphics g){}
+	void Notebook_paint(Notebook c, Graphics g){}
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/button.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,153 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.button;
+
+import dynamin.all_core;
+import dynamin.all_gui;
+import dynamin.all_painting;
+import tango.io.Stdout;
+import dynamin.c.cairo; // TODO: remove
+
+// TODO: maybe change to ControlState and add Disabled ?
+enum ButtonState {
+	Normal, Hot, Pressed
+}
+// TODO: move to another file
+enum TextImageRelation {
+	Overlay, TextBeforeImage, ImageBeforeText, TextAboveImage, ImageAboveText
+}
+
+/**
+ * A button is a control that can be clicked to perform an action.
+ *
+ * The appearance of a button with Windows Classic:
+ *
+ * $(IMAGE ../web/example_button.png)
+ */
+class Button : Control {
+protected:
+	ButtonState _state;
+	override void whenMouseDragged(MouseEventArgs e) {
+		if(contains(e.x, e.y))
+			state = ButtonState.Pressed;
+		else
+			state = ButtonState.Normal;
+	}
+	override void whenMouseEntered(EventArgs e) {
+		state = ButtonState.Hot;
+	}
+	override void whenMouseLeft(EventArgs e) {
+		state = ButtonState.Normal;
+	}
+	override void whenMouseDown(MouseEventArgs e) {
+		if(e.button == MouseButton.Left) {
+			state = ButtonState.Pressed;
+			focus();
+		}
+	}
+	override void whenMouseUp(MouseEventArgs e) {
+		if(state != ButtonState.Pressed) return;
+		state = ButtonState.Hot;
+		clicked(new EventArgs);
+	}
+	override void whenKeyDown(KeyEventArgs e) {
+		if(e.key == Key.Space)
+			state = ButtonState.Pressed;
+	}
+	override void whenKeyUp(KeyEventArgs e) {
+		if(e.key == Key.Space) {
+			if(state != ButtonState.Pressed) return;
+			state = ButtonState.Normal;
+			clicked(new EventArgs);
+		}
+	}
+	override void whenPainting(PaintingEventArgs e) {
+		Theme.current.Button_paint(this, e.graphics);
+		return;
+		// TODO: move to a theme or delete
+		with(e.graphics) {
+			if(_state == ButtonState.Hot)
+				source = Color(200, 0, 0);
+			else if(_state == ButtonState.Pressed)
+				source = Color(110, 0, 0);
+			else
+				source = Color(220, 80, 80);
+			moveTo(3, 0);
+			lineTo(width-3, 0);
+			lineTo(width, 3);
+			lineTo(width, height-3);
+			lineTo(width-3, height);
+			lineTo(3, height);
+			lineTo(0, height-3);
+			lineTo(0, 3);
+			closePath();
+			fill();
+			auto grad = cairo_pattern_create_linear(0, 0, 0, height/2+3);
+			cairo_pattern_add_color_stop_rgba(grad, 0, 1, 1, 1, .4);
+			cairo_pattern_add_color_stop_rgba(grad, 1, 1, 1, 1, .05);
+			cairo_set_source(handle, grad);
+			cairo_pattern_destroy(grad);
+			moveTo(3, 0);
+			lineTo(width-3, 0);
+			lineTo(width, 3);
+			lineTo(width, cast(int)height/2-3);
+			lineTo(width-3, cast(int)height/2);
+			lineTo(3, cast(int)height/2);
+			lineTo(0, cast(int)height/2+3);
+			lineTo(0, 3);
+			closePath();
+			fill();
+
+			source = _state == ButtonState.Pressed ? Color.White : Color.Black;
+			fontSize = 13;
+			drawText(text, 5, height/2);
+		}
+		return;
+
+	}
+	public void paintFore(Graphics g) {
+		auto extents = g.getTextExtents(text);
+		g.drawText(text, (width-extents.width)/2, (height-extents.height)/2);
+	}
+
+public:
+	/// Override this method in a subclass to handle the Clicked event.
+	protected void whenClicked(EventArgs e) { }
+	/// This event occurs after the button has been clicked.
+	Event!() clicked;
+
+	this() {
+		clicked = new Event!()(&whenClicked);
+		_focusable = true;
+	}
+	this(string text) {
+		this();
+		this.text = text;
+	}
+	override Size bestSize() { return Theme.current.Button_bestSize(this); }
+	ButtonState state() { return _state; }
+	void state(ButtonState s) { _state = s; repaint(); }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/check_box.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,88 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.check_box;
+
+import dynamin.all_core;
+import dynamin.all_gui;
+import dynamin.all_painting;
+import tango.io.Stdout;
+
+enum CheckState {
+	///
+	Unchecked,
+	///
+	Checked,
+	///
+	Both
+}
+
+/**
+ * A control that can be checked or unchecked independent of other controls.
+ *
+ * The appearance of a check box with Windows Classic:
+ *
+ * $(IMAGE ../web/example_check_box.png)
+ */
+class CheckBox : Button {
+protected:
+	CheckState _checkState = CheckState.Unchecked;
+	override void whenClicked(EventArgs e) {
+		checked = !checked;
+		focus();
+	}
+	override void whenPainting(PaintingEventArgs e) {
+		Theme.current.CheckBox_paint(this, e.graphics);
+	}
+
+public:
+	/// This event occurs after .
+	Event!() checkedChanged;
+	/// Override this method in a subclass to handle the SelectedChanged event.
+	protected void whenCheckedChanged(EventArgs e) { }
+	this() {
+		checkedChanged = new Event!()(&whenCheckedChanged);
+		_focusable = true;
+	}
+	this(string text) {
+		this();
+		this.text = text;
+	}
+	bool checked() {
+		return _checkState == CheckState.Checked;
+	}
+	void checked(bool b) {
+		_checkState = b ? CheckState.Checked : CheckState.Unchecked;
+		repaint();
+		checkedChanged(new EventArgs);
+	}
+	override Size bestSize() {
+		return Size(70, 15);
+	}
+	override void paintFore(Graphics g) {
+		g.drawText(text, 0, (height-g.getTextExtents(text).height)/2);
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/clipboard.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,105 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.clipboard;
+
+import dynamin.core.string;
+import dynamin.gui.window;
+import dynamin.gui_backend;
+import tango.io.Stdout;
+
+/*
+ * Goals of this class:
+ * - Allow copying and pasting of plain text, 32 bpp images, file lists,
+ *   formatted text, and user defined data
+ * - As this does not depend on the audio classes, copying audio data should
+ *   be handled in the audio code.
+ */
+///
+static class Clipboard {
+static:
+private:
+	mixin ClipboardBackend;
+public:
+	/**
+	 * This event occurs after the data on clipboard has been changed.
+	 * Note: This event will not always be fired when the data on the clipboard
+	 * changes, as some backends have no way of being notified of such an event.
+	 */
+	//Event!() DataChanged;
+	static this() {
+		//DataChanged = new Event!()(null);
+	}
+
+	/**
+	 * Sets the data on the clipboard to the specified _text.
+	 */
+	void setText(string text) {
+		backend_setText(text);
+	}
+	alias setText copyText;
+	/**
+	 * Gets text from the clipboard. If the clipboard does not have any text
+	 * or has an empty string, null is returned.
+	 */
+	string getText() {
+		return backend_getText();
+	}
+	alias getText pasteText;
+	// TODO: don't need this?
+	bool containsText() {
+		return backend_containsText();
+	}
+
+}
+
+/**
+ * On the X window system, this holds the current selection. Whenever the
+ * selection is changed in a program, X's selection should be updated using
+ * this class.
+ * On Windows, this class does not do anything.
+ *
+ * See the Clipboard documentation for a description of the functionality
+ * of this class.
+ */
+static class Selection {
+static:
+private:
+	mixin SelectionBackend;
+public:
+	///
+	void setText(string text) {
+		backend_setText(text);
+	}
+	///
+	string getText() {
+		return backend_getText();
+	}
+	///
+	bool containsText() {
+		return backend_containsText();
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/container.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,266 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.container;
+
+import dynamin.all_core;
+import dynamin.all_painting;
+import dynamin.all_gui;
+import dynamin.gui.control;
+import dynamin.gui.events;
+import tango.io.Stdout;
+
+alias List!(Control) ControlList;
+
+///
+class Container : Control {
+protected:
+	ControlList _children;
+	Size _minSize;
+	Size _maxSize;
+
+	override void whenResized(EventArgs e) {
+		layout();
+	}
+public:
+	/// This event occurs after the control's minimum size has been changed.
+	Event!() minSizeChanged;
+	/// Override this method in a subclass to handle the minSizeChanged event.
+	protected void whenMinSizeChanged(EventArgs e) { }
+
+	/// This event occurs after the control's maximum size has been changed.
+	Event!() maxSizeChanged;
+	/// Override this method in a subclass to handle the maxSizeChanged event.
+	protected void whenMaxSizeChanged(EventArgs e) { }
+
+	this() {
+		minSizeChanged = new Event!()(&whenMinSizeChanged);
+		maxSizeChanged = new Event!()(&whenMaxSizeChanged);
+		_children = new ControlList();
+
+		elasticX = true;
+		elasticY = true;
+	}
+	override void dispatchPainting(PaintingEventArgs e) {
+		super.dispatchPainting(e);
+		foreach(c; _children) {
+			e.graphics.save();
+			e.graphics.translate(c.x, c.y);
+			c.setupGraphics(e.graphics);
+			c.painting(e);
+			e.graphics.restore();
+		}
+	}
+	// TODO: make these use common code...get rid of copy and paste
+	override void dispatchMouseDown(MouseEventArgs e) {
+		auto c = getChildAtPoint(e.location);
+		if(c && getCaptorControl() !is this) {
+			e.location = e.location - c.location;
+			c.mouseDown(e);
+			e.location = e.location + c.location;
+		} else {
+			super.dispatchMouseDown(e);
+		}
+	}
+	override void dispatchMouseUp(MouseEventArgs e) {
+		auto c = getChildAtPoint(e.location);
+		if(c && getCaptorControl() !is this) {
+			e.location = e.location - c.location;
+			c.mouseUp(e);
+			e.location = e.location + c.location;
+		} else {
+			super.dispatchMouseUp(e);
+		}
+	}
+	override void dispatchMouseMoved(MouseEventArgs e) {
+		auto c = getChildAtPoint(e.location);
+		if(c && getCaptorControl() !is this) {
+			e.location = e.location - c.location;
+			c.mouseMoved(e);
+			e.location = e.location + c.location;
+		} else {
+			super.dispatchMouseMoved(e);
+		}
+	}
+	override void dispatchMouseDragged(MouseEventArgs e) {
+		auto c = getChildAtPoint(e.location);
+		if(c && getCaptorControl() !is this) {
+			e.location = e.location - c.location;
+			c.mouseDragged(e);
+			e.location = e.location + c.location;
+		} else {
+			super.dispatchMouseDragged(e);
+		}
+	}
+	/**
+	 * Gets the child control at the specified point. If there are
+	 * multiple child controls at the point, the topmost control is returned.
+	 * If there is no child control at the point, null is returned. The returned
+	 * control, if any, is a direct child of this container.
+	 */
+	Control getChildAtPoint(real[] pt) {
+		assert(pt.length == 2, "pt must be just an x and y");
+		return getChildAtPoint(Point(pt[0], pt[1]));
+	}
+	/// ditto
+	Control getChildAtPoint(real x, real y) {
+		return getChildAtPoint(Point(x, y));
+	}
+	/// ditto
+	Control getChildAtPoint(Point pt) {
+		for(int i = _children.count-1; i >= 0; --i) {
+			pt = pt - _children[i].location;
+			scope(exit) pt = pt + _children[i].location;
+			if(_children[i].contains(pt))
+				return _children[i];
+		}
+		return null;
+	}
+	/**
+	 * Never returns null. If there is no descendant at the specified point,
+	 * this container will be returned.
+	 */
+	Control getDescendantAtPoint(real[] pt) {
+		assert(pt.length == 2, "pt must be just an x and y");
+		return getDescendantAtPoint(Point(pt[0], pt[1]));
+	}
+	/// ditto
+	Control getDescendantAtPoint(real x, real y) {
+		return getDescendantAtPoint(Point(x, y));
+	}
+	/// ditto
+	Control getDescendantAtPoint(Point pt) {
+		Container des = this;
+		while(true) {
+			auto child = des.getChildAtPoint(pt);
+			if(!child)
+				return des;
+			auto isContainer = cast(Container)child;
+			if(isContainer) {
+				des = isContainer;
+				pt = pt - des.location;
+				// loop around with this container
+			} else {
+				return child;
+			}
+		}
+	}
+	/**
+	 * Gets or sets the minimum size of this window. A minimum width or
+	 * height of 0 means that there is no minimum width or height.
+	 * The default is Size(0, 0).
+	 */
+	Size minSize() { return _minSize; }
+	/// ditto
+	void minSize(Size size) {
+		_minSize = size;
+		minSizeChanged(new EventArgs);
+	}
+	/// ditto
+	void minSize(real[] size) {
+		assert(size.length == 2, "size must be just a width and height");
+		minSize = Size(size[0], size[1]);
+	}
+	///
+	real minWidth() { return _minSize.width; }
+	///
+	real minHeight() { return _minSize.height; }
+	/**
+	 * Gets or sets the maximum size of this window. A maximum width or
+	 * height of 0 means that there is no maximum width or height.
+	 * The default is Size(0, 0).
+	 */
+	Size maxSize() { return _maxSize; }
+	/// ditto
+	void maxSize(Size size) {
+		_maxSize = size;
+		minSizeChanged(new EventArgs);
+	}
+	/// ditto
+	void maxSize(real[] size) {
+		assert(size.length == 2, "size must be just a width and height");
+		maxSize = Size(size[0], size[1]);
+	}
+	///
+	real maxWidth() { return _maxSize.width; }
+	///
+	real maxHeight() { return _maxSize.height; }
+	/**
+	 * Causes this container to position its child controls. Called on every
+	 * resize. Usually, this function will get each child's best size, and
+	 * then set each child's location and height. The definition in Container
+	 * is empty, as it is intended for subclasses to override.
+	 */
+	void layout() {
+	}
+	protected void add(Control child) {
+		if(child.parent)
+			child.parent.remove(child);
+		_children.add(child);
+		child.parent = this;
+		repaint();
+		//ControlAdded(EventArgs e); // TODO: add event
+	}
+	protected void remove(Control child) {
+		_children.remove(child);
+		child.parent = null;
+		repaint();
+		//ControlRemoved(EventArgs e); // TODO: add event
+	}
+	protected int opApply(int delegate(inout Control item) dg) {
+		for(uint i = 0; i < _children.count; ++i) {
+			auto tmp = _children[i];
+			if(int result = dg(tmp))
+				return result;
+		}
+		return 0;
+	}
+	protected int opApply(int delegate(inout uint index, inout Control item) dg) {
+		for(uint i = 0; i < _children.count; ++i) {
+			auto tmp = _children[i];
+			if(int result = dg(i, tmp))
+				return result;
+		}
+		return 0;
+	}
+}
+
+// TODO: calling panel.children.add(button) will cause a crash
+// because the button's parent is not set to the panel
+// need to add a change handler on children?
+class Panel : Container {
+	ControlList children() { return _children; }
+	void add(Control child) { super.add(child); };
+	void remove(Control child) { super.remove(child); };
+	int opApply(int delegate(inout Control item) dg) {
+		return super.opApply(dg);
+	}
+	int opApply(int delegate(inout uint index, inout Control item) dg) {
+		return super.opApply(dg);
+	}
+//	override protected void whenPainting() {
+//	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/control.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,516 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.control;
+
+import dynamin.all_core;
+import dynamin.all_painting;
+import dynamin.gui.container;
+import dynamin.gui.events;
+import dynamin.gui.window;
+import dynamin.gui.cursor;
+import tango.io.Stdout;
+
+Control hotControl;
+// the hot control is the one the mouse is over
+package void setHotControl(Control c) {
+	if(c !is hotControl) {
+		if(hotControl)
+			hotControl.mouseLeft(new EventArgs);
+		hotControl = c;
+		if(c)
+			c.mouseEntered(new EventArgs);
+	}
+}
+package Control getHotControl() { return hotControl; }
+Control captorControl;
+package void setCaptorControl(Control c) {
+	captorControl = c;
+}
+package Control getCaptorControl() { return captorControl; }
+
+/**
+ * The painting event is an exception to the rule that added handlers are called
+ * before whenPainting. The painting event is far more useful since
+ * added handles are called after the control's whenPainting has finished.
+ * Otherwise, anything handlers painted would likely be painted over when the
+ * control painted.
+ */
+class Control {
+protected:
+	bool _visible;
+	bool _focusable;
+	bool _focused;
+	package Point _location;
+	package Size _size;
+	string _text;
+	Color _backColor;
+	Color _foreColor;
+	Font _font;
+	Cursor _cursor;
+	Container _parent;
+	bool _elasticX, _elasticY;
+public:
+	/// This event occurs after the control has been moved.
+	Event!() moved;
+	/// Override this method in a subclass to handle the Moved event.
+	protected void whenMoved(EventArgs e) { }
+
+	/// This event occurs after the control has been resized.
+	Event!() resized;
+	/// Override this method in a subclass to handle the Resized event.
+	protected void whenResized(EventArgs e) { }
+
+	/// This event occurs after the mouse has entered the control.
+	Event!() mouseEntered;
+	/// Override this method in a subclass to handle the MouseEntered event.
+	protected void whenMouseEntered(EventArgs e) { }
+
+	/// This event occurs after the mouse has left the control.
+	Event!() mouseLeft;
+	/// Override this method in a subclass to handle the MouseLeft event.
+	protected void whenMouseLeft(EventArgs e) { }
+
+	/// This event occurs after a mouse button is pressed.
+	Event!(MouseEventArgs) mouseDown;
+	/// Override this method in a subclass to handle the MouseDown event.
+	protected void whenMouseDown(MouseEventArgs e) { }
+
+	/// This event occurs after a mouse button is released.
+	Event!(MouseEventArgs) mouseUp;
+	/// Override this method in a subclass to handle the MouseUp event.
+	protected void whenMouseUp(MouseEventArgs e) { }
+
+	/// This event occurs after the mouse has been moved.
+	Event!(MouseEventArgs) mouseMoved;
+	/// Override this method in a subclass to handle the MouseMoved event.
+	protected void whenMouseMoved(MouseEventArgs e) { }
+
+	/// This event occurs after the mouse has been moved.
+	Event!(MouseEventArgs) mouseDragged;
+	/// Override this method in a subclass to handle the MouseMoved event.
+	protected void whenMouseDragged(MouseEventArgs e) { }
+
+	/// This event occurs after the mouse wheel has been turned.
+	Event!(MouseTurnedEventArgs) mouseTurned;
+	/// Override this method in a subclass to handle the MouseTurned event.
+	protected void whenMouseTurned(MouseTurnedEventArgs e) { }
+
+	/// This event occurs after a key is pressed.
+	Event!(KeyEventArgs) keyDown;
+	/// Override this method in a subclass to handle the KeyDown event.
+	protected void whenKeyDown(KeyEventArgs e) { }
+
+	/// This event occurs after a character key is pressed.
+	Event!(KeyTypedEventArgs) keyTyped;
+	/// Override this method in a subclass to handle the KeyTyped event.
+	protected void whenKeyTyped(KeyTypedEventArgs e) { }
+
+	/// This event occurs after a key is released.
+	Event!(KeyEventArgs) keyUp;
+	/// Override this method in a subclass to handle the KeyUp event.
+	protected void whenKeyUp(KeyEventArgs e) { }
+
+	/// This event occurs when the control needs to be painted.
+	Event!(PaintingEventArgs) painting;
+	/// Override this method in a subclass to handle the painting event.
+	protected void whenPainting(PaintingEventArgs e) {
+		e.graphics.source = backColor;
+		e.graphics.paint();
+	}
+
+	protected void dispatchMouseEntered(EventArgs e) {
+		Cursor.setCurrent(this, _cursor);
+		mouseEntered.callHandlers(e);
+		mouseEntered.callMainHandler(e);
+	}
+	protected void dispatchMouseDown(MouseEventArgs e) {
+		setCaptorControl(this);
+		mouseDown.callHandlers(e);
+		mouseDown.callMainHandler(e);
+	}
+	protected void dispatchMouseUp(MouseEventArgs e) {
+		setCaptorControl(null);
+		mouseUp.callHandlers(e);
+		mouseUp.callMainHandler(e);
+	}
+	protected void dispatchMouseMoved(MouseEventArgs e) {
+		setHotControl(this);
+		mouseMoved.callHandlers(e);
+		mouseMoved.callMainHandler(e);
+	}
+	protected void dispatchMouseDragged(MouseEventArgs e) {
+		setHotControl(this);
+		mouseDragged.callHandlers(e);
+		mouseDragged.callMainHandler(e);
+	}
+	protected void dispatchMouseTurned(MouseTurnedEventArgs e) {
+		mouseTurned.callHandlers(e);
+		mouseTurned.callMainHandler(e);
+		if(!e.stopped && _parent)
+			_parent.mouseTurned(e);
+	}
+	protected void dispatchPainting(PaintingEventArgs e) {
+		e.graphics.save();
+		painting.callMainHandler(e);
+		e.graphics.restore();
+		e.graphics.save();
+		// TODO: every call to a handler should be wrapped in a save/restore
+		painting.callHandlers(e);
+		e.graphics.restore();
+	}
+	this() {
+		moved = new Event!()(&whenMoved);
+		resized = new Event!()(&whenResized);
+		mouseEntered = new Event!()(&whenMouseEntered, &dispatchMouseEntered);
+		mouseLeft = new Event!()(&whenMouseLeft);
+		mouseDown = new Event!(MouseEventArgs)(&whenMouseDown, &dispatchMouseDown);
+		mouseUp = new Event!(MouseEventArgs)(&whenMouseUp, &dispatchMouseUp);
+		mouseMoved = new Event!(MouseEventArgs)(&whenMouseMoved, &dispatchMouseMoved);
+		mouseDragged = new Event!(MouseEventArgs)(&whenMouseDragged, &dispatchMouseDragged);
+		mouseTurned = new Event!(MouseTurnedEventArgs)(&whenMouseTurned, &dispatchMouseTurned);
+		keyDown = new Event!(KeyEventArgs)(&whenKeyDown);
+		keyTyped = new Event!(KeyTypedEventArgs)(&whenKeyTyped);
+		keyUp = new Event!(KeyEventArgs)(&whenKeyUp);
+		painting = new Event!(PaintingEventArgs)(&whenPainting, &dispatchPainting);
+
+		_location = Point(0, 0);
+		_size = Size(100, 100);
+		_text = "";
+		_focusable = false;
+		_focused = false;
+		_visible = true;
+		_cursor = Cursor.Arrow;
+		_elasticX = false;
+		_elasticY = false;
+
+		// TODO: remove these when themes mature
+		_foreColor = Color.Black;
+		_font = new Font("Tahoma", 11);
+	}
+	protected Graphics quickCreateGraphics() {
+		if(_parent is null)
+			return null;
+		auto g = _parent.quickCreateGraphics();
+		g.translate(location);
+		return g;
+	}
+	/**
+	 * Sets the specified Graphics' font and source to this control's font
+	 * and foreground color. Also, clips to this control's rectangle.
+	 */
+	void setupGraphics(Graphics g) {
+		g.rectangle(0, 0, width, height);
+		g.clip();
+		g.font = font;
+		g.source = foreColor;
+	}
+	/**
+	 * Creates a Graphics, calls the specified delegate with it, and deletes
+	 * it to release resources.
+	 */
+	void withGraphics(void delegate(Graphics g) dg) {
+		auto g = quickCreateGraphics();
+		setupGraphics(g);
+		dg(g);
+		delete g;
+	}
+	/**
+	 *
+	 */
+	bool focused() {
+		return _focused;
+	}
+	/**
+	 *
+	 */
+	void focus() {
+		if(!_focusable)
+			return;
+		Control c = this;
+		while(c.parent)
+			c = c.parent;
+		if(auto win = cast(Window)c) {
+			if(win.focusedControl) {
+				win.focusedControl._focused = false;
+				win.focusedControl.repaint();
+			}
+			win.focusedControl = this;
+			_focused = true;
+			repaint();
+		}
+	}
+	/**
+	 * Returns whether this control is on the screen. A control is
+	 * on screen if one of its ancestors is a top level window. Whether or
+	 * not the control is visible has no bearing on this value. If a control
+	 * has no parent, then it is clearly not on the screen.
+	 */
+	bool onScreen() {
+		return _parent && _parent.onScreen;
+	}
+	/**
+	 * Gets the location of this control in screen coordinates. An exception is
+	 * thrown if this control is not on the screen.
+	 */
+	Point screenLocation() {
+		if(!_parent)
+			throw new Exception("control is not on screen");
+		return _parent.screenLocation + location;
+	}
+	/**
+	 * Converts the specified point in content coordinates into screen
+	 * coordinates. An exception is thrown if this control is not on the screen.
+	 */
+	Point contentToScreen(Point pt) { // TODO: content?? even on Window??
+		if(!_parent)
+			throw new Exception("control is not on screen");
+		return _parent.contentToScreen(pt + location);
+	}
+	/**
+	 * Converts the specified point in screen coordinates into content
+	 * coordinates. An exception is thrown if this control is not on the screen.
+	 */
+	Point screenToContent(Point pt) {
+		if(!_parent)
+			throw new Exception("control is not on screen");
+		return _parent.screenToContent(pt) - location; // TODO: borders
+	}
+	/**
+	 * Converts the specified point in this control's content coordinates
+	 * into the specified control's content coordinates. An exception is
+	 * thrown if this control is not on the screen.
+	 */
+	Point contentToContent(Point pt, Control c) {
+		return c.screenToContent(contentToScreen(pt));
+	}
+	/**
+	 * Returns whether the specified point is inside this control.
+	 */
+	bool contains(Point pt) {
+		return pt.x >= 0 && pt.y >= 0 && pt.x < width && pt.y < height;
+	}
+	/// ditto
+	bool contains(real x, real y) {
+		return contains(Point(x, y));
+	}
+	bool topLevel() { return false; }
+	// TODO: return NativeControl/Window?
+	Control getTopLevel() {
+		Control c = this;
+		while(c.parent)
+			c = c.parent;
+		return c.topLevel ? c : null;
+	}
+	/**
+	 * Gets this control's parent.
+	 */
+	Container parent() { return _parent; }
+	package void parent(Container c) {
+		_parent = c;
+		//ParentChanged(new EventArgs); // TODO: add event
+	}
+	/**
+	 * Gets or sets whether is control is visible. The default is true, except on top-level windows.
+	 */
+	//void visible(bool b) { visible = b; }
+	bool visible() { return _visible; }
+
+	/**
+	 * Gets or sets the location of this control in its parent's content
+	 * coordinates.
+	 * Examples:
+	 * -----
+	 * control.location = [5, 35];
+	 * control.location = Point(50, 50);
+	 * -----
+	 */
+	Point location() { return _location; }
+	/// ditto
+	void location(Point pt) {
+		if((cast(Window)_parent) !is null)
+			throw new Exception("cannot set location of a window's content");
+		pt.x = round(pt.x);
+		pt.y = round(pt.y);
+		repaint();
+		_location = pt;
+		repaint();
+		moved(new EventArgs);
+	}
+	/// ditto
+	void location(real[] pt) {
+		assert(pt.length == 2, "pt must be just an x and y");
+		location = Point(pt[0], pt[1]);
+	}
+
+	/**
+	 * Gets or sets the size of this control.
+	 * Examples:
+	 * -----
+	 * control.size = [75, 23];
+	 * control.size = Size(80, 20);
+	 * -----
+	 */
+	Size size() { return _size; }
+	/// ditto
+	void size(Size newSize) {
+		if(newSize.width < 0)
+			newSize.width = 0;
+		if(newSize.height < 0)
+			newSize.height = 0;
+		newSize.width = round(newSize.width);
+		newSize.height = round(newSize.height);
+		repaint();
+		_size = newSize;
+		repaint();
+		resized(new EventArgs);
+	}
+	/// ditto
+	void size(real[] newSize) {
+		assert(newSize.length == 2, "size must be just a width and height");
+		size = Size(newSize[0], newSize[1]);
+	}
+
+	/**
+	 * Gets the size at which this control looks the best. It is intended that
+	 * the control not be made smaller than this size, and only be made larger
+	 * if it is elastic, or if it needs to be aligned with other controls.
+	 *
+	 * This property should be overridden in subclasses to return a best size.
+	 */
+	Size bestSize() { return Size(100, 100); }
+	/**
+	 * Gets the distance from the top of this control to the baseline of
+	 * the first line of this control's text. If this control does not have
+	 * text, 0 may be returned.
+	 */
+	int baseline() { return 0; }
+
+	/// Same as location.x
+	real x() { return location.x; }
+	/// Same as location.y
+	real y() { return location.y; }
+	/// Same as size.width
+	real width() { return size.width; }
+	/// Same as size.height
+	real height() { return size.height; }
+
+	/**
+	 * Gets or sets whether this control is elastic horizontally or vertically.
+	 * If a control is elastic, then it is intended to be made larger than its
+	 * best size.
+	 */
+	bool elasticX() { return _elasticX; }
+	/// ditto
+	void elasticX(bool b) { _elasticX = b; }
+	/// ditto
+	bool elasticY() { return _elasticY; }
+	/// ditto
+	void elasticY(bool b) { _elasticY = b; }
+	/**
+	 * Gets or sets the text that this control shows.
+	 */
+	string text() { return _text.dup; }
+	/// ditto
+	void text(string str) {
+		_text = str.dup;
+		repaint();
+		//TextChanged(EventArgs e) // TODO: add event
+	}
+
+	/**
+	 * Gets or sets the background color of this control.
+	 */
+	Color backColor() { return _backColor; }
+	/// ditto
+	void backColor(Color c) {
+		_backColor = c;
+		repaint();
+	}
+
+	/**
+	 * Gets or sets the foreground color of this control.
+	 */
+	Color foreColor() { return _foreColor; }
+	/// ditto
+	void foreColor(Color c) {
+		_foreColor = c;
+		repaint();
+	}
+
+	/**
+	 * Gets or sets the font of this control uses to display text. A value of
+	 * null means that the font is unset. When the font is null, the
+	 * current theme's font is used. The default is null.
+	 */
+	Font font() {
+		// TODO: if font is null (unset), return from theme
+		//if(font is null)
+		//	return Theme.Current.Control_Font(this);
+		//else
+		return _font;
+	}
+	/// ditto
+	void font(Font f) {
+		_font = f;
+		repaint();
+	}
+	Cursor cursor() {
+		return _cursor;
+	}
+	void cursor(Cursor cur) {
+		if(_cursor is cur)
+			return;
+		_cursor = cur;
+		if(getHotControl() is this)
+			Cursor.setCurrent(this, _cursor);
+	}
+
+	/**
+	 * Causes the part of the control inside the specified
+	 * rectangle to be repainted. The rectangle is in content coordinates.
+	 *
+	 * The control will not be repainted before this method returns; rather,
+	 * the area is just marked as needing to be repainted. The next time there
+	 * are no other system events to be processed, a painting event will called.
+	 */
+	void repaint(Rect rect) {
+		// TODO: make sure that parts clipped off by the parent are not invalidated
+		if(_parent)
+			_parent.repaint(rect + location);
+	}
+	/// ditto
+	void repaint(real x, real y, real width, real height) {
+		repaint(Rect(x, y, width, height));
+	}
+	/**
+	 * Causes the entire control to be repainted.
+	 */
+	void repaint() {
+		repaint(Rect(0, 0, width, height));
+	}
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/cursor.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,200 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.cursor;
+
+import tango.io.Stdout;
+import dynamin.gui_backend;
+
+///
+class Cursor {
+private:
+	mixin CursorBackend;
+public:
+static:
+	void setCurrent(Control c, Cursor cur) {
+		backend_SetCurrent(c, cur);
+	}
+	/**
+	 * Gets a blank cursor used for an activity, such as typing, with which
+	 * the cursor may interfere.
+	 */
+	Cursor None() {
+		return backend_None;
+	}
+	/**
+	 * Gets the cursor used normally.
+	 *
+	 * The appearance with Windows XP:
+	 *
+	 * $(IMAGE ../web/example_arrow_cursor.png)
+	 */
+	Cursor Arrow() {
+		return backend_Arrow;
+	}
+	/**
+	 * Gets the cursor used when the computer is accomplishing some
+	 * task that does not prevent the user from continuing work.
+	 *
+	 * The appearance with Windows XP:
+	 *
+	 * $(IMAGE ../web/example_wait_arrow_cursor.png)
+	 */
+	Cursor WaitArrow() {
+		return backend_WaitArrow;
+	}
+	/**
+	 * Gets the cursor used when the computer is accomplishing some
+	 * task that prevents the user from continuing work.
+	 *
+	 * The appearance with Windows XP:
+	 *
+	 * $(IMAGE ../web/example_wait_cursor.png)
+	 */
+	Cursor Wait() {
+		return backend_Wait;
+	}
+	/**
+	 * Gets the cursor used when the mouse is over selectable text.
+	 * In text that supports dragging, this should not be used when the
+	 * mouse is over already selected text, as the text is not then
+	 * selectable, but draggable.
+	 *
+	 * The appearance with Windows XP:
+	 *
+	 * $(IMAGE ../web/example_text_cursor.png)
+	 */
+	Cursor Text() {
+		return backend_Text;
+	}
+	/**
+	 * Gets the cursor used when the mouse is over a link.
+	 *
+	 * The appearance with Windows XP:
+	 *
+	 * $(IMAGE ../web/example_hand_cursor.png)
+	 */
+	Cursor Hand() {
+		return backend_Hand;
+	}
+	/**
+	 * Gets the cursor used when moving something.
+	 *
+	 * The appearance with Windows XP:
+	 *
+	 * $(IMAGE ../web/example_move_cursor.png)
+	 */
+	Cursor Move() {
+		return backend_Move;
+	}
+	/**
+	 * Gets the cursor used when resizing the left or right
+	 * sides of something.
+	 *
+	 * The appearance with Windows XP:
+	 *
+	 * $(IMAGE ../web/example_resize_horiz_cursor.png)
+	 */
+	Cursor ResizeHoriz() {
+		return backend_ResizeHoriz;
+	}
+	/**
+	 * Gets the cursor used when resizing the top or bottom
+	 * sides of something.
+	 *
+	 * The appearance with Windows XP:
+	 *
+	 * $(IMAGE ../web/example_resize_vert_cursor.png)
+	 */
+	Cursor ResizeVert() {
+		return backend_ResizeVert;
+	}
+	/**
+	 * Gets the cursor used when resizing the top-left or bottom-right
+	 * corners of something.
+	 *
+	 * The appearance with Windows XP:
+	 *
+	 * $(IMAGE ../web/example_resize_backslash_cursor.png)
+	 */
+	Cursor ResizeBackslash() {
+		return backend_ResizeBackslash;
+	}
+	/**
+	 * Gets the cursor used when resizing the bottom-left or top-right
+	 * corners of something.
+	 *
+	 * The appearance with Windows XP:
+	 *
+	 * $(IMAGE ../web/example_resize_slash_cursor.png)
+	 */
+	Cursor ResizeSlash() {
+		return backend_ResizeSlash;
+	}
+	/**
+	 * Gets the cursor used when the mouse is over something that
+	 * will accept what has been dragged.
+	 *
+	 * The appearance with Windows XP:
+	 *
+	 * $(IMAGE ../web/example_drag_cursor.png)
+	 */
+	Cursor Drag() {
+		return backend_Drag;
+	}
+	/**
+	 * Gets the cursor used when the mouse is over something that
+	 * will not accept what has been dragged.
+	 *
+	 * The appearance with Windows XP:
+	 *
+	 * $(IMAGE ../web/example_invalid_drag_cursor.png)
+	 */
+	Cursor InvalidDrag() {
+		return backend_InvalidDrag;
+	}
+	/**
+	 * Gets the cursor used when the mouse is over a gutter that
+	 * will select a line of text when clicked.
+	 *
+	 * The appearance with Windows XP:
+	 *
+	 * $(IMAGE ../web/example_reversed_arrow_cursor.png)
+	 */
+	Cursor ReversedArrow() {
+		return backend_ReversedArrow;
+	}
+	/**
+	 * Gets the cursor that is sometimes used for selecting.
+	 *
+	 * The appearance with Windows XP:
+	 *
+	 * $(IMAGE ../web/example_crosshair_cursor.png)
+	 */
+	Cursor Crosshair() {
+		return backend_Crosshair;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/directory_dialog.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,91 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.directory_dialog;
+
+import dynamin.c.windows;
+import dynamin.all_core;
+import dynamin.gui.window;
+import tango.io.Stdout;
+import Utf = tango.text.convert.Utf;
+//import std.c.windows.com;
+
+/**
+ *
+ *
+ * The appearance of a directory dialog with Windows Classic:
+ *
+ * $(IMAGE ../web/example_directory_dialog.png)
+ */
+class DirectoryDialog { // use FILE_CHOOSER_ACTION_SELECT_FOLDER
+private:
+	string _directory;
+public:
+	string directory() {
+		return _directory;
+	}
+	void directory(string str) {
+		throw new Exception("Sorry, DirectoryDialog.directory(string) not yet working");
+		_directory = str;
+	}
+	DialogResult showDialog() {
+		BROWSEINFO bi;
+		//bi.hwndOwner = ;
+		bi.lpszTitle = "Choose a folder:";
+		bi.ulFlags |= BIF_RETURNONLYFSDIRS;
+		bi.ulFlags |= BIF_USENEWUI;
+
+		// TODO: I can't get this #!@#! COM stuff working...
+		//IShellFolder* shf;
+		//SHGetDesktopFolder(&shf);
+		//ITEMIDLIST* pidlInit;
+		//shf.ParseDisplayName(null, null, toWcharPointer(directory), null, &pidlInit, null);
+		//shf.Release();
+		//bi.pidlRoot = pidlInit;
+
+		ITEMIDLIST* pidl = SHBrowseForFolder(&bi);
+		//CoTaskMemFree(pidlInit);
+		if(!pidl)
+			return DialogResult.Cancel;
+		wchar[MAX_PATH+1] dirBuffer; // MAX_PATH is 260
+		if(!SHGetPathFromIDList(pidl, dirBuffer.ptr)) {
+			Stdout("GetPathFromIDList() failed").newline;
+			return DialogResult.Cancel;
+		}
+		CoTaskMemFree(pidl);
+		int index = MAX_PATH;
+		foreach(i, c; dirBuffer)
+			if(c == 0) { // find first null
+				index = i;
+				if(dirBuffer[i-1] != '\\') {
+					dirBuffer[i] = '\\';
+					index++;
+				}
+				break;
+			}
+		_directory = Utf.toString(dirBuffer[0..index]);
+		return DialogResult.OK;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/events.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,157 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.events;
+
+import dynamin.all_core;
+import dynamin.all_painting;
+import dynamin.all_gui;
+
+///
+enum MouseButton {
+	None,     ///
+	Left,     ///
+	Right,    ///
+	Middle,   ///
+	XButton1, ///
+	XButton2  ///
+}
+
+///
+class PaintingEventArgs : EventArgs {
+	Graphics g;
+	//NativeGraphics ng;
+public:
+	///
+	this(Graphics g) {
+		this.g = g;
+	}
+	///
+	Graphics graphics() { return g; }
+}
+
+///
+class MouseEventArgs : StopEventArgs {
+	Point _location;
+	MouseButton _button;
+public:
+	///
+	this(real x, real y, MouseButton b) {
+		_location = Point(x, y);
+		_button = b;
+	}
+	///
+	Point location() { return _location; }
+	///
+	void location(Point pt) { _location = pt; }
+	///
+	real x() { return _location.x; }
+	///
+	real y() { return _location.y; }
+	///
+	MouseButton button() { return _button; }
+}
+///
+class MouseTurnedEventArgs : StopEventArgs {
+	int _delta;
+	double _scrollAmount;
+public:
+	this(int delta, double scrollAmount) {
+		_delta = delta;
+		_scrollAmount = scrollAmount;
+	}
+	/*
+	 * This is the amount the mouse wheel was turned.
+	 */
+	//int delta() { return _delta; }
+	/**
+	 * The amount that a control should scroll in response to this event.
+	 * In a text control, this is the number of lines to scroll.
+	 * This will be negative if the control should scroll upward and positive
+	 * if the control should scroll downward. If the amount to be scrolled
+	 * is more than what is visible on screen, only what is on screen
+	 * should be scrolled.
+	 *
+	 * All users of this class should check scrollScreen to see whether to
+	 * scroll one screen or to scroll the amount by this.
+	 */
+	double scrollAmount() { return _scrollAmount; }
+	/**
+	 * On some systems, such as Windows, there is the option of setting
+	 * the mouse wheel to scroll a screen at a time, the same as the page up
+	 * and page down keys do. If this option is turned on, scrollScreen will
+	 * return true and scrollAmount will return ±3. If the option is turned off,
+	 * scrollScreen will return false.
+	 */
+	bool scrollScreen() {
+		return false;
+	}
+}
+///
+class KeyEventArgs : StopEventArgs {
+	Key _key;
+	bool _repeat;
+public:
+	this(Key key, bool repeat) {
+		_key = key;
+		_repeat = repeat;
+	}
+	/**
+	 * Returns: the key that was typed.
+	 */
+	Key key() { return _key; }
+	/**
+	 * Gets whether this key event was generated by the user holding
+	 * down the key.
+	 * Returns: true if the key was already down before this event, false
+	 *          if the key was just pressed
+	 */
+	bool repeat() { return _repeat; }
+}
+///
+class KeyTypedEventArgs : StopEventArgs {
+	dchar _ch;
+	bool _repeat;
+public:
+	this(dchar c, bool repeat) {
+		_ch = c;
+		_repeat = repeat;
+	}
+	/**
+	 * Gets whether this key event was generated from the user holding
+	 * down the key.
+	 * Returns: true if the key was already down before this event, false
+	 *          if the key was just pressed
+	 */
+	bool repeat() { return _repeat; }
+	/**
+	 * Gets the character that was typed by the user. Many keys on the
+	 * keyboard will not generate a KeyTyped event, as they do not represent
+	 * characters. Shift, Insert, Home, F7, and Caps Lock are just some of
+	 * the keys that do not represent characters.
+	 */
+	dchar character() { return _ch; }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/file_dialog.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,270 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.file_dialog;
+
+import dynamin.c.windows;
+import dynamin.all_core;
+import dynamin.gui.window;
+import tango.io.Stdout;
+import Utf = tango.text.convert.Utf;
+
+// not used by programs
+struct FileDialogFilter {
+	string name;
+	string[] extensions;
+	// ignored for now
+	bool delegate(string fileName) shouldShow;
+}
+
+/**
+ * As this is an abstract class, use OpenFileDialog or SaveFileDialog instead.
+ *
+ * TODO: figure out a way to allow the user to type in a custom filter?
+ * TODO: on Linux, use a GTK dialog if available, otherwise use a custom one.
+ *
+ * The appearance of a file dialog with Windows Classic:
+ *
+ * $(IMAGE ../web/example_file_dialog.png)
+ */
+abstract class FileDialog {
+protected:
+	bool _multipleSelection;
+	string _initialFileName;
+	string _text;
+	string _directory;
+	string[] _files;
+	FileDialogFilter[] _filters;
+	int _selectedFilter;
+public:
+	/**
+	 * Adds a filter that only shows files with the specified extensions.
+	 * Note that the "All Files (*.*)" filter is added automatically when the
+	 * dialog is shown if not added previously because I don't like when
+	 * programs don't let me have control over what extension to use and
+	 * don't let me be able to see all the files.
+	 * Examples:
+	 * -----
+	 * dialog.addFilter("All Files (*.*)");
+	 * dialog.addFilter("Cascading Style Sheets (*.css)", "css");
+	 * dialog.addFilter("Web Pages (*.html, *.htm)", "html", "htm");
+	 * -----
+	 */
+	void addFilter(string name, string[] exts...) {
+		FileDialogFilter filter;
+		filter.name = name;
+		filter.extensions = exts;
+
+		_filters.length = _filters.length + 1;
+		_filters[_filters.length-1] = filter;
+	}
+	/**
+	 * Adds a filter that only shows files with which the specified delegate
+	 * returns true for.
+	 * BUG: not implemented
+	 */
+	void addFilter(string name, string ext, bool delegate(string fileName) shouldShow) {
+		// TODO:
+		throw new Exception("addFilter(string, string, delegate) not implemented");
+		FileDialogFilter filter;
+		filter.name = name;
+		filter.extensions = [ext];
+		filter.shouldShow = shouldShow;
+
+		_filters.length = _filters.length + 1;
+		_filters[_filters.length-1] = filter;
+	}
+	/// Gets or sets the selected filter. An index of 0 is the first one added.
+	int selectedFilter() { return _selectedFilter; }
+	/// ditto
+	void selectedFilter(int index) { _selectedFilter = index; }
+	/**
+	 * Gets or sets whether more than one file can be selected.
+	 * The default is true for an OpenFileDialog and false for SaveFileDialog.
+	 */
+	bool multipleSelection() { return _multipleSelection; }
+	/// ditto
+	void multipleSelection(bool b) { _multipleSelection = b;	}
+	/// Gets or sets the text that is displayed in the dialog's title bar.
+	string text() { return _text; }
+	/// ditto
+	void text(string str) { _text = str; }
+	/**
+	 * Sets the text in the file name text box to the specified string.
+	 * Example:
+	 * -----
+	 * dialog.initialFileName = "Untitled";
+	 * -----
+	 */
+	void initialFileName(string str) {
+		// TODO: make sure str is not a path?
+		_initialFileName = str;
+	}
+	/**
+	 * Sets the directory that the FileDialog shows. If this is null,
+	 * the default directory is used when the dialog is first shown.
+	 * After the dialog has been shown, this is set to the directory
+	 * the user was last looking at.
+	 */
+	void directory(string str) {
+		_directory = str;
+	}
+	/// TODO: Should this be SelectedDirectory ?
+	string directory() {
+		return _directory;
+	}
+	/**
+	 * Gets the files selected by the user.
+	 * If the user did not type a file name extension, the correct one
+	 * will be added according to the selected filter.
+	 */
+	string[] files() { return _files; }
+	/// Gets the first of the files selected by the user.
+	string file() { return _files[0]; }
+	protected int getXFileName(OPENFILENAME* ofn);
+	// TODO: parameters
+	// TODO: should ShowDialog take any parameters?
+	//       what should happen if no owner is set?
+	//       Windows Forms sets the owner to the currently active window in the application
+	//       do the same? or have no owner (really annoying, as window can get below)?
+	DialogResult showDialog() {
+		OPENFILENAME ofn;
+		ofn.lStructSize = OPENFILENAME.sizeof;
+		//ofn.hwndOwner = ;
+
+		bool allFilesFilter = false;
+		foreach(filter; _filters)
+			if(filter.extensions.length == 0)
+				allFilesFilter = true;
+		if(!allFilesFilter) addFilter("All Files (*.*)");
+
+		string filterStr;
+		foreach(filter; _filters) {
+			if(filter.shouldShow)
+				continue;
+			string[] exts = filter.extensions.dup;
+			if(exts.length == 0)
+				exts = [cast(string)"*.*"];
+			else
+				for(int i = 0; i < exts.length; ++i)
+					exts[i] = "*." ~ exts[i];
+			filterStr ~= filter.name ~ "\0" ~ exts.join(";") ~ "\0";
+		}
+		filterStr ~= "\0";
+		ofn.lpstrFilter = filterStr.toWcharPtr();
+		ofn.nFilterIndex = _selectedFilter + 1;
+		wchar[] filesBufferW = Utf.toString16(_initialFileName~"\0");
+		filesBufferW.length = 4096;
+		scope(exit) delete filesBufferW;
+		ofn.lpstrFile = filesBufferW.ptr;
+		ofn.nMaxFile = filesBufferW.length;
+		ofn.lpstrInitialDir = _directory.toWcharPtr();
+		ofn.lpstrTitle = _text.toWcharPtr();
+		ofn.Flags = OFN_EXPLORER;
+		//if(canChooseLinks)
+		//	ofn.Flags |= OFN_NODEREFERENCELINKS;
+		ofn.Flags |= OFN_FILEMUSTEXIST;
+		ofn.Flags |= OFN_HIDEREADONLY;
+		ofn.Flags |= OFN_OVERWRITEPROMPT;
+		if(_multipleSelection)
+			ofn.Flags |= OFN_ALLOWMULTISELECT;
+
+		if(!getXFileName(&ofn)) {
+			if(CommDlgExtendedError() == FNERR_BUFFERTOOSMALL)
+				MessageBoxW(null, "Too many files picked.", "Error", 0);
+			return DialogResult.Cancel;
+		}
+
+		_selectedFilter = ofn.nFilterIndex - 1;
+		// must zero FFFF chars here because the
+		// parsing here assumes the unused part of the string is zeroed
+		foreach(i, c; filesBufferW)
+			if(c == 0xFFFF)
+				filesBufferW[i] = 0;
+		int index; // index of null char right after the last non-null char
+		for(index = filesBufferW.length; index > 0; --index)
+			if(filesBufferW[index-1] != 0)
+				break;
+		auto filesBuffer = Utf.toString(filesBufferW[0..index]);
+		scope(exit) delete filesBuffer;
+		if(filesBuffer.contains('\0')) { // multiple files
+			auto arr = filesBuffer.split("\0");
+			_directory = arr[0];
+			// make sure directory ends with a backslash
+			// "C:\" does but "C:\Program Files" does not
+			if(!_directory.endsWith("\\"))
+				_directory ~= "\\";
+			_files = new string[arr.length-1];
+			for(int i = 1; i < arr.length; ++i) {
+				if(arr[i].contains('\\')) // a dereferenced link--absolute
+					_files[i-1] = arr[i];
+				else
+					_files[i-1] = _directory ~ arr[i];
+			}
+		} else { //single file
+			assert(filesBuffer.contains('\\'));
+			_directory = filesBuffer[0..filesBuffer.findLast("\\")].dup;
+			_files = [filesBuffer.dup];
+		}
+
+		// if "All Files (*.*)" filter is not selected
+		if(_filters[selectedFilter].extensions.length > 0) {
+			// go over every chosen file and add the selected filter's
+			// extension if the file doesn't already have one from the selected filter
+			for(int i = 0; i < _files.length; ++i) {
+				bool validExt = false;
+				foreach(ext; _filters[selectedFilter].extensions)
+					if(_files[i].downcase().endsWith(ext.downcase()))
+						validExt = true;
+				if(!validExt)
+					_files[i] ~= "." ~ _filters[selectedFilter].extensions[0].downcase();
+			}
+		}
+
+		return DialogResult.OK;
+	}
+}
+
+///
+class OpenFileDialog : FileDialog {
+	this() {
+		_multipleSelection = true;
+		// different settings
+	}
+	protected int getXFileName(OPENFILENAME* ofn) {
+		return GetOpenFileName(ofn);
+	}
+}
+
+///
+class SaveFileDialog : FileDialog {
+	this() {
+		_multipleSelection = false;
+		// different settings
+	}
+	protected int getXFileName(OPENFILENAME* ofn) {
+		return GetSaveFileName(ofn);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/key.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,156 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.key;
+
+import dynamin.core.string;
+
+/**
+ * No Menu key is included because Shift+F10 is used instead.
+ * Note: Windows does not send KeyDown events for PrintScreen, only KeyUp.
+ */
+enum Key {
+	None,
+	Escape,
+	Tab,
+	Backspace,
+	Enter,
+	Space,
+
+	Left,
+	Right,
+	Up,
+	Down,
+
+	Insert,
+	Delete,
+	Home,
+	End,
+	PageUp,
+	PageDown,
+
+	PrintScreen,
+	Pause,
+
+	CapsLock,
+	NumLock,
+	ScrollLock,
+
+	NumPad0,
+	NumPad1,
+	NumPad2,
+	NumPad3,
+	NumPad4,
+	NumPad5,
+	NumPad6,
+	NumPad7,
+	NumPad8,
+	NumPad9,
+	NumPadDivide,
+	NumPadMultiply,
+	NumPadSubtract,
+	NumPadAdd,
+	NumPadDecimal, // TODO: NumPadPoint?
+
+	Backquote,
+	Minus,
+	Equals,
+	OpenBracket,
+	CloseBracket,
+	Backslash,
+	Semicolon,
+	Quote,
+	Comma,
+	Period,
+	Slash,
+
+	/// Windows sends these messages when the Menu key is pressed and released:
+	/// Menu pressed, Menu released, Shift pressed, F10 pressed, F10 released, Shift released
+	/// So if any program responds to either Menu or Shift+F10, it will work right.
+	Menu, // right of spacebar, between WinKey and Ctrl
+
+	// digits
+	D0 = 0x30, D1, D2, D3, D4, D5, D6, D7, D8, D9 = 0x39,
+	A = 0x41, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z = 0x5A,
+	F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
+
+	VolumeUp,
+	VolumeDown,
+	VolumeMute,
+
+	PlayPause,
+	Stop,
+	NextTrack,
+	PrevTrack,
+
+	Shift   = 0x10000,
+	Control = 0x20000,
+	Alt     = 0x40000
+}
+
+/*string KeyToString(Key key) {
+	static string[] table = [
+	"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
+	"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
+	"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
+	"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
+	"Escape", "Tab", "Backspace", "Enter", "Space",
+	"Left Arrow", "Right Arrow", "Up Arrow", "Down Arrow",
+	"Insert", "Delete", "Home", "End", "Page Up", "Page Down",
+	"Print Screen", "Pause",
+	"Caps Lock", "Num Lock", "Scroll Lock",
+	"NumPad0", "NumPad1", "NumPad2", "NumPad3", "NumPad4",
+	"NumPad5", "NumPad6", "NumPad7", "NumPad8", "NumPad9",
+	"NumPad/", "NumPad*", "NumPad-", "NumPad+", "NumPad.",
+	"`", "-", "=", "[", "]", "\\", ";", "'", ",", ".", "/",
+	"Shift", "Ctrl", "Alt"
+	];
+	return table[key];
+}
+Key toKey(string str) {
+	foreach(i, s; table)
+		if(s == str)
+			return i;
+	return
+}
+unittest {
+	assert(keyToString(Key.D0) == "0");
+	assert(keyToString(Key.A) == "A");
+	assert(keyToString(Key.N) == "N");
+	assert(keyToString(Key.F1) == "F1");
+	assert(keyToString(Key.Escape) == "Escape");
+	assert(keyToString(Key.Left) == "Left");
+	assert(keyToString(Key.Up) == "Up");
+	assert(keyToString(Key.Insert) == "Insert");
+	assert(keyToString(Key.PrintScreen) == "Print Screen");
+	assert(keyToString(Key.Pause) == "Pause");
+	assert(keyToString(Key.CapsLock) == "CapsLock");
+	assert(keyToString(Key.NumPad0) == "NumPad0");
+	assert(keyToString(Key.NumPad5) == "NumPad5");
+	assert(keyToString(Key.NumPadDivide) == "NumPad/");
+	assert(keyToString(Key.Backquote) == "`");
+	assert(keyToString(Key.Control) == "Ctrl");
+}*/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/label.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,54 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.label;
+
+import dynamin.core.string;
+import dynamin.all_painting;
+import dynamin.all_gui;
+
+///
+class Label : Control {
+protected:
+	override Size bestSize() {
+		Size s;
+		withGraphics((Graphics g) { s = g.getTextExtents(text); });
+		return s;
+	}
+	override void whenPainting(PaintingEventArgs e) {
+		with(e.graphics) {
+			drawText(text, 0, (height-getTextExtents(text).height)/2);
+		}
+	}
+public:
+	this() {
+	}
+	this(string text) {
+		this();
+		this.text = text;
+	}
+	override int baseline() { return 10; }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/layout.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,569 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.layout;
+
+import dynamin.all_gui;
+import dynamin.gui.control;
+import dynamin.all_painting;
+import dynamin.core.string;
+import tango.io.Stdout;
+import dynamin.core.benchmark;
+
+// this is a temporary file to hold layout code until I figure out what
+// files to put it in
+
+/*
+Opera's find dialog:
+
+auto whatLabel = win.content.add(new Label("Find What"));
+...
+
+V(	whatLabel
+	H( findBox findButton )
+	H( V(wholeWordCheck caseCheck) ~ V(upRadio downRadio) ~)
+	H( ~ closeButton )
+)
+*/
+
+enum LayoutType {
+	None, Table, Control, Filler, Spacer
+}
+enum Elasticity {
+	No, Semi, Yes
+}
+struct LayoutGroup {
+	LayoutType type;
+	LayoutGroup* parent;
+	LayoutGroup[] children; // used if type == LayoutType.Horiz or Vert or Table
+	Control control;        // used if type == LayoutType.Control
+	int numColumns;         // used if type == LayoutType.Table
+	int numRows() { return children.length / numColumns; }
+
+	bool cacheActive;
+	private Elasticity _elasticXCache, _elasticYCache;
+	private Size _bestSizeCache;
+	private int _baselineCache;
+
+	// spacing variables
+	int spacing = 8;
+	static LayoutGroup opCall(LayoutType type, LayoutGroup* parent) {
+		LayoutGroup layout;
+		layout.type = type;
+		layout.parent = parent;
+		layout.children.length = 3;
+		layout.children.length = 0;
+		return layout;
+	}
+
+	void setCache() {
+		for(int i = 0; i < children.length; ++i) // can't use foreach--copies
+			children[i].setCache();
+		_elasticXCache = _elasticX;
+		_elasticYCache = _elasticY;
+		_bestSizeCache = _bestSize;
+		_baselineCache = _baseline;
+		cacheActive = true;
+	}
+	void clearCache() {
+		cacheActive = false;
+		for(int i = 0; i < children.length; ++i) // can't use foreach--copies
+			children[i].clearCache();
+	}
+	Elasticity elasticX() { return cacheActive ? _elasticXCache : _elasticX; }
+	Elasticity elasticY() { return cacheActive ? _elasticYCache : _elasticY; }
+	Size bestSize() { return cacheActive ? _bestSizeCache : _bestSize; }
+	int  baseline() { return cacheActive ? _baselineCache : _baseline; }
+
+	//{{{ _elasticX()
+	private Elasticity _elasticX() {
+		switch(type) {
+		case LayoutType.Control:
+			return control.elasticX ? Elasticity.Yes : Elasticity.No;
+		case LayoutType.Table:
+			auto e = Elasticity.No;
+			foreach(layout; children) {
+				if(layout.elasticX > e)
+					e = layout.elasticX;
+				if(e == Elasticity.Yes)
+					return e;
+			}
+			return e;
+		case LayoutType.Filler:
+			return Elasticity.Semi;
+		case LayoutType.Spacer:
+			return Elasticity.No;
+		}
+	}
+	//}}}
+	//{{{ _elasticY()
+	private Elasticity _elasticY() {
+		switch(type) {
+		case LayoutType.Control:
+			return control.elasticY ? Elasticity.Yes : Elasticity.No;
+		case LayoutType.Table:
+			auto e = Elasticity.No;
+			foreach(layout; children) {
+				if(layout.elasticY > e)
+					e = layout.elasticY;
+				if(e == Elasticity.Yes)
+					return e;
+			}
+			return e;
+		case LayoutType.Filler:
+			return Elasticity.Semi;
+		case LayoutType.Spacer:
+			return Elasticity.No;
+		}
+	}
+	//}}}
+
+	//{{{ _bestSize()
+	private Size _bestSize() {
+		switch(type) {
+		case LayoutType.Control:
+			return control.bestSize;
+		case LayoutType.Table:
+			scope colsInfo = new ColRowInfo[numColumns];
+			scope rowsInfo = new ColRowInfo[numRows];
+			TableInfo info;
+			getTableSizes(colsInfo, rowsInfo, info);
+			return info.bestSize;
+		case LayoutType.Filler:
+		case LayoutType.Spacer:
+			return Size(0, 0);
+		}
+	}
+	//}}}
+	//{{{ _baseline()
+	private int _baseline() {
+		switch(type) {
+		case LayoutType.Control:
+			return control.baseline;
+		case LayoutType.Table:
+		case LayoutType.Filler:
+		case LayoutType.Spacer:
+			return 0;
+		}
+	}
+	//}}}
+
+	//{{{ layout()
+	void layout(Rect rect) {
+		switch(type) {
+		case LayoutType.Control:
+			control.location = Point(rect.x, rect.y);
+			control.size = Size(rect.width, rect.height);
+			return;
+		case LayoutType.Table:
+			scope colsInfo = new ColRowInfo[numColumns];
+			scope rowsInfo = new ColRowInfo[numRows];
+			TableInfo info;
+			getTableSizes(colsInfo, rowsInfo, info);
+
+			real extraWidth  = rect.width  - bestSize.width;
+			real extraHeight = rect.height - bestSize.height;
+
+			void distExtra(ref real extra, ref ColRowInfo info,
+			              ref real totalElastic, ref int semis, Elasticity e) {
+				if(info.elastic == Elasticity.No || extra <= 0)
+					return;
+				if(e == Elasticity.Semi &&
+						info.elastic == Elasticity.Semi) {
+					auto thisExtra = extra / semis;
+					extra -= thisExtra;
+					semis--;
+					info.bestSize += thisExtra;
+				} else if(e == Elasticity.Yes &&
+						info.elastic == Elasticity.Yes) {
+					auto thisExtra = extra * info.bestSize/totalElastic;
+					extra -= thisExtra;
+					totalElastic -= info.bestSize; // subtract original size
+					info.bestSize += thisExtra;
+				}
+			}
+			real y = 0;
+			for(int row = 0; row < numRows; ++row) { // go over each row
+				distExtra(extraHeight, rowsInfo[row], info.elasticHeight, info.semiRows, elasticY);
+
+				real x = 0;
+				for(int col = 0; col < numColumns; ++col) {
+					distExtra(extraWidth, colsInfo[col], info.elasticWidth, info.semiColumns, elasticX);
+
+					auto layout = children[row * numColumns + col];
+
+					Rect r = Point(x, y) + layout.bestSize;
+
+					if(layout.baseline > 0)
+						r.y = r.y + rowsInfo[row].baseline - layout.baseline;
+					if(layout.elasticX)
+						r.width = colsInfo[col].bestSize;
+					if(layout.elasticY)
+						r.height = rowsInfo[row].bestSize;
+
+					layout.layout(r + Point(rect.x, rect.y));
+
+					x += colsInfo[col].bestSize +
+					    (colsInfo[col].filler ? 0 : spacing);
+				}
+				y += rowsInfo[row].bestSize +
+				    (rowsInfo[row].filler ? 0 : spacing);
+			}
+			return;
+		case LayoutType.Filler:
+		case LayoutType.Spacer:
+			return;
+		}
+	}
+	//}}}
+
+	struct ColRowInfo {
+		real bestSize = 0;  // large enough to hold the largest control
+		Elasticity elastic = Elasticity.No;
+		bool filler = true; // if the entire column/row is filler
+		real baseline;      // only applies to rows: max baseline in row
+	}
+	struct TableInfo {
+		// number of semi-elastic columns/rows
+		int semiColumns = 0; int semiRows = 0;
+		// the sum of fully elastic width/height, not including semi
+		real elasticWidth = 0, elasticHeight = 0;
+		Size bestSize = Size(0, 0);
+	}
+	//{{{ getTableSizes()
+	// Fills in the passed in array with the column and row sizes, as well
+	// as whether they are elastic. The passed in arrays must be the right
+	// sizes. They may be stack allocated. The table best size does
+	// including spacing, but column and row best sizes do not.
+	private void getTableSizes(ColRowInfo[] colsInfo, ColRowInfo[] rowsInfo, ref TableInfo info) {
+		assert(children.length % numColumns == 0);
+		assert(type == LayoutType.Table);
+
+		assert(colsInfo.length == numColumns);
+		assert(rowsInfo.length == numRows);
+
+		real max = 0, temp;
+		LayoutGroup* l;
+
+		int sp = 0;
+		for(int col = 0; col < numColumns; ++col) { // go down each column
+			for(int row = 0; row < numRows; ++row) {
+				l = &children[row * numColumns + col];
+				max = l.bestSize.width > max ? l.bestSize.width : max;
+				if(l.elasticX > colsInfo[col].elastic)
+					colsInfo[col].elastic = l.elasticX;
+				if(l.type != LayoutType.Filler)
+					colsInfo[col].filler = false;
+			}
+			colsInfo[col].bestSize = max;
+			if(colsInfo[col].elastic == Elasticity.Yes)
+				info.elasticWidth += max;
+			else if(colsInfo[col].elastic == Elasticity.Semi)
+				info.semiColumns++;
+			info.bestSize.width = info.bestSize.width + sp + max;
+			sp = (colsInfo[col].filler ? 0 : spacing);
+			max = 0;
+		}
+
+		real maxBl = 0;
+		sp = 0;
+		for(int row = 0; row < numRows; ++row) { // go over each row
+			for(int col = 0; col < numColumns; ++col) {
+				l = &children[row * numColumns + col];
+				max = l.bestSize.height > max ? l.bestSize.height : max;
+				maxBl = l.baseline > maxBl ? l.baseline : maxBl;
+				if(l.elasticY > rowsInfo[row].elastic)
+					rowsInfo[row].elastic = l.elasticY;
+				if(l.type != LayoutType.Filler)
+					rowsInfo[row].filler = false;
+			}
+			rowsInfo[row].bestSize = max;
+			rowsInfo[row].baseline = maxBl;
+			if(rowsInfo[row].elastic == Elasticity.Yes)
+				info.elasticHeight += max;
+			else if(rowsInfo[row].elastic == Elasticity.Semi)
+				info.semiRows++;
+			info.bestSize.height = info.bestSize.height + sp + max;
+			sp = (rowsInfo[row].filler ? 0 : spacing);
+			max = maxBl = 0;
+		}
+	}
+	//}}}
+}
+
+//{{{ LayoutPanel class
+class LayoutPanel : Panel {
+	LayoutGroup root;
+	LayoutGroup* current;
+	void startLayout(int ncolumns) {
+		if(current is null) {
+			root = LayoutGroup(LayoutType.Table, null);
+			root.numColumns = ncolumns;
+			current = &root;
+			return;
+		}
+		current.children.length = current.children.length+1;
+		current.children[$-1] = LayoutGroup(LayoutType.Table, current);
+		current.children[$-1].numColumns = ncolumns;
+		current = &current.children[$-1];
+	}
+	void endLayout() {
+		current = current.parent;
+	}
+	override void add(Control c) {
+		if(current is null)
+			throw new Exception("Cannot add a control until a layout is started");
+		current.children.length = current.children.length+1;
+		current.children[$-1] = LayoutGroup(LayoutType.Control, current);
+		current.children[$-1].control = c;
+		super.add(c);
+	}
+	void addFiller() {
+		current.children.length = current.children.length+1;
+		current.children[$-1] = LayoutGroup(LayoutType.Filler, current);
+	}
+	void addSpacer() {
+		current.children.length = current.children.length+1;
+		current.children[$-1] = LayoutGroup(LayoutType.Spacer, current);
+	}
+
+	override Size bestSize() {
+		return root.bestSize + Size(root.spacing*2, root.spacing*2);
+	}
+	override bool elasticX() { return root.elasticX == Elasticity.Yes; }
+	override bool elasticY() { return root.elasticY == Elasticity.Yes; }
+	override void layout() {
+		//benchmarkAndWrite("layout", {
+		root.setCache();
+		int sp = root.spacing;
+		root.layout(Rect(sp, sp, width-2*sp, height-2*sp));
+		root.clearCache();
+		//});
+	}
+}
+//}}}
+
+//{{{ createLayout() etc.
+/**
+ * Note: if you do this:
+ * -----
+ * char[] s = createLayout("V( b1 H(b2 b3) )");
+ * -----
+ * Then the program will crash when compiled with the -release flag. (I am
+ * pretty sure it is a DMD bug, but I don't have time to make a testcase
+ * for a bug that does not bother me.) This will work correctly:
+ * -----
+ * const char[] s = createLayout("V( b1 H(b2 b3) )");
+ * -----
+ * Because then the function is interpreted at compile time with CTFE.
+ */
+string createLayout(string layout) {
+	string code = "delegate LayoutPanel() {\n";
+	code ~= "auto panel = new LayoutPanel;\n";
+	assert(getToken(layout) == "H" || getToken(layout) == "V" ||
+		getToken(layout) == "T", "layout type 'H', 'V', or 'T' expected");
+	code ~= parseLayout(layout);
+	code ~= "return panel;\n";
+	code ~= "}()";
+	return code;
+}
+
+void skipWS(ref string str) {
+	int i = 0;
+	while(" \t\n\r\v\f".contains(str[i]))
+		i++;
+	str = str[i..$];
+}
+// advances to the next token and returns it
+string nextToken(ref string str) {
+	skipWS(str);
+	str = str[getToken(str).length..$];
+	return getToken(str);
+}
+// returns H or V or ( or ) or myControl
+// gets the current token
+string getToken(string str) {
+	string idChars =
+		"_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+	// TODO: // for line comments?
+	skipWS(str);
+	if("()~[]-".contains(str[0])) {
+		return str[0..1];
+	} else if(idChars.contains(str[0])) {
+		int i = 1;
+		while(idChars.contains(str[i]))
+			i++;
+		return str[0..i];
+	} else {
+		assert(0, "unknown character: " ~ str[0]);
+	}
+}
+
+// {{{ copied from Phobos
+char[] ctfeUintToString(uint u) {
+	char[uint.sizeof * 3] buffer = void;
+	int ndigits;
+	char[] result;
+	char[] digits = "0123456789";
+
+	ndigits = 0;
+	if (u < 10)
+		// Avoid storage allocation for simple stuff
+	result = digits[u .. u + 1];
+	else
+	{
+		while (u)
+		{
+			uint c = (u % 10) + '0';
+			u /= 10;
+			ndigits++;
+			buffer[buffer.length - ndigits] = cast(char)c;
+		}
+		result = new char[ndigits];
+		result[] = buffer[buffer.length - ndigits .. buffer.length];
+	}
+	return result;
+}
+uint ctfeStringToUint(char[] s)
+{
+	int length = s.length;
+
+	if (!length)
+		return 0;
+
+	uint v = 0;
+
+	for (int i = 0; i < length; i++)
+	{
+		char c = s[i];
+		if (c >= '0' && c <= '9')
+		{
+			if (v < uint.max/10 || (v == uint.max/10 && c <= '5'))
+				v = v * 10 + (c - '0');
+			else
+				return 0;
+		}
+		else
+			return 0;
+    }
+    return v;
+}
+//}}}
+
+uint parseBody(ref string layout, ref string bcode) {
+	uint count = 0;
+	assert(nextToken(layout) == "(", "open parenthesis expected");
+	while(nextToken(layout) != ")") {
+		if(getToken(layout) == "~")
+			bcode = bcode ~ "panel.addFiller();\n";
+		else if(getToken(layout) == "-")
+			bcode = bcode ~ "panel.addSpacer();\n";
+		else
+			bcode = bcode ~ parseLayout(layout);
+		count++;
+	}
+	bcode = bcode ~ "panel.endLayout();\n";
+	return count;
+}
+
+string parseLayout(ref string layout) {
+	string code = "";
+
+	if(getToken(layout) == "H") {
+		string bodyCode;
+		auto count = parseBody(layout, bodyCode);
+		code ~= "panel.startLayout(" ~ ctfeUintToString(count) ~ ");\n";
+		code ~= bodyCode;
+	} else if(getToken(layout) == "V") {
+		code ~= "panel.startLayout(1);\n";
+		parseBody(layout, code);
+	} else if(getToken(layout) == "T") {
+		assert(nextToken(layout) == "[", "open bracket expected");
+		nextToken(layout);
+		assert("0123456789".contains(getToken(layout)[0]),
+			"number of table columns expected");
+		uint columns = ctfeStringToUint(getToken(layout));
+		code ~= "panel.startLayout(" ~ getToken(layout) ~ ");\n";
+		assert(nextToken(layout) == "]", "close bracket expected");
+		assert(parseBody(layout, code) % columns == 0,
+			"number of controls must be a multiple of number of columns");
+	} else {
+		code ~= "panel.add(" ~ getToken(layout) ~ ");\n";
+	}
+
+	return code;
+}
+
+//{{{ parser tests
+static assert(createLayout("H()") != "not evaluatable at compile time");
+//pragma(msg, createLayout("V()"));
+
+static assert(createLayout("V(c1 c2)") ==
+`delegate LayoutPanel() {
+auto panel = new LayoutPanel;
+panel.startLayout(1);
+panel.add(c1);
+panel.add(c2);
+panel.endLayout();
+return panel;
+}()`);
+static assert(createLayout("V(c1 ~ c2 H(c3 -) c4)") ==
+`delegate LayoutPanel() {
+auto panel = new LayoutPanel;
+panel.startLayout(1);
+panel.add(c1);
+panel.addFiller();
+panel.add(c2);
+panel.startLayout(2);
+panel.add(c3);
+panel.addSpacer();
+panel.endLayout();
+panel.add(c4);
+panel.endLayout();
+return panel;
+}()`);
+static assert(createLayout("V( c1 T[2](c2 c3) c4 )") ==
+`delegate LayoutPanel() {
+auto panel = new LayoutPanel;
+panel.startLayout(1);
+panel.add(c1);
+panel.startLayout(2);
+panel.add(c2);
+panel.add(c3);
+panel.endLayout();
+panel.add(c4);
+panel.endLayout();
+return panel;
+}()`);
+//}}}
+
+//}}}
+
+unittest {
+	// TODO: set to basic theme
+	// test a few basic layouts and verify pixel locations and sizes
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/list_box.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,116 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.list_box;
+
+import dynamin.all_painting;
+import dynamin.all_core;
+import dynamin.all_gui;
+
+/**
+ * A control that shows a list of items that can be selected.
+ *
+ * The appearance of a list box with Windows Classic:
+ *
+ * $(IMAGE ../web/example_list_box.png)
+ */
+class ListBox : Scrollable {
+protected:
+	List!(string) _items;
+	int _selectedIndex = -1;
+
+	override void whenKeyDown(KeyEventArgs e) {
+			if(e.key == Key.Down) {
+				if(selectedIndex + 1 < _items.count)
+					selectedIndex = selectedIndex + 1;
+			} else if(e.key == Key.Up) {
+				if(selectedIndex - 1 >= 0)
+					selectedIndex = selectedIndex - 1;
+			}
+		}
+	override Size bestSize() {
+		return Size(0, 13*_items.count);
+	}
+	override void whenContentPainting(PaintingEventArgs e) {
+		with(e.graphics) {
+			source = backColor;
+			paint();
+			auto clip = getClipExtents();
+			int start = cast(int)clip.y/13, end = cast(int)clip.bottom/13+1;
+			for(int i = start; i < _items.count && i < end; ++i) {
+				source = Color.Black;
+				if(i == _selectedIndex) {
+					// TODO: hack
+					//Source = WindowsTheme.getColor(dynamin.c.windows.COLOR_HIGHLIGHT);
+					rectangle(0, i*13, width, 13);
+					fill();
+					//Source = WindowsTheme.getColor(dynamin.c.windows.COLOR_HIGHLIGHTTEXT);
+				}
+				drawText(_items[i], 3, i*13);
+			}
+		}
+	}
+	override void whenContentMouseDown(MouseEventArgs e) {
+		focus();
+		selectedIndex = cast(int)e.y/13;
+	}
+public:
+
+	/// This event occurs after the selection has changed.
+	Event!() selectionChanged;
+	/// Override this method in a subclass to handle the SelectionChanged event.
+	protected void whenSelectionChanged(EventArgs e) { }
+
+	void listItemsChanged() {
+		super.layout();
+		repaint();
+	}
+
+	///
+	this() {
+		selectionChanged = new Event!()(&whenSelectionChanged);
+		_items = new List!(string)(&listItemsChanged);
+
+		super();
+		_focusable = true;
+		// TODO: hack
+		backColor = WindowsTheme.getColor(5);
+	}
+	///
+	List!(string) items() {
+		return _items;
+	}
+	///
+	int selectedIndex() { return _selectedIndex; }
+	/// ditto
+	void selectedIndex(int i) {
+		if(i == _selectedIndex)
+			return;
+		_selectedIndex = i;
+		repaint();
+		selectionChanged(new EventArgs);
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/notebook.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,192 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.notebook;
+
+import dynamin.core.event;
+import dynamin.core.list;
+import dynamin.all_gui;
+import dynamin.all_painting;
+import dynamin.core.string;
+import tango.core.Exception;
+
+///
+class TabPage {
+protected:
+	string _text;
+	Control _content;
+	Point _tabLocation;
+	Size _tabSize;
+public:
+	/**
+	 * Gets or sets the text displayed on the tab of this tab page.
+	 */
+	string text() { return _text; }
+	/// ditto
+	void text(string str) { _text = str; }
+	/**
+	 * Gets or sets the control that is shown in this tab page.
+	 */
+	Control content() { return _content; }
+	/// ditto
+	void content(Control c) { _content = c; }
+	Point tabLocation() { return _tabLocation; }
+	void tabLocation(Point pt) { _tabLocation = pt; }
+	Size tabSize() { return _tabSize; }
+	void tabSize(Size sz) { _tabSize = sz; }
+}
+
+/**
+ * A notebook is a control that has tabs and changes what it displays
+ * depending upon which tab is selected.
+ *
+ * The appearance of a notebook with Windows Classic:
+ *
+ * $(IMAGE ../web/example_notebook.png)
+ */
+class Notebook : Container {
+protected:
+	List!(TabPage) _tabPages;
+	int _selectedIndex = -1;
+	bool _multipleLines = true;
+	Control _content;
+	package int _tabAreaSize;
+	override void whenMouseDown(MouseEventArgs e) {
+		if(e.button != MouseButton.Left)
+			return;
+		foreach(i, page; _tabPages) {
+			if((page.tabLocation+page.tabSize).contains(e.location))
+				selectedIndex = i;
+		}
+	}
+	override void whenPainting(PaintingEventArgs e) {
+		Theme.current.Notebook_paint(this, e.graphics);
+		foreach(page; _tabPages) {
+			if(page is selectedTabPage)
+				continue;
+
+			Theme.current.Tab_paint(page, this, e.graphics);
+		}
+		Theme.current.Tab_paint(selectedTabPage, this, e.graphics);
+	}
+	void whenTabPagesChanged() {
+		if(_tabPages.count == 0)
+			selectedIndex = -1;
+		else if(selectedIndex == -1)
+			selectedIndex = 0;
+		layout();
+	}
+public:
+	/// This event occurs after a different tab is selected.
+	Event!() selectionChanged;
+	/// Override this method in a subclass to handle the SelectionChanged event.
+	protected void whenSelectionChanged(EventArgs e) {
+		if(_content !is null)
+			_children.remove(_content);
+		_content = null;
+		if(_selectedIndex >= 0) {
+			_content = selectedTabPage.content;
+			add(_content);
+		}
+		layout();
+	}
+
+	this() {
+		selectionChanged = new Event!()(&whenSelectionChanged);
+
+		_tabPages = new List!(TabPage)(&whenTabPagesChanged);
+		_focusable = true;
+	}
+	override void layout() {
+		_tabAreaSize = 20;
+		int x = 2;
+		foreach(page; _tabPages) {
+			page.tabLocation = Point(x, 2);
+			page.tabSize = Size(70, 18);
+			x += 70;
+		}
+		if(_content) {
+			auto border = Theme.current.Notebook_borderSize(this);
+			_content.location = [border.left, _tabAreaSize+border.top];
+			_content.size = [width-border.left-border.right, height-border.top-border.bottom-_tabAreaSize];
+		}
+	}
+	/**
+	  * Gets the tab pages displayed in this notebook.
+	  * Examples:
+	  * -----
+	  * TabPage advancedPage = new TabPage;
+	  * advancedPage.text = "Advanced";
+	  * advancedPage.content = advancedPanel; // a previously created Panel
+	  * tabbedView.TabPages.Add(advancedPage);
+	  * -----
+	  */
+	List!(TabPage) tabPages() { return _tabPages; }
+	/**
+	 * Gets or sets the selected tab using its index. An index of -1 means
+	 * there is no selected tab.
+	 */
+	int selectedIndex() { return _selectedIndex; }
+	/// ditto
+	void selectedIndex(int index) {
+		if(index < -1)
+			throw new IllegalArgumentException("index cannot be less than -1");
+		_selectedIndex = index;
+		selectionChanged(new EventArgs);
+	}
+
+	/**
+	  * Gets or sets the selected tab using its tab page.
+	  * A value of null means there is no selected tab.
+	  * A specified tab page must be in the TabPages list.
+	  */
+	TabPage selectedTabPage() {
+		if(_selectedIndex == -1)
+			return null;
+		else
+			return _tabPages[_selectedIndex];
+	}
+	/// ditto
+	void selectedTabPage(TabPage p) {
+		if(p is null) {
+			selectedIndex = -1;
+			return;
+		}
+		foreach(i, page; _tabPages) {
+			if(p is page) {
+				selectedIndex = i;
+				break;
+			}
+		}
+		// if here, do nothing
+	}
+	bool multipleLines() { return _multipleLines; }
+	void multipleLines(bool b) {
+		if(b == false)
+			throw new Exception("sorry, MultipleLines = false not implemented");
+		_multipleLines = b;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/office_theme.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,44 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.office_theme;
+
+static this() {
+	addTheme(new OfficeTheme());
+}
+
+enum OfficeStyle {
+	//Office2000 = 1
+	OfficeXP = 2, Office2003
+}
+// this theme paints its tool bars and menus as Office XP or Office 2003
+// It paints other controls exactly as the WindowsTheme does, as it is derived
+// from it.
+class OfficeTheme : WindowsTheme {
+	OfficeStyle _style = OfficeStyle.Office2003;
+	void officeStyle(OfficeStyle s) { _style = s; }
+	OfficeStyle officeStyle() { return _style; }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/opengl_view.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,33 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.opengl_view;
+
+import tango.io.Stdout;
+
+class OpenGLView : Control {
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/radio_button.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,109 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.radio_button;
+
+import dynamin.all_core;
+import dynamin.all_gui;
+import dynamin.all_painting;
+import tango.io.Stdout;
+
+/**
+ * A control that can be checked only if other radio buttons are unchecked.
+ *
+ * The appearance of a radio button with Windows Classic:
+ *
+ * $(IMAGE ../web/example_radio_button.png)
+ */
+class RadioButton : CheckBox {
+protected:
+	int _group = 1;
+	RadioButton[] collectGroup(ref int checkedIndex) {
+		Window topLevel = cast(Window)getTopLevel();
+		if(!topLevel)
+			return null;
+		RadioButton[] radios;
+		void collectFromPanel(Panel container) {
+			foreach(control; container) {
+				if(auto r = cast(RadioButton)control) {
+					if(r.group != group)
+						continue;
+					radios.length = radios.length + 1;
+					radios[$-1] = r;
+					if(r.checked)
+						checkedIndex = radios.length-1;
+				} else if(auto c = cast(Panel)control)
+					collectFromPanel(c);
+			}
+		}
+		checkedIndex = -1;
+		collectFromPanel(topLevel.content);
+		return radios;
+	}
+	override void whenKeyDown(KeyEventArgs e) {
+		// TODO: when GetTopLevel() is changed to return NativeControl,
+		// update this
+		int index;
+		RadioButton[] radios = collectGroup(index);
+		if(radios is null)
+			return;
+		if(e.key == Key.Down || e.key == Key.Right) {
+			if(++index >= radios.length)
+				index = 0;
+		} else if(e.key == Key.Up || e.key == Key.Left) {
+			if(--index < 0)
+				index = radios.length-1;
+		}
+		radios[index].clicked(new EventArgs);
+	}
+	override void whenPainting(PaintingEventArgs e) {
+		Theme.current.RadioButton_paint(this, e.graphics);
+	}
+	override void whenClicked(EventArgs e) {
+		int index;
+		RadioButton[] radios = collectGroup(index);
+		foreach(r; radios)
+			r.checked = false;
+		checked = true;
+		focus();
+
+	}
+public:
+	this() {
+	}
+	this(string text) {
+		this();
+		this.text = text;
+	}
+	override Size bestSize() {
+		return Size(70, 15);
+	}
+	/**
+	 * Gets or sets what group this radio button is a part of. The default is 1.
+	 */
+	int group() { return _group; }
+	/// ditto
+	void group(int i) { _group = i; }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/screen.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,75 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.screen;
+
+import dynamin.painting.coordinates;
+
+// TODO: Screen.size and Screen.DesktopRect  ?
+//ScreenList[] Screens() { }
+//Screen PrimaryScreen() { return MonitorFromPoint(0, 0); }
+//GetMonitorInfo(HMONITOR, MONITORINFO*)
+//EnumDisplayMonitors(HDC, RECT*, MONITORENUMPROC, LPARAM);
+/*
+class Screen {
+static {
+	Screen[] getAll()
+	Screen primary()
+}
+	Size size()
+	Rect desktopRect()
+}
+*/
+
+/*
+On Windows, Screen mainly wraps an HMONITOR.
+On X, Screen mainly wraps a Screen*
+*/
+version(Windows) {
+
+import dynamin.c.windows;
+/// Returns: the area on the primary monitor that is not covered by the taskbar
+Rect desktopRect() { // TODO: move
+	RECT rect;
+	SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
+	return Rect(rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top);
+}
+/// Returns: the screen resolution of the primary monitor
+Size screenSize() { // TODO: move
+	return Size(GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
+}
+
+} else {
+import dynamin.gui.backend;
+Rect desktopRect() { // TODO: move
+	int* data = cast(int*)getXWindowProperty(display,
+		XRootWindow(display, XDefaultScreen(display)), XA._NET_WORKAREA);
+	scope(exit) XFree(data);
+	return Rect(data[0], data[1], data[2], data[3]);
+}
+
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/scroll_bar.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,301 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.scroll_bar;
+
+import dynamin.all_core;
+import dynamin.all_gui;
+import dynamin.all_painting;
+import tango.io.Stdout;
+
+enum ArrowDirection {
+	Left, Right, Up, Down
+}
+class ArrowButton : Button {
+	ArrowDirection _direction;
+	ArrowDirection direction() { return _direction; }
+	void direction(ArrowDirection dir) { _direction = dir; }
+	override void whenPainting(PaintingEventArgs e) {
+		Theme.current.ArrowButton_paint(this, e.graphics);
+	}
+}
+class ScrollBarTrack : Button {
+	override void whenPainting(PaintingEventArgs e) {
+		Theme.current.ScrollBarTrack_paint(this, e.graphics);
+	}
+}
+class ScrollBarThumb : Button {
+	override void whenMouseDown(MouseEventArgs e) {
+	}
+	override void whenMouseDragged(MouseEventArgs e) {
+	}
+	override void whenPainting(PaintingEventArgs e) {
+		Theme.current.ScrollBarThumb_paint(this, e.graphics);
+	}
+}
+//debug=ScrollBar;
+
+///
+abstract class ScrollBar : Container {
+protected:
+	ScrollBarTrack _track1, _track2;
+	ScrollBarThumb _thumb;
+	ArrowButton _button1, _button2;
+	int _value = 0, _maxValue = 100;
+	real _visibleValue = 0.5;
+	real _thumbDragLoc = -1;
+	// stores the location of the thumb as a percentage of the track
+	real _thumbPos;
+	this() {
+		valueChanged = new Event!()(&whenValueChanged);
+
+		_track1 = new ScrollBarTrack;
+		_track2 = new ScrollBarTrack;
+		_thumb = new ScrollBarThumb;
+		_button1 = new ArrowButton;
+		_button2 = new ArrowButton;
+		_track1.mouseDown += &whenTrack1MouseDown;
+		_track2.mouseDown += &whenTrack2MouseDown;
+		_thumb.mouseDown += &whenThumbMouseDown;
+		_thumb.mouseUp += &whenThumbMouseUp;
+		_thumb.mouseDragged += &whenThumbMouseDragged;
+		_button1.mouseDown += &whenButton1MouseDown;
+		_button2.mouseDown += &whenButton2MouseDown;
+		add(_track1);
+		add(_track2);
+		add(_thumb);
+		add(_button1);
+		add(_button2);
+		size = _size;
+	}
+	void whenThumbMouseDown(MouseEventArgs e);
+	void whenThumbMouseDragged(MouseEventArgs e);
+	void whenThumbMouseUp(MouseEventArgs e) {
+		// This gives the behavior of the thumb jumping when MaxValue
+		// is smaller than the track to exactly represent where
+		// Value is, as jEdit and some of Windows' controls do.
+		_thumbPos = 0;
+		updateControls();
+	}
+	void whenTrack1MouseDown(MouseEventArgs e) {
+		value = value - 100;
+	}
+	void whenTrack2MouseDown(MouseEventArgs e) {
+		value = value + 100;
+	}
+	void whenButton1MouseDown(MouseEventArgs e) {
+		value = value - 10;
+	}
+	void whenButton2MouseDown(MouseEventArgs e) {
+		value = value + 10;
+	}
+	void putControl(Control c, real location, real size);
+	real breadth();
+	real length();
+	override void whenResized(EventArgs e) {
+		updateControls();
+	}
+	void layoutControls(Control[] controls, real[] sizes) {
+		assert(controls.length == sizes.length);
+		real loc = 0;
+		for(int i = 0; i < controls.length; ++i) {
+			putControl(controls[i], loc, sizes[i]);
+			loc += sizes[i];
+		}
+	}
+	// updates controls based on what value, visible value, and max value are
+	void updateControls() {
+		if(breadth*2 > length) {
+			return;
+			// no track or thumb
+		}
+		// if thumbPos does not represent the current value
+		if(cast(int)(_thumbPos * _maxValue) != _value)
+			_thumbPos = _value / cast(real)_maxValue;
+		auto totalSz = length;
+		auto buttonSz = breadth;
+		auto totalTrackSz = totalSz - buttonSz*2;
+		auto thumbSz = round(totalTrackSz*_visibleValue);
+		auto thumbLoc = buttonSz+(totalTrackSz-thumbSz)*_thumbPos;
+		if(thumbSz < 8)
+			thumbSz = 8;
+		auto track1Sz = thumbLoc-buttonSz;
+		auto track2Sz = totalTrackSz-track1Sz-thumbSz;
+		if(track1Sz < 0) track1Sz = 0;
+		if(track2Sz < 0) track2Sz = 0;
+debug(ScrollBar) {
+		Stdout.format("value={}", value).newline;
+		Stdout.format("visibleValue={}", visibleValue).newline;
+		Stdout.format("maxValue={}", maxValue).newline;
+		Stdout.format("totalSz={}", totalSz).newline;
+		Stdout.format("buttonSz={}", buttonSz).newline;
+		Stdout.format("totalTrackSz={}", totalTrackSz).newline;
+		Stdout.format("thumbSz={}", thumbSz).newline;
+		Stdout.format("track1Sz={}", track1Sz).newline;
+		Stdout.format("track2Sz={}", track2Sz).newline;
+		Stdout("********").newline;
+		assert(2*buttonSz+track1Sz+thumbSz+track2Sz <= totalSz + 0.01);
+}
+		layoutControls([cast(Control)
+		               _button1, _track1,  _thumb,  _track2,  _button2],
+		               [buttonSz, track1Sz, thumbSz, track2Sz, buttonSz]);
+	}
+public:
+	/// This event occurs after Value has been changed.
+	Event!() valueChanged;
+	/// Override this method in a subclass to handle the ValueChanged event.
+	protected void whenValueChanged(EventArgs e) { }
+
+	override Size bestSize() {
+		if(cast(VScrollBar)this)
+			return Size(Theme.current.ScrollBar_size(), 100);
+		else
+			return Size(100, Theme.current.ScrollBar_size());
+	}
+	///
+	real thumbLocation();
+	/// ditto
+	void thumbLocation(real loc) {
+		// TODO: return if no thumb (too small for one)
+		if(loc < trackStart)
+			loc = trackStart;
+		if(loc > trackEnd - thumbSize)
+			loc = trackEnd - thumbSize;
+		if(floatsEqual(loc, thumbLocation, 0.1))
+			return;
+
+		_thumbPos = (loc - trackStart) / (trackSize - thumbSize);
+		value = cast(int)(_thumbPos * _maxValue);
+		updateControls();
+	}
+	///
+	real thumbSize();
+	///
+	real trackStart();
+	///
+	real trackEnd();
+	///
+	real trackSize() { return trackEnd-trackStart; }
+	///
+	int value() { return _value; }
+	/// ditto
+	void value(int val) {
+		if(val < 0)
+			val = 0;
+		else if(val > _maxValue)
+			val = _maxValue;
+		if(val == _value)
+			return;
+		_value = val;
+		updateControls();
+		valueChanged(new EventArgs);
+	}
+	///
+	int maxValue() { return _maxValue; }
+	/// ditto
+	void maxValue(int val) {
+		if(val < 1)
+			val = 1;
+		if(val == _maxValue)
+			return;
+		_maxValue = val;
+		if(_value > _maxValue)
+			_value = _maxValue;
+		if(_visibleValue > _maxValue)
+			_visibleValue = _maxValue;
+		updateControls();
+	}
+	/**
+	 * A floating-point number between 0 and 1 that specifies how large the
+	 * thumb should be compared to the track. A value of 0 makes the thumb its
+	 * minimum size, and a value of 1 makes the thumb take up all of the track.
+	 * The default is 0.5.
+	 */
+	real visibleValue() { return _visibleValue; }
+	/// ditto
+	void visibleValue(real val) {
+		if(val < 0)
+			val = 0;
+		else if(val > 1)
+			val = 1;
+		if(val == _visibleValue)
+			return;
+		_visibleValue = val;
+		updateControls();
+	}
+}
+///
+class HScrollBar : ScrollBar {
+	this() {
+		_button1.direction = ArrowDirection.Left;
+		_button2.direction = ArrowDirection.Right;
+	}
+protected:
+	void whenThumbMouseDown(MouseEventArgs e) {
+		_thumbDragLoc = e.location.x;
+	}
+	void whenThumbMouseDragged(MouseEventArgs e) {
+		_thumb.state = ButtonState.Pressed;
+		thumbLocation = e.location.x + _thumb.location.x - _thumbDragLoc;
+	}
+	void putControl(Control c, real location, real size) {
+		c.location = [location, 0.0];
+		c.size = [size, height];
+	}
+	real breadth() { return height; }
+	real length() { return width; }
+	alias ScrollBar.thumbLocation thumbLocation;
+	real thumbLocation() { return _thumb.x; }
+	real thumbSize() { return _thumb.width; }
+	real trackStart() { return _track1.x; }
+	real trackEnd() { return _track2.x+_track2.width; }
+}
+///
+class VScrollBar : ScrollBar {
+	this() {
+		_button1.direction = ArrowDirection.Up;
+		_button2.direction = ArrowDirection.Down;
+	}
+protected:
+	void whenThumbMouseDown(MouseEventArgs e) {
+		_thumbDragLoc = e.location.y;
+	}
+	void whenThumbMouseDragged(MouseEventArgs e) {
+		_thumb.state = ButtonState.Pressed;
+		thumbLocation = e.location.y + _thumb.location.y - _thumbDragLoc;
+	}
+	void putControl(Control c, real location, real size) {
+		c.location = [0.0, location];
+		c.size = [width, size];
+	}
+	real breadth() { return width; }
+	real length() { return height; }
+	alias ScrollBar.thumbLocation thumbLocation;
+	real thumbLocation() { return _thumb.y; }
+	real thumbSize() { return _thumb.height; }
+	real trackStart() { return _track1.y; }
+	real trackEnd() { return _track2.y+_track2.height; }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/scrollable.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,308 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.scrollable;
+
+import dynamin.all_core;
+import dynamin.all_painting;
+import dynamin.all_gui;
+import tango.io.Stdout;
+
+///
+enum VisibleScrollBars {
+	/**
+	  * Shows a vertical scroll bar when it is needed. A horizontal scroll bar
+	  * is never shown. A text box with line wrap turned on could use this
+	  * option.
+	  */
+	Vertical,
+	/**
+	  * Shows a horizontal scroll bar when it is needed. A vertical scroll bar
+	  * is never shown. The list view in Windows Explorer could use this option.
+	  */
+	Horizontal,
+	/**
+	 * Shows either scroll bar when it is needed.
+	 * A control, such as the detail view in Windows Explorer, that changes its
+	 * width when not being scrolled should use this option.
+	 */
+	Each,
+	/**
+	  * Shows both scroll bars when both are needed.
+	  * A control, such as a text box, that changes its width based upon
+	  * where it is scrolled to should use this option.
+	  */
+	Both,
+	/**
+	  * Does not show any scroll bars, even if they are needed.
+	  * A single-line text box uses this option.
+	  */
+	None
+}
+
+class ScrollableClipper : Control {
+protected:
+	override void whenPainting(PaintingEventArgs e) {
+		auto par = cast(Scrollable)_parent;
+		e.graphics.fillRule = GraphicsFillRule.EvenOdd;
+		e.graphics.rectangle(Rect(0, 0, width, height));
+		e.graphics.rectangle(par.contentVisibleRect);
+		e.graphics.clip();
+		e.graphics.source = par.foreColor;
+		Theme.current.Scrollable_paint(par, e.graphics);
+	}
+public:
+	override bool contains(Point pt) {
+		auto par = cast(Scrollable)_parent;
+		if(par is null)
+			return false;
+		return !par.contentVisibleRect.contains(pt);
+	}
+}
+
+class ScrollableContent : Panel {
+private:
+	Scrollable sc;
+	package this(Scrollable s) {
+		sc = s;
+	}
+protected:
+	public override Size bestSize() {
+		return sc.contentBestSize;
+	}
+	override void whenMoved(EventArgs e) {
+		sc.whenContentMoved(e);
+	}
+	override void whenResized(EventArgs e) {
+		sc.whenContentResized(e);
+	}
+	override void whenMouseEntered(EventArgs e) {
+		sc.whenContentMouseEntered(e);
+	}
+	override void whenMouseLeft(EventArgs e) {
+		sc.whenContentMouseLeft(e);
+	}
+	override void whenMouseDown(MouseEventArgs e) {
+		sc.whenContentMouseDown(e);
+	}
+	override void whenMouseUp(MouseEventArgs e) {
+		sc.whenContentMouseUp(e);
+	}
+	override void whenMouseMoved(MouseEventArgs e) {
+		sc.whenContentMouseMoved(e);
+	}
+	override void whenMouseDragged(MouseEventArgs e) {
+		sc.whenContentMouseDragged(e);
+	}
+	override void whenPainting(PaintingEventArgs e) {
+		sc.whenContentPainting(e);
+	}
+}
+
+/**
+ * Here is a skeleton implementation of a scrollable control:
+ * -----
+ * class YourControl : Scrollable {
+ *   protected override void whenContentPainting(PaintingEventArgs e) {
+ * 	   with(e.graphics) {
+ * 	     drawText("Hello World", 5, 5);
+ * 	   }
+ *   }
+ * }
+ * -----
+ */
+abstract class Scrollable : Container {
+private:
+	Panel _content;
+protected:
+	VisibleScrollBars _scrollBars;
+	ScrollBar _hScrollBar, _vScrollBar;
+	ScrollableClipper _clipper;
+
+	real _sbSize;
+	real _leftControlsWidth, _topControlsHeight;
+	// the area the content could be shown if no scroll bars
+	Rect _availableRect;
+	// the area the content is actually visible, after scroll bars are
+	// taken into account
+	Rect _visibleRect;
+	bool _hVisible, _vVisible;
+	override void whenMouseTurned(MouseTurnedEventArgs e) {
+		_vScrollBar.value = _vScrollBar.value + cast(int)(10*e.scrollAmount);
+	}
+
+	Size contentBestSize() { return Size(0, 0); } ///
+	void whenContentMoved(EventArgs e) { } ///
+	void whenContentResized(EventArgs e) { } ///
+	void whenContentMouseEntered(EventArgs e) { } ///
+	void whenContentMouseLeft(EventArgs e) { } ///
+	void whenContentMouseDown(MouseEventArgs e) { } ///
+	void whenContentMouseUp(MouseEventArgs e) { } ///
+	void whenContentMouseMoved(MouseEventArgs e) { } ///
+	void whenContentMouseDragged(MouseEventArgs e) { } ///
+	void whenContentPainting(PaintingEventArgs e) { } ///
+
+	this() {
+		_elasticX = true;
+		_elasticY = true;
+
+		_scrollBars = VisibleScrollBars.Each;
+		//content = c;
+		_content = new ScrollableContent(this);
+		_content.location = [borderSize.left, borderSize.top];
+		add(_content);
+
+		_clipper = new ScrollableClipper;
+		add(_clipper);
+
+		_vScrollBar = new VScrollBar;
+		_vScrollBar.valueChanged += &whenValueChanged;
+		_hScrollBar = new HScrollBar;
+		_hScrollBar.valueChanged += &whenValueChanged;
+		layout();
+	}
+	void whenValueChanged(EventArgs e) {
+		_content.location = [_visibleRect.x-_hScrollBar.value, _visibleRect.y-_vScrollBar.value];
+	}
+public:
+	override void layout() {
+		_sbSize = Theme.current.ScrollBar_size;
+		_leftControlsWidth = 0;
+		//foreach(c; leftControls)
+		//	total += c.bestSize.width;
+		_topControlsHeight = 0;
+		//foreach(c; topControls)
+		//	total += c.bestSize.height;
+		_availableRect = Rect(0, 0, width, height) - borderSize -
+			BorderSize(_leftControlsWidth, _topControlsHeight, 0, 0);
+		_hVisible = HScrollBarVisible;
+		_vVisible = VScrollBarVisible;
+		_visibleRect = _availableRect;
+		if(_hVisible)
+			_visibleRect.height = _visibleRect.height - _sbSize;
+		if(_vVisible)
+			_visibleRect.width = _visibleRect.width - _sbSize;
+
+		remove(_hScrollBar);
+		remove(_vScrollBar);
+		if(_hVisible)
+			add(_hScrollBar);
+		if(_vVisible)
+			add(_vScrollBar);
+
+		_content.size = _content.bestSize;
+		if(_content.width < _visibleRect.width)
+			_content.size = Size(_visibleRect.width, _content.height);
+		if(_content.height < _visibleRect.height)
+			_content.size = Size(_content.width, _visibleRect.height);
+		_clipper.size = size;
+
+		_vScrollBar.maxValue = cast(int)(_content.height-_visibleRect.height);
+		_vScrollBar.visibleValue = _visibleRect.height/_content.height;
+		_hScrollBar.maxValue = cast(int)(_content.width-_visibleRect.width);
+		_hScrollBar.visibleValue = _visibleRect.width/_content.width;
+
+		_vScrollBar.location = [_visibleRect.right, _visibleRect.y];
+		_vScrollBar.size = [_sbSize, _visibleRect.height];
+		_hScrollBar.location = [_visibleRect.x, _visibleRect.bottom];
+		_hScrollBar.size = [_visibleRect.width, _sbSize];
+
+		whenValueChanged(null);
+	}
+	/**
+	 * Gets or sets which scroll bars are shown. The default is Each.
+	 */
+	VisibleScrollBars visibleScrollBars() { return _scrollBars; }
+	/// ditto
+	void visibleScrollBars(VisibleScrollBars bars) { // TODO: rename? SBPolicy?
+		_scrollBars = bars;
+	}
+	/**
+	 * Gets whether the horizontal scroll bar is currently shown.
+	 */
+	bool HScrollBarVisible() {
+		switch(_scrollBars) {
+		case VisibleScrollBars.None:
+		case VisibleScrollBars.Vertical:
+			return false;
+		case VisibleScrollBars.Horizontal:
+			return _content.bestSize.width > _availableRect.width;
+		case VisibleScrollBars.Each:
+			// if vertical scroll bar shown
+			if(_content.bestSize.height > _availableRect.height)
+				return _content.bestSize.width > _availableRect.width-_sbSize;
+			else
+				return _content.bestSize.width > _availableRect.width;
+		case VisibleScrollBars.Both:
+			return _content.bestSize.width > _availableRect.width ||
+				_content.bestSize.height > _availableRect.height;
+		}
+	}
+	/**
+	 * Gets whether the vertical scroll bar is currently shown.
+	 */
+	bool VScrollBarVisible() {
+		switch(_scrollBars) {
+		case VisibleScrollBars.None:
+		case VisibleScrollBars.Horizontal:
+			return false;
+		case VisibleScrollBars.Vertical:
+			return _content.bestSize.height > _availableRect.height;
+		case VisibleScrollBars.Each:
+			// if horizontal scroll bar shown
+			if(_content.bestSize.width > _availableRect.width)
+				return _content.bestSize.height > _availableRect.height-_sbSize;
+			else
+				return _content.bestSize.height > _availableRect.height;
+		case VisibleScrollBars.Both:
+			return _content.bestSize.height > _availableRect.height ||
+				_content.bestSize.width > _availableRect.width;
+		}
+	}
+	/**
+	 * Gets the combined width of all the controls docked on the left side of
+	 * this scrollable.
+	 */
+	real leftControlsWidth() {
+		return _leftControlsWidth;
+	}
+	/**
+	 * Gets the combined height of all the controls docked on the top side of
+	 * this scrollable.
+	 */
+	real topControlsHeight() {
+		return _topControlsHeight;
+	}
+
+	/// Returns the area inside the border, scroll bars, and any controls on the side.
+	Rect contentVisibleRect() {
+		return _visibleRect;
+	}
+	Panel content() { return _content; }
+	BorderSize borderSize() {
+		return Theme.current.Scrollable_borderSize(this);
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/text_box.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,128 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.text_box;
+
+import dynamin.all_painting;
+import dynamin.all_core;
+import dynamin.all_gui;
+
+/**
+ * A control that allows text to be entered and edited.
+ *
+ * The appearance of a text box with Windows Classic:
+ *
+ * $(IMAGE ../web/example_text_box.png)
+ */
+class TextBox : Scrollable {
+protected:
+	string _text;
+	int _caret = 0;
+	override Size contentBestSize() {
+		return Size(0, 0);
+	}
+	override void whenContentPainting(PaintingEventArgs e) {
+		with(e.graphics) {
+			source = Color.White;
+			paint();
+			source = Color.Black;
+			drawText(_text, 1, 1);
+			if(focused) {
+				Size ex = getTextExtents(_text[0.._caret]);
+				moveTo(1+ex.width+0.5, 1);
+				lineTo(1+ex.width+0.5, 14);
+				stroke();
+			}
+		}
+	}
+	override void whenContentMouseDown(MouseEventArgs e) {
+		focus();
+		repaint();
+	}
+	override void whenKeyDown(KeyEventArgs e) {
+		switch(e.key) {
+		case Key.Backspace:
+			if(_caret > 0)
+				_text = _text[0.._caret-1] ~ _text[_caret..$];
+			if(_caret > 0)
+				_caret--;
+			break;
+		case Key.Delete:
+			if(_text.length > _caret)
+				_text = _text[0.._caret] ~ _text[_caret+1..$];
+			break;
+		case Key.Right:
+			if(_caret < _text.length)
+				_caret++;
+			break;
+		case Key.Left:
+			if(_caret > 0)
+				_caret--;
+			break;
+		case Key.Home:
+			_caret = 0;
+			break;
+		case Key.End:
+			_caret = _text.length;
+			break;
+		default:
+			return;
+		}
+		repaint();
+	}
+	override void whenKeyTyped(KeyTypedEventArgs e) {
+		_text = format("{}{}{}", _text[0.._caret], e.character, _text[_caret..$]);
+		_caret++;
+		repaint();
+	}
+	override void whenMouseDown(MouseEventArgs e) {
+		focus();
+	}
+public:
+	override Size bestSize() {
+		// TODO: columns and rows
+		return Size(100, 20);
+	}
+	override int baseline() { return 14; }
+
+	/// This event occurs after the selection has changed.
+	Event!() SelectionChanged;
+	/// Override this method in a subclass to handle the SelectionChanged event.
+	protected void whenSelectionChanged(EventArgs e) { }
+
+	this() {
+		SelectionChanged = new Event!()(&whenSelectionChanged);
+
+		super();
+		_focusable = true;
+		content.cursor = Cursor.Text;
+
+		// TODO: change if Multiline is added
+		visibleScrollBars = VisibleScrollBars.None;
+		elasticX = true;
+		elasticY = false;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/theme.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,143 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.theme;
+
+import dynamin.core.string;
+import dynamin.all_painting;
+import dynamin.all_gui;
+import dynamin.c.cairo;
+import tango.stdc.stdlib;
+
+/**
+ * Draws a rectangle with 1 pixel wide lines inside the specified bounds. The
+ * left and top are drawn with the first color, and the right and
+ * bottom are drawn with the second color.
+ */
+void draw3dRectangle(Graphics g, real x, real y, real width, real height,
+	Color c1, Color c2) {
+	g.lineWidth = 1;
+
+	g.source = c1;
+	g.moveTo(x+0.5, y+height-1);
+	g.lineTo(x+0.5, y+0.5);
+	g.lineTo(x+width-1, y+0.5);
+	g.stroke();
+
+	g.source = c2;
+	g.moveTo(x+width-0.5, y);
+	g.lineTo(x+width-0.5, y+height-0.5);
+	g.lineTo(x, y+height-0.5);
+	g.stroke();
+}
+
+void drawCheckerboard(Graphics g, real x, real y, real width, real height,
+	Color c1, Color c2, int squareSize = 1) {
+	drawCheckerboard(g, Rect(x, y, width, height), c1, c2, squareSize);
+}
+void drawCheckerboard(Graphics g, Rect rect,
+	Color c1, Color c2, int squareSize = 1) {
+	int width = cast(int)rect.width;
+	int height = cast(int)rect.height;
+	auto format = c1.A == 255 && c2.A == 255 ?
+		CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32;
+	int* data = cast(int*)malloc(width*height*4);
+	scope(exit) free(data);
+	c1.R = c1.R*c1.A/255;
+	c1.G = c1.G*c1.A/255;
+	c1.B = c1.B*c1.A/255;
+	c2.R = c2.R*c2.A/255;
+	c2.G = c2.G*c2.A/255;
+	c2.B = c2.B*c2.A/255;
+	int pixel1 = *cast(int*)&c1;
+	int pixel2 = *cast(int*)&c2;
+
+	bool oddRow;
+	int squareSize2 = squareSize * 2;
+	for(int i = 0; i < height; ++i) {
+		oddRow = i % squareSize2 < squareSize;
+		for(int j = 0; j < width; ++j) {
+			if(oddRow ^ j % squareSize2 < squareSize)
+				data[j+i*width] = pixel1;
+			else
+				data[j+i*width] = pixel2;
+		}
+	}
+
+	auto surface = cairo_image_surface_create_for_data(cast(char*)data, format, width, height, width*4);
+	g.save();
+	cairo_set_source_surface(g.handle, surface, rect.x, rect.y);
+	g.rectangle(rect);
+	g.fill();
+	g.restore();
+	cairo_surface_destroy(surface);
+}
+
+abstract class Theme {
+static {
+	int _curIndex = 0;
+	Theme[] _themes;
+	Theme[] getAll() { return _themes.dup; }
+	void add(Theme theme) { // TODO: rename to register()?
+		_themes.length = _themes.length+1;
+		_themes[$-1] = theme;
+	}
+	Theme current() { return _themes[_curIndex]; }
+	void current(Theme theme) {
+		foreach(i, t; _themes) {
+			if(t is theme) {
+				_curIndex = i;
+				// TODO: relayout all windows
+			}
+		}
+	}
+	//Theme system() { return ; }
+}
+	string name();
+
+	//all theme methods follow
+
+	// TODO: need to have bestSize, BorderSize, foreColor, backColor,
+	//       Font, and Paint for all controls
+	// TODO: have default imps for foreColor, backColor, and Font:
+	//       { return Control_ForeColor; }
+	//       { return Control_Font; }
+	void Window_paint(Window c, Graphics g);
+	Size Button_bestSize(Button c);
+	void Button_paint(Button c, Graphics g);
+	void CheckBox_paint(CheckBox c, Graphics g);
+	void RadioButton_paint(CheckBox c, Graphics g);
+	void ScrollBarTrack_paint(ScrollBarTrack c, Graphics g);
+	void ScrollBarThumb_paint(ScrollBarThumb c, Graphics g);
+	real ScrollBar_size();
+	void ArrowButton_paint(ArrowButton c, Graphics g);
+	BorderSize Scrollable_borderSize(Scrollable c);
+	void Scrollable_paint(Scrollable c, Graphics g);
+	BorderSize Notebook_borderSize(Notebook c);
+	void Tab_paint(TabPage page, Notebook c, Graphics g);
+	void Notebook_paint(Notebook c, Graphics g);
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/window.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,391 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.window;
+
+import dynamin.c.cairo;
+import dynamin.all_core;
+import dynamin.all_painting;
+import dynamin.all_gui;
+import dynamin.gui.control;
+import dynamin.gui.backend;
+import dynamin.gui.container;
+import dynamin.gui.events;
+import tango.io.Stdout;
+import tango.core.Exception;
+import tango.core.Thread;
+
+static class Application {
+static:
+	mixin ApplicationBackend;
+	package Thread eventThread;
+	void run(Window w = null) {
+		Window.hasProcessedEvents = true;
+
+		auto thread = Thread.getThis();
+		assert(eventThread is null || eventThread is thread,
+			"Application.run called from two different threads");
+		eventThread = thread;
+
+		backend_run(w);
+	}
+	//void invoke(void delegate() dg) {
+		//
+	//}
+	//void invokeNow(void delegate() dg)
+}
+
+enum DialogResult {
+	///
+	OK,
+	///
+	Yes,
+	///
+	No,
+	///
+	Cancel,
+	///
+	Custom
+}
+
+///
+enum Position {
+	/// Specifies being at the top-left corner.
+	TopLeft,
+	/// Specifies being centered between the top-left corner and the top-right corner.
+	Top,
+	/// Specifies being at the top-right corner.
+	TopRight,
+	/// Specifies being centered between the top-left corner and the bottom-left corner.
+	Left,
+	/// Specifies being centered between all corners.
+	Center,
+	/// Specifies being centered between the top-right corner and the bottom-right corner.
+	Right,
+	/// Specifies being at the bottom-left corner.
+	BottomLeft,
+	/// Specifies being centered between the bottom-left corner and the bottom-right corner.
+	Bottom,
+	/// Specifies being at the bottom-right corner.
+	BottomRight
+}
+
+/**
+ * The different types of borders that a window may have.
+ * These do not affect whether the window is resizable--
+ * use Window.Resizable for that.
+ */
+enum WindowBorderStyle {
+	/** Specifies that the window has no border around the content area. */
+	None,
+	/**
+	 * Specifies that the window has a normal border with a title bar, icon,
+	 * and minimize button.
+	 */
+	Normal,
+	/** Specifies that the window has a normal border without a minimize button. */
+	Dialog,
+	/** Specifies that the window has the border of a floating tool box. */
+	Tool
+}
+
+alias List!(Control) ControlList;
+//Frames and Dialogs are identical except that Dialogs do not have minimize and
+//maximize buttons, are not shown on the taskbar, and can be modal.
+/**
+ * A window is a top level control that has no parent. Its location is relative
+ * to the top-left corner of the screen.
+ * A window can have no border, the border of a normal window, or the border
+ * of a tool window.
+ *
+ * The appearance of a window with Windows Classic:
+ *
+ * $(IMAGE ../web/example_window.png)
+ */
+class Window : Container {
+	private static hasProcessedEvents = false;
+	~this() { // this should be a static ~this, but I get a circular dep error
+		if(!hasProcessedEvents) {
+			Stdout("Warning: a window was created, but Application.run");
+			Stdout(" was not called to process events").newline;
+		}
+	}
+protected:
+	mixin WindowBackend;
+	BorderSize _borderSize;
+	Window _owner;
+	WindowBorderStyle _borderStyle;
+	bool _resizable = true;
+	Panel _content;
+	Control _focusedControl;
+	package Control focusedControl() { return _focusedControl; }
+	package void focusedControl(Control c) {
+		_focusedControl = c;
+	}
+	override void dispatchPainting(PaintingEventArgs e) {
+		Theme.current.Window_paint(this, e.graphics);
+		super.dispatchPainting(e);
+	}
+public:
+	this() {
+		_children = new ControlList();
+		content = new Panel;
+
+		_visible = false;
+		_minSize = Size(0, 0);
+		_maxSize = Size(0, 0);
+		_borderStyle = WindowBorderStyle.Normal;
+		recreateHandle();
+	}
+	this(string text) {
+		this();
+		this.text = text;
+	}
+
+	void content(Panel panel) {
+		if(panel is null)
+			throw new IllegalArgumentException("content must not be null");
+		// TODO: remove handlers
+		super.remove(panel);
+		super.add(_content = panel);
+		_content.resized += &whenContentResized;
+		_content.minSizeChanged += &whenContentMinSizeChanged;
+		_content.maxSizeChanged += &whenContentMaxSizeChanged;
+
+		auto best = _content.bestSize;
+		_content.minSize = best;
+		_content.maxSize = Size(_content.elasticX ? 0 : best.width,
+		                        _content.elasticY ? 0 : best.height);
+		resizable = _content.maxSize != best; // avoid calling elasticX/Y again
+		_content.size = best;
+
+	}
+	bool ignoreResize;
+	void whenContentResized(EventArgs e) {
+		if(ignoreResize)
+			return;
+		ignoreResize = true;
+		size = _content.size + _borderSize;
+		ignoreResize = false;
+	}
+	void whenContentMinSizeChanged(EventArgs e) {
+		if(!handleCreated)
+			return;
+		backend_contentMinSizeChanged;
+	}
+	void whenContentMaxSizeChanged(EventArgs e) {
+		if(!handleCreated)
+			return;
+		backend_contentMaxSizeChanged;
+	}
+	override void whenResized(EventArgs e) {
+		if(ignoreResize)
+			return;
+		_content._location = Point(_borderSize.left, _borderSize.top);
+		ignoreResize = true;
+		_content.size = _size-_borderSize;
+		ignoreResize = false;
+	}
+	Panel content() {
+		return _content;
+	}
+	/**
+	 * If the handle has not yet been created, calling this will cause it to be.
+	 * Under the Windows backend, returns a HWND.
+	 * Under the X backend, returns a Window.
+	 * Returns: the backend specific native handle.
+	 */
+	typeof(_handle) handle() {
+		if(!handleCreated)
+			recreateHandle();
+		assert(Thread.getThis() is Application.eventThread ||
+				Application.eventThread is null,
+			"controls must be accessed and changed only on the event thread");
+		return _handle;
+	}
+	bool handleCreated() { return backend_handleCreated; }
+	void recreateHandle() {
+		backend_recreateHandle();
+	}
+	override protected Graphics quickCreateGraphics() {
+		if(!handleCreated)
+			return null;
+		return backend_quickCreateGraphics();
+	}
+	override bool onScreen() {
+		return true;
+	}
+	override Point screenLocation() {
+		return location;
+	}
+	override Point contentToScreen(Point pt) {
+		return pt + location;
+	}
+	override Point screenToContent(Point pt) {
+		return pt - location;
+	}
+	override bool topLevel() { return true; }
+	override Container parent() { return null; }
+	// TODO: because you should always be able to click a window from the taskbar,
+	//       then show it on taskbar if window has an owner, but don't if it does not
+	void owner(Window w) {
+		_owner = w;
+		if(!handleCreated)
+			return;
+		recreateHandle();
+	}
+	Window owner() { return _owner; }
+	alias Control.visible visible;
+	void visible(bool b) {
+		_visible = b;
+		backend_visible = b;
+	}
+	/**
+	 * Gets or sets what border this window will have around its contents.
+	 * The default is WindowBorderStyle.Normal.
+	 */
+	WindowBorderStyle borderStyle() { return _borderStyle; }
+	/// ditto
+	void borderStyle(WindowBorderStyle border) {
+		if(border > WindowBorderStyle.Tool)
+			throw new IllegalArgumentException("Window.borderStyle(): invalid border style");
+		_borderStyle = border;
+		backend_borderStyle = border;
+	}
+	alias Control.repaint repaint;
+	void repaint(Rect rect) {
+		if(!handleCreated)
+			return;
+		backend_repaint(rect);
+	}
+	/**
+	 * An array of rectangles in screen coordinates that the window will be
+	 * snapped to.
+	 */
+	Rect[] snapRects = null;
+	/**
+	 * Convenience method that sets SnapRects to an array
+	 * with just the specified Rect.
+	 */
+	void snapRect(Rect rect) {
+		snapRects = [rect];
+	}
+	/**
+	 * The SnapDistance specifies how close a window has to be to a
+	 * snap rectangle for the window to snap to it. The default is 10 pixels.
+	 */
+	uint snapDistance = 10;
+	/**
+	 * Gets or sets whether this window can be resized by the user.
+	 * The default is true.
+	 */
+	bool resizable() { return _resizable; }
+	/// ditto
+	void resizable(bool b) { // TODO: set based upon whether content is elastic?
+		_resizable = b;
+		if(!handleCreated)
+			return;
+		backend_resizable = b;
+	}
+	// TODO: 1.0  MinSize -> contentMinSize  MaxSize -> contentMaxSize
+	alias Control.location location;
+	void location(Point pt) {
+		super.location(pt);
+		if(!handleCreated)
+			return;
+		backend_location = pt;
+	}
+	alias Control.size size;
+	void size(Size size) {
+		super.size(size);
+		_content.size = size - _borderSize;
+		if(!handleCreated)
+			return;
+		backend_size = size;
+	}
+	/**
+	 * Gets the size of the border/frame around this window.
+	 */
+	BorderSize borderSize() {
+		return _borderSize;
+	}
+	alias Control.text text;
+	void text(string str) {
+		super.text(str);
+		if(!handleCreated)
+			return;
+		backend_text = str;
+	}
+	/**
+	 * Moves this window to the specified position relative to
+	 * the specified control. If no control is specified, the
+	 * window is positioned relative to the screen.
+	 */
+	void position(Position pos, Control c = null) {
+		Rect rect;
+		if(c && c.onScreen) {
+			rect = c.screenLocation + c.size;
+		} else {
+			rect = desktopRect;
+		}
+		Point newLoc = Point();
+		switch(pos) {
+		case Position.TopLeft:
+		case Position.Left:
+		case Position.BottomLeft:
+			newLoc.x = rect.x;
+			break;
+		case Position.Top:
+		case Position.Center:
+		case Position.Bottom:
+			newLoc.x = rect.x + (rect.width - width)/2;
+			break;
+		case Position.TopRight:
+		case Position.Right:
+		case Position.BottomRight:
+			newLoc.x = rect.x + rect.width - width;
+			break;
+		}
+		switch(pos) {
+		case Position.TopLeft:
+		case Position.Top:
+		case Position.TopRight:
+			newLoc.y = rect.y;
+			break;
+		case Position.Left:
+		case Position.Center:
+		case Position.Right:
+			newLoc.y = rect.y + (rect.height - height)/2;
+			break;
+		case Position.BottomLeft:
+		case Position.Bottom:
+		case Position.BottomRight:
+			newLoc.y = rect.y + rect.height - height;
+			break;
+		}
+		location = newLoc;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/windows_clipboard.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,75 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.windows_clipboard;
+
+import dynamin.c.windows;
+import Utf = tango.text.convert.Utf;
+
+template ClipboardBackend() {
+	void backend_setText(string text) {
+		if(!OpenClipboard(msgWnd))
+			return;
+		EmptyClipboard();
+		auto wtext = Utf.toString16(text);
+		HGLOBAL hmem = GlobalAlloc(GMEM_MOVEABLE, (wtext.length+1)*wchar.sizeof);
+		wchar* data = cast(wchar*)GlobalLock(hmem);
+		data[0..wtext.length] = wtext;
+		data[wtext.length] = 0;
+		GlobalUnlock(hmem);
+		SetClipboardData(CF_UNICODETEXT, data);
+		CloseClipboard();
+	}
+	string backend_getText() {
+		if(!OpenClipboard(msgWnd))
+			return null;
+		wchar* data = cast(wchar*)GetClipboardData(CF_UNICODETEXT);
+		CloseClipboard();
+		if(data is null)
+			return null;
+		int i = 0;
+		while(data[i] != '\0')
+			++i;
+		if(i == 0)
+			return null;
+		return Utf.toString(data[0..i]);
+	}
+	bool backend_containsText() {
+		return IsClipboardFormatAvailable(CF_UNICODETEXT) ? true : false;
+	}
+}
+
+// Windows only has one clipboard
+template SelectionBackend() {
+	void backend_setText(string text) {
+	}
+	string backend_getText() {
+		return null;
+	}
+	bool backend_containsText() {
+		return false;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/windows_cursor.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,140 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.windows_cursor;
+
+public import dynamin.c.windows;
+public import tango.io.Stdout;
+public import dynamin.gui.control;
+
+// TODO: use import("AeroMouseDrag.png"), import("MouseDrag.png"), etc.
+// to embed png into executable. Then, use lodepng to decode it and use
+// CreateIconIndirect to create a cusor from them if possible.
+
+template CursorBackend() {
+	HCURSOR _handle;
+	bool isBuiltin = false;
+	this(HCURSOR h) {
+		_handle = h;
+		isBuiltin = true;
+	}
+static:
+	void backend_SetCurrent(Control c, Cursor cur) {
+		assert(cur.isBuiltin); // TODO: allow custom cursors
+		SetCursor(cur._handle);
+	}
+	Cursor none = null;
+	Cursor arrow = null;
+	Cursor waitArrow = null;
+	Cursor wait = null;
+	Cursor text = null;
+	Cursor hand = null;
+	Cursor move = null;
+	Cursor resizeHoriz = null;
+	Cursor resizeVert = null;
+	Cursor resizeBackslash = null;
+	Cursor resizeSlash = null;
+	Cursor drag = null; // from resource
+	Cursor invalidDrag = null;
+	Cursor reversedArrow = null; // from resource
+	Cursor crosshair = null;
+
+	Cursor backend_maybeLoad(Cursor* cache, int curRes) {
+		if(*cache is null) {
+			HCURSOR hcur = LoadImage(null, MAKEINTRESOURCE(curRes),
+					IMAGE_CURSOR,  0, 0, LR_SHARED | LR_DEFAULTSIZE);
+			if(hcur is null)
+				Stdout.format("LoadImage() failed loading cursor {}", curRes).newline;
+			else
+				*cache = new Cursor(hcur);
+		}
+		return *cache;
+	}
+	Cursor backend_maybeLoad(Cursor* cache, wchar[] name) {
+		if(*cache is null) {
+			HCURSOR hcur = LoadImage(GetModuleHandle(null), name.ptr,
+				IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
+			if(hcur is null)
+				Stdout.format("LoadImage() failed loading cursor {}", name).newline;
+			else
+				*cache = new Cursor(hcur);
+		}
+		return *cache;
+	}
+	Cursor backend_None() {
+		if(none is null)
+			none = new Cursor(cast(HCURSOR)null);
+		return none;
+	}
+	Cursor backend_Arrow() {
+		return backend_maybeLoad(&arrow, OCR_NORMAL);
+	}
+	Cursor backend_WaitArrow() {
+		return backend_maybeLoad(&waitArrow, OCR_APPSTARTING);
+	}
+	Cursor backend_Wait() {
+		return backend_maybeLoad(&wait, OCR_WAIT);
+	}
+	Cursor backend_Text() {
+		return backend_maybeLoad(&text, OCR_IBEAM);
+	}
+	Cursor backend_Hand() {
+		return backend_maybeLoad(&hand, OCR_HAND); // Windows 98 & newer
+	}
+	Cursor backend_Move() {
+		return backend_maybeLoad(&move, OCR_SIZEALL);
+	}
+	Cursor backend_ResizeHoriz() {
+		return backend_maybeLoad(&resizeHoriz, OCR_SIZEWE);
+	}
+	Cursor backend_ResizeVert() {
+		return backend_maybeLoad(&resizeVert, OCR_SIZENS);
+	}
+	Cursor backend_ResizeBackslash() {
+		return backend_maybeLoad(&resizeBackslash, OCR_SIZENWSE);
+	}
+	Cursor backend_ResizeSlash() {
+		return backend_maybeLoad(&resizeSlash, OCR_SIZENESW);
+	}
+	Cursor backend_Drag() {
+		if(checkWindowsVersion(WindowsVersion.WindowsVista))
+			return backend_maybeLoad(&drag, "AeroDragCur");
+		else
+			return backend_maybeLoad(&drag, "DragCur");
+	}
+	Cursor backend_InvalidDrag() {
+		return backend_maybeLoad(&invalidDrag, OCR_NO);
+	}
+	Cursor backend_ReversedArrow() {
+		if(checkWindowsVersion(WindowsVersion.WindowsVista))
+			return backend_maybeLoad(&drag, "AeroReversedArrowCur");
+		else
+			return backend_maybeLoad(&drag, "ReversedArrowCur");
+	}
+	Cursor backend_Crosshair() {
+		return backend_maybeLoad(&crosshair, OCR_CROSS);
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/windows_theme.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,541 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.windows_theme;
+
+import dynamin.core.string;
+import dynamin.all_painting;
+import dynamin.all_gui;
+import tango.io.Stdout;
+import Utf = tango.text.convert.Utf;
+version(Windows) import dynamin.c.windows;
+version(Windows) import dynamin.c.windows_tmschema;
+import dynamin.gui_backend;
+
+version(Windows) {
+} else {
+enum {
+	COLOR_SCROLLBAR       = 0,
+	COLOR_BACKGROUND      = 1,
+	COLOR_ACTIVECAPTION   = 2,
+	COLOR_INACTIVECAPTION = 3,
+	COLOR_MENU            = 4,
+	COLOR_WINDOW          = 5,
+	COLOR_WINDOWFRAME     = 6,
+	COLOR_MENUTEXT        = 7,
+	COLOR_WINDOWTEXT      = 8,
+	COLOR_CAPTIONTEXT     = 9,
+	COLOR_ACTIVEBORDER    = 10,
+	COLOR_INACTIVEBORDER  = 11,
+	COLOR_APPWORKSPACE    = 12,
+	COLOR_HIGHLIGHT       = 13,
+	COLOR_HIGHLIGHTTEXT   = 14,
+	COLOR_BTNFACE         = 15,
+	COLOR_BTNSHADOW       = 16,
+	COLOR_GRAYTEXT        = 17,
+	COLOR_BTNTEXT         = 18,
+	COLOR_INACTIVECAPTIONTEXT = 19,
+	COLOR_BTNHIGHLIGHT    = 20,
+
+	COLOR_3DDKSHADOW      = 21,
+	COLOR_3DLIGHT         = 22,
+	COLOR_INFOTEXT        = 23,
+	COLOR_INFOBK          = 24,
+
+	COLOR_HOTLIGHT        = 26,
+	COLOR_GRADIENTACTIVECAPTION = 27,
+	COLOR_GRADIENTINACTIVECAPTION = 28,
+	COLOR_MENUHILIGHT     = 29,
+	COLOR_MENUBAR         = 30,
+
+	COLOR_DESKTOP         = COLOR_BACKGROUND,
+	COLOR_3DFACE          = COLOR_BTNFACE,
+	COLOR_3DSHADOW        = COLOR_BTNSHADOW,
+	COLOR_3DHIGHLIGHT     = COLOR_BTNHIGHLIGHT,
+	COLOR_3DHILIGHT       = COLOR_BTNHIGHLIGHT,
+	COLOR_BTNHILIGHT      = COLOR_BTNHIGHLIGHT
+}
+}
+
+/**
+ * This file/module, even though it starts with 'windows_', is not part
+ * of the windows backend. It is a theme that should run on any system,
+ * but can only show visual styles under Windows because the
+ * Windows API is needed.
+ */
+
+static this() {
+	Theme.add(new WindowsTheme());
+}
+
+/**
+ * This theme should work with any backend, but can only paint the Windows
+ * classic look if not on Windows XP, Vista, or newer.
+ */
+class WindowsTheme : Theme {
+version(Windows) {
+	static string defaultFont() { // TODO: rename
+		NONCLIENTMETRICS ncMetrics;
+		ncMetrics.cbSize = NONCLIENTMETRICS.sizeof;
+		SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
+			NONCLIENTMETRICS.sizeof, &ncMetrics, 0);
+		// TODO: need to strip the nulls off the name
+		return Utf.toString(ncMetrics.lfMessageFont.lfFaceName);
+	}
+}
+version(Windows) {
+	static void printSysColors() {
+		int[string] colors = [
+		"COLOR_SCROLLBAR"[] : COLOR_SCROLLBAR,
+		"COLOR_BACKGROUND" : COLOR_BACKGROUND,
+		"COLOR_ACTIVECAPTION" : COLOR_ACTIVECAPTION,
+		"COLOR_INACTIVECAPTION" : COLOR_INACTIVECAPTION,
+		"COLOR_MENU" : COLOR_MENU,
+		"COLOR_WINDOW" : COLOR_WINDOW,
+		"COLOR_WINDOWFRAME" : COLOR_WINDOWFRAME,
+		"COLOR_MENUTEXT" : COLOR_MENUTEXT,
+		"COLOR_WINDOWTEXT" : COLOR_WINDOWTEXT,
+		"COLOR_CAPTIONTEXT" : COLOR_CAPTIONTEXT,
+		"COLOR_ACTIVEBORDER" : COLOR_ACTIVEBORDER,
+		"COLOR_INACTIVEBORDER" : COLOR_INACTIVEBORDER,
+		"COLOR_APPWORKSPACE" : COLOR_APPWORKSPACE,
+		"COLOR_HIGHLIGHT" : COLOR_HIGHLIGHT,
+		"COLOR_HIGHLIGHTTEXT" : COLOR_HIGHLIGHTTEXT,
+		"COLOR_BTNFACE" : COLOR_BTNFACE,
+		"COLOR_BTNSHADOW" : COLOR_BTNSHADOW,
+		"COLOR_GRAYTEXT" : COLOR_GRAYTEXT,
+		"COLOR_BTNTEXT" : COLOR_BTNTEXT,
+		"COLOR_INACTIVECAPTIONTEXT" : COLOR_INACTIVECAPTIONTEXT,
+		"COLOR_BTNHIGHLIGHT" : COLOR_BTNHIGHLIGHT,
+		"COLOR_3DDKSHADOW" : COLOR_3DDKSHADOW,
+		"COLOR_3DLIGHT" : COLOR_3DLIGHT,
+		"COLOR_INFOTEXT" : COLOR_INFOTEXT,
+		"COLOR_INFOBK" : COLOR_INFOBK,
+		"COLOR_HOTLIGHT" : COLOR_HOTLIGHT,
+		"COLOR_GRADIENTACTIVECAPTION" : COLOR_GRADIENTACTIVECAPTION,
+		"COLOR_GRADIENTINACTIVECAPTION" : COLOR_GRADIENTINACTIVECAPTION,
+		"COLOR_MENUHILIGHT" : COLOR_MENUHILIGHT,
+		"COLOR_MENUBAR" : COLOR_MENUBAR,
+
+		"COLOR_DESKTOP" : COLOR_DESKTOP,
+		"COLOR_3DFACE" : COLOR_3DFACE,
+		"COLOR_3DSHADOW" : COLOR_3DSHADOW,
+		"COLOR_3DHIGHLIGHT" : COLOR_3DHIGHLIGHT,
+		"COLOR_3DHILIGHT" : COLOR_3DHILIGHT,
+		"COLOR_BTNHILIGHT" : COLOR_BTNHILIGHT
+		];
+		foreach(key, value; colors)
+			Stdout.format("{,-27} : {}", key, getColor(value).toUtf8).newline;
+
+	}
+}
+	static Color getColor(int index) {
+version(Windows) {
+		uint sysColor = GetSysColor(index);
+		return Color(GetRValue(sysColor), GetGValue(sysColor), GetBValue(sysColor));
+} else {
+		return Color.Black; // TODO: temp to get X backend to compile
+
+		uint sysColor = 0;
+		switch(sysColor) {
+		default:
+			assert(0, "error: default case hit");
+		}
+}
+	}
+
+	//{{{ utility functions
+	int findUxState(Button c, int disabled, int normal, int hot, int pressed) {
+		if(c.state == ButtonState.Normal)
+			return normal;
+		else if(c.state == ButtonState.Hot)
+			return hot;
+		else if(c.state == ButtonState.Pressed)
+			return pressed;
+	}
+
+	/// draws a classic check, which is 7 wide and 7 high
+	void drawCheck(Graphics g, real x, real y) {
+		g.source = getColor(COLOR_WINDOWTEXT);
+		auto checkYs = [2, 3, 4, 3, 2, 1, 0];
+		foreach(i, cy; checkYs) {
+			g.moveTo(x + i + 0.5, y + cy);
+			g.lineTo(x + i + 0.5, y + cy + 3);
+			g.stroke();
+		}
+	}
+	//}}}
+
+	string name() {
+		return "Windows";
+	}
+
+	void Window_paint(Window c, Graphics g) {
+		g.source = getColor(COLOR_3DFACE);
+		g.paint();
+	}
+	//{{{ Button
+	Size Button_bestSize(Button c) {
+		// default button size is 17 points tall
+		return Size(75, 23);
+	}
+
+	void Button_paint(Button c, Graphics g) {
+version(Windows) {
+		if(Ux.isThemeActive()) {
+			auto uxState = findUxState(c, PBS_DISABLED, PBS_NORMAL, PBS_HOT, PBS_PRESSED);
+			Ux.drawBackground(g, Rect(0, 0, c.width, c.height), "BUTTON", BP_PUSHBUTTON, uxState);
+
+			g.source = getColor(COLOR_WINDOWTEXT);
+			c.paintFore(g);
+			return;
+		}
+}
+		g.source = getColor(COLOR_3DFACE);
+		g.paint();
+		if(c.state == ButtonState.Pressed) {
+			draw3dRectangle(g, 0, 0, c.width, c.height,
+				getColor(COLOR_3DDKSHADOW), getColor(COLOR_3DHIGHLIGHT));
+			draw3dRectangle(g, 1, 1, c.width-2, c.height-2,
+				getColor(COLOR_3DSHADOW), getColor(COLOR_3DLIGHT));
+		} else {
+			draw3dRectangle(g, 0, 0, c.width, c.height,
+				getColor(COLOR_3DHIGHLIGHT), getColor(COLOR_3DDKSHADOW));
+			draw3dRectangle(g, 1, 1, c.width-2, c.height-2,
+				getColor(COLOR_3DLIGHT), getColor(COLOR_3DSHADOW));
+		}
+		if(c.focused) {
+			g.source = getColor(COLOR_WINDOWTEXT);
+			g.setDash([1, 1], 0.5);
+			g.rectangle(3.5, 3.5, c.width-7, c.height-7);
+			g.stroke();
+		}
+		g.source = getColor(COLOR_WINDOWTEXT);
+		c.paintFore(g);
+	}
+	//}}}
+
+	//{{{ CheckBox
+	void CheckBox_paint(CheckBox c, Graphics g) {
+version(Windows) {
+		if(Ux.isThemeActive()) {
+			int uxState;
+			if(c.checked) {
+				uxState = findUxState(c, CBS_CHECKEDDISABLED, CBS_CHECKEDNORMAL, CBS_CHECKEDHOT, CBS_CHECKEDPRESSED);
+			} else {
+				uxState = findUxState(c, CBS_UNCHECKEDDISABLED, CBS_UNCHECKEDNORMAL, CBS_UNCHECKEDHOT, CBS_UNCHECKEDPRESSED);
+			}
+			Ux.drawBackground(g, Rect(0, 0, 13, c.height), "BUTTON", BP_CHECKBOX, uxState);
+
+			g.source = getColor(COLOR_WINDOWTEXT);
+			g.translate(15, 0);
+			c.paintFore(g);
+			return;
+		}
+}
+
+		if(c.state == ButtonState.Pressed)
+			g.source = getColor(COLOR_3DFACE);
+		else
+			g.source = getColor(COLOR_WINDOW);
+
+		g.rectangle(0, 0, 13, 13);
+		g.fill();
+		draw3dRectangle(g, 0, 0, 13, 13,
+			getColor(COLOR_3DSHADOW), getColor(COLOR_3DHIGHLIGHT));
+		draw3dRectangle(g, 1, 1, 11, 11,
+			getColor(COLOR_3DDKSHADOW), getColor(COLOR_3DLIGHT));
+
+		if(c.checked)
+			drawCheck(g, 3, 3);
+		//	drawCheck(g, 0, 0, 13, 13);
+
+		g.source = getColor(COLOR_WINDOWTEXT);
+		g.translate(15, 0);
+		c.paintFore(g);
+	}
+	//}}}
+
+	//{{{ RadioButton
+	void RadioButton_paint(CheckBox c, Graphics g) {
+version(Windows) {
+		if(Ux.isThemeActive()) {
+			int uxState;
+			if(c.checked) {
+				uxState = findUxState(c, RBS_CHECKEDDISABLED, RBS_CHECKEDNORMAL, RBS_CHECKEDHOT, RBS_CHECKEDPRESSED);
+			} else {
+				uxState = findUxState(c, RBS_UNCHECKEDDISABLED, RBS_UNCHECKEDNORMAL, RBS_UNCHECKEDHOT, RBS_UNCHECKEDPRESSED);
+			}
+			Ux.drawBackground(g, Rect(0, 0, 13, c.height), "BUTTON", BP_RADIOBUTTON, uxState);
+
+			g.source = getColor(COLOR_WINDOWTEXT);
+			g.translate(15, 0);
+			c.paintFore(g);
+			return;
+		}
+}
+
+		if(c.state == ButtonState.Pressed)
+			g.source = getColor(COLOR_3DFACE);
+		else
+			g.source = getColor(COLOR_WINDOW);
+
+		g.rectangle(2, 2, 8, 8);
+		g.fill();
+		const double[][] outerLines = [
+		[1.5, 8,   1.5, 10],  [0.5, 4,   0.5, 8],
+		[1.5, 2,   1.5, 4],   [2.0, 1.5,   4, 1.5],
+		[4.0, 0.5,   8, 0.5], [8.0, 1.5,   10, 1.5]];
+		const double[][] innerLines = [
+		[2.5, 8,   2.5, 9],  [1.5, 4,   1.5, 8],
+		[2.5, 2,   2.5, 4],   [3.0, 2.5,   4, 2.5],
+		[4.0, 1.5,   8, 1.5], [8.0, 2.5,   10, 2.5]];
+		g.source = getColor(COLOR_3DSHADOW);
+		foreach(line; outerLines) {
+			g.moveTo(line[0], line[1]);
+			g.lineTo(line[2], line[3]);
+			g.stroke();
+		}
+		g.source = getColor(COLOR_3DDKSHADOW);
+		foreach(line; innerLines) {
+			g.moveTo(line[0], line[1]);
+			g.lineTo(line[2], line[3]);
+			g.stroke();
+		}
+		g.source = getColor(COLOR_3DHIGHLIGHT);
+		foreach(line; outerLines) {
+			g.moveTo(12-line[0], 12-line[1]);
+			g.lineTo(12-line[2], 12-line[3]);
+			g.stroke();
+		}
+		g.source = getColor(COLOR_3DLIGHT);
+		foreach(line; innerLines) {
+			g.moveTo(12-line[0], 12-line[1]);
+			g.lineTo(12-line[2], 12-line[3]);
+			g.stroke();
+		}
+
+		if(c.checked) {
+			g.source = getColor(COLOR_WINDOWTEXT);
+			g.translate(4, 4);
+			g.rectangle(1, 0, 2, 1);
+			g.rectangle(0, 1, 4, 2);
+			g.rectangle(1, 3, 2, 1);
+			g.fill();
+			g.translate(-4, -4);
+		}
+
+		g.source = getColor(COLOR_WINDOWTEXT);
+		g.translate(15, 0);
+		c.paintFore(g);
+	}
+	//}}}
+
+	//{{{ ScrollBar
+	void ScrollBarTrack_paint(ScrollBarTrack c, Graphics g) {
+version(Windows) {
+		if(Ux.isThemeActive()) {
+			auto uxState = findUxState(c, SCRBS_DISABLED, SCRBS_NORMAL, SCRBS_HOT, SCRBS_PRESSED);
+			if(cast(VScrollBar)c.parent) {
+				Ux.drawBackground(g, Rect(0, 0, c.width, c.height), "SCROLLBAR", SBP_UPPERTRACKVERT, uxState);
+			} else {
+				Ux.drawBackground(g, Rect(0, 0, c.width, c.height), "SCROLLBAR", SBP_UPPERTRACKHORZ, uxState);
+			}
+			return;
+		}
+}
+
+	Color c1 = getColor(COLOR_3DHIGHLIGHT), c2 = getColor(COLOR_SCROLLBAR);
+	if(c.state == ButtonState.Pressed) {
+		c1.invert();
+		c2.invert();
+	}
+	int x = cast(int)round(c.x), y = cast(int)round(c.y);
+	drawCheckerboard(g, -(x & 1), -(y & 1), c.width + 1, c.height + 1,
+		c1, c2);
+	}
+
+	void ScrollBarThumb_paint(ScrollBarThumb c, Graphics g) {
+version(Windows) {
+		if(Ux.isThemeActive()) {
+			auto uxState = findUxState(c, SCRBS_DISABLED, SCRBS_NORMAL, SCRBS_HOT, SCRBS_PRESSED);
+			if(cast(VScrollBar)c.parent) {
+				Ux.drawBackground(g, Rect(0, 0, c.width, c.height), "SCROLLBAR", SBP_THUMBBTNVERT, uxState);
+				if(c.height > 16)
+					Ux.drawBackground(g, Rect(0, 0, c.width, c.height), "SCROLLBAR", SBP_GRIPPERVERT, 0);
+			} else {
+				Ux.drawBackground(g, Rect(0, 0, c.width, c.height), "SCROLLBAR", SBP_THUMBBTNHORZ, uxState);
+				if(c.width > 16)
+					Ux.drawBackground(g, Rect(0, 0, c.width, c.height), "SCROLLBAR", SBP_GRIPPERHORZ, 0);
+			}
+			return;
+		}
+}
+
+		g.source = getColor(COLOR_3DFACE);
+		g.paint();
+		draw3dRectangle(g, 0, 0, c.width, c.height,
+			getColor(COLOR_3DLIGHT), getColor(COLOR_3DDKSHADOW));
+		draw3dRectangle(g, 1, 1, c.width-2, c.height-2,
+			getColor(COLOR_3DHIGHLIGHT), getColor(COLOR_3DSHADOW));
+	}
+
+	real ScrollBar_size() {
+		// TODO: all themes should get this from SystemGui.ScrollBarSize
+	version(Windows)
+		return GetSystemMetrics(SM_CXVSCROLL);
+	else
+		return 18;
+	}
+
+	void ArrowButton_paint(ArrowButton c, Graphics g) {
+version(Windows) {
+		if(Ux.isThemeActive()) {
+			int uxState;
+			if(c.direction == ArrowDirection.Left)
+				uxState = findUxState(c, ABS_LEFTDISABLED, ABS_LEFTNORMAL, ABS_LEFTHOT, ABS_LEFTPRESSED);
+			else if(c.direction == ArrowDirection.Right)
+				uxState = findUxState(c, ABS_RIGHTDISABLED, ABS_RIGHTNORMAL, ABS_RIGHTHOT, ABS_RIGHTPRESSED);
+			else if(c.direction == ArrowDirection.Up)
+				uxState = findUxState(c, ABS_UPDISABLED, ABS_UPNORMAL, ABS_UPHOT, ABS_UPPRESSED);
+			else
+				uxState = findUxState(c, ABS_DOWNDISABLED, ABS_DOWNNORMAL, ABS_DOWNHOT, ABS_DOWNPRESSED);
+			Ux.drawBackground(g, Rect(0, 0, c.width, c.height), "SCROLLBAR", SBP_ARROWBTN, uxState);
+			return;
+		}
+}
+
+		g.source = getColor(COLOR_3DFACE);
+		g.paint();
+		if(c.state == ButtonState.Pressed) {
+			g.source = getColor(COLOR_3DDKSHADOW);
+			g.rectangle(0.5, 0.5, c.width-1, c.height-1);
+			g.stroke();
+		} else {
+			draw3dRectangle(g, 0, 0, c.width, c.height,
+				getColor(COLOR_3DLIGHT), getColor(COLOR_3DDKSHADOW));
+			draw3dRectangle(g, 1, 1, c.width-2, c.height-2,
+				getColor(COLOR_3DHIGHLIGHT), getColor(COLOR_3DSHADOW));
+		}
+	}
+	//}}}
+
+	//{{{ Scrollable
+	BorderSize Scrollable_borderSize(Scrollable c) {
+		return BorderSize(2, 2, 2, 2);
+	}
+
+	void Scrollable_paint(Scrollable c, Graphics g) {
+version(Windows) {
+		if(Ux.isThemeActive()) {
+			Ux.drawBackground(g, Rect(0, 0, c.width, c.height), "EDIT", EP_EDITTEXT, ETS_NORMAL);
+			// TODO: get this working
+			//g.Source = c.backColor;
+			g.source = getColor(COLOR_WINDOW);
+			g.rectangle(1.5, 1.5, c.width-3, c.height-3);
+			g.stroke();
+			return;
+		}
+}
+		draw3dRectangle(g, 0, 0, c.width, c.height,
+			getColor(COLOR_3DSHADOW), getColor(COLOR_3DHIGHLIGHT));
+		draw3dRectangle(g, 1, 1, c.width-2, c.height-2,
+			getColor(COLOR_3DDKSHADOW), getColor(COLOR_3DLIGHT));
+	}
+	//}}}
+
+	//{{{ Notebook
+	BorderSize Notebook_borderSize(Notebook c) {
+version(Windows) {
+		if(Ux.isThemeActive())
+			return BorderSize(2, 2, 4, 4);
+}
+		return BorderSize(2, 2, 2, 2);
+	}
+	void Tab_paint(TabPage page, Notebook c, Graphics g) {
+version(Windows) {
+		if(Ux.isThemeActive()) {
+			g.translate(page.tabLocation);
+			//auto uxState = findUxState(c, PBS_DISABLED, PBS_NORMAL, PBS_HOT, PBS_PRESSED);
+			auto uxState = TIS_NORMAL;
+			BorderSize selectedDelta;
+			if(page is c.selectedTabPage) {
+				uxState = TIS_SELECTED;
+				selectedDelta = BorderSize(2, 2, 2, 2);
+			}
+			Ux.drawBackground(g, Point() + page.tabSize + selectedDelta, "TAB", TABP_TABITEM, uxState);
+			g.drawText(page.text, 5, (page.tabSize.height-g.getTextExtents(page.text).height)/2);
+			g.translate(-page.tabLocation);
+			return;
+		}
+}
+		g.translate(page.tabLocation);
+		g.source = getColor(COLOR_3DHIGHLIGHT);
+		g.moveTo(0.5, 2);
+		g.lineTo(0.5, page.tabSize.height);
+		g.moveTo(1, 1.5);
+		g.lineTo(2, 1.5);
+		g.moveTo(2, 0.5);
+		g.lineTo(page.tabSize.width-2, 0.5);
+		g.stroke();
+		g.source = Color(128, 128, 128);
+		g.moveTo(page.tabSize.width-1.5, 2);
+		g.lineTo(page.tabSize.width-1.5, page.tabSize.height);
+		g.stroke();
+		g.source = Color(64, 64, 64);
+		g.moveTo(page.tabSize.width-2, 1.5);
+		g.lineTo(page.tabSize.width-1, 1.5);
+		g.moveTo(page.tabSize.width-0.5, 2);
+		g.lineTo(page.tabSize.width-0.5, page.tabSize.height);
+		g.stroke();
+		g.source = getColor(COLOR_WINDOWTEXT);
+		g.drawText(page.text, 5, (page.tabSize.height-g.getTextExtents(page.text).height)/2);
+		g.translate(-page.tabLocation);
+	}
+	void Notebook_paint(Notebook c, Graphics g) {
+version(Windows) {
+		if(Ux.isThemeActive()) {
+			//auto uxState = findUxState(c, PBS_DISABLED, PBS_NORMAL, PBS_HOT, PBS_PRESSED);
+			Ux.drawBackground(g, Rect(0, c._tabAreaSize, c.width, c.height-c._tabAreaSize), "TAB", TABP_PANE, 0);
+			g.save();
+			g.rectangle(4, 4, c.width-8, c.height-8);
+			g.clip();
+			for(float i = 4; i < c.width-8+10; i += 10)
+				Ux.drawBackground(g, Rect(i, c._tabAreaSize+4, 10, c.height*1.7-8-(c._tabAreaSize+4)), "TAB", TABP_BODY, 0);
+			g.restore();
+
+			return;
+		}
+}
+		draw3dRectangle(g, 0, c._tabAreaSize, c.width, c.height,
+			getColor(COLOR_3DHIGHLIGHT), Color(64, 64, 64));
+		draw3dRectangle(g, 1, c._tabAreaSize+1, c.width-2, c.height-2,
+			Color(212, 208, 200), Color(128, 128, 128));
+
+	}
+	//}}}
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/windows_window.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,978 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.windows_window;
+
+public import dynamin.c.windows;
+public import dynamin.c.cairo;
+public import dynamin.c.cairo_win32;
+public import dynamin.all_core;
+public import dynamin.all_gui;
+public import dynamin.gui.window;
+public import dynamin.gui.key;
+public import dynamin.all_painting;
+public import tango.io.Stdout;
+
+///
+enum WindowsVersion {
+	///
+	Windows95,  ///
+	Windows98,  ///
+	WindowsMe,  ///
+	Windows2000,///
+	WindowsXP,  ///
+	WindowsVista
+}
+/**
+ * Returns true if the version of Windows that is runninng now is the
+ * specified version or newer.
+ */
+bool checkWindowsVersion(WindowsVersion ver) {
+	// Windows Server "Longhorn" is 6.0
+	// Windows Vista is 6.0
+	// Windows Server 2003 is 5.2
+	// Windows XP is 5.1
+	// Windows Me is 4.90
+	// Windows 98 is 4.10
+	// Windows 95 is 4.0
+	// Windows NT is 4.0
+	OSVERSIONINFO info;
+	info.dwOSVersionInfoSize = OSVERSIONINFO.sizeof;
+	GetVersionEx(&info);
+	DWORD major, minor;
+	switch(ver) {
+	case WindowsVersion.Windows95:    major = 4; minor = 0; break;
+	case WindowsVersion.Windows98:    major = 4; minor = 10; break;
+	case WindowsVersion.WindowsMe:    major = 4; minor = 90; break;
+	case WindowsVersion.Windows2000:  major = 5; minor = 0; break;
+	case WindowsVersion.WindowsXP:    major = 5; minor = 1; break;
+	case WindowsVersion.WindowsVista: major = 6; minor = 0; break;
+	}
+	return info.dwMajorVersion > major ||
+		(info.dwMajorVersion == major && info.dwMinorVersion >= minor);
+}
+/* unittest {
+	Stdout.format("Windows95 or newer: {}", checkWindowsVersion(WindowsVersion.Windows95)).newline;
+	Stdout.format("Windows98 or newer: {}", checkWindowsVersion(WindowsVersion.Windows98)).newline;
+	Stdout.format("WindowsMe or newer: {}", checkWindowsVersion(WindowsVersion.WindowsMe)).newline;
+	Stdout.format("Windows2000 or newer: {}", checkWindowsVersion(WindowsVersion.Windows2000)).newline;
+	Stdout.format("WindowsXP or newer: {}", checkWindowsVersion(WindowsVersion.WindowsXP)).newline;
+} */
+
+// TODO: the way I have stored references using SetProp() will not work
+// if/when D gets a copying collector. I will need to store a key with
+// SetProp() and use that key to store the reference in either an
+// array or a hashtable.
+Window[HWND] windows;
+void setControl(HWND hwnd, Window win) {
+	if(win is null)
+		windows.remove(hwnd);
+	else
+		windows[hwnd] = win;
+}
+/**
+ * Returns: the Dynamin NativeControl that wraps the specified handle
+ */
+// TODO: change return type to NativeControl
+Window getControl(HWND hwnd) {
+	assert(IsWindow(hwnd), "Invalid HWND");
+	auto tmp = hwnd in windows;
+	return tmp is null ? null : *tmp;
+}
+
+template ApplicationBackend() {
+	void backend_run(Window w) {
+		bool isWindowVisible() {
+			if(w is null) return true;
+			return w.visible;
+		}
+		MSG msg;
+		BOOL ret;
+		while(isWindowVisible() && (ret = GetMessage(&msg, null, 0, 0)) != 0) {
+			if(ret == -1)
+				Stdout("GetMessage() failed!").newline;
+			TranslateMessage(&msg);
+			DispatchMessage(&msg);
+		}
+	}
+}
+
+/*
+ * The reason backends use the backend_ prefix and:
+ * mixin Backend();
+ * instead of using just the method name and
+ * mixin Backend() backend;
+ * is that D mistakenly calls the method in the class, rather than
+ * the one mixed-in...causing infinite recursion/stack overflow
+ */
+//{{{ WindowBackend
+template WindowBackend() {
+	HWND _handle;
+	bool backend_handleCreated() { return _handle !is null; }
+	//WS_CAPTION == WS_BORDER | WS_DLGFRAME;
+	void backend_recreateHandle() {
+		LONG style, exStyle;
+		backend_getWindowStyles(style, exStyle);
+		style &= ~WS_VISIBLE; // don't create visible
+		// TODO: set the owner with CreateWindowEx
+		HWND newHandle = CreateWindowEx(exStyle, "DynaminWindow", _text.toWcharPtr(),
+			style, cast(int)x, cast(int)y, cast(int)width, cast(int)height,
+			null, null, GetModuleHandle(null), null);
+		if(!newHandle)
+			Stdout("CreateWindowEx() failed").newline;
+		setControl(newHandle, this);
+
+		// Windows does not completely obey the styles given in CreateWindowEx()
+		SetWindowLong(newHandle, GWL_STYLE, style);
+		SetWindowLong(newHandle, GWL_EXSTYLE, exStyle);
+		SetWindowPos(newHandle, null, 0, 0, 0, 0,
+			SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+
+		if(handleCreated) {
+			// TODO: move native children to new window?
+
+			// set z-order to right above old window
+			// SetWindowPos() puts the window above the specified
+			// window in the z-order
+			SetWindowPos(newHandle, _handle, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+			if(IsWindowVisible(_handle))
+				ShowWindow(newHandle, SW_SHOWNA);
+			DestroyWindow(_handle);
+		}
+		_handle = newHandle;
+		backend_nativeToBorderSize();
+	}
+	extern(C) static void freeDC(void* hdc) {
+		ReleaseDC(null, hdc);
+	}
+	Graphics backend_quickCreateGraphics() {
+		HDC hdc = GetDC(handle);
+		cairo_surface_t* surface = cairo_win32_surface_create(hdc);
+		cairo_surface_set_user_data(surface, cast(cairo_user_data_key_t*)1,
+			hdc, &freeDC);
+		cairo_t* cr = cairo_create(surface);
+		cairo_surface_destroy(surface);
+		cairo_translate(cr, -borderSize.left, -borderSize.top);
+		auto g = new Graphics(cr);
+		cairo_destroy(cr);
+		return g;
+	}
+	void backend_visible(bool b) {
+		//if not created, create the handle by calling Handle()
+		ShowWindow(handle, b ? SW_SHOW : SW_HIDE);
+	}
+	void backend_borderStyle(WindowBorderStyle border) {
+		backend_updateWindowStyles();
+	}
+	void backend_repaint(Rect rect) {
+		RECT wrect;
+		wrect.left = cast(int)(rect.x-_borderSize.left);
+		wrect.top = cast(int)(rect.y-_borderSize.top);
+		wrect.right = wrect.left+cast(int)rect.width;
+		wrect.bottom = wrect.top+cast(int)rect.height;
+		InvalidateRect(handle, &wrect, false);
+	}
+	void backend_resizable(bool b) {
+		backend_updateWindowStyles();
+	}
+	void backend_contentMinSizeChanged() {
+	}
+	void backend_contentMaxSizeChanged() {
+		backend_updateWindowStyles();
+	}
+	void backend_location(Point pt) {
+		SetWindowPos(handle, null,
+			cast(int)pt.x, cast(int)pt.y, 0, 0,
+			SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
+	}
+	void backend_size(Size size) {
+		SetWindowPos(handle, null,
+			0, 0, cast(int)size.width, cast(int)size.height,
+			SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
+	}
+	void backend_text(string str) {
+		SetWindowText(handle, str.toWcharPtr());
+	}
+	//{{{ backend specific
+
+	void backend_nativeToLocationSize() {
+		RECT winRect;
+		GetWindowRect(handle, &winRect);
+		_location.x = winRect.left;
+		_location.y = winRect.top;
+		_size.width = winRect.right-winRect.left;
+		_size.height = winRect.bottom-winRect.top;
+	}
+	package void backend_nativeToBorderSize() {
+		RECT clientRect, winRect;
+		POINT clientLoc;
+		GetClientRect(handle, &clientRect);
+		GetWindowRect(handle, &winRect);
+		ClientToScreen(handle, &clientLoc);
+		_borderSize.left = clientLoc.x-winRect.left;
+		_borderSize.top = clientLoc.y-winRect.top;
+		_borderSize.right = winRect.right-clientLoc.x-clientRect.right;
+		_borderSize.bottom = winRect.bottom-clientLoc.y-clientRect.bottom;
+		backend_nativeToLocationSize();
+	}
+	void backend_getWindowStyles(out LONG style, out LONG exStyle) {
+		if(handleCreated) {
+			style = GetWindowLong(handle, GWL_STYLE);
+			exStyle = GetWindowLong(handle, GWL_EXSTYLE);
+		}
+		void SetIf(LONG s, bool b) {
+			// if condition satisfied, add style, otherwise clear style
+			b ? (style |= s) : (style &= ~s);
+		}
+		SetIf(WS_DLGFRAME, borderStyle != WindowBorderStyle.None);
+		SetIf(WS_BORDER, borderStyle != WindowBorderStyle.None);
+		SetIf(WS_THICKFRAME,
+			resizable && borderStyle != WindowBorderStyle.None);
+		SetIf(WS_MINIMIZEBOX, borderStyle == WindowBorderStyle.Normal);
+		SetIf(WS_MAXIMIZEBOX,
+			borderStyle == WindowBorderStyle.Normal && resizable &&
+			content.maxWidth == 0 && content.maxHeight == 0);
+		SetIf(WS_SYSMENU, borderStyle != WindowBorderStyle.None);
+		if(borderStyle == WindowBorderStyle.Tool)
+			exStyle |= WS_EX_TOOLWINDOW;
+		else
+			exStyle &= ~WS_EX_TOOLWINDOW;
+	}
+	void backend_updateWindowStyles() {
+		if(!handleCreated)
+			return;
+		LONG style, exStyle;
+		backend_getWindowStyles(style, exStyle);
+		SetWindowLong(handle, GWL_STYLE, style);
+		SetWindowLong(handle, GWL_EXSTYLE, exStyle);
+		SetWindowPos(handle, null, 0, 0, 0, 0,
+			SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+	}
+		//--FixedToorWindow--
+		//Style: 0x16c80000
+		//ExStyle:  0x10180
+		//--SizableToorWindow--
+		//Style: 0x16cc0000
+		//ExStyle:  0x10180
+		//--FixedDialog--
+		//Style: 0x16c80000
+		//ExStyle:  0x10101
+		//--None--
+		//Style: 0x16010000
+		//ExStyle:  0x10000
+		//--FixedSingle--
+		//Style: 0x16c80000
+		//ExStyle:  0x10100
+		//--Sizable--
+		//Style: 0x16cc0000
+		//ExStyle:  0x10100
+	//}}}
+}
+//}}}
+
+//{{{ Ux class
+class Ux {
+static:
+private:
+	HMODULE uxLib = null;
+	// TODO: these are the wrong calling convention!!
+	extern(Windows) {
+		BOOL function() _IsAppThemed;
+		BOOL function() _IsThemeActive;
+		HTHEME function(HWND hwnd, LPCWSTR pszClassList) _OpenThemeData;
+		HRESULT function(HTHEME hTheme) _CloseThemeData;
+		HRESULT function(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
+			RECT* pRect, RECT* pClipRect) _DrawThemeBackground;
+	}
+	static this() {
+		uxLib = LoadLibrary("uxtheme");
+		if(uxLib) {
+			_IsAppThemed = cast(typeof(_IsAppThemed))
+				GetProcAddress(uxLib, "IsAppThemed");
+			_IsThemeActive = cast(typeof(_IsThemeActive))
+				GetProcAddress(uxLib, "IsThemeActive");
+			_OpenThemeData = cast(typeof(_OpenThemeData))
+				GetProcAddress(uxLib, "OpenThemeData");
+			_CloseThemeData = cast(typeof(_CloseThemeData))
+				GetProcAddress(uxLib, "CloseThemeData");
+			_DrawThemeBackground = cast(typeof(_DrawThemeBackground))
+				GetProcAddress(uxLib, "DrawThemeBackground");
+		}
+	}
+	HTHEME[string] cache;
+	// opens an HTHEME for the specified controlName and caches it
+	// next time, just returns the HTHEME from the cache
+	HTHEME getHTHEME(string controlName) {
+		HTHEME* hthemePtr = controlName in cache;
+		HTHEME htheme = controlName in cache;
+		if(hthemePtr) {
+			htheme = *hthemePtr;
+		} else {
+			htheme = _OpenThemeData(null, controlName.toWcharPtr());
+			if(!htheme) {
+				if(_IsThemeActive())
+					throw new Exception("invalid uxtheme controlName");
+				else
+					throw new Exception("no theme active");
+			}
+			cache[controlName] = htheme;
+		}
+		return htheme;
+	}
+	// This is called when the WM_THEMECHANGED message is sent
+	package void themeChanged() {
+		foreach(htheme; cache.values)
+			_CloseThemeData(htheme);
+		cache = null;
+		updateThemeActive = true;
+	}
+	bool themeActive;
+	bool updateThemeActive = true;
+public:
+	// cache this value, as this function was showing up in profiles
+	bool isThemeActive() {
+		if(updateThemeActive) {
+			themeActive = uxLib && _IsThemeActive() && _IsAppThemed();
+			updateThemeActive = false;
+		}
+		return themeActive;
+	}
+	// draw directly onto the HDC with the uxTheme API if the following
+	// three conditions are met:
+	// - the Graphics must be drawing to an HDC (duh)
+	// - there cannot be a scale or rotation...translation can be handled
+	// - the clip must be a pixel-aligned rectangle
+	bool drawBackground(Graphics g, Rect rect, string controlName, int part, int state) {
+		if(!uxLib)
+			throw new Exception("UxPaintBackground(): uxtheme library not found!");
+		HTHEME htheme = getHTHEME(controlName);
+		static if(true) {
+
+		HDC hdc = cairo_win32_surface_get_dc(cairo_get_target(g.handle));
+		//HDC hdc = null;
+		bool isMatrixTranslationOnly() {
+			cairo_matrix_t matrix;
+			cairo_get_matrix(g.handle, &matrix);
+			return matrix.xx == 1 && matrix.xy == 0 &&
+				matrix.xy == 0 && matrix.yy == 1;
+		}
+		bool isClipIntegerRect(cairo_rectangle_list_t* list) {
+			return list.status != CAIRO_STATUS_CLIP_NOT_REPRESENTABLE;
+		}
+		RECT locRect;
+		auto list = cairo_copy_clip_rectangle_list(g.handle);
+		scope(exit) cairo_rectangle_list_destroy(list);
+		if(hdc && isMatrixTranslationOnly() && isClipIntegerRect(list)) {
+			double x = rect.x, y = rect.y;
+			double right = rect.right, bottom = rect.bottom;
+			cairo_user_to_device(g.handle, &x, &y);
+			cairo_user_to_device(g.handle, &right, &bottom);
+			locRect.left = cast(int)x;
+			locRect.top = cast(int)y;
+			locRect.right = cast(int)right;
+			locRect.bottom = cast(int)bottom;
+			cairo_surface_flush(cairo_get_target(g.handle));
+
+			RECT clip;
+			for(int i = 0; i < list.num_rectangles; ++i) {
+				cairo_user_to_device(g.handle, &(list.rectangles[i].x), &(list.rectangles[i].y));
+				clip.left = cast(int)list.rectangles[i].x;
+				clip.top = cast(int)list.rectangles[i].y;
+				clip.right = clip.left + cast(int)list.rectangles[i].width;
+				clip.bottom = clip.top + cast(int)list.rectangles[i].height;
+				_DrawThemeBackground(htheme, hdc, part, state, &locRect, &clip);
+			}
+
+			cairo_surface_mark_dirty(cairo_get_target(g.handle));
+		} else {
+			assert(0, "So far, visual styles are supported only with no rotation, no scaling, a rectangular clip, and HDC surfaces.");
+			static if(0) {
+			BITMAPINFO bmi;
+			bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+			bmi.bmiHeader.biWidth = width;
+			bmi.bmiHeader.biHeight = -height; // top-down DIB
+			bmi.bmiHeader.biPlanes = 1;
+			bmi.bmiHeader.biBitCount = 32;
+			bmi.bmiHeader.biCompression = BI_RGB;
+			//bmi.bmiHeader.biSizeImage = width * height * 4;
+			hbmpBlack = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS,
+				&bmpBits, NULL, 0);
+			hbmpWhite = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS,
+				&bmpBits, NULL, 0);
+			//draw on a black background and on a white background
+			//calculate alpha and colors and draw that image with cairo
+			// DO TESTS WITH ROTATION AND SCALING
+			_DrawThemeBackground(htheme, hdc, part, state, null/*change*/, null);
+			DeleteObject(hbmpBlack);
+			DeleteObject(hbmpWhite);
+			}
+		}
+
+		}
+		return false;
+	}
+}
+//}}}
+
+//{{{ module constructor and destructor
+HWND msgWnd;
+static this() {
+	assert(cairo_version() >= CAIRO_VERSION_ENCODE(1, 4, 0),
+		"cairo version 1.4.0 or newer is required");
+
+	/* create window classes */
+	WNDCLASSEX wc;
+	wc.cbSize = wc.sizeof;
+	wc.style = 0;
+	wc.hInstance = GetModuleHandle(null);
+
+	wc.lpfnWndProc = &dynaminMsgWindowProc;
+	wc.lpszClassName = "DynaminMsgWindow";
+	if(!RegisterClassExW(&wc))
+		Stdout("RegisterClassEx() failed registering class 'DynaminMsgWindow'").newline;
+
+	wc.lpfnWndProc = &dynaminWindowProc;
+	//wc.hbrBackground = cast(HBRUSH)16;
+
+	wc.lpszClassName = "DynaminWindow";
+	if(!RegisterClassEx(&wc))
+		Stdout("RegisterClassEx() failed registering class 'DynaminWindow'").newline;
+
+	wc.style = CS_SAVEBITS;
+	wc.lpszClassName = "DynaminPopup";
+	if(!RegisterClassExW(&wc))
+		Stdout("RegisterClassEx() failed registering class 'DynaminPopup'").newline;
+
+
+	msgWnd = CreateWindowEx(0, "DynaminMsgWindow", "",
+		0,
+		0, 0, 0, 0,
+		null, null, GetModuleHandle(null), null);
+	if(!msgWnd)
+		Stdout("CreateWindowEx() failed").newline;
+
+	/* initialize COM */
+	auto ret = CoInitializeEx(null, COINIT_APARTMENTTHREADED);
+	if(ret != S_OK && ret != S_FALSE)
+		Stdout("Failed to initialize COM").newline;
+
+}
+static ~this() {
+	CoUninitialize();
+}
+//}}}
+
+//{{{ dynaminWindowProc()
+extern(Windows)
+LRESULT dynaminWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+	//used in WM_MOVING
+	static int dragX, dragY; //the cursor location in window coordinates when the drag was started
+	static bool trackingMouseLeave = false;
+	auto c = getControl(hwnd);
+	//{{{ helper functions
+	void createMouseEvent(MouseButton button, void delegate(MouseEventArgs args) func) {
+		scope args = new MouseEventArgs(
+			cast(short)LOWORD(lParam)+c.borderSize.left,
+			cast(short)HIWORD(lParam)+c.borderSize.top, button);
+		func(args);
+	}
+	void snapSide(inout int sideToSnap, float side1, float side2) {
+		if(sideToSnap >= side1-c.snapDistance && sideToSnap <= side1+c.snapDistance)
+			sideToSnap = cast(int)side1;
+		if(sideToSnap >= side2-c.snapDistance && sideToSnap <= side2+c.snapDistance)
+			sideToSnap = cast(int)side2;
+	}
+	auto emptyFunc = (Rect snapRect) {};
+	// used to snap vertical sides, left and right
+	void snapVSide(inout int side, RECT* rect, void delegate(Rect snapRect) func = emptyFunc) {
+		if(c.snapRects is null)
+			return;
+		foreach(snapRect; c.snapRects) {
+			if(rect.bottom >= snapRect.y && rect.top <= snapRect.bottom) {
+				snapSide(side, snapRect.x, snapRect.right);
+				func(snapRect);
+			}
+		}
+	}
+	// used to snap horizontal sides, top and bottom
+	void snapHSide(inout int side, RECT* rect, void delegate(Rect snapRect) func = emptyFunc) {
+		if(c.snapRects is null)
+			return;
+		foreach(snapRect; c.snapRects) {
+			if(rect.right >= snapRect.x && rect.left <= snapRect.right) {
+				snapSide(side, snapRect.y, snapRect.bottom);
+				func(snapRect);
+			}
+		}
+	}
+	MouseButton getFromXBUTTONX() {
+		switch(HIWORD(wParam)) {
+		case XBUTTON1: return MouseButton.XButton1;
+		case XBUTTON2: return MouseButton.XButton2;
+		default: return MouseButton.None;
+		}
+	}
+	//}}}
+	switch(uMsg) {
+	case WM_ENTERSIZEMOVE: //when the user starts moving or resizing the window
+		//{{{
+		POINT cursor;
+		GetCursorPos(&cursor);
+		RECT rect;
+		GetWindowRect(hwnd, &rect);
+		dragX = cursor.x-rect.left;
+		dragY = cursor.y-rect.top;
+		return 0;
+		//}}}
+	case WM_MOVING:
+		//{{{
+		if(c.snapRects is null || c.snapRects.length == 0)
+			break;
+		RECT* rect = cast(RECT*)lParam;
+		int rectWidth = rect.right-rect.left;
+		int rectHeight = rect.bottom-rect.top;
+		POINT cursor;
+		GetCursorPos(&cursor);
+		rect.left = cursor.x-dragX;
+		rect.top = cursor.y-dragY;
+		void updateRightAndBottom() {
+			rect.right = rect.left+rectWidth;
+			rect.bottom = rect.top+rectHeight;
+		}
+		updateRightAndBottom();
+		snapVSide(rect.left, rect, (Rect snapRect) {
+			snapSide(rect.left, snapRect.x-rectWidth, snapRect.right-rectWidth);
+		});
+		updateRightAndBottom();
+		snapHSide(rect.top, rect, (Rect snapRect) {
+			snapSide(rect.top, snapRect.y-rectHeight, snapRect.bottom-rectHeight);
+		});
+		updateRightAndBottom();
+		snapVSide(rect.left, rect, (Rect snapRect) {
+			snapSide(rect.left, snapRect.x-rectWidth, snapRect.right-rectWidth);
+		});
+		updateRightAndBottom();
+		return true;
+		//}}}
+	case WM_SIZING:
+		//{{{
+		Size minSize = c.content.minSize+c.borderSize;
+		Size maxSize = c.content.maxSize+c.borderSize;
+		RECT* rect = cast(RECT*)lParam;
+		switch(wParam) {
+		case WMSZ_TOPLEFT:
+			snapHSide(rect.top, rect);
+			goto case WMSZ_LEFT;
+		case WMSZ_BOTTOMLEFT:
+			snapHSide(rect.bottom, rect);
+		case WMSZ_LEFT:
+			snapVSide(rect.left, rect);
+			// adjust left according to min and max
+			if(c.content.maxWidth != 0)
+				rect.left = max(rect.left, rect.right-cast(int)maxSize.width);
+			if(c.content.minWidth != 0)
+				rect.left = min(rect.left, rect.right-cast(int)minSize.width);
+			break;
+		case WMSZ_TOPRIGHT:
+			snapHSide(rect.top, rect);
+			goto case WMSZ_RIGHT;
+		case WMSZ_BOTTOMRIGHT:
+			snapHSide(rect.bottom, rect);
+		case WMSZ_RIGHT:
+			snapVSide(rect.right, rect);
+			// adjust right according to min and max
+			if(c.content.maxWidth != 0)
+				rect.right = min(rect.right, rect.left+cast(int)maxSize.width);
+			if(c.content.minWidth != 0)
+				rect.right = max(rect.right, rect.left+cast(int)minSize.width);
+			break;
+		default: break;
+		}
+		switch(wParam) {
+		case WMSZ_TOPLEFT:  //already snapped left above
+		case WMSZ_TOPRIGHT: //already snapped right above
+		case WMSZ_TOP:
+			snapHSide(rect.top, rect);
+			// adjust top according to min and max
+			if(c.content.maxHeight != 0)
+				rect.top = max(rect.top, rect.bottom-cast(int)maxSize.height);
+			if(c.content.minHeight != 0)
+				rect.top = min(rect.top, rect.bottom-cast(int)minSize.height);
+			break;
+		case WMSZ_BOTTOMLEFT:  //already snapped left above
+		case WMSZ_BOTTOMRIGHT: //already snapped right above
+		case WMSZ_BOTTOM:
+			snapHSide(rect.bottom, rect);
+			// adjust bottom according to min and max
+			if(c.content.maxHeight != 0)
+				rect.bottom = min(rect.bottom, rect.top+cast(int)maxSize.height);
+			if(c.content.minHeight != 0)
+				rect.bottom = max(rect.bottom, rect.top+cast(int)minSize.height);
+			break;
+		default: break;
+		}
+		return true;
+		//}}}
+	case WM_MOVE:
+		RECT rect;
+		GetWindowRect(hwnd, &rect);
+		c._location = Point(rect.left, rect.top);
+		scope args = new EventArgs();
+		c.moved(args);
+		return 0;
+	case WM_SIZE:
+		if(wParam == SIZE_MINIMIZED)
+			break;
+		RECT rect;
+		GetWindowRect(hwnd, &rect);
+		c._size = Size(rect.right-rect.left, rect.bottom-rect.top);
+		c.backend_nativeToBorderSize();
+		scope args = new EventArgs();
+		c.resized(args);
+		return 0;
+	case WM_MOUSEMOVE:
+		if(!trackingMouseLeave) {
+			TRACKMOUSEEVENT tme;
+			tme.cbSize = TRACKMOUSEEVENT.sizeof;
+			tme.dwFlags = TME_LEAVE;
+			tme.hWndTrack = hwnd;
+			tme.dwHoverTime = 0;
+			TrackMouseEvent(&tme);
+			trackingMouseLeave = true;
+		}
+		auto pt = Point(cast(short)LOWORD(lParam)+c.borderSize.left, cast(short)HIWORD(lParam)+c.borderSize.top);
+		Control captor = getCaptorControl();
+		if(captor)
+			pt = c.contentToContent(pt, captor);
+		else
+			captor = c;
+		scope event = new MouseEventArgs(
+			pt.x, pt.y, MouseButton.None);
+		if(wParam &
+			(MK_LBUTTON | MK_MBUTTON | MK_RBUTTON |
+			MK_XBUTTON1 | MK_XBUTTON2)) {
+			captor.mouseDragged(event);
+		} else {
+			captor.mouseMoved(event);
+		}
+		return 0;
+	case WM_MOUSELEAVE:
+		trackingMouseLeave = false;
+		setHotControl(null);
+		return 0;
+	case WM_LBUTTONDOWN:
+		SetCapture(hwnd);
+		createMouseEvent(MouseButton.Left, (MouseEventArgs args) {
+			c.mouseDown(args);
+		});
+		return 0;
+	case WM_MBUTTONDOWN:
+		SetCapture(hwnd);
+		createMouseEvent(MouseButton.Middle, (MouseEventArgs args) {
+			c.mouseDown(args);
+		});
+		return 0;
+	case WM_RBUTTONDOWN:
+		SetCapture(hwnd);
+		createMouseEvent(MouseButton.Right, (MouseEventArgs args) {
+			c.mouseDown(args);
+		});
+		return 0;
+	case WM_XBUTTONDOWN:
+		SetCapture(hwnd);
+		auto button = getFromXBUTTONX();
+		if(!button) break;
+		createMouseEvent(button, (MouseEventArgs args) {
+			c.mouseDown(args);
+		});
+		return true;
+	case WM_LBUTTONUP:
+		ReleaseCapture();
+		createMouseEvent(MouseButton.Left, (MouseEventArgs args) {
+			c.mouseUp(args);
+		});
+		return 0;
+	case WM_MBUTTONUP:
+		ReleaseCapture();
+		createMouseEvent(MouseButton.Middle, (MouseEventArgs args) {
+			c.mouseUp(args);
+		});
+		return 0;
+	case WM_RBUTTONUP:
+		ReleaseCapture();
+		createMouseEvent(MouseButton.Right, (MouseEventArgs args) {
+			c.mouseUp(args);
+		});
+		return 0;
+	case WM_XBUTTONUP:
+		ReleaseCapture();
+		auto button = getFromXBUTTONX();
+		if(!button) break;
+		createMouseEvent(button, (MouseEventArgs args) {
+			c.mouseUp(args);
+		});
+		return true;
+	case WM_MOUSEWHEEL:
+		int scrollLines;
+		SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, 0);
+		if(scrollLines == 0xFFFFFFFF)
+			scrollLines = 3;
+		int delta = -cast(short)HIWORD(wParam);
+		auto screenPt = Point(LOWORD(lParam), HIWORD(lParam));
+		auto des = c.getDescendantAtPoint(c.screenToContent(screenPt));
+		des.mouseTurned(
+			new MouseTurnedEventArgs(delta, delta*scrollLines/120.0) );
+		return 0;
+	case WM_SYSKEYDOWN:
+		//Stdout.format("WM_SYSKEYDOWN: {:x}", cast(int)wParam).newline;
+		if(wParam == 0x79) return 0;
+		break;
+	case WM_KEYDOWN:
+		//Stdout.format("WM_KEYDOWN:    {:x}", cast(int)wParam).newline;
+		Control focused = c.focusedControl ? c.focusedControl : c;
+		focused.keyDown(new KeyEventArgs(
+			VKToKey(wParam), cast(bool)(lParam & (1 << 30)) ));
+		return 0;
+	case WM_SYSKEYUP:
+		//Stdout.format("WM_SYSKEYUP: {:x}", cast(int)wParam).newline;
+		if(wParam == 0x79) return 0;
+		break;
+	case WM_KEYUP:
+		//Stdout.format("WM_KEYUP:    {:x}", cast(int)wParam).newline;
+		Control focused = c.focusedControl ? c.focusedControl : c;
+		focused.keyUp(new KeyEventArgs( VKToKey(wParam), false ));
+		return 0;
+	case WM_CHAR:
+		// DO NOT use the repeat count from the lParam to send multiple events
+		// I hate when programs do that
+
+		//stop backspace and escape and shift+enter
+		if(wParam == 0x08 || wParam == 0x1B || wParam == 0x0A)
+			break;
+		// don't process characters typed while control is down
+		if(HIBYTE(GetKeyState(VK_CONTROL)))
+			break;
+		if(wParam == 0x0D) // change \r to \n
+			wParam = 0x0A;
+		bool repeat;
+		repeat = cast(bool)(lParam & (1 << 30));
+		Control focused = c.focusedControl ? c.focusedControl : c;
+		focused.keyTyped(new KeyTypedEventArgs(cast(dchar)wParam, repeat));
+		return 0;
+	case WM_PRINT:
+		paintToHDC(cast(HDC)wParam, getControl(hwnd), null);
+		return 0;
+	case WM_PAINT:
+		PAINTSTRUCT ps;
+		BeginPaint(hwnd, &ps);
+		RECT* clip = &ps.rcPaint;
+
+		HDC hdcBuffer = CreateCompatibleDC(ps.hdc);
+		HBITMAP hbmpBuffer = CreateCompatibleBitmap(ps.hdc,
+			clip.right-clip.left, clip.bottom-clip.top);
+		HBITMAP hbmpDefault = SelectObject(hdcBuffer, hbmpBuffer);
+
+		paintToHDC(hdcBuffer, getControl(hwnd), clip);
+
+		BitBlt(ps.hdc, clip.left, clip.top, clip.right-clip.left, clip.bottom-clip.top,
+			hdcBuffer, 0, 0, SRCCOPY);
+		SelectObject(hdcBuffer, hbmpDefault);
+		DeleteDC(hdcBuffer);
+		DeleteObject(hbmpBuffer);
+
+		EndPaint(hwnd, &ps);
+		return 0;
+	case WM_CLOSE:
+		c.visible = false;
+		//DestroyWindow(hwnd);
+		//PostQuitMessage(0);
+		return 0;
+	case WM_DESTROY:
+		return 0;
+	default:
+		break;
+	}
+	return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+//}}}
+
+//{{{ paintToHDC()
+void paintToHDC(HDC hdc, Window w, RECT* clip) {
+	cairo_surface_t* surface = cairo_win32_surface_create(hdc);
+	cairo_t* cr = cairo_create(surface);
+	cairo_surface_destroy(surface);
+
+	if(clip) {
+		cairo_translate(cr,
+			-clip.left-w.borderSize.left, -clip.top-w.borderSize.top);
+
+		//cairo_rectangle(cr, clip.left, clip.top, clip.right-clip.left, clip.bottom-clip.top);
+	}
+	//if(w.Opaque) {
+		//cairo_set_source_rgb(cr, w.content.backColor.R/255.0, w.content.backColor.G/255.0, w.content.backColor.B/255.0);
+		//cairo_paint(cr);
+	//}
+
+	//cairo_set_source_rgb(cr, .3, .3, .3);
+	//cairo_paint(cr);
+
+	//cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
+	//cairo_paint(cr);
+	//cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+
+	cairo_set_source_rgb(cr, 0, 0, 0);
+	cairo_set_line_width(cr, 1.0);
+
+	//HBITMAP hbmp = LoadImage(null, cast(wchar*)cast(ushort)OIC_WARNING,
+	//	IMAGE_ICON, 0, 0, LR_SHARED);
+	//if(hbmp == null)
+	//	Stdout.format("LoadImage failed. GetLastError()={}", GetLastError()).newline;
+
+	//auto imgSurface = cairo_image_surface_create_for_data();
+	//cairo_set_source_surface(cr, imgSurface, 50, 50);
+	//cairo_paint(cr);
+	//cairo_surface_destroy(imgSurface);
+
+	auto g = new Graphics(cr);
+	scope args = new PaintingEventArgs(g);
+	w.painting(args);
+	delete g;
+	cairo_destroy(cr);
+}
+//}}}
+
+//{{{ VKToKey()
+Key VKToKey(int code) {
+	switch(code) {
+	case VK_F1:  return Key.F1;
+	case VK_F2:  return Key.F2;
+	case VK_F3:  return Key.F3;
+	case VK_F4:  return Key.F4;
+	case VK_F5:  return Key.F5;
+	case VK_F6:  return Key.F6;
+	case VK_F7:  return Key.F7;
+	case VK_F8:  return Key.F8;
+	case VK_F9:  return Key.F9;
+	case VK_F10: return Key.F10;
+	case VK_F11: return Key.F11;
+	case VK_F12: return Key.F12;
+
+	case VK_ESCAPE: return Key.Escape;
+	case VK_TAB:    return Key.Tab;
+	case VK_BACK:   return Key.Backspace;
+	case VK_RETURN: return Key.Enter;
+	case VK_SPACE:  return Key.Space;
+
+	case VK_LEFT:  return Key.Left;
+	case VK_RIGHT: return Key.Right;
+	case VK_UP:    return Key.Up;
+	case VK_DOWN:  return Key.Down;
+
+	case VK_INSERT: return Key.Insert;
+	case VK_DELETE: return Key.Delete;
+	case VK_HOME:   return Key.Home;
+	case VK_END:    return Key.End;
+	case VK_PRIOR:  return Key.PageUp;
+	case VK_NEXT:   return Key.PageDown;
+
+	case VK_SNAPSHOT: return Key.PrintScreen;
+	case VK_PAUSE:    return Key.Pause;
+
+	case VK_CAPITAL: return Key.CapsLock;
+	case VK_NUMLOCK: return Key.NumLock;
+	case VK_SCROLL:  return Key.ScrollLock;
+
+	case VK_NUMPAD0:  return Key.NumPad0;
+	case VK_NUMPAD1:  return Key.NumPad1;
+	case VK_NUMPAD2:  return Key.NumPad2;
+	case VK_NUMPAD3:  return Key.NumPad3;
+	case VK_NUMPAD4:  return Key.NumPad4;
+	case VK_NUMPAD5:  return Key.NumPad5;
+	case VK_NUMPAD6:  return Key.NumPad6;
+	case VK_NUMPAD7:  return Key.NumPad7;
+	case VK_NUMPAD8:  return Key.NumPad8;
+	case VK_NUMPAD9:  return Key.NumPad9;
+	case VK_DIVIDE:   return Key.NumPadDivide;
+	case VK_MULTIPLY: return Key.NumPadMultiply;
+	case VK_SUBTRACT: return Key.NumPadSubtract;
+	case VK_ADD:      return Key.NumPadAdd;
+	case VK_DECIMAL:  return Key.NumPadDecimal;
+
+	case VK_OEM_3:      return Key.Backquote;
+	case VK_OEM_MINUS:  return Key.Minus;
+	case VK_OEM_PLUS:   return Key.Equals;
+	case VK_OEM_4:      return Key.OpenBracket;
+	case VK_OEM_6:      return Key.CloseBracket;
+	case VK_OEM_5:      return Key.Backslash;
+	case VK_OEM_1:      return Key.Semicolon;
+	case VK_OEM_7:      return Key.Quote;
+	case VK_OEM_COMMA:  return Key.Comma;
+	case VK_OEM_PERIOD: return Key.Period;
+	case VK_OEM_2:      return Key.Slash;
+
+	//case VK_APPS: return Key.Menu;
+
+	case VK_SHIFT:   return Key.Shift;
+	case VK_CONTROL: return Key.Control;
+	case VK_MENU:    return Key.Alt;
+
+	//case VK_: return Key.;
+	default:
+		if(code >= 0x30 && code <= 0x39) // Key.D0 - Key.D9
+			return cast(Key)code;
+		if(code >= 0x41 && code <= 0x5A) // Key.A - Key.Z
+			return cast(Key)code;
+		return cast(Key)0;
+	}
+}
+//}}}
+
+// Use the msgWnd for the following:
+// Timers
+// Clipboard.DataChanged event
+// Clipboard?
+// Taskbar created event using RegisterWindowMessage("TaskbarCreated")
+// different settings changed events?
+// screen saver enabled?
+extern(Windows)
+LRESULT dynaminMsgWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+	switch(uMsg) {
+	case WM_THEMECHANGED:
+		Ux.themeChanged();
+		return 0;
+	case WM_POWERBROADCAST:
+		if(wParam == PBT_APMRESUMESUSPEND || wParam == PBT_APMRESUMECRITICAL)
+			Environment.backend_increaseTimerRes();
+		return 0;
+	case WM_TIMER:
+	case WM_CHANGECBCHAIN:
+	case WM_DRAWCLIPBOARD:
+	case WM_TIMECHANGE: //??
+	default:
+		break;
+	}
+	return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/x_clipboard.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,129 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.x_clipboard;
+
+public import dynamin.core.string;
+public import dynamin.core.environment;
+public import dynamin.gui.x_window;
+
+// TODO: get notified of selection changes by using the XFixes extension
+// TODO: use the ClipboardManager to ensure that the clipboard data stays after
+//       the program is closed
+
+/* Fairly obvious, but:
+	selecting but with no explicit copy should only set PRIMARY,
+	never CLIPBOARD
+
+	middle mouse button should paste PRIMARY, never CLIPBOARD
+
+	explicit cut/copy commands (i.e. menu items, toolbar buttons)
+	should always set CLIPBOARD to the currently-selected data (i.e. conceptually copy PRIMARY to CLIPBOARD)
+
+	explicit paste commands should paste CLIPBOARD, not PRIMARY
+
+	possibly contradicting the ICCCM, clients don't need to support
+	SECONDARY, though if anyone can figure out what it's good for they should feel free to use it for that
+*/
+extern(C) XBool isRequestOrNotify(XDisplay* d, XEvent* e, XPointer arg) {
+	// either
+	// - SelectionRequest & the msgWin is the owner
+	// - SelectionNotify & another program is owner is giving data
+	return (e.type == SelectionRequest || e.type == SelectionNotify) &&
+		e.xany.window == msgWin;
+
+}
+
+string backend_getSelText(XAtom sel, ref ClipboardData data) {
+	XConvertSelection(display, sel, XA.UTF8_STRING, XA.DYNAMIN_SELECTION, msgWin, CurrentTime);
+	XSync(display, false);
+	auto start = Environment.runningTime;
+	XEvent ev;
+	while(true) {
+		// don't wait more than a second
+		if(Environment.runningTime - start > 1000)
+			return null;
+		if(!XCheckIfEvent(display, &ev, &isRequestOrNotify, null))
+			continue;
+		if(ev.type == SelectionRequest)
+				return data.data[0..data.length];
+		// must be SelectionNotify past here
+
+		auto selEv = &ev.xselection;
+		if(selEv.property == None)
+			return null;
+
+		int count;
+		char* propData = cast(char*)getXWindowProperty(display, msgWin,
+			selEv.property, &count);
+		scope(exit) XFree(propData);
+		XDeleteProperty(display, msgWin, selEv.property);
+
+		string str = new char[count];
+		str[] = propData[0..count];
+		return str;
+	}
+}
+struct ClipboardData {
+	XAtom target;
+	char* data;
+	uint length; // number of bytes in data
+}
+// always called from the event thread...don't have to avoid static data
+void backend_setSelText(XAtom sel, string text, ref ClipboardData data) {
+	XSetSelectionOwner(display, sel, msgWin, CurrentTime);
+	data.target = XA.UTF8_STRING;
+	data.data = text.ptr;
+	data.length = text.length;
+
+	XConvertSelection(display, XA.CLIPBOARD_MANAGER, XA.SAVE_TARGETS, None, msgWin, CurrentTime);
+}
+
+template ClipboardBackend() {
+	ClipboardData data; // make array when supporting multiple types (PNG & BMP)
+	void backend_setText(string text) {
+		backend_setSelText(XA.CLIPBOARD, text, data);
+	}
+	string backend_getText() {
+		return backend_getSelText(XA.CLIPBOARD, data);
+	}
+	bool backend_containsText() {
+		return backend_getText() != null;
+	}
+}
+
+template SelectionBackend() {
+	ClipboardData data; // make array when supporting multiple types (PNG & BMP)
+	void backend_setText(string text) {
+		backend_setSelText(XA.PRIMARY, text, data);
+	}
+	string backend_getText() {
+		return backend_getSelText(XA.PRIMARY, data);
+	}
+	bool backend_containsText() {
+		return backend_getText() != null;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/x_cursor.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,75 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+module dynamin.gui.x_cursor;
+
+public import tango.io.Stdout;
+public import dynamin.gui.control;
+
+template CursorBackend() {
+static:
+	void backend_SetCurrent(Control c, Cursor cur) {
+	}
+	Cursor none = null;
+	Cursor arrow = null;
+	Cursor waitArrow = null;
+	Cursor wait = null;
+	Cursor text = null;
+	Cursor hand = null;
+	Cursor move = null;
+	Cursor resizeHoriz = null;
+	Cursor resizeVert = null;
+	Cursor resizeBackslash = null;
+	Cursor resizeSlash = null;
+	Cursor drag = null; // from resource
+	Cursor invalidDrag = null;
+	Cursor reversedArrow = null; // from resource
+	Cursor crosshair = null;
+
+	Cursor backend_None() {
+		return null;
+	}
+	Cursor backend_Arrow() {
+		return null;
+	}
+	Cursor backend_WaitArrow() {
+		return null;
+	}
+	Cursor backend_Wait() {
+		return null;
+	}
+	Cursor backend_Text() {
+		return null;
+	}
+	Cursor backend_Hand() {
+		return null;
+	}
+	Cursor backend_Move() {
+		return null;
+	}
+	Cursor backend_ResizeHoriz() {
+		return null;
+	}
+	Cursor backend_ResizeVert() {
+		return null;
+	}
+	Cursor backend_ResizeBackslash() {
+		return null;
+	}
+	Cursor backend_ResizeSlash() {
+		return null;
+	}
+	Cursor backend_Drag() {
+		return null;
+	}
+	Cursor backend_InvalidDrag() {
+		return null;
+	}
+	Cursor backend_ReversedArrow() {
+		return null;
+	}
+	Cursor backend_Crosshair() {
+		return null;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/x_key.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,135 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+
+
+Key XKeyCodeToKey(int code) {
+	switch(code) {
+	case XK_parenright:
+	case XK_0: return Key.D0;
+	case XK_exclam:
+	case XK_1: return Key.D1;
+	case XK_at:
+	case XK_2: return Key.D2;
+	case XK_numbersign:
+	case XK_3: return Key.D3;
+	case XK_dollar:
+	case XK_4: return Key.D4;
+	case XK_percent:
+	case XK_5: return Key.D5;
+	case XK_asciicircum:
+	case XK_6: return Key.D6;
+	case XK_ampersand:
+	case XK_7: return Key.D7;
+	case XK_asterisk:
+	case XK_8: return Key.D8;
+	case XK_parenleft:
+	case XK_9: return Key.D9;
+
+	case XK_F1: return Key.F1;
+	case XK_F2: return Key.F2;
+	case XK_F3: return Key.F3;
+	case XK_F4: return Key.F4;
+	case XK_F5: return Key.F5;
+	case XK_F6: return Key.F6;
+	case XK_F7: return Key.F7;
+	case XK_F8: return Key.F8;
+	case XK_F9: return Key.F9;
+	case XK_F10: return Key.F10;
+	case XK_F11: return Key.F11;
+	case XK_F12: return Key.F12;
+
+	case XK_Escape: return Key.Escape;
+	case XK_Tab: return Key.Tab;
+	case XK_BackSpace: return Key.Backspace;
+	case XK_Return: return Key.Enter;
+	case XK_KP_Enter: return Key.Enter;
+	case XK_space: return Key.Space;
+
+	case XK_KP_Left:
+	case XK_Left: return Key.Left;
+	case XK_KP_Right:
+	case XK_Right: return Key.Right;
+	case XK_KP_Up:
+	case XK_Up: return Key.Up;
+	case XK_KP_Down:
+	case XK_Down: return Key.Down;
+
+	case XK_KP_Insert:
+	case XK_Insert: return Key.Insert;
+	case XK_KP_Delete:
+	case XK_Delete: return Key.Delete;
+	case XK_KP_Home:
+	case XK_Home: return Key.Home;
+	case XK_KP_End:
+	case XK_End: return Key.End;
+	case XK_KP_Prior:
+	case XK_Prior: return Key.PageUp;
+	case XK_KP_Next:
+	case XK_Next: return Key.PageDown;
+
+	case XK_Sys_Req: return Key.PrintScreen;
+	case XK_Pause: return Key.Pause;
+
+	case XK_Caps_Lock: return Key.CapsLock;
+	case XK_Num_Lock: return Key.NumLock;
+	case XK_Scroll_Lock: return Key.ScrollLock;
+
+	case XK_KP_0: return Key.NumPad0;
+	case XK_KP_1: return Key.NumPad1;
+	case XK_KP_2: return Key.NumPad2;
+	case XK_KP_3: return Key.NumPad3;
+	case XK_KP_4: return Key.NumPad4;
+	case XK_KP_5: return Key.NumPad5;
+	case XK_KP_6: return Key.NumPad6;
+	case XK_KP_7: return Key.NumPad7;
+	case XK_KP_8: return Key.NumPad8;
+	case XK_KP_9: return Key.NumPad9;
+	case XK_KP_Divide: return Key.NumPadDivide;
+	case XK_KP_Multiply: return Key.NumPadMultiply;
+	case XK_KP_Subtract: return Key.NumPadSubtract;
+	case XK_KP_Add: return Key.NumPadAdd;
+	case XK_KP_Decimal: return Key.NumPadDecimal;
+
+	case XK_grave:
+	case XK_asciitilde: return Key.Backquote;
+	case XK_minus:
+	case XK_underscore: return Key.Minus;
+	case XK_equal:
+	case XK_plus: return Key.Equals;
+	case XK_bracketleft:
+	case XK_braceleft: return Key.OpenBracket;
+	case XK_bracketright:
+	case XK_braceright: return Key.CloseBracket;
+	case XK_backslash:
+	case XK_bar: return Key.Backslash;
+	case XK_semicolon:
+	case XK_colon: return Key.Semicolon;
+	case XK_apostrophe:
+	case XK_quotedbl: return Key.Quote;
+	case XK_comma:
+	case XK_less: return Key.Comma;
+	case XK_period:
+	case XK_greater: return Key.Period;
+	case XK_slash:
+	case XK_question: return Key.Slash;
+
+	//case XK_Menu: return Key.Menu;
+
+	case XK_Shift_L:
+	case XK_Shift_R: return Key.Shift;
+	case XK_Control_L:
+	case XK_Control_R: return Key.Control;
+	case XK_Alt_L:
+	case XK_Alt_R: return Key.Alt;
+
+	//case XK_: return Key.;
+	default:
+		if(code >= 0x41 && code <= 0x5A) // Key.A - Key.Z
+			return cast(Key)code;
+		if(code >= 0x61 && code <= 0x7A) // Key.A - Key.Z
+			return cast(Key)(code-32);
+		return 0;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/x_window.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,729 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.x_window;
+
+public import dynamin.core.string;
+public import dynamin.core.global;
+public import dynamin.core.math;
+public import dynamin.gui.window;
+public import dynamin.c.xlib;
+public import dynamin.c.xlib : XWindow = Window;
+public import dynamin.c.xmu;
+public import dynamin.c.cairo;
+public import dynamin.c.cairo_xlib;
+public import tango.stdc.string;
+public import tango.io.Stdout;
+
+/*
+** Window property:
+** _NET_FRAME_EXTENTS(CARDINAL) = 4, 4, 23, 4
+**    left, right, top and bottom border sizes
+*/
+
+Window[XWindow] windows;
+void setControl(XWindow handle, Window win) {
+	if(win is null)
+		windows.remove(handle);
+	else
+		windows[handle] = win;
+}
+
+Window getControl(XWindow handle) {
+	auto tmp = handle in windows;
+	return tmp is null ? null : *tmp;
+}
+
+/**
+ * A simpler method that returns all the data in a property.
+ * NOTE: the returned data still has to be freed with XFree()
+ */
+void* getXWindowProperty(XDisplay* d, XWindow w, XAtom prop, int* numRet = null) {
+	XAtom actualType;
+	int actualFormat;
+	uint nitems, bytesAfter;
+	void* ptr;
+	XGetWindowProperty(d, w, prop,
+		0, 0xFFFFFFFF, false, AnyPropertyType,
+		&actualType, &actualFormat, &nitems, &bytesAfter,
+		&ptr);
+	if(numRet) *numRet = nitems;
+	return ptr;
+}
+bool isWMPropertySupported(XAtom prop) {
+	int count;
+	XAtom* atoms = cast(XAtom*)getXWindowProperty(display,
+		root, XA._NET_SUPPORTED, &count);
+	scope(exit) XFree(atoms);
+	for(int i = 0; i < count; ++i)
+		if(atoms[i] == prop)
+			return true;
+	return false;
+}
+bool isTopLevelReparented(XWindow w) {
+	XWindow root, parent;
+	XWindow* children;
+	uint numChildren;
+	XQueryTree(display, w,
+		&root, &parent, &children, &numChildren);
+	return parent != root;
+}
+
+XDisplay* display;
+XWindow root;
+XWindow msgWin;
+abstract class XA { // X atoms
+static:
+	XAtom _NET_SUPPORTED, _NET_WM_NAME, _NET_WORKAREA, _NET_FRAME_EXTENTS;
+	XAtom _NET_REQUEST_FRAME_EXTENTS;
+	XAtom _NET_MOVERESIZE_WINDOW;
+	XAtom _NET_WM_WINDOW_TYPE;
+	XAtom _NET_WM_WINDOW_TYPE_MENU, _NET_WM_WINDOW_TYPE_UTILITY;
+	XAtom _NET_WM_WINDOW_TYPE_SPLASH;
+	XAtom _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_WINDOW_TYPE_NORMAL;
+	XAtom WM_PROTOCOLS, WM_DELETE_WINDOW, _NET_WM_SYNC_REQUEST;
+	XAtom UTF8_STRING, ATOM;
+	XAtom _MOTIF_WM_HINTS;
+	XAtom CLIPBOARD, PRIMARY, TARGETS, CLIPBOARD_MANAGER, SAVE_TARGETS;
+	XAtom DYNAMIN_SELECTION;
+}
+static this() {
+	display = XOpenDisplay(null);
+	if(!display)
+		Stdout("XOpenDisplay() failed").newline;
+	root = XRootWindow(display, XDefaultScreen(display));
+
+	msgWin = XCreateSimpleWindow(display, root, 0, 0, 1, 1, 0, 0, 0);
+
+	XA._NET_SUPPORTED   = XInternAtom(display, "_NET_SUPPORTED", false);
+	XA._NET_WM_NAME     = XInternAtom(display, "_NET_WM_NAME", false);
+	XA._NET_WORKAREA    = XInternAtom(display, "_NET_WORKAREA", false);
+	XA._NET_FRAME_EXTENTS = XInternAtom(display, "_NET_FRAME_EXTENTS", false);
+	XA._NET_REQUEST_FRAME_EXTENTS =
+		XInternAtom(display, "_NET_REQUEST_FRAME_EXTENTS", false);
+	XA._NET_MOVERESIZE_WINDOW =
+		XInternAtom(display, "_NET_MOVERESIZE_WINDOW", false);
+	XA._NET_WM_WINDOW_TYPE =
+		XInternAtom(display, "_NET_WM_WINDOW_TYPE", false);
+	XA._NET_WM_WINDOW_TYPE_MENU =
+		XInternAtom(display, "_NET_WM_WINDOW_TYPE_MENU", false);
+	XA._NET_WM_WINDOW_TYPE_UTILITY =
+		XInternAtom(display, "_NET_WM_WINDOW_TYPE_UTILITY", false);
+	XA._NET_WM_WINDOW_TYPE_SPLASH =
+		XInternAtom(display, "_NET_WM_WINDOW_TYPE_SPLASH", false);
+	XA._NET_WM_WINDOW_TYPE_DIALOG =
+		XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", false);
+	XA._NET_WM_WINDOW_TYPE_NORMAL =
+		XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", false);
+	XA.WM_PROTOCOLS     = XInternAtom(display, "WM_PROTOCOLS", false);
+	XA.WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", false);
+	XA._NET_WM_SYNC_REQUEST =
+		XInternAtom(display, "_NET_WM_SYNC_REQUEST", false);
+	XA.UTF8_STRING      = XInternAtom(display, "UTF8_STRING", false);
+	XA.ATOM = XInternAtom(display, "ATOM", false);
+	XA._MOTIF_WM_HINTS  = XInternAtom(display, "_MOTIF_WM_HINTS", false);
+	XA.CLIPBOARD = XInternAtom(display, "CLIPBOARD", false);
+	XA.PRIMARY = XInternAtom(display, "PRIMARY", false);
+	XA.TARGETS = XInternAtom(display, "TARGETS", false);
+	XA.CLIPBOARD_MANAGER = XInternAtom(display, "CLIPBOARD_MANAGER", false);
+	XA.SAVE_TARGETS = XInternAtom(display, "SAVE_TARGETS", false);
+
+	XA.DYNAMIN_SELECTION = XInternAtom(display, "DYNAMIN_SELECTION", false);
+	if(!isWMPropertySupported(XA._NET_WM_NAME))
+		Stdout("warning: WM does not support _NET_WM_NAME").newline;
+	if(!isWMPropertySupported(XA._NET_WORKAREA))
+		Stdout("warning: WM does not support _NET_WORKAREA").newline;
+	if(!isWMPropertySupported(XA._NET_FRAME_EXTENTS))
+		Stdout("warning: WM does not support _NET_FRAME_EXTENTS").newline;
+	if(!isWMPropertySupported(XA._NET_WORKAREA))
+		Stdout("warning: WM does not support _NET_WORKAREA").newline;
+}
+
+struct _InvalidRect {
+	XWindow window;
+	int x, y, width, height;
+	_InvalidRect getUnion(_InvalidRect rect) {
+		auto x2 = min(x, rect.x);
+		auto y2 = min(y, rect.y);
+		_InvalidRect rect2;
+		rect2.window = window;
+		rect2.width = max(x+width, rect.x+rect.width)-x2;
+		rect2.height = max(y+height, rect.y+rect.height)-y2;
+		rect2.x = x2;
+		rect2.y = y2;
+		return rect2;
+	}
+}
+/+struct InvalidRect {
+	XWindow window;
+	int x, y, width, height;
+}
+
+static class PaintQueue {
+static:
+	//LinkedList!(InvalidRect) rects;
+	InvalidRect[] rects;
+	static this() {
+		//rects = new LinkedList!(InvalidRect)(20);
+		rects.length = 20;
+		rects.length = 0;
+	}
+	void add(XWindow win, int x, int y, int width, int height) {
+		rects.length = rects.length + 1;
+		rects[$-1].window = win;
+		rects[$-1].x = x;
+		rects[$-1].y = y;
+		rects[$-1].width = width;
+		rects[$-1].height = height;
+	}
+	bool shouldMerge(int x1, int y1, int width1, int height1,
+		int x2, int y2, int width2, int height2) {
+		return x2 <= x1 + width1 && y2 <= y1 + height1 &&
+			x2 + width2 >= x1 && y2 + height2 >= y1;
+	}
+	void Union(inout int x, inout int y, inout int width, inout int height,
+		int x2, int y2, int width2, int height2) {
+	}
+	void compress() {
+	}
+}+/
+
+//{{{ ApplicationBackend
+template ApplicationBackend() {
+	void backend_run(Window w) {
+		bool isWindowVisible() {
+			if(w is null) return true;
+			return w.visible;
+		}
+		XEvent ev;
+		while(isWindowVisible()) {
+			if(XEventsQueued(display, QueuedAlready) == 0) {
+				_InvalidRect[] rects = Window.invalidRects;
+				Window.invalidRects.length = 0;
+				while(rects.length > 0) {
+					auto rect = rects[0];
+					// TODO: fix this...right now it gens one Expose with
+					// the union of invalid rects
+					for(int i = rects.length-1; i >= 0; --i) {
+						if(rect.window == rects[i].window) {
+							rect = rect.getUnion(rects[i]);
+
+							arrayCopy!(_InvalidRect)(rects, i+1, rects, i, rects.length-i-1);
+							rects.length = rects.length-1;
+						}
+					}
+					ev.xexpose.type = Expose;
+					ev.xexpose.display = display;
+					ev.xexpose.window = rect.window;
+					ev.xexpose.x = rect.x;
+					ev.xexpose.y = rect.y;
+					ev.xexpose.width = rect.width;
+					ev.xexpose.height = rect.height;
+					ev.xexpose.count = -2; // came from here
+					XPutBackEvent(display, &ev);
+				}
+			}
+			XNextEvent(display, &ev);
+			auto evDisplay = ev.xany.display;
+			auto evWindow = ev.xany.window;
+			Window c = getControl(evWindow);
+			// c will be null for SelectionRequest events
+			//if(c is null)
+			//	continue;
+			//{{{ helper functions
+			void createMouseEvent(void delegate(MouseEventArgs args) func) {
+				MouseButton button;
+				auto buttonEv = ev.xbutton;
+				switch(buttonEv.button) {
+				case 1: button = MouseButton.Left; break;
+				case 2: button = MouseButton.Middle; break;
+				case 3: button = MouseButton.Right; break;
+				default: return;
+				}
+				scope args = new MouseEventArgs(buttonEv.x+c._borderSize.left, buttonEv.y+c._borderSize.top, button);
+				func(args);
+			}
+			//}}}
+			switch(ev.type) {
+			case MapNotify:
+				c.mapped = true;
+				break;
+			case UnmapNotify:
+				_InvalidRect[] rects = Window.invalidRects;
+				for(int i = rects.length-1; i >= 0; --i) {
+					if(rects[i].window == evWindow) {
+						arrayCopy!(_InvalidRect)(
+							rects, i+1, rects, i, rects.length-i-1);
+						rects.length = rects.length-1;
+					}
+				}
+				Window.invalidRects = rects;
+				c.mapped = false;
+				break;
+			case DestroyNotify:
+				setControl(evWindow, null);
+				break;
+			case ClientMessage:
+				auto clientEv = ev.xclient;
+				if(clientEv.message_type != XA.WM_PROTOCOLS)
+					break;
+				if(clientEv.data.l[0] == XA.WM_DELETE_WINDOW) {
+					XDestroyWindow(evDisplay, evWindow);
+					return; // TODO: check event, and figure out what to do
+				}
+				break;
+			case KeyPress:
+				break;
+			case KeyRelease:
+				break;
+			case ButtonPress:
+				//Button4 is wheel scroll up
+				//Button5 is wheel scroll down
+				createMouseEvent((MouseEventArgs args) { c.mouseDown(args); });
+				break;
+			case ButtonRelease:
+				createMouseEvent((MouseEventArgs args) { c.mouseUp(args); });
+				break;
+			case MotionNotify:
+				auto motionEv = ev.xmotion;
+				Control captor = getCaptorControl();
+				Point pt = Point(motionEv.x+c.borderSize.left, motionEv.y+c.borderSize.top);
+				if(captor)
+					pt = c.contentToContent(pt, captor);
+				else
+					captor = c;
+				scope args = new MouseEventArgs(pt.x, pt.y, MouseButton.None);
+				if(motionEv.state &
+					(Button1Mask | Button2Mask | Button3Mask)) {
+					captor.mouseDragged(args);
+				} else
+					captor.mouseMoved(args);
+				break;
+			case EnterNotify:
+				break;
+			case LeaveNotify:
+				break;
+			case FocusIn:
+				break;
+			case FocusOut:
+				break;
+			case Expose:
+				// TODO: move the painting code out of here and:
+				//  make a PaintQueue class and put this here:
+				//  PaintQueue.add(c, exposeEv.x, exposeEv.y, exposeEv.width, exposeEv.height);
+				// then, in Window.repaint(), have this:
+				//  PaintQueue.add(this, cast(int)x, cast(int)exposeEv.y, cast(int)exposeEv.width, cast(int)exposeEv.height);
+				// Have a PaintQueue.Compress method that merges
+				// all invalidated rects that touch or overlap.
+				// In the if(!XPending(..)) above, just loop over all the
+				// rects in the PaintQueue, painting them.
+				auto exposeEv = ev.xexpose;
+				if(exposeEv.count != -2) {
+					c.repaint(exposeEv.x, exposeEv.y, exposeEv.width, exposeEv.height);
+					break;
+				}
+				//printf("repainting x=%d, y=%d, width=%d, height=%d\n",
+				//	exposeEv.x, exposeEv.y, exposeEv.width, exposeEv.height);
+
+				auto surfaceWin = cairo_xlib_surface_create(
+					evDisplay, evWindow,
+					XDefaultVisual(evDisplay, XDefaultScreen(evDisplay)),
+					cast(int)c.width, cast(int)c.height);
+				// TODO: ^ should be contentWidth/height or got from evWindow
+				auto crWin = cairo_create(surfaceWin);
+				cairo_surface_destroy(surfaceWin);
+
+				auto surfaceBuff = cairo_surface_create_similar(surfaceWin, CAIRO_CONTENT_COLOR, exposeEv.width, exposeEv.height);
+				// TODO: use cairo_translate instead, I guess, as
+				// I had to change the Windows backend to it...
+				cairo_surface_set_device_offset(surfaceBuff, -exposeEv.x-c._borderSize.left, -exposeEv.y-c._borderSize.top);
+				auto crBuff = cairo_create(surfaceBuff);
+				cairo_surface_destroy(surfaceBuff);
+
+				cairo_set_source_rgb(crBuff, w.content.backColor.R/255.0, w.content.backColor.G/255.0, w.content.backColor.B/255.0);
+				cairo_paint(crBuff);
+
+				cairo_set_source_rgb(crBuff, 0, 0, 0);
+				cairo_set_line_width(crBuff, 1.0);
+
+				auto g = new Graphics(crBuff);
+				scope args = new PaintingEventArgs(g);
+				c.painting(args);
+				delete g;
+
+				cairo_surface_set_device_offset(surfaceBuff, -exposeEv.x, -exposeEv.y);
+				cairo_set_source_surface(crWin, surfaceBuff, 0, 0);
+				cairo_rectangle(crWin, exposeEv.x, exposeEv.y, exposeEv.width, exposeEv.height);
+				cairo_fill(crWin);
+
+				cairo_destroy(crBuff);
+				cairo_destroy(crWin);
+				break;
+			case PropertyNotify:
+				auto propertyEv = ev.xproperty;
+				if(propertyEv.atom == XA._NET_FRAME_EXTENTS &&
+					propertyEv.state != PropertyDelete)
+					c.backend_nativeToBorderSize();
+				break;
+			case ConfigureNotify:
+				auto configureEv = ev.xconfigure;
+				c.repaint();
+				c.backend_nativeToLocationSize();
+				break;
+			case SelectionRequest:
+				auto selReqEv = ev.xselectionrequest;
+				XEvent fullEv;
+				auto selEv = &fullEv.xselection;
+				selEv.type = SelectionNotify;
+				selEv.requestor = selReqEv.requestor;
+				selEv.selection = selReqEv.selection;
+				selEv.target = selReqEv.target;
+				if(selReqEv.property != None)
+					selEv.property = selReqEv.property;
+				else
+					selEv.property = XA.DYNAMIN_SELECTION;
+				selEv.time = selReqEv.time;
+				Stdout.format("requestor: {}", selReqEv.requestor).newline;
+				Stdout.format("target: {}", selReqEv.target).newline;
+				ClipboardData* data; // change to array when supporting multiple
+				if(selReqEv.selection == XA.CLIPBOARD)
+					data = &Clipboard.data;
+				else if(selReqEv.selection == XA.PRIMARY)
+					data = &Selection.data;
+				else {
+					selEv.property = None;
+					XSendEvent(display, selEv.requestor, false, 0, &fullEv);
+					break;
+				}
+				if(selReqEv.target == XA.TARGETS) {
+					XChangeProperty(display, selEv.requestor, selEv.property,
+						selEv.target, 32, PropModeReplace, &data.target, 1);
+					XSendEvent(display, selEv.requestor, false, 0, &fullEv);
+					break;
+				} else if(selReqEv.target != data.target) {
+					selEv.property = None;
+					XSendEvent(display, selEv.requestor, false, 0, &fullEv);
+					break;
+				}
+				XChangeProperty(display, selEv.requestor, selEv.property,
+					data.target, 8, PropModeReplace, data.data, data.length);
+				XSendEvent(display, selEv.requestor, false, 0, &fullEv);
+				break;
+			default:
+				break;
+			}
+		}
+	}
+}
+//}}}
+
+public import tango.stdc.time;
+template WindowBackend() {
+	invariant {
+		//if(_handle == 0)
+		//	return;
+		//XWindow root, parent;
+		//XWindow* children;
+		//uint numChildren;
+		//XQueryTree(display, _handle,
+		//	&root, &parent, &children, &numChildren);
+		//XFree(children);
+		//int x, y;
+		//XWindow child;
+		//XTranslateCoordinates(display, _handle, root, 0, 0, &x, &y, &child);
+		//assert(_location.X == x-_borderSize.Left);
+		//assert(_location.Y == y-_borderSize.Top);
+		//XWindowAttributes attribs;
+		//XGetWindowAttributes(display, _handle, &attribs);
+	}
+	XWindow _handle;
+	bool mapped = false;
+	bool backend_handleCreated() { return _handle > 0; }
+	void backend_recreateHandle() {
+		auto primaryScreenNum = XDefaultScreen(display);
+		//XColor color;
+		//color.red = 65535*backColor.R/255;
+		//color.green = 65535*backColor.G/255;
+		//color.blue = 65535*backColor.B/255;
+		//if(XAllocColor(display, XDefaultColormap(display, primaryScreenNum), &color))
+		//	printf("XAllocColor() failed\n");
+
+		XSetWindowAttributes attribs;
+		attribs.bit_gravity = NorthWestGravity;
+		// TODO: should be backColor, and should change when backColor changes
+		// call XSetWindowBackground() for this
+		attribs.background_pixel = XWhitePixel(display, primaryScreenNum);
+		attribs.event_mask =
+			KeyPressMask |
+			KeyReleaseMask |
+			ButtonPressMask |
+			ButtonReleaseMask |
+			EnterWindowMask |
+			LeaveWindowMask |
+			PointerMotionMask |
+			ButtonMotionMask |
+			ExposureMask |
+			FocusChangeMask |
+			StructureNotifyMask |
+			PropertyChangeMask;
+		XWindow newHandle = XCreateWindow(
+			display, root,
+			cast(int)x, cast(int)y,
+			backend_insideWidth, backend_insideHeight,
+			0, CopyFromParent, InputOutput, null,
+			CWBitGravity | CWBackPixel | CWEventMask, &attribs);
+
+		setControl(newHandle, this);
+		auto protocols = [XA.WM_DELETE_WINDOW];
+		XSetWMProtocols(display, newHandle, protocols.ptr, protocols.length);
+		if(handleCreated) {
+			XDestroyWindow(display, _handle);
+			XSync(display, false);
+		}
+		_handle = newHandle;
+		text = _text; // move the text over to the new window
+		visible = _visible;
+		borderStyle = _borderStyle;
+		//backend_nativeToBorderSize();
+	}
+	Graphics backend_quickCreateGraphics() {
+		auto surface = cairo_xlib_surface_create(display, _handle,
+			XDefaultVisual(display, XDefaultScreen(display)),
+			cast(int)width, cast(int)height);
+		auto cr = cairo_create(surface);
+		cairo_surface_destroy(surface);
+		cairo_translate(cr, -borderSize.left, -borderSize.top);
+		auto g = new Graphics(cr);
+		cairo_destroy(cr);
+		return g;
+	}
+	void backend_visible(bool b) {
+		if(b)
+			// if not created, create the handle by calling handle()
+			XMapWindow(display, handle);
+		else
+			XUnmapWindow(display, _handle);
+	}
+	void backend_borderStyle(WindowBorderStyle border) {
+		backend_update_NET_WM_WINDOW_TYPE();
+		backend_update_MOTIF_WM_HINTS();
+		backend_nativeToBorderSize();
+	}
+	static _InvalidRect[] invalidRects;
+	void backend_repaint(Rect rect) {
+		invalidRects.length = invalidRects.length+1;
+		invalidRects[$-1].window = _handle;
+		invalidRects[$-1].x = cast(int)(rect.x-borderSize.left);
+		invalidRects[$-1].y = cast(int)(rect.y-borderSize.top);
+		invalidRects[$-1].width = cast(int)rect.width+1;
+		invalidRects[$-1].height = cast(int)rect.height+1;
+		//printf("invalidating x=%.1f, y=%.1f, width=%.1f, height=%.1f\n", rect.X, rect.Y, rect.width, rect.height);
+	}
+	void backend_resizable(bool b) {
+		backend_updateWM_NORMAL_HINTS();
+	}
+	void backend_contentMinSizeChanged() {
+		backend_updateWM_NORMAL_HINTS();
+	}
+	void backend_contentMaxSizeChanged() {
+		backend_updateWM_NORMAL_HINTS();
+	}
+	void backend_location(Point pt) {
+		backend_updateWM_NORMAL_HINTS();
+		backend_locationSizeToNative();
+	}
+	void backend_size(Size size) {
+		backend_updateWM_NORMAL_HINTS();
+		backend_locationSizeToNative();
+	}
+	void backend_text(string str) {
+		//auto tmp = str.ToCharPtr();
+		//XTextProperty strProperty;
+		//if(!XStringListToTextProperty(&tmp, 1, &strProperty))
+			//printf("XStringListToTextProperty() failed - out of memory\n");
+		//XSetWMName(display, _handle, &strProperty);
+		XChangeProperty(display, _handle, XA._NET_WM_NAME, XA.UTF8_STRING, 8, PropModeReplace, str.ptr, str.length);
+	}
+	//{{{ backend specific
+	void backend_updateWM_NORMAL_HINTS() {
+		XSizeHints* sizeHints = XAllocSizeHints();
+		scope(exit) XFree(sizeHints);
+		sizeHints.flags = PMinSize | PMaxSize | PPosition | PSize;
+		if(resizable) {
+			sizeHints.min_width  = cast(int)content.minWidth;
+			sizeHints.min_height = cast(int)content.minHeight;
+			sizeHints.max_width  =
+				content.maxWidth > 0 ? cast(int)content.maxWidth : 30_000;
+			sizeHints.max_height =
+				content.maxHeight > 0 ? cast(int)content.maxHeight : 30_000;
+		} else {
+			sizeHints.min_width  = sizeHints.max_width  = backend_insideWidth;
+			sizeHints.min_height = sizeHints.max_height = backend_insideHeight;
+		}
+		sizeHints.x = cast(int)x;
+		sizeHints.y = cast(int)y;
+		sizeHints.width = backend_insideWidth;
+		sizeHints.height = backend_insideHeight;
+		XSetWMNormalHints(display, _handle, sizeHints);
+	}
+	void backend_update_MOTIF_WM_HINTS() {
+		int[4] mwmHints;
+		mwmHints[0] = 1 << 1;  // flags
+		mwmHints[2] = borderStyle == WindowBorderStyle.None ? 0 : 1;  // decor
+		XChangeProperty(display, _handle,
+			XA._MOTIF_WM_HINTS, XA._MOTIF_WM_HINTS, 32, PropModeReplace, mwmHints.ptr, mwmHints.length);
+	}
+	void backend_update_NET_WM_WINDOW_TYPE() {
+		XAtom[] atoms;
+		// with Metacity, the decor is not being repainted from normal>dialog
+		// because they are the same size
+		if(borderStyle == WindowBorderStyle.Dialog)
+			atoms = [XA._NET_WM_WINDOW_TYPE_DIALOG];
+		else if(borderStyle == WindowBorderStyle.Tool)
+			atoms = [XA._NET_WM_WINDOW_TYPE_UTILITY];
+		else
+			atoms = [XA._NET_WM_WINDOW_TYPE_NORMAL];
+		XChangeProperty(display, _handle,
+			XA._NET_WM_WINDOW_TYPE, XA.ATOM, 32, PropModeReplace, atoms.ptr, atoms.length);
+	}
+	// returns what width the x window should be...not including borders
+	int backend_insideWidth() {
+		return cast(int)(size.width-borderSize.left-borderSize.right);
+	}
+	// returns what height the x window should be...not including borders
+	int backend_insideHeight() {
+		return cast(int)(size.height-borderSize.top-borderSize.bottom);
+	}
+	// applies the currently set location and size to the native X Window
+	void backend_locationSizeToNative() {
+		Point pt = _location;
+		if(!isTopLevelReparented(_handle)) {
+			pt.x = pt.x + _borderSize.left;
+			pt.y = pt.y + _borderSize.top;
+		}
+		XMoveResizeWindow(display, _handle, cast(int)pt.x, cast(int)pt.y,
+			backend_insideWidth, backend_insideHeight);
+		// XMoveWindow:
+		//   Under Metacity, sets the location of the WM's frame.
+		//   Under Compiz, sets the location of the window.
+		// XResizeWindow:
+		//   Under Metacity and Compiz, sets the size of the window not
+		//   including the WM's frame.
+	}
+	// sets location and size based on where the native X Window is
+	void backend_nativeToLocationSize() {
+		XWindow root, parent;
+		XWindow* children;
+		uint numChildren;
+		XQueryTree(display, _handle,
+			&root, &parent, &children, &numChildren);
+		XFree(children);
+
+		int x, y;
+		XWindow child;
+		XTranslateCoordinates(display, _handle, root, 0, 0, &x, &y, &child);
+		_location.x = x - _borderSize.left;
+		_location.y = y - _borderSize.top;
+		scope args = new EventArgs;
+		moved(args);
+		XWindowAttributes attribs;
+		XGetWindowAttributes(display, _handle, &attribs);
+		_size.width = attribs.width+_borderSize.left+_borderSize.right;
+		_size.height = attribs.height+_borderSize.top+_borderSize.bottom;
+		resized(args);
+
+		//content._location = Point(_borderSize.Left, _borderSize.Top);
+		//content._size = Size(attribs.width, attribs.height);
+		//Stdout.format("location updated to {}", _location).newline;
+		//Stdout.format("size updated to {}", _size).newline;
+	}
+	void backend_nativeToBorderSize() {
+		_borderSize = backend_getBorderSize();
+		//Stdout("borderSize updated to ", _borderSize).newline;
+		backend_nativeToLocationSize();
+	}
+	BorderSize backend_getBorderSize() {
+		if(!isWMPropertySupported(XA._NET_FRAME_EXTENTS) ||
+			borderStyle == WindowBorderStyle.None)
+			return BorderSize();
+		// create handle if necessary
+		auto reqHandle = handle;
+		bool requested = false;
+
+		//{{{ requestExtents()
+		void requestExtents() {
+			if(isWMPropertySupported(XA._NET_REQUEST_FRAME_EXTENTS)) {
+				XEvent ev;
+				ev.xclient.type = ClientMessage;
+				ev.xclient.window = handle;
+				ev.xclient.message_type = XA._NET_REQUEST_FRAME_EXTENTS;
+				ev.xclient.format = 8;
+				XSendEvent(display, root, false,
+					SubstructureNotifyMask | SubstructureRedirectMask, &ev);
+			} else { // compiz and beryl do not yet support requesting
+				XSetWindowAttributes attribs;
+				reqHandle = XCreateWindow(display,
+					root, 0, 0, 1, 1,
+					0, CopyFromParent, InputOnly, null, 0, &attribs);
+
+				XWMHints* hints = XAllocWMHints();
+				scope(exit) XFree(hints);
+				hints.flags = InputHint;
+				hints.input = false;
+				XSetWMHints(display, reqHandle, hints);
+
+				auto mainHandle = _handle;
+				_handle = reqHandle;
+				backend_updateWM_NORMAL_HINTS();
+				backend_update_NET_WM_WINDOW_TYPE();
+				backend_update_MOTIF_WM_HINTS();
+				backend_visible = true;
+				backend_visible = false;
+				_handle = mainHandle;
+			}
+			requested = true;
+		}
+		//}}}
+
+		if(!mapped)
+			requestExtents();
+		int* extents;
+		while(true) {
+			XSync(display, false);
+			extents = cast(int*)getXWindowProperty(display, reqHandle,
+				XA._NET_FRAME_EXTENTS);
+			if(extents !is null)
+				break;
+			if(!requested)
+				requestExtents();
+		}
+		scope(exit) XFree(extents);
+		if(reqHandle != _handle)
+			XDestroyWindow(display, reqHandle);
+		return BorderSize(extents[0], extents[2], extents[1], extents[3]);
+	}
+	//}}}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui_backend.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,43 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui_backend;
+
+version(Windows) {
+	public import dynamin.gui.windows_clipboard;
+	public import dynamin.gui.windows_cursor;
+	//public import dynamin.gui.windows_directory_dialog;
+	//public import dynamin.gui.windows_file_dialog;
+	//public import dynamin.gui.windows_screen;
+	public import dynamin.gui.windows_window;
+} else {
+	public import dynamin.gui.x_clipboard;
+	public import dynamin.gui.x_cursor;
+	//public import dynamin.gui.x_directory_dialog;
+	//public import dynamin.gui.x_file_dialog;
+	//public import dynamin.gui.x_screen;
+	public import dynamin.gui.x_window;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/lodepng/common.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,736 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/***************************************************************************************************
+Types and functions common to both the encode and decoder of lodepng, as well as image format
+conversion routines
+
+License:
+Copyright (c) 2005-2007 Lode Vandevenne
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+  - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.<br>
+  - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.<br>
+  - Neither the name of Lode Vandevenne nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission.<br>
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Authors: Lode Vandevenne (original version in C++), Lutger Blijdestijn (D version) : lutger dot blijdestijn at gmail dot com,
+  Stewart Gordon (modifications)
+
+Date: August 7, 2007
+
+References:
+$(LINK2 http://members.gamedev.net/lode/projects/LodePNG/, Original lodepng) <br>
+$(LINK2 http://www.w3.org/TR/PNG/, PNG Specification) <br>
+$(LINK2 http://www.libpng.org/pub/png/pngsuite.html, PNG Suite: set of test images) <br>
+$(LINK2 http://optipng.sourceforge.net/, OptiPNG: tool to experimentally optimize png images)
+***************************************************************************************************/
+module dynamin.lodepng.common;
+import dynamin.lodepng.util;
+import dynamin.lodepng.zlib_codec;
+
+/***************************************************************************************************
+	Png specific exception.
+
+		Instead of errors codes, this port of lodepng makes use of exceptions.
+		At the moment, the decoder is very strict and will tolerate no errors whatsoever even
+		if they could safely be ignored. CRC checking is always done.
+		This might be slightly relaxed in a future release to be more in line with the
+		recommendations of the specification.
+
+***************************************************************************************************/
+class PngException : Exception
+{
+	this(char[] msg)
+	{
+		super(msg);
+	}
+}
+
+package alias _enforce!(PngException) pngEnforce;
+
+/***************************************************************************************************
+	An enumeration of the color types supported by the png format.
+
+	see the $(LINK2 http://www.w3.org/TR/PNG/index-noobject.html#6Colour-values, png specification)
+	for details.
+
+***************************************************************************************************/
+enum ColorType : ubyte
+{
+	Greyscale = 0, /// allowed bit depths: 1, 2, 4, 8 and 16
+	RGB = 2, /// allowed bit depths: 8 and 16
+	Palette = 3, /// allowed bit depths: 1, 2, 4 and 8
+	GreyscaleAlpha = 4, /// allowed bit depths: 8 and 16
+	RGBA = 6, /// allowed bit depths: 8 and 16
+	Any = 7, /// one of the above
+}
+
+/***************************************************************************************************
+Convert source pixels from the color type described by info the color type destColorType.
+
+	Conversion can be from any color type supported by the png specification to
+	24 / 32 bit RGB(A). If RGBA is specified and the info has a colorkey, transparency is applied.
+
+	If a buffer is given, it may be used to store the result and a slice from it can be returned.
+
+	Returns: converted image in RGB(A) format, pixels are from left to right, top to bottom
+***************************************************************************************************/
+ubyte[] convert(in ubyte[] source, /+const+/ ref PngInfo info, ColorType destColorType = ColorType.RGBA)
+{
+	ubyte[] buffer;
+	return convert(source, info, buffer, destColorType);
+}
+
+/// ditto
+ubyte[] convert(in ubyte[] source, /+const+/in PngInfo info, ref ubyte[] buffer, ColorType destColorType = ColorType.RGBA)
+{
+	if (!(destColorType == ColorType.RGBA || destColorType == ColorType.RGB))
+	{
+		destColorType = ColorType.RGBA;
+		assert(false, "destColorType should be one of: RGB, RGBA");
+	}
+	return _convert(source, info, buffer, destColorType);
+}
+
+/***************************************************************************************************
+	Description of the image.
+
+***************************************************************************************************/
+struct PngImage
+{
+	/// constructor
+	static PngImage opCall(uint w, uint h, ubyte bd, ColorType ct)
+	{
+		PngImage result;
+		with (result)
+		{
+			width = w;
+			height = h;
+			bitDepth = bd;
+			colorType = ct;
+			bpp = bitDepth * numChannels(colorType);
+		}
+		return result;
+	}
+	uint    width; /// in pixels
+	uint    height; /// in pixels
+	ubyte   bitDepth; /// bits per color channel, see also: ColorType
+	ubyte   bpp; /// bits per pixel
+	ColorType colorType; /// the color format, see also: ColorType
+}
+
+/***************************************************************************************************
+Png file and image description.
+
+	A simple data type describing the png file. Usually the image field will contain all the
+	required information.
+
+	There is one member that behaves a bit different: parseText(bool). This is used to tell
+	the decoder whether to ignore textual metadata (which it does by default).
+
+***************************************************************************************************/
+struct PngInfo
+{
+
+	PngImage image; /// Information related to the image, see also: PngImage.
+
+	/***********************************************************************************************
+	Recommended background color or empty if none is given.
+
+		Interpretation of the array depends on the color type:
+
+		$(DL
+			$(DT palette)
+				$(DD palette[backgroundColor[0]] is the background color)
+			$(DT greyscale (8 bit or less) )
+				$(DD backgroundColor[0] is used)
+			$(DT RGB(A) (8 bit or less))
+				$(DD backgroundColor[0..3] is the rgb triplet used)
+			$(DT 16-bit greyscale or RGB(A))
+				$(DD  same as above, but the color/greyscale channels are 2 bytes wide))
+
+	***********************************************************************************************/
+	ubyte[] backgroundColor;
+
+	/***********************************************************************************************
+	Palette colors or empty if this image does not make use of it.
+
+		  Each palette entry is a 32-bit RGBA pixel represented as an ubyte[4].
+		  When a colorkey is specified, it is automatically applied to the palette by the decoder.
+
+	***********************************************************************************************/
+	ubyte[4][] palette;
+
+	/***********************************************************************************************
+		Whether there is a colorkey transparency associated with the png image.
+
+			Note that this is applicable only when the image has no seperate alpha channel, and
+			the image format is not ColorType.Palette. In the latter case, transparency is always
+			stored in the palette by the decoder.
+			The transparent color is stored in keyR, keyG and keyB, for brevity greyscale is in keyR.
+	***********************************************************************************************/
+	bool   colorKey = false;
+	ushort keyR;     ///
+	ushort keyG;     ///
+	ushort keyB;     ///
+
+	/// By default, lodepng will not parse textual metadata, set to true if this is desired.
+	void parseText(bool flag)
+	{
+		parseTextChunks = flag;
+	}
+
+	/// Whether parsing of metadata is enabled.
+	bool parseText()
+	{
+		return parseTextChunks;
+	}
+
+	/***********************************************************************************************
+		Retrieve the dictionary of textual metadata.
+
+			When no text is read, because it was set to be ignored or there wasn't any,
+			this returns null so check the return value.
+
+			In the case that a PngInfo instance is passed more than once to the api,
+			an existing dictionary is not reused. Instead a new one will be
+			created. It is therefore not necessary to copy anything, the strings they are all yours.
+
+	***********************************************************************************************/
+	PngText text()
+	{
+		return textual;
+	}
+
+	/// Returns whether any unicode text has been stored.
+	bool hasUnicodeText()
+	{
+		return textual !is null && textual.unicodeText.length > 0;
+	}
+
+	/// Returns whether any latin-1 text has been stored.
+	bool hasLatin1Text()
+	{
+		return textual !is null && textual.latin1Text.length > 0;
+	}
+
+	package
+	{
+		ubyte interlace;
+		PngText textual;
+	}
+
+	private bool parseTextChunks = true;
+}
+
+/// Dictionary of key-value textual metadata in utf-8 and / or latin-1 encoding.
+class PngText
+{
+	///
+	this()
+	{
+	}
+
+	///
+	this(char[][char[]] unicodeText)
+	{
+		this.unicodeText = unicodeText;
+	}
+
+	///
+	this(ubyte[][ubyte[]] latin1Text)
+	{
+		this.latin1Text = latin1Text;
+	}
+
+	/// Visit utf-8 dictionary
+	int opApply(int delegate(ref char[] keyword, ref char[] contents) dg)
+	{
+		int result = 0;
+		foreach(index, value; unicodeText)
+		{
+			result = dg(index, value);
+			if (result)
+				return result;
+		}
+		return 0;
+	}
+
+	/// Visit latin-1 dictionary
+	int opApply(int delegate(ref ubyte[] keyword, ref ubyte[] contents) dg)
+	{
+		int result = 0;
+		foreach(index, value; latin1Text)
+		{
+			result = dg(index, value);
+			if (result)
+				return result;
+		}
+		return 0;
+	}
+
+	/// Assign key-value pair
+	PngText opIndexAssign(ubyte[] value, ubyte[] keyword)
+	{
+		latin1Text[value] = keyword;
+		return this;
+	}
+
+	/// ditto
+	PngText opIndexAssign(char[] value, char[] keyword)
+	{
+		unicodeText[value] = keyword;
+		return this;
+	}
+
+	package
+	{
+		char[][char[]] unicodeText;
+		ubyte[][ubyte[]] latin1Text;
+	}
+}
+
+/// Number of color or alpha channels associated with this color type.
+uint numChannels(ColorType colorType)
+in
+{
+	assert(colorType != ColorType.Any, "cannot determine number of channels with ColorType.Any");
+}
+body
+{
+	return [ 0 : 1, 2 : 3, 3 : 1, 4 : 2, 6 : 4, 7 : 0] [colorType];
+}
+
+///
+bool isGreyscale(ColorType colorType)
+{
+	return colorType == ColorType.Greyscale || colorType == ColorType.GreyscaleAlpha;
+}
+
+///
+bool hasAlphaChannel(ColorType colorType)
+{
+	return colorType == ColorType.RGBA || colorType == ColorType.GreyscaleAlpha;
+}
+
+/// Whether the image contains any alpha information (channel / colorkey / palette)
+bool hasAlpha(/+const+/ ref PngInfo info)
+{
+	if (hasAlphaChannel(info.image.colorType) || info.colorKey)
+		return true;
+	else if (info.image.colorType == ColorType.Palette)
+		foreach(color; info.palette)
+			if (color[3] != 255)
+				return true;
+	return false;
+}
+
+/* ************************************************************************************************
+	PACKAGE SECTION, not so interesting stuff
+
+************************************************************************************************* */
+package
+{
+	const IHDR = toUint('I', 'H', 'D', 'R');
+	const IDAT = toUint('I', 'D', 'A', 'T');
+	const PLTE = toUint('P', 'L', 'T', 'E');
+	const tRNS = toUint('t', 'R', 'N', 'S');
+	const bKGD = toUint('b', 'K', 'G', 'D');
+	const IEND = toUint('I', 'E', 'N', 'D');
+	const tEXt = toUint('t', 'E', 'X', 't');
+	const iTXt = toUint('i', 'T', 'X', 't');
+	const zTXt = toUint('z', 'T', 'X', 't');
+	const cHRM = toUint('c', 'H', 'R', 'M');
+	const gAMA = toUint('g', 'A', 'M', 'A');
+	const hIST = toUint('h', 'I', 'S', 'T');
+	const iCCP = toUint('i', 'C', 'C', 'P');
+	const oFFs = toUint('o', 'F', 'F', 's');
+	const pCAL = toUint('p', 'C', 'A', 'L');
+	const pHYs = toUint('p', 'H', 'Y', 's');
+	const sBIT = toUint('s', 'B', 'I', 'T');
+	const sCAL = toUint('s', 'C', 'A', 'L');
+	const sPLT = toUint('s', 'P', 'L', 'T');
+	const sRGB = toUint('s', 'R', 'G', 'B');
+
+	//return type is a LodePNG error code
+	bool checkColorValidity(uint colorType, uint bitDepth)
+	{
+		alias bitDepth bd;
+		switch(colorType)
+		{
+			case 0: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) //grey
+						return false;
+				break;
+			case 2: if(!(bd == 8 || bd == 16)) //RGB
+						return false;
+				break;
+			case 3: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) //palette
+						return false;
+				break;
+			case 4: if(!(bd == 8 || bd == 16)) //greyalpha
+						return false;
+				break;
+			case 6: if(!(bd == 8 || bd == 16)) //RGBA
+						return false;
+				break;
+			default:
+				mixin(pngEnforce(`false`, "invalid png color format"));
+		}
+		return true;
+	}
+
+	// Paeth predicter, used by one of the PNG filter types
+	int paethPredictor(int a, int b, int c)
+	{
+		int p = a + b - c;
+		int pa = p > a ? p - a : a - p;
+		int pb = p > b ? p - b : b - p;
+		int pc = p > c ? p - c : c - p;
+
+		if(pa <= pb && pa <= pc) return a;
+		else if(pb <= pc) return b;
+		return c;
+	}
+
+	uint readBitFromStreamReversed(ref size_t bitpointer, ubyte[] bitstream)
+	{
+		return (bitstream[bitpointer / 8] >> (7 - bitpointer++ & 0x7)) & 1;
+	}
+}
+
+bool checkCRC(uint crc, ubyte[] data)
+{
+	return crc == czlib.crc32(0, cast(ubyte *)data, data.length);
+}
+
+bool checkCRC(ubyte[] crc, ubyte[] data)
+{
+	return toUint(crc) == czlib.crc32(0, cast(ubyte *)data, data.length);
+}
+
+uint createCRC(ubyte[] data)
+{
+	return czlib.crc32(0, cast(ubyte *)data, data.length);
+}
+
+struct Chunk
+{
+	static Chunk fromStream(ubyte[] byteStream)
+	in
+	{
+		assert(byteStream.length >= 12);
+	}
+	body
+	{
+		Chunk result;
+		uint dataLength = toUint(byteStream);
+		result.type = toUint(byteStream[4 .. 8]);
+		if (dataLength)
+			result.data = byteStream[8 .. 8 + dataLength];
+		mixin(pngEnforce("checkCRC(byteStream[8 + dataLength..12 + dataLength], byteStream[4..8 + dataLength])",
+						 "CRC check failed"));
+		return result;
+	}
+
+	int opCmp(Chunk other)
+	{
+		int result = chunkOrder(type, other.type);
+
+		/* Setting of the afterIDAT member is not yet implemented.  It would
+		 * be used to prevent unknown ancillary chunks from being moved from
+		 * before the IDAT chunk to after it, or vice versa.
+
+		if (result == 0) {
+			result = cast(int) afterIDAT - cast(int) other.afterIDAT;
+		}
+		if (result == 0) {
+			// if one of them is IDAT, the other is before IDAT
+			if (type == IDAT) return 1;
+			if (other.type == IDAT) return -1;
+		}
+		 */
+		return result;
+	}
+
+	uint length() { return data.length + 12; }
+
+	uint type;
+	ubyte[] data;
+	//bool afterIDAT;
+}
+
+
+private int chunkOrder(uint type1, uint type2) {
+	if (type1 == type2) return 0;
+
+	// sort out IHDR and IEND first
+	if (type1 == IHDR) return -1;
+	if (type1 == IEND) return 1;
+	if (type2 == IHDR) return 1;
+	if (type2 == IEND) return -1;
+
+	// now sort out PLTE and IDAT
+	switch (type1) {
+		case PLTE:
+			switch (type2) {
+				case gAMA: case cHRM: case sRGB: case iCCP: case sBIT:
+					return 1;
+
+				case tRNS: case bKGD: case hIST: case IDAT:
+					return -1;
+
+				default:
+					return 0;
+			}
+
+		case IDAT:
+			switch (type2) {
+				case gAMA: case cHRM: case sRGB: case iCCP: case sBIT:
+				case PLTE: case tRNS: case bKGD: case hIST: case pHYs:
+				case sPLT: case oFFs: case pCAL: case sCAL:
+					return 1;
+
+				default:
+					return 0;
+			}
+
+		default:
+			if (type2 == PLTE || type2 == IDAT) {
+				return -chunkOrder(type2, type1);
+			}
+	}
+
+	// if we reach this point, they're both ancillary chunks
+	switch (type1) {
+		case gAMA: case cHRM: case sRGB: case iCCP: case sBIT:
+			switch (type2) {
+				case tRNS: case bKGD: case hIST: case IDAT:
+					return -1;
+
+				default:
+					return 0;
+			}
+
+		case tRNS: case bKGD: case hIST: case IDAT:
+			switch (type2) {
+				case gAMA: case cHRM: case sRGB: case iCCP: case sBIT:
+					return 1;
+
+				default:
+					return 0;
+			}
+
+		default:
+			return 0;
+	}
+}
+
+
+uint toUint(ubyte a, ubyte b, ubyte c, ubyte d) { return a << 24 | b << 16 | c << 8 | d; }
+uint toUint(ubyte[] source)
+	in { assert(source.length >= 4); }
+	body { return source[0] << 24 | source[1] << 16 | source[2] << 8 | source[3]; }
+
+private ubyte[] _convert(in ubyte[] source, /+const+/ ref PngInfo info, ref ubyte[] buffer, ColorType destColorType)
+{
+	bool alpha = hasAlphaChannel(destColorType);
+
+	if (destColorType == info.image.colorType)
+	{
+		if ((destColorType == ColorType.RGBA || destColorType == ColorType.RGB) && info.image.bitDepth == 8)
+		{
+			buffer = source;
+			return buffer;
+		}
+	}
+
+
+	uint w = info.image.width;
+	uint h = info.image.height;
+	ubyte[] res = buffer;
+	ubyte colors = alpha ? 4 : 3;
+
+	res.length = w * h * colors;
+
+	assert(source.length >=  (w * (info.image.bpp / 8  ) * h));
+
+	 if (!info.colorKey && alpha)
+		for(size_t i = 0; i < w * h; i++)
+			res[4 * i + 3] = 255;
+
+	switch(info.image.colorType)
+	{
+		case 0: //greyscale color
+		{
+			if(info.image.bitDepth == 8)
+			{
+				foreach(i, pixel; source[0 .. w * h])
+					res[colors * i + 0] = res[colors * i + 1] = res[colors * i + 2] = pixel;
+				if (info.colorKey && alpha)
+					foreach(i, pixel; source[0 .. w * h])
+						res[colors * i + 3] = (pixel == info.keyR) ? 0 : 255;
+			}
+			else if (info.image.bitDepth == 8)
+			{
+				for(size_t i = 0; i < w * h; i++)
+					res[colors * i + 0] = res[colors * i + 1] = res[colors * i + 2] = source[i * 2];
+				if (info.colorKey && alpha)
+					for(size_t i = 0; i < w * h; i++)
+						res[colors * i + 3] = (256U * source[i] + source[i + 1] == info.keyR) ? 0 : 255;
+			}
+			else
+			{
+				if (!info.colorKey)
+				{
+					for(size_t i = 0; i < w * h; i++)
+					{
+						size_t bp = info.image.bitDepth * i;
+						uint value = 0;
+						uint pot = 1 << info.image.bitDepth; //power of two
+						for(size_t j = 0; j < info.image.bitDepth; j++)
+						{
+							pot /= 2;
+							uint _bit = readBitFromStreamReversed(bp, source);
+							value += pot * _bit;
+						}
+						//scale value from 0 to 255
+						value = (value * 255) / ((1 << info.image.bitDepth) - 1);
+						res[colors * i + 0] = res[colors * i + 1] = res[colors * i + 2] = cast(ubyte)(value);
+					}
+				}
+				else if (alpha)
+				{
+					for(size_t i = 0; i < w * h; i++)
+					{
+						size_t bp = info.image.bitDepth * i;
+						uint value = 0;
+						uint pot = 1 << info.image.bitDepth; //power of two
+						for(size_t j = 0; j < info.image.bitDepth; j++)
+						{
+							pot /= 2;
+							uint _bit = readBitFromStreamReversed(bp, source);
+							value += pot * _bit;
+						}
+						res[colors * i + 3] = ( value && ((1U << info.image.bitDepth) - 1U) ==
+							info.keyR && ((1U << info.image.bitDepth) - 1U)) ? 0 : 255;
+						value = (value * 255) / ((1 << info.image.bitDepth) - 1);
+						res[colors * i + 0] = res[colors * i + 1] = res[colors * i + 2] = cast(ubyte)(value);
+					}
+				}
+			}
+		}
+		break;
+		case 2: //RGB color
+		{
+			if(info.image.bitDepth == 8)
+			{
+				for(size_t i = 0; i < w * h; i++)
+					for(size_t c = 0; c < 3; c++)
+						res[colors * i + c] =
+								source[3 * i + c];
+				if (info.colorKey && alpha)
+					for(size_t i = 0; i < w * h; i++)
+						res[colors * i + 3] = (source[3 * i + 0] == info.keyR && source[3 * i + 1] ==
+							info.keyG && source[3 * i + 2] == info.keyB) ? 0 : 255;
+			}
+			else // 16 bit
+			{
+				for(size_t i = 0; i < w * h; i++)
+					for(size_t c = 0; c < 3; c++)
+						res[colors * i + c] = source[6 * i + 2 * c];
+				if (info.colorKey && alpha)
+					for(size_t i = 0; i < w * h; i++)
+						res[colors * i + 3] = (256U * source[6 * i + 0] + source[6 * i + 1] ==
+							 info.keyR && 256U * source[6 * i + 2] + source[6 * i + 3] ==
+							 info.keyG && 256U * source[6 * i + 4] + source[6 * i + 5] ==
+							 info.keyB) ? 0 : 255;
+			}
+			break;
+		}
+		case 3: //indexed color (palette)
+		{
+			if(info.image.bitDepth == 8)
+				for(size_t i = 0; i < w * h; i++)
+					res[colors * i..colors * i + colors] = info.palette[source[i]][0..colors];
+			else if(info.image.bitDepth < 8)
+			{
+				for(size_t i = 0; i < w * h; i++)
+				{
+					size_t bp = info.image.bitDepth * i;
+					uint value = 0;
+					uint pot = 1 << info.image.bitDepth; //power of two
+					for(size_t j = 0; j < info.image.bitDepth; j++)
+					{
+						pot /= 2;
+						uint _bit = readBitFromStreamReversed(bp, source);
+						value += pot * _bit;
+					}
+					res[colors * i.. colors * i + colors] = info.palette[value][0..colors];
+				}
+			}
+			break;
+		}
+		case 4: //greyscale with alpha
+		{
+			if(info.image.bitDepth == 8)
+			{
+				for(size_t i = 0; i < w * h; i++)
+				{
+					res[colors * i + 0] = res[colors * i + 1] = res[colors * i + 2] = source[i * 2 + 0];
+					if (alpha)
+						res[colors * i + 3] = source[i * 2 + 1];
+				}
+			}
+			else if(info.image.bitDepth == 16)
+			{
+				for(size_t i = 0; i < w * h; i++)
+				{
+					//most significant byte
+					res[colors * i + 0] = res[colors * i + 1] = res[colors * i + 2] = source[4 * i];
+					if (alpha)
+						res[colors * i + 3] = source[4 * i + 2];
+				}
+			}
+			break;
+		}
+		case 6: //RGB with alpha
+		{
+			if (alpha && info.image.bitDepth == 16)
+			{
+				for(size_t i = 0; i < w * h; i++)
+					res[colors * i .. colors * i + colors] =
+						[ source[8 * i], source[8 * i + 2], source[8 * i + 4], source[8 * i + 6] ];
+			}
+			else if (info.image.bitDepth == 16)
+			{
+				for(size_t i = 0; i < w * h; i++)
+					res[colors * i .. colors * i + 3] =
+						[ source[8 * i], source[8 * i + 2], source[8 * i + 4] ];
+			}
+			else // must be RGB
+				for(size_t i = 0; i < w * h; i++)
+					res[colors * i .. colors * i + 3] = source[4 * i .. 4 * i + 3];
+			break;
+		}
+		default:
+			mixin(pngEnforce("false", "invalid conversion"));
+			break;
+
+	}
+	return res;
+}
+
+//package const char[] constructor = "static typeof(*this) create() { typeof(*this) instance; return instance; }";
+debug package char[] chunkTypeToString(uint chunkType)
+{
+	char[] result = new char[4];
+	result[0] = chunkType >> 24 ;
+	result[1] = chunkType >> 16;
+	result[2] = chunkType >> 8;
+	result[3] = chunkType;
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/lodepng/decode.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,649 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/***************************************************************************************************
+License:
+Copyright (c) 2005-2007 Lode Vandevenne
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+  - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.<br>
+  - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.<br>
+  - Neither the name of Lode Vandevenne nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission.<br>
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Authors: Lode Vandevenne (original version in C++), Lutger Blijdestijn (D version) : lutger dot blijdestijn at gmail dot com.
+
+About:
+The decoder is small but sufficient for most purposes. It is compliant to the png specification and
+has been tested with the png suite. The api is procedural and simple, meant to be easily integrated. It is compatible with the
+Phobos and Tango libraries. For Tango, lodepng expects the zlib binding from phobos in etc.c.zlib.d<br>
+This module publicly imports lodepng.Common, where you'll find the data types used by both the encoder
+and decoder, as well as some utility and image format conversion routines.
+
+Date: August 7, 2007
+
+Features:
+The following features are supported by the decoder:
+<ul>
+    <li> conformant decoding of PNGs (all color types, bit depth, interlace mode, CRC checking, etc.)</li>
+    <li> support for translucent PNG's, including translucent palettes and color key</li>
+    <li> textual key-value meta-data</li>
+    <li> the following chunks are interpreted by the decoder
+        <ul>
+            <li>IHDR (image information)</li>
+            <li>PLTE (color palette)</li>
+            <li>IDAT (pixel data)</li>
+            <li>IEND (the final chunk)</li>
+            <li>tRNS (transparency for palettized images)</li>
+            <li>bKGD (suggested background color)</li>
+            <li>tEXt (uncompressed latin-1 key-value strings)</li>
+            <li>zTXt (compressed latin-1 key-value strings)</li>
+            <li>iTXt (utf8 key-value strings)</li>
+        </ul>
+   </li>
+</UL>
+
+Limitations:
+The following features are not supported.
+<ul>
+    <li> editing a PNG image (unless you use the decoder, then edit it, then use the
+        encoder, but ignored chunks will then be gone from the original image)</li>
+    <li> Streaming / progressive display. All data must be available and is processed in one call.</li>
+    <li> The following optional chunk types are not interpreted by the decoder
+        <ul>
+            <li>cHRM (device independent color info)
+            <li>gAMA (device independent color info)
+            <li>iCCP (device independent color info)
+            <li>sBIT (original number of significant bits)
+            <li>sRGB (device independent color info)
+            <li>pHYs (physical pixel dimensions)
+            <li>sPLT (suggested reduced palette)
+            <li>tIME (last image modification time)
+        </ul>
+    </li>
+</ul>
+
+Examples:
+Here is an example how you could use LodePNG with opengl, see the api documentation for details.
+---
+uint loadPNG(char[] filename)
+{
+    uint textureID;
+
+    glEnable(GL_TEXTURE_2D);
+    glGenTextures(1, &textureID);
+    glBindTexture(GL_TEXTURE_2D, textureID);
+    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+
+    PngInfo info;
+    ubyte[] image = decode32(cast(ubyte[])std.file.read(filename), info);
+
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, info.image.width, info.image.height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+                        image.ptr);
+    return textureID;
+}
+---
+
+References:
+$(LINK2 http://members.gamedev.net/lode/projects/LodePNG/, Original lodepng) <br>
+$(LINK2 http://www.w3.org/TR/PNG/, PNG Specification) <br>
+$(LINK2 http://www.libpng.org/pub/png/pngsuite.html, PNG Suite: set of test images) <br>
+$(LINK2 http://optipng.sourceforge.net/, OptiPNG: tool to experimentally optimize png images)
+*/
+
+module dynamin.lodepng.decode;
+import dynamin.lodepng.zlib_codec;
+import dynamin.lodepng.util;
+import std.intrinsic;
+
+public import dynamin.lodepng.common;
+
+/***************************************************************************************************
+    Parse png image header from memory. The first 33 bytes of the png file need to be available
+
+        throws: PngException
+
+***************************************************************************************************/
+PngImage readHeader(in ubyte[] source)
+{
+    ubyte interlace;
+    return readHeader(source, interlace);
+}
+
+/// ditto
+PngImage readHeader(in ubyte[] source, out ubyte interlace)
+in
+{
+    assert(source.length >= HEADER_SIZE, "array is too small to contain png header");
+}
+body // see spec: http://www.w3.org/TR/PNG/#11IHDR
+{
+    mixin(pngEnforce(`source.length >= HEADER_SIZE`, "png header data is too small"));
+    mixin(pngEnforce(`source[0..8] == [cast(ubyte)137, 80, 78, 71, 13, 10, 26, 10]`, "invalid png header "));
+    mixin(pngEnforce((toUint(source[12..16]) == IHDR).stringof, "invalid png header"));
+    mixin(pngEnforce(`checkCRC(source[29 .. 33], source[12 .. 29])`, "invalid CRC"));
+
+    PngImage result;
+    ubyte interlaceMethod;
+
+    with (result)
+    {
+        width = toUint(source[16..20]);
+        height = toUint(source[20..24]);
+        bitDepth = source[24];
+        colorType = cast(ColorType)source[25];
+        mixin(pngEnforce( `source[26] == 0`, "unsupported compression method in png header" ));
+        mixin(pngEnforce( `source[27] == 0`, "unsupported filter method in png header" ));
+        mixin(pngEnforce( `checkColorValidity(colorType, bitDepth)`, "invalid header: wrong color format" ));
+        interlaceMethod = source[28];
+        mixin(pngEnforce( `interlaceMethod < 2`, "invalid  interlace method in png header" ));
+        bpp = numChannels(colorType) * bitDepth;
+    }
+    interlace = interlaceMethod;
+    return result;
+}
+
+/***************************************************************************************************
+    Decode source png file
+
+        If a buffer is provided, it may be used to store the result. See bufferSize for details.
+
+        Throws: PngException
+        Returns: Decoded image pixels. The color format of the resulting image is the
+        same as the source image, see lodepng.Common.convert and decode32 if a specific color format
+        is desired.
+
+***************************************************************************************************/
+ubyte[] decode(in ubyte[] source, ref PngInfo info, ubyte[] buffer = null)
+{
+    info.image = readHeader(source, info.interlace);
+
+    // holds interlaced filtered scanlines
+    ubyte[] ilaceBuffer;
+
+	// to allocate memory as needed
+    if (info.interlace == 1)
+        buffer.length = ((info.image.width * info.image.bpp + 7) / 8) * info.image.height + (info.image.height * 2); // guess
+    else
+        buffer.length = ((info.image.width * info.image.bpp + 7) / 8) * info.image.height + info.image.height;
+
+
+    if (info.interlace == 1)
+        ilaceBuffer.length = buffer.length - info.image.height;
+
+    auto inflator = DecodeStream.create(buffer);
+
+	foreach(chunk; StreamChunkIter(source[HEADER_SIZE - 1 .. $]))
+	{
+		switch(chunk.type)
+		{
+			case IDAT:
+				inflator(chunk.data);
+				break;
+			case PLTE:
+				mixin(pngEnforce(`chunk.data.length <= 256 * 3`, "palette size is too large"));
+				info.palette.length = chunk.data.length / 3;
+				foreach(index, ref ubyte[4] color; info.palette)
+				{
+				    color[0..3] = chunk.data[index * 3 .. index * 3 + 3];
+				    color[3] = 255;
+				}
+
+				break;
+            case tRNS:
+                if (chunk.data.length == 0)
+                    break;
+                info.colorKey = true;
+                if (info.image.colorType == ColorType.Palette) // index-values
+            	{
+            	    mixin(pngEnforce(`chunk.data.length <= info.palette.length`, "palette size is too large"));
+            	    foreach(index, alpha; chunk.data)
+            	        info.palette[index][3] = alpha;
+            	}
+            	else if (info.image.colorType == ColorType.RGB)
+            	{
+            	    info.keyR = 256U * chunk.data[0] + chunk.data[1];
+            	    info.keyG = 256U * chunk.data[2] + chunk.data[3];
+            	    info.keyB = 256U * chunk.data[4] + chunk.data[5];
+            	}
+            	else if (info.image.colorType == ColorType.Greyscale)
+            	{
+            	    info.keyR = 256U * chunk.data[0] + chunk.data[1];
+            	}
+            	else
+                    assert(false);
+                break;
+            case bKGD:
+            	if (info.image.colorType == ColorType.Palette || info.image.bitDepth == 16)
+            	    info.backgroundColor = chunk.data.dup;
+                else
+                {
+            	    info.backgroundColor.length = chunk.data.length / 2;
+                        foreach(index, ref value; info.backgroundColor)
+                            value = chunk.data[index * 2];
+            	}
+            	break;
+            case zTXt:
+                if (info.parseText)
+                {
+                    if (info.textual is null)
+                        info.textual = new PngText;
+                    auto sep = strFind(cast(char[])chunk.data, 0);
+                    if (sep > 0)
+                    {
+                        if (chunk.data[sep + 1] == 0)
+                            info.textual[chunk.data[0..sep]] = chunk.data[sep + 2 .. $];
+                        else
+                        {
+                            ubyte[] value;
+                            auto decoder = DecodeStream.create(value);
+                            decoder(chunk.data[sep + 2 .. $]);
+                            info.textual[chunk.data[0..sep]] = value;
+                        }
+                    }
+                }
+                break;
+            case tEXt:
+                if (info.parseText)
+                {
+                    if (info.textual is null)
+                        info.textual = new PngText;
+                    auto sep = strFind(cast(char[])chunk.data, 0);
+                    if (sep > 0)
+                    {
+                        info.textual[chunk.data[0..sep]] = chunk.data[sep + 1 .. $];
+                    }
+                }
+                break;
+            case iTXt:
+            	if (info.parseText)
+                {
+                    if (info.textual is null)
+                        info.textual = new PngText;
+                    auto sep = strFind(cast(char[])chunk.data, 0);
+                    char[] keyword = cast(char[])chunk.data[0..sep];
+                    bool compressed = chunk.data[sep + 1] == 0 ? false : true;
+                    sep += strFind(cast(char[])chunk.data[sep + 3 .. $], 0) + 3;
+                    sep += strFind(cast(char[])chunk.data[sep + 1 .. $], 0) + 1;
+                    if (!compressed)
+                        info.textual[keyword] = cast(char[])chunk.data[sep + 1..$];
+                    else
+                    {
+                        ubyte[] value;
+                        auto decoder = DecodeStream.create(value);
+                        decoder(chunk.data[sep + 1..$]);
+                        info.textual[keyword] = cast(char[])value;
+                    }
+                }
+            	break;
+			default:
+                mixin(pngEnforce(`(bt(&chunk.type, 6) < 0)`, "unrecognized critical chunk"));
+				break;
+		}
+	}
+	assert(inflator.hasEnded);
+	buffer = inflator();
+
+    return (info.interlace == 0) ? reconstruct(buffer, info.image)
+                                 : deinterlace(buffer, ilaceBuffer, info.image);
+}
+
+/***************************************************************************************************
+    Decode source png file to RGBA format
+
+        Throws: PngException
+        Returns: decoded image pixels in 32-bit RGBA format
+***************************************************************************************************/
+ubyte[] decode32(in ubyte[] source, ref PngInfo info, ubyte[] buffer = null)
+{
+    buffer = decode(source, info, buffer);
+    buffer = convert(buffer, info, ColorType.RGBA);
+    info.image.colorType = ColorType.RGBA;
+    info.image.bpp = 32;
+    info.image.bitDepth = 8;
+    info.palette.length = 0;
+    return buffer;
+}
+
+
+
+
+/***************************************************************************************************
+    Predict size of buffer needed for decoding
+
+        Estimate of the amount of heap memory needed to decode an image. Interlaced images, images
+        with a color format of less than 8 bits per pixel and the parsing of certain information
+        such as text will allocate more heap memory.
+***************************************************************************************************/
+uint bufferSize( /+const+/ ref PngImage image)
+{
+    return ((image.width * image.bpp + 7) / 8) * image.height + image.height;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//                                                                                                //
+//                          PRIVATE SECTION                                                       //
+//                                                                                                //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+private:
+
+const uint HEADER_SIZE = 34;
+
+struct StreamChunkIter
+{
+	int opApply(int delegate(ref Chunk chunk) visitor)
+	{
+		int result = 0;
+		uint pos = 0;
+		Chunk chunk;
+		while(pos + 12 <= stream.length)
+		{
+			chunk = Chunk.fromStream(stream[pos..$]);
+			if (chunk.type == IEND)
+				break;
+			result = visitor(chunk);
+			if (result)
+				return result;
+			pos += chunk.length;
+		}
+		return result;
+	}
+	ubyte[] stream;
+}
+
+//filter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works
+//byte per byte (bytewidth = 1)precon is the previous filtered scanline, recon the result, scanline
+//the current one
+void unFilterFirstScanline(ubyte[] result, ubyte[] scanline, uint bytewidth, uint filterType)
+in
+{
+    assert(filterType >= 0 && filterType <= 4);
+}
+body
+{
+    mixin(pngEnforce(`filterType >= 0 && filterType <= 4`, "wrong filter byte: image corrupt?"));
+    switch(filterType)
+	{
+		case 0:
+			for(uint i = 0; i < scanline.length; i++)
+                result[i] = scanline[i];
+			break;
+		case 1:
+            for(uint i = 0; i < bytewidth; i++)
+                result[i] = scanline[i];
+            for(uint i = bytewidth; i < scanline.length; i++)
+                result[i] = scanline[i] + result[i - bytewidth];
+        break;
+		case 2:
+			for(size_t i = 0; i < scanline.length; i++)
+                result[i] = scanline[i];
+			break;
+		case 3:
+            for(size_t i = 0; i < bytewidth; i++)
+                result[i] = scanline[i];
+			for(size_t i = bytewidth; i < scanline.length; i++)
+                result[i] = (scanline[i] + result[i - bytewidth]) / 2;
+			break;
+		case 4:
+            for(size_t i = 0; i < bytewidth; i++)
+                result[i] = scanline[i];
+            for(size_t i = bytewidth; i < scanline.length; i++)
+                result[i] = cast(ubyte)(scanline[i] + paethPredictor(result[i - bytewidth], 0, 0));
+            break;
+		default:
+            mixin(pngEnforce("false", "wrong type of filter"));
+            break;
+
+	}
+}
+
+//filter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works
+//byte per byte (bytewidth = 1) precon is the previous filtered scanline, recon the result,
+//scanline the current one
+void unFilterScanline(ubyte[] result, ubyte[] scanline, ubyte[] previous, uint bytewidth,
+                     uint filterType)
+in
+{
+    assert(filterType >= 0 && filterType <= 4);
+}
+body
+{
+	switch(filterType)
+	{
+		case 4:
+			for(size_t i = 0; i < bytewidth; i++)
+                result[i] = cast(ubyte)(scanline[i] + previous[i] );
+			for(size_t i = bytewidth; i < scanline.length; i++)
+                result[i] = cast(ubyte)(scanline[i] +
+                            paethPredictor(result[i - bytewidth],
+                                           previous[i],
+                                           previous[i - bytewidth]));
+            break;
+		case 3:
+			for(size_t i = 0; i < bytewidth; i++)
+                result[i] = scanline[i] + previous[i] / 2;
+			for(size_t i = bytewidth; i < scanline.length; i++)
+                result[i] = scanline[i] + ((result[i - bytewidth] + previous[i]) / 2);
+			break;
+		case 2:
+            for(size_t i = 0; i < scanline.length; i++)
+                result[i] = scanline[i] + previous[i];
+			break;
+		case 1:
+            for(size_t i = 0; i < bytewidth; i++)
+                result[i] = scanline[i];
+            for(size_t i = bytewidth; i < scanline.length; i++)
+                result[i] = scanline[i] + result[i - bytewidth];
+        break;
+        case 0:
+            for(uint i = 0; i < scanline.length; i++) result[i] = scanline[i];
+            break;
+		default:
+            mixin(pngEnforce("false", "wrong type of filter"));
+            break;
+	}
+}
+
+ubyte[] deinterlace(in ubyte[] scanlines, ref ubyte[] result, ref PngImage image)
+{
+    const x = 0;
+    const y = 1;
+    uint bytewidth = (image.bpp + 7) / 8; // bytewidth is used for filtering
+    ubyte[] source;
+    ubyte[] scanlineo = new ubyte[image.width * bytewidth]; //"old" scanline
+    ubyte[] scanlinen = new ubyte[image.width * bytewidth]; //"new" scanline
+
+    uint[7][2] passDim;
+    passDim[x][0] = (image.width + 7) / 8; passDim[y][0] = (image.height + 7) / 8;
+    passDim[x][1] = (image.width + 3) / 8; passDim[y][1] = (image.height + 7) / 8;
+    passDim[x][2] = (image.width + 3) / 4; passDim[y][2] = (image.height + 3) / 8;
+    passDim[x][3] = (image.width + 1) / 4; passDim[y][3] = (image.height + 3) / 4;
+    passDim[x][4] = (image.width + 1) / 2; passDim[y][4] = (image.height + 1) / 4;
+    passDim[x][5] = (image.width + 0) / 2; passDim[y][5] = (image.height + 1) / 2;
+    passDim[x][6] = (image.width + 0) / 1; passDim[y][6] = (image.height + 0) / 2;
+
+    // locate start in source of each pass, note:
+    // (HACK) pass can be empty, sets both x and y axis to zero to recognize this in processPass
+
+    size_t[7] passstart;
+    passstart[0] = 0;
+    for(int i = 0; i < 6; i++)
+    {
+        if (passDim[y][i] * passDim[x][i] == 0)
+            passDim[y][i] = passDim[x][i] = 0;
+
+        passstart[i + 1] = passstart[i] + (passDim[y][i] * (1 + (passDim[x][i] * image.bpp + 7) / 8));
+    }
+
+    void adam7Pass( size_t passleft, size_t passtop, size_t spacex, size_t spacey,
+                    size_t passw, size_t passh)
+    {
+        size_t linelength = 1 + ((image.bpp * passw + 7) / 8);
+        size_t linestart = 0;
+        uint filterType = source[0];
+
+        void placePixels(uint s)
+        {
+            if(image.bpp >= 8)
+                for(size_t i = 0; i < passw; i++)
+                    for(size_t b = 0; b < bytewidth; b++) //b = current byte of this pixel
+                        result[bytewidth * image.width * (passtop + spacey * s) + bytewidth *
+                               (passleft + spacex * i) + b] = scanlinen[bytewidth * i + b];
+            else
+            {
+                for(size_t i = 0; i < passw; i++)
+                {
+                    size_t outbitp = image.bpp * image.width * (passtop + spacey * s) +
+                                     image.bpp * (passleft + spacex * i);
+                    for(size_t b = 0; b < image.bpp; b++) //b = current bit of this pixel
+                    {
+                        size_t obp = outbitp + b;
+                        //where bitpos 0 refers to the LSB, bitpot 7 to the MSB of a byte
+                        size_t obitpos = 7 - (obp & 0x7);
+                        size_t bp = i * image.bpp + b;
+                        //where bitpos 0 refers to the LSB, bitpot 7 to the MSB of a byte
+                        size_t bitpos = 7 - (bp & 0x7);
+                        uint _bit = (scanlinen[bp / 8] >> bitpos) & 1;
+                        result[obp / 8] = cast(ubyte)((result[obp / 8] & ~(1 << obitpos)) |
+                                            (_bit << obitpos));
+                    }
+                }
+            }
+        }
+
+        void swapScanlines()
+        {
+            ubyte[] temp = scanlinen;
+            scanlinen = scanlineo;
+            scanlineo = temp;
+        }
+
+        unFilterFirstScanline(scanlinen, source[linestart + 1 .. linestart + linelength],
+                                      bytewidth, filterType);
+        placePixels(0);
+        swapScanlines();
+
+        for(uint s = 1; s < passh; s++)
+        {
+            linestart = s * linelength;
+            filterType = source[linestart];
+
+            unFilterScanline(scanlinen, source[linestart + 1 .. linestart + linelength],
+                             scanlineo, bytewidth, filterType);
+            placePixels(s);
+            swapScanlines();
+        }
+    }
+
+    void processPass(size_t pass, size_t left, size_t top, size_t sx, size_t sy)
+    {
+        // check if pass is empty
+        if (pass > 0 && (passDim[x][pass] * passDim[y][pass] == 0))
+            return;
+        source = scanlines[passstart[pass]..$];
+        adam7Pass( left, top, sx, sy, passDim[x][pass], passDim[y][pass]);
+    }
+
+
+    processPass(0, 0, 0, 8, 8);
+    processPass(1, 4, 0, 8, 8);
+    processPass(2, 0, 4, 4, 8);
+    processPass(3, 2, 0, 4, 4);
+    processPass(4, 0, 2, 2, 4);
+    processPass(5, 1, 0, 2, 2);
+    processPass(6, 0, 1, 1, 2);
+
+    return result;
+}
+
+ubyte[] reconstruct(ref ubyte[] buffer, /+const+/ ref PngImage image)
+{
+    //filter and interlace
+	uint bytewidth = (image.bpp + 7) / 8; // bytewidth is used for filtering
+	uint outlength = image.height * image.width * bytewidth;
+
+
+    size_t linestart = 0; //start of current scanline
+
+    // bits are tightly packed, but scanlines are always padded to 1 byte bounderies:
+    uint scanlength = ((image.width * image.bpp) + 7) / 8;
+
+    mixin(pngEnforce( `buffer.length >= (scanlength + 1) * image.height && buffer.length > 0`, "invalid size of source data"));
+
+    uint filterType = buffer[linestart];
+
+
+    if(image.bpp >= 8) //byte per byte
+    {
+        filterType = buffer[linestart];
+        unFilterFirstScanline(buffer, buffer[1..1 + scanlength], bytewidth, filterType);
+
+        //go to start of next scanline
+        linestart += 1 + scanlength;
+
+        for(size_t s = 1; s < image.height; ++s)
+        {
+            filterType = buffer[linestart];
+            unFilterScanline(buffer[linestart - s..$],
+                    buffer[linestart + 1 .. linestart + 1 + scanlength],
+                    buffer[(s - 1) * image.width * bytewidth..$],
+                    bytewidth, filterType);
+
+
+            //go to start of next scanline
+            linestart += 1 + scanlength;
+        }
+    }
+    else //less than 8 bits per pixel, so fill it up bit per bit
+    {
+        size_t obp = 0; //out bit pointer
+
+        ubyte[] templine = new ubyte[scanlength];
+        filterType = buffer[linestart];
+        unFilterFirstScanline(templine, buffer[1 .. 1 + scanlength], bytewidth, filterType);
+
+        linestart += 1 + scanlength;
+
+        //bp is here bit pointer in templine
+        for(size_t bp = 0; bp < image.width * image.bpp; bp++)
+        {
+            size_t obitpos = 7 - (obp & 0x7);
+            size_t bitpos = 7 - (bp & 0x7);
+            uint _bit = (templine[bp / 8] >> bitpos) & 1;
+            buffer[obp / 8] = cast(ubyte)((buffer[obp / 8] & ~(1 << obitpos)) |
+                                            (_bit << obitpos)); //set current bit
+            obp++;
+        }
+
+        for(size_t s = 1; s < image.height; ++s)
+        {
+            filterType = buffer[linestart];
+            unFilterScanline(   templine,
+                                buffer[linestart + 1 .. linestart + 1 + scanlength],
+                                buffer[(s - 1) * scanlength..$],
+                                bytewidth,
+                                filterType);
+
+            //bp is here bit pointer in templine
+            for(size_t bp = 0; bp < image.width * image.bpp; bp++)
+            {
+                size_t obitpos = 7 - (obp & 0x7);
+                size_t bitpos = 7 - (bp & 0x7);
+                uint _bit = (templine[bp / 8] >> bitpos) & 1;
+                buffer[obp / 8] = cast(ubyte)((buffer[obp / 8] & ~(1 << obitpos)) |
+                                                (_bit << obitpos)); //set current bit
+                obp++;
+            }
+
+            //go to start of next scanline
+            linestart += 1 + ((image.width * image.bpp + 7) / 8);
+        }
+    }
+	return buffer;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/lodepng/encode.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,672 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/***************************************************************************************************
+License:
+Copyright (c) 2005-2007 Lode Vandevenne
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+  - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.<br>
+  - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.<br>
+  - Neither the name of Lode Vandevenne nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission.<br>
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Authors: Lode Vandevenne (original version in C++), Lutger Blijdestijn (D version) : lutger dot blijdestijn at gmail dot com,
+  Stewart Gordon (bug fixes)
+
+Date: August 7, 2007
+
+About:
+The lodepng encoder can encode images of any of the supported png color formats to 24-bit RGB or
+32-bit RGBA png images. Conversion, if needed, is done automatically. It is compatible with the
+Phobos and Tango libraries. For Tango, lodepng expects the zlib binding from phobos in etc.c.zlib.d<br>
+This module publicly imports lodepng.Common, where you'll find the data types used by both the encoder
+and decoder, as well as some utility and image format conversion routines.<br>
+
+Features:
+The following features are supported by the encoder:
+<ul>
+	<li> conformant encoding of 24-bit RGB and 32-bit RGBA PNG images </li>
+	<li> automatic conversion of other color formats </li>
+	<li> setting the compression and filter methods </li>
+	<li> textual key-value metadata: normal and compressed latin1, unicode (utf-8) </li>
+	<li> transparency / colorkey </li>
+	<li> the following chunks are written by the encoder
+		<ul>
+			<li>IHDR (image information)</li>
+			<li>IDAT (pixel data)</li>
+			<li>IEND (the final chunk)</li>
+			<li>tRNS (colorkey)</li>
+			<li>bKGD (suggested background color) </li>
+			<li>tEXt (uncompressed latin-1 key-value strings)</li>
+			<li>zTXt (compressed latin-1 key-value strings)</li>
+			<li>iTXt (utf8 key-value strings)</li>
+		</ul>
+   </li>
+</UL>
+
+<b>Limitations:</b><br>
+The following features are not supported.
+<ul>
+	<li> Ouput in any color formats other than 24-bit RGB or 32-bit RGBA</li>
+	<li> Interlacing </li>
+	<li> The following chunk types are not written by the encoder
+		<ul>
+			<li>PLTE (color palette)</li>
+			<li>cHRM (device independent color info) </li>
+			<li>gAMA (device independent color info) </li>
+			<li>iCCP (device independent color info) </li>
+			<li>sBIT (original number of significant bits) </li>
+			<li>sRGB (device independent color info) </li>
+			<li>pHYs (physical pixel dimensions) </li>
+			<li>sPLT (suggested reduced palette) </li>
+			<li>tIME (last image modification time) </li>
+		</ul>
+	</li>
+</ul>
+
+References:
+$(LINK2 http://members.gamedev.net/lode/projects/LodePNG/, Original lodepng) <br>
+$(LINK2 http://www.w3.org/TR/PNG/, PNG Specification) <br>
+$(LINK2 http://www.libpng.org/pub/png/pngsuite.html, PNG Suite: set of test images) <br>
+$(LINK2 http://optipng.sourceforge.net/, OptiPNG: tool to experimentally optimize png images)
+***************************************************************************************************/
+module dynamin.lodepng.encode;
+
+version (Tango)
+{
+	import tango.math.Math;
+}
+else
+	import std.math;
+import dynamin.lodepng.zlib_codec;
+public import dynamin.lodepng.common;
+
+/***************************************************************************************************
+	Returns a png image of the raw pixels provided by source and described by original
+
+		This function will attempt to convert to 24-bit RGB if it is a lossless operation, otherwise
+		the resulting png image will be in the 32-bit RGBA format. The array returned can be written to disk
+		as a png file.
+
+		throws: PngException
+***************************************************************************************************/
+ubyte[] encode(in ubyte[] source, in PngInfo original)
+{
+	ubyte[] buf;
+	return _encode(source, original, EncodeOptions(), buf);
+}
+
+/**************************************************************************************************
+	Returns a png image of the raw pixels provided by source and described by original.
+
+		See also: EncodeOptions
+
+		throws: PngException
+***************************************************************************************************/
+ubyte[] encode(in ubyte[] source, in PngInfo original, in EncodeOptions options)
+{
+	ubyte[] buf;
+	return _encode(source, original, options, buf);
+}
+
+/***************************************************************************************************
+	Combinations of filter and compression trade-offs
+
+		The png compression scheme works by applying lossless filters on the image data and then
+		compressing with the deflate (zlib) algorithm. The filters condition the data for better
+		compression. This enumeration can be used with EncodeOptions, see FilterStrategy and
+		CompressionStrategy for more control over how the image is encoded.
+
+***************************************************************************************************/
+enum EncodingStrategy
+{
+	/// source data is stored as is
+	Store,
+
+	/** this gives zero compression but can be useful for storage in a zlib compressed file, in
+	which case the filtering conditions the data for better compression **/
+	FilterOnly,
+
+	/// aim for reasonable compression but with emphasis on speed
+	Fast,
+
+	/// aim for good compression / speed tradeoff
+	Normal,
+
+	/// aim for best compression
+	Best
+}
+
+/***************************************************************************************************
+	Filter method
+
+		The png specification defines five types of filters. In addition each scanline can have a
+		different filtering method applied (Dynamic). The latter method gives the best compression,
+		when a fixed method is preffered usually paeth works best.
+
+***************************************************************************************************/
+enum FilterStrategy : ubyte
+{
+	None = 0, ///
+	Up = 1, ///
+	Sub = 2, ///
+	Average = 3, ///
+	Paeth = 4, ///
+	Dynamic, ///
+}
+
+/***************************************************************************************************
+	Zlib compression method
+
+		Which compression scheme works best depends on the type of image. Tools such as optipng can
+		figure this out experimentally.
+
+***************************************************************************************************/
+enum CompressionStrategy : ubyte
+{
+
+	Default = 0, ///
+	Filtered = 1, ///
+	RLE = 3, ///
+	None = ubyte.max ///
+}
+
+/// Controls how a png image is encoded and what auxiliary data is to be written
+struct EncodeOptions
+{
+	/// constructors
+	static EncodeOptions opCall()
+	{
+		EncodeOptions result;
+		return result;
+	}
+
+	/// ditto
+	static EncodeOptions opCall(ColorType colorType,
+								EncodingStrategy strategy = EncodingStrategy.Normal,
+								bool autoRemoveAlpha = true)
+	{
+		EncodeOptions result;
+		result.setStrategy(strategy);
+
+		with (result)
+		{
+			autoRemoveAlpha = autoRemoveAlpha;
+			targetColorType = colorType;
+		}
+		return result;
+	}
+
+	/// ditto
+	static EncodeOptions opCall(EncodingStrategy strategy,
+								bool autoRemoveAlpha = true)
+	{
+		EncodeOptions result;
+		result.setStrategy(strategy);
+		return result;
+	}
+
+	/// ditto
+	static EncodeOptions opCall(CompressionStrategy compression, FilterStrategy filter = FilterStrategy.Dynamic)
+	{
+		EncodeOptions result;
+		result.compressionLevel = 9;
+		result.compressionStrategy = compression;
+		result.filterStrategy = filter;
+		return result;
+	}
+
+	invariant
+	{
+		assert(compressionLevel >=0 && compressionLevel <= 9, "invalid zlib compression level");
+		assert(targetColorType == ColorType.Any ||
+				targetColorType == ColorType.RGB ||
+				targetColorType == ColorType.RGBA, "colortype is not supported");
+	}
+
+	/***********************************************************************************************
+		The colortype of the target image
+
+			lodepng can only encode in RGB(A) format. If the format is set ColorType.Any, RGB or
+			RGBA is chosen depending on whether the source image has an alpha channel.
+	***********************************************************************************************/
+	ColorType targetColorType = ColorType.Any;
+
+	/***********************************************************************************************
+		Remove alpha channel
+
+			If set to true and the source image has an alpha channel, this will be removed if (and
+			only if) the image is fully opaque or a colorkey can be written. This is considered a
+			lossless operation.
+	***********************************************************************************************/
+	bool autoRemoveAlpha = true;
+
+	PngText text; /// key-value strings to be written, see also: lodepng.Common.PngText
+	bool compressText = false; /// if zlib compression is to be used on text
+
+	ubyte[] backgroundColor; /// suggested background RGB-triplet, must be either 3 or 6 bytes
+
+	bool  colorKey = false; /// colorkey, set to true if it is to be encoded
+	ubyte keyR;     /// red/greyscale component of color key
+	ubyte keyG;     /// green component of color key
+	ubyte keyB;     /// blue component of color key
+
+	ubyte compressionLevel = 6; /// zlib compression level, affects memory use. Must be in range 0-9
+	FilterStrategy filterStrategy = FilterStrategy.Dynamic; /// see FilterStrategy
+	CompressionStrategy compressionStrategy = CompressionStrategy.RLE; /// see CompressionStrategy
+
+	/// This will set compressionLevel, compressionStrategy and filterStrategy
+	void setStrategy(EncodingStrategy strategy)
+	{
+		switch (strategy)
+		{
+			case EncodingStrategy.Store:
+				compressionLevel = 0;
+				compressionStrategy = CompressionStrategy.None;
+				filterStrategy = FilterStrategy.None;
+				break;
+			case EncodingStrategy.FilterOnly:
+				compressionLevel = 0;
+				compressionStrategy = CompressionStrategy.None;
+				filterStrategy = FilterStrategy.Dynamic;
+				break;
+			case EncodingStrategy.Fast:
+				compressionLevel = 6;
+				compressionStrategy = CompressionStrategy.RLE;
+				filterStrategy = FilterStrategy.Paeth;
+				break;
+			case EncodingStrategy.Normal:
+				compressionLevel = 6;
+				compressionStrategy = CompressionStrategy.RLE;
+				filterStrategy = FilterStrategy.Dynamic;
+				break;
+			case EncodingStrategy.Best:
+				compressionLevel = 9;
+				compressionStrategy = CompressionStrategy.Filtered;
+				filterStrategy = FilterStrategy.Dynamic;
+				break;
+/+ TODO: decide to implement or leave it out
+			case EncodingStrategy.ByteCrunch:
+				compressionLevel = 9;
+				compressionStrategy = CompressionStrategy.Filtered;
+				filterStrategy = FilterStrategy.Dynamic;
+				break;
++/
+			default:
+				assert(false, "I don't know about this strategy");
+				break;
+		}
+	}
+}
+
+private
+{
+	ubyte[] _encode( in ubyte[] source, in PngInfo original, in EncodeOptions options, ref ubyte[] buffer)
+	{
+		// TODO: be more sparing with memory here, can at least avoid one array copy
+
+		Chunk[] ChunkList;
+
+		// find out what colortype of target should be and whether colorkey (tRNS) should be made
+		ColorType destColor = (options.targetColorType == ColorType.RGB ||
+							   options.targetColorType == ColorType.RGBA) ? options.targetColorType :
+							   original.image.colorType;
+		if (!(destColor == ColorType.RGB || destColor == ColorType.RGBA))
+			destColor = (hasAlphaChannel(original.image.colorType)) ? ColorType.RGBA : ColorType.RGB;
+		if (options.autoRemoveAlpha && destColor == ColorType.RGBA && !options.colorKey)
+		{
+			ubyte[] colorKey;
+			if (opaqueOrColorkey(source, original, colorKey))
+			{
+				if (colorKey.length)
+					ChunkList ~= Chunk(tRNS, colorKey);
+				destColor = ColorType.RGB;
+			}
+		}
+
+		// properties of image to be written
+		auto image = PngImage(original.image.width, original.image.height, 8, destColor);
+
+		// convert original if necessary
+		ubyte[] pixels;
+		if (original.image.colorType != destColor || original.image.bitDepth != 8)
+			pixels = convert(source, original, destColor);
+		else
+			pixels = source;
+
+		ChunkList ~= Chunk(IHDR, headerData(image));
+		ChunkList ~= Chunk(IDAT,
+			Encoder.create(options.compressionStrategy, options.compressionLevel)(filter(pixels, image, options.filterStrategy)));
+		if(options.colorKey)
+			ChunkList ~= Chunk(tRNS, [0, cast(ubyte)options.keyR, 0, cast(ubyte)options.keyG, 0, cast(ubyte)options.keyB]);
+		if(options.backgroundColor.length == 3)
+			ChunkList ~= Chunk( bKGD, [
+								0, options.backgroundColor[0],
+								0, options.backgroundColor[1],
+								0, options.backgroundColor[2] ]);
+		else if(options.backgroundColor.length == 6)
+			   ChunkList ~= Chunk( bKGD, options.backgroundColor);
+		if (options.text !is null && (options.text.latin1Text.length > 0 || options.text.unicodeText.length > 0))
+			ChunkList ~= chunkifyText(options);
+		ChunkList.sort;
+		ChunkList ~= Chunk(IEND, []);
+
+		// pre-allocate space needed
+		uint pngLength = 8;
+		foreach(chunk; ChunkList)
+			pngLength += chunk.length;
+		buffer.length = pngLength;
+		buffer.length = 0;
+
+		// create and write all data
+		writeSignature(buffer);
+		foreach(chunk; ChunkList)
+			writeChunk(buffer, chunk);
+
+		return buffer;
+	}
+
+	Chunk[] chunkifyText(EncodeOptions options)
+	{
+		Chunk[] result;
+		if (options.compressText)
+		{
+			auto enc = Encoder.create();
+			foreach(ubyte[] keyword, ubyte[] value; options.text)
+				result ~= Chunk(zTXt, keyword ~ cast(ubyte[])[0, 0] ~ enc(value));
+			foreach(char[] keyword, char[] value; options.text)
+				result ~= Chunk(iTXt, cast(ubyte[])keyword ~ cast(ubyte[])[0, 1, 0, 0, 0] ~ enc(cast(ubyte[])value));
+		}
+		else
+		{
+			foreach(ubyte[] keyword, ubyte[] value; options.text)
+				result ~= Chunk(tEXt, keyword ~ cast(ubyte[])[0] ~ value);
+			foreach(char[] keyword, char[] value; options.text)
+				result ~= Chunk(iTXt, cast(ubyte[])keyword ~ cast(ubyte[])[0, 0, 0, 0, 0] ~ cast(ubyte[])value);
+		}
+		return result;
+	}
+
+	ubyte[] filter(in ubyte[] source, ref PngImage image, FilterStrategy filterMethod = FilterStrategy.Dynamic)
+	{
+		/* adaptive filtering */
+
+		ubyte[] buffer = new ubyte[image.width * (image.bpp / 8) * image.height];
+		uint bytewidth = (image.bpp + 7) / 8;
+
+		uint scanlength = image.width * bytewidth;
+		buffer.length = image.height * (scanlength + 1) + image.height;
+		ubyte[] line, previous;
+		buffer.length = 0;
+		line = source[0..scanlength];
+		ubyte bestFilter = 0;
+
+		uint absSum(ubyte[] array)
+		{
+			uint result = 0;
+			foreach(value; array)
+				result += abs(cast(int)(cast(byte)value));
+			return result;
+		}
+
+		uint smallest = absSum(filterMap(line, &None, bytewidth));
+
+		void setSmallest(uint sum, ubyte filterType)
+		{
+			if (sum < smallest)
+			{
+				smallest = sum;
+				bestFilter = filterType;
+			}
+		}
+
+		if (filterMethod == FilterStrategy.Dynamic)
+		{
+			for (ubyte f = 1; f < 5; f++)
+				setSmallest(absSum(dynFilterMap(line, f, bytewidth)), f);
+			buffer ~= bestFilter;
+			buffer ~= dynFilterMap(line, bestFilter, bytewidth);
+
+			for (int y = 1; y < image.height; y++)
+			{
+				line = source[scanlength * y..scanlength * y + scanlength];
+				previous = source[scanlength * (y - 1)..scanlength * y];
+
+				bestFilter = 0;
+				smallest = absSum(filterMap(line, &None, bytewidth));
+				for (ubyte f = 1; f < 5; f++)
+					 setSmallest(absSum(dynFilterMap(previous, line, f, bytewidth)), f);
+
+				buffer ~= bestFilter;
+				buffer ~= dynFilterMap(previous, line, bestFilter, bytewidth);
+			}
+		}
+		else
+		{
+			buffer ~= cast(ubyte)filterMethod;
+			buffer ~= dynFilterMap(line, cast(ubyte)filterMethod, bytewidth);
+
+			for (int y = 1; y < image.height; y++)
+			{
+				line = source[scanlength * y..scanlength * y + scanlength];
+				previous = source[scanlength * (y - 1)..scanlength * y];
+
+				buffer ~= cast(ubyte)filterMethod;
+				buffer ~= dynFilterMap(previous, line, cast(ubyte)filterMethod, bytewidth);
+			}
+		}
+		return buffer;
+	}
+
+	bool opaqueOrColorkey(in ubyte[] image, in PngInfo info, out ubyte[] colorKey)
+	{
+		if (info.image.colorType == ColorType.Greyscale || info.image.colorType == ColorType.RGB)
+			return false;
+		else if (info.image.colorType == ColorType.RGBA )
+		{
+			uint numpixels = info.image.width * info.image.height;
+
+			ubyte[3] ckey;
+			bool hasCkey = false;
+
+			for(size_t i = 0; i < numpixels; i++)
+			{
+				if(image[i * 4 + 3] != 255)
+				{
+					if(image[i * 4 + 3] == 0)
+					{
+						if (hasCkey)
+						{
+							if (ckey[0] != image[i * 4] || ckey[1] != image[i * 4 + 1] ||
+								ckey[2] != image[i * 4 + 2] )
+								return false;
+						}
+						else
+						{
+							hasCkey = true;
+							ckey[0..2] = image[i * 4 .. i * 4 + 2];
+						}
+					}
+					else
+						return false;
+				}
+			}
+			if (hasCkey)
+			{
+				colorKey = new ubyte[6];
+				colorKey[1] = ckey[0];
+				colorKey[3] = ckey[1];
+				colorKey[5] = ckey[2];
+			}
+			return true;
+		}
+		else if(info.image.colorType == ColorType.GreyscaleAlpha)
+		{
+			size_t numpixels = image.length / 2;
+
+			ubyte[1] ckey;
+			bool hasCkey = false;
+
+			for(size_t i = 0; i < numpixels; i++)
+			{
+				if(image[i * 2 + 1] != 255)
+				{
+					if (hasCkey)
+					{
+						if (ckey[0] != image[i * 2])
+							return false;
+					}
+					else
+					{
+						hasCkey = true;
+						ckey[0] = image[i * 2];
+					}
+				}
+			}
+			if (hasCkey)
+			{
+				colorKey = new ubyte[2];
+				colorKey[1] = ckey[0];
+			}
+			return true;
+		}
+		else if (info.image.colorType == ColorType.Palette)
+		{
+			// TODO: implement this (compression optimization)
+			return false;
+		}
+	}
+
+	ubyte[] headerData(PngImage image)
+	{
+		ubyte[] header = new ubyte[13];
+		header.length = 0;
+
+		header.concatUint(image.width);
+		header.concatUint(image.height);
+		header ~= image.bitDepth;
+		header ~= image.colorType;
+		header ~= 0; //compression method
+		header ~= 0; //filter method
+		header ~= 0; //interlace method
+
+		return header;
+	}
+
+	void concatUint(ref ubyte[] bytestream, uint num)
+	{
+		bytestream.length = bytestream.length + 4;
+		bytestream[$-4] = num >> 24;
+		bytestream[$-3] = num >> 16;
+		bytestream[$-2] = num >> 8;
+		bytestream[$-1] = num;
+	}
+
+	void writeChunk(ref ubyte[] bytestream, ref Chunk chunk)
+	{
+		bytestream.concatUint(chunk.data.length);
+
+		bytestream.length = bytestream.length + 4;
+		bytestream[$-4] = (chunk.type & 0xff000000) >> 24 ;
+		bytestream[$-3] = (chunk.type & 0x00ff0000) >> 16;
+		bytestream[$-2] = (chunk.type & 0x0000ff00) >> 8;
+		bytestream[$-1] =  chunk.type & 0x000000ff;
+
+		bytestream.length = bytestream.length + chunk.data.length;
+		bytestream[$ - chunk.data.length..$] = chunk.data;
+
+		uint CRC = createCRC(bytestream[$ - chunk.data.length - 4.. $]);
+		bytestream.concatUint(CRC);
+	}
+
+	void writeSignature(ref ubyte[] byteStream)
+	{
+		byteStream ~= [137, 80, 78, 71, 13, 10, 26, 10];
+	}
+
+	/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+		From the png spec: pixels for filtering are defined as follows such, where x is the current
+		being filtered and c, b correspond to the previous scanline:
+			c b
+			a x
+
+	filters:    construction                                                    Reconstruction
+	0   None    Filt(x) = Orig(x)                                               Recon(x) = Filt(x)
+	1   Sub     Filt(x) = Orig(x) - Orig(a)                                     Recon(x) = Filt(x) + Recon(a)
+	2   Up      Filt(x) = Orig(x) - Orig(b)                                     Recon(x) = Filt(x) + Recon(b)
+	3   Average	Filt(x) = Orig(x) - floor((Orig(a) + Orig(b)) / 2)              Recon(x) = Filt(x) + floor((Recon(a) + Recon(b)) / 2)
+	4   Paeth   Filt(x) = Orig(x) - PaethPredictor(Orig(a), Orig(b), Orig(c))   Recon(x) = Filt(x) + PaethPredictor(Recon(a), Recon(b), Recon(c)
+	++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
+	ubyte None      (ubyte c, ubyte b, ubyte a, ubyte x) { return x; }
+	ubyte Sub       (ubyte c, ubyte b, ubyte a, ubyte x) { return x - a; }
+	ubyte Up        (ubyte c, ubyte b, ubyte a, ubyte x) { return x - b; }
+	ubyte Average   (ubyte c, ubyte b, ubyte a, ubyte x) { return x - (a + b) / 2; }
+	ubyte Paeth     (ubyte c, ubyte b, ubyte a, ubyte x) { return x - paethPredictor(a,b,c); }
+
+	/*  map for filtering. seq1 contains the previous scanline and seq2 the current
+	*   pixels are passed in the order they appear in the serialized image (left to right, top to bottom)
+	*/
+	ubyte[] filterMap(T)(ubyte[] seq1, ubyte[] seq2, T op, uint bytewidth)
+	{
+		ubyte[] result;
+		result.length = seq1.length < seq2.length ? seq1.length : seq2.length;
+
+		auto bw = bytewidth;
+		while (bw--)
+			result[bw] = op(0, seq1[bw], 0, seq2[bw]);
+		for (int i = bytewidth; i < result.length; i++)
+			result[i] = op(seq1[i - bytewidth], seq1[i], seq2[i - bytewidth], seq2[i]);
+		return result;
+	}
+
+	/*  see filterMap above, this is a special case for the top scanline, where pixels from the
+	*   previous scanline are set to zero
+	*/
+	ubyte[] filterMap(T)(ubyte[] seq, T op, uint bytewidth)
+	{
+		ubyte[] result;
+		result.length = seq.length;
+
+		auto bw = bytewidth;
+		while (bw--)
+			result[bw] = op(0, 0, 0, seq[bw]);
+		for (int i = bytewidth; i < result.length; i++)
+			result[i] = op(0, 0, seq[i - bytewidth], seq[i]);
+		return result;
+	}
+
+	ubyte[] dynFilterMap(ubyte[] seq1, ubyte[] seq2, ubyte fOp, uint bytewidth)
+	{
+		switch(fOp)
+		{
+			case 0: return filterMap(seq1,seq2, &None, bytewidth);
+			case 1:	return filterMap(seq1,seq2, &Sub, bytewidth);
+			case 2: return filterMap(seq1,seq2, &Up, bytewidth);
+			case 3: return filterMap(seq1,seq2, &Average, bytewidth);
+			case 4: return filterMap(seq1,seq2, &Paeth, bytewidth);
+			default:
+				mixin(pngEnforce(`false`, "wrong png filter"));
+			break;
+		}
+	}
+
+	ubyte[] dynFilterMap(ubyte[] seq, ubyte fOp, uint bytewidth)
+	{
+		switch(fOp)
+		{
+			case 0: return filterMap(seq, &None, bytewidth);
+			case 1: return filterMap(seq, &Sub, bytewidth);
+			case 2: return filterMap(seq, &Up, bytewidth);
+			case 3: return filterMap(seq, &Average, bytewidth);
+			case 4: return filterMap(seq, &Paeth, bytewidth);
+			default:
+				mixin(pngEnforce(`false`, "wrong png filter"));
+			break;
+		}
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/lodepng/util.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,65 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/// utility and some wrappers for tango / phobos compatibility
+module dynamin.lodepng.util;
+
+
+    //////////////////////////////////////////////////////////////////////////////////////
+    //                                                                                  //
+    //  This module provides some utility functions internal to lodepng and wraps or    //
+    //  aliases a few phobos / tango dependent functions under a common api             //
+    //                                                                                  //
+    //////////////////////////////////////////////////////////////////////////////////////
+
+version(Tango)
+{
+    import tango.stdc.stringz;
+    import tango.text.Util;
+
+    char[] _enforce(T)(char[] cond, char[] msg)
+    {
+        // HACK
+        return `if (!(`~ cond ~`)) throw new `~ T.stringof ~` ( "`~ cond ~`: `~ msg ~` (in " ~
+                `~ `__FILE__` ~` ~ ")" );`;
+    }
+
+    /+ TODO: implement this with toString
+    char[] _enforce(T)(char[] cond, char[] msg)
+    {
+        // HACK
+        return `if (!(`~ cond ~`)) throw new `~ T.stringof ~` ( "`~ cond ~`: `~ msg ~` (in " ~
+                `~ `__FILE__` ~` ~ " at " ~ `~ `ToString!(__LINE__)` ~` ~ ")" );`;
+    }
+    +/
+
+    alias toStringz toCString;
+    alias tango.text.Util.locate!(char, char) strFind;
+
+}
+else
+{
+    public import std.metastrings;
+    import std.string;
+
+
+    char[] _enforce(T)(char[] cond, char[] msg)
+    {
+       return std.metastrings.Format!
+       (
+            `if (!(%s)) throw new %s (        "%s: %s (in " ~ %s ~ " at " ~ %s ~ ")" );`,
+                   cond,          T.stringof, cond,msg,      `__FILE__`,   `std.metastrings.ToString!(__LINE__)`
+       );
+    }
+
+
+
+    alias std.string.find strFind;
+    alias toString toCString;
+}
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/lodepng/zlib_codec.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,146 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/// wraps zlib
+module dynamin.lodepng.zlib_codec;
+pragma(lib, "zlib");
+
+version (Tango)
+{
+    import czlib = tango.io.compress.c.zlib;
+}
+else
+{
+    import czlib = etc.c.zlib;
+}
+
+import dynamin.lodepng.util;
+
+
+// buffered decompression of zlib streams, avoiding GC provocation where possible
+struct DecodeStream
+{
+    // TODO: should be scope class for releasing zlib resources on scope exit
+
+    // constructor: initialize with target, will be resized as needed
+    static DecodeStream create(ref ubyte[] dest)
+    {
+        DecodeStream result;
+        result.dest = dest;
+
+        result.zlibStream.next_out = dest.ptr;
+        result.zlibStream.avail_out = dest.length;
+        result.zlibStream.avail_out = dest.length; //Z_BINARY
+        result.zlibStream.data_type = czlib.Z_BINARY;
+
+        return result;
+    }
+
+    ubyte[] opCall()
+    {
+        return dest;
+    }
+
+    void opCall(in ubyte[] input)
+    {
+        zlibStream.next_in = input.ptr;
+        zlibStream.avail_in = input.length;
+
+        if (!isInit)
+        {
+            isInit = true;
+            msg = czlib.inflateInit(&zlibStream);
+	        if (msg)
+	        {
+	            czlib.inflateEnd(&zlibStream);
+	            throw new Exception("");// TODO: toString(msg));
+	        }
+        }
+
+        while(zlibStream.avail_in)
+        {
+            msg = czlib.inflate(&zlibStream, czlib.Z_NO_FLUSH);
+
+            if (msg == czlib.Z_STREAM_END)
+            {
+                czlib.inflateEnd(&zlibStream);
+                hasEnded = true;
+                dest.length = zlibStream.total_out;
+                return;
+            }
+            else if (msg != czlib.Z_OK)
+            {
+                czlib.inflateEnd(&zlibStream);
+                throw new Exception("");// TODO: toString(zlibStream.msg));
+            }
+            else if(zlibStream.avail_out == 0)
+            {
+                dest.length = dest.length * 2;
+                zlibStream.next_out = &dest[dest.length / 2];
+                zlibStream.avail_out = dest.length / 2;
+            }
+        }
+    }
+
+    bool isInit = false;
+    bool hasEnded = false;
+
+    private
+	{
+	    ubyte[] dest;
+	    int msg = 0;
+	    czlib.z_stream zlibStream;
+	}
+}
+
+struct Encoder
+{
+    static Encoder create(ubyte strategy = czlib.Z_RLE, uint clevel = 9)
+    {
+        Encoder result;
+        result.level = clevel;
+        result.strategy = strategy;
+        return result;
+    }
+    ubyte[] opCall(in ubyte[] source)
+    {
+        ubyte[] result;
+        return this.opCall(source, result);
+
+    }
+    ubyte[] opCall(in ubyte[] source, ref ubyte[] buffer)
+    {
+        if (source.length == 0)
+        {
+            buffer.length = 0;
+            return buffer;
+        }
+
+        buffer.length = source.length / 4;
+        czlib.z_stream stream;
+        stream.next_in = source.ptr;
+        stream.avail_in = source.length;
+        stream.next_out = buffer.ptr;
+        stream.avail_out = buffer.length;
+        stream.data_type = czlib.Z_BINARY;
+
+        czlib.deflateInit2(&stream, 9, czlib.Z_DEFLATED, 15, level, strategy);
+        auto msg = czlib.deflate(&stream, czlib.Z_FINISH);
+        while(msg != czlib.Z_STREAM_END)
+        {
+            buffer.length = buffer.length + buffer.length;
+            stream.next_out = &buffer[buffer.length / 2];
+            stream.avail_out = buffer.length / 2;
+            msg = czlib.deflate(&stream, czlib.Z_FINISH);
+        }
+
+        assert(msg == czlib.Z_STREAM_END);
+        buffer.length = stream.total_out;
+        czlib.deflateEnd(&stream);
+
+        return buffer;
+    }
+
+    private ubyte strategy = czlib.Z_RLE;
+    private ubyte level = 9;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/painting/all.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,31 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.painting.all;
+
+public import dynamin.painting.color;
+public import dynamin.painting.coordinates;
+public import dynamin.painting.graphics;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/painting/color.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,832 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.painting.color;
+
+import dynamin.core.string;
+
+/**
+ * If a color has an alpha of 255, it is fully opaque.
+ *
+ * The pre-defined colors are those recognized by the SVG standard
+ * found at $(LINK http://www.w3.org/TR/SVG/types.html#ColorKeywords).
+ */
+align(1)
+struct Color { // TODO: make a class and make a Pixel32 struct
+	// TODO: actually...color should stay a struct, but use 32-bit floats
+	// TODO: should use 32-bit floats
+version(BigEndian) {
+	ubyte A;
+	ubyte R;
+	ubyte G;
+	ubyte B;
+} else {
+	ubyte B;
+	ubyte G;
+	ubyte R;
+	ubyte A;
+}
+	/+
+	this(ubyte r, ubyte g, ubyte b, ubyte a = 255) {
+		this.r = r/255.0;
+		this.g = g/255.0;
+		this.b = b/255.0;
+		this.a = a/255.0;
+	}
+	this(real r, real g, real b, real a = 1.0) {
+		this.r = r;
+		this.g = g;
+		this.b = b;
+		this.a = a;
+	}
+	private real getMin() {
+	}
+	private real getMax() {
+		real max = r;
+		if(g > max)
+			max = g;
+		if(b > max)
+			max = b;
+		return max;
+	}
+	/**
+	 * From 0 to 360 degrees.
+	 */
+	real gethue() {
+		real min = min();
+		real max = max();
+		real delta = max - min;
+		if(max == 0)  // r = g = b = 0 = black
+			return 0;
+
+		real hue;
+		if(r == max)
+			hue = (g - b) / delta;     // between yellow and magenta
+		else if(g == max)
+			hue = 2 + (b - r) / delta; // between cyan and yellow
+		else
+			hue = 4 + (r - g) / delta; // between magenta and cyan
+
+		hue *= 60;
+		if(hue < 0)
+			hue += 360;
+
+	}
+	real getSaturation() {
+	}
+	real getValue() {
+		return getMax();
+	}
+	+/
+	/**
+	 * Changes this color to its inverse.
+	 * The inverse of black is white. The inverse of blue is yellow.
+	 */
+	void invert() {
+		R = 255 - R;
+		G = 255 - G;
+		B = 255 - B;
+	}
+	string toUtf8() {
+		return format("Color [R={,3}, G={,3}, B={,3}]", R, G, B);
+	}
+static:
+	Color opCall(ubyte a, ubyte r, ubyte g, ubyte b) {
+		Color c;
+		c.A = a;
+		c.R = r;
+		c.G = g;
+		c.B = b;
+		return c;
+	}
+	Color opCall(ubyte r, ubyte g, ubyte b) {
+		return Color(255, r, g, b);
+	}
+	Color opCall(uint argb) {
+		return Color(argb >> 24, argb & 0xFFFFFF >> 16, argb & 0xFFFF >> 8, argb & 0xFF);
+	}
+
+	/**
+	 * <code style="background-color: rgb(240, 248, 255);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color AliceBlue() {       return Color(240, 248, 255); }
+	/**
+	 * <code style="background-color: rgb(250, 235, 215);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color AntiqueWhite() {    return Color(250, 235, 215); }
+	/**
+	 * <code style="background-color: rgb(  0, 255, 255);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Aqua() {            return Color(  0, 255, 255); }
+	/**
+	 * <code style="background-color: rgb(127, 255, 212);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Aquamarine() {      return Color(127, 255, 212); }
+	/**
+	 * <code style="background-color: rgb(240, 255, 255);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Azure() {           return Color(240, 255, 255); }
+	/**
+	 * <code style="background-color: rgb(245, 245, 220);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Beige() {           return Color(245, 245, 220); }
+	/**
+	 * <code style="background-color: rgb(255, 228, 196);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Bisque() {          return Color(255, 228, 196); }
+	/**
+	 * <code style="background-color: rgb(  0,   0,   0);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Black() {           return Color(  0,   0,   0); }
+	/**
+	 * <code style="background-color: rgb(255, 235, 205);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color BlanchedAlmond() {  return Color(255, 235, 205); }
+	/**
+	 * <code style="background-color: rgb(  0,   0, 255);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Blue() {            return Color(  0,   0, 255); }
+	/**
+	 * <code style="background-color: rgb(138,  43, 226);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color BlueViolet() {      return Color(138,  43, 226); }
+	/**
+	 * <code style="background-color: rgb(165,  42,  42);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Brown() {           return Color(165,  42,  42); }
+	/**
+	 * <code style="background-color: rgb(222, 184, 135);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color BurlyWood() {       return Color(222, 184, 135); }
+	/**
+	 * <code style="background-color: rgb( 95, 158, 160);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color CadetBlue() {       return Color( 95, 158, 160); }
+	/**
+	 * <code style="background-color: rgb(127, 255,   0);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Chartreuse() {      return Color(127, 255,   0); }
+	/**
+	 * <code style="background-color: rgb(210, 105,  30);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Chocolate() {       return Color(210, 105,  30); }
+	/**
+	 * <code style="background-color: rgb(255, 127,  80);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Coral() {           return Color(255, 127,  80); }
+	/**
+	 * <code style="background-color: rgb(100, 149, 237);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color CornflowerBlue() {  return Color(100, 149, 237); }
+	/**
+	 * <code style="background-color: rgb(255, 248, 220);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Cornsilk() {        return Color(255, 248, 220); }
+	/**
+	 * <code style="background-color: rgb(220,  20,  60);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Crimson() {         return Color(220,  20,  60); }
+	/**
+	 * <code style="background-color: rgb(  0, 255, 255);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Cyan() {            return Color(  0, 255, 255); }
+	/**
+	 * <code style="background-color: rgb(  0,   0, 139);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DarkBlue() {        return Color(  0,   0, 139); }
+	/**
+	 * <code style="background-color: rgb(  0, 139, 139);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DarkCyan() {        return Color(  0, 139, 139); }
+	/**
+	 * <code style="background-color: rgb(184, 134,  11);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DarkGoldenrod() {   return Color(184, 134,  11); }
+	/**
+	 * <code style="background-color: rgb(169, 169, 169);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DarkGray() {        return Color(169, 169, 169); }
+	/**
+	 * <code style="background-color: rgb(  0, 100,   0);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DarkGreen() {       return Color(  0, 100,   0); }
+	/**
+	 * <code style="background-color: rgb(189, 183, 107);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DarkKhaki() {       return Color(189, 183, 107); }
+	/**
+	 * <code style="background-color: rgb(139,   0, 139);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DarkMagenta() {     return Color(139,   0, 139); }
+	/**
+	 * <code style="background-color: rgb( 85, 107,  47);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DarkOliveGreen() {  return Color( 85, 107,  47); }
+	/**
+	 * <code style="background-color: rgb(255, 140,   0);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DarkOrange() {      return Color(255, 140,   0); }
+	/**
+	 * <code style="background-color: rgb(153,  50, 204);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DarkOrchid() {      return Color(153,  50, 204); }
+	/**
+	 * <code style="background-color: rgb(139,   0,   0);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DarkRed() {         return Color(139,   0,   0); }
+	/**
+	 * <code style="background-color: rgb(233, 150, 122);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DarkSalmon() {      return Color(233, 150, 122); }
+	/**
+	 * <code style="background-color: rgb(143, 188, 143);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DarkSeaGreen() {    return Color(143, 188, 143); }
+	/**
+	 * <code style="background-color: rgb( 72,  61, 139);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DarkSlateBlue() {   return Color( 72,  61, 139); }
+	/**
+	 * <code style="background-color: rgb( 47,  79,  79);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DarkSlateGray() {   return Color( 47,  79,  79); }
+	/**
+	 * <code style="background-color: rgb(  0, 206, 209);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DarkTurquoise() {   return Color(  0, 206, 209); }
+	/**
+	 * <code style="background-color: rgb(148,   0, 211);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DarkViolet() {      return Color(148,   0, 211); }
+	/**
+	 * <code style="background-color: rgb(255,  20, 147);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DeepPink() {        return Color(255,  20, 147); }
+	/**
+	 * <code style="background-color: rgb(  0, 191, 255);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DeepSkyBlue() {     return Color(  0, 191, 255); }
+	/**
+	 * <code style="background-color: rgb(105, 105, 105);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DimGray() {         return Color(105, 105, 105); }
+	/**
+	 * <code style="background-color: rgb( 30, 144, 255);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color DodgerBlue() {      return Color( 30, 144, 255); }
+	/**
+	 * <code style="background-color: rgb(178,  34,  34);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Firebrick() {       return Color(178,  34,  34); }
+	/**
+	 * <code style="background-color: rgb(255, 250, 240);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color FloralWhite() {     return Color(255, 250, 240); }
+	/**
+	 * <code style="background-color: rgb( 34, 139,  34);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color ForestGreen() {     return Color( 34, 139,  34); }
+	/**
+	 * <code style="background-color: rgb(255,   0, 255);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Fuchsia() {         return Color(255,   0, 255); }
+	/**
+	 * <code style="background-color: rgb(220, 220, 220);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Gainsboro() {       return Color(220, 220, 220); }
+	/**
+	 * <code style="background-color: rgb(248, 248, 255);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color GhostWhite() {      return Color(248, 248, 255); }
+	/**
+	 * <code style="background-color: rgb(255, 215,   0);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Gold() {            return Color(255, 215,   0); }
+	/**
+	 * <code style="background-color: rgb(218, 165,  32);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Goldenrod() {       return Color(218, 165,  32); }
+	/**
+	 * <code style="background-color: rgb(128, 128, 128);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Gray() {            return Color(128, 128, 128); }
+	/**
+	 * <code style="background-color: rgb(  0, 128,   0);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Green() {           return Color(  0, 128,   0); }
+	/**
+	 * <code style="background-color: rgb(173, 255,  47);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color GreenYellow() {     return Color(173, 255,  47); }
+	/**
+	 * <code style="background-color: rgb(240, 255, 240);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Honeydew() {        return Color(240, 255, 240); }
+	/**
+	 * <code style="background-color: rgb(255, 105, 180);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color HotPink() {         return Color(255, 105, 180); }
+	/**
+	 * <code style="background-color: rgb(205,  92,  92);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color IndianRed() {       return Color(205,  92,  92); }
+	/**
+	 * <code style="background-color: rgb( 75,   0, 130);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Indigo() {          return Color( 75,   0, 130); }
+	/**
+	 * <code style="background-color: rgb(255, 255, 240);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Ivory() {           return Color(255, 255, 240); }
+	/**
+	 * <code style="background-color: rgb(240, 230, 140);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Khaki() {           return Color(240, 230, 140); }
+	/**
+	 * <code style="background-color: rgb(230, 230, 250);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Lavender() {        return Color(230, 230, 250); }
+	/**
+	 * <code style="background-color: rgb(255, 240, 245);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color LavenderBlush() {   return Color(255, 240, 245); }
+	/**
+	 * <code style="background-color: rgb(124, 252,   0);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color LawnGreen() {       return Color(124, 252,   0); }
+	/**
+	 * <code style="background-color: rgb(255, 250, 205);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color LemonChiffon() {    return Color(255, 250, 205); }
+	/**
+	 * <code style="background-color: rgb(173, 216, 230);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color LightBlue() {       return Color(173, 216, 230); }
+	/**
+	 * <code style="background-color: rgb(240, 128, 128);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color LightCoral() {      return Color(240, 128, 128); }
+	/**
+	 * <code style="background-color: rgb(224, 255, 255);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color LightCyan() {       return Color(224, 255, 255); }
+	/**
+	 * <code style="background-color: rgb(250, 250, 210);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color LightGoldenrodYellow() { return Color(250, 250, 210); }
+	/**
+	 * <code style="background-color: rgb(211, 211, 211);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color LightGray() {       return Color(211, 211, 211); }
+	/**
+	 * <code style="background-color: rgb(144, 238, 144);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color LightGreen() {      return Color(144, 238, 144); }
+	/**
+	 * <code style="background-color: rgb(255, 182, 193);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color LightPink() {       return Color(255, 182, 193); }
+	/**
+	 * <code style="background-color: rgb(255, 160, 122);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color LightSalmon() {     return Color(255, 160, 122); }
+	/**
+	 * <code style="background-color: rgb( 32, 178, 170);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color LightSeaGreen() {   return Color( 32, 178, 170); }
+	/**
+	 * <code style="background-color: rgb(135, 206, 250);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color LightSkyBlue() {    return Color(135, 206, 250); }
+	/**
+	 * <code style="background-color: rgb(119, 136, 153);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color LightSlateGray() {  return Color(119, 136, 153); }
+	/**
+	 * <code style="background-color: rgb(176, 196, 222);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color LightSteelBlue() {  return Color(176, 196, 222); }
+	/**
+	 * <code style="background-color: rgb(255, 255, 224);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color LightYellow() {     return Color(255, 255, 224); }
+	/**
+	 * <code style="background-color: rgb(  0, 255,   0);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Lime() {            return Color(  0, 255,   0); }
+	/**
+	 * <code style="background-color: rgb( 50, 205,  50);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color LimeGreen() {       return Color( 50, 205,  50); }
+	/**
+	 * <code style="background-color: rgb(250, 240, 230);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Linen() {           return Color(250, 240, 230); }
+	/**
+	 * <code style="background-color: rgb(255,   0, 255);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Magenta() {         return Color(255,   0, 255); }
+	/**
+	 * <code style="background-color: rgb(128,   0,   0);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Maroon() {          return Color(128,   0,   0); }
+	/**
+	 * <code style="background-color: rgb(102, 205, 170);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color MediumAquamarine() { return Color(102, 205, 170); }
+	/**
+	 * <code style="background-color: rgb(  0,   0, 205);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color MediumBlue() {      return Color(  0,   0, 205); }
+	/**
+	 * <code style="background-color: rgb(186,  85, 211);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color MediumOrchid() {    return Color(186,  85, 211); }
+	/**
+	 * <code style="background-color: rgb(147, 112, 219);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color MediumPurple() {    return Color(147, 112, 219); }
+	/**
+	 * <code style="background-color: rgb( 60, 179, 113);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color MediumSeaGreen() {  return Color( 60, 179, 113); }
+	/**
+	 * <code style="background-color: rgb(123, 104, 238);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color MediumSlateBlue() { return Color(123, 104, 238); }
+	/**
+	 * <code style="background-color: rgb(  0, 250, 154);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color MediumSpringGreen() { return Color(  0, 250, 154); }
+	/**
+	 * <code style="background-color: rgb( 72, 209, 204);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color MediumTurquoise() { return Color( 72, 209, 204); }
+	/**
+	 * <code style="background-color: rgb(199,  21, 133);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color MediumVioletRed() { return Color(199,  21, 133); }
+	/**
+	 * <code style="background-color: rgb( 25,  25, 112);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color MidnightBlue() {    return Color( 25,  25, 112); }
+	/**
+	 * <code style="background-color: rgb(245, 255, 250);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color MintCream() {       return Color(245, 255, 250); }
+	/**
+	 * <code style="background-color: rgb(255, 228, 225);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color MistyRose() {       return Color(255, 228, 225); }
+	/**
+	 * <code style="background-color: rgb(255, 228, 181);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Moccasin() {        return Color(255, 228, 181); }
+	/**
+	 * <code style="background-color: rgb(255, 222, 173);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color NavajoWhite() {     return Color(255, 222, 173); }
+	/**
+	 * <code style="background-color: rgb(  0,   0, 128);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Navy() {            return Color(  0,   0, 128); }
+	/**
+	 * <code style="background-color: rgb(253, 245, 230);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color OldLace() {         return Color(253, 245, 230); }
+	/**
+	 * <code style="background-color: rgb(128, 128,   0);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Olive() {           return Color(128, 128,   0); }
+	/**
+	 * <code style="background-color: rgb(107, 142,  35);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color OliveDrab() {       return Color(107, 142,  35); }
+	/**
+	 * <code style="background-color: rgb(255, 165,   0);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Orange() {          return Color(255, 165,   0); }
+	/**
+	 * <code style="background-color: rgb(255,  69,   0);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color OrangeRed() {       return Color(255,  69,   0); }
+	/**
+	 * <code style="background-color: rgb(218, 112, 214);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Orchid() {          return Color(218, 112, 214); }
+	/**
+	 * <code style="background-color: rgb(238, 232, 170);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color PaleGoldenrod() {   return Color(238, 232, 170); }
+	/**
+	 * <code style="background-color: rgb(152, 251, 152);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color PaleGreen() {       return Color(152, 251, 152); }
+	/**
+	 * <code style="background-color: rgb(175, 238, 238);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color PaleTurquoise() {   return Color(175, 238, 238); }
+	/**
+	 * <code style="background-color: rgb(219, 112, 147);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color PaleVioletRed() {   return Color(219, 112, 147); }
+	/**
+	 * <code style="background-color: rgb(255, 239, 213);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color PapayaWhip() {      return Color(255, 239, 213); }
+	/**
+	 * <code style="background-color: rgb(255, 218, 185);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color PeachPuff() {       return Color(255, 218, 185); }
+	/**
+	 * <code style="background-color: rgb(205, 133,  63);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Peru() {            return Color(205, 133,  63); }
+	/**
+	 * <code style="background-color: rgb(255, 192, 203);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Pink() {            return Color(255, 192, 203); }
+	/**
+	 * <code style="background-color: rgb(221, 160, 221);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Plum() {            return Color(221, 160, 221); }
+	/**
+	 * <code style="background-color: rgb(176, 224, 230);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color PowderBlue() {      return Color(176, 224, 230); }
+	/**
+	 * <code style="background-color: rgb(128,   0, 128);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Purple() {          return Color(128,   0, 128); }
+	/**
+	 * <code style="background-color: rgb(255,   0,   0);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Red() {             return Color(255,   0,   0); }
+	/**
+	 * <code style="background-color: rgb(188, 143, 143);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color RosyBrown() {       return Color(188, 143, 143); }
+	/**
+	 * <code style="background-color: rgb( 65, 105, 225);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color RoyalBlue() {       return Color( 65, 105, 225); }
+	/**
+	 * <code style="background-color: rgb(139,  69,  19);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color SaddleBrown() {     return Color(139,  69,  19); }
+	/**
+	 * <code style="background-color: rgb(250, 128, 114);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Salmon() {          return Color(250, 128, 114); }
+	/**
+	 * <code style="background-color: rgb(244, 164,  96);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color SandyBrown() {      return Color(244, 164,  96); }
+	/**
+	 * <code style="background-color: rgb( 46, 139,  87);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color SeaGreen() {        return Color( 46, 139,  87); }
+	/**
+	 * <code style="background-color: rgb(255, 245, 238);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Seashell() {        return Color(255, 245, 238); }
+	/**
+	 * <code style="background-color: rgb(160,  82,  45);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Sienna() {          return Color(160,  82,  45); }
+	/**
+	 * <code style="background-color: rgb(192, 192, 192);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Silver() {          return Color(192, 192, 192); }
+	/**
+	 * <code style="background-color: rgb(135, 206, 235);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color SkyBlue() {         return Color(135, 206, 235); }
+	/**
+	 * <code style="background-color: rgb(106,  90, 205);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color SlateBlue() {       return Color(106,  90, 205); }
+	/**
+	 * <code style="background-color: rgb(112, 128, 144);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color SlateGray() {       return Color(112, 128, 144); }
+	/**
+	 * <code style="background-color: rgb(255, 250, 250);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Snow() {            return Color(255, 250, 250); }
+	/**
+	 * <code style="background-color: rgb(  0, 255, 127);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color SpringGreen() {     return Color(  0, 255, 127); }
+	/**
+	 * <code style="background-color: rgb( 70, 130, 180);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color SteelBlue() {       return Color( 70, 130, 180); }
+	/**
+	 * <code style="background-color: rgb(210, 180, 140);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Tan() {             return Color(210, 180, 140); }
+	/**
+	 * <code style="background-color: rgb(  0, 128, 128);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Teal() {            return Color(  0, 128, 128); }
+	/**
+	 * <code style="background-color: rgb(216, 191, 216);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Thistle() {         return Color(216, 191, 216); }
+	/**
+	 * <code style="background-color: rgb(255,  99,  71);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Tomato() {          return Color(255,  99,  71); }
+	/**
+	 * <code style="background-color: rgb( 64, 224, 208);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Turquoise() {       return Color( 64, 224, 208); }
+	/**
+	 * <code style="background-color: rgb(238, 130, 238);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Violet() {          return Color(238, 130, 238); }
+	/**
+	 * <code style="background-color: rgb(245, 222, 179);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Wheat() {           return Color(245, 222, 179); }
+	/**
+	 * <code style="background-color: rgb(255, 255, 255);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color White() {           return Color(255, 255, 255); }
+	/**
+	 * <code style="background-color: rgb(245, 245, 245);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color WhiteSmoke() {      return Color(245, 245, 245); }
+	/**
+	 * <code style="background-color: rgb(255, 255,   0);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color Yellow() {          return Color(255, 255,   0); }
+	/**
+	 * <code style="background-color: rgb(154, 205,  50);">
+	 * &nbsp;&nbsp;&nbsp;</code>
+	 */
+	Color YellowGreen() {     return Color(154, 205,  50); }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/painting/coordinates.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,345 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.painting.coordinates;
+
+import dynamin.core.string;
+import dynamin.core.math;
+
+///
+struct Point {
+private:
+	float _x = 0, _y = 0;
+public:
+	///
+	static Point opCall() {
+		Point pt;
+		return pt;
+	}
+	///
+	static Point opCall(float x, float y) {
+		Point pt;
+		pt._x = x;
+		pt._y = y;
+		return pt;
+	}
+	///
+	float x() { return _x; }
+	///
+	void x(float f) { _x = f; }
+	///
+	float y() { return _y; }
+	///
+	void y(float f) { _y = f; }
+	///
+	Point opNeg() {
+		Point pt2;
+		pt2._x = -_x;
+		pt2._y = -_y;
+		return pt2;
+	}
+	///
+	Point opAdd(Point pt) {
+		Point pt2;
+		pt2._x = _x + pt._x;
+		pt2._y = _y + pt._y;
+		return pt2;
+	}
+	///
+	Point opSub(Point pt) {
+		Point pt2;
+		pt2._x = _x - pt._x;
+		pt2._y = _y - pt._y;
+		return pt2;
+	}
+	///
+	Rect opAdd(Size size) {
+		Rect rect;
+		rect._x = _x;
+		rect._y = _y;
+		rect._width = size._width;
+		rect._height = size._height;
+		return rect;
+	}
+	string toString() {
+		return format("Point [x={}, y={}]", _x, _y);
+	}
+}
+
+///
+struct Size {
+private:
+	float _width = 0, _height = 0;
+public:
+	///
+	static Size opCall() {
+		Size size;
+		return size;
+	}
+	///
+	static Size opCall(float width, float height) {
+		Size size;
+		size._width = width;
+		size._height = height;
+		return size;
+	}
+	///
+	float width() { return _width; }
+	///
+	void width(float f) { _width = f; }
+	///
+	float height() { return _height; }
+	///
+	void height(float f) { _height = f; }
+	///
+	Size opAdd(Size size) {
+		Size size2;
+		size2._width = _width + size._width;
+		size2._height = _height + size._height;
+		return size2;
+	}
+	///
+	Size opSub(Size size) {
+		Size size2;
+		size2._width = _width - size._width;
+		size2._height = _height - size._height;
+		return size2;
+	}
+	///
+	Size opAdd(BorderSize border) {
+		Size size2;
+		size2._width = _width + border._left + border._right;
+		size2._height = _height + border._top + border._bottom;
+		return size2;
+	}
+	///
+	Size opSub(BorderSize border) {
+		Size size2;
+		size2._width = _width - border._left - border._right;
+		size2._height = _height - border._top - border._bottom;
+		return size2;
+	}
+	string toString() {
+		return format("Size [width={}, height={}]", _width, _height);
+	}
+}
+
+///
+struct Rect {
+private:
+	float _x = 0, _y = 0, _width = 0, _height = 0;
+public:
+	static Rect opCall() {
+		Rect rect;
+		return rect;
+	}
+	static Rect opCall(float x, float y, float width, float height) {
+		Rect rect;
+		rect._x = x;
+		rect._y = y;
+		rect._width = width;
+		rect._height = height;
+		return rect;
+	}
+	///
+	float x() { return _x; }
+	///
+	void x(float f) { _x = f; }
+	///
+	float y() { return _y; }
+	///
+	void y(float f) { _y = f; }
+	///
+	float width() { return _width; }
+	///
+	void width(float f) { _width = f; }
+	///
+	float height() { return _height; }
+	///
+	void height(float f) { _height = f; }
+	///
+	float right() { return _x+_width; }
+	///
+	float bottom() { return _y+_height; }
+	///
+	Rect opAdd(Rect rect) {
+		Rect rect2;
+		rect2._x = _x + rect._x;
+		rect2._y = _y + rect._y;
+		rect2._width = _width + rect._width;
+		rect2._height = _height + rect._height;
+		return rect2;
+	}
+	///
+	Rect opSub(Rect rect) {
+		Rect rect2;
+		rect2._x = _x - rect._x;
+		rect2._y = _y - rect._y;
+		rect2._width = _width - rect._width;
+		rect2._height = _height - rect._height;
+		return rect2;
+	}
+	///
+	Rect opAdd(Point pt) {
+		Rect rect2;
+		rect2._x = _x + pt._x;
+		rect2._y = _y + pt._y;
+		rect2._width = _width;
+		rect2._height = _height;
+		return rect2;
+	}
+	///
+	Rect opSub(Point pt) {
+		Rect rect2;
+		rect2._x = _x - pt._x;
+		rect2._y = _y - pt._y;
+		rect2._width = _width;
+		rect2._height = _height;
+		return rect2;
+	}
+	///
+	Rect opAdd(Size size) {
+		Rect rect2;
+		rect2._x = _x;
+		rect2._y = _y;
+		rect2._width = _width + size._width;
+		rect2._height = _height + size._height;
+		return rect2;
+	}
+	///
+	Rect opSub(Size size) {
+		Rect rect2;
+		rect2._x = _x;
+		rect2._y = _y;
+		rect2._width = _width - size._width;
+		rect2._height = _height - size._height;
+		return rect2;
+	}
+	///
+	Rect opAdd(BorderSize border) {
+		Rect rect2;
+		rect2._x = _x - border._left;
+		rect2._y = _y - border._top;
+		rect2._width = _width + border._left + border._right;
+		rect2._height = _height + border._top + border._bottom;
+		return rect2;
+	}
+	///
+	Rect opSub(BorderSize border) {
+		Rect rect2;
+		rect2._x = _x + border._left;
+		rect2._y = _y + border._top;
+		rect2._width = _width - border._left - border._right;
+		rect2._height = _height - border._top - border._bottom;
+		return rect2;
+	}
+	bool contains(Point pt) {
+		return pt.x >= x && pt.y >= y && pt.x < right && pt.y < bottom;
+	}
+	Rect getUnion(Rect rect) {
+		auto x2 = min(_x, rect._x);
+		auto y2 = min(_y, rect._y);
+		Rect rect2;
+		rect2._width = max(_x+_width, rect._x+rect._width)-x2;
+		rect2._height = max(_y+_height, rect._y+rect._height)-y2;
+		rect2._x = x2;
+		rect2._y = y2;
+		return rect2;
+	}
+	string toString() {
+		return format("Rect [x={}, y={}, width={}, height={}]",
+			_x, _y, _width, _height);
+	}
+}
+unittest {
+	assert(Rect(20, 2, 70, 5).getUnion(Rect(22, 3, 70, 4)) == Rect(20, 2, 72, 5));
+}
+
+///
+struct BorderSize {
+private:
+	float _left = 0, _top = 0, _right = 0, _bottom = 0;
+public:
+	static BorderSize opCall() {
+		BorderSize border;
+		return border;
+	}
+	static BorderSize opCall(float _left, float _top, float _right, float _bottom) {
+		BorderSize border;
+		border._left = _left;
+		border._top = _top;
+		border._right = _right;
+		border._bottom = _bottom;
+		return border;
+	}
+	///
+	float left() { return _left; }
+	///
+	void left(float f) { _left = f; }
+	///
+	float top() { return _top; }
+	///
+	void top(float f) { _top = f; }
+	///
+	float right() { return _right; }
+	///
+	void right(float f) { _right = f; }
+	///
+	float bottom() { return _bottom; }
+	///
+	void bottom(float f) { _bottom = f; }
+	///
+	BorderSize opAdd(BorderSize border) {
+		BorderSize border2;
+		border2._left = _left + border._left;
+		border2._right = _right + border._right;
+		border2._top = _top + border._top;
+		border2._bottom = _bottom + border._bottom;
+		return border2;
+	}
+	///
+	BorderSize opSub(BorderSize border) {
+		BorderSize border2;
+		border2._left = _left - border._left;
+		border2._right = _right - border._right;
+		border2._top = _top - border._top;
+		border2._bottom = _bottom - border._bottom;
+		return border2;
+	}
+	string toString() {
+		return format("BorderSize [_left={}, _top={}, _right={}, _bottom={}]",
+			_left, _top, _right, _bottom);
+	}
+}
+
+unittest {
+	Point pt = Point(7, 9);
+	assert(pt.x == 7);
+	assert(pt.y == 9);
+	Rect rect = pt + Size(15, 13);
+	assert(rect == Rect(7, 9, 15, 13));
+	assert(Size(15, 10) + BorderSize(3, 5, 1, 7) == Size(19, 22));
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/painting/graphics.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,589 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.painting.graphics;
+
+import dynamin.c.cairo;
+import dynamin.core.string;
+import dynamin.core.math;
+import dynamin.core.file;
+import dynamin.painting.coordinates;
+import dynamin.painting.color;
+import tango.io.Stdout;
+
+///
+class Font {
+private:
+	string _family;
+	int _style = 0;
+	int _size;
+public:
+	this(string family_ = "", int size = 10, bool b = false, bool i = false, bool u = false) {
+		this.family = family_;
+		this.size = size;
+		bold = b;
+		italic = i;
+		underline = u;
+	}
+	/**
+	 * Gets or sets the family name of this font. Common font family names on
+	 * Windows are "Arial", "Times New Roman", and "Tahoma".
+	 */
+	string family() { return _family; }
+	/// ditto
+	void family(string str) { _family = str; }
+	///
+	int style() { return _style; }
+	/// ditto
+	void style(int s) { _style = s; }
+	/// Gets or sets whether this font is bold.
+	bool bold() { return cast(bool)(_style & 1); }
+	/// ditto
+	void bold(bool b) { b ? (_style |= 1) : (_style &= ~1); }
+	/// Gets or sets whether this font is italic.
+	bool italic() { return cast(bool)(_style & 2); }
+	/// ditto
+	void italic(bool b) { b ? (_style |= 2) : (_style &= ~2); }
+	/// Gets or sets whether this font is underline.
+	bool underline() { return cast(bool)(_style & 4); }
+	/// ditto
+	void underline(bool b) { b ? (_style |= 4) : (_style &= ~4); }
+	/// Gets or sets whether this font is strikethrough.
+	bool strikethrough() { return cast(bool)(_style & 8); }
+	/// ditto
+	void strikethrough(bool b) { b ? (_style |= 8) : (_style &= ~8); }
+	/**
+	 * Gets or sets the size of this font in user space units, not in points.
+	 * This size is the ascent plus the descent, not including the leading.
+	 */
+	int size() { return _size; }
+	/// ditto
+	void size(int s) { _size = s; }
+}
+
+///
+struct FontExtents {
+	///
+	real ascent;
+	///
+	real descent;
+	///
+	real leading() { return height - ascent - descent; }
+	///
+	real height;
+	///
+	real maxAdvance;
+}
+
+//import lodepng = dynamin.lodepng.decode;
+/// An RGBA 32-bit-per-pixel image.
+class Image {
+	Color* _data;
+	uint _width, _height;
+	Color* data() { return _data; }
+	uint width() { return _width; }
+	uint height() { return _height; }
+	protected this() {
+	}
+	Color* opIndex(int x, int y) {
+		return _data + x+y*_width;
+	}
+	static Image load(string file) {
+		static if(false) {
+		auto img = new Image;
+		lodepng.PngInfo info;
+		img._data = cast(Color*)lodepng.decode32(readFileBytes(file), info);
+		img._width = info.image.width;
+		img._height = info.image.height;
+
+		ubyte r;
+		for(uint i = 0; i < img.width * img.height; ++i) {
+			// lodepng returns data as ABGR instead of the ARGB that cairo,
+			// Windows, and I think X use.
+			r = img.data[i].R;
+			img.data[i].R = img.data[i].B;
+			img.data[i].B = r;
+			// cairo, Windows, and I think X use pre-multiplied alpha
+			img.data[i].R = img.data[i].R * img.data[i].A / 255;
+			img.data[i].G = img.data[i].G * img.data[i].A / 255;
+			img.data[i].B = img.data[i].B * img.data[i].A / 255;
+		}
+
+		return img;
+		} else { return null; }
+	}
+}
+
+///
+enum GraphicsOperator {
+	Clear,  ///
+
+	Source, ///
+	Over,   ///
+	In,     ///
+	Out,    ///
+	Atop,   ///
+
+	Dest,     ///
+	DestOver, ///
+	DestIn,   ///
+	DestOut,  ///
+	DestAtop, ///
+
+	Xor,     ///
+	Add,     ///
+	Saturate ///
+}
+
+///
+enum GraphicsFillRule {
+	         ///
+	Winding,
+	EvenOdd  ///
+}
+/**
+ * Example:
+ * -----
+ * graphics.source = Color.Gold;
+ * graphics.rectangle(40, 10, 100, 120);
+ * graphics.fill();
+ * graphics.source = Color.Black;
+ *
+ * graphics.lineWidth = 20;
+ *
+ * // GraphicsLineCap.Butt is default
+ * graphics.moveTo(40, 30);
+ * graphics.lineTo(140, 30);
+ * graphics.stroke();
+ *
+ * graphics.lineCap = GraphicsLineCap.Round;
+ * graphics.moveTo(40, 70);
+ * graphics.lineTo(140, 70);
+ * graphics.stroke();
+ *
+ * graphics.lineCap = GraphicsLineCap.Square;
+ * graphics.moveTo(40, 110);
+ * graphics.lineTo(140, 110);
+ * graphics.stroke();
+ * -----
+ * $(IMAGE ../web/example_line_cap.png)
+ */
+enum GraphicsLineCap {
+	/**
+	 * Uses no ending. The line ends exactly at the end point.
+	 */
+	Butt,
+	/**
+	 * Uses a rounded ending with the center of the circle at the end point.
+	 * Therefore, the cap extends past the end point for half the line width.
+	 */
+	Round,
+	/**
+	 * Uses a square ending with the center of the square at the end point.
+	 * Therefore, the cap extends past the end point for half the line width.
+	 */
+	Square
+}
+
+// cairo_copy_clip_rectangles  --> Rectangle[] ClipRectangles()
+// cairo_get_dash  --> Dashes()
+// cairo_get_color_stop_rgba  --> ColorStops()
+/**
+ * A Graphics object uses its source to draw on its target. Its target is set
+ * when it is created, but its source can be changed whenever desired. For
+ * example, for a painting event, the target of a Graphics is the control
+ * being painted. In other cases it could be an image. Its source is usually a
+ * color, but could be a gradient, an image, or some other pattern.
+ *
+ * If the documentation here is not sufficient, cairo might have
+ * better documentation at $(LINK http://www.cairographics.org/manual/).
+ */
+class Graphics {
+private:
+	cairo_t* cr;
+public:
+	this(cairo_t* cr) {
+		this.cr = cr;
+		cairo_reference(cr);
+		checkStatus();
+	}
+	~this() {
+		cairo_destroy(cr);
+	}
+	/**
+	 * Returns: a pointer to the cairo context (cairo_t*) that backs this object
+	 */
+	cairo_t* handle() { return cr; }
+	void checkStatus() {
+		cairo_status_t status = cairo_status(cr);
+		if(status == CAIRO_STATUS_SUCCESS)
+			return;
+
+		Stdout("Cairo error: ")(cairo_status_to_string(status)).newline;
+		assert(0);
+	}
+	///
+	void moveTo(real x, real y) {
+		cairo_move_to(cr, x, y);
+	}
+	/// ditto
+	void moveTo(Point pt) {
+		moveTo(pt.x, pt.y);
+	}
+	///
+	void lineTo(real x, real y) {
+		cairo_line_to(cr, x, y);
+	}
+	/// ditto
+	void lineTo(Point pt) {
+		lineTo(pt.x, pt.y);
+	}
+	///
+	void curveTo(Point pt1, Point pt2, Point pt3) {
+		curveTo(pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y);
+	}
+	/// ditto
+	void curveTo(real x1, real y1, real x2, real y2, real x3, real y3) {
+		cairo_curve_to(cr, x1, y1, x2, y2, x3, y3);
+	}
+	///
+	void relMoveTo(real x, real y) {
+		cairo_rel_move_to(cr, x, y);
+	}
+	/// ditto
+	void relMoveTo(Point pt) {
+		relMoveTo(pt.x, pt.y);
+	}
+	///
+	void relLineTo(real x, real y) {
+		cairo_rel_line_to(cr, x, y);
+	}
+	/// ditto
+	void relLineTo(Point pt) {
+		relLineTo(pt.x, pt.y);
+	}
+	///
+	void relCurveTo(Point pt1, Point pt2, Point pt3) {
+		relCurveTo(pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y);
+	}
+	/// ditto
+	void relCurveTo(real x1, real y1, real x2, real y2, real x3, real y3) {
+		cairo_rel_curve_to(cr, x1, y1, x2, y2, x3, y3);
+	}
+	/**
+	 * Adds an arc to the current path. A line is added connecting the
+	 * current point to the beginning of the arc.
+	 * Example:
+	 * -----
+	 * graphics.moveTo(5, 5);
+	 * graphics.arc(50.5, 80.5, 40, 40, -0.2, PI/2);
+	 * graphics.stroke();
+	 * -----
+	 * $(IMAGE ../web/example_arc.png)
+	 */
+	void arc(Point ptc, real radius, real angle1, real angle2) {
+		arc(ptc.x, ptc.y, radius, angle1, angle2);
+	}
+	/// ditto
+	void arc(real xc, real yc, real radius, real angle1, real angle2) {
+		cairo_arc(cr, xc, yc, radius, angle1, angle2);
+	}
+	/// ditto
+	void arc(real xc, real yc, real xradius, real yradius, real angle1, real angle2) {
+		cairo_save(cr);
+		cairo_translate(cr, xc, yc);
+		cairo_scale(cr, xradius, yradius);
+		cairo_arc(cr, 0, 0, 1, angle1, angle2);
+		cairo_restore(cr);
+	}
+	/**
+	 * Adds an ellipse as a closed sub-path--a line will not connect it
+	 * to the current point.
+	 * Example:
+	 * -----
+	 * graphics.ellipse(70.5, 50.5, 60, 25);
+	 * graphics.stroke();
+	 * -----
+	 * $(IMAGE ../web/example_ellipse.png)
+	 */
+	void ellipse(real xc, real yc, real radius) {
+		cairo_new_sub_path(cr);
+		cairo_arc(cr, xc, yc, radius, 0, Pi * 2);
+		cairo_close_path(cr);
+	}
+	/// ditto
+	void ellipse(real xc, real yc, real xradius, real yradius) {
+		cairo_new_sub_path(cr);
+		arc(xc, yc, xradius, yradius, 0, Pi * 2);
+		cairo_close_path(cr);
+	}
+	/**
+	 * Adds a rectangle as a sub-path--a line will not connect it
+	 * to the current point.
+	 * Example:
+	 * -----
+	 * graphics.rectangle(5.5, 5.5, 70, 20);
+	 * graphics.stroke();
+	 * -----
+	 * $(IMAGE ../web/example_rectangle.png)
+	 */
+	void rectangle(Rect rect) {
+		rectangle(rect.x, rect.y, rect.width, rect.height);
+	}
+	/// ditto
+	void rectangle(real x, real y, real width, real height) {
+		cairo_rectangle(cr, x, y, width, height);
+	}
+	///
+	void closePath() {
+		cairo_close_path(cr);
+	}
+	/**
+	 * Draws the outline of the current path.
+	 * Example:
+	 * -----
+	 * graphics.moveTo(12.5, 14.5);
+	 * graphics.lineTo(123.5, 22.5);
+	 * graphics.lineTo(139.5, 108.5);
+	 * graphics.lineTo(49.5, 86.5);
+	 * graphics.closePath();
+	 * graphics.stroke();
+	 * -----
+	 * $(IMAGE ../web/example_stroke.png)
+	 */
+	void stroke() {
+		cairo_stroke(cr);
+	}
+	/**
+	 * Draws the inside of the current path.
+	 * Example:
+	 * -----
+	 * graphics.MoveTo(12.5, 14.5);
+	 * graphics.LineTo(123.5, 22.5);
+	 * graphics.LineTo(139.5, 108.5);
+	 * graphics.LineTo(49.5, 86.5);
+	 * graphics.ClosePath();
+	 * graphics.Fill();
+	 * -----
+	 * $(IMAGE ../web/example_fill.png)
+	 */
+	void fill() {
+		cairo_fill(cr);
+	}
+	/**
+	 * Paints the current source everywhere within the current clip region.
+	 * Examples:
+	 * -----
+	 * graphics.source = Color(255, 128, 0);
+	 * graphics.paint();
+	 * -----
+	 * $(IMAGE ../web/example_paint.png)
+	 */
+	void paint() {
+		cairo_paint(cr);
+	}
+	/**
+	 * Gets or sets the line _width used for stroking.
+	 * Example:
+	 * -----
+	 * graphics.ellipse(40.5, 30.5, 30, 20);
+	 * graphics.lineWidth = 1;
+	 * graphics.stroke();
+	 * graphics.ellipse(40.5, 80.5, 30, 20);
+	 * graphics.lineWidth = 5;
+	 * graphics.stroke();
+	 * -----
+	 * $(IMAGE ../web/example_line_width.png)
+	 */
+	real lineWidth() {
+		return cairo_get_line_width(cr);
+	}
+	/// ditto
+	void lineWidth(real width) {
+		cairo_set_line_width(cr, width);
+	}
+	/**
+	 * Gets or sets the line cap used for stroking.
+	 *
+	 * The line cap is only examined when the stroke is performed, not before.
+	 * Therefore, drawing two lines, each with a different line cap, would
+	 * require calling stroke twice.
+	 */
+	GraphicsLineCap lineCap() {
+		return cast(GraphicsLineCap)cairo_get_line_cap(cr);
+	}
+	/// ditto
+	void lineCap(GraphicsLineCap cap) {
+		cairo_set_line_cap(cr, cap);
+	}
+	/**
+	 * Sets the font size to the specified size in user space units, not
+	 * in points.
+	 */
+	void fontSize(real size) {
+		assert(size != 0);
+		cairo_set_font_size(cr, size);
+	}
+	/**
+	 * Set the font used to draw text.
+	 */
+	void font(Font f) {
+		assert(f.size != 0);
+		cairo_set_font_size(cr, f.size);
+		cairo_select_font_face(cr, toCharPtr(f.family), f.italic, f.bold);
+	}
+	// TODO: if text is all ascii, do fast path with no uniscribe
+	void drawText(string text, real x, real y) {
+		auto extents = getFontExtents;
+		cairo_font_extents_t fextents;
+		cairo_font_extents(cr, &fextents);
+		cairo_move_to(cr, x, y + fextents.ascent);
+		cairo_show_text(cr, toCharPtr(text)); // 99% of time spent in show_text
+		checkStatus();
+	}
+	///
+	Size getTextExtents(string text) {
+		cairo_text_extents_t textents;
+		cairo_text_extents(cr, toCharPtr(text), &textents);
+		cairo_font_extents_t fextents;
+		cairo_font_extents(cr, &fextents);
+		return Size(textents.x_advance, fextents.height);
+	}
+	///
+	FontExtents getFontExtents() {  // TODO: make property?
+		cairo_font_extents_t fextents;
+		cairo_font_extents(cr, &fextents);
+		FontExtents extents;
+		extents.ascent = fextents.ascent;
+		extents.descent = fextents.descent;
+		extents.height = fextents.height;
+		extents.maxAdvance = fextents.max_x_advance;
+		return extents;
+	}
+	///
+	Rect getClipExtents() {  // TODO: make property?
+		double x, y, width, height;
+		cairo_clip_extents(cr, &x, &y, &width, &height);
+		return Rect(x, y, width, height);
+	}
+	///
+	void save() {
+		cairo_save(cr);
+	}
+	///
+	void restore() {
+		cairo_restore(cr);
+		checkStatus();
+	}
+	///
+	void clip() {
+		cairo_clip(cr);
+	}
+	///
+	void translate(Point pt) {
+		translate(pt.x, pt.y);
+	}
+	/// ditto
+	void translate(real x, real y) {
+		cairo_translate(cr, x, y);
+	}
+	///
+	void scale(real x, real y) {
+		cairo_scale(cr, x, y);
+	}
+	///
+	void rotate(real angle) {
+		cairo_rotate(cr, angle);
+	}
+	///
+	GraphicsOperator operator() {
+		return cast(GraphicsOperator)cairo_get_operator(cr);
+	}
+	///
+	void operator(GraphicsOperator op) {
+		cairo_set_operator(cr, cast(cairo_operator_t)op);
+	}
+	/**
+	 * Sets the dash pattern to be used when lines are drawn.
+	 */
+	void setDash(real[] dashes, real offset) {
+		auto cdashes = new double[dashes.length];
+		foreach(int i, real r; dashes)
+			cdashes[i] = r;
+		cairo_set_dash(cr, cdashes.ptr, cdashes.length, offset);
+	}
+	/**
+	 * Gets or sets the fill rule the current fill rule.
+	 * The default is GraphicsFillRule.Winding.
+	 */
+	GraphicsFillRule fillRule() {
+		return cast(GraphicsFillRule)cairo_get_fill_rule(cr);
+	}
+	/// ditto
+	void fillRule(GraphicsFillRule rule) {
+		cairo_set_fill_rule(cr, cast(cairo_fill_rule_t)rule);
+	}
+	/**
+	 * The temporary surface created will be the same size as the current clip. To speed up using this function, call Clip() to the area you will be drawing in.
+	 */
+	void pushGroup() {
+		cairo_push_group(cr);
+	}
+	//popGroup() { // TODO: returning a pattern
+	//	cairo_pop_group(cr);
+	//}
+	/**
+	 * Terminates the redirection begun by a call to PushGroup() or
+	 * PushGroupWithContent() and installs the resulting pattern as the
+	 * source pattern.
+	 */
+	void popGroupToSource() {
+		cairo_pop_group_to_source(cr);
+		checkStatus();
+	}
+	// TODO: figure out the best way to set the source and get the source
+	void source(Color c) {
+		cairo_set_source_rgba(cr, c.R/255.0, c.G/255.0, c.B/255.0, c.A/255.0);
+	}
+	//void source(Pattern s) {}
+	//void setSource(Surface s, real x = 0, real y = 0) {}
+	// TODO: remove this function and have users do:
+	// g.setSource(img, x, y);
+	// g.paint();
+	// ???
+	// paintSource(Image, real, real) ?
+	/// Draws the specified image unscaled.
+	void drawImage(Image image, real x, real y) {
+		auto surface= cairo_image_surface_create_for_data(cast(char*)image.data,
+			CAIRO_FORMAT_ARGB32, image.width, image.height, image.width*4);
+		save();
+		cairo_set_source_surface(cr, surface, x, y);
+		cairo_paint(cr);
+		restore();
+		cairo_surface_destroy(surface);
+	}
+	// Draws the specified image scaled to the specified width and height.
+	//void drawImage(Image image, real x, real y, real width, real height);
+}