2003/10/19 02:21:59
[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 BLT(dest, dx1, dy1, dx2, dy2, src, sx1, sy1, sx2, sy2, op)                                   \
358     if ((dx2 - dx1 == sx2 - sx1) && (dy2 - dy1 == sy2 - sy1))                                        \
359         BitBlt(dest, dx1, dy1, dx2 - dx1, dy2 - dy1, src, sx1, sy1, op);                             \
360     else                                                                                             \
361         StretchBlt(dest, dx1, dy1, dx2 - dx1, dy2 - dy1, src, sx1, sy1, sx2 - sx1, sy2 - sy1, op);
362         
363 void org::xwt::plat::Win32$Win32PixelBuffer::drawPicture(org::xwt::Picture* source0,
364                                                          jint dx1, jint dy1, jint dx2, jint dy2,
365                                                          jint sx1, jint sy1, jint sx2, jint sy2,
366                                                          jint rgb, jboolean alphaOnly) {
367     org::xwt::plat::Win32$Win32Picture* source = (org::xwt::plat::Win32$Win32Picture*)source0;
368
369     if (source->hasalpha) {
370
371         if (scratch == NULL || scratch_w < dx2 - dx1 || scratch_h < dy2 - dy1) {
372             if (scratch_dc != NULL) DeleteDC(scratch_dc);
373             if (scratch != NULL) DeleteObject(scratch);
374             scratch_w = max(dx2 - dx1, scratch_w);
375             scratch_h = max(dy2 - dy1, scratch_h);
376
377             BITMAPINFO bitmapinfo;
378             memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
379             bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
380             bitmapinfo.bmiHeader.biWidth = scratch_w;
381             bitmapinfo.bmiHeader.biHeight = -1 * scratch_h;
382             bitmapinfo.bmiHeader.biPlanes = 1;
383             bitmapinfo.bmiHeader.biBitCount = 32;
384             bitmapinfo.bmiHeader.biCompression = BI_RGB;
385
386             // create section DIB
387             scratch = CreateDIBSection(NULL, &bitmapinfo, DIB_RGB_COLORS, (void**)&scratch_bits, NULL, 0);
388             scratch_dc = CreateCompatibleDC((HDC)org::xwt::plat::Win32::desktop_dc);
389             SelectObject(scratch_dc, scratch);
390         }
391
392         // copy from screen to scratch
393         BitBlt((HDC)scratch_dc, 0, 0, dx2 - dx1, dy2 - dy1, (HDC)hdc, dx1, dy1, SRCCOPY);
394
395         // apply alpha-blending to scratch
396         jint* dat = elements(source->data);
397
398         for(int x = max(clipx1, dx1) - dx1; x < min(clipx2, dx2) - dx1; x++)
399             for(int y = max(clipy1, dy1) - dy1; y < min(clipy2, dy2) - dy1; y++) {
400                 int sx = (x * (sx2 - sx1)) / (dx2 - dx1) + sx1;
401                 int sy = (y * (sy2 - sy1)) / (dy2 - dy1) + sy1;
402                 jint dst = scratch_bits[y * scratch_w + x];
403
404                 // FEATURE: see if we can leverage GDI to do something more clever here with alphaOnly
405                 jint src = alphaOnly ? rgb : dat[sy * source->getWidth() + sx];
406                 jint alpha = (dat[sy * source->getWidth() + sx] & 0xFF000000) >> 24;
407                 jint r = (((src & 0x00FF0000) >> 16) * alpha + ((dst & 0x00FF0000) >> 16) * (0xFF - alpha)) / 0xFF;
408                 jint g = (((src & 0x0000FF00) >> 8)  * alpha + ((dst & 0x0000FF00) >> 8)  * (0xFF - alpha)) / 0xFF;
409                 jint b = (((src & 0x000000FF))       * alpha + ((dst & 0x000000FF))       * (0xFF - alpha)) / 0xFF;
410                 scratch_bits[y * scratch_w + x] = (r << 16) | (g << 8) | b;
411             }
412
413         // copy back from scratch to screen
414         BitBlt((HDC)hdc, dx1, dy1, dx2 - dx1, dy2 - dy1, (HDC)scratch_dc, 0, 0, SRCCOPY);
415
416     } else {
417
418         // FIXME: support alphaOnly case here
419         if (source->hasmask) {
420             BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->maskdc, sx1, sy1, sx2, sy2, SRCAND);
421             BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->hdc, sx1, sy1, sx2, sy2, SRCPAINT);
422         } else {
423             BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->hdc, sx1, sy1, sx2, sy2, SRCCOPY);
424         }
425
426     }
427
428 }
429
430 void org::xwt::plat::Win32$Win32PixelBuffer::fillRect(jint x, jint y, jint x2, jint y2, jint color) {
431     jint w = x2 - x;
432     jint h = y2 - y;
433
434     // sadly, the ability to change the color of a brush didn't arrive until Win2k...
435     HBRUSH brush = CreateSolidBrush(PALETTERGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
436     if (!brush) return;
437     RECT rect = { x, y, x + w, y + h };
438     FillRect((HDC)hdc, &rect, brush);
439     DeleteObject(brush);
440 }
441
442 void org::xwt::plat::Win32$Win32Surface::blit(org::xwt::PixelBuffer* s, jint sx, jint sy, jint dx, jint dy, jint dx2, jint dy2) {
443     // we create the DC lazily to get around some strange race condition in WinXP
444     if (hdc == 0) hdc = (jint)GetDC((HWND)hwnd);
445     BitBlt((HDC)hdc, dx, dy, dx2 - dx, dy2 - dy, (HDC)(((org::xwt::plat::Win32$Win32PixelBuffer*)s)->hdc), sx, sy, SRCCOPY);
446 }
447
448 void org::xwt::plat::Win32$Win32PixelBuffer::natInit() {
449     hbitmap = (jint)CreateCompatibleBitmap((HDC)org::xwt::plat::Win32::desktop_dc, w, h);
450     hdc = (jint)CreateCompatibleDC((HDC)org::xwt::plat::Win32::desktop_dc);
451     SetBkMode((HDC)hdc, TRANSPARENT);
452     SelectObject((HDC)hdc, (HBITMAP)hbitmap);
453 }
454
455 void org::xwt::plat::Win32$Win32PixelBuffer::setClip(jint x, jint y, jint x2, jint y2) {
456     clipx1 = x; clipx2 = x2; clipy1 = y; clipy2 = y2;
457     HRGN hrgn = CreateRectRgn(x, y, x2, y2);
458     SelectClipRgn((HDC)hdc, hrgn);
459     DeleteObject(hrgn);
460 }
461
462 void org::xwt::plat::Win32$Win32PixelBuffer::finalize() {
463     DeleteObject((void*)hdc);
464     DeleteObject((void*)hbitmap);
465 }
466
467
468
469 // Win32Picture /////////////////////////////////////////////////////////////////////////
470
471 void org::xwt::plat::Win32$Win32Picture::natInit() {
472
473     BITMAPINFO bitmapinfo;
474     memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
475     bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
476     bitmapinfo.bmiHeader.biWidth = w;
477     bitmapinfo.bmiHeader.biHeight = -1 * h;
478     bitmapinfo.bmiHeader.biPlanes = 1;
479     bitmapinfo.bmiHeader.biBitCount = 32;
480     bitmapinfo.bmiHeader.biCompression = BI_RGB;
481
482     hbitmap = (jint)CreateCompatibleBitmap((HDC)org::xwt::plat::Win32::desktop_dc, w, h);
483     hdc = (jint)CreateCompatibleDC((HDC)org::xwt::plat::Win32::desktop_dc);
484     SelectObject((HDC)hdc, (HBITMAP)hbitmap);
485     uint32_t* dat = (uint32_t*)elements(data);
486     for(int i=0; i<data->length; i++) if ((dat[i] & 0xFF000000) == 0x00000000) dat[i] = 0x00000000;
487     StretchDIBits((HDC)hdc, 0, 0, w, h, 0, 0, w, h, elements(data), &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
488
489     jint _copy[min(1024, data->length)];
490     jint* copy = data->length > 1024 ? (jint*)malloc(data->length * 4) : _copy;
491
492     memcpy(copy, elements(data), data->length * 4);
493     for(int i=0; i<data->length; i++)
494         if ((copy[i] & 0xFF000000) == 0x00000000) {
495             hasmask = 1;
496             copy[i] = 0x00FFFFFF;
497         } else if ((copy[i] & 0xFF000000) == 0xFF000000) {
498             copy[i] = 0x00000000;
499         } else {
500             hasalpha = 1;
501             hasmask = 0;
502             if (data->length > 1024) free(copy);
503             return;
504         }
505
506     if (!hasmask) {
507         if (data->length > 1024) free(copy);
508         return;
509     }
510
511     //    hmask = (jint)CreateBitmap(w, h, 1, 1, NULL);
512     hmask = (jint)CreateCompatibleBitmap((HDC)org::xwt::plat::Win32::desktop_dc, w, h);
513     maskdc = (jint)CreateCompatibleDC(NULL);
514     SelectObject((HDC)maskdc, (HBITMAP)hmask);
515     StretchDIBits((HDC)maskdc, 0, 0, w, h, 0, 0, w, h, copy, &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
516     if (data->length > 1024) free(copy);
517 }
518
519
520
521 // Win32Surface /////////////////////////////////////////////////////////////////////////////
522
523 void org::xwt::plat::Win32$Win32Surface::natInit(jboolean framed) {
524
525     // Ask the message-handling thread to create a window for us
526     PostThreadMessage((DWORD)org::xwt::plat::Win32::messagePumpThread, WM_USER + 2, (WPARAM)framed, (LPARAM)this);
527     hwndCreated->block();
528
529     // turn on incremental GC now that we have a user-visible interface
530     // [[this is causing segfaults; enable it later...]]
531     // GC_enable_incremental();
532
533     ShowWindow ((HWND)hwnd, SW_SHOWDEFAULT);
534 }
535
536 void org::xwt::plat::Win32$Win32Surface::finalize() { /* DeleteObject((void*)hwnd); */ }
537 void org::xwt::plat::Win32$Win32Surface::toFront() { BringWindowToTop((HWND)hwnd); }
538 void org::xwt::plat::Win32$Win32Surface::toBack() { SetWindowPos((HWND)hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); }
539 void org::xwt::plat::Win32$Win32Surface::_dispose() { PostMessage((HWND)hwnd, WM_USER_DISPOSE, 0, 0); }
540 void org::xwt::plat::Win32$Win32Surface::setInvisible(jboolean h) { ShowWindow((HWND)hwnd, h ? SW_HIDE : SW_SHOWNORMAL); }
541 void org::xwt::plat::Win32$Win32Surface::_setMinimized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMINIMIZED : SW_SHOWNORMAL); }
542 void org::xwt::plat::Win32$Win32Surface::_setMaximized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); }
543 void org::xwt::plat::Win32$Win32Surface::postCursorChange() { PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0); }
544
545 void org::xwt::plat::Win32$Win32Surface::setLocation() {
546     POINT point;
547     RECT rect;
548     point.x = 0;
549     point.y = 0;
550     ClientToScreen((HWND)hwnd, &point);
551     GetWindowRect((HWND)hwnd, &rect);
552     SetWindowPos((HWND)hwnd, NULL, root->x - (point.x - rect.left), root->y - (point.y - rect.top), 0, 0, SWP_NOZORDER | SWP_NOSIZE);
553 }
554
555 void org::xwt::plat::Win32$Win32Surface::setSize(jint w, jint h) {
556     RECT client_rect, window_rect;
557     GetClientRect((HWND)hwnd, &client_rect);
558     GetWindowRect((HWND)hwnd, &window_rect);
559     int width = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left) + w;
560     int height = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top) + h;
561     SetWindowPos((HWND)hwnd, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE);
562 }
563
564 void org::xwt::plat::Win32$Win32Surface::setTitleBarText(java::lang::String* title) {
565     int len = min(1024, JvGetStringUTFLength(title));
566     char buf[len + 1];
567     buf[len] = '\0';
568     JvGetStringUTFRegion(title, 0, len, buf);
569     SetWindowText((HWND)hwnd, buf);
570 }
571
572 void org::xwt::plat::Win32$Win32Surface::setIcon(org::xwt::Picture* p0) {
573
574     org::xwt::plat::Win32$Win32Picture* p = (org::xwt::plat::Win32$Win32Picture*)p0;
575     int icon_width = GetSystemMetrics(SM_CXSMICON);
576     int icon_height = GetSystemMetrics(SM_CYSMICON);
577
578     // we create the DC lazily to get around some strange race condition in WinXP
579     if (hdc == 0) hdc = (jint)GetDC((HWND)hwnd);
580
581     // create the bitmap
582     HBITMAP bit = CreateCompatibleBitmap((HDC)hdc, icon_width, icon_height);
583     HDC memdc = CreateCompatibleDC((HDC)hdc);
584     SelectObject(memdc, bit);
585     BLT((HDC)memdc, 0, 0, icon_width, icon_height, (HDC)(p->hdc), 0, 0, p->getWidth(), p->getHeight(), SRCCOPY);
586
587     // create the mask
588     jint* dat = elements(p->data);
589     HBITMAP bit_mask = CreateBitmap(icon_width, icon_height * 2, 1, 1, NULL);
590     SelectObject(memdc, bit_mask);
591     for(int x=0; x<icon_width; x++)
592         for(int y=0; y<icon_height; y++) {
593             int source = dat[(x * p->getWidth()) / icon_width + ((y * p->getHeight()) / icon_height) * (p->getWidth())];
594             if ((source & 0xFF000000) != 0x00000000) SetPixel(memdc, x, y, PALETTERGB(0, 0, 0));
595             else SetPixel(memdc, x, y, PALETTERGB(255, 255, 255));
596         }
597
598     // instantiate the icon and assign it to the window class
599     ICONINFO ici;
600     ici.fIcon = 1;
601     ici.hbmMask = bit_mask;
602     ici.hbmColor = bit;
603     HICON hicon = CreateIconIndirect(&ici);
604     HICON oldicon = (HICON)SetClassLong((HWND)hwnd, GCL_HICONSM, (LONG)hicon);
605     if (oldicon != NULL) DestroyIcon(oldicon);
606     DeleteObject(memdc);
607 }
608
609 static jstring keyToString(WPARAM wParam);
610 jint org::xwt::plat::Win32$Win32Surface::WndProc(jint _hwnd, jint _iMsg, jint _wParam, jint _lParam) {
611
612     UINT iMsg = (UINT)_iMsg;
613     WPARAM wParam = (WPARAM)_wParam;
614     LPARAM lParam = (LPARAM)_lParam;
615
616     int oldmousex, oldmousey;
617     MINMAXINFO* mmi;
618     POINT point;
619     HWND hwnd2;
620     RECT rect, rect2;
621     RECT client_rect, window_rect;
622     jboolean newinside;
623     int16_t mouse_x;
624     int16_t mouse_y;
625     int addwidth, addheight;
626
627     switch(iMsg) {
628     case WM_DEVMODECHANGE: break;    // FEATURE: color depth changed
629     case WM_DISPLAYCHANGE: break;    // FEATURE: screen size changed
630     case WM_MOUSEWHEEL: break;       // FEATURE: Mouse Wheel
631
632     case WM_SYSKEYDOWN: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
633     case WM_KEYDOWN:
634         KeyPressed(keyToString(wParam));
635         return 0;
636
637     case WM_SYSKEYUP: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
638     case WM_KEYUP:
639         KeyReleased(keyToString(wParam));
640         return 0;
641
642     case WM_SETFOCUS: Focused(true); return 0;
643     case WM_KILLFOCUS: Focused(false); return 0;
644     case WM_LBUTTONDBLCLK: DoubleClick(1); return 0;
645     case WM_RBUTTONDBLCLK: DoubleClick(2); return 0;
646     case WM_MBUTTONDBLCLK: DoubleClick(3); return 0;
647     case WM_LBUTTONDOWN: Press(1); return 0;
648     case WM_RBUTTONDOWN: Press(2); return 0;
649     case WM_MBUTTONDOWN: Press(3); return 0;
650     case WM_CLOSE: Close(); return 0;
651     case WM_ERASEBKGND: return 0;
652
653     case WM_LBUTTONUP:
654     case WM_RBUTTONUP:
655     case WM_MBUTTONUP:
656         Release(iMsg == WM_LBUTTONUP ? 1 : (iMsg == WM_RBUTTONUP ? 2 : 3));
657         if (captured && !inside) {
658             ReleaseCapture();
659             SetCursor((HCURSOR)previous_cursor);
660         }
661         return 0;
662
663     case WM_MOVING:
664         GetClientRect((HWND)hwnd, &rect);
665         PosChange(rect.left, rect.top);
666         return 0;
667
668     case WM_SIZE:
669         if (wParam == SIZE_MINIMIZED) {
670             if (maximized) Maximized(false);
671             Minimized(true);
672         } else if (wParam == SIZE_MAXIMIZED) {
673             if (minimized) Minimized(false); 
674             Maximized(true);
675         } else {
676             if (minimized) Minimized(false); 
677             if (maximized) Maximized(false);
678         }
679         // deliberately fall through to WM_SIZING
680
681     case WM_SIZING:
682         GetClientRect((HWND)hwnd, &rect);
683         SizeChange(rect.right - rect.left, rect.bottom - rect.top);
684         return 0;
685
686     case WM_MOUSEMOVE:
687         if (mousex == lParam & 0xFFFF && mousey == (lParam & 0xFFFF0000) >> 16) return 0;
688
689         GetClientRect((HWND)hwnd, &rect);
690         point.x = mouse_x = lParam & 0xFFFF;
691         point.y = mouse_y = (lParam & 0xFFFF0000) >> 16;
692         ClientToScreen((HWND)hwnd, &point);
693         hwnd2 = WindowFromPoint(point);
694
695         newinside = hwnd2 == (HWND)hwnd && mouse_x > 0 && mouse_y > 0 && mouse_x < (rect.right - rect.left) && mouse_y < (rect.bottom - rect.top) ? 1 : 0;
696
697         Move(mouse_x, mouse_y);
698
699         if (newinside && !inside) {
700             inside = 1;
701             SetCapture((HWND)hwnd);
702             captured = 1;
703             previous_cursor = (jint)GetCursor();
704             PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0);
705
706         } else if (!newinside && inside) {
707             inside = 0;
708             if (!button1 && !button2 && !button3) {
709                 ReleaseCapture();
710                 captured = 0;
711                 SetCursor((HCURSOR)previous_cursor);
712             }
713         }
714
715         return 0;
716
717     case WM_USER_SETCURSOR:
718         // I can't find documentation of this anywhere, but in my experience, an event must be received between an invocation
719         // of SetCapture() and SetCursor(). Furthermore, it seems that after calling SetCursor(), the cursor will not change until a
720         // return from WndProc (ie the completion of the processing of some event).  Hence, we use WM_USER to indicate that the screen
721         // cursor should be re-set to "current_cursor".
722         SetCursor((HCURSOR)current_cursor);
723         return 0;
724
725     case WM_USER_DISPOSE:
726         // used to signal that we should destroy ourselves
727         DestroyWindow((HWND)hwnd);
728         return 0;
729
730     case WM_GETMINMAXINFO:
731         GetClientRect((HWND)hwnd, &client_rect);
732         GetWindowRect((HWND)hwnd, &window_rect);
733         addwidth = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left);
734         addheight = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top);
735         mmi = (MINMAXINFO*)lParam;
736         mmi->ptMinTrackSize.x = ((uint32_t)root->minwidth) + addwidth;
737         mmi->ptMinTrackSize.y = ((uint32_t)root->minheight) + addheight;
738         mmi->ptMaxTrackSize.x = min(org::xwt::plat::Win32::getScreenWidth(), ((uint32_t)root->maxwidth) + addwidth);
739         mmi->ptMaxTrackSize.y = min(org::xwt::plat::Win32::getScreenHeight(), ((uint32_t)root->maxheight) + addheight);
740         return 0;
741         
742     case WM_PAINT: 
743         PAINTSTRUCT ps;
744         BeginPaint((HWND)org::xwt::plat::Win32$Win32Surface::hwnd, &ps);
745         Dirty(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top);
746         EndPaint((HWND)org::xwt::plat::Win32$Win32Surface::hwnd, &ps);
747         return 0;
748
749     default: break;
750     }  
751
752     return DefWindowProc((HWND)hwnd, iMsg, wParam, lParam);
753 }
754
755
756 // Key Translator Helper /////////////////////////////////////////////////////////////////////
757
758 static char keyarr [256] = { 0 };
759 static jstring keyToString(WPARAM wParam) {
760     char arr[8];
761     keyarr[VK_CAPITAL] = GetKeyState(VK_CAPITAL);
762     keyarr[VK_LSHIFT] = GetKeyState(VK_LSHIFT);
763     keyarr[VK_RSHIFT] = GetKeyState(VK_RSHIFT);
764     keyarr[VK_SHIFT] = GetKeyState(VK_SHIFT);
765
766     if (ToAsciiEx(wParam, 0, (BYTE*)keyarr, (WORD*)arr, 0, GetKeyboardLayout(0)) == 1) {
767         switch (arr[0]) {
768         case '\t': return JvNewStringLatin1("tab");
769         case 0x1b: return JvNewStringLatin1("escape");
770         case '\n': return JvNewStringLatin1("enter");
771         case '\r': return JvNewStringLatin1("enter");
772         case 0x08: return JvNewStringLatin1("back_space");
773         default: return JvNewStringLatin1(arr, 1);
774         }
775         
776     } else switch (wParam) {
777     case VK_CLEAR: return JvNewStringLatin1("clear");
778     case VK_SHIFT: return JvNewStringLatin1("shift");
779     case VK_CONTROL: return JvNewStringLatin1("control");
780     case VK_CAPITAL: return JvNewStringLatin1("caps_lock");
781     case VK_MENU: return JvNewStringLatin1("alt");
782     case VK_PAUSE: return JvNewStringLatin1("pause");
783     case VK_PRIOR: return JvNewStringLatin1("page_up");
784     case VK_NEXT: return JvNewStringLatin1("page_down");
785     case VK_END: return JvNewStringLatin1("end");
786     case VK_HOME: return JvNewStringLatin1("home");
787     case VK_LEFT: return JvNewStringLatin1("left");
788     case VK_UP: return JvNewStringLatin1("up");
789     case VK_RIGHT: return JvNewStringLatin1("right");
790     case VK_DOWN: return JvNewStringLatin1("down");
791     case VK_INSERT: return JvNewStringLatin1("insert");
792     case VK_DELETE: return JvNewStringLatin1("delete");
793     case VK_NUMPAD0: return JvNewStringLatin1("numpad0");
794     case VK_NUMPAD1: return JvNewStringLatin1("numpad1");
795     case VK_NUMPAD2: return JvNewStringLatin1("numpad2");
796     case VK_NUMPAD3: return JvNewStringLatin1("numpad3");
797     case VK_NUMPAD4: return JvNewStringLatin1("numpad4");
798     case VK_NUMPAD5: return JvNewStringLatin1("numpad5");
799     case VK_NUMPAD6: return JvNewStringLatin1("numpad6");
800     case VK_NUMPAD7: return JvNewStringLatin1("numpad7");
801     case VK_NUMPAD8: return JvNewStringLatin1("numpad8");
802     case VK_NUMPAD9: return JvNewStringLatin1("numpad9");
803     case VK_F1: return JvNewStringLatin1("f1");
804     case VK_F2: return JvNewStringLatin1("f2");
805     case VK_F3: return JvNewStringLatin1("f3");
806     case VK_F4: return JvNewStringLatin1("f4");
807     case VK_F5: return JvNewStringLatin1("f5");
808     case VK_F6: return JvNewStringLatin1("f6");
809     case VK_F7: return JvNewStringLatin1("f7");
810     case VK_F8: return JvNewStringLatin1("f8");
811     case VK_F9: return JvNewStringLatin1("f9");
812     case VK_F10: return JvNewStringLatin1("f10");
813     case VK_F11: return JvNewStringLatin1("f11");
814     case VK_F12: return JvNewStringLatin1("f12");
815     case VK_NUMLOCK: return JvNewStringLatin1("num_lock");
816     case VK_SCROLL: return JvNewStringLatin1("scroll_lock");
817     case VK_LSHIFT: return JvNewStringLatin1("shift");
818     case VK_RSHIFT: return JvNewStringLatin1("shift");
819     case VK_LCONTROL: return JvNewStringLatin1("control");
820     case VK_RCONTROL: return JvNewStringLatin1("control");
821     }
822     return NULL;
823 }