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