mass rename and rebranding from xwt to ibex - fixed to use ixt files
[org.ibex.core.git] / src / org / ibex / plat / Win32.cc
diff --git a/src/org/ibex/plat/Win32.cc b/src/org/ibex/plat/Win32.cc
new file mode 100644 (file)
index 0000000..fda911b
--- /dev/null
@@ -0,0 +1,834 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [LGPL]
+#include "GCJ.cc"
+
+// we have to do this because the jpeg libraries use the symbol 'INT32'
+#define INT32 WIN32_INT32
+
+// this has to precede the others so we don't get collisions on min/max
+#include <org/ibex/js/JS.h>
+#include <org/ibex/Box.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <windows.h>
+#include <mmsystem.h>
+#undef STRICT
+#undef MAX_PRIORITY
+#undef MIN_PRIORITY
+#undef NORM_PRIORITY
+
+#include <gcj/cni.h>
+
+#include <java/lang/Integer.h>
+#include <java/util/Hashtable.h>
+#include <org/ibex/Box.h>
+#include <org/ibex/Surface.h>
+#include <org/ibex/PixelBuffer.h>
+#include <org/ibex/Picture.h>
+#include <org/ibex/Platform.h>
+#include <org/ibex/plat/Win32.h>
+#include <org/ibex/plat/Win32$Win32Surface.h>
+#include <org/ibex/plat/Win32$Win32PixelBuffer.h>
+#include <org/ibex/plat/Win32$Win32Picture.h>
+#include <org/ibex/util/Log.h>
+#include <org/ibex/util/Semaphore.h>
+
+// for debugging
+#include <java/lang/System.h>
+#include <java/io/PrintStream.h>
+
+#define WM_USER_SETCURSOR WM_USER
+#define WM_USER_DISPOSE (WM_USER + 1)
+#define WM_USER_CREATEWINDOW (WM_USER + 2)
+#define WS_NORMAL (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
+
+// FEATURE: there are lots of places where HANDLE's get casted to jint's -- this will break on Win64
+//          a clean way to do this would be to '#define jraw (gnu::gcj::RawData*)'
+
+// Callbacks ////////////////////////////////////////////////////////////////////
+
+LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
+    org::ibex::plat::Win32$Win32Surface* surface =
+        (org::ibex::plat::Win32$Win32Surface*)org::ibex::plat::Win32::hwndToWin32SurfaceMap->get(new java::lang::Integer((jint)hwnd));
+
+    if (surface != NULL) {
+        return (LRESULT)surface->WndProc((jint)hwnd, (jint)iMsg, (jint)wParam, (jint)lParam);
+
+    } else {
+        // this is really lame -- Win32 insists on being able to call your WndProc BEFORE CreateWindow returns...
+        return DefWindowProc(hwnd, iMsg, wParam, lParam);
+    }
+}
+
+
+// Initialization ////////////////////////////////////////////////////////////////////
+
+static int window_class_counter = 0;
+
+jstring org::ibex::plat::Win32::getTempPath() {
+    char buf[1024];
+    DWORD ret = GetTempPath(1024, buf);
+    if (ret == 0) criticalAbort(JvNewStringLatin1("GetTempPath() failed"));
+    return JvNewStringLatin1(buf);
+}
+
+// XOR mask for the hand cursor
+static unsigned char hand_cursor_xor[32 * 4] = {
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x0C, 0x00, 0x00,
+  0x00, 0x0C, 0x00, 0x00,
+  0x00, 0x0C, 0x00, 0x00,
+  0x00, 0x0C, 0x00, 0x00,
+  0x00, 0x0C, 0x00, 0x00,
+  0x00, 0x0D, 0x80, 0x00,
+  0x00, 0x0D, 0xB0, 0x00,
+  0x00, 0x0D, 0xB4, 0x00,
+  0x00, 0x0D, 0xB6, 0x00,
+  0x00, 0xCF, 0xF6, 0x00,
+  0x00, 0xEF, 0xFE, 0x00,
+  0x00, 0x6F, 0xFE, 0x00,
+  0x00, 0x2F, 0xFE, 0x00,
+  0x00, 0x3F, 0xFE, 0x00,
+  0x00, 0x1F, 0xFE, 0x00,
+  0x00, 0x1F, 0xFC, 0x00,
+  0x00, 0x0F, 0xFC, 0x00,
+  0x00, 0x0F, 0xFC, 0x00,
+  0x00, 0x07, 0xF8, 0x00,
+  0x00, 0x07, 0xF8, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00
+};
+
+// AND mask for the hand cursor
+static unsigned char hand_cursor_and[32 * 4] = {
+  0xFF, 0xF3, 0xFF, 0xFF,
+  0xFF, 0xE1, 0xFF, 0xFF,
+  0xFF, 0xE1, 0xFF, 0xFF,
+  0xFF, 0xE1, 0xFF, 0xFF,
+  0xFF, 0xE1, 0xFF, 0xFF,
+  0xFF, 0xE0, 0x7F, 0xFF,
+  0xFF, 0xE0, 0x0F, 0xFF,
+  0xFF, 0xE0, 0x03, 0xFF,
+  0xFF, 0xE0, 0x01, 0xFF,
+  0xFF, 0x20, 0x00, 0xFF,
+  0xFE, 0x00, 0x00, 0xFF,
+  0xFE, 0x00, 0x00, 0xFF,
+  0xFF, 0x00, 0x00, 0xFF,
+  0xFF, 0x80, 0x00, 0xFF,
+  0xFF, 0x80, 0x00, 0xFF,
+  0xFF, 0xC0, 0x00, 0xFF,
+  0xFF, 0xC0, 0x01, 0xFF,
+  0xFF, 0xE0, 0x01, 0xFF,
+  0xFF, 0xE0, 0x01, 0xFF,
+  0xFF, 0xF0, 0x03, 0xFF,
+  0xFF, 0xF0, 0x03, 0xFF,
+  0xFF, 0xF0, 0x03, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF
+};
+
+void org::ibex::plat::Win32::natPreInit() {
+    if (org::ibex::util::Log::verbose) {
+        AllocConsole();
+        freopen("CONOUT$", "w+t", stderr);
+    }
+}
+
+void org::ibex::plat::Win32::natInit() {
+
+    // grab desktop dc/handle
+    desktop_handle = (jint)GetDesktopWindow();
+    desktop_dc = (jint)GetDC((HWND)desktop_handle);
+
+    // create cursors
+    org::ibex::plat::Win32::wait_cursor = (jint)LoadCursor(NULL, IDC_WAIT);
+    org::ibex::plat::Win32::default_cursor = (jint)LoadCursor(NULL, IDC_ARROW);
+    org::ibex::plat::Win32::crosshair_cursor = (jint)LoadCursor(NULL, IDC_CROSS);
+    org::ibex::plat::Win32::text_cursor = (jint)LoadCursor(NULL, IDC_IBEAM);
+    org::ibex::plat::Win32::move_cursor = (jint)LoadCursor(NULL, IDC_SIZEALL);
+    org::ibex::plat::Win32::sizenesw_cursor = (jint)LoadCursor(NULL, IDC_SIZENESW);
+    org::ibex::plat::Win32::sizens_cursor = (jint)LoadCursor(NULL, IDC_SIZENS);
+    org::ibex::plat::Win32::sizenwse_cursor = (jint)LoadCursor(NULL, IDC_SIZENWSE);
+    org::ibex::plat::Win32::sizewe_cursor = (jint)LoadCursor(NULL, IDC_SIZEWE);
+    org::ibex::plat::Win32::hand_cursor = (jint)CreateCursor(GetModuleHandle(NULL), 14, 1, 32, 32, hand_cursor_and, hand_cursor_xor);
+
+    messagePumpThread = (jint)GetCurrentThreadId();
+    messagePumpStarted->release();
+
+    MSG msg;
+    while(GetMessage(&msg, (HWND)NULL, 0, 0) > 0) {
+
+        if (msg.message == WM_USER_CREATEWINDOW) {
+            org::ibex::plat::Win32$Win32Surface *surface = (org::ibex::plat::Win32$Win32Surface*)msg.lParam;
+
+            // we must create a unique window class name for each
+            // window so that minimization icons can be set independantly
+            char buf[255];
+            sprintf(buf, "Ibex_WINDOW_CLASS_%i", window_class_counter++);
+
+            WNDCLASSEX wc;
+            wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
+            wc.lpfnWndProc = WndProc;
+            wc.cbClsExtra = 0;
+            wc.cbSize = sizeof(WNDCLASSEX);
+            wc.cbWndExtra = 0;
+            wc.hInstance = GetModuleHandle(NULL);
+            wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+            wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL),
+                                          MAKEINTRESOURCE(5),
+                                          IMAGE_ICON,
+                                          GetSystemMetrics(SM_CXSMICON),
+                                          GetSystemMetrics(SM_CYSMICON),
+                                          LR_DEFAULTCOLOR);
+            wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+            wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
+            wc.lpszMenuName = "menu";
+            wc.lpszClassName = buf;
+            RegisterClassEx(&wc);
+
+            surface->hwnd = (jint)CreateWindow(wc.lpszClassName, TEXT(""),
+                                               msg.wParam ? WS_NORMAL : WS_POPUP,
+                                               200, 200, 100, 100,
+                                               (HWND__*)NULL, (HMENU__*)NULL,
+                                               GetModuleHandle(NULL), (LPVOID)NULL);
+            
+            SetFocus((HWND)surface->hwnd);
+            surface->hwndCreated->release();
+            
+        } else {
+            TranslateMessage(&msg);
+            if (msg.message == WM_LBUTTONDOWN || msg.message == WM_RBUTTONDOWN || msg.message == WM_MBUTTONDOWN) SetFocus(msg.hwnd);
+            DispatchMessage(&msg);
+        }
+
+    }
+    java::lang::System::exit(-1);
+}
+
+
+// Platform Methods ///////////////////////////////////////////////////////////////////
+
+jstring org::ibex::plat::Win32::_getEnv(jstring key) {
+    int len = JvGetStringUTFLength(key);
+    char buf[len + 1];
+    JvGetStringUTFRegion(key, 0, len, buf);
+    buf[len] = '\0';
+    char buf2[1024];
+    DWORD ret = GetEnvironmentVariable(buf, buf2, 1024);
+    if (ret > 0 && ret < 1024) return JvNewStringLatin1(buf2);
+    return NULL;
+}
+
+jstring org::ibex::plat::Win32::_fileDialog(jstring suggestedFileName, jboolean write) {
+
+    char buf[1024];
+    OPENFILENAME ofn;
+    memset(buf, 0, 1024);
+    memset(&ofn, 0, sizeof(OPENFILENAME));
+
+    if (suggestedFileName != NULL)
+        JvGetStringUTFRegion(suggestedFileName, 0, min(1023, JvGetStringUTFLength(suggestedFileName)), buf);
+    
+    ofn.lStructSize = sizeof(OPENFILENAME);
+    ofn.nMaxCustFilter = 0;
+    ofn.lpstrFile = buf;
+    ofn.nMaxFile = 1024;
+
+    if (write) ofn.Flags |= OFN_OVERWRITEPROMPT;
+    ofn.Flags |= OFN_HIDEREADONLY;
+
+    int ret = write ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn);
+    return ret == 0 ? NULL : JvNewStringLatin1(buf);
+}
+
+void org::ibex::plat::Win32::__detectProxy(JArray<jstring>* container) {
+
+    HKEY hkey;
+    char buf[1024];
+    DWORD buflen = 1024;
+    DWORD type;
+    LONG result = RegOpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", &hkey);
+    if (result != ERROR_SUCCESS) return;
+    
+    buf[0] = '\0';
+    type = REG_SZ;
+    buflen = 1024;
+    result = RegQueryValueEx(hkey, "AutoConfigURL", NULL, &type, (LPBYTE)buf, &buflen);
+    buf[buflen] = '\0';
+    if (result == ERROR_SUCCESS) elements(container)[2] = JvNewStringLatin1(buf);
+
+    buf[0] = '\0';
+    type = REG_BINARY;
+    RegQueryValueEx(hkey, "ProxyEnable", NULL, &type, (LPBYTE)buf, &buflen);
+    if (buf[0] != 1) return;
+
+    buf[0] = '\0';
+    type = REG_SZ;
+    buflen = 1024;
+    RegQueryValueEx(hkey, "ProxyServer", NULL, &type, (LPBYTE)buf, &buflen);
+    buf[buflen] = '\0';
+    elements(container)[0] = JvNewStringLatin1(buf);
+
+    buf[0] = '\0';
+    buflen = 1024;
+    RegQueryValueEx(hkey, "ProxyOverride", NULL, &type, (LPBYTE)buf, &buflen);
+    buf[buflen] = '\0';
+    elements(container)[1] = JvNewStringLatin1(buf);
+}
+
+jstring org::ibex::plat::Win32::_getClipBoard() {
+    OpenClipboard((HWND)desktop_handle);
+    HGLOBAL hmem = GetClipboardData(CF_TEXT);
+    if (hmem == NULL) return NULL;
+    char* buf = (char*)GlobalLock(hmem);
+    if (buf == NULL) return NULL;
+    jstring ret = JvNewStringLatin1(buf);
+    GlobalUnlock(hmem);
+    CloseClipboard();
+    return ret;
+}
+
+void org::ibex::plat::Win32::_setClipBoard(jstring s) {
+    OpenClipboard((HWND)desktop_handle);
+    HGLOBAL hmem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, JvGetStringUTFLength(s) + 1);
+    if (hmem == NULL) return;
+    char* buf = (char*)GlobalLock(hmem);
+    if (buf == NULL) return;
+    JvGetStringUTFRegion(s, 0, JvGetStringUTFLength(s), buf);
+    buf[JvGetStringUTFLength(s)] = '\0';
+    GlobalUnlock(hmem);
+    SetClipboardData(CF_TEXT, hmem);
+    CloseClipboard();
+}
+
+void org::ibex::plat::Win32::_criticalAbort(jstring message) {
+    char buf[JvGetStringUTFLength(message) + 1];
+    buf[JvGetStringUTFLength(message)] = '\0';
+    JvGetStringUTFRegion(message, 0, JvGetStringUTFLength(message), buf);
+    MessageBox (NULL, buf, "Ibex Cannot Continue", MB_OK | MB_ICONSTOP | MB_TASKMODAL | MB_SETFOREGROUND);
+    java::lang::System::exit(-1);
+}
+
+jint org::ibex::plat::Win32::_getScreenWidth() {
+    RECT rect;
+    SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
+    return rect.right - rect.left;
+}
+
+jint org::ibex::plat::Win32::_getScreenHeight() {
+    RECT rect;
+    SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
+    return rect.bottom - rect.top;
+}
+
+jboolean org::ibex::plat::Win32::_newBrowserWindow_(jstring url) {
+
+    int len = min(2048, JvGetStringUTFLength(url));
+    char buf[len + 1];
+    JvGetStringUTFRegion(url, 0, len, buf);
+    buf[len] = '\0';
+
+    SHELLEXECUTEINFO ei;
+    memset(&ei, 0, sizeof(ei));
+    ei.cbSize = sizeof(ei);
+    ei.lpVerb = "open";
+    ei.lpFile = buf;
+    ei.fMask  = SEE_MASK_NOCLOSEPROCESS;
+    ei.nShow  = SW_SHOWDEFAULT;
+    return (ShellExecuteEx(&ei) == 0);
+
+}
+
+
+// Win32PixelBuffer /////////////////////////////////////////////////////////////////////////
+
+// This is a scratch area; when blitting a translucent image, we copy the underlying data here first.
+// Since all drawing operations are single-threaded, it's safe to use a global here.
+static HBITMAP scratch = NULL;
+static HDC scratch_dc = NULL;
+static jint* scratch_bits = NULL;
+static jint scratch_w = 0;
+static jint scratch_h = 0;
+
+#define max(a,b) ((a)>(b)?(a):(b))
+#define min(a,b) ((a)<(b)?(a):(b))
+
+void org::ibex::plat::Win32$Win32PixelBuffer::drawPicture(org::ibex::Picture* source0,
+                                                         jint dx, jint dy,
+                                                         jint cx1, jint cy1, jint cx2, jint cy2,
+                                                         jint rgb, jboolean alphaOnly) {
+    org::ibex::plat::Win32$Win32Picture* source = (org::ibex::plat::Win32$Win32Picture*)source0;
+
+    cx1 = max(dx, cx1);
+    cy1 = max(dy, cy1);
+    cx2 = min(dx + source->getWidth(), cx2);
+    cy2 = min(dy + source->getHeight(), cy2);
+    if (cx1 >= cx2 || cy1 >= cy2) return; 
+
+    if (source->hasalpha) {
+
+        if (scratch == NULL || scratch_w < cx2 - cx1 || scratch_h < cy2 - cy1) {
+            if (scratch_dc != NULL) DeleteDC(scratch_dc);
+            if (scratch != NULL) DeleteObject(scratch);
+            scratch_w = max(cx2 - cx1, scratch_w);
+            scratch_h = max(cy2 - cy1, scratch_h);
+
+            BITMAPINFO bitmapinfo;
+            memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
+            bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+            bitmapinfo.bmiHeader.biWidth = scratch_w;
+            bitmapinfo.bmiHeader.biHeight = -1 * scratch_h;
+            bitmapinfo.bmiHeader.biPlanes = 1;
+            bitmapinfo.bmiHeader.biBitCount = 32;
+            bitmapinfo.bmiHeader.biCompression = BI_RGB;
+
+            // create section DIB
+            scratch = CreateDIBSection(NULL, &bitmapinfo, DIB_RGB_COLORS, (void**)&scratch_bits, NULL, 0);
+            scratch_dc = CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
+            SelectObject(scratch_dc, scratch);
+        }
+
+        // copy from screen to scratch
+        BitBlt((HDC)scratch_dc, 0, 0, cx2 - cx1, cy2 - cy1, (HDC)hdc, cx1, cy1, SRCCOPY);
+
+        // apply alpha-blending to scratch
+        jint* dat = elements(source->data);
+
+        for(int x = cx1; x < cx2; x++)
+            for(int y = cy1; y < cy2; y++) {
+                jint dst = scratch_bits[(y - dy) * scratch_w + (x - dx)];
+
+                // FEATURE: see if we can leverage GDI to do something more clever here with alphaOnly
+                jint src = alphaOnly ? rgb : dat[(y - dy) * source->getWidth() + x - dx];
+                jint alpha = (dat[(y - dy) * source->getWidth() + x - dx] & 0xFF000000) >> 24;
+                jint r = (((src & 0x00FF0000) >> 16) * alpha + ((dst & 0x00FF0000) >> 16) * (0xFF - alpha)) / 0xFF;
+                jint g = (((src & 0x0000FF00) >> 8)  * alpha + ((dst & 0x0000FF00) >> 8)  * (0xFF - alpha)) / 0xFF;
+                jint b = (((src & 0x000000FF))       * alpha + ((dst & 0x000000FF))       * (0xFF - alpha)) / 0xFF;
+                scratch_bits[(y - dy) * scratch_w + (x - dx)] = (r << 16) | (g << 8) | b;
+            }
+
+        // copy back from scratch to screen
+        BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)scratch_dc, 0, 0, SRCCOPY);
+
+    } else {
+
+        // FIXME: support alphaOnly case here
+        if (source->hasmask) {
+            BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->maskdc, cx1 - dx, cy1 - dy, SRCAND);
+            BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->hdc, cx1 - dx, cy1 - dy, SRCPAINT);
+        } else {
+            BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->hdc, cx1 - dx, cy1 - dy, SRCCOPY);
+        }
+
+    }
+
+}
+
+void org::ibex::plat::Win32$Win32PixelBuffer::fillRect(jint x, jint y, jint x2, jint y2, jint color) {
+    jint w = x2 - x;
+    jint h = y2 - y;
+
+    // sadly, the ability to change the color of a brush didn't arrive until Win2k...
+    HBRUSH brush = CreateSolidBrush(PALETTERGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
+    if (!brush) return;
+    RECT rect = { x, y, x + w, y + h };
+    FillRect((HDC)hdc, &rect, brush);
+    DeleteObject(brush);
+}
+
+void org::ibex::plat::Win32$Win32Surface::blit(org::ibex::PixelBuffer* s, jint sx, jint sy, jint dx, jint dy, jint dx2, jint dy2) {
+    // we create the DC lazily to get around some strange race condition in WinXP
+    if (hdc == 0) hdc = (jint)GetDC((HWND)hwnd);
+    BitBlt((HDC)hdc, dx, dy, dx2 - dx, dy2 - dy, (HDC)(((org::ibex::plat::Win32$Win32PixelBuffer*)s)->hdc), sx, sy, SRCCOPY);
+}
+
+void org::ibex::plat::Win32$Win32PixelBuffer::natInit() {
+    hbitmap = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, w, h);
+    hdc = (jint)CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
+    SetBkMode((HDC)hdc, TRANSPARENT);
+    SelectObject((HDC)hdc, (HBITMAP)hbitmap);
+}
+
+void org::ibex::plat::Win32$Win32PixelBuffer::finalize() {
+    DeleteObject((void*)hdc);
+    DeleteObject((void*)hbitmap);
+}
+
+
+
+// Win32Picture /////////////////////////////////////////////////////////////////////////
+
+void org::ibex::plat::Win32$Win32Picture::natInit() {
+
+    BITMAPINFO bitmapinfo;
+    memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
+    bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
+    bitmapinfo.bmiHeader.biWidth = width;
+    bitmapinfo.bmiHeader.biHeight = -1 * height;
+    bitmapinfo.bmiHeader.biPlanes = 1;
+    bitmapinfo.bmiHeader.biBitCount = 32;
+    bitmapinfo.bmiHeader.biCompression = BI_RGB;
+
+    hbitmap = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, width, height);
+    hdc = (jint)CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
+    SelectObject((HDC)hdc, (HBITMAP)hbitmap);
+    uint32_t* dat = (uint32_t*)elements(data);
+    for(int i=0; i<data->length; i++) if ((dat[i] & 0xFF000000) == 0x00000000) dat[i] = 0x00000000;
+    StretchDIBits((HDC)hdc, 0, 0, width, height, 0, 0, width, height, elements(data), &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
+
+    jint _copy[min(1024, data->length)];
+    jint* copy = data->length > 1024 ? (jint*)malloc(data->length * 4) : _copy;
+
+    memcpy(copy, elements(data), data->length * 4);
+    for(int i=0; i<data->length; i++)
+        if ((copy[i] & 0xFF000000) == 0x00000000) {
+            hasmask = 1;
+            copy[i] = 0x00FFFFFF;
+        } else if ((copy[i] & 0xFF000000) == 0xFF000000) {
+            copy[i] = 0x00000000;
+        } else {
+            hasalpha = 1;
+            hasmask = 0;
+            if (data->length > 1024) free(copy);
+            return;
+        }
+
+    if (!hasmask) {
+        if (data->length > 1024) free(copy);
+        return;
+    }
+
+    //    hmask = (jint)CreateBitmap(w, h, 1, 1, NULL);
+    hmask = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, width, height);
+    maskdc = (jint)CreateCompatibleDC(NULL);
+    SelectObject((HDC)maskdc, (HBITMAP)hmask);
+    StretchDIBits((HDC)maskdc, 0, 0, width, height, 0, 0, width, height, copy, &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
+    if (data->length > 1024) free(copy);
+}
+
+
+
+// Win32Surface /////////////////////////////////////////////////////////////////////////////
+
+void org::ibex::plat::Win32$Win32Surface::natInit(jboolean framed) {
+
+    // Ask the message-handling thread to create a window for us
+    PostThreadMessage((DWORD)org::ibex::plat::Win32::messagePumpThread, WM_USER + 2, (WPARAM)framed, (LPARAM)this);
+    hwndCreated->block();
+
+    // turn on incremental GC now that we have a user-visible interface
+    // [[this is causing segfaults; enable it later...]]
+    // GC_enable_incremental();
+
+    ShowWindow ((HWND)hwnd, SW_SHOWDEFAULT);
+}
+
+void org::ibex::plat::Win32$Win32Surface::finalize() { /* DeleteObject((void*)hwnd); */ }
+void org::ibex::plat::Win32$Win32Surface::toFront() { BringWindowToTop((HWND)hwnd); }
+void org::ibex::plat::Win32$Win32Surface::toBack() { SetWindowPos((HWND)hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); }
+void org::ibex::plat::Win32$Win32Surface::_dispose() { PostMessage((HWND)hwnd, WM_USER_DISPOSE, 0, 0); }
+void org::ibex::plat::Win32$Win32Surface::setInvisible(jboolean h) { ShowWindow((HWND)hwnd, h ? SW_HIDE : SW_SHOWNORMAL); }
+void org::ibex::plat::Win32$Win32Surface::_setMinimized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMINIMIZED : SW_SHOWNORMAL); }
+void org::ibex::plat::Win32$Win32Surface::_setMaximized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); }
+void org::ibex::plat::Win32$Win32Surface::postCursorChange() { PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0); }
+
+void org::ibex::plat::Win32$Win32Surface::setLocation() {
+    POINT point;
+    RECT rect;
+    point.x = 0;
+    point.y = 0;
+    ClientToScreen((HWND)hwnd, &point);
+    GetWindowRect((HWND)hwnd, &rect);
+    SetWindowPos((HWND)hwnd, NULL, root->x - (point.x - rect.left), root->y - (point.y - rect.top), 0, 0, SWP_NOZORDER | SWP_NOSIZE);
+}
+
+void org::ibex::plat::Win32$Win32Surface::_setSize(jint w, jint h) {
+    RECT client_rect, window_rect;
+    GetClientRect((HWND)hwnd, &client_rect);
+    GetWindowRect((HWND)hwnd, &window_rect);
+    int width = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left) + w;
+    int height = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top) + h;
+    SetWindowPos((HWND)hwnd, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE);
+}
+
+void org::ibex::plat::Win32$Win32Surface::setTitleBarText(java::lang::String* title) {
+    int len = min(1024, JvGetStringUTFLength(title));
+    char buf[len + 1];
+    buf[len] = '\0';
+    JvGetStringUTFRegion(title, 0, len, buf);
+    SetWindowText((HWND)hwnd, buf);
+}
+
+void org::ibex::plat::Win32$Win32Surface::setIcon(org::ibex::Picture* p0) {
+
+    org::ibex::plat::Win32$Win32Picture* p = (org::ibex::plat::Win32$Win32Picture*)p0;
+    int icon_width = GetSystemMetrics(SM_CXSMICON);
+    int icon_height = GetSystemMetrics(SM_CYSMICON);
+
+    // we create the DC lazily to get around some strange race condition in WinXP
+    if (hdc == 0) hdc = (jint)GetDC((HWND)hwnd);
+
+    // create the bitmap
+    HBITMAP bit = CreateCompatibleBitmap((HDC)hdc, icon_width, icon_height);
+    HDC memdc = CreateCompatibleDC((HDC)hdc);
+    SelectObject(memdc, bit);
+    BitBlt((HDC)memdc, 0, 0, icon_width, icon_height, (HDC)(p->hdc), 0, 0, SRCCOPY);
+
+    // create the mask
+    jint* dat = elements(p->data);
+    HBITMAP bit_mask = CreateBitmap(icon_width, icon_height * 2, 1, 1, NULL);
+    SelectObject(memdc, bit_mask);
+    for(int x=0; x<icon_width; x++)
+        for(int y=0; y<icon_height; y++) {
+            int source = dat[(x * p->getWidth()) / icon_width + ((y * p->getHeight()) / icon_height) * (p->getWidth())];
+            if ((source & 0xFF000000) != 0x00000000) SetPixel(memdc, x, y, PALETTERGB(0, 0, 0));
+            else SetPixel(memdc, x, y, PALETTERGB(255, 255, 255));
+        }
+
+    // instantiate the icon and assign it to the window class
+    ICONINFO ici;
+    ici.fIcon = 1;
+    ici.hbmMask = bit_mask;
+    ici.hbmColor = bit;
+    HICON hicon = CreateIconIndirect(&ici);
+    HICON oldicon = (HICON)SetClassLong((HWND)hwnd, GCL_HICONSM, (LONG)hicon);
+    if (oldicon != NULL) DestroyIcon(oldicon);
+    DeleteObject(memdc);
+}
+
+static jstring keyToString(WPARAM wParam);
+jint org::ibex::plat::Win32$Win32Surface::WndProc(jint _hwnd, jint _iMsg, jint _wParam, jint _lParam) {
+
+    UINT iMsg = (UINT)_iMsg;
+    WPARAM wParam = (WPARAM)_wParam;
+    LPARAM lParam = (LPARAM)_lParam;
+
+    int oldmousex, oldmousey;
+    MINMAXINFO* mmi;
+    int resizable;
+    POINT point;
+    HWND hwnd2;
+    RECT rect, rect2;
+    RECT client_rect, window_rect;
+    jboolean newinside;
+    int16_t mouse_x;
+    int16_t mouse_y;
+    int addwidth, addheight;
+
+    switch(iMsg) {
+    case WM_DEVMODECHANGE: break;    // FEATURE: color depth changed
+    case WM_DISPLAYCHANGE: break;    // FEATURE: screen size changed
+    case WM_MOUSEWHEEL: break;       // FEATURE: Mouse Wheel
+
+    case WM_SYSKEYDOWN: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
+    case WM_KEYDOWN:
+        KeyPressed(keyToString(wParam));
+        return 0;
+
+    case WM_SYSKEYUP: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
+    case WM_KEYUP:
+        KeyReleased(keyToString(wParam));
+        return 0;
+
+    case WM_SETFOCUS: Focused(true); return 0;
+    case WM_KILLFOCUS: Focused(false); return 0;
+    case WM_LBUTTONDBLCLK: DoubleClick(1); return 0;
+    case WM_RBUTTONDBLCLK: DoubleClick(2); return 0;
+    case WM_MBUTTONDBLCLK: DoubleClick(3); return 0;
+    case WM_LBUTTONDOWN: Press(1); return 0;
+    case WM_RBUTTONDOWN: Press(2); return 0;
+    case WM_MBUTTONDOWN: Press(3); return 0;
+    case WM_CLOSE: Close(); return 0;
+    case WM_ERASEBKGND: return 0;
+
+    case WM_LBUTTONUP:
+    case WM_RBUTTONUP:
+    case WM_MBUTTONUP:
+        Release(iMsg == WM_LBUTTONUP ? 1 : (iMsg == WM_RBUTTONUP ? 2 : 3));
+        if (captured && !inside) {
+            ReleaseCapture();
+            SetCursor((HCURSOR)previous_cursor);
+        }
+        return 0;
+
+    case WM_MOVING:
+        GetClientRect((HWND)hwnd, &rect);
+        PosChange(rect.left, rect.top);
+        return 0;
+
+    case WM_SIZE:
+        if (wParam == SIZE_MINIMIZED) {
+            if (maximized) Maximized(false);
+            Minimized(true);
+        } else if (wParam == SIZE_MAXIMIZED) {
+            if (minimized) Minimized(false); 
+            Maximized(true);
+        } else {
+            if (minimized) Minimized(false); 
+            if (maximized) Maximized(false);
+        }
+        // deliberately fall through to WM_SIZING
+
+    case WM_SIZING:
+        GetClientRect((HWND)hwnd, &rect);
+        SizeChange(rect.right - rect.left, rect.bottom - rect.top);
+        return 0;
+
+    case WM_MOUSEMOVE:
+        if (mousex == lParam & 0xFFFF && mousey == (lParam & 0xFFFF0000) >> 16) return 0;
+
+        GetClientRect((HWND)hwnd, &rect);
+        point.x = mouse_x = lParam & 0xFFFF;
+        point.y = mouse_y = (lParam & 0xFFFF0000) >> 16;
+        ClientToScreen((HWND)hwnd, &point);
+        hwnd2 = WindowFromPoint(point);
+
+        newinside = hwnd2 == (HWND)hwnd && mouse_x > 0 && mouse_y > 0 && mouse_x < (rect.right - rect.left) && mouse_y < (rect.bottom - rect.top) ? 1 : 0;
+
+        Move(mouse_x, mouse_y);
+
+        if (newinside && !inside) {
+            inside = 1;
+            SetCapture((HWND)hwnd);
+            captured = 1;
+            previous_cursor = (jint)GetCursor();
+            PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0);
+
+        } else if (!newinside && inside) {
+            inside = 0;
+            if (!button1 && !button2 && !button3) {
+                ReleaseCapture();
+                captured = 0;
+                SetCursor((HCURSOR)previous_cursor);
+            }
+        }
+
+        return 0;
+
+    case WM_USER_SETCURSOR:
+        // I can't find documentation of this anywhere, but in my experience, an event must be received between an invocation
+        // of SetCapture() and SetCursor(). Furthermore, it seems that after calling SetCursor(), the cursor will not change until a
+        // return from WndProc (ie the completion of the processing of some event).  Hence, we use WM_USER to indicate that the screen
+        // cursor should be re-set to "current_cursor".
+        SetCursor((HCURSOR)current_cursor);
+        return 0;
+
+    case WM_USER_DISPOSE:
+        // used to signal that we should destroy ourselves
+        DestroyWindow((HWND)hwnd);
+        return 0;
+
+    case WM_GETMINMAXINFO:
+        GetClientRect((HWND)hwnd, &client_rect);
+        GetWindowRect((HWND)hwnd, &window_rect);
+        addwidth = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left);
+        addheight = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top);
+        mmi = (MINMAXINFO*)lParam;
+        mmi->ptMinTrackSize.x = ((uint32_t)root->minwidth) + addwidth;
+        mmi->ptMinTrackSize.y = ((uint32_t)root->minheight) + addheight;
+        resizable = !((root->minwidth == root->maxwidth) && (root->minheight == root->maxheight));
+        mmi->ptMaxTrackSize.x = resizable ? org::ibex::plat::Win32::getScreenWidth() : mmi->ptMinTrackSize.x;
+        mmi->ptMaxTrackSize.y = resizable ? org::ibex::plat::Win32::getScreenHeight() : mmi->ptMinTrackSize.y;
+        return 0;
+        
+    case WM_PAINT: 
+        PAINTSTRUCT ps;
+        BeginPaint((HWND)org::ibex::plat::Win32$Win32Surface::hwnd, &ps);
+        Dirty(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top);
+        EndPaint((HWND)org::ibex::plat::Win32$Win32Surface::hwnd, &ps);
+        return 0;
+
+    default: break;
+    }  
+
+    return DefWindowProc((HWND)hwnd, iMsg, wParam, lParam);
+}
+
+
+// Key Translator Helper /////////////////////////////////////////////////////////////////////
+
+static char keyarr [256] = { 0 };
+static jstring keyToString(WPARAM wParam) {
+    char arr[8];
+    keyarr[VK_CAPITAL] = GetKeyState(VK_CAPITAL);
+    keyarr[VK_LSHIFT] = GetKeyState(VK_LSHIFT);
+    keyarr[VK_RSHIFT] = GetKeyState(VK_RSHIFT);
+    keyarr[VK_SHIFT] = GetKeyState(VK_SHIFT);
+
+    if (ToAsciiEx(wParam, 0, (BYTE*)keyarr, (WORD*)arr, 0, GetKeyboardLayout(0)) == 1) {
+        switch (arr[0]) {
+        case '\t': return JvNewStringLatin1("tab");
+        case 0x1b: return JvNewStringLatin1("escape");
+        case '\n': return JvNewStringLatin1("enter");
+        case '\r': return JvNewStringLatin1("enter");
+        case 0x08: return JvNewStringLatin1("back_space");
+        default: return JvNewStringLatin1(arr, 1);
+        }
+        
+    } else switch (wParam) {
+    case VK_CLEAR: return JvNewStringLatin1("clear");
+    case VK_SHIFT: return JvNewStringLatin1("shift");
+    case VK_CONTROL: return JvNewStringLatin1("control");
+    case VK_CAPITAL: return JvNewStringLatin1("caps_lock");
+    case VK_MENU: return JvNewStringLatin1("alt");
+    case VK_PAUSE: return JvNewStringLatin1("pause");
+    case VK_PRIOR: return JvNewStringLatin1("page_up");
+    case VK_NEXT: return JvNewStringLatin1("page_down");
+    case VK_END: return JvNewStringLatin1("end");
+    case VK_HOME: return JvNewStringLatin1("home");
+    case VK_LEFT: return JvNewStringLatin1("left");
+    case VK_UP: return JvNewStringLatin1("up");
+    case VK_RIGHT: return JvNewStringLatin1("right");
+    case VK_DOWN: return JvNewStringLatin1("down");
+    case VK_INSERT: return JvNewStringLatin1("insert");
+    case VK_DELETE: return JvNewStringLatin1("delete");
+    case VK_NUMPAD0: return JvNewStringLatin1("numpad0");
+    case VK_NUMPAD1: return JvNewStringLatin1("numpad1");
+    case VK_NUMPAD2: return JvNewStringLatin1("numpad2");
+    case VK_NUMPAD3: return JvNewStringLatin1("numpad3");
+    case VK_NUMPAD4: return JvNewStringLatin1("numpad4");
+    case VK_NUMPAD5: return JvNewStringLatin1("numpad5");
+    case VK_NUMPAD6: return JvNewStringLatin1("numpad6");
+    case VK_NUMPAD7: return JvNewStringLatin1("numpad7");
+    case VK_NUMPAD8: return JvNewStringLatin1("numpad8");
+    case VK_NUMPAD9: return JvNewStringLatin1("numpad9");
+    case VK_F1: return JvNewStringLatin1("f1");
+    case VK_F2: return JvNewStringLatin1("f2");
+    case VK_F3: return JvNewStringLatin1("f3");
+    case VK_F4: return JvNewStringLatin1("f4");
+    case VK_F5: return JvNewStringLatin1("f5");
+    case VK_F6: return JvNewStringLatin1("f6");
+    case VK_F7: return JvNewStringLatin1("f7");
+    case VK_F8: return JvNewStringLatin1("f8");
+    case VK_F9: return JvNewStringLatin1("f9");
+    case VK_F10: return JvNewStringLatin1("f10");
+    case VK_F11: return JvNewStringLatin1("f11");
+    case VK_F12: return JvNewStringLatin1("f12");
+    case VK_NUMLOCK: return JvNewStringLatin1("num_lock");
+    case VK_SCROLL: return JvNewStringLatin1("scroll_lock");
+    case VK_LSHIFT: return JvNewStringLatin1("shift");
+    case VK_RSHIFT: return JvNewStringLatin1("shift");
+    case VK_LCONTROL: return JvNewStringLatin1("control");
+    case VK_RCONTROL: return JvNewStringLatin1("control");
+    }
+    return NULL;
+}