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