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