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