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