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