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