mass rename and rebranding from xwt to ibex - fixed to use ixt files
[org.ibex.core.git] / src / org / ibex / plat / Win32.cc
1 // Copyright 2002 Adam Megacz, see the COPYING file for licensing [LGPL]
2 #include "GCJ.cc"
3
4 // we have to do this because the jpeg libraries use the symbol 'INT32'
5 #define INT32 WIN32_INT32
6
7 // this has to precede the others so we don't get collisions on min/max
8 #include <org/ibex/js/JS.h>
9 #include <org/ibex/Box.h>
10
11 #include <stdint.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14
15 #include <windows.h>
16 #include <mmsystem.h>
17 #undef STRICT
18 #undef MAX_PRIORITY
19 #undef MIN_PRIORITY
20 #undef NORM_PRIORITY
21
22 #include <gcj/cni.h>
23
24 #include <java/lang/Integer.h>
25 #include <java/util/Hashtable.h>
26 #include <org/ibex/Box.h>
27 #include <org/ibex/Surface.h>
28 #include <org/ibex/PixelBuffer.h>
29 #include <org/ibex/Picture.h>
30 #include <org/ibex/Platform.h>
31 #include <org/ibex/plat/Win32.h>
32 #include <org/ibex/plat/Win32$Win32Surface.h>
33 #include <org/ibex/plat/Win32$Win32PixelBuffer.h>
34 #include <org/ibex/plat/Win32$Win32Picture.h>
35 #include <org/ibex/util/Log.h>
36 #include <org/ibex/util/Semaphore.h>
37
38 // for debugging
39 #include <java/lang/System.h>
40 #include <java/io/PrintStream.h>
41
42 #define WM_USER_SETCURSOR WM_USER
43 #define WM_USER_DISPOSE (WM_USER + 1)
44 #define WM_USER_CREATEWINDOW (WM_USER + 2)
45 #define WS_NORMAL (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
46
47 // FEATURE: there are lots of places where HANDLE's get casted to jint's -- this will break on Win64
48 //          a clean way to do this would be to '#define jraw (gnu::gcj::RawData*)'
49
50 // Callbacks ////////////////////////////////////////////////////////////////////
51
52 LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
53     org::ibex::plat::Win32$Win32Surface* surface =
54         (org::ibex::plat::Win32$Win32Surface*)org::ibex::plat::Win32::hwndToWin32SurfaceMap->get(new java::lang::Integer((jint)hwnd));
55
56     if (surface != NULL) {
57         return (LRESULT)surface->WndProc((jint)hwnd, (jint)iMsg, (jint)wParam, (jint)lParam);
58
59     } else {
60         // this is really lame -- Win32 insists on being able to call your WndProc BEFORE CreateWindow returns...
61         return DefWindowProc(hwnd, iMsg, wParam, lParam);
62     }
63 }
64
65
66 // Initialization ////////////////////////////////////////////////////////////////////
67
68 static int window_class_counter = 0;
69
70 jstring org::ibex::plat::Win32::getTempPath() {
71     char buf[1024];
72     DWORD ret = GetTempPath(1024, buf);
73     if (ret == 0) criticalAbort(JvNewStringLatin1("GetTempPath() failed"));
74     return JvNewStringLatin1(buf);
75 }
76
77 // XOR mask for the hand cursor
78 static unsigned char hand_cursor_xor[32 * 4] = {
79   0x00, 0x00, 0x00, 0x00,
80   0x00, 0x0C, 0x00, 0x00,
81   0x00, 0x0C, 0x00, 0x00,
82   0x00, 0x0C, 0x00, 0x00,
83   0x00, 0x0C, 0x00, 0x00,
84   0x00, 0x0C, 0x00, 0x00,
85   0x00, 0x0D, 0x80, 0x00,
86   0x00, 0x0D, 0xB0, 0x00,
87   0x00, 0x0D, 0xB4, 0x00,
88   0x00, 0x0D, 0xB6, 0x00,
89   0x00, 0xCF, 0xF6, 0x00,
90   0x00, 0xEF, 0xFE, 0x00,
91   0x00, 0x6F, 0xFE, 0x00,
92   0x00, 0x2F, 0xFE, 0x00,
93   0x00, 0x3F, 0xFE, 0x00,
94   0x00, 0x1F, 0xFE, 0x00,
95   0x00, 0x1F, 0xFC, 0x00,
96   0x00, 0x0F, 0xFC, 0x00,
97   0x00, 0x0F, 0xFC, 0x00,
98   0x00, 0x07, 0xF8, 0x00,
99   0x00, 0x07, 0xF8, 0x00,
100   0x00, 0x00, 0x00, 0x00,
101   0x00, 0x00, 0x00, 0x00,
102   0x00, 0x00, 0x00, 0x00,
103   0x00, 0x00, 0x00, 0x00,
104   0x00, 0x00, 0x00, 0x00,
105   0x00, 0x00, 0x00, 0x00,
106   0x00, 0x00, 0x00, 0x00,
107   0x00, 0x00, 0x00, 0x00,
108   0x00, 0x00, 0x00, 0x00,
109   0x00, 0x00, 0x00, 0x00,
110   0x00, 0x00, 0x00, 0x00
111 };
112
113 // AND mask for the hand cursor
114 static unsigned char hand_cursor_and[32 * 4] = {
115   0xFF, 0xF3, 0xFF, 0xFF,
116   0xFF, 0xE1, 0xFF, 0xFF,
117   0xFF, 0xE1, 0xFF, 0xFF,
118   0xFF, 0xE1, 0xFF, 0xFF,
119   0xFF, 0xE1, 0xFF, 0xFF,
120   0xFF, 0xE0, 0x7F, 0xFF,
121   0xFF, 0xE0, 0x0F, 0xFF,
122   0xFF, 0xE0, 0x03, 0xFF,
123   0xFF, 0xE0, 0x01, 0xFF,
124   0xFF, 0x20, 0x00, 0xFF,
125   0xFE, 0x00, 0x00, 0xFF,
126   0xFE, 0x00, 0x00, 0xFF,
127   0xFF, 0x00, 0x00, 0xFF,
128   0xFF, 0x80, 0x00, 0xFF,
129   0xFF, 0x80, 0x00, 0xFF,
130   0xFF, 0xC0, 0x00, 0xFF,
131   0xFF, 0xC0, 0x01, 0xFF,
132   0xFF, 0xE0, 0x01, 0xFF,
133   0xFF, 0xE0, 0x01, 0xFF,
134   0xFF, 0xF0, 0x03, 0xFF,
135   0xFF, 0xF0, 0x03, 0xFF,
136   0xFF, 0xF0, 0x03, 0xFF,
137   0xFF, 0xFF, 0xFF, 0xFF,
138   0xFF, 0xFF, 0xFF, 0xFF,
139   0xFF, 0xFF, 0xFF, 0xFF,
140   0xFF, 0xFF, 0xFF, 0xFF,
141   0xFF, 0xFF, 0xFF, 0xFF,
142   0xFF, 0xFF, 0xFF, 0xFF,
143   0xFF, 0xFF, 0xFF, 0xFF,
144   0xFF, 0xFF, 0xFF, 0xFF,
145   0xFF, 0xFF, 0xFF, 0xFF,
146   0xFF, 0xFF, 0xFF, 0xFF
147 };
148
149 void org::ibex::plat::Win32::natPreInit() {
150     if (org::ibex::util::Log::verbose) {
151         AllocConsole();
152         freopen("CONOUT$", "w+t", stderr);
153     }
154 }
155
156 void org::ibex::plat::Win32::natInit() {
157
158     // grab desktop dc/handle
159     desktop_handle = (jint)GetDesktopWindow();
160     desktop_dc = (jint)GetDC((HWND)desktop_handle);
161
162     // create cursors
163     org::ibex::plat::Win32::wait_cursor = (jint)LoadCursor(NULL, IDC_WAIT);
164     org::ibex::plat::Win32::default_cursor = (jint)LoadCursor(NULL, IDC_ARROW);
165     org::ibex::plat::Win32::crosshair_cursor = (jint)LoadCursor(NULL, IDC_CROSS);
166     org::ibex::plat::Win32::text_cursor = (jint)LoadCursor(NULL, IDC_IBEAM);
167     org::ibex::plat::Win32::move_cursor = (jint)LoadCursor(NULL, IDC_SIZEALL);
168     org::ibex::plat::Win32::sizenesw_cursor = (jint)LoadCursor(NULL, IDC_SIZENESW);
169     org::ibex::plat::Win32::sizens_cursor = (jint)LoadCursor(NULL, IDC_SIZENS);
170     org::ibex::plat::Win32::sizenwse_cursor = (jint)LoadCursor(NULL, IDC_SIZENWSE);
171     org::ibex::plat::Win32::sizewe_cursor = (jint)LoadCursor(NULL, IDC_SIZEWE);
172     org::ibex::plat::Win32::hand_cursor = (jint)CreateCursor(GetModuleHandle(NULL), 14, 1, 32, 32, hand_cursor_and, hand_cursor_xor);
173
174     messagePumpThread = (jint)GetCurrentThreadId();
175     messagePumpStarted->release();
176
177     MSG msg;
178     while(GetMessage(&msg, (HWND)NULL, 0, 0) > 0) {
179
180         if (msg.message == WM_USER_CREATEWINDOW) {
181             org::ibex::plat::Win32$Win32Surface *surface = (org::ibex::plat::Win32$Win32Surface*)msg.lParam;
182
183             // we must create a unique window class name for each
184             // window so that minimization icons can be set independantly
185             char buf[255];
186             sprintf(buf, "Ibex_WINDOW_CLASS_%i", window_class_counter++);
187
188             WNDCLASSEX wc;
189             wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
190             wc.lpfnWndProc = WndProc;
191             wc.cbClsExtra = 0;
192             wc.cbSize = sizeof(WNDCLASSEX);
193             wc.cbWndExtra = 0;
194             wc.hInstance = GetModuleHandle(NULL);
195             wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
196             wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL),
197                                           MAKEINTRESOURCE(5),
198                                           IMAGE_ICON,
199                                           GetSystemMetrics(SM_CXSMICON),
200                                           GetSystemMetrics(SM_CYSMICON),
201                                           LR_DEFAULTCOLOR);
202             wc.hCursor = LoadCursor(NULL, IDC_ARROW);
203             wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
204             wc.lpszMenuName = "menu";
205             wc.lpszClassName = buf;
206             RegisterClassEx(&wc);
207
208             surface->hwnd = (jint)CreateWindow(wc.lpszClassName, TEXT(""),
209                                                msg.wParam ? WS_NORMAL : WS_POPUP,
210                                                200, 200, 100, 100,
211                                                (HWND__*)NULL, (HMENU__*)NULL,
212                                                GetModuleHandle(NULL), (LPVOID)NULL);
213             
214             SetFocus((HWND)surface->hwnd);
215             surface->hwndCreated->release();
216             
217         } else {
218             TranslateMessage(&msg);
219             if (msg.message == WM_LBUTTONDOWN || msg.message == WM_RBUTTONDOWN || msg.message == WM_MBUTTONDOWN) SetFocus(msg.hwnd);
220             DispatchMessage(&msg);
221         }
222
223     }
224     java::lang::System::exit(-1);
225 }
226
227
228 // Platform Methods ///////////////////////////////////////////////////////////////////
229
230 jstring org::ibex::plat::Win32::_getEnv(jstring key) {
231     int len = JvGetStringUTFLength(key);
232     char buf[len + 1];
233     JvGetStringUTFRegion(key, 0, len, buf);
234     buf[len] = '\0';
235     char buf2[1024];
236     DWORD ret = GetEnvironmentVariable(buf, buf2, 1024);
237     if (ret > 0 && ret < 1024) return JvNewStringLatin1(buf2);
238     return NULL;
239 }
240
241 jstring org::ibex::plat::Win32::_fileDialog(jstring suggestedFileName, jboolean write) {
242
243     char buf[1024];
244     OPENFILENAME ofn;
245     memset(buf, 0, 1024);
246     memset(&ofn, 0, sizeof(OPENFILENAME));
247
248     if (suggestedFileName != NULL)
249         JvGetStringUTFRegion(suggestedFileName, 0, min(1023, JvGetStringUTFLength(suggestedFileName)), buf);
250     
251     ofn.lStructSize = sizeof(OPENFILENAME);
252     ofn.nMaxCustFilter = 0;
253     ofn.lpstrFile = buf;
254     ofn.nMaxFile = 1024;
255
256     if (write) ofn.Flags |= OFN_OVERWRITEPROMPT;
257     ofn.Flags |= OFN_HIDEREADONLY;
258
259     int ret = write ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn);
260     return ret == 0 ? NULL : JvNewStringLatin1(buf);
261 }
262
263 void org::ibex::plat::Win32::__detectProxy(JArray<jstring>* container) {
264
265     HKEY hkey;
266     char buf[1024];
267     DWORD buflen = 1024;
268     DWORD type;
269     LONG result = RegOpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", &hkey);
270     if (result != ERROR_SUCCESS) return;
271     
272     buf[0] = '\0';
273     type = REG_SZ;
274     buflen = 1024;
275     result = RegQueryValueEx(hkey, "AutoConfigURL", NULL, &type, (LPBYTE)buf, &buflen);
276     buf[buflen] = '\0';
277     if (result == ERROR_SUCCESS) elements(container)[2] = JvNewStringLatin1(buf);
278
279     buf[0] = '\0';
280     type = REG_BINARY;
281     RegQueryValueEx(hkey, "ProxyEnable", NULL, &type, (LPBYTE)buf, &buflen);
282     if (buf[0] != 1) return;
283
284     buf[0] = '\0';
285     type = REG_SZ;
286     buflen = 1024;
287     RegQueryValueEx(hkey, "ProxyServer", NULL, &type, (LPBYTE)buf, &buflen);
288     buf[buflen] = '\0';
289     elements(container)[0] = JvNewStringLatin1(buf);
290
291     buf[0] = '\0';
292     buflen = 1024;
293     RegQueryValueEx(hkey, "ProxyOverride", NULL, &type, (LPBYTE)buf, &buflen);
294     buf[buflen] = '\0';
295     elements(container)[1] = JvNewStringLatin1(buf);
296 }
297
298 jstring org::ibex::plat::Win32::_getClipBoard() {
299     OpenClipboard((HWND)desktop_handle);
300     HGLOBAL hmem = GetClipboardData(CF_TEXT);
301     if (hmem == NULL) return NULL;
302     char* buf = (char*)GlobalLock(hmem);
303     if (buf == NULL) return NULL;
304     jstring ret = JvNewStringLatin1(buf);
305     GlobalUnlock(hmem);
306     CloseClipboard();
307     return ret;
308 }
309
310 void org::ibex::plat::Win32::_setClipBoard(jstring s) {
311     OpenClipboard((HWND)desktop_handle);
312     HGLOBAL hmem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, JvGetStringUTFLength(s) + 1);
313     if (hmem == NULL) return;
314     char* buf = (char*)GlobalLock(hmem);
315     if (buf == NULL) return;
316     JvGetStringUTFRegion(s, 0, JvGetStringUTFLength(s), buf);
317     buf[JvGetStringUTFLength(s)] = '\0';
318     GlobalUnlock(hmem);
319     SetClipboardData(CF_TEXT, hmem);
320     CloseClipboard();
321 }
322
323 void org::ibex::plat::Win32::_criticalAbort(jstring message) {
324     char buf[JvGetStringUTFLength(message) + 1];
325     buf[JvGetStringUTFLength(message)] = '\0';
326     JvGetStringUTFRegion(message, 0, JvGetStringUTFLength(message), buf);
327     MessageBox (NULL, buf, "Ibex Cannot Continue", MB_OK | MB_ICONSTOP | MB_TASKMODAL | MB_SETFOREGROUND);
328     java::lang::System::exit(-1);
329 }
330
331 jint org::ibex::plat::Win32::_getScreenWidth() {
332     RECT rect;
333     SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
334     return rect.right - rect.left;
335 }
336
337 jint org::ibex::plat::Win32::_getScreenHeight() {
338     RECT rect;
339     SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
340     return rect.bottom - rect.top;
341 }
342
343 jboolean org::ibex::plat::Win32::_newBrowserWindow_(jstring url) {
344
345     int len = min(2048, JvGetStringUTFLength(url));
346     char buf[len + 1];
347     JvGetStringUTFRegion(url, 0, len, buf);
348     buf[len] = '\0';
349
350     SHELLEXECUTEINFO ei;
351     memset(&ei, 0, sizeof(ei));
352     ei.cbSize = sizeof(ei);
353     ei.lpVerb = "open";
354     ei.lpFile = buf;
355     ei.fMask  = SEE_MASK_NOCLOSEPROCESS;
356     ei.nShow  = SW_SHOWDEFAULT;
357     return (ShellExecuteEx(&ei) == 0);
358
359 }
360
361
362 // Win32PixelBuffer /////////////////////////////////////////////////////////////////////////
363
364 // This is a scratch area; when blitting a translucent image, we copy the underlying data here first.
365 // Since all drawing operations are single-threaded, it's safe to use a global here.
366 static HBITMAP scratch = NULL;
367 static HDC scratch_dc = NULL;
368 static jint* scratch_bits = NULL;
369 static jint scratch_w = 0;
370 static jint scratch_h = 0;
371
372 #define max(a,b) ((a)>(b)?(a):(b))
373 #define min(a,b) ((a)<(b)?(a):(b))
374
375 void org::ibex::plat::Win32$Win32PixelBuffer::drawPicture(org::ibex::Picture* source0,
376                                                          jint dx, jint dy,
377                                                          jint cx1, jint cy1, jint cx2, jint cy2,
378                                                          jint rgb, jboolean alphaOnly) {
379     org::ibex::plat::Win32$Win32Picture* source = (org::ibex::plat::Win32$Win32Picture*)source0;
380
381     cx1 = max(dx, cx1);
382     cy1 = max(dy, cy1);
383     cx2 = min(dx + source->getWidth(), cx2);
384     cy2 = min(dy + source->getHeight(), cy2);
385     if (cx1 >= cx2 || cy1 >= cy2) return; 
386
387     if (source->hasalpha) {
388
389         if (scratch == NULL || scratch_w < cx2 - cx1 || scratch_h < cy2 - cy1) {
390             if (scratch_dc != NULL) DeleteDC(scratch_dc);
391             if (scratch != NULL) DeleteObject(scratch);
392             scratch_w = max(cx2 - cx1, scratch_w);
393             scratch_h = max(cy2 - cy1, scratch_h);
394
395             BITMAPINFO bitmapinfo;
396             memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
397             bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
398             bitmapinfo.bmiHeader.biWidth = scratch_w;
399             bitmapinfo.bmiHeader.biHeight = -1 * scratch_h;
400             bitmapinfo.bmiHeader.biPlanes = 1;
401             bitmapinfo.bmiHeader.biBitCount = 32;
402             bitmapinfo.bmiHeader.biCompression = BI_RGB;
403
404             // create section DIB
405             scratch = CreateDIBSection(NULL, &bitmapinfo, DIB_RGB_COLORS, (void**)&scratch_bits, NULL, 0);
406             scratch_dc = CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
407             SelectObject(scratch_dc, scratch);
408         }
409
410         // copy from screen to scratch
411         BitBlt((HDC)scratch_dc, 0, 0, cx2 - cx1, cy2 - cy1, (HDC)hdc, cx1, cy1, SRCCOPY);
412
413         // apply alpha-blending to scratch
414         jint* dat = elements(source->data);
415
416         for(int x = cx1; x < cx2; x++)
417             for(int y = cy1; y < cy2; y++) {
418                 jint dst = scratch_bits[(y - dy) * scratch_w + (x - dx)];
419
420                 // FEATURE: see if we can leverage GDI to do something more clever here with alphaOnly
421                 jint src = alphaOnly ? rgb : dat[(y - dy) * source->getWidth() + x - dx];
422                 jint alpha = (dat[(y - dy) * source->getWidth() + x - dx] & 0xFF000000) >> 24;
423                 jint r = (((src & 0x00FF0000) >> 16) * alpha + ((dst & 0x00FF0000) >> 16) * (0xFF - alpha)) / 0xFF;
424                 jint g = (((src & 0x0000FF00) >> 8)  * alpha + ((dst & 0x0000FF00) >> 8)  * (0xFF - alpha)) / 0xFF;
425                 jint b = (((src & 0x000000FF))       * alpha + ((dst & 0x000000FF))       * (0xFF - alpha)) / 0xFF;
426                 scratch_bits[(y - dy) * scratch_w + (x - dx)] = (r << 16) | (g << 8) | b;
427             }
428
429         // copy back from scratch to screen
430         BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)scratch_dc, 0, 0, SRCCOPY);
431
432     } else {
433
434         // FIXME: support alphaOnly case here
435         if (source->hasmask) {
436             BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->maskdc, cx1 - dx, cy1 - dy, SRCAND);
437             BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->hdc, cx1 - dx, cy1 - dy, SRCPAINT);
438         } else {
439             BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->hdc, cx1 - dx, cy1 - dy, SRCCOPY);
440         }
441
442     }
443
444 }
445
446 void org::ibex::plat::Win32$Win32PixelBuffer::fillRect(jint x, jint y, jint x2, jint y2, jint color) {
447     jint w = x2 - x;
448     jint h = y2 - y;
449
450     // sadly, the ability to change the color of a brush didn't arrive until Win2k...
451     HBRUSH brush = CreateSolidBrush(PALETTERGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
452     if (!brush) return;
453     RECT rect = { x, y, x + w, y + h };
454     FillRect((HDC)hdc, &rect, brush);
455     DeleteObject(brush);
456 }
457
458 void org::ibex::plat::Win32$Win32Surface::blit(org::ibex::PixelBuffer* s, jint sx, jint sy, jint dx, jint dy, jint dx2, jint dy2) {
459     // we create the DC lazily to get around some strange race condition in WinXP
460     if (hdc == 0) hdc = (jint)GetDC((HWND)hwnd);
461     BitBlt((HDC)hdc, dx, dy, dx2 - dx, dy2 - dy, (HDC)(((org::ibex::plat::Win32$Win32PixelBuffer*)s)->hdc), sx, sy, SRCCOPY);
462 }
463
464 void org::ibex::plat::Win32$Win32PixelBuffer::natInit() {
465     hbitmap = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, w, h);
466     hdc = (jint)CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
467     SetBkMode((HDC)hdc, TRANSPARENT);
468     SelectObject((HDC)hdc, (HBITMAP)hbitmap);
469 }
470
471 void org::ibex::plat::Win32$Win32PixelBuffer::finalize() {
472     DeleteObject((void*)hdc);
473     DeleteObject((void*)hbitmap);
474 }
475
476
477
478 // Win32Picture /////////////////////////////////////////////////////////////////////////
479
480 void org::ibex::plat::Win32$Win32Picture::natInit() {
481
482     BITMAPINFO bitmapinfo;
483     memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
484     bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
485     bitmapinfo.bmiHeader.biWidth = width;
486     bitmapinfo.bmiHeader.biHeight = -1 * height;
487     bitmapinfo.bmiHeader.biPlanes = 1;
488     bitmapinfo.bmiHeader.biBitCount = 32;
489     bitmapinfo.bmiHeader.biCompression = BI_RGB;
490
491     hbitmap = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, width, height);
492     hdc = (jint)CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
493     SelectObject((HDC)hdc, (HBITMAP)hbitmap);
494     uint32_t* dat = (uint32_t*)elements(data);
495     for(int i=0; i<data->length; i++) if ((dat[i] & 0xFF000000) == 0x00000000) dat[i] = 0x00000000;
496     StretchDIBits((HDC)hdc, 0, 0, width, height, 0, 0, width, height, elements(data), &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
497
498     jint _copy[min(1024, data->length)];
499     jint* copy = data->length > 1024 ? (jint*)malloc(data->length * 4) : _copy;
500
501     memcpy(copy, elements(data), data->length * 4);
502     for(int i=0; i<data->length; i++)
503         if ((copy[i] & 0xFF000000) == 0x00000000) {
504             hasmask = 1;
505             copy[i] = 0x00FFFFFF;
506         } else if ((copy[i] & 0xFF000000) == 0xFF000000) {
507             copy[i] = 0x00000000;
508         } else {
509             hasalpha = 1;
510             hasmask = 0;
511             if (data->length > 1024) free(copy);
512             return;
513         }
514
515     if (!hasmask) {
516         if (data->length > 1024) free(copy);
517         return;
518     }
519
520     //    hmask = (jint)CreateBitmap(w, h, 1, 1, NULL);
521     hmask = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, width, height);
522     maskdc = (jint)CreateCompatibleDC(NULL);
523     SelectObject((HDC)maskdc, (HBITMAP)hmask);
524     StretchDIBits((HDC)maskdc, 0, 0, width, height, 0, 0, width, height, copy, &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
525     if (data->length > 1024) free(copy);
526 }
527
528
529
530 // Win32Surface /////////////////////////////////////////////////////////////////////////////
531
532 void org::ibex::plat::Win32$Win32Surface::natInit(jboolean framed) {
533
534     // Ask the message-handling thread to create a window for us
535     PostThreadMessage((DWORD)org::ibex::plat::Win32::messagePumpThread, WM_USER + 2, (WPARAM)framed, (LPARAM)this);
536     hwndCreated->block();
537
538     // turn on incremental GC now that we have a user-visible interface
539     // [[this is causing segfaults; enable it later...]]
540     // GC_enable_incremental();
541
542     ShowWindow ((HWND)hwnd, SW_SHOWDEFAULT);
543 }
544
545 void org::ibex::plat::Win32$Win32Surface::finalize() { /* DeleteObject((void*)hwnd); */ }
546 void org::ibex::plat::Win32$Win32Surface::toFront() { BringWindowToTop((HWND)hwnd); }
547 void org::ibex::plat::Win32$Win32Surface::toBack() { SetWindowPos((HWND)hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); }
548 void org::ibex::plat::Win32$Win32Surface::_dispose() { PostMessage((HWND)hwnd, WM_USER_DISPOSE, 0, 0); }
549 void org::ibex::plat::Win32$Win32Surface::setInvisible(jboolean h) { ShowWindow((HWND)hwnd, h ? SW_HIDE : SW_SHOWNORMAL); }
550 void org::ibex::plat::Win32$Win32Surface::_setMinimized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMINIMIZED : SW_SHOWNORMAL); }
551 void org::ibex::plat::Win32$Win32Surface::_setMaximized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); }
552 void org::ibex::plat::Win32$Win32Surface::postCursorChange() { PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0); }
553
554 void org::ibex::plat::Win32$Win32Surface::setLocation() {
555     POINT point;
556     RECT rect;
557     point.x = 0;
558     point.y = 0;
559     ClientToScreen((HWND)hwnd, &point);
560     GetWindowRect((HWND)hwnd, &rect);
561     SetWindowPos((HWND)hwnd, NULL, root->x - (point.x - rect.left), root->y - (point.y - rect.top), 0, 0, SWP_NOZORDER | SWP_NOSIZE);
562 }
563
564 void org::ibex::plat::Win32$Win32Surface::_setSize(jint w, jint h) {
565     RECT client_rect, window_rect;
566     GetClientRect((HWND)hwnd, &client_rect);
567     GetWindowRect((HWND)hwnd, &window_rect);
568     int width = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left) + w;
569     int height = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top) + h;
570     SetWindowPos((HWND)hwnd, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE);
571 }
572
573 void org::ibex::plat::Win32$Win32Surface::setTitleBarText(java::lang::String* title) {
574     int len = min(1024, JvGetStringUTFLength(title));
575     char buf[len + 1];
576     buf[len] = '\0';
577     JvGetStringUTFRegion(title, 0, len, buf);
578     SetWindowText((HWND)hwnd, buf);
579 }
580
581 void org::ibex::plat::Win32$Win32Surface::setIcon(org::ibex::Picture* p0) {
582
583     org::ibex::plat::Win32$Win32Picture* p = (org::ibex::plat::Win32$Win32Picture*)p0;
584     int icon_width = GetSystemMetrics(SM_CXSMICON);
585     int icon_height = GetSystemMetrics(SM_CYSMICON);
586
587     // we create the DC lazily to get around some strange race condition in WinXP
588     if (hdc == 0) hdc = (jint)GetDC((HWND)hwnd);
589
590     // create the bitmap
591     HBITMAP bit = CreateCompatibleBitmap((HDC)hdc, icon_width, icon_height);
592     HDC memdc = CreateCompatibleDC((HDC)hdc);
593     SelectObject(memdc, bit);
594     BitBlt((HDC)memdc, 0, 0, icon_width, icon_height, (HDC)(p->hdc), 0, 0, SRCCOPY);
595
596     // create the mask
597     jint* dat = elements(p->data);
598     HBITMAP bit_mask = CreateBitmap(icon_width, icon_height * 2, 1, 1, NULL);
599     SelectObject(memdc, bit_mask);
600     for(int x=0; x<icon_width; x++)
601         for(int y=0; y<icon_height; y++) {
602             int source = dat[(x * p->getWidth()) / icon_width + ((y * p->getHeight()) / icon_height) * (p->getWidth())];
603             if ((source & 0xFF000000) != 0x00000000) SetPixel(memdc, x, y, PALETTERGB(0, 0, 0));
604             else SetPixel(memdc, x, y, PALETTERGB(255, 255, 255));
605         }
606
607     // instantiate the icon and assign it to the window class
608     ICONINFO ici;
609     ici.fIcon = 1;
610     ici.hbmMask = bit_mask;
611     ici.hbmColor = bit;
612     HICON hicon = CreateIconIndirect(&ici);
613     HICON oldicon = (HICON)SetClassLong((HWND)hwnd, GCL_HICONSM, (LONG)hicon);
614     if (oldicon != NULL) DestroyIcon(oldicon);
615     DeleteObject(memdc);
616 }
617
618 static jstring keyToString(WPARAM wParam);
619 jint org::ibex::plat::Win32$Win32Surface::WndProc(jint _hwnd, jint _iMsg, jint _wParam, jint _lParam) {
620
621     UINT iMsg = (UINT)_iMsg;
622     WPARAM wParam = (WPARAM)_wParam;
623     LPARAM lParam = (LPARAM)_lParam;
624
625     int oldmousex, oldmousey;
626     MINMAXINFO* mmi;
627     int resizable;
628     POINT point;
629     HWND hwnd2;
630     RECT rect, rect2;
631     RECT client_rect, window_rect;
632     jboolean newinside;
633     int16_t mouse_x;
634     int16_t mouse_y;
635     int addwidth, addheight;
636
637     switch(iMsg) {
638     case WM_DEVMODECHANGE: break;    // FEATURE: color depth changed
639     case WM_DISPLAYCHANGE: break;    // FEATURE: screen size changed
640     case WM_MOUSEWHEEL: break;       // FEATURE: Mouse Wheel
641
642     case WM_SYSKEYDOWN: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
643     case WM_KEYDOWN:
644         KeyPressed(keyToString(wParam));
645         return 0;
646
647     case WM_SYSKEYUP: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
648     case WM_KEYUP:
649         KeyReleased(keyToString(wParam));
650         return 0;
651
652     case WM_SETFOCUS: Focused(true); return 0;
653     case WM_KILLFOCUS: Focused(false); return 0;
654     case WM_LBUTTONDBLCLK: DoubleClick(1); return 0;
655     case WM_RBUTTONDBLCLK: DoubleClick(2); return 0;
656     case WM_MBUTTONDBLCLK: DoubleClick(3); return 0;
657     case WM_LBUTTONDOWN: Press(1); return 0;
658     case WM_RBUTTONDOWN: Press(2); return 0;
659     case WM_MBUTTONDOWN: Press(3); return 0;
660     case WM_CLOSE: Close(); return 0;
661     case WM_ERASEBKGND: return 0;
662
663     case WM_LBUTTONUP:
664     case WM_RBUTTONUP:
665     case WM_MBUTTONUP:
666         Release(iMsg == WM_LBUTTONUP ? 1 : (iMsg == WM_RBUTTONUP ? 2 : 3));
667         if (captured && !inside) {
668             ReleaseCapture();
669             SetCursor((HCURSOR)previous_cursor);
670         }
671         return 0;
672
673     case WM_MOVING:
674         GetClientRect((HWND)hwnd, &rect);
675         PosChange(rect.left, rect.top);
676         return 0;
677
678     case WM_SIZE:
679         if (wParam == SIZE_MINIMIZED) {
680             if (maximized) Maximized(false);
681             Minimized(true);
682         } else if (wParam == SIZE_MAXIMIZED) {
683             if (minimized) Minimized(false); 
684             Maximized(true);
685         } else {
686             if (minimized) Minimized(false); 
687             if (maximized) Maximized(false);
688         }
689         // deliberately fall through to WM_SIZING
690
691     case WM_SIZING:
692         GetClientRect((HWND)hwnd, &rect);
693         SizeChange(rect.right - rect.left, rect.bottom - rect.top);
694         return 0;
695
696     case WM_MOUSEMOVE:
697         if (mousex == lParam & 0xFFFF && mousey == (lParam & 0xFFFF0000) >> 16) return 0;
698
699         GetClientRect((HWND)hwnd, &rect);
700         point.x = mouse_x = lParam & 0xFFFF;
701         point.y = mouse_y = (lParam & 0xFFFF0000) >> 16;
702         ClientToScreen((HWND)hwnd, &point);
703         hwnd2 = WindowFromPoint(point);
704
705         newinside = hwnd2 == (HWND)hwnd && mouse_x > 0 && mouse_y > 0 && mouse_x < (rect.right - rect.left) && mouse_y < (rect.bottom - rect.top) ? 1 : 0;
706
707         Move(mouse_x, mouse_y);
708
709         if (newinside && !inside) {
710             inside = 1;
711             SetCapture((HWND)hwnd);
712             captured = 1;
713             previous_cursor = (jint)GetCursor();
714             PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0);
715
716         } else if (!newinside && inside) {
717             inside = 0;
718             if (!button1 && !button2 && !button3) {
719                 ReleaseCapture();
720                 captured = 0;
721                 SetCursor((HCURSOR)previous_cursor);
722             }
723         }
724
725         return 0;
726
727     case WM_USER_SETCURSOR:
728         // I can't find documentation of this anywhere, but in my experience, an event must be received between an invocation
729         // of SetCapture() and SetCursor(). Furthermore, it seems that after calling SetCursor(), the cursor will not change until a
730         // return from WndProc (ie the completion of the processing of some event).  Hence, we use WM_USER to indicate that the screen
731         // cursor should be re-set to "current_cursor".
732         SetCursor((HCURSOR)current_cursor);
733         return 0;
734
735     case WM_USER_DISPOSE:
736         // used to signal that we should destroy ourselves
737         DestroyWindow((HWND)hwnd);
738         return 0;
739
740     case WM_GETMINMAXINFO:
741         GetClientRect((HWND)hwnd, &client_rect);
742         GetWindowRect((HWND)hwnd, &window_rect);
743         addwidth = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left);
744         addheight = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top);
745         mmi = (MINMAXINFO*)lParam;
746         mmi->ptMinTrackSize.x = ((uint32_t)root->minwidth) + addwidth;
747         mmi->ptMinTrackSize.y = ((uint32_t)root->minheight) + addheight;
748         resizable = !((root->minwidth == root->maxwidth) && (root->minheight == root->maxheight));
749         mmi->ptMaxTrackSize.x = resizable ? org::ibex::plat::Win32::getScreenWidth() : mmi->ptMinTrackSize.x;
750         mmi->ptMaxTrackSize.y = resizable ? org::ibex::plat::Win32::getScreenHeight() : mmi->ptMinTrackSize.y;
751         return 0;
752         
753     case WM_PAINT: 
754         PAINTSTRUCT ps;
755         BeginPaint((HWND)org::ibex::plat::Win32$Win32Surface::hwnd, &ps);
756         Dirty(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top);
757         EndPaint((HWND)org::ibex::plat::Win32$Win32Surface::hwnd, &ps);
758         return 0;
759
760     default: break;
761     }  
762
763     return DefWindowProc((HWND)hwnd, iMsg, wParam, lParam);
764 }
765
766
767 // Key Translator Helper /////////////////////////////////////////////////////////////////////
768
769 static char keyarr [256] = { 0 };
770 static jstring keyToString(WPARAM wParam) {
771     char arr[8];
772     keyarr[VK_CAPITAL] = GetKeyState(VK_CAPITAL);
773     keyarr[VK_LSHIFT] = GetKeyState(VK_LSHIFT);
774     keyarr[VK_RSHIFT] = GetKeyState(VK_RSHIFT);
775     keyarr[VK_SHIFT] = GetKeyState(VK_SHIFT);
776
777     if (ToAsciiEx(wParam, 0, (BYTE*)keyarr, (WORD*)arr, 0, GetKeyboardLayout(0)) == 1) {
778         switch (arr[0]) {
779         case '\t': return JvNewStringLatin1("tab");
780         case 0x1b: return JvNewStringLatin1("escape");
781         case '\n': return JvNewStringLatin1("enter");
782         case '\r': return JvNewStringLatin1("enter");
783         case 0x08: return JvNewStringLatin1("back_space");
784         default: return JvNewStringLatin1(arr, 1);
785         }
786         
787     } else switch (wParam) {
788     case VK_CLEAR: return JvNewStringLatin1("clear");
789     case VK_SHIFT: return JvNewStringLatin1("shift");
790     case VK_CONTROL: return JvNewStringLatin1("control");
791     case VK_CAPITAL: return JvNewStringLatin1("caps_lock");
792     case VK_MENU: return JvNewStringLatin1("alt");
793     case VK_PAUSE: return JvNewStringLatin1("pause");
794     case VK_PRIOR: return JvNewStringLatin1("page_up");
795     case VK_NEXT: return JvNewStringLatin1("page_down");
796     case VK_END: return JvNewStringLatin1("end");
797     case VK_HOME: return JvNewStringLatin1("home");
798     case VK_LEFT: return JvNewStringLatin1("left");
799     case VK_UP: return JvNewStringLatin1("up");
800     case VK_RIGHT: return JvNewStringLatin1("right");
801     case VK_DOWN: return JvNewStringLatin1("down");
802     case VK_INSERT: return JvNewStringLatin1("insert");
803     case VK_DELETE: return JvNewStringLatin1("delete");
804     case VK_NUMPAD0: return JvNewStringLatin1("numpad0");
805     case VK_NUMPAD1: return JvNewStringLatin1("numpad1");
806     case VK_NUMPAD2: return JvNewStringLatin1("numpad2");
807     case VK_NUMPAD3: return JvNewStringLatin1("numpad3");
808     case VK_NUMPAD4: return JvNewStringLatin1("numpad4");
809     case VK_NUMPAD5: return JvNewStringLatin1("numpad5");
810     case VK_NUMPAD6: return JvNewStringLatin1("numpad6");
811     case VK_NUMPAD7: return JvNewStringLatin1("numpad7");
812     case VK_NUMPAD8: return JvNewStringLatin1("numpad8");
813     case VK_NUMPAD9: return JvNewStringLatin1("numpad9");
814     case VK_F1: return JvNewStringLatin1("f1");
815     case VK_F2: return JvNewStringLatin1("f2");
816     case VK_F3: return JvNewStringLatin1("f3");
817     case VK_F4: return JvNewStringLatin1("f4");
818     case VK_F5: return JvNewStringLatin1("f5");
819     case VK_F6: return JvNewStringLatin1("f6");
820     case VK_F7: return JvNewStringLatin1("f7");
821     case VK_F8: return JvNewStringLatin1("f8");
822     case VK_F9: return JvNewStringLatin1("f9");
823     case VK_F10: return JvNewStringLatin1("f10");
824     case VK_F11: return JvNewStringLatin1("f11");
825     case VK_F12: return JvNewStringLatin1("f12");
826     case VK_NUMLOCK: return JvNewStringLatin1("num_lock");
827     case VK_SCROLL: return JvNewStringLatin1("scroll_lock");
828     case VK_LSHIFT: return JvNewStringLatin1("shift");
829     case VK_RSHIFT: return JvNewStringLatin1("shift");
830     case VK_LCONTROL: return JvNewStringLatin1("control");
831     case VK_RCONTROL: return JvNewStringLatin1("control");
832     }
833     return NULL;
834 }