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