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