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