2002/05/04 07:25:32
[org.ibex.core.git] / src / org / xwt / plat / Win32.cc
1 // Copyright 2002 Adam Megacz, see the COPYING file for licensing [LGPL]
2
3 // kinda ugly; we use -DCOMPILE_DLL to combine two .cc files into one
4 #ifndef COMPILE_DLL
5
6 // this has to precede the others so we don't get collisions on min/max
7 #include <org/xwt/Box.h>
8
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12
13 #include <windows.h>
14 #include <mmsystem.h>
15 #undef STRICT
16 #undef MAX_PRIORITY
17 #undef MIN_PRIORITY
18 #undef NORM_PRIORITY
19
20 #include <gcj/cni.h>
21
22 #include <java/lang/Integer.h>
23 #include <java/util/Hashtable.h>
24 #include <org/xwt/Box.h>
25 #include <org/xwt/Surface.h>
26 #include <org/xwt/DoubleBuffer.h>
27 #include <org/xwt/Picture.h>
28 #include <org/xwt/Platform.h>
29 #include <org/xwt/Platform$ParsedFont.h>
30 #include <org/xwt/plat/Win32.h>
31 #include <org/xwt/plat/Win32$Win32Font.h>
32 #include <org/xwt/plat/Win32$Win32Surface.h>
33 #include <org/xwt/plat/Win32$Win32DoubleBuffer.h>
34 #include <org/xwt/plat/Win32$Win32Picture.h>
35 #include <org/xwt/util/Semaphore.h>
36
37 // for debugging
38 #include <java/lang/System.h>
39 #include <java/io/PrintStream.h>
40
41 #define WM_USER_SETCURSOR WM_USER
42 #define WM_USER_DISPOSE (WM_USER + 1)
43 #define WM_USER_CREATEWINDOW (WM_USER + 2)
44 #define WS_NORMAL (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
45
46 // FEATURE: there are lots of places where HANDLE's get casted to jint's -- this will break on Win64
47 //          a clean way to do this would be to '#define jraw (gnu::gcj::RawData*)'
48
49 // Callbacks ////////////////////////////////////////////////////////////////////
50
51 LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
52     org::xwt::plat::Win32$Win32Surface* surface =
53         (org::xwt::plat::Win32$Win32Surface*)org::xwt::plat::Win32::hwndToWin32SurfaceMap->get(new java::lang::Integer((jint)hwnd));
54
55     if (surface != NULL) {
56         return (LRESULT)surface->WndProc((jint)hwnd, (jint)iMsg, (jint)wParam, (jint)lParam);
57
58     } else {
59         // this is really lame -- Win32 insists on being able to call your WndProc BEFORE CreateWindow returns...
60         return DefWindowProc(hwnd, iMsg, wParam, lParam);
61     }
62 }
63
64 // This function iterates over each family (lparam == 0), and then over each size (lparam == 1)
65 int CALLBACK fontproc(const LOGFONTA* enumlogfont, const TEXTMETRICA* tm, long unsigned int type, LPARAM lparam) {
66
67     if (lparam == 0) {
68         LOGFONT lf;
69         lf.lfCharSet = ANSI_CHARSET;
70         strncpy(lf.lfFaceName, enumlogfont->lfFaceName, 32);
71         lf.lfPitchAndFamily = 0;
72         EnumFontFamiliesEx((HDC)org::xwt::plat::Win32::desktop_dc, &lf, fontproc, 1, 0);
73
74     } else {
75         org::xwt::plat::Win32::addFont(JvNewStringLatin1(enumlogfont->lfFaceName),
76                                        ((type & RASTER_FONTTYPE) == 0) ? 0 : tm->tmHeight,
77                                        tm->tmItalic == 0 ? 0 : 1, 
78                                        tm->tmWeight <= 400 ? 0 : 1);
79     }
80     return 1;
81 }
82
83
84 // Initialization ////////////////////////////////////////////////////////////////////
85
86 static int window_class_counter = 0;
87
88 jstring org::xwt::plat::Win32::getTempPath() {
89     char buf[1024];
90     DWORD ret = GetTempPath(1024, buf);
91     if (ret == 0) criticalAbort(JvNewStringLatin1("GetTempPath() failed"));
92     return JvNewStringLatin1(buf);
93 }
94
95 // XOR mask for the hand cursor
96 static unsigned char hand_cursor_xor[32 * 4] = {
97   0x00, 0x00, 0x00, 0x00,
98   0x00, 0x0C, 0x00, 0x00,
99   0x00, 0x0C, 0x00, 0x00,
100   0x00, 0x0C, 0x00, 0x00,
101   0x00, 0x0C, 0x00, 0x00,
102   0x00, 0x0C, 0x00, 0x00,
103   0x00, 0x0D, 0x80, 0x00,
104   0x00, 0x0D, 0xB0, 0x00,
105   0x00, 0x0D, 0xB4, 0x00,
106   0x00, 0x0D, 0xB6, 0x00,
107   0x00, 0xCF, 0xF6, 0x00,
108   0x00, 0xEF, 0xFE, 0x00,
109   0x00, 0x6F, 0xFE, 0x00,
110   0x00, 0x2F, 0xFE, 0x00,
111   0x00, 0x3F, 0xFE, 0x00,
112   0x00, 0x1F, 0xFE, 0x00,
113   0x00, 0x1F, 0xFC, 0x00,
114   0x00, 0x0F, 0xFC, 0x00,
115   0x00, 0x0F, 0xFC, 0x00,
116   0x00, 0x07, 0xF8, 0x00,
117   0x00, 0x07, 0xF8, 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   0x00, 0x00, 0x00, 0x00
129 };
130
131 // AND mask for the hand cursor
132 static unsigned char hand_cursor_and[32 * 4] = {
133   0xFF, 0xF3, 0xFF, 0xFF,
134   0xFF, 0xE1, 0xFF, 0xFF,
135   0xFF, 0xE1, 0xFF, 0xFF,
136   0xFF, 0xE1, 0xFF, 0xFF,
137   0xFF, 0xE1, 0xFF, 0xFF,
138   0xFF, 0xE0, 0x7F, 0xFF,
139   0xFF, 0xE0, 0x0F, 0xFF,
140   0xFF, 0xE0, 0x03, 0xFF,
141   0xFF, 0xE0, 0x01, 0xFF,
142   0xFF, 0x20, 0x00, 0xFF,
143   0xFE, 0x00, 0x00, 0xFF,
144   0xFE, 0x00, 0x00, 0xFF,
145   0xFF, 0x00, 0x00, 0xFF,
146   0xFF, 0x80, 0x00, 0xFF,
147   0xFF, 0x80, 0x00, 0xFF,
148   0xFF, 0xC0, 0x00, 0xFF,
149   0xFF, 0xC0, 0x01, 0xFF,
150   0xFF, 0xE0, 0x01, 0xFF,
151   0xFF, 0xE0, 0x01, 0xFF,
152   0xFF, 0xF0, 0x03, 0xFF,
153   0xFF, 0xF0, 0x03, 0xFF,
154   0xFF, 0xF0, 0x03, 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   0xFF, 0xFF, 0xFF, 0xFF
165 };
166
167
168 void org::xwt::plat::Win32::natInit() {
169
170     // grab desktop dc/handle
171     desktop_handle = (jint)GetDesktopWindow();
172     desktop_dc = (jint)GetDC((HWND)desktop_handle);
173
174     // create cursors
175     org::xwt::plat::Win32::wait_cursor = (jint)LoadCursor(NULL, IDC_WAIT);
176     org::xwt::plat::Win32::default_cursor = (jint)LoadCursor(NULL, IDC_ARROW);
177     org::xwt::plat::Win32::crosshair_cursor = (jint)LoadCursor(NULL, IDC_CROSS);
178     org::xwt::plat::Win32::text_cursor = (jint)LoadCursor(NULL, IDC_IBEAM);
179     org::xwt::plat::Win32::move_cursor = (jint)LoadCursor(NULL, IDC_SIZEALL);
180     org::xwt::plat::Win32::sizenesw_cursor = (jint)LoadCursor(NULL, IDC_SIZENESW);
181     org::xwt::plat::Win32::sizens_cursor = (jint)LoadCursor(NULL, IDC_SIZENS);
182     org::xwt::plat::Win32::sizenwse_cursor = (jint)LoadCursor(NULL, IDC_SIZENWSE);
183     org::xwt::plat::Win32::sizewe_cursor = (jint)LoadCursor(NULL, IDC_SIZEWE);
184     org::xwt::plat::Win32::hand_cursor = (jint)CreateCursor(GetModuleHandle(NULL), 14, 1, 32, 32, hand_cursor_and, hand_cursor_xor);
185
186     // enumerate fonts
187     LOGFONT lf;
188     lf.lfCharSet = ANSI_CHARSET;
189     lf.lfFaceName[0] = 0;
190     lf.lfPitchAndFamily = 0;
191     EnumFontFamiliesEx((HDC)desktop_dc, &lf, fontproc, 0, 0);
192
193     messagePumpThread = (jint)GetCurrentThreadId();
194     messagePumpStarted->release();
195
196     MSG msg;
197     while(GetMessage(&msg, (HWND)NULL, 0, 0) > 0) {
198
199         if (msg.message == WM_USER_CREATEWINDOW) {
200             org::xwt::plat::Win32$Win32Surface *surface = (org::xwt::plat::Win32$Win32Surface*)msg.lParam;
201
202             // we must create a unique window class name for each
203             // window so that minimization icons can be set independantly
204             char buf[255];
205             sprintf(buf, "XWT_WINDOW_CLASS_%i", window_class_counter++);
206
207             WNDCLASSEX wc;
208             wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
209             wc.lpfnWndProc = WndProc;
210             wc.cbClsExtra = 0;
211             wc.cbSize = sizeof(WNDCLASSEX);
212             wc.cbWndExtra = 0;
213             wc.hInstance = GetModuleHandle(NULL);
214             wc.hIcon = NULL;
215             wc.hIconSm = NULL;
216             wc.hCursor = NULL;
217             wc.hbrBackground = (HBRUSH)(COLOR_SCROLLBAR + 1);
218             wc.lpszMenuName = NULL;
219             wc.lpszClassName = buf;
220             RegisterClassEx(&wc);
221             
222             surface->hwnd = (jint)CreateWindow(wc.lpszClassName, TEXT(""), msg.wParam ? WS_NORMAL : WS_POPUP, 200, 200, 100, 100,
223                                                (HWND__*)NULL, (HMENU__*)NULL, GetModuleHandle(NULL), (LPVOID)NULL);
224             SetFocus((HWND)surface->hwnd);
225             surface->hwndCreated->release();
226             
227         } else {
228             TranslateMessage(&msg);
229             if (msg.message == WM_LBUTTONDOWN || msg.message == WM_RBUTTONDOWN || msg.message == WM_MBUTTONDOWN) SetFocus(msg.hwnd);
230             DispatchMessage(&msg);
231         }
232
233     }
234     java::lang::System::exit(-1);
235 }
236
237
238 // Platform Methods ///////////////////////////////////////////////////////////////////
239
240 void org::xwt::plat::Win32::__detectProxy(JArray<jstring>* container) {
241
242     HKEY hkey;
243     char buf[1024];
244     DWORD buflen = 1024;
245     DWORD type;
246     LONG result = RegOpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", &hkey);
247     if (result != ERROR_SUCCESS) return;
248     
249     buf[0] = '\0';
250     type = REG_SZ;
251     buflen = 1024;
252     result = RegQueryValueEx(hkey, "AutoConfigURL", NULL, &type, (LPBYTE)buf, &buflen);
253     buf[buflen] = '\0';
254     if (result == ERROR_SUCCESS) elements(container)[2] = JvNewStringLatin1(buf);
255
256     buf[0] = '\0';
257     type = REG_BINARY;
258     RegQueryValueEx(hkey, "ProxyEnable", NULL, &type, (LPBYTE)buf, &buflen);
259     if (buf[0] != 1) return;
260
261     buf[0] = '\0';
262     type = REG_SZ;
263     buflen = 1024;
264     RegQueryValueEx(hkey, "ProxyServer", NULL, &type, (LPBYTE)buf, &buflen);
265     buf[buflen] = '\0';
266     elements(container)[0] = JvNewStringLatin1(buf);
267
268     buf[0] = '\0';
269     buflen = 1024;
270     RegQueryValueEx(hkey, "ProxyOverride", NULL, &type, (LPBYTE)buf, &buflen);
271     buf[buflen] = '\0';
272     elements(container)[1] = JvNewStringLatin1(buf);
273 }
274
275 jstring org::xwt::plat::Win32::_getClipBoard() {
276     OpenClipboard((HWND)desktop_handle);
277     HGLOBAL hmem = GetClipboardData(CF_TEXT);
278     if (hmem == NULL) return NULL;
279     char* buf = (char*)GlobalLock(hmem);
280     if (buf == NULL) return NULL;
281     jstring ret = JvNewStringLatin1(buf);
282     GlobalUnlock(hmem);
283     CloseClipboard();
284     return ret;
285 }
286
287 void org::xwt::plat::Win32::_setClipBoard(jstring s) {
288     OpenClipboard((HWND)desktop_handle);
289     HGLOBAL hmem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, JvGetStringUTFLength(s) + 1);
290     if (hmem == NULL) return;
291     char* buf = (char*)GlobalLock(hmem);
292     if (buf == NULL) return;
293     JvGetStringUTFRegion(s, 0, JvGetStringUTFLength(s), buf);
294     buf[JvGetStringUTFLength(s)] = '\0';
295     GlobalUnlock(hmem);
296     SetClipboardData(CF_TEXT, hmem);
297     CloseClipboard();
298 }
299
300 void org::xwt::plat::Win32::_criticalAbort(jstring message) {
301     char buf[JvGetStringUTFLength(message) + 1];
302     buf[JvGetStringUTFLength(message)] = '\0';
303     JvGetStringUTFRegion(message, 0, JvGetStringUTFLength(message), buf);
304     MessageBox (NULL, buf, "XWT Critical Abort", MB_OK | MB_ICONSTOP | MB_TASKMODAL | MB_SETFOREGROUND);
305     java::lang::System::exit(-1);
306 }
307
308 jint org::xwt::plat::Win32::_getScreenWidth() {
309     RECT rect;
310     SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
311     return rect.right - rect.left;
312 }
313
314 jint org::xwt::plat::Win32::_getScreenHeight() {
315     RECT rect;
316     SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
317     return rect.bottom - rect.top;
318 }
319
320 org::xwt::plat::Win32$Win32Font* org::xwt::plat::Win32::mapFont(org::xwt::Platform$ParsedFont* pf) {
321     org::xwt::plat::Win32$Win32Font* ret = new org::xwt::plat::Win32$Win32Font();
322     LOGFONT logfont;
323     memset(&logfont, 0, sizeof(LOGFONT));
324     logfont.lfHeight = -MulDiv(pf->size, GetDeviceCaps((HDC)org::xwt::plat::Win32::desktop_dc, LOGPIXELSY), 72);
325     if (pf->italic) logfont.lfItalic = 1;
326     if (pf->bold) logfont.lfWeight = FW_BOLD;
327     logfont.lfCharSet = ANSI_CHARSET;
328
329     JvGetStringUTFRegion(pf->name, 0, min(31, JvGetStringUTFLength(pf->name)), logfont.lfFaceName);
330     logfont.lfFaceName[min(31, JvGetStringUTFLength(pf->name))] = 0;
331
332     ret->hfont = (jint)CreateFontIndirect(&logfont);
333     SelectObject((HDC)desktop_dc, (HFONT)(ret->hfont));
334
335     TEXTMETRIC tm;
336     GetTextMetrics((HDC)desktop_dc, &tm);
337     POINT p;
338     p.x = 0; p.y = tm.tmAscent;
339     LPtoDP((HDC)desktop_dc, &p, 1); 
340     ret->maxAscent = p.y;
341
342     p.x = 0; p.y = tm.tmDescent;
343     LPtoDP((HDC)desktop_dc, &p, 1);
344     ret->maxDescent = p.y;
345
346     return ret;
347 }
348
349 jint org::xwt::plat::Win32::_stringWidth(jstring font, jstring text) {
350
351     HFONT hfont = (HFONT)(getFont(font)->hfont);
352     SelectObject((HDC)org::xwt::plat::Win32::desktop_dc, hfont);
353
354     int len = min(1024, JvGetStringUTFLength(text));
355     char buf[len + 1];
356     buf[len] = '\0';
357     JvGetStringUTFRegion(text, 0, len, buf);
358     
359     SIZE size;
360     GetTextExtentPoint32((HDC)org::xwt::plat::Win32::desktop_dc, buf, len, &size);
361     return size.cx;
362 }
363
364 jboolean org::xwt::plat::Win32::_newBrowserWindow_(jstring url) {
365
366     int len = min(2048, JvGetStringUTFLength(url));
367     char buf[len + 1];
368     JvGetStringUTFRegion(url, 0, len, buf);
369     buf[len] = '\0';
370
371     SHELLEXECUTEINFO ei;
372     memset(&ei, 0, sizeof(ei));
373     ei.cbSize = sizeof(ei);
374     ei.lpVerb = "open";
375     ei.lpFile = buf;
376     ei.fMask  = SEE_MASK_NOCLOSEPROCESS;
377     ei.nShow  = SW_SHOWDEFAULT;
378     return (ShellExecuteEx(&ei) == 0);
379
380 }
381
382
383 // Win32DoubleBuffer /////////////////////////////////////////////////////////////////////////
384
385 // This is a scratch area; when blitting a translucent image, we copy the underlying data here first.
386 // Since all drawing operations are single-threaded, it's safe to use a global here.
387 static HBITMAP scratch = NULL;
388 static HDC scratch_dc = NULL;
389 static jint* scratch_bits = NULL;
390 static jint scratch_w = 0;
391 static jint scratch_h = 0;
392
393 #define BLT(dest, dx1, dy1, dx2, dy2, src, sx1, sy1, sx2, sy2, op)                                   \
394     if ((dx2 - dx1 == sx2 - sx1) && (dy2 - dy1 == sy2 - sy1))                                        \
395         BitBlt(dest, dx1, dy1, dx2 - dx1, dy2 - dy1, src, sx1, sy1, op);                             \
396     else                                                                                             \
397         StretchBlt(dest, dx1, dy1, dx2 - dx1, dy2 - dy1, src, sx1, sy1, sx2 - sx1, sy2 - sy1, op);
398         
399 void org::xwt::plat::Win32$Win32DoubleBuffer::drawPicture(org::xwt::Picture* source0,
400                                                           jint dx1, jint dy1, jint dx2, jint dy2,
401                                                           jint sx1, jint sy1, jint sx2, jint sy2) {
402
403     org::xwt::plat::Win32$Win32Picture* source = (org::xwt::plat::Win32$Win32Picture*)source0;
404
405     if (source->hasalpha) {
406
407         if (scratch == NULL || scratch_w < dx2 - dx1 || scratch_h < dy2 - dy1) {
408             if (scratch_dc != NULL) DeleteDC(scratch_dc);
409             if (scratch != NULL) DeleteObject(scratch);
410             scratch_w = max(dx2 - dx1, scratch_w);
411             scratch_h = max(dy2 - dy1, scratch_h);
412
413             BITMAPINFO bitmapinfo;
414             memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
415             bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
416             bitmapinfo.bmiHeader.biWidth = scratch_w;
417             bitmapinfo.bmiHeader.biHeight = -1 * scratch_h;
418             bitmapinfo.bmiHeader.biPlanes = 1;
419             bitmapinfo.bmiHeader.biBitCount = 32;
420             bitmapinfo.bmiHeader.biCompression = BI_RGB;
421
422             // create section DIB
423             scratch = CreateDIBSection(NULL, &bitmapinfo, DIB_RGB_COLORS, (void**)&scratch_bits, NULL, 0);
424             scratch_dc = CreateCompatibleDC((HDC)org::xwt::plat::Win32::desktop_dc);
425             SelectObject(scratch_dc, scratch);
426         }
427
428         // copy from screen to scratch
429         BitBlt((HDC)scratch_dc, 0, 0, dx2 - dx1, dy2 - dy1, (HDC)hdc, dx1, dy1, SRCCOPY);
430
431         // apply alpha-blending to scratch
432         jint* dat = elements(source->data);
433
434         for(int x = max(clipx1, dx1) - dx1; x < min(clipx2, dx2) - dx1; x++)
435             for(int y = max(clipy1, dy1) - dy1; y < min(clipy2, dy2) - dy1; y++) {
436                 int sx = (x * (sx2 - sx1)) / (dx2 - dx1) + sx1;
437                 int sy = (y * (sy2 - sy1)) / (dy2 - dy1) + sy1;
438                 jint dst = scratch_bits[y * scratch_w + x];
439                 jint src = dat[sy * source->getWidth() + sx];
440                 jint alpha = (src & 0xFF000000) >> 24;
441                 jint r = (((src & 0x00FF0000) >> 16) * alpha + ((dst & 0x00FF0000) >> 16) * (0xFF - alpha)) / 0xFF;
442                 jint g = (((src & 0x0000FF00) >> 8)  * alpha + ((dst & 0x0000FF00) >> 8)  * (0xFF - alpha)) / 0xFF;
443                 jint b = (((src & 0x000000FF))       * alpha + ((dst & 0x000000FF))       * (0xFF - alpha)) / 0xFF;
444                 scratch_bits[y * scratch_w + x] = (r << 16) | (g << 8) | b;
445             }
446
447         // copy back from scratch to screen
448         BitBlt((HDC)hdc, dx1, dy1, dx2 - dx1, dy2 - dy1, (HDC)scratch_dc, 0, 0, SRCCOPY);
449
450     } else {
451         if (source->hasmask) {
452             BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->maskdc, sx1, sy1, sx2, sy2, SRCAND);
453             BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->hdc, sx1, sy1, sx2, sy2, SRCPAINT);
454         } else {
455             BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->hdc, sx1, sy1, sx2, sy2, SRCCOPY);
456         }
457
458     }
459
460 }
461
462 void org::xwt::plat::Win32$Win32DoubleBuffer::drawString(jstring font, jstring text, jint x, jint y, jint color) {
463
464     org::xwt::plat::Win32$Win32Font* wf = org::xwt::plat::Win32::getFont(font);
465     SelectObject((HDC)hdc, (HFONT)(wf->hfont));
466
467     // Platform API passes us the y-pos of the bottom of the text; we need the top
468     y -= wf->maxAscent;
469
470     int len = min(1024, JvGetStringUTFLength(text));
471     char buf[len + 1];
472     buf[len] = '\0';
473     JvGetStringUTFRegion(text, 0, len, buf);
474
475     SetTextColor((HDC)hdc, PALETTERGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
476     TextOut((HDC)hdc, x, y, buf, len);
477 }
478
479 void org::xwt::plat::Win32$Win32DoubleBuffer::fillRect(jint x, jint y, jint x2, jint y2, jint color) {
480     jint w = x2 - x;
481     jint h = y2 - y;
482
483     // sadly, the ability to change the color of a brush didn't arrive until Win2k...
484     HBRUSH brush = CreateSolidBrush(PALETTERGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
485     if (!brush) return;
486     RECT rect = { x, y, x + w, y + h };
487     FillRect((HDC)hdc, &rect, brush);
488     DeleteObject(brush);
489 }
490
491 void org::xwt::plat::Win32$Win32Surface::blit(org::xwt::DoubleBuffer* s, jint sx, jint sy, jint dx, jint dy, jint dx2, jint dy2) {
492     BitBlt((HDC)hdc, dx, dy, dx2 - dx, dy2 - dy, (HDC)(((org::xwt::plat::Win32$Win32DoubleBuffer*)s)->hdc), sx, sy, SRCCOPY);
493 }
494
495 void org::xwt::plat::Win32$Win32DoubleBuffer::natInit() {
496     hbitmap = (jint)CreateCompatibleBitmap((HDC)org::xwt::plat::Win32::desktop_dc, w, h);
497     hdc = (jint)CreateCompatibleDC((HDC)org::xwt::plat::Win32::desktop_dc);
498     SetBkMode((HDC)hdc, TRANSPARENT);
499     SelectObject((HDC)hdc, (HBITMAP)hbitmap);
500 }
501
502 void org::xwt::plat::Win32$Win32DoubleBuffer::setClip(jint x, jint y, jint x2, jint y2) {
503     clipx1 = x; clipx2 = x2; clipy1 = y; clipy2 = y2;
504     HRGN hrgn = CreateRectRgn(x, y, x2, y2);
505     SelectClipRgn((HDC)hdc, hrgn);
506     DeleteObject(hrgn);
507 }
508
509 void org::xwt::plat::Win32$Win32DoubleBuffer::finalize() {
510     DeleteObject((void*)hdc);
511     DeleteObject((void*)hbitmap);
512 }
513
514
515
516 // Win32Picture /////////////////////////////////////////////////////////////////////////
517
518 void org::xwt::plat::Win32$Win32Picture::natInit() {
519
520     BITMAPINFO bitmapinfo;
521     memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
522     bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
523     bitmapinfo.bmiHeader.biWidth = w;
524     bitmapinfo.bmiHeader.biHeight = -1 * h;
525     bitmapinfo.bmiHeader.biPlanes = 1;
526     bitmapinfo.bmiHeader.biBitCount = 32;
527     bitmapinfo.bmiHeader.biCompression = BI_RGB;
528
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     SelectObject((HDC)hdc, (HBITMAP)hbitmap);
532     uint32_t* dat = (uint32_t*)elements(data);
533     for(int i=0; i<data->length; i++) if ((dat[i] & 0xFF000000) == 0x00000000) dat[i] = 0x00000000;
534     StretchDIBits((HDC)hdc, 0, 0, w, h, 0, 0, w, h, elements(data), &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
535
536     jint _copy[min(1024, data->length)];
537     jint* copy = data->length > 1024 ? (jint*)malloc(data->length * 4) : _copy;
538
539     memcpy(copy, elements(data), data->length * 4);
540     for(int i=0; i<data->length; i++)
541         if ((copy[i] & 0xFF000000) == 0x00000000) {
542             hasmask = 1;
543             copy[i] = 0x00FFFFFF;
544         } else if ((copy[i] & 0xFF000000) == 0xFF000000) {
545             copy[i] = 0x00000000;
546         } else {
547             hasalpha = 1;
548             hasmask = 0;
549             if (data->length > 1024) free(copy);
550             return;
551         }
552
553     if (!hasmask) {
554         if (data->length > 1024) free(copy);
555         return;
556     }
557
558     //    hmask = (jint)CreateBitmap(w, h, 1, 1, NULL);
559     hmask = (jint)CreateCompatibleBitmap((HDC)org::xwt::plat::Win32::desktop_dc, w, h);
560     maskdc = (jint)CreateCompatibleDC(NULL);
561     SelectObject((HDC)maskdc, (HBITMAP)hmask);
562     StretchDIBits((HDC)maskdc, 0, 0, w, h, 0, 0, w, h, copy, &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
563     if (data->length > 1024) free(copy);
564 }
565
566
567
568 // Win32Surface /////////////////////////////////////////////////////////////////////////////
569
570 void org::xwt::plat::Win32$Win32Surface::natInit(jboolean framed) {
571
572     // Ask the message-handling thread to create a window for us
573     PostThreadMessage((DWORD)org::xwt::plat::Win32::messagePumpThread, WM_USER + 2, (WPARAM)framed, (LPARAM)this);
574     hwndCreated->block();
575
576     // turn on incremental GC now that we have a user-visible interface
577     // [[this is causing segfaults; enable it later...]]
578     // GC_enable_incremental();
579
580     ShowWindow ((HWND)hwnd, SW_SHOWDEFAULT);
581     hdc = (jint)GetDC((HWND)hwnd);
582 }
583
584 void org::xwt::plat::Win32$Win32Surface::finalize() { /* DeleteObject((void*)hwnd); */ }
585 void org::xwt::plat::Win32$Win32Surface::toFront() { BringWindowToTop((HWND)hwnd); }
586 void org::xwt::plat::Win32$Win32Surface::toBack() { SetWindowPos((HWND)hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); }
587 void org::xwt::plat::Win32$Win32Surface::_dispose() { PostMessage((HWND)hwnd, WM_USER_DISPOSE, 0, 0); }
588 void org::xwt::plat::Win32$Win32Surface::setInvisible(jboolean h) { ShowWindow((HWND)hwnd, h ? SW_HIDE : SW_SHOWNORMAL); }
589 void org::xwt::plat::Win32$Win32Surface::_setMinimized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMINIMIZED : SW_SHOWNORMAL); }
590 void org::xwt::plat::Win32$Win32Surface::_setMaximized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); }
591 void org::xwt::plat::Win32$Win32Surface::postCursorChange() { PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0); }
592
593 void org::xwt::plat::Win32$Win32Surface::setLocation(jint x, jint y) {
594     POINT point;
595     RECT rect;
596     point.x = 0;
597     point.y = 0;
598     ClientToScreen((HWND)hwnd, &point);
599     GetWindowRect((HWND)hwnd, &rect);
600     SetWindowPos((HWND)hwnd, NULL, x - (point.x - rect.left), y - (point.y - rect.top), 0, 0, SWP_NOZORDER | SWP_NOSIZE);
601 }
602
603 void org::xwt::plat::Win32$Win32Surface::setSize(jint w, jint h) {
604     RECT client_rect, window_rect;
605     GetClientRect((HWND)hwnd, &client_rect);
606     GetWindowRect((HWND)hwnd, &window_rect);
607     int addwidth = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left);
608     int addheight = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top);
609     SetWindowPos((HWND)hwnd, NULL, 0, 0, w + addwidth, h + addheight, SWP_NOZORDER | SWP_NOMOVE);
610 }
611
612 void org::xwt::plat::Win32$Win32Surface::setTitleBarText(java::lang::String* title) {
613     int len = min(1024, JvGetStringUTFLength(title));
614     char buf[len + 1];
615     buf[len] = '\0';
616     JvGetStringUTFRegion(title, 0, len, buf);
617     SetWindowText((HWND)hwnd, buf);
618 }
619
620 void org::xwt::plat::Win32$Win32Surface::setIcon(org::xwt::Picture* p0) {
621
622     org::xwt::plat::Win32$Win32Picture* p = (org::xwt::plat::Win32$Win32Picture*)p0;
623     int icon_width = GetSystemMetrics(SM_CXSMICON);
624     int icon_height = GetSystemMetrics(SM_CYSMICON);
625
626     // create the bitmap
627     HBITMAP bit = CreateCompatibleBitmap((HDC)hdc, icon_width, icon_height);
628     HDC memdc = CreateCompatibleDC((HDC)hdc);
629     SelectObject(memdc, bit);
630     BLT((HDC)memdc, 0, 0, icon_width, icon_height, (HDC)(p->hdc), 0, 0, p->getWidth(), p->getHeight(), SRCCOPY);
631
632     // create the mask
633     jint* dat = elements(p->data);
634     HBITMAP bit_mask = CreateBitmap(icon_width, icon_height * 2, 1, 1, NULL);
635     SelectObject(memdc, bit_mask);
636     for(int x=0; x<icon_width; x++)
637         for(int y=0; y<icon_height; y++) {
638             int source = dat[(x * p->getWidth()) / icon_width + ((y * p->getHeight()) / icon_height) * (p->getWidth())];
639             if ((source & 0xFF000000) != 0x00000000) SetPixel(memdc, x, y, PALETTERGB(0, 0, 0));
640             else SetPixel(memdc, x, y, PALETTERGB(255, 255, 255));
641         }
642
643     // instantiate the icon and assign it to the window class
644     ICONINFO ici;
645     ici.fIcon = 1;
646     ici.hbmMask = bit_mask;
647     ici.hbmColor = bit;
648     HICON hicon = CreateIconIndirect(&ici);
649     HICON oldicon = (HICON)SetClassLong((HWND)hwnd, GCL_HICONSM, (LONG)hicon);
650     if (oldicon != NULL) DestroyIcon(oldicon);
651     DeleteObject(memdc);
652 }
653
654 static jstring keyToString(WPARAM wParam);
655 jint org::xwt::plat::Win32$Win32Surface::WndProc(jint _hwnd, jint _iMsg, jint _wParam, jint _lParam) {
656
657     UINT iMsg = (UINT)_iMsg;
658     WPARAM wParam = (WPARAM)_wParam;
659     LPARAM lParam = (LPARAM)_lParam;
660
661     int oldmousex, oldmousey;
662     MINMAXINFO* mmi;
663     POINT point;
664     HWND hwnd2;
665     RECT rect, rect2;
666     jboolean newinside;
667     int16_t mouse_x;
668     int16_t mouse_y;
669
670     switch(iMsg) {
671     case WM_DEVMODECHANGE: break;    // FEATURE: color depth changed
672     case WM_DISPLAYCHANGE: break;    // FEATURE: screen size changed
673     case WM_FONTCHANGE: break;       // FEATURE: set of fonts changed
674     case WM_MOUSEWHEEL: break;       // FEATURE: Mouse Wheel
675
676     case WM_SYSKEYDOWN: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
677     case WM_KEYDOWN:
678         KeyPressed(keyToString(wParam));
679         return 0;
680
681     case WM_SYSKEYUP: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
682     case WM_KEYUP:
683         KeyReleased(keyToString(wParam));
684         return 0;
685
686     case WM_SETFOCUS: Focused(true); return 0;
687     case WM_KILLFOCUS: Focused(false); return 0;
688     case WM_LBUTTONDBLCLK: DoubleClick(1); return 0;
689     case WM_RBUTTONDBLCLK: DoubleClick(2); return 0;
690     case WM_MBUTTONDBLCLK: DoubleClick(3); return 0;
691     case WM_LBUTTONDOWN: Press(1); return 0;
692     case WM_RBUTTONDOWN: Press(2); return 0;
693     case WM_MBUTTONDOWN: Press(3); return 0;
694     case WM_CLOSE: Close(); return 0;
695     case WM_ERASEBKGND: return 0;
696
697     case WM_LBUTTONUP:
698     case WM_RBUTTONUP:
699     case WM_MBUTTONUP:
700         Release(iMsg == WM_LBUTTONUP ? 1 : (iMsg == WM_RBUTTONUP ? 2 : 3));
701         if (captured && !inside) {
702             ReleaseCapture();
703             SetCursor((HCURSOR)previous_cursor);
704         }
705         return 0;
706
707     case WM_MOVING:
708         GetClientRect((HWND)hwnd, &rect);
709         PosChange(rect.left, rect.top);
710         return 0;
711
712     case WM_SIZE:
713         if (wParam == SIZE_MINIMIZED) {
714             if (maximized) Maximized(false);
715             Minimized(true);
716         } else if (wParam == SIZE_MAXIMIZED) {
717             if (minimized) Minimized(false); 
718             Maximized(true);
719         } else {
720             if (minimized) Minimized(false); 
721             if (maximized) Maximized(false);
722         }
723         // deliberately fall through to WM_SIZING
724
725     case WM_SIZING:
726         GetClientRect((HWND)hwnd, &rect);
727         SizeChange(rect.right - rect.left, rect.bottom - rect.top);
728         return 0;
729
730     case WM_MOUSEMOVE:
731         if (mousex == lParam & 0xFFFF && mousey == (lParam & 0xFFFF0000) >> 16) return 0;
732
733         GetClientRect((HWND)hwnd, &rect);
734         point.x = mouse_x = lParam & 0xFFFF;
735         point.y = mouse_y = (lParam & 0xFFFF0000) >> 16;
736         ClientToScreen((HWND)hwnd, &point);
737         hwnd2 = WindowFromPoint(point);
738
739         newinside = hwnd2 == (HWND)hwnd && mouse_x > 0 && mouse_y > 0 && mouse_x < (rect.right - rect.left) && mouse_y < (rect.bottom - rect.top) ? 1 : 0;
740
741         Move(mouse_x, mouse_y);
742
743         if (newinside && !inside) {
744             inside = 1;
745             SetCapture((HWND)hwnd);
746             captured = 1;
747             previous_cursor = (jint)GetCursor();
748             PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0);
749
750         } else if (!newinside && inside) {
751             inside = 0;
752             if (!button1 && !button2 && !button3) {
753                 ReleaseCapture();
754                 captured = 0;
755                 SetCursor((HCURSOR)previous_cursor);
756             }
757         }
758
759         return 0;
760
761     case WM_USER_SETCURSOR:
762         // I can't find documentation of this anywhere, but in my experience, an event must be received between an invocation
763         // of SetCapture() and SetCursor(). Furthermore, it seems that after calling SetCursor(), the cursor will not change until a
764         // return from WndProc (ie the completion of the processing of some event).  Hence, we use WM_USER to indicate that the screen
765         // cursor should be re-set to "current_cursor".
766         SetCursor((HCURSOR)current_cursor);
767         return 0;
768
769     case WM_USER_DISPOSE:
770         // used to signal that we should destroy ourselves
771         DestroyWindow((HWND)hwnd);
772         return 0;
773
774     case WM_GETMINMAXINFO:
775         mmi = (MINMAXINFO*)lParam;
776         mmi->ptMinTrackSize.x = root->dmin(0);
777         mmi->ptMinTrackSize.y = root->dmin(1);
778         mmi->ptMaxTrackSize.x = root->dmax(0);
779         mmi->ptMaxTrackSize.y = root->dmax(1);
780         return 0;
781         
782     case WM_PAINT: 
783         PAINTSTRUCT ps;
784         BeginPaint((HWND)org::xwt::plat::Win32$Win32Surface::hwnd, &ps);
785         Dirty(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top);
786         EndPaint((HWND)org::xwt::plat::Win32$Win32Surface::hwnd, &ps);
787         return 0;
788
789     default: break;
790     }  
791
792     return DefWindowProc((HWND)hwnd, iMsg, wParam, lParam);
793 }
794
795
796 // Key Translator Helper /////////////////////////////////////////////////////////////////////
797
798 static char keyarr [256] = { 0 };
799 static jstring keyToString(WPARAM wParam) {
800     char arr[4];
801     keyarr[VK_CAPITAL] = GetKeyState(VK_CAPITAL);
802     keyarr[VK_LSHIFT] = GetKeyState(VK_LSHIFT);
803     keyarr[VK_RSHIFT] = GetKeyState(VK_RSHIFT);
804     keyarr[VK_SHIFT] = GetKeyState(VK_SHIFT);
805     
806     if (ToAscii(wParam, 0, (BYTE*)keyarr, (WORD*)arr, 0) == 1) {
807         switch (arr[0]) {
808         case '\t': return JvNewStringLatin1("tab");
809         case 0x1b: return JvNewStringLatin1("escape");
810         case '\n': return JvNewStringLatin1("enter");
811         case '\r': return JvNewStringLatin1("enter");
812         case 0x08: return JvNewStringLatin1("back_space");
813         default: return JvNewStringLatin1(arr, 1);
814         }
815         
816     } else switch (wParam) {
817     case VK_CLEAR: return JvNewStringLatin1("clear");
818     case VK_SHIFT: return JvNewStringLatin1("shift");
819     case VK_CONTROL: return JvNewStringLatin1("control");
820     case VK_CAPITAL: return JvNewStringLatin1("caps_lock");
821     case VK_MENU: return JvNewStringLatin1("alt");
822     case VK_PAUSE: return JvNewStringLatin1("pause");
823     case VK_PRIOR: return JvNewStringLatin1("page_up");
824     case VK_NEXT: return JvNewStringLatin1("page_down");
825     case VK_END: return JvNewStringLatin1("end");
826     case VK_HOME: return JvNewStringLatin1("home");
827     case VK_LEFT: return JvNewStringLatin1("left");
828     case VK_UP: return JvNewStringLatin1("up");
829     case VK_RIGHT: return JvNewStringLatin1("right");
830     case VK_DOWN: return JvNewStringLatin1("down");
831     case VK_INSERT: return JvNewStringLatin1("insert");
832     case VK_DELETE: return JvNewStringLatin1("delete");
833     case VK_NUMPAD0: return JvNewStringLatin1("numpad0");
834     case VK_NUMPAD1: return JvNewStringLatin1("numpad1");
835     case VK_NUMPAD2: return JvNewStringLatin1("numpad2");
836     case VK_NUMPAD3: return JvNewStringLatin1("numpad3");
837     case VK_NUMPAD4: return JvNewStringLatin1("numpad4");
838     case VK_NUMPAD5: return JvNewStringLatin1("numpad5");
839     case VK_NUMPAD6: return JvNewStringLatin1("numpad6");
840     case VK_NUMPAD7: return JvNewStringLatin1("numpad7");
841     case VK_NUMPAD8: return JvNewStringLatin1("numpad8");
842     case VK_NUMPAD9: return JvNewStringLatin1("numpad9");
843     case VK_F1: return JvNewStringLatin1("f1");
844     case VK_F2: return JvNewStringLatin1("f2");
845     case VK_F3: return JvNewStringLatin1("f3");
846     case VK_F4: return JvNewStringLatin1("f4");
847     case VK_F5: return JvNewStringLatin1("f5");
848     case VK_F6: return JvNewStringLatin1("f6");
849     case VK_F7: return JvNewStringLatin1("f7");
850     case VK_F8: return JvNewStringLatin1("f8");
851     case VK_F9: return JvNewStringLatin1("f9");
852     case VK_F10: return JvNewStringLatin1("f10");
853     case VK_F11: return JvNewStringLatin1("f11");
854     case VK_F12: return JvNewStringLatin1("f12");
855     case VK_NUMLOCK: return JvNewStringLatin1("num_lock");
856     case VK_SCROLL: return JvNewStringLatin1("scroll_lock");
857     case VK_LSHIFT: return JvNewStringLatin1("shift");
858     case VK_RSHIFT: return JvNewStringLatin1("shift");
859     case VK_LCONTROL: return JvNewStringLatin1("control");
860     case VK_RCONTROL: return JvNewStringLatin1("control");
861     }
862     return NULL;
863 }
864
865
866
867
868 ///////////////////////////////////////////////////////////////////////////////////////
869 ///////////////////////////////////////////////////////////////////////////////////////
870 ///////////////////////////////////////////////////////////////////////////////////////
871
872 #else /* COMPILE_DLL */
873
874
875 //
876 // A simple DLL to invoke xwt.exe and pass it any arguments found in the <object/> tag
877 //
878
879 #include <windows.h>
880 #include <initguid.h>
881 #include <objbase.h>
882 #include <oaidl.h>
883 #include <oleauto.h>
884 #include <olectl.h>
885
886
887 // Globals ////////////////////////////////////////////////////////////////////////
888
889 using namespace std;
890
891 #define CLSID_STRING_SIZE 39
892
893 const char XWT_friendlyName[] = "XWT ActiveX Control (build " BUILDID ")";
894 const char XWT_versionIndependantProgramID[] = "XWT.ActiveX";
895 const char XWT_programID[] = "XWT.ActiveX (build " BUILDID ")";
896 extern "C" const CLSID XWT_clsid = CLSID_STRUCT;
897 static HMODULE g_hModule = NULL;    //DLL handle
898
899
900
901
902 // Superclasses ////////////////////////////////////////////////////////////////////////
903
904 // Option bit definitions for IObjectSafety:
905 #define INTERFACESAFE_FOR_UNTRUSTED_CALLER  0x00000001  // Caller of interface may be untrusted
906 #define INTERFACESAFE_FOR_UNTRUSTED_DATA    0x00000002  // Data passed into interface may be untrusted
907
908 // {CB5BDC81-93C1-11cf-8F20-00805F2CD064}
909 DEFINE_GUID(IID_IObjectSafety, 0xcb5bdc81, 0x93c1, 0x11cf, 0x8f, 0x20, 0x0, 0x80, 0x5f, 0x2c, 0xd0, 0x64);
910
911 interface IObjectSafety : public IUnknown {
912  public:
913     virtual HRESULT __stdcall GetInterfaceSafetyOptions(REFIID riid, DWORD __RPC_FAR *pdwSupportedOptions, DWORD __RPC_FAR *pdwEnabledOptions) = 0;
914     virtual HRESULT __stdcall SetInterfaceSafetyOptions(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions) = 0;
915 };
916
917 interface IShoeHorn : IPersistPropertyBag, IObjectSafety { };
918
919
920
921 // Entry Points ////////////////////////////////////////////////////////////////////////
922
923 // to get mingw to stop nagging me
924 int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { }
925
926 // determines whether or not the DLL can be unloaded; always allow this since we don't do too much
927 STDAPI __declspec(dllexport) DllCanUnloadNow(void) { return S_OK; }
928
929 extern "C" __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID /*lpReserved*/) {
930     if (dwReason == DLL_PROCESS_ATTACH) g_hModule = (HINSTANCE)hModule;
931     return TRUE;
932 }
933
934
935
936 // Other ///////////////////////////////////////////////////////////////////////////////////
937
938 // simple assert() replacement that pops open a message box if there are errors
939 void check(int val, char* message) {
940     if (!val) {
941         MessageBox (NULL, message, "XWT Critical Abort", MB_OK | MB_ICONSTOP | MB_TASKMODAL | MB_SETFOREGROUND);
942         exit(-1);
943     }
944 }
945
946 void clsidToString(const CLSID& clsid, char* str, int length) {
947     check(length >= CLSID_STRING_SIZE, "clsidToString(): string too short");
948         LPOLESTR wide_str = NULL;
949         HRESULT hr = StringFromCLSID(clsid, &wide_str);
950         check(SUCCEEDED(hr), "StringFromCLSID() failed in clsidToString()");
951         wcstombs(str, wide_str, length);
952         CoTaskMemFree(wide_str);
953 }
954
955 void setRegistryKey(const char* key, const char* subkey, const char* value) {
956         HKEY hKey;
957         char keyBuf[1024];
958         strcpy(keyBuf, key);
959         if (subkey != NULL) {
960                 strcat(keyBuf, "\\");
961                 strcat(keyBuf, subkey );
962         }
963         long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, keyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL);
964         if (value != NULL)
965         check(RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)value, strlen(value) + 1) == ERROR_SUCCESS,
966               "RegSetValueEx() failed in setRegistryKey()");
967         RegCloseKey(hKey);
968 }
969
970 void deleteRegistryKey(HKEY parent, const char* target) {
971         HKEY hKeyChild;
972         RegOpenKeyEx(parent, target, 0, KEY_ALL_ACCESS, &hKeyChild);
973
974         // Iterate over children, deleting them
975         FILETIME time;
976         char szBuffer[256];
977         DWORD dwSize = 256;
978         while (RegEnumKeyEx(hKeyChild, 0, szBuffer, &dwSize, NULL, NULL, NULL, &time) == S_OK) {
979                 deleteRegistryKey(hKeyChild, szBuffer);
980                 dwSize = 256;
981         }
982
983         RegCloseKey(hKeyChild);
984         RegDeleteKey(parent, target);
985 }
986
987 STDAPI __declspec(dllexport) DllRegisterServer(void) {
988         char moduleName[512];
989     HRESULT result = GetModuleFileName(g_hModule, moduleName, sizeof(moduleName)/sizeof(char));
990         check(result, "GetModuleFileName() failed in RegisterServer()");
991
992         char clsidString[CLSID_STRING_SIZE];
993         clsidToString(XWT_clsid, clsidString, sizeof(clsidString));
994
995         // build the key
996         char key[64];
997         strcpy(key, "CLSID\\");
998         strcat(key, clsidString);
999   
1000         setRegistryKey(key, NULL, XWT_friendlyName);
1001         setRegistryKey(key, "InprocServer32", moduleName);
1002         setRegistryKey(key, "ProgID", XWT_programID);
1003         setRegistryKey(key, "VersionIndependentProgID", XWT_versionIndependantProgramID);
1004         setRegistryKey(XWT_versionIndependantProgramID, NULL, XWT_friendlyName); 
1005         setRegistryKey(XWT_versionIndependantProgramID, "CLSID", clsidString);
1006         setRegistryKey(XWT_versionIndependantProgramID, "CurVer", XWT_programID);
1007         setRegistryKey(XWT_programID, NULL, XWT_friendlyName); 
1008         setRegistryKey(XWT_programID, "CLSID", clsidString);
1009         return S_OK;
1010 }
1011
1012 STDAPI __declspec(dllexport) DllUnregisterServer(void) {
1013         char clsidString[CLSID_STRING_SIZE];
1014         clsidToString(XWT_clsid, clsidString, sizeof(clsidString));
1015
1016         // build the key
1017         char key[64];
1018         strcpy(key, "CLSID\\");
1019         strcat(key, clsidString);
1020
1021         deleteRegistryKey(HKEY_CLASSES_ROOT, key);
1022         deleteRegistryKey(HKEY_CLASSES_ROOT, XWT_versionIndependantProgramID);
1023         deleteRegistryKey(HKEY_CLASSES_ROOT, XWT_programID);
1024         return S_OK;
1025 }
1026
1027
1028
1029 // ShoeHorn //////////////////////////////////////////////////////////////////////////////////
1030
1031 class ShoeHorn : public IShoeHorn {
1032     public:
1033     virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) {
1034         if(iid == IID_IUnknown) *ppv = static_cast<IShoeHorn*>(this);
1035         else if(iid == XWT_clsid) *ppv = static_cast<IShoeHorn*>(this);
1036         else if(iid == IID_IPersistPropertyBag) *ppv = static_cast<IPersistPropertyBag*>(this);
1037         else if(iid == IID_IObjectSafety) *ppv = static_cast<IObjectSafety*>(this);
1038         else { *ppv = NULL; return E_NOINTERFACE; }
1039         reinterpret_cast<IUnknown*>(*ppv)->AddRef();
1040         return S_OK;
1041     }
1042     virtual ULONG __stdcall AddRef() { return InterlockedIncrement(&m_cRef); }
1043     virtual ULONG __stdcall Release() {
1044         if(InterlockedDecrement(&m_cRef) == 0) { delete this; return 0; }
1045         return m_cRef;
1046     }
1047     virtual HRESULT __stdcall GetClassID(CLSID*) { return S_OK; }
1048     virtual HRESULT __stdcall InitNew() { return S_OK; }
1049     virtual HRESULT __stdcall Save(IPropertyBag*, int, int) { return S_OK; }
1050     virtual HRESULT __stdcall SetInterfaceSafetyOptions(REFIID riid, DWORD pdwSupportedOptions, DWORD pdwEnabledOptions) { return S_OK; }
1051     virtual HRESULT __stdcall GetInterfaceSafetyOptions(REFIID riid, DWORD* pdwSupportedOptions, DWORD* pdwEnabledOptions) {
1052         if (pdwSupportedOptions != NULL) *pdwSupportedOptions |= INTERFACESAFE_FOR_UNTRUSTED_DATA;
1053         if (pdwEnabledOptions != NULL) *pdwSupportedOptions |= INTERFACESAFE_FOR_UNTRUSTED_DATA;
1054         return S_OK;
1055     }
1056
1057     virtual HRESULT __stdcall Load(IPropertyBag* pPropBag, IErrorLog* pErrorLog) {
1058         VARIANT v;
1059         v.vt = VT_BSTR;
1060         HRESULT hrRead;
1061         
1062         WCHAR wc[100];
1063         char url[100];
1064         
1065         MultiByteToWideChar(CP_ACP, 0, "initial-xwar-url", -1, wc, 100);
1066         pPropBag->Read(wc, &v, pErrorLog);
1067         check(WideCharToMultiByte(CP_ACP, 0, v.bstrVal, -1, url, 100, NULL, NULL),
1068               "WideCharToMultiByte() failed in ShoeHorn::Load()");
1069         
1070         HKEY hkey;
1071         LONG result = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\ActiveX Cache", &hkey);
1072         check(result == ERROR_SUCCESS, "RegOpenKey() failed in ShoeHorn::Load()");
1073         
1074         // iterate over all the activex cache locations until we find ourselves
1075         for(int i=0; i<9; i++) {
1076             DWORD buflen = 999;
1077             char buf[1000];
1078             VALENT valents[20];
1079             DWORD type;
1080             char which[2];
1081
1082             which[0] = '0' + i;
1083             which[1] = '\0';
1084             result = RegQueryValueEx(hkey, which, NULL, &type, (BYTE*)buf, &buflen);
1085             if (result != ERROR_SUCCESS)
1086                 if (i == 0) {
1087                     check(0, "RegQueryValueEx() failed in ShoeHorn::Load()");
1088                 } else {
1089                     break;
1090                 }
1091             buf[buflen] = '\0';
1092             
1093             char cmdline[200];
1094             for(int i=0; i<200; i++) cmdline[i] = '\0';
1095             strncpy(cmdline, buf, 200);
1096             strncpy(cmdline + strlen(cmdline), "\\xwt-" BUILDID ".exe", 200 - strlen(cmdline));
1097             strncpy(cmdline + strlen(cmdline), " ", 200 - strlen(cmdline));
1098             strncpy(cmdline + strlen(cmdline), url, 200 - strlen(cmdline));
1099             
1100             PROCESS_INFORMATION pInfo;
1101             STARTUPINFO sInfo;
1102             sInfo.cb = sizeof(STARTUPINFO);
1103             sInfo.lpReserved = NULL;
1104             sInfo.lpReserved2 = NULL;
1105             sInfo.cbReserved2 = 0;
1106             sInfo.lpDesktop = NULL;
1107             sInfo.lpTitle = NULL;
1108             sInfo.dwFlags = 0;
1109             sInfo.dwX = 0;
1110             sInfo.dwY = 0;
1111             sInfo.dwFillAttribute = 0;
1112             sInfo.wShowWindow = SW_SHOW;
1113             BOOL b = CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &sInfo, &pInfo);
1114             if (b) return S_OK;
1115         }
1116
1117         check(0, "unable to locate xwt-" BUILDID ".exe in ActiveX cache folders");
1118     }
1119
1120     ShoeHorn() : m_cRef(1) { };
1121     ~ShoeHorn() { };
1122     private: long m_cRef;
1123 };
1124
1125
1126
1127
1128 // ClassFactory //////////////////////////////////////////////////////////////////////////
1129
1130 class ClassFactory : public IClassFactory {
1131     public:
1132     virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) {
1133         if(iid == IID_IUnknown) *ppv = static_cast<IClassFactory*>(this);
1134         else if(iid == IID_IClassFactory) *ppv = static_cast<IClassFactory*>(this);
1135         else {
1136             *ppv = NULL;
1137             return E_NOINTERFACE;
1138         }
1139         reinterpret_cast<IUnknown*>(*ppv)->AddRef();
1140         return S_OK;
1141     }
1142
1143     ClassFactory() : m_cRef(1) { }
1144     ~ClassFactory() { }
1145     virtual HRESULT __stdcall LockServer(BOOL bLock) { return S_OK; }
1146     virtual ULONG __stdcall AddRef() { return InterlockedIncrement(&m_cRef); }
1147     virtual ULONG __stdcall Release() {
1148         if(InterlockedDecrement(&m_cRef) == 0) {
1149             delete this;
1150             return 0;
1151         }
1152         return m_cRef;
1153     }
1154
1155     virtual HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void** ppv) {
1156         if(pUnknownOuter != NULL) return CLASS_E_NOAGGREGATION;
1157         ShoeHorn* s = new ShoeHorn;
1158         if(s == NULL) return E_OUTOFMEMORY;
1159         HRESULT hr = s->QueryInterface(iid, ppv);
1160         s->Release();
1161         return hr;
1162     }
1163     
1164     private: long m_cRef;
1165 };
1166
1167
1168 extern "C" __stdcall HRESULT DllGetClassObject(const CLSID& clsid, const IID& iid, void** ppv) {
1169     if(clsid != XWT_clsid) return CLASS_E_CLASSNOTAVAILABLE;
1170     ClassFactory* pFactory = new ClassFactory; 
1171     if(pFactory == NULL) return E_OUTOFMEMORY;
1172     HRESULT hr = pFactory->QueryInterface(iid, ppv);
1173     pFactory->Release();
1174     return hr;
1175 }
1176
1177 #endif