2002/07/02 00:18: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::_fileDialog(jstring suggestedFileName, jboolean write) {
240
241     char buf[1024];
242     OPENFILENAME ofn;
243     memset(buf, 0, 1024);
244     memset(&ofn, 0, sizeof(OPENFILENAME));
245
246     if (suggestedFileName != NULL)
247         JvGetStringUTFRegion(suggestedFileName, 0, min(1023, JvGetStringUTFLength(suggestedFileName)), buf);
248     
249     ofn.lStructSize = sizeof(OPENFILENAME);
250     ofn.nMaxCustFilter = 0;
251     ofn.lpstrFile = buf;
252     ofn.nMaxFile = 1024;
253
254     if (write) ofn.Flags |= OFN_OVERWRITEPROMPT;
255     ofn.Flags |= OFN_HIDEREADONLY;
256
257     int ret = write ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn);
258     return ret == 0 ? NULL : JvNewStringLatin1(buf);
259 }
260
261 void org::xwt::plat::Win32::__detectProxy(JArray<jstring>* container) {
262
263     HKEY hkey;
264     char buf[1024];
265     DWORD buflen = 1024;
266     DWORD type;
267     LONG result = RegOpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", &hkey);
268     if (result != ERROR_SUCCESS) return;
269     
270     buf[0] = '\0';
271     type = REG_SZ;
272     buflen = 1024;
273     result = RegQueryValueEx(hkey, "AutoConfigURL", NULL, &type, (LPBYTE)buf, &buflen);
274     buf[buflen] = '\0';
275     if (result == ERROR_SUCCESS) elements(container)[2] = JvNewStringLatin1(buf);
276
277     buf[0] = '\0';
278     type = REG_BINARY;
279     RegQueryValueEx(hkey, "ProxyEnable", NULL, &type, (LPBYTE)buf, &buflen);
280     if (buf[0] != 1) return;
281
282     buf[0] = '\0';
283     type = REG_SZ;
284     buflen = 1024;
285     RegQueryValueEx(hkey, "ProxyServer", NULL, &type, (LPBYTE)buf, &buflen);
286     buf[buflen] = '\0';
287     elements(container)[0] = JvNewStringLatin1(buf);
288
289     buf[0] = '\0';
290     buflen = 1024;
291     RegQueryValueEx(hkey, "ProxyOverride", NULL, &type, (LPBYTE)buf, &buflen);
292     buf[buflen] = '\0';
293     elements(container)[1] = JvNewStringLatin1(buf);
294 }
295
296 jstring org::xwt::plat::Win32::_getClipBoard() {
297     OpenClipboard((HWND)desktop_handle);
298     HGLOBAL hmem = GetClipboardData(CF_TEXT);
299     if (hmem == NULL) return NULL;
300     char* buf = (char*)GlobalLock(hmem);
301     if (buf == NULL) return NULL;
302     jstring ret = JvNewStringLatin1(buf);
303     GlobalUnlock(hmem);
304     CloseClipboard();
305     return ret;
306 }
307
308 void org::xwt::plat::Win32::_setClipBoard(jstring s) {
309     OpenClipboard((HWND)desktop_handle);
310     HGLOBAL hmem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, JvGetStringUTFLength(s) + 1);
311     if (hmem == NULL) return;
312     char* buf = (char*)GlobalLock(hmem);
313     if (buf == NULL) return;
314     JvGetStringUTFRegion(s, 0, JvGetStringUTFLength(s), buf);
315     buf[JvGetStringUTFLength(s)] = '\0';
316     GlobalUnlock(hmem);
317     SetClipboardData(CF_TEXT, hmem);
318     CloseClipboard();
319 }
320
321 void org::xwt::plat::Win32::_criticalAbort(jstring message) {
322     char buf[JvGetStringUTFLength(message) + 1];
323     buf[JvGetStringUTFLength(message)] = '\0';
324     JvGetStringUTFRegion(message, 0, JvGetStringUTFLength(message), buf);
325     MessageBox (NULL, buf, "XWT Cannot Continue", MB_OK | MB_ICONSTOP | MB_TASKMODAL | MB_SETFOREGROUND);
326     java::lang::System::exit(-1);
327 }
328
329 jint org::xwt::plat::Win32::_getScreenWidth() {
330     RECT rect;
331     SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
332     return rect.right - rect.left;
333 }
334
335 jint org::xwt::plat::Win32::_getScreenHeight() {
336     RECT rect;
337     SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
338     return rect.bottom - rect.top;
339 }
340
341 org::xwt::plat::Win32$Win32Font* org::xwt::plat::Win32::mapFont(org::xwt::Platform$ParsedFont* pf) {
342     org::xwt::plat::Win32$Win32Font* ret = new org::xwt::plat::Win32$Win32Font();
343     LOGFONT logfont;
344     memset(&logfont, 0, sizeof(LOGFONT));
345     logfont.lfHeight = -MulDiv(pf->size, GetDeviceCaps((HDC)org::xwt::plat::Win32::desktop_dc, LOGPIXELSY), 72);
346     if (pf->italic) logfont.lfItalic = 1;
347     if (pf->bold) logfont.lfWeight = FW_BOLD;
348     logfont.lfCharSet = ANSI_CHARSET;
349
350     JvGetStringUTFRegion(pf->name, 0, min(31, JvGetStringUTFLength(pf->name)), logfont.lfFaceName);
351     logfont.lfFaceName[min(31, JvGetStringUTFLength(pf->name))] = 0;
352
353     ret->hfont = (jint)CreateFontIndirect(&logfont);
354     SelectObject((HDC)desktop_dc, (HFONT)(ret->hfont));
355
356     TEXTMETRIC tm;
357     GetTextMetrics((HDC)desktop_dc, &tm);
358     POINT p;
359     p.x = 0; p.y = tm.tmAscent;
360     LPtoDP((HDC)desktop_dc, &p, 1); 
361     ret->maxAscent = p.y;
362
363     p.x = 0; p.y = tm.tmDescent;
364     LPtoDP((HDC)desktop_dc, &p, 1);
365     ret->maxDescent = p.y;
366
367     return ret;
368 }
369
370 jint org::xwt::plat::Win32::_stringWidth(jstring font, jstring text) {
371
372     HFONT hfont = (HFONT)(getFont(font)->hfont);
373     SelectObject((HDC)org::xwt::plat::Win32::desktop_dc, hfont);
374
375     int len = min(1024, JvGetStringUTFLength(text));
376     char buf[len + 1];
377     buf[len] = '\0';
378     JvGetStringUTFRegion(text, 0, len, buf);
379     
380     SIZE size;
381     GetTextExtentPoint32((HDC)org::xwt::plat::Win32::desktop_dc, buf, len, &size);
382     return size.cx;
383 }
384
385 jboolean org::xwt::plat::Win32::_newBrowserWindow_(jstring url) {
386
387     int len = min(2048, JvGetStringUTFLength(url));
388     char buf[len + 1];
389     JvGetStringUTFRegion(url, 0, len, buf);
390     buf[len] = '\0';
391
392     SHELLEXECUTEINFO ei;
393     memset(&ei, 0, sizeof(ei));
394     ei.cbSize = sizeof(ei);
395     ei.lpVerb = "open";
396     ei.lpFile = buf;
397     ei.fMask  = SEE_MASK_NOCLOSEPROCESS;
398     ei.nShow  = SW_SHOWDEFAULT;
399     return (ShellExecuteEx(&ei) == 0);
400
401 }
402
403
404 // Win32DoubleBuffer /////////////////////////////////////////////////////////////////////////
405
406 // This is a scratch area; when blitting a translucent image, we copy the underlying data here first.
407 // Since all drawing operations are single-threaded, it's safe to use a global here.
408 static HBITMAP scratch = NULL;
409 static HDC scratch_dc = NULL;
410 static jint* scratch_bits = NULL;
411 static jint scratch_w = 0;
412 static jint scratch_h = 0;
413
414 #define BLT(dest, dx1, dy1, dx2, dy2, src, sx1, sy1, sx2, sy2, op)                                   \
415     if ((dx2 - dx1 == sx2 - sx1) && (dy2 - dy1 == sy2 - sy1))                                        \
416         BitBlt(dest, dx1, dy1, dx2 - dx1, dy2 - dy1, src, sx1, sy1, op);                             \
417     else                                                                                             \
418         StretchBlt(dest, dx1, dy1, dx2 - dx1, dy2 - dy1, src, sx1, sy1, sx2 - sx1, sy2 - sy1, op);
419         
420 void org::xwt::plat::Win32$Win32DoubleBuffer::drawPicture(org::xwt::Picture* source0,
421                                                           jint dx1, jint dy1, jint dx2, jint dy2,
422                                                           jint sx1, jint sy1, jint sx2, jint sy2) {
423
424     org::xwt::plat::Win32$Win32Picture* source = (org::xwt::plat::Win32$Win32Picture*)source0;
425
426     if (source->hasalpha) {
427
428         if (scratch == NULL || scratch_w < dx2 - dx1 || scratch_h < dy2 - dy1) {
429             if (scratch_dc != NULL) DeleteDC(scratch_dc);
430             if (scratch != NULL) DeleteObject(scratch);
431             scratch_w = max(dx2 - dx1, scratch_w);
432             scratch_h = max(dy2 - dy1, scratch_h);
433
434             BITMAPINFO bitmapinfo;
435             memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
436             bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
437             bitmapinfo.bmiHeader.biWidth = scratch_w;
438             bitmapinfo.bmiHeader.biHeight = -1 * scratch_h;
439             bitmapinfo.bmiHeader.biPlanes = 1;
440             bitmapinfo.bmiHeader.biBitCount = 32;
441             bitmapinfo.bmiHeader.biCompression = BI_RGB;
442
443             // create section DIB
444             scratch = CreateDIBSection(NULL, &bitmapinfo, DIB_RGB_COLORS, (void**)&scratch_bits, NULL, 0);
445             scratch_dc = CreateCompatibleDC((HDC)org::xwt::plat::Win32::desktop_dc);
446             SelectObject(scratch_dc, scratch);
447         }
448
449         // copy from screen to scratch
450         BitBlt((HDC)scratch_dc, 0, 0, dx2 - dx1, dy2 - dy1, (HDC)hdc, dx1, dy1, SRCCOPY);
451
452         // apply alpha-blending to scratch
453         jint* dat = elements(source->data);
454
455         for(int x = max(clipx1, dx1) - dx1; x < min(clipx2, dx2) - dx1; x++)
456             for(int y = max(clipy1, dy1) - dy1; y < min(clipy2, dy2) - dy1; y++) {
457                 int sx = (x * (sx2 - sx1)) / (dx2 - dx1) + sx1;
458                 int sy = (y * (sy2 - sy1)) / (dy2 - dy1) + sy1;
459                 jint dst = scratch_bits[y * scratch_w + x];
460                 jint src = dat[sy * source->getWidth() + sx];
461                 jint alpha = (src & 0xFF000000) >> 24;
462                 jint r = (((src & 0x00FF0000) >> 16) * alpha + ((dst & 0x00FF0000) >> 16) * (0xFF - alpha)) / 0xFF;
463                 jint g = (((src & 0x0000FF00) >> 8)  * alpha + ((dst & 0x0000FF00) >> 8)  * (0xFF - alpha)) / 0xFF;
464                 jint b = (((src & 0x000000FF))       * alpha + ((dst & 0x000000FF))       * (0xFF - alpha)) / 0xFF;
465                 scratch_bits[y * scratch_w + x] = (r << 16) | (g << 8) | b;
466             }
467
468         // copy back from scratch to screen
469         BitBlt((HDC)hdc, dx1, dy1, dx2 - dx1, dy2 - dy1, (HDC)scratch_dc, 0, 0, SRCCOPY);
470
471     } else {
472         if (source->hasmask) {
473             BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->maskdc, sx1, sy1, sx2, sy2, SRCAND);
474             BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->hdc, sx1, sy1, sx2, sy2, SRCPAINT);
475         } else {
476             BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->hdc, sx1, sy1, sx2, sy2, SRCCOPY);
477         }
478
479     }
480
481 }
482
483 void org::xwt::plat::Win32$Win32DoubleBuffer::drawString(jstring font, jstring text, jint x, jint y, jint color) {
484
485     org::xwt::plat::Win32$Win32Font* wf = org::xwt::plat::Win32::getFont(font);
486     SelectObject((HDC)hdc, (HFONT)(wf->hfont));
487
488     // Platform API passes us the y-pos of the bottom of the text; we need the top
489     y -= wf->maxAscent;
490
491     int len = min(1024, JvGetStringUTFLength(text));
492     char buf[len + 1];
493     buf[len] = '\0';
494     JvGetStringUTFRegion(text, 0, len, buf);
495
496     SetTextColor((HDC)hdc, PALETTERGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
497     TextOut((HDC)hdc, x, y, buf, len);
498 }
499
500 void org::xwt::plat::Win32$Win32DoubleBuffer::fillRect(jint x, jint y, jint x2, jint y2, jint color) {
501     jint w = x2 - x;
502     jint h = y2 - y;
503
504     // sadly, the ability to change the color of a brush didn't arrive until Win2k...
505     HBRUSH brush = CreateSolidBrush(PALETTERGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
506     if (!brush) return;
507     RECT rect = { x, y, x + w, y + h };
508     FillRect((HDC)hdc, &rect, brush);
509     DeleteObject(brush);
510 }
511
512 void org::xwt::plat::Win32$Win32Surface::blit(org::xwt::DoubleBuffer* s, jint sx, jint sy, jint dx, jint dy, jint dx2, jint dy2) {
513     BitBlt((HDC)hdc, dx, dy, dx2 - dx, dy2 - dy, (HDC)(((org::xwt::plat::Win32$Win32DoubleBuffer*)s)->hdc), sx, sy, SRCCOPY);
514 }
515
516 void org::xwt::plat::Win32$Win32DoubleBuffer::natInit() {
517     hbitmap = (jint)CreateCompatibleBitmap((HDC)org::xwt::plat::Win32::desktop_dc, w, h);
518     hdc = (jint)CreateCompatibleDC((HDC)org::xwt::plat::Win32::desktop_dc);
519     SetBkMode((HDC)hdc, TRANSPARENT);
520     SelectObject((HDC)hdc, (HBITMAP)hbitmap);
521 }
522
523 void org::xwt::plat::Win32$Win32DoubleBuffer::setClip(jint x, jint y, jint x2, jint y2) {
524     clipx1 = x; clipx2 = x2; clipy1 = y; clipy2 = y2;
525     HRGN hrgn = CreateRectRgn(x, y, x2, y2);
526     SelectClipRgn((HDC)hdc, hrgn);
527     DeleteObject(hrgn);
528 }
529
530 void org::xwt::plat::Win32$Win32DoubleBuffer::finalize() {
531     DeleteObject((void*)hdc);
532     DeleteObject((void*)hbitmap);
533 }
534
535
536
537 // Win32Picture /////////////////////////////////////////////////////////////////////////
538
539 void org::xwt::plat::Win32$Win32Picture::natInit() {
540
541     BITMAPINFO bitmapinfo;
542     memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
543     bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
544     bitmapinfo.bmiHeader.biWidth = w;
545     bitmapinfo.bmiHeader.biHeight = -1 * h;
546     bitmapinfo.bmiHeader.biPlanes = 1;
547     bitmapinfo.bmiHeader.biBitCount = 32;
548     bitmapinfo.bmiHeader.biCompression = BI_RGB;
549
550     hbitmap = (jint)CreateCompatibleBitmap((HDC)org::xwt::plat::Win32::desktop_dc, w, h);
551     hdc = (jint)CreateCompatibleDC((HDC)org::xwt::plat::Win32::desktop_dc);
552     SelectObject((HDC)hdc, (HBITMAP)hbitmap);
553     uint32_t* dat = (uint32_t*)elements(data);
554     for(int i=0; i<data->length; i++) if ((dat[i] & 0xFF000000) == 0x00000000) dat[i] = 0x00000000;
555     StretchDIBits((HDC)hdc, 0, 0, w, h, 0, 0, w, h, elements(data), &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
556
557     jint _copy[min(1024, data->length)];
558     jint* copy = data->length > 1024 ? (jint*)malloc(data->length * 4) : _copy;
559
560     memcpy(copy, elements(data), data->length * 4);
561     for(int i=0; i<data->length; i++)
562         if ((copy[i] & 0xFF000000) == 0x00000000) {
563             hasmask = 1;
564             copy[i] = 0x00FFFFFF;
565         } else if ((copy[i] & 0xFF000000) == 0xFF000000) {
566             copy[i] = 0x00000000;
567         } else {
568             hasalpha = 1;
569             hasmask = 0;
570             if (data->length > 1024) free(copy);
571             return;
572         }
573
574     if (!hasmask) {
575         if (data->length > 1024) free(copy);
576         return;
577     }
578
579     //    hmask = (jint)CreateBitmap(w, h, 1, 1, NULL);
580     hmask = (jint)CreateCompatibleBitmap((HDC)org::xwt::plat::Win32::desktop_dc, w, h);
581     maskdc = (jint)CreateCompatibleDC(NULL);
582     SelectObject((HDC)maskdc, (HBITMAP)hmask);
583     StretchDIBits((HDC)maskdc, 0, 0, w, h, 0, 0, w, h, copy, &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
584     if (data->length > 1024) free(copy);
585 }
586
587
588
589 // Win32Surface /////////////////////////////////////////////////////////////////////////////
590
591 void org::xwt::plat::Win32$Win32Surface::natInit(jboolean framed) {
592
593     // Ask the message-handling thread to create a window for us
594     PostThreadMessage((DWORD)org::xwt::plat::Win32::messagePumpThread, WM_USER + 2, (WPARAM)framed, (LPARAM)this);
595     hwndCreated->block();
596
597     // turn on incremental GC now that we have a user-visible interface
598     // [[this is causing segfaults; enable it later...]]
599     // GC_enable_incremental();
600
601     ShowWindow ((HWND)hwnd, SW_SHOWDEFAULT);
602     hdc = (jint)GetDC((HWND)hwnd);
603 }
604
605 void org::xwt::plat::Win32$Win32Surface::finalize() { /* DeleteObject((void*)hwnd); */ }
606 void org::xwt::plat::Win32$Win32Surface::toFront() { BringWindowToTop((HWND)hwnd); }
607 void org::xwt::plat::Win32$Win32Surface::toBack() { SetWindowPos((HWND)hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); }
608 void org::xwt::plat::Win32$Win32Surface::_dispose() { PostMessage((HWND)hwnd, WM_USER_DISPOSE, 0, 0); }
609 void org::xwt::plat::Win32$Win32Surface::setInvisible(jboolean h) { ShowWindow((HWND)hwnd, h ? SW_HIDE : SW_SHOWNORMAL); }
610 void org::xwt::plat::Win32$Win32Surface::_setMinimized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMINIMIZED : SW_SHOWNORMAL); }
611 void org::xwt::plat::Win32$Win32Surface::_setMaximized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); }
612 void org::xwt::plat::Win32$Win32Surface::postCursorChange() { PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0); }
613
614 void org::xwt::plat::Win32$Win32Surface::setLocation(jint x, jint y) {
615     POINT point;
616     RECT rect;
617     point.x = 0;
618     point.y = 0;
619     ClientToScreen((HWND)hwnd, &point);
620     GetWindowRect((HWND)hwnd, &rect);
621     SetWindowPos((HWND)hwnd, NULL, x - (point.x - rect.left), y - (point.y - rect.top), 0, 0, SWP_NOZORDER | SWP_NOSIZE);
622 }
623
624 void org::xwt::plat::Win32$Win32Surface::setSize(jint w, jint h) {
625     RECT client_rect, window_rect;
626     GetClientRect((HWND)hwnd, &client_rect);
627     GetWindowRect((HWND)hwnd, &window_rect);
628     int addwidth = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left);
629     int addheight = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top);
630     SetWindowPos((HWND)hwnd, NULL, 0, 0, w + addwidth, h + addheight, SWP_NOZORDER | SWP_NOMOVE);
631 }
632
633 void org::xwt::plat::Win32$Win32Surface::setTitleBarText(java::lang::String* title) {
634     int len = min(1024, JvGetStringUTFLength(title));
635     char buf[len + 1];
636     buf[len] = '\0';
637     JvGetStringUTFRegion(title, 0, len, buf);
638     SetWindowText((HWND)hwnd, buf);
639 }
640
641 void org::xwt::plat::Win32$Win32Surface::setIcon(org::xwt::Picture* p0) {
642
643     org::xwt::plat::Win32$Win32Picture* p = (org::xwt::plat::Win32$Win32Picture*)p0;
644     int icon_width = GetSystemMetrics(SM_CXSMICON);
645     int icon_height = GetSystemMetrics(SM_CYSMICON);
646
647     // create the bitmap
648     HBITMAP bit = CreateCompatibleBitmap((HDC)hdc, icon_width, icon_height);
649     HDC memdc = CreateCompatibleDC((HDC)hdc);
650     SelectObject(memdc, bit);
651     BLT((HDC)memdc, 0, 0, icon_width, icon_height, (HDC)(p->hdc), 0, 0, p->getWidth(), p->getHeight(), SRCCOPY);
652
653     // create the mask
654     jint* dat = elements(p->data);
655     HBITMAP bit_mask = CreateBitmap(icon_width, icon_height * 2, 1, 1, NULL);
656     SelectObject(memdc, bit_mask);
657     for(int x=0; x<icon_width; x++)
658         for(int y=0; y<icon_height; y++) {
659             int source = dat[(x * p->getWidth()) / icon_width + ((y * p->getHeight()) / icon_height) * (p->getWidth())];
660             if ((source & 0xFF000000) != 0x00000000) SetPixel(memdc, x, y, PALETTERGB(0, 0, 0));
661             else SetPixel(memdc, x, y, PALETTERGB(255, 255, 255));
662         }
663
664     // instantiate the icon and assign it to the window class
665     ICONINFO ici;
666     ici.fIcon = 1;
667     ici.hbmMask = bit_mask;
668     ici.hbmColor = bit;
669     HICON hicon = CreateIconIndirect(&ici);
670     HICON oldicon = (HICON)SetClassLong((HWND)hwnd, GCL_HICONSM, (LONG)hicon);
671     if (oldicon != NULL) DestroyIcon(oldicon);
672     DeleteObject(memdc);
673 }
674
675 static jstring keyToString(WPARAM wParam);
676 jint org::xwt::plat::Win32$Win32Surface::WndProc(jint _hwnd, jint _iMsg, jint _wParam, jint _lParam) {
677
678     UINT iMsg = (UINT)_iMsg;
679     WPARAM wParam = (WPARAM)_wParam;
680     LPARAM lParam = (LPARAM)_lParam;
681
682     int oldmousex, oldmousey;
683     MINMAXINFO* mmi;
684     POINT point;
685     HWND hwnd2;
686     RECT rect, rect2;
687     jboolean newinside;
688     int16_t mouse_x;
689     int16_t mouse_y;
690
691     switch(iMsg) {
692     case WM_DEVMODECHANGE: break;    // FEATURE: color depth changed
693     case WM_DISPLAYCHANGE: break;    // FEATURE: screen size changed
694     case WM_FONTCHANGE: break;       // FEATURE: set of fonts changed
695     case WM_MOUSEWHEEL: break;       // FEATURE: Mouse Wheel
696
697     case WM_SYSKEYDOWN: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
698     case WM_KEYDOWN:
699         KeyPressed(keyToString(wParam));
700         return 0;
701
702     case WM_SYSKEYUP: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
703     case WM_KEYUP:
704         KeyReleased(keyToString(wParam));
705         return 0;
706
707     case WM_SETFOCUS: Focused(true); return 0;
708     case WM_KILLFOCUS: Focused(false); return 0;
709     case WM_LBUTTONDBLCLK: DoubleClick(1); return 0;
710     case WM_RBUTTONDBLCLK: DoubleClick(2); return 0;
711     case WM_MBUTTONDBLCLK: DoubleClick(3); return 0;
712     case WM_LBUTTONDOWN: Press(1); return 0;
713     case WM_RBUTTONDOWN: Press(2); return 0;
714     case WM_MBUTTONDOWN: Press(3); return 0;
715     case WM_CLOSE: Close(); return 0;
716     case WM_ERASEBKGND: return 0;
717
718     case WM_LBUTTONUP:
719     case WM_RBUTTONUP:
720     case WM_MBUTTONUP:
721         Release(iMsg == WM_LBUTTONUP ? 1 : (iMsg == WM_RBUTTONUP ? 2 : 3));
722         if (captured && !inside) {
723             ReleaseCapture();
724             SetCursor((HCURSOR)previous_cursor);
725         }
726         return 0;
727
728     case WM_MOVING:
729         GetClientRect((HWND)hwnd, &rect);
730         PosChange(rect.left, rect.top);
731         return 0;
732
733     case WM_SIZE:
734         if (wParam == SIZE_MINIMIZED) {
735             if (maximized) Maximized(false);
736             Minimized(true);
737         } else if (wParam == SIZE_MAXIMIZED) {
738             if (minimized) Minimized(false); 
739             Maximized(true);
740         } else {
741             if (minimized) Minimized(false); 
742             if (maximized) Maximized(false);
743         }
744         // deliberately fall through to WM_SIZING
745
746     case WM_SIZING:
747         GetClientRect((HWND)hwnd, &rect);
748         SizeChange(rect.right - rect.left, rect.bottom - rect.top);
749         return 0;
750
751     case WM_MOUSEMOVE:
752         if (mousex == lParam & 0xFFFF && mousey == (lParam & 0xFFFF0000) >> 16) return 0;
753
754         GetClientRect((HWND)hwnd, &rect);
755         point.x = mouse_x = lParam & 0xFFFF;
756         point.y = mouse_y = (lParam & 0xFFFF0000) >> 16;
757         ClientToScreen((HWND)hwnd, &point);
758         hwnd2 = WindowFromPoint(point);
759
760         newinside = hwnd2 == (HWND)hwnd && mouse_x > 0 && mouse_y > 0 && mouse_x < (rect.right - rect.left) && mouse_y < (rect.bottom - rect.top) ? 1 : 0;
761
762         Move(mouse_x, mouse_y);
763
764         if (newinside && !inside) {
765             inside = 1;
766             SetCapture((HWND)hwnd);
767             captured = 1;
768             previous_cursor = (jint)GetCursor();
769             PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0);
770
771         } else if (!newinside && inside) {
772             inside = 0;
773             if (!button1 && !button2 && !button3) {
774                 ReleaseCapture();
775                 captured = 0;
776                 SetCursor((HCURSOR)previous_cursor);
777             }
778         }
779
780         return 0;
781
782     case WM_USER_SETCURSOR:
783         // I can't find documentation of this anywhere, but in my experience, an event must be received between an invocation
784         // of SetCapture() and SetCursor(). Furthermore, it seems that after calling SetCursor(), the cursor will not change until a
785         // return from WndProc (ie the completion of the processing of some event).  Hence, we use WM_USER to indicate that the screen
786         // cursor should be re-set to "current_cursor".
787         SetCursor((HCURSOR)current_cursor);
788         return 0;
789
790     case WM_USER_DISPOSE:
791         // used to signal that we should destroy ourselves
792         DestroyWindow((HWND)hwnd);
793         return 0;
794
795     case WM_GETMINMAXINFO:
796         mmi = (MINMAXINFO*)lParam;
797         mmi->ptMinTrackSize.x = root->dmin(0);
798         mmi->ptMinTrackSize.y = root->dmin(1);
799         mmi->ptMaxTrackSize.x = root->dmax(0);
800         mmi->ptMaxTrackSize.y = root->dmax(1);
801         return 0;
802         
803     case WM_PAINT: 
804         PAINTSTRUCT ps;
805         BeginPaint((HWND)org::xwt::plat::Win32$Win32Surface::hwnd, &ps);
806         Dirty(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top);
807         EndPaint((HWND)org::xwt::plat::Win32$Win32Surface::hwnd, &ps);
808         return 0;
809
810     default: break;
811     }  
812
813     return DefWindowProc((HWND)hwnd, iMsg, wParam, lParam);
814 }
815
816
817 // Key Translator Helper /////////////////////////////////////////////////////////////////////
818
819 static char keyarr [256] = { 0 };
820 static jstring keyToString(WPARAM wParam) {
821     char arr[8];
822     keyarr[VK_CAPITAL] = GetKeyState(VK_CAPITAL);
823     keyarr[VK_LSHIFT] = GetKeyState(VK_LSHIFT);
824     keyarr[VK_RSHIFT] = GetKeyState(VK_RSHIFT);
825     keyarr[VK_SHIFT] = GetKeyState(VK_SHIFT);
826
827     if (ToAsciiEx(wParam, 0, (BYTE*)keyarr, (WORD*)arr, 0, GetKeyboardLayout(0)) == 1) {
828         switch (arr[0]) {
829         case '\t': return JvNewStringLatin1("tab");
830         case 0x1b: return JvNewStringLatin1("escape");
831         case '\n': return JvNewStringLatin1("enter");
832         case '\r': return JvNewStringLatin1("enter");
833         case 0x08: return JvNewStringLatin1("back_space");
834         default: return JvNewStringLatin1(arr, 1);
835         }
836         
837     } else switch (wParam) {
838     case VK_CLEAR: return JvNewStringLatin1("clear");
839     case VK_SHIFT: return JvNewStringLatin1("shift");
840     case VK_CONTROL: return JvNewStringLatin1("control");
841     case VK_CAPITAL: return JvNewStringLatin1("caps_lock");
842     case VK_MENU: return JvNewStringLatin1("alt");
843     case VK_PAUSE: return JvNewStringLatin1("pause");
844     case VK_PRIOR: return JvNewStringLatin1("page_up");
845     case VK_NEXT: return JvNewStringLatin1("page_down");
846     case VK_END: return JvNewStringLatin1("end");
847     case VK_HOME: return JvNewStringLatin1("home");
848     case VK_LEFT: return JvNewStringLatin1("left");
849     case VK_UP: return JvNewStringLatin1("up");
850     case VK_RIGHT: return JvNewStringLatin1("right");
851     case VK_DOWN: return JvNewStringLatin1("down");
852     case VK_INSERT: return JvNewStringLatin1("insert");
853     case VK_DELETE: return JvNewStringLatin1("delete");
854     case VK_NUMPAD0: return JvNewStringLatin1("numpad0");
855     case VK_NUMPAD1: return JvNewStringLatin1("numpad1");
856     case VK_NUMPAD2: return JvNewStringLatin1("numpad2");
857     case VK_NUMPAD3: return JvNewStringLatin1("numpad3");
858     case VK_NUMPAD4: return JvNewStringLatin1("numpad4");
859     case VK_NUMPAD5: return JvNewStringLatin1("numpad5");
860     case VK_NUMPAD6: return JvNewStringLatin1("numpad6");
861     case VK_NUMPAD7: return JvNewStringLatin1("numpad7");
862     case VK_NUMPAD8: return JvNewStringLatin1("numpad8");
863     case VK_NUMPAD9: return JvNewStringLatin1("numpad9");
864     case VK_F1: return JvNewStringLatin1("f1");
865     case VK_F2: return JvNewStringLatin1("f2");
866     case VK_F3: return JvNewStringLatin1("f3");
867     case VK_F4: return JvNewStringLatin1("f4");
868     case VK_F5: return JvNewStringLatin1("f5");
869     case VK_F6: return JvNewStringLatin1("f6");
870     case VK_F7: return JvNewStringLatin1("f7");
871     case VK_F8: return JvNewStringLatin1("f8");
872     case VK_F9: return JvNewStringLatin1("f9");
873     case VK_F10: return JvNewStringLatin1("f10");
874     case VK_F11: return JvNewStringLatin1("f11");
875     case VK_F12: return JvNewStringLatin1("f12");
876     case VK_NUMLOCK: return JvNewStringLatin1("num_lock");
877     case VK_SCROLL: return JvNewStringLatin1("scroll_lock");
878     case VK_LSHIFT: return JvNewStringLatin1("shift");
879     case VK_RSHIFT: return JvNewStringLatin1("shift");
880     case VK_LCONTROL: return JvNewStringLatin1("control");
881     case VK_RCONTROL: return JvNewStringLatin1("control");
882     }
883     return NULL;
884 }