2003/09/25 14:01:19
[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/Platform.h>
30 #include <org/xwt/plat/Win32.h>
31 #include <org/xwt/plat/Win32$Win32Surface.h>
32 #include <org/xwt/plat/Win32$Win32PixelBuffer.h>
33 #include <org/xwt/plat/Win32$Win32Picture.h>
34 #include <org/xwt/util/Log.h>
35 #include <org/xwt/util/Semaphore.h>
36
37 // for debugging
38 #include <java/lang/System.h>
39 #include <java/io/PrintStream.h>
40
41 #define WM_USER_SETCURSOR WM_USER
42 #define WM_USER_DISPOSE (WM_USER + 1)
43 #define WM_USER_CREATEWINDOW (WM_USER + 2)
44 #define WS_NORMAL (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
45
46 // FEATURE: there are lots of places where HANDLE's get casted to jint's -- this will break on Win64
47 //          a clean way to do this would be to '#define jraw (gnu::gcj::RawData*)'
48
49 // Callbacks ////////////////////////////////////////////////////////////////////
50
51 LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
52     org::xwt::plat::Win32$Win32Surface* surface =
53         (org::xwt::plat::Win32$Win32Surface*)org::xwt::plat::Win32::hwndToWin32SurfaceMap->get(new java::lang::Integer((jint)hwnd));
54
55     if (surface != NULL) {
56         return (LRESULT)surface->WndProc((jint)hwnd, (jint)iMsg, (jint)wParam, (jint)lParam);
57
58     } else {
59         // this is really lame -- Win32 insists on being able to call your WndProc BEFORE CreateWindow returns...
60         return DefWindowProc(hwnd, iMsg, wParam, lParam);
61     }
62 }
63
64
65 // Initialization ////////////////////////////////////////////////////////////////////
66
67 static int window_class_counter = 0;
68
69 jstring org::xwt::plat::Win32::getTempPath() {
70     char buf[1024];
71     DWORD ret = GetTempPath(1024, buf);
72     if (ret == 0) criticalAbort(JvNewStringLatin1("GetTempPath() failed"));
73     return JvNewStringLatin1(buf);
74 }
75
76 // XOR mask for the hand cursor
77 static unsigned char hand_cursor_xor[32 * 4] = {
78   0x00, 0x00, 0x00, 0x00,
79   0x00, 0x0C, 0x00, 0x00,
80   0x00, 0x0C, 0x00, 0x00,
81   0x00, 0x0C, 0x00, 0x00,
82   0x00, 0x0C, 0x00, 0x00,
83   0x00, 0x0C, 0x00, 0x00,
84   0x00, 0x0D, 0x80, 0x00,
85   0x00, 0x0D, 0xB0, 0x00,
86   0x00, 0x0D, 0xB4, 0x00,
87   0x00, 0x0D, 0xB6, 0x00,
88   0x00, 0xCF, 0xF6, 0x00,
89   0x00, 0xEF, 0xFE, 0x00,
90   0x00, 0x6F, 0xFE, 0x00,
91   0x00, 0x2F, 0xFE, 0x00,
92   0x00, 0x3F, 0xFE, 0x00,
93   0x00, 0x1F, 0xFE, 0x00,
94   0x00, 0x1F, 0xFC, 0x00,
95   0x00, 0x0F, 0xFC, 0x00,
96   0x00, 0x0F, 0xFC, 0x00,
97   0x00, 0x07, 0xF8, 0x00,
98   0x00, 0x07, 0xF8, 0x00,
99   0x00, 0x00, 0x00, 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 };
111
112 // AND mask for the hand cursor
113 static unsigned char hand_cursor_and[32 * 4] = {
114   0xFF, 0xF3, 0xFF, 0xFF,
115   0xFF, 0xE1, 0xFF, 0xFF,
116   0xFF, 0xE1, 0xFF, 0xFF,
117   0xFF, 0xE1, 0xFF, 0xFF,
118   0xFF, 0xE1, 0xFF, 0xFF,
119   0xFF, 0xE0, 0x7F, 0xFF,
120   0xFF, 0xE0, 0x0F, 0xFF,
121   0xFF, 0xE0, 0x03, 0xFF,
122   0xFF, 0xE0, 0x01, 0xFF,
123   0xFF, 0x20, 0x00, 0xFF,
124   0xFE, 0x00, 0x00, 0xFF,
125   0xFE, 0x00, 0x00, 0xFF,
126   0xFF, 0x00, 0x00, 0xFF,
127   0xFF, 0x80, 0x00, 0xFF,
128   0xFF, 0x80, 0x00, 0xFF,
129   0xFF, 0xC0, 0x00, 0xFF,
130   0xFF, 0xC0, 0x01, 0xFF,
131   0xFF, 0xE0, 0x01, 0xFF,
132   0xFF, 0xE0, 0x01, 0xFF,
133   0xFF, 0xF0, 0x03, 0xFF,
134   0xFF, 0xF0, 0x03, 0xFF,
135   0xFF, 0xF0, 0x03, 0xFF,
136   0xFF, 0xFF, 0xFF, 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 };
147
148
149 void org::xwt::plat::Win32::natInit() {
150
151     // grab desktop dc/handle
152     desktop_handle = (jint)GetDesktopWindow();
153     desktop_dc = (jint)GetDC((HWND)desktop_handle);
154
155     // create cursors
156     org::xwt::plat::Win32::wait_cursor = (jint)LoadCursor(NULL, IDC_WAIT);
157     org::xwt::plat::Win32::default_cursor = (jint)LoadCursor(NULL, IDC_ARROW);
158     org::xwt::plat::Win32::crosshair_cursor = (jint)LoadCursor(NULL, IDC_CROSS);
159     org::xwt::plat::Win32::text_cursor = (jint)LoadCursor(NULL, IDC_IBEAM);
160     org::xwt::plat::Win32::move_cursor = (jint)LoadCursor(NULL, IDC_SIZEALL);
161     org::xwt::plat::Win32::sizenesw_cursor = (jint)LoadCursor(NULL, IDC_SIZENESW);
162     org::xwt::plat::Win32::sizens_cursor = (jint)LoadCursor(NULL, IDC_SIZENS);
163     org::xwt::plat::Win32::sizenwse_cursor = (jint)LoadCursor(NULL, IDC_SIZENWSE);
164     org::xwt::plat::Win32::sizewe_cursor = (jint)LoadCursor(NULL, IDC_SIZEWE);
165     org::xwt::plat::Win32::hand_cursor = (jint)CreateCursor(GetModuleHandle(NULL), 14, 1, 32, 32, hand_cursor_and, hand_cursor_xor);
166
167     messagePumpThread = (jint)GetCurrentThreadId();
168     messagePumpStarted->release();
169
170     MSG msg;
171     while(GetMessage(&msg, (HWND)NULL, 0, 0) > 0) {
172
173         if (msg.message == WM_USER_CREATEWINDOW) {
174             org::xwt::plat::Win32$Win32Surface *surface = (org::xwt::plat::Win32$Win32Surface*)msg.lParam;
175
176             // we must create a unique window class name for each
177             // window so that minimization icons can be set independantly
178             char buf[255];
179             sprintf(buf, "XWT_WINDOW_CLASS_%i", window_class_counter++);
180
181             WNDCLASSEX wc;
182             wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
183             wc.lpfnWndProc = WndProc;
184             wc.cbClsExtra = 0;
185             wc.cbSize = sizeof(WNDCLASSEX);
186             wc.cbWndExtra = 0;
187             wc.hInstance = GetModuleHandle(NULL);
188             wc.hIcon = NULL;
189             wc.hIconSm = NULL;
190             wc.hCursor = NULL;
191             wc.hbrBackground = (HBRUSH)(COLOR_SCROLLBAR + 1);
192             wc.lpszMenuName = NULL;
193             wc.lpszClassName = buf;
194             RegisterClassEx(&wc);
195             
196             surface->hwnd = (jint)CreateWindow(wc.lpszClassName, TEXT(""), msg.wParam ? WS_NORMAL : WS_POPUP, 200, 200, 100, 100,
197                                                (HWND__*)NULL, (HMENU__*)NULL, GetModuleHandle(NULL), (LPVOID)NULL);
198             SetFocus((HWND)surface->hwnd);
199             surface->hwndCreated->release();
200             
201         } else {
202             TranslateMessage(&msg);
203             if (msg.message == WM_LBUTTONDOWN || msg.message == WM_RBUTTONDOWN || msg.message == WM_MBUTTONDOWN) SetFocus(msg.hwnd);
204             DispatchMessage(&msg);
205         }
206
207     }
208     java::lang::System::exit(-1);
209 }
210
211
212 // Platform Methods ///////////////////////////////////////////////////////////////////
213
214 jstring org::xwt::plat::Win32::_getEnv(jstring key) {
215     int len = JvGetStringUTFLength(key);
216     char buf[len + 1];
217     JvGetStringUTFRegion(key, 0, len, buf);
218     buf[len] = '\0';
219     char buf2[1024];
220     DWORD ret = GetEnvironmentVariable(buf, buf2, 1024);
221     if (ret > 0 && ret < 1024) return JvNewStringLatin1(buf2);
222     return NULL;
223 }
224
225 jstring org::xwt::plat::Win32::_fileDialog(jstring suggestedFileName, jboolean write) {
226
227     char buf[1024];
228     OPENFILENAME ofn;
229     memset(buf, 0, 1024);
230     memset(&ofn, 0, sizeof(OPENFILENAME));
231
232     if (suggestedFileName != NULL)
233         JvGetStringUTFRegion(suggestedFileName, 0, min(1023, JvGetStringUTFLength(suggestedFileName)), buf);
234     
235     ofn.lStructSize = sizeof(OPENFILENAME);
236     ofn.nMaxCustFilter = 0;
237     ofn.lpstrFile = buf;
238     ofn.nMaxFile = 1024;
239
240     if (write) ofn.Flags |= OFN_OVERWRITEPROMPT;
241     ofn.Flags |= OFN_HIDEREADONLY;
242
243     int ret = write ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn);
244     return ret == 0 ? NULL : JvNewStringLatin1(buf);
245 }
246
247 void org::xwt::plat::Win32::__detectProxy(JArray<jstring>* container) {
248
249     HKEY hkey;
250     char buf[1024];
251     DWORD buflen = 1024;
252     DWORD type;
253     LONG result = RegOpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", &hkey);
254     if (result != ERROR_SUCCESS) return;
255     
256     buf[0] = '\0';
257     type = REG_SZ;
258     buflen = 1024;
259     result = RegQueryValueEx(hkey, "AutoConfigURL", NULL, &type, (LPBYTE)buf, &buflen);
260     buf[buflen] = '\0';
261     if (result == ERROR_SUCCESS) elements(container)[2] = JvNewStringLatin1(buf);
262
263     buf[0] = '\0';
264     type = REG_BINARY;
265     RegQueryValueEx(hkey, "ProxyEnable", NULL, &type, (LPBYTE)buf, &buflen);
266     if (buf[0] != 1) return;
267
268     buf[0] = '\0';
269     type = REG_SZ;
270     buflen = 1024;
271     RegQueryValueEx(hkey, "ProxyServer", NULL, &type, (LPBYTE)buf, &buflen);
272     buf[buflen] = '\0';
273     elements(container)[0] = JvNewStringLatin1(buf);
274
275     buf[0] = '\0';
276     buflen = 1024;
277     RegQueryValueEx(hkey, "ProxyOverride", NULL, &type, (LPBYTE)buf, &buflen);
278     buf[buflen] = '\0';
279     elements(container)[1] = JvNewStringLatin1(buf);
280 }
281
282 jstring org::xwt::plat::Win32::_getClipBoard() {
283     OpenClipboard((HWND)desktop_handle);
284     HGLOBAL hmem = GetClipboardData(CF_TEXT);
285     if (hmem == NULL) return NULL;
286     char* buf = (char*)GlobalLock(hmem);
287     if (buf == NULL) return NULL;
288     jstring ret = JvNewStringLatin1(buf);
289     GlobalUnlock(hmem);
290     CloseClipboard();
291     return ret;
292 }
293
294 void org::xwt::plat::Win32::_setClipBoard(jstring s) {
295     OpenClipboard((HWND)desktop_handle);
296     HGLOBAL hmem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, JvGetStringUTFLength(s) + 1);
297     if (hmem == NULL) return;
298     char* buf = (char*)GlobalLock(hmem);
299     if (buf == NULL) return;
300     JvGetStringUTFRegion(s, 0, JvGetStringUTFLength(s), buf);
301     buf[JvGetStringUTFLength(s)] = '\0';
302     GlobalUnlock(hmem);
303     SetClipboardData(CF_TEXT, hmem);
304     CloseClipboard();
305 }
306
307 void org::xwt::plat::Win32::_criticalAbort(jstring message) {
308     char buf[JvGetStringUTFLength(message) + 1];
309     buf[JvGetStringUTFLength(message)] = '\0';
310     JvGetStringUTFRegion(message, 0, JvGetStringUTFLength(message), buf);
311     MessageBox (NULL, buf, "XWT Cannot Continue", MB_OK | MB_ICONSTOP | MB_TASKMODAL | MB_SETFOREGROUND);
312     java::lang::System::exit(-1);
313 }
314
315 jint org::xwt::plat::Win32::_getScreenWidth() {
316     RECT rect;
317     SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
318     return rect.right - rect.left;
319 }
320
321 jint org::xwt::plat::Win32::_getScreenHeight() {
322     RECT rect;
323     SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
324     return rect.bottom - rect.top;
325 }
326
327 jboolean org::xwt::plat::Win32::_newBrowserWindow_(jstring url) {
328
329     int len = min(2048, JvGetStringUTFLength(url));
330     char buf[len + 1];
331     JvGetStringUTFRegion(url, 0, len, buf);
332     buf[len] = '\0';
333
334     SHELLEXECUTEINFO ei;
335     memset(&ei, 0, sizeof(ei));
336     ei.cbSize = sizeof(ei);
337     ei.lpVerb = "open";
338     ei.lpFile = buf;
339     ei.fMask  = SEE_MASK_NOCLOSEPROCESS;
340     ei.nShow  = SW_SHOWDEFAULT;
341     return (ShellExecuteEx(&ei) == 0);
342
343 }
344
345
346 // Win32PixelBuffer /////////////////////////////////////////////////////////////////////////
347
348 // This is a scratch area; when blitting a translucent image, we copy the underlying data here first.
349 // Since all drawing operations are single-threaded, it's safe to use a global here.
350 static HBITMAP scratch = NULL;
351 static HDC scratch_dc = NULL;
352 static jint* scratch_bits = NULL;
353 static jint scratch_w = 0;
354 static jint scratch_h = 0;
355
356 #define BLT(dest, dx1, dy1, dx2, dy2, src, sx1, sy1, sx2, sy2, op)                                   \
357     if ((dx2 - dx1 == sx2 - sx1) && (dy2 - dy1 == sy2 - sy1))                                        \
358         BitBlt(dest, dx1, dy1, dx2 - dx1, dy2 - dy1, src, sx1, sy1, op);                             \
359     else                                                                                             \
360         StretchBlt(dest, dx1, dy1, dx2 - dx1, dy2 - dy1, src, sx1, sy1, sx2 - sx1, sy2 - sy1, op);
361         
362 void org::xwt::plat::Win32$Win32PixelBuffer::drawPicture(org::xwt::Picture* source0,
363                                                           jint dx1, jint dy1, jint dx2, jint dy2,
364                                                           jint sx1, jint sy1, jint sx2, jint sy2) {
365     org::xwt::plat::Win32$Win32Picture* source = (org::xwt::plat::Win32$Win32Picture*)source0;
366
367     if (source->hasalpha) {
368
369         if (scratch == NULL || scratch_w < dx2 - dx1 || scratch_h < dy2 - dy1) {
370             if (scratch_dc != NULL) DeleteDC(scratch_dc);
371             if (scratch != NULL) DeleteObject(scratch);
372             scratch_w = max(dx2 - dx1, scratch_w);
373             scratch_h = max(dy2 - dy1, scratch_h);
374
375             BITMAPINFO bitmapinfo;
376             memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
377             bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
378             bitmapinfo.bmiHeader.biWidth = scratch_w;
379             bitmapinfo.bmiHeader.biHeight = -1 * scratch_h;
380             bitmapinfo.bmiHeader.biPlanes = 1;
381             bitmapinfo.bmiHeader.biBitCount = 32;
382             bitmapinfo.bmiHeader.biCompression = BI_RGB;
383
384             // create section DIB
385             scratch = CreateDIBSection(NULL, &bitmapinfo, DIB_RGB_COLORS, (void**)&scratch_bits, NULL, 0);
386             scratch_dc = CreateCompatibleDC((HDC)org::xwt::plat::Win32::desktop_dc);
387             SelectObject(scratch_dc, scratch);
388         }
389
390         // copy from screen to scratch
391         BitBlt((HDC)scratch_dc, 0, 0, dx2 - dx1, dy2 - dy1, (HDC)hdc, dx1, dy1, SRCCOPY);
392
393         // apply alpha-blending to scratch
394         jint* dat = elements(source->data);
395
396         for(int x = max(clipx1, dx1) - dx1; x < min(clipx2, dx2) - dx1; x++)
397             for(int y = max(clipy1, dy1) - dy1; y < min(clipy2, dy2) - dy1; y++) {
398                 int sx = (x * (sx2 - sx1)) / (dx2 - dx1) + sx1;
399                 int sy = (y * (sy2 - sy1)) / (dy2 - dy1) + sy1;
400                 jint dst = scratch_bits[y * scratch_w + x];
401                 jint src = dat[sy * source->getWidth() + sx];
402                 jint alpha = (src & 0xFF000000) >> 24;
403                 jint r = (((src & 0x00FF0000) >> 16) * alpha + ((dst & 0x00FF0000) >> 16) * (0xFF - alpha)) / 0xFF;
404                 jint g = (((src & 0x0000FF00) >> 8)  * alpha + ((dst & 0x0000FF00) >> 8)  * (0xFF - alpha)) / 0xFF;
405                 jint b = (((src & 0x000000FF))       * alpha + ((dst & 0x000000FF))       * (0xFF - alpha)) / 0xFF;
406                 scratch_bits[y * scratch_w + x] = (r << 16) | (g << 8) | b;
407             }
408
409         // copy back from scratch to screen
410         BitBlt((HDC)hdc, dx1, dy1, dx2 - dx1, dy2 - dy1, (HDC)scratch_dc, 0, 0, SRCCOPY);
411
412     } else {
413         if (source->hasmask) {
414             BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->maskdc, sx1, sy1, sx2, sy2, SRCAND);
415             BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->hdc, sx1, sy1, sx2, sy2, SRCPAINT);
416         } else {
417             BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->hdc, sx1, sy1, sx2, sy2, SRCCOPY);
418         }
419
420     }
421
422 }
423
424 void org::xwt::plat::Win32$Win32PixelBuffer::fillRect(jint x, jint y, jint x2, jint y2, jint color) {
425     jint w = x2 - x;
426     jint h = y2 - y;
427
428     // sadly, the ability to change the color of a brush didn't arrive until Win2k...
429     HBRUSH brush = CreateSolidBrush(PALETTERGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
430     if (!brush) return;
431     RECT rect = { x, y, x + w, y + h };
432     FillRect((HDC)hdc, &rect, brush);
433     DeleteObject(brush);
434 }
435
436 void org::xwt::plat::Win32$Win32Surface::blit(org::xwt::PixelBuffer* s, jint sx, jint sy, jint dx, jint dy, jint dx2, jint dy2) {
437     // we create the DC lazily to get around some strange race condition in WinXP
438     if (hdc == 0) hdc = (jint)GetDC((HWND)hwnd);
439     BitBlt((HDC)hdc, dx, dy, dx2 - dx, dy2 - dy, (HDC)(((org::xwt::plat::Win32$Win32PixelBuffer*)s)->hdc), sx, sy, SRCCOPY);
440 }
441
442 void org::xwt::plat::Win32$Win32PixelBuffer::natInit() {
443     hbitmap = (jint)CreateCompatibleBitmap((HDC)org::xwt::plat::Win32::desktop_dc, w, h);
444     hdc = (jint)CreateCompatibleDC((HDC)org::xwt::plat::Win32::desktop_dc);
445     SetBkMode((HDC)hdc, TRANSPARENT);
446     SelectObject((HDC)hdc, (HBITMAP)hbitmap);
447 }
448
449 void org::xwt::plat::Win32$Win32PixelBuffer::setClip(jint x, jint y, jint x2, jint y2) {
450     clipx1 = x; clipx2 = x2; clipy1 = y; clipy2 = y2;
451     HRGN hrgn = CreateRectRgn(x, y, x2, y2);
452     SelectClipRgn((HDC)hdc, hrgn);
453     DeleteObject(hrgn);
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     BLT((HDC)memdc, 0, 0, icon_width, icon_height, (HDC)(p->hdc), 0, 0, p->getWidth(), p->getHeight(), 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 }