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