8e0caaeb37f5b1b45d2f5f96f88930166ed6990a
[org.ibex.core.git] / src / org / ibex / plat / Win32.cc
1 // Copyright 2000-2005 the Contributors, as shown in the revision logs.
2 // Licensed under the GNU General Public License version 2 ("the License").
3 // You may not use this file except in compliance with the License.
4
5 #include "GCJ.cc"
6
7 // we have to do this because the jpeg libraries use the symbol 'INT32'
8 #define INT32 WIN32_INT32
9
10 // this has to precede the others so we don't get collisions on min/max
11 #include <org/ibex/js/JS.h>
12 #include <org/ibex/Box.h>
13
14 #include <stdint.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17
18 #include <windows.h>
19 #include <mmsystem.h>
20 #undef STRICT
21 #undef MAX_PRIORITY
22 #undef MIN_PRIORITY
23 #undef NORM_PRIORITY
24
25 #include <gcj/cni.h>
26
27 #include <java/lang/Integer.h>
28 #include <java/util/Hashtable.h>
29 #include <org/ibex/Box.h>
30 #include <org/ibex/Surface.h>
31 #include <org/ibex/PixelBuffer.h>
32 #include <org/ibex/Picture.h>
33 #include <org/ibex/Platform.h>
34 #include <org/ibex/plat/Win32.h>
35 #include <org/ibex/plat/Win32$Win32Surface.h>
36 #include <org/ibex/plat/Win32$Win32PixelBuffer.h>
37 #include <org/ibex/plat/Win32$Win32Picture.h>
38 #include <org/ibex/util/Log.h>
39 #include <org/ibex/util/Semaphore.h>
40
41 // for debugging
42 #include <java/lang/System.h>
43 #include <java/io/PrintStream.h>
44
45 #define WM_USER_SETCURSOR WM_USER
46 #define WM_USER_DISPOSE (WM_USER + 1)
47 #define WM_USER_CREATEWINDOW (WM_USER + 2)
48 #define WS_NORMAL (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
49
50 // FEATURE: there are lots of places where HANDLE's get casted to jint's -- this will break on Win64
51 //          a clean way to do this would be to '#define jraw (gnu::gcj::RawData*)'
52
53 // Callbacks ////////////////////////////////////////////////////////////////////
54
55 LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
56     org::ibex::plat::Win32$Win32Surface* surface =
57         (org::ibex::plat::Win32$Win32Surface*)org::ibex::plat::Win32::hwndToWin32SurfaceMap->get(new java::lang::Integer((jint)hwnd));
58
59     if (surface != NULL) {
60         return (LRESULT)surface->WndProc((jint)hwnd, (jint)iMsg, (jint)wParam, (jint)lParam);
61
62     } else {
63         // this is really lame -- Win32 insists on being able to call your WndProc BEFORE CreateWindow returns...
64         return DefWindowProc(hwnd, iMsg, wParam, lParam);
65     }
66 }
67
68
69 // Initialization ////////////////////////////////////////////////////////////////////
70
71 static int window_class_counter = 0;
72
73 jstring org::ibex::plat::Win32::getTempPath() {
74     char buf[1024];
75     DWORD ret = GetTempPath(1024, buf);
76     if (ret == 0) criticalAbort(JvNewStringLatin1("GetTempPath() failed"));
77     return JvNewStringLatin1(buf);
78 }
79
80 // XOR mask for the hand cursor
81 static unsigned char hand_cursor_xor[32 * 4] = {
82   0x00, 0x00, 0x00, 0x00,
83   0x00, 0x0C, 0x00, 0x00,
84   0x00, 0x0C, 0x00, 0x00,
85   0x00, 0x0C, 0x00, 0x00,
86   0x00, 0x0C, 0x00, 0x00,
87   0x00, 0x0C, 0x00, 0x00,
88   0x00, 0x0D, 0x80, 0x00,
89   0x00, 0x0D, 0xB0, 0x00,
90   0x00, 0x0D, 0xB4, 0x00,
91   0x00, 0x0D, 0xB6, 0x00,
92   0x00, 0xCF, 0xF6, 0x00,
93   0x00, 0xEF, 0xFE, 0x00,
94   0x00, 0x6F, 0xFE, 0x00,
95   0x00, 0x2F, 0xFE, 0x00,
96   0x00, 0x3F, 0xFE, 0x00,
97   0x00, 0x1F, 0xFE, 0x00,
98   0x00, 0x1F, 0xFC, 0x00,
99   0x00, 0x0F, 0xFC, 0x00,
100   0x00, 0x0F, 0xFC, 0x00,
101   0x00, 0x07, 0xF8, 0x00,
102   0x00, 0x07, 0xF8, 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   0x00, 0x00, 0x00, 0x00,
112   0x00, 0x00, 0x00, 0x00,
113   0x00, 0x00, 0x00, 0x00
114 };
115
116 // AND mask for the hand cursor
117 static unsigned char hand_cursor_and[32 * 4] = {
118   0xFF, 0xF3, 0xFF, 0xFF,
119   0xFF, 0xE1, 0xFF, 0xFF,
120   0xFF, 0xE1, 0xFF, 0xFF,
121   0xFF, 0xE1, 0xFF, 0xFF,
122   0xFF, 0xE1, 0xFF, 0xFF,
123   0xFF, 0xE0, 0x7F, 0xFF,
124   0xFF, 0xE0, 0x0F, 0xFF,
125   0xFF, 0xE0, 0x03, 0xFF,
126   0xFF, 0xE0, 0x01, 0xFF,
127   0xFF, 0x20, 0x00, 0xFF,
128   0xFE, 0x00, 0x00, 0xFF,
129   0xFE, 0x00, 0x00, 0xFF,
130   0xFF, 0x00, 0x00, 0xFF,
131   0xFF, 0x80, 0x00, 0xFF,
132   0xFF, 0x80, 0x00, 0xFF,
133   0xFF, 0xC0, 0x00, 0xFF,
134   0xFF, 0xC0, 0x01, 0xFF,
135   0xFF, 0xE0, 0x01, 0xFF,
136   0xFF, 0xE0, 0x01, 0xFF,
137   0xFF, 0xF0, 0x03, 0xFF,
138   0xFF, 0xF0, 0x03, 0xFF,
139   0xFF, 0xF0, 0x03, 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   0xFF, 0xFF, 0xFF, 0xFF,
148   0xFF, 0xFF, 0xFF, 0xFF,
149   0xFF, 0xFF, 0xFF, 0xFF
150 };
151
152 void org::ibex::plat::Win32::natPreInit() {
153   // Win32 throws stderr in the trash unless you designate your binary as a "console binary"
154   if (org::ibex::util::Log::logstream == java::lang::System::err) {
155         AllocConsole();
156         freopen("CONOUT$", "w+t", stderr);
157     }
158 }
159
160 void org::ibex::plat::Win32::natInit() {
161
162     // grab desktop dc/handle
163     desktop_handle = (jint)GetDesktopWindow();
164     desktop_dc = (jint)GetDC((HWND)desktop_handle);
165
166     // create cursors
167     org::ibex::plat::Win32::wait_cursor = (jint)LoadCursor(NULL, IDC_WAIT);
168     org::ibex::plat::Win32::default_cursor = (jint)LoadCursor(NULL, IDC_ARROW);
169     org::ibex::plat::Win32::crosshair_cursor = (jint)LoadCursor(NULL, IDC_CROSS);
170     org::ibex::plat::Win32::text_cursor = (jint)LoadCursor(NULL, IDC_IBEAM);
171     org::ibex::plat::Win32::move_cursor = (jint)LoadCursor(NULL, IDC_SIZEALL);
172     org::ibex::plat::Win32::sizenesw_cursor = (jint)LoadCursor(NULL, IDC_SIZENESW);
173     org::ibex::plat::Win32::sizens_cursor = (jint)LoadCursor(NULL, IDC_SIZENS);
174     org::ibex::plat::Win32::sizenwse_cursor = (jint)LoadCursor(NULL, IDC_SIZENWSE);
175     org::ibex::plat::Win32::sizewe_cursor = (jint)LoadCursor(NULL, IDC_SIZEWE);
176     org::ibex::plat::Win32::hand_cursor = (jint)CreateCursor(GetModuleHandle(NULL), 14, 1, 32, 32, hand_cursor_and, hand_cursor_xor);
177
178     messagePumpThread = (jint)GetCurrentThreadId();
179     messagePumpStarted->release();
180
181     MSG msg;
182     while(GetMessage(&msg, (HWND)NULL, 0, 0) > 0) {
183
184         if (msg.message == WM_USER_CREATEWINDOW) {
185             org::ibex::plat::Win32$Win32Surface *surface = (org::ibex::plat::Win32$Win32Surface*)msg.lParam;
186
187             // we must create a unique window class name for each
188             // window so that minimization icons can be set independantly
189             char buf[255];
190             sprintf(buf, "Ibex_WINDOW_CLASS_%i", window_class_counter++);
191
192             WNDCLASSEX wc;
193             wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
194             wc.lpfnWndProc = WndProc;
195             wc.cbClsExtra = 0;
196             wc.cbSize = sizeof(WNDCLASSEX);
197             wc.cbWndExtra = 0;
198             wc.hInstance = GetModuleHandle(NULL);
199             wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
200             wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL),
201                                           MAKEINTRESOURCE(5),
202                                           IMAGE_ICON,
203                                           GetSystemMetrics(SM_CXSMICON),
204                                           GetSystemMetrics(SM_CYSMICON),
205                                           LR_DEFAULTCOLOR);
206             wc.hCursor = LoadCursor(NULL, IDC_ARROW);
207             wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
208             wc.lpszMenuName = "menu";
209             wc.lpszClassName = buf;
210             RegisterClassEx(&wc);
211
212             surface->hwnd = (jint)CreateWindow(wc.lpszClassName, TEXT(""),
213                                                msg.wParam ? WS_NORMAL : WS_POPUP,
214                                                200, 200, 100, 100,
215                                                (HWND__*)NULL, (HMENU__*)NULL,
216                                                GetModuleHandle(NULL), (LPVOID)NULL);
217             
218             SetFocus((HWND)surface->hwnd);
219             surface->hwndCreated->release();
220             
221         } else {
222             TranslateMessage(&msg);
223             if (msg.message == WM_LBUTTONDOWN || msg.message == WM_RBUTTONDOWN || msg.message == WM_MBUTTONDOWN) SetFocus(msg.hwnd);
224             DispatchMessage(&msg);
225         }
226
227     }
228     java::lang::System::exit(-1);
229 }
230
231
232 // Platform Methods ///////////////////////////////////////////////////////////////////
233
234 jstring org::ibex::plat::Win32::_getEnv(jstring key) {
235     int len = JvGetStringUTFLength(key);
236     char buf[len + 1];
237     JvGetStringUTFRegion(key, 0, len, buf);
238     buf[len] = '\0';
239     char buf2[1024];
240     DWORD ret = GetEnvironmentVariable(buf, buf2, 1024);
241     if (ret > 0 && ret < 1024) return JvNewStringLatin1(buf2);
242     return NULL;
243 }
244
245 jstring org::ibex::plat::Win32::_fileDialog(jstring suggestedFileName, jboolean write) {
246
247     char buf[1024];
248     OPENFILENAME ofn;
249     memset(buf, 0, 1024);
250     memset(&ofn, 0, sizeof(OPENFILENAME));
251
252     if (suggestedFileName != NULL)
253         JvGetStringUTFRegion(suggestedFileName, 0, min(1023, JvGetStringUTFLength(suggestedFileName)), buf);
254     
255     ofn.lStructSize = sizeof(OPENFILENAME);
256     ofn.nMaxCustFilter = 0;
257     ofn.lpstrFile = buf;
258     ofn.nMaxFile = 1024;
259
260     if (write) ofn.Flags |= OFN_OVERWRITEPROMPT;
261     ofn.Flags |= OFN_HIDEREADONLY;
262
263     int ret = write ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn);
264     return ret == 0 ? NULL : JvNewStringLatin1(buf);
265 }
266
267 void org::ibex::plat::Win32::__detectProxy(JArray<jstring>* container) {
268
269     HKEY hkey;
270     char buf[1024];
271     DWORD buflen = 1024;
272     DWORD type;
273     LONG result = RegOpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", &hkey);
274     if (result != ERROR_SUCCESS) return;
275     
276     buf[0] = '\0';
277     type = REG_SZ;
278     buflen = 1024;
279     result = RegQueryValueEx(hkey, "AutoConfigURL", NULL, &type, (LPBYTE)buf, &buflen);
280     buf[buflen] = '\0';
281     if (result == ERROR_SUCCESS) elements(container)[2] = JvNewStringLatin1(buf);
282
283     buf[0] = '\0';
284     type = REG_BINARY;
285     RegQueryValueEx(hkey, "ProxyEnable", NULL, &type, (LPBYTE)buf, &buflen);
286     if (buf[0] != 1) return;
287
288     buf[0] = '\0';
289     type = REG_SZ;
290     buflen = 1024;
291     RegQueryValueEx(hkey, "ProxyServer", NULL, &type, (LPBYTE)buf, &buflen);
292     buf[buflen] = '\0';
293     elements(container)[0] = JvNewStringLatin1(buf);
294
295     buf[0] = '\0';
296     buflen = 1024;
297     RegQueryValueEx(hkey, "ProxyOverride", NULL, &type, (LPBYTE)buf, &buflen);
298     buf[buflen] = '\0';
299     elements(container)[1] = JvNewStringLatin1(buf);
300 }
301
302 jstring org::ibex::plat::Win32::_getClipBoard() {
303     OpenClipboard((HWND)desktop_handle);
304     HGLOBAL hmem = GetClipboardData(CF_TEXT);
305     if (hmem == NULL) return NULL;
306     char* buf = (char*)GlobalLock(hmem);
307     if (buf == NULL) return NULL;
308     jstring ret = JvNewStringLatin1(buf);
309     GlobalUnlock(hmem);
310     CloseClipboard();
311     return ret;
312 }
313
314 void org::ibex::plat::Win32::_setClipBoard(jstring s) {
315     OpenClipboard((HWND)desktop_handle);
316     HGLOBAL hmem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, JvGetStringUTFLength(s) + 1);
317     if (hmem == NULL) return;
318     char* buf = (char*)GlobalLock(hmem);
319     if (buf == NULL) return;
320     JvGetStringUTFRegion(s, 0, JvGetStringUTFLength(s), buf);
321     buf[JvGetStringUTFLength(s)] = '\0';
322     GlobalUnlock(hmem);
323     SetClipboardData(CF_TEXT, hmem);
324     CloseClipboard();
325 }
326
327 void org::ibex::plat::Win32::_criticalAbort(jstring message) {
328     char buf[JvGetStringUTFLength(message) + 1];
329     buf[JvGetStringUTFLength(message)] = '\0';
330     JvGetStringUTFRegion(message, 0, JvGetStringUTFLength(message), buf);
331     MessageBox (NULL, buf, "Ibex Cannot Continue", MB_OK | MB_ICONSTOP | MB_TASKMODAL | MB_SETFOREGROUND);
332     java::lang::System::exit(-1);
333 }
334
335 jint org::ibex::plat::Win32::_getScreenWidth() {
336     RECT rect;
337     SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
338     return rect.right - rect.left;
339 }
340
341 jint org::ibex::plat::Win32::_getScreenHeight() {
342     RECT rect;
343     SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
344     return rect.bottom - rect.top;
345 }
346
347 jboolean org::ibex::plat::Win32::_newBrowserWindow_(jstring url) {
348
349     int len = min(2048, JvGetStringUTFLength(url));
350     char buf[len + 1];
351     JvGetStringUTFRegion(url, 0, len, buf);
352     buf[len] = '\0';
353
354     SHELLEXECUTEINFO ei;
355     memset(&ei, 0, sizeof(ei));
356     ei.cbSize = sizeof(ei);
357     ei.lpVerb = "open";
358     ei.lpFile = buf;
359     ei.fMask  = SEE_MASK_NOCLOSEPROCESS;
360     ei.nShow  = SW_SHOWDEFAULT;
361     return (ShellExecuteEx(&ei) == 0);
362
363 }
364
365
366 // Win32PixelBuffer /////////////////////////////////////////////////////////////////////////
367
368 // This is a scratch area; when blitting a translucent image, we copy the underlying data here first.
369 // Since all drawing operations are single-threaded, it's safe to use a global here.
370 static HBITMAP scratch = NULL;
371 static HDC scratch_dc = NULL;
372 static jint* scratch_bits = NULL;
373 static jint scratch_w = 0;
374 static jint scratch_h = 0;
375
376 #define max(a,b) ((a)>(b)?(a):(b))
377 #define min(a,b) ((a)<(b)?(a):(b))
378
379 void org::ibex::plat::Win32$Win32PixelBuffer::drawPicture(org::ibex::graphics::Picture* source0,
380                                                          jint dx, jint dy,
381                                                          jint cx1, jint cy1, jint cx2, jint cy2,
382                                                          jint rgb, jboolean alphaOnly) {
383     org::ibex::plat::Win32$Win32Picture* source = (org::ibex::plat::Win32$Win32Picture*)source0;
384
385     cx1 = max(dx, cx1);
386     cy1 = max(dy, cy1);
387     cx2 = min(dx + source->getWidth(), cx2);
388     cy2 = min(dy + source->getHeight(), cy2);
389     if (cx1 >= cx2 || cy1 >= cy2) return; 
390
391     if (source->hasalpha) {
392
393         if (scratch == NULL || scratch_w < cx2 - cx1 || scratch_h < cy2 - cy1) {
394             if (scratch_dc != NULL) DeleteDC(scratch_dc);
395             if (scratch != NULL) DeleteObject(scratch);
396             scratch_w = max(cx2 - cx1, scratch_w);
397             scratch_h = max(cy2 - cy1, scratch_h);
398
399             BITMAPINFO bitmapinfo;
400             memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
401             bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
402             bitmapinfo.bmiHeader.biWidth = scratch_w;
403             bitmapinfo.bmiHeader.biHeight = -1 * scratch_h;
404             bitmapinfo.bmiHeader.biPlanes = 1;
405             bitmapinfo.bmiHeader.biBitCount = 32;
406             bitmapinfo.bmiHeader.biCompression = BI_RGB;
407
408             // create section DIB
409             scratch = CreateDIBSection(NULL, &bitmapinfo, DIB_RGB_COLORS, (void**)&scratch_bits, NULL, 0);
410             scratch_dc = CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
411             SelectObject(scratch_dc, scratch);
412         }
413
414         // copy from screen to scratch
415         BitBlt((HDC)scratch_dc, 0, 0, cx2 - cx1, cy2 - cy1, (HDC)hdc, cx1, cy1, SRCCOPY);
416
417         // apply alpha-blending to scratch
418         jint* dat = elements(source->data);
419
420         for(int x = cx1; x < cx2; x++)
421             for(int y = cy1; y < cy2; y++) {
422                 jint dst = scratch_bits[(y - dy) * scratch_w + (x - dx)];
423
424                 // FEATURE: see if we can leverage GDI to do something more clever here with alphaOnly
425                 jint src = alphaOnly ? rgb : dat[(y - dy) * source->getWidth() + x - dx];
426                 jint alpha = (dat[(y - dy) * source->getWidth() + x - dx] & 0xFF000000) >> 24;
427                 jint r = (((src & 0x00FF0000) >> 16) * alpha + ((dst & 0x00FF0000) >> 16) * (0xFF - alpha)) / 0xFF;
428                 jint g = (((src & 0x0000FF00) >> 8)  * alpha + ((dst & 0x0000FF00) >> 8)  * (0xFF - alpha)) / 0xFF;
429                 jint b = (((src & 0x000000FF))       * alpha + ((dst & 0x000000FF))       * (0xFF - alpha)) / 0xFF;
430                 scratch_bits[(y - dy) * scratch_w + (x - dx)] = (r << 16) | (g << 8) | b;
431             }
432
433         // copy back from scratch to screen
434         BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)scratch_dc, 0, 0, SRCCOPY);
435
436     } else {
437
438         // FIXME: support alphaOnly case here
439         if (source->hasmask) {
440             BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->maskdc, cx1 - dx, cy1 - dy, SRCAND);
441             BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->hdc, cx1 - dx, cy1 - dy, SRCPAINT);
442         } else {
443             BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->hdc, cx1 - dx, cy1 - dy, SRCCOPY);
444         }
445
446     }
447
448 }
449
450 void org::ibex::plat::Win32$Win32PixelBuffer::fillRect(jint x, jint y, jint x2, jint y2, jint color) {
451     jint w = x2 - x;
452     jint h = y2 - y;
453
454     // sadly, the ability to change the color of a brush didn't arrive until Win2k...
455     HBRUSH brush = CreateSolidBrush(PALETTERGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
456     if (!brush) return;
457     RECT rect = { x, y, x + w, y + h };
458     FillRect((HDC)hdc, &rect, brush);
459     DeleteObject(brush);
460 }
461
462 void org::ibex::plat::Win32$Win32Surface::blit(org::ibex::graphics::PixelBuffer* s, jint sx, jint sy, jint dx, jint dy, jint dx2, jint dy2) {
463     // we create the DC lazily to get around some strange race condition in WinXP
464     if (hdc == 0) hdc = (jint)GetDC((HWND)hwnd);
465     BitBlt((HDC)hdc, dx, dy, dx2 - dx, dy2 - dy, (HDC)(((org::ibex::plat::Win32$Win32PixelBuffer*)s)->hdc), sx, sy, SRCCOPY);
466 }
467
468 void org::ibex::plat::Win32$Win32PixelBuffer::natInit() {
469     hbitmap = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, w, h);
470     hdc = (jint)CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
471     SetBkMode((HDC)hdc, TRANSPARENT);
472     SelectObject((HDC)hdc, (HBITMAP)hbitmap);
473 }
474
475 void org::ibex::plat::Win32$Win32PixelBuffer::finalize() {
476     DeleteObject((void*)hdc);
477     DeleteObject((void*)hbitmap);
478 }
479
480
481
482 // Win32Picture /////////////////////////////////////////////////////////////////////////
483
484 void org::ibex::plat::Win32$Win32Picture::natInit() {
485
486     BITMAPINFO bitmapinfo;
487     memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
488     bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
489     bitmapinfo.bmiHeader.biWidth = width;
490     bitmapinfo.bmiHeader.biHeight = -1 * height;
491     bitmapinfo.bmiHeader.biPlanes = 1;
492     bitmapinfo.bmiHeader.biBitCount = 32;
493     bitmapinfo.bmiHeader.biCompression = BI_RGB;
494
495     hbitmap = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, width, height);
496     hdc = (jint)CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
497     SelectObject((HDC)hdc, (HBITMAP)hbitmap);
498     uint32_t* dat = (uint32_t*)elements(data);
499     for(int i=0; i<data->length; i++) if ((dat[i] & 0xFF000000) == 0x00000000) dat[i] = 0x00000000;
500     StretchDIBits((HDC)hdc, 0, 0, width, height, 0, 0, width, height, elements(data), &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
501
502     jint _copy[min(1024, data->length)];
503     jint* copy = data->length > 1024 ? (jint*)malloc(data->length * 4) : _copy;
504
505     memcpy(copy, elements(data), data->length * 4);
506     for(int i=0; i<data->length; i++)
507         if ((copy[i] & 0xFF000000) == 0x00000000) {
508             hasmask = 1;
509             copy[i] = 0x00FFFFFF;
510         } else if ((copy[i] & 0xFF000000) == 0xFF000000) {
511             copy[i] = 0x00000000;
512         } else {
513             hasalpha = 1;
514             hasmask = 0;
515             if (data->length > 1024) free(copy);
516             return;
517         }
518
519     if (!hasmask) {
520         if (data->length > 1024) free(copy);
521         return;
522     }
523
524     //    hmask = (jint)CreateBitmap(w, h, 1, 1, NULL);
525     hmask = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, width, height);
526     maskdc = (jint)CreateCompatibleDC(NULL);
527     SelectObject((HDC)maskdc, (HBITMAP)hmask);
528     StretchDIBits((HDC)maskdc, 0, 0, width, height, 0, 0, width, height, copy, &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
529     if (data->length > 1024) free(copy);
530 }
531
532
533
534 // Win32Surface /////////////////////////////////////////////////////////////////////////////
535
536 void org::ibex::plat::Win32$Win32Surface::natInit(jboolean framed) {
537
538     // Ask the message-handling thread to create a window for us
539     PostThreadMessage((DWORD)org::ibex::plat::Win32::messagePumpThread, WM_USER + 2, (WPARAM)framed, (LPARAM)this);
540     hwndCreated->block();
541
542     // turn on incremental GC now that we have a user-visible interface
543     // [[this is causing segfaults; enable it later...]]
544     // GC_enable_incremental();
545
546     ShowWindow ((HWND)hwnd, SW_SHOWDEFAULT);
547 }
548
549 void org::ibex::plat::Win32$Win32Surface::finalize() { /* DeleteObject((void*)hwnd); */ }
550 void org::ibex::plat::Win32$Win32Surface::toFront() { BringWindowToTop((HWND)hwnd); }
551 void org::ibex::plat::Win32$Win32Surface::toBack() { SetWindowPos((HWND)hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); }
552 void org::ibex::plat::Win32$Win32Surface::_dispose() { PostMessage((HWND)hwnd, WM_USER_DISPOSE, 0, 0); }
553 void org::ibex::plat::Win32$Win32Surface::setInvisible(jboolean h) { ShowWindow((HWND)hwnd, h ? SW_HIDE : SW_SHOWNORMAL); }
554 void org::ibex::plat::Win32$Win32Surface::_setMinimized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMINIMIZED : SW_SHOWNORMAL); }
555 void org::ibex::plat::Win32$Win32Surface::_setMaximized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); }
556 void org::ibex::plat::Win32$Win32Surface::postCursorChange() { PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0); }
557
558 void org::ibex::plat::Win32$Win32Surface::setLocation() {
559     POINT point;
560     RECT rect;
561     point.x = 0;
562     point.y = 0;
563     ClientToScreen((HWND)hwnd, &point);
564     GetWindowRect((HWND)hwnd, &rect);
565     SetWindowPos((HWND)hwnd, NULL, root->x - (point.x - rect.left), root->y - (point.y - rect.top), 0, 0, SWP_NOZORDER | SWP_NOSIZE);
566 }
567
568 void org::ibex::plat::Win32$Win32Surface::_setSize(jint w, jint h) {
569     RECT client_rect, window_rect;
570     GetClientRect((HWND)hwnd, &client_rect);
571     GetWindowRect((HWND)hwnd, &window_rect);
572     int width = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left) + w;
573     int height = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top) + h;
574     SetWindowPos((HWND)hwnd, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE);
575 }
576
577 void org::ibex::plat::Win32$Win32Surface::setTitleBarText(java::lang::String* title) {
578     int len = min(1024, JvGetStringUTFLength(title));
579     char buf[len + 1];
580     buf[len] = '\0';
581     JvGetStringUTFRegion(title, 0, len, buf);
582     SetWindowText((HWND)hwnd, buf);
583 }
584
585 void org::ibex::plat::Win32$Win32Surface::setIcon(org::ibex::graphics::Picture* p0) {
586
587     org::ibex::plat::Win32$Win32Picture* p = (org::ibex::plat::Win32$Win32Picture*)p0;
588     int icon_width = GetSystemMetrics(SM_CXSMICON);
589     int icon_height = GetSystemMetrics(SM_CYSMICON);
590
591     // we create the DC lazily to get around some strange race condition in WinXP
592     if (hdc == 0) hdc = (jint)GetDC((HWND)hwnd);
593
594     // create the bitmap
595     HBITMAP bit = CreateCompatibleBitmap((HDC)hdc, icon_width, icon_height);
596     HDC memdc = CreateCompatibleDC((HDC)hdc);
597     SelectObject(memdc, bit);
598     BitBlt((HDC)memdc, 0, 0, icon_width, icon_height, (HDC)(p->hdc), 0, 0, SRCCOPY);
599
600     // create the mask
601     jint* dat = elements(p->data);
602     HBITMAP bit_mask = CreateBitmap(icon_width, icon_height * 2, 1, 1, NULL);
603     SelectObject(memdc, bit_mask);
604     for(int x=0; x<icon_width; x++)
605         for(int y=0; y<icon_height; y++) {
606             int source = dat[(x * p->getWidth()) / icon_width + ((y * p->getHeight()) / icon_height) * (p->getWidth())];
607             if ((source & 0xFF000000) != 0x00000000) SetPixel(memdc, x, y, PALETTERGB(0, 0, 0));
608             else SetPixel(memdc, x, y, PALETTERGB(255, 255, 255));
609         }
610
611     // instantiate the icon and assign it to the window class
612     ICONINFO ici;
613     ici.fIcon = 1;
614     ici.hbmMask = bit_mask;
615     ici.hbmColor = bit;
616     HICON hicon = CreateIconIndirect(&ici);
617     HICON oldicon = (HICON)SetClassLong((HWND)hwnd, GCL_HICONSM, (LONG)hicon);
618     if (oldicon != NULL) DestroyIcon(oldicon);
619     DeleteObject(memdc);
620 }
621
622 static jstring keyToString(WPARAM wParam);
623 jint org::ibex::plat::Win32$Win32Surface::WndProc(jint _hwnd, jint _iMsg, jint _wParam, jint _lParam) {
624
625     UINT iMsg = (UINT)_iMsg;
626     WPARAM wParam = (WPARAM)_wParam;
627     LPARAM lParam = (LPARAM)_lParam;
628
629     int oldmousex, oldmousey;
630     MINMAXINFO* mmi;
631     int resizable;
632     POINT point;
633     HWND hwnd2;
634     RECT rect, rect2;
635     RECT client_rect, window_rect;
636     jboolean newinside;
637     int16_t mouse_x;
638     int16_t mouse_y;
639     int addwidth, addheight;
640
641     switch(iMsg) {
642     case WM_DEVMODECHANGE: break;    // FEATURE: color depth changed
643     case WM_DISPLAYCHANGE: break;    // FEATURE: screen size changed
644     case WM_MOUSEWHEEL:
645       VScroll(((jfloat)((wParam & 0xffff0000) >> 16)) / (jfloat)120);
646       break;
647
648     case WM_SYSKEYDOWN: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
649     case WM_KEYDOWN:
650         KeyPressed(keyToString(wParam));
651         return 0;
652
653     case WM_SYSKEYUP: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
654     case WM_KEYUP:
655         KeyReleased(keyToString(wParam));
656         return 0;
657
658     case WM_SETFOCUS: Focused(true); return 0;
659     case WM_KILLFOCUS: Focused(false); return 0;
660     case WM_LBUTTONDBLCLK: DoubleClick(1); return 0;
661     case WM_RBUTTONDBLCLK: DoubleClick(2); return 0;
662     case WM_MBUTTONDBLCLK: DoubleClick(3); return 0;
663     case WM_LBUTTONDOWN: Press(1); return 0;
664     case WM_RBUTTONDOWN: Press(2); return 0;
665     case WM_MBUTTONDOWN: Press(3); return 0;
666     case WM_CLOSE: Close(); return 0;
667     case WM_ERASEBKGND: return 0;
668
669     case WM_LBUTTONUP:
670     case WM_RBUTTONUP:
671     case WM_MBUTTONUP:
672         Release(iMsg == WM_LBUTTONUP ? 1 : (iMsg == WM_RBUTTONUP ? 2 : 3));
673         if (captured && !inside) {
674             ReleaseCapture();
675             SetCursor((HCURSOR)previous_cursor);
676         }
677         return 0;
678
679     case WM_MOVING:
680         GetClientRect((HWND)hwnd, &rect);
681         PosChange(rect.left, rect.top);
682         return 0;
683
684     case WM_SIZE:
685         if (wParam == SIZE_MINIMIZED) {
686             if (maximized) Maximized(false);
687             Minimized(true);
688         } else if (wParam == SIZE_MAXIMIZED) {
689             if (minimized) Minimized(false); 
690             Maximized(true);
691         } else {
692             if (minimized) Minimized(false); 
693             if (maximized) Maximized(false);
694         }
695         // deliberately fall through to WM_SIZING
696
697     case WM_SIZING:
698         GetClientRect((HWND)hwnd, &rect);
699         SizeChange(rect.right - rect.left, rect.bottom - rect.top);
700         return 0;
701
702     case WM_MOUSEMOVE:
703         if (mousex == lParam & 0xFFFF && mousey == (lParam & 0xFFFF0000) >> 16) return 0;
704
705         GetClientRect((HWND)hwnd, &rect);
706         point.x = mouse_x = lParam & 0xFFFF;
707         point.y = mouse_y = (lParam & 0xFFFF0000) >> 16;
708         ClientToScreen((HWND)hwnd, &point);
709         hwnd2 = WindowFromPoint(point);
710
711         newinside = hwnd2 == (HWND)hwnd && mouse_x > 0 && mouse_y > 0 && mouse_x < (rect.right - rect.left) && mouse_y < (rect.bottom - rect.top) ? 1 : 0;
712
713         Move(mouse_x, mouse_y);
714
715         if (newinside && !inside) {
716             inside = 1;
717             SetCapture((HWND)hwnd);
718             captured = 1;
719             previous_cursor = (jint)GetCursor();
720             PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0);
721
722         } else if (!newinside && inside) {
723             inside = 0;
724             if (!button1 && !button2 && !button3) {
725                 ReleaseCapture();
726                 captured = 0;
727                 SetCursor((HCURSOR)previous_cursor);
728             }
729         }
730
731         return 0;
732
733     case WM_USER_SETCURSOR:
734         // I can't find documentation of this anywhere, but in my experience, an event must be received between an invocation
735         // of SetCapture() and SetCursor(). Furthermore, it seems that after calling SetCursor(), the cursor will not change until a
736         // return from WndProc (ie the completion of the processing of some event).  Hence, we use WM_USER to indicate that the screen
737         // cursor should be re-set to "current_cursor".
738         SetCursor((HCURSOR)current_cursor);
739         return 0;
740
741     case WM_USER_DISPOSE:
742         // used to signal that we should destroy ourselves
743         DestroyWindow((HWND)hwnd);
744         return 0;
745
746     case WM_GETMINMAXINFO:
747         GetClientRect((HWND)hwnd, &client_rect);
748         GetWindowRect((HWND)hwnd, &window_rect);
749         addwidth = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left);
750         addheight = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top);
751         mmi = (MINMAXINFO*)lParam;
752         mmi->ptMinTrackSize.x = ((uint32_t)root->minwidth) + addwidth;
753         mmi->ptMinTrackSize.y = ((uint32_t)root->minheight) + addheight;
754         resizable = !((root->minwidth == root->maxwidth) && (root->minheight == root->maxheight));
755         mmi->ptMaxTrackSize.x = resizable ? org::ibex::plat::Win32::getScreenWidth() : mmi->ptMinTrackSize.x;
756         mmi->ptMaxTrackSize.y = resizable ? org::ibex::plat::Win32::getScreenHeight() : mmi->ptMinTrackSize.y;
757         return 0;
758         
759     case WM_PAINT: 
760         PAINTSTRUCT ps;
761         BeginPaint((HWND)org::ibex::plat::Win32$Win32Surface::hwnd, &ps);
762         Dirty(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top);
763         EndPaint((HWND)org::ibex::plat::Win32$Win32Surface::hwnd, &ps);
764         return 0;
765
766     default: break;
767     }  
768
769     return DefWindowProc((HWND)hwnd, iMsg, wParam, lParam);
770 }
771
772
773 // Key Translator Helper /////////////////////////////////////////////////////////////////////
774
775 static char keyarr [256] = { 0 };
776 static jstring keyToString(WPARAM wParam) {
777     char arr[8];
778     keyarr[VK_CAPITAL] = GetKeyState(VK_CAPITAL);
779     keyarr[VK_LSHIFT] = GetKeyState(VK_LSHIFT);
780     keyarr[VK_RSHIFT] = GetKeyState(VK_RSHIFT);
781     keyarr[VK_SHIFT] = GetKeyState(VK_SHIFT);
782
783     if (ToAsciiEx(wParam, 0, (BYTE*)keyarr, (WORD*)arr, 0, GetKeyboardLayout(0)) == 1) {
784         switch (arr[0]) {
785         case '\t': return JvNewStringLatin1("tab");
786         case 0x1b: return JvNewStringLatin1("escape");
787         case '\n': return JvNewStringLatin1("enter");
788         case '\r': return JvNewStringLatin1("enter");
789         case 0x08: return JvNewStringLatin1("back_space");
790         default: return JvNewStringLatin1(arr, 1);
791         }
792         
793     } else switch (wParam) {
794     case VK_CLEAR: return JvNewStringLatin1("clear");
795     case VK_SHIFT: return JvNewStringLatin1("shift");
796     case VK_CONTROL: return JvNewStringLatin1("control");
797     case VK_CAPITAL: return JvNewStringLatin1("caps_lock");
798     case VK_MENU: return JvNewStringLatin1("alt");
799     case VK_PAUSE: return JvNewStringLatin1("pause");
800     case VK_PRIOR: return JvNewStringLatin1("page_up");
801     case VK_NEXT: return JvNewStringLatin1("page_down");
802     case VK_END: return JvNewStringLatin1("end");
803     case VK_HOME: return JvNewStringLatin1("home");
804     case VK_LEFT: return JvNewStringLatin1("left");
805     case VK_UP: return JvNewStringLatin1("up");
806     case VK_RIGHT: return JvNewStringLatin1("right");
807     case VK_DOWN: return JvNewStringLatin1("down");
808     case VK_INSERT: return JvNewStringLatin1("insert");
809     case VK_DELETE: return JvNewStringLatin1("delete");
810     case VK_NUMPAD0: return JvNewStringLatin1("numpad0");
811     case VK_NUMPAD1: return JvNewStringLatin1("numpad1");
812     case VK_NUMPAD2: return JvNewStringLatin1("numpad2");
813     case VK_NUMPAD3: return JvNewStringLatin1("numpad3");
814     case VK_NUMPAD4: return JvNewStringLatin1("numpad4");
815     case VK_NUMPAD5: return JvNewStringLatin1("numpad5");
816     case VK_NUMPAD6: return JvNewStringLatin1("numpad6");
817     case VK_NUMPAD7: return JvNewStringLatin1("numpad7");
818     case VK_NUMPAD8: return JvNewStringLatin1("numpad8");
819     case VK_NUMPAD9: return JvNewStringLatin1("numpad9");
820     case VK_F1: return JvNewStringLatin1("f1");
821     case VK_F2: return JvNewStringLatin1("f2");
822     case VK_F3: return JvNewStringLatin1("f3");
823     case VK_F4: return JvNewStringLatin1("f4");
824     case VK_F5: return JvNewStringLatin1("f5");
825     case VK_F6: return JvNewStringLatin1("f6");
826     case VK_F7: return JvNewStringLatin1("f7");
827     case VK_F8: return JvNewStringLatin1("f8");
828     case VK_F9: return JvNewStringLatin1("f9");
829     case VK_F10: return JvNewStringLatin1("f10");
830     case VK_F11: return JvNewStringLatin1("f11");
831     case VK_F12: return JvNewStringLatin1("f12");
832     case VK_NUMLOCK: return JvNewStringLatin1("num_lock");
833     case VK_SCROLL: return JvNewStringLatin1("scroll_lock");
834     case VK_LSHIFT: return JvNewStringLatin1("shift");
835     case VK_RSHIFT: return JvNewStringLatin1("shift");
836     case VK_LCONTROL: return JvNewStringLatin1("control");
837     case VK_RCONTROL: return JvNewStringLatin1("control");
838     }
839     return NULL;
840 }