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