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