2002/04/30 01:47:34
[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         // 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 jboolean org::xwt::plat::Win32::_newBrowserWindow_(jstring url) {
326
327     int len = min(2048, JvGetStringUTFLength(url));
328     char buf[len + 1];
329     JvGetStringUTFRegion(url, 0, len, buf);
330     buf[len] = '\0';
331
332     SHELLEXECUTEINFO ei;
333     memset(&ei, 0, sizeof(ei));
334     ei.cbSize = sizeof(ei);
335     ei.lpVerb = "open";
336     ei.lpFile = buf;
337     ei.fMask  = SEE_MASK_NOCLOSEPROCESS;
338     ei.nShow  = SW_SHOWDEFAULT;
339     return (ShellExecuteEx(&ei) == 0);
340
341 }
342
343
344 // Win32DoubleBuffer /////////////////////////////////////////////////////////////////////////
345
346 // This is a scratch area; when blitting a translucent image, we copy the underlying data here first.
347 // Since all drawing operations are single-threaded, it's safe to use a global here.
348 static HBITMAP scratch = NULL;
349 static HDC scratch_dc = NULL;
350 static jint* scratch_bits = NULL;
351 static jint scratch_w = 0;
352 static jint scratch_h = 0;
353
354 #define BLT(dest, dx1, dy1, dx2, dy2, src, sx1, sy1, sx2, sy2, op)                                   \
355     if ((dx2 - dx1 == sx2 - sx1) && (dy2 - dy1 == sy2 - sy1))                                        \
356         BitBlt(dest, dx1, dy1, dx2 - dx1, dy2 - dy1, src, sx1, sy1, op);                             \
357     else                                                                                             \
358         StretchBlt(dest, dx1, dy1, dx2 - dx1, dy2 - dy1, src, sx1, sy1, sx2 - sx1, sy2 - sy1, op);
359         
360 void org::xwt::plat::Win32$Win32DoubleBuffer::drawPicture(org::xwt::Picture* source0,
361                                                           jint dx1, jint dy1, jint dx2, jint dy2,
362                                                           jint sx1, jint sy1, jint sx2, jint sy2) {
363
364     org::xwt::plat::Win32$Win32Picture* source = (org::xwt::plat::Win32$Win32Picture*)source0;
365
366     if (source->hasalpha) {
367
368         if (scratch == NULL || scratch_w < dx2 - dx1 || scratch_h < dy2 - dy1) {
369             if (scratch_dc != NULL) DeleteDC(scratch_dc);
370             if (scratch != NULL) DeleteObject(scratch);
371             scratch_w = max(dx2 - dx1, scratch_w);
372             scratch_h = max(dy2 - dy1, scratch_h);
373
374             BITMAPINFO bitmapinfo;
375             memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
376             bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
377             bitmapinfo.bmiHeader.biWidth = scratch_w;
378             bitmapinfo.bmiHeader.biHeight = -1 * scratch_h;
379             bitmapinfo.bmiHeader.biPlanes = 1;
380             bitmapinfo.bmiHeader.biBitCount = 32;
381             bitmapinfo.bmiHeader.biCompression = BI_RGB;
382
383             // create section DIB
384             scratch = CreateDIBSection(NULL, &bitmapinfo, DIB_RGB_COLORS, (void**)&scratch_bits, NULL, 0);
385             scratch_dc = CreateCompatibleDC((HDC)org::xwt::plat::Win32::desktop_dc);
386             SelectObject(scratch_dc, scratch);
387         }
388
389         // copy from screen to scratch
390         BitBlt((HDC)scratch_dc, 0, 0, dx2 - dx1, dy2 - dy1, (HDC)hdc, dx1, dy1, SRCCOPY);
391
392         // apply alpha-blending to scratch
393         jint* dat = elements(source->data);
394
395         for(int x = max(clipx1, dx1) - dx1; x < min(clipx2, dx2) - dx1; x++)
396             for(int y = max(clipy1, dy1) - dy1; y < min(clipy2, dy2) - dy1; y++) {
397                 int sx = (x * (sx2 - sx1)) / (dx2 - dx1) + sx1;
398                 int sy = (y * (sy2 - sy1)) / (dy2 - dy1) + sy1;
399                 jint dst = scratch_bits[y * scratch_w + x];
400                 jint src = dat[sy * source->getWidth() + sx];
401                 jint alpha = (src & 0xFF000000) >> 24;
402                 jint r = (((src & 0x00FF0000) >> 16) * alpha + ((dst & 0x00FF0000) >> 16) * (0xFF - alpha)) / 0xFF;
403                 jint g = (((src & 0x0000FF00) >> 8)  * alpha + ((dst & 0x0000FF00) >> 8)  * (0xFF - alpha)) / 0xFF;
404                 jint b = (((src & 0x000000FF))       * alpha + ((dst & 0x000000FF))       * (0xFF - alpha)) / 0xFF;
405                 scratch_bits[y * scratch_w + x] = (r << 16) | (g << 8) | b;
406             }
407
408         // copy back from scratch to screen
409         BitBlt((HDC)hdc, dx1, dy1, dx2 - dx1, dy2 - dy1, (HDC)scratch_dc, 0, 0, SRCCOPY);
410
411     } else {
412         if (source->hasmask) {
413             BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->maskdc, sx1, sy1, sx2, sy2, SRCAND);
414             BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->hdc, sx1, sy1, sx2, sy2, SRCPAINT);
415         } else {
416             BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->hdc, sx1, sy1, sx2, sy2, SRCCOPY);
417         }
418
419     }
420
421 }
422
423 void org::xwt::plat::Win32$Win32DoubleBuffer::drawString(jstring font, jstring text, jint x, jint y, jint color) {
424
425     org::xwt::plat::Win32$Win32Font* wf = org::xwt::plat::Win32::getFont(font);
426     SelectObject((HDC)hdc, (HFONT)(wf->hfont));
427
428     // Platform API passes us the y-pos of the bottom of the text; we need the top
429     y -= wf->maxAscent;
430
431     int len = min(1024, JvGetStringUTFLength(text));
432     char buf[len + 1];
433     buf[len] = '\0';
434     JvGetStringUTFRegion(text, 0, len, buf);
435
436     SetTextColor((HDC)hdc, PALETTERGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
437     TextOut((HDC)hdc, x, y, buf, len);
438 }
439
440 void org::xwt::plat::Win32$Win32DoubleBuffer::fillRect(jint x, jint y, jint x2, jint y2, jint color) {
441     jint w = x2 - x;
442     jint h = y2 - y;
443
444     // sadly, the ability to change the color of a brush didn't arrive until Win2k...
445     HBRUSH brush = CreateSolidBrush(PALETTERGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
446     if (!brush) return;
447     RECT rect = { x, y, x + w, y + h };
448     FillRect((HDC)hdc, &rect, brush);
449     DeleteObject(brush);
450 }
451
452 void org::xwt::plat::Win32$Win32Surface::blit(org::xwt::DoubleBuffer* s, jint sx, jint sy, jint dx, jint dy, jint dx2, jint dy2) {
453     BitBlt((HDC)hdc, dx, dy, dx2 - dx, dy2 - dy, (HDC)(((org::xwt::plat::Win32$Win32DoubleBuffer*)s)->hdc), sx, sy, SRCCOPY);
454 }
455
456 void org::xwt::plat::Win32$Win32DoubleBuffer::natInit() {
457     hbitmap = (jint)CreateCompatibleBitmap((HDC)org::xwt::plat::Win32::desktop_dc, w, h);
458     hdc = (jint)CreateCompatibleDC((HDC)org::xwt::plat::Win32::desktop_dc);
459     SetBkMode((HDC)hdc, TRANSPARENT);
460     SelectObject((HDC)hdc, (HBITMAP)hbitmap);
461 }
462
463 void org::xwt::plat::Win32$Win32DoubleBuffer::setClip(jint x, jint y, jint x2, jint y2) {
464     clipx1 = x; clipx2 = x2; clipy1 = y; clipy2 = y2;
465     HRGN hrgn = CreateRectRgn(x, y, x2, y2);
466     SelectClipRgn((HDC)hdc, hrgn);
467     DeleteObject(hrgn);
468 }
469
470 void org::xwt::plat::Win32$Win32DoubleBuffer::finalize() {
471     DeleteObject((void*)hdc);
472     DeleteObject((void*)hbitmap);
473 }
474
475
476
477 // Win32Picture /////////////////////////////////////////////////////////////////////////
478
479 void org::xwt::plat::Win32$Win32Picture::natInit() {
480
481     BITMAPINFO bitmapinfo;
482     memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
483     bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
484     bitmapinfo.bmiHeader.biWidth = w;
485     bitmapinfo.bmiHeader.biHeight = -1 * h;
486     bitmapinfo.bmiHeader.biPlanes = 1;
487     bitmapinfo.bmiHeader.biBitCount = 32;
488     bitmapinfo.bmiHeader.biCompression = BI_RGB;
489
490     hbitmap = (jint)CreateCompatibleBitmap((HDC)org::xwt::plat::Win32::desktop_dc, w, h);
491     hdc = (jint)CreateCompatibleDC((HDC)org::xwt::plat::Win32::desktop_dc);
492     SelectObject((HDC)hdc, (HBITMAP)hbitmap);
493     uint32_t* dat = (uint32_t*)elements(data);
494     for(int i=0; i<data->length; i++) if ((dat[i] & 0xFF000000) == 0x00000000) dat[i] = 0x00000000;
495     StretchDIBits((HDC)hdc, 0, 0, w, h, 0, 0, w, h, elements(data), &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
496
497     jint _copy[min(1024, data->length)];
498     jint* copy = data->length > 1024 ? (jint*)malloc(data->length * 4) : _copy;
499
500     memcpy(copy, elements(data), data->length * 4);
501     for(int i=0; i<data->length; i++)
502         if ((copy[i] & 0xFF000000) == 0x00000000) {
503             hasmask = 1;
504             copy[i] = 0x00FFFFFF;
505         } else if ((copy[i] & 0xFF000000) == 0xFF000000) {
506             copy[i] = 0x00000000;
507         } else {
508             hasalpha = 1;
509             hasmask = 0;
510             if (data->length > 1024) free(copy);
511             return;
512         }
513
514     if (!hasmask) {
515         if (data->length > 1024) free(copy);
516         return;
517     }
518
519     //    hmask = (jint)CreateBitmap(w, h, 1, 1, NULL);
520     hmask = (jint)CreateCompatibleBitmap((HDC)org::xwt::plat::Win32::desktop_dc, w, h);
521     maskdc = (jint)CreateCompatibleDC(NULL);
522     SelectObject((HDC)maskdc, (HBITMAP)hmask);
523     StretchDIBits((HDC)maskdc, 0, 0, w, h, 0, 0, w, h, copy, &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
524     if (data->length > 1024) free(copy);
525 }
526
527
528
529 // Win32Surface /////////////////////////////////////////////////////////////////////////////
530
531 void org::xwt::plat::Win32$Win32Surface::natInit(jboolean framed) {
532
533     // Ask the message-handling thread to create a window for us
534     PostThreadMessage((DWORD)org::xwt::plat::Win32::messagePumpThread, WM_USER + 2, (WPARAM)framed, (LPARAM)this);
535     hwndCreated->block();
536
537     // turn on incremental GC now that we have a user-visible interface
538     // [[this is causing segfaults; enable it later...]]
539     // GC_enable_incremental();
540
541     ShowWindow ((HWND)hwnd, SW_SHOWDEFAULT);
542     hdc = (jint)GetDC((HWND)hwnd);
543 }
544
545 void org::xwt::plat::Win32$Win32Surface::finalize() { /* DeleteObject((void*)hwnd); */ }
546 void org::xwt::plat::Win32$Win32Surface::toFront() { BringWindowToTop((HWND)hwnd); }
547 void org::xwt::plat::Win32$Win32Surface::toBack() { SetWindowPos((HWND)hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); }
548 void org::xwt::plat::Win32$Win32Surface::_dispose() { PostMessage((HWND)hwnd, WM_USER_DISPOSE, 0, 0); }
549 void org::xwt::plat::Win32$Win32Surface::setInvisible(jboolean h) { ShowWindow((HWND)hwnd, h ? SW_HIDE : SW_SHOWNORMAL); }
550 void org::xwt::plat::Win32$Win32Surface::_setMinimized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMINIMIZED : SW_SHOWNORMAL); }
551 void org::xwt::plat::Win32$Win32Surface::_setMaximized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); }
552 void org::xwt::plat::Win32$Win32Surface::postCursorChange() { PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0); }
553
554 void org::xwt::plat::Win32$Win32Surface::setLocation(jint x, jint y) {
555     POINT point;
556     RECT rect;
557     point.x = 0;
558     point.y = 0;
559     ClientToScreen((HWND)hwnd, &point);
560     GetWindowRect((HWND)hwnd, &rect);
561     SetWindowPos((HWND)hwnd, NULL, x - (point.x - rect.left), y - (point.y - rect.top), 0, 0, SWP_NOZORDER | SWP_NOSIZE);
562 }
563
564 void org::xwt::plat::Win32$Win32Surface::setSize(jint w, jint h) {
565     RECT client_rect, window_rect;
566     GetClientRect((HWND)hwnd, &client_rect);
567     GetWindowRect((HWND)hwnd, &window_rect);
568     int addwidth = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left);
569     int addheight = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top);
570     SetWindowPos((HWND)hwnd, NULL, 0, 0, w + addwidth, h + addheight, SWP_NOZORDER | SWP_NOMOVE);
571 }
572
573 void org::xwt::plat::Win32$Win32Surface::setTitleBarText(java::lang::String* title) {
574     int len = min(1024, JvGetStringUTFLength(title));
575     char buf[len + 1];
576     buf[len] = '\0';
577     JvGetStringUTFRegion(title, 0, len, buf);
578     SetWindowText((HWND)hwnd, buf);
579 }
580
581 void org::xwt::plat::Win32$Win32Surface::setIcon(org::xwt::Picture* p0) {
582
583     org::xwt::plat::Win32$Win32Picture* p = (org::xwt::plat::Win32$Win32Picture*)p0;
584     int icon_width = GetSystemMetrics(SM_CXSMICON);
585     int icon_height = GetSystemMetrics(SM_CYSMICON);
586
587     // create the bitmap
588     HBITMAP bit = CreateCompatibleBitmap((HDC)hdc, icon_width, icon_height);
589     HDC memdc = CreateCompatibleDC((HDC)hdc);
590     SelectObject(memdc, bit);
591     BLT((HDC)memdc, 0, 0, icon_width, icon_height, (HDC)(p->hdc), 0, 0, p->getWidth(), p->getHeight(), SRCCOPY);
592
593     // create the mask
594     jint* dat = elements(p->data);
595     HBITMAP bit_mask = CreateBitmap(icon_width, icon_height * 2, 1, 1, NULL);
596     SelectObject(memdc, bit_mask);
597     for(int x=0; x<icon_width; x++)
598         for(int y=0; y<icon_height; y++) {
599             int source = dat[(x * p->getWidth()) / icon_width + ((y * p->getHeight()) / icon_height) * (p->getWidth())];
600             if ((source & 0xFF000000) != 0x00000000) SetPixel(memdc, x, y, PALETTERGB(0, 0, 0));
601             else SetPixel(memdc, x, y, PALETTERGB(255, 255, 255));
602         }
603
604     // instantiate the icon and assign it to the window class
605     ICONINFO ici;
606     ici.fIcon = 1;
607     ici.hbmMask = bit_mask;
608     ici.hbmColor = bit;
609     HICON hicon = CreateIconIndirect(&ici);
610     HICON oldicon = (HICON)SetClassLong((HWND)hwnd, GCL_HICONSM, (LONG)hicon);
611     if (oldicon != NULL) DestroyIcon(oldicon);
612     DeleteObject(memdc);
613 }
614
615 static jstring keyToString(WPARAM wParam);
616 jint org::xwt::plat::Win32$Win32Surface::WndProc(jint _hwnd, jint _iMsg, jint _wParam, jint _lParam) {
617
618     UINT iMsg = (UINT)_iMsg;
619     WPARAM wParam = (WPARAM)_wParam;
620     LPARAM lParam = (LPARAM)_lParam;
621     int oldmousex, oldmousey;
622     MINMAXINFO* mmi;
623     POINT point;
624     HWND hwnd2;
625     RECT rect, rect2;
626     jboolean newinside;
627     int16_t mouse_x;
628     int16_t mouse_y;
629
630     switch(iMsg) {
631     case WM_DEVMODECHANGE: break;    // FEATURE: color depth changed
632     case WM_DISPLAYCHANGE: break;    // FEATURE: screen size changed
633     case WM_FONTCHANGE: break;       // FEATURE: set of fonts changed
634     case WM_MOUSEWHEEL: break;       // FEATURE: Mouse Wheel
635
636     case WM_SYSKEYDOWN: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
637     case WM_KEYDOWN:
638         KeyPressed(keyToString(wParam));
639         return 0;
640
641     case WM_SYSKEYUP: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
642     case WM_KEYUP:
643         KeyReleased(keyToString(wParam));
644         return 0;
645
646     case WM_SETFOCUS: Focused(true); return 0;
647     case WM_KILLFOCUS: Focused(false); return 0;
648     case WM_LBUTTONDBLCLK: DoubleClick(1); return 0;
649     case WM_RBUTTONDBLCLK: DoubleClick(2); return 0;
650     case WM_MBUTTONDBLCLK: DoubleClick(3); return 0;
651     case WM_LBUTTONDOWN: Press(1); return 0;
652     case WM_RBUTTONDOWN: Press(2); return 0;
653     case WM_MBUTTONDOWN: Press(3); return 0;
654     case WM_CLOSE: Close(); return 0;
655     case WM_ERASEBKGND: return 0;
656
657     case WM_LBUTTONUP:
658     case WM_RBUTTONUP:
659     case WM_MBUTTONUP:
660         Release(iMsg == WM_LBUTTONUP ? 1 : (iMsg == WM_RBUTTONUP ? 2 : 3));
661         if (captured && !inside) {
662             ReleaseCapture();
663             SetCursor((HCURSOR)previous_cursor);
664         }
665         return 0;
666
667     case WM_MOVING:
668         GetClientRect((HWND)hwnd, &rect);
669         PosChange(rect.left, rect.top);
670         return 0;
671
672     case WM_SIZE:
673         if (wParam == SIZE_MINIMIZED) {
674             if (maximized) Maximized(false);
675             Minimized(true);
676         } else if (wParam == SIZE_MAXIMIZED) {
677             if (minimized) Minimized(false); 
678             Maximized(true);
679         } else {
680             if (minimized) Minimized(false); 
681             if (maximized) Maximized(false);
682         }
683         // deliberately fall through to WM_SIZING
684
685     case WM_SIZING:
686         GetClientRect((HWND)hwnd, &rect);
687         SizeChange(rect.right - rect.left, rect.bottom - rect.top);
688         return 0;
689
690     case WM_MOUSEMOVE:
691         if (mousex == lParam & 0xFFFF && mousey == (lParam & 0xFFFF0000) >> 16) return 0;
692
693         GetClientRect((HWND)hwnd, &rect);
694         point.x = mouse_x = lParam & 0xFFFF;
695         point.y = mouse_y = (lParam & 0xFFFF0000) >> 16;
696         ClientToScreen((HWND)hwnd, &point);
697         hwnd2 = WindowFromPoint(point);
698
699         newinside = hwnd2 == (HWND)hwnd && mouse_x > 0 && mouse_y > 0 && mouse_x < (rect.right - rect.left) && mouse_y < (rect.bottom - rect.top) ? 1 : 0;
700
701         Move(mouse_x, mouse_y);
702
703         if (newinside && !inside) {
704             inside = 1;
705             SetCapture((HWND)hwnd);
706             captured = 1;
707             previous_cursor = (jint)GetCursor();
708             PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0);
709
710         } else if (!newinside && inside) {
711             inside = 0;
712             if (!button1 && !button2 && !button3) {
713                 ReleaseCapture();
714                 captured = 0;
715                 SetCursor((HCURSOR)previous_cursor);
716             }
717         }
718
719         return 0;
720
721     case WM_USER_SETCURSOR:
722         // I can't find documentation of this anywhere, but in my experience, an event must be received between an invocation
723         // of SetCapture() and SetCursor(). Furthermore, it seems that after calling SetCursor(), the cursor will not change until a
724         // return from WndProc (ie the completion of the processing of some event).  Hence, we use WM_USER to indicate that the screen
725         // cursor should be re-set to "current_cursor".
726         SetCursor((HCURSOR)current_cursor);
727         return 0;
728
729     case WM_USER_DISPOSE:
730         // used to signal that we should destroy ourselves
731         DestroyWindow((HWND)hwnd);
732         return 0;
733
734     case WM_GETMINMAXINFO:
735         mmi = (MINMAXINFO*)lParam;
736         mmi->ptMinTrackSize.x = root->dmin(0);
737         mmi->ptMinTrackSize.y = root->dmin(1);
738         mmi->ptMaxTrackSize.x = root->dmax(0);
739         mmi->ptMaxTrackSize.y = root->dmax(1);
740         return 0;
741         
742     case WM_PAINT: 
743         PAINTSTRUCT ps;
744         BeginPaint((HWND)org::xwt::plat::Win32$Win32Surface::hwnd, &ps);
745         Dirty(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top);
746         EndPaint((HWND)org::xwt::plat::Win32$Win32Surface::hwnd, &ps);
747         return 0;
748
749     default: break;
750     }  
751
752     return DefWindowProc((HWND)hwnd, iMsg, wParam, lParam);
753 }
754
755
756 // Key Translator Helper /////////////////////////////////////////////////////////////////////
757
758 static char keyarr [256] = { 0 };
759 static jstring keyToString(WPARAM wParam) {
760     char arr[4];
761     keyarr[VK_CAPITAL] = GetKeyState(VK_CAPITAL);
762     keyarr[VK_LSHIFT] = GetKeyState(VK_LSHIFT);
763     keyarr[VK_RSHIFT] = GetKeyState(VK_RSHIFT);
764     keyarr[VK_SHIFT] = GetKeyState(VK_SHIFT);
765     
766     if (ToAscii(wParam, 0, (BYTE*)keyarr, (WORD*)arr, 0) == 1) {
767         switch (arr[0]) {
768         case '\t': return JvNewStringLatin1("tab");
769         case 0x1b: return JvNewStringLatin1("escape");
770         case '\n': return JvNewStringLatin1("enter");
771         case '\r': return JvNewStringLatin1("enter");
772         case 0x08: return JvNewStringLatin1("back_space");
773         default: return JvNewStringLatin1(arr, 1);
774         }
775         
776     } else switch (wParam) {
777     case VK_CLEAR: return JvNewStringLatin1("clear");
778     case VK_SHIFT: return JvNewStringLatin1("shift");
779     case VK_CONTROL: return JvNewStringLatin1("control");
780     case VK_CAPITAL: return JvNewStringLatin1("caps_lock");
781     case VK_MENU: return JvNewStringLatin1("alt");
782     case VK_PAUSE: return JvNewStringLatin1("pause");
783     case VK_PRIOR: return JvNewStringLatin1("page_up");
784     case VK_NEXT: return JvNewStringLatin1("page_down");
785     case VK_END: return JvNewStringLatin1("end");
786     case VK_HOME: return JvNewStringLatin1("home");
787     case VK_LEFT: return JvNewStringLatin1("left");
788     case VK_UP: return JvNewStringLatin1("up");
789     case VK_RIGHT: return JvNewStringLatin1("right");
790     case VK_DOWN: return JvNewStringLatin1("down");
791     case VK_INSERT: return JvNewStringLatin1("insert");
792     case VK_DELETE: return JvNewStringLatin1("delete");
793     case VK_NUMPAD0: return JvNewStringLatin1("numpad0");
794     case VK_NUMPAD1: return JvNewStringLatin1("numpad1");
795     case VK_NUMPAD2: return JvNewStringLatin1("numpad2");
796     case VK_NUMPAD3: return JvNewStringLatin1("numpad3");
797     case VK_NUMPAD4: return JvNewStringLatin1("numpad4");
798     case VK_NUMPAD5: return JvNewStringLatin1("numpad5");
799     case VK_NUMPAD6: return JvNewStringLatin1("numpad6");
800     case VK_NUMPAD7: return JvNewStringLatin1("numpad7");
801     case VK_NUMPAD8: return JvNewStringLatin1("numpad8");
802     case VK_NUMPAD9: return JvNewStringLatin1("numpad9");
803     case VK_F1: return JvNewStringLatin1("f1");
804     case VK_F2: return JvNewStringLatin1("f2");
805     case VK_F3: return JvNewStringLatin1("f3");
806     case VK_F4: return JvNewStringLatin1("f4");
807     case VK_F5: return JvNewStringLatin1("f5");
808     case VK_F6: return JvNewStringLatin1("f6");
809     case VK_F7: return JvNewStringLatin1("f7");
810     case VK_F8: return JvNewStringLatin1("f8");
811     case VK_F9: return JvNewStringLatin1("f9");
812     case VK_F10: return JvNewStringLatin1("f10");
813     case VK_F11: return JvNewStringLatin1("f11");
814     case VK_F12: return JvNewStringLatin1("f12");
815     case VK_NUMLOCK: return JvNewStringLatin1("num_lock");
816     case VK_SCROLL: return JvNewStringLatin1("scroll_lock");
817     case VK_LSHIFT: return JvNewStringLatin1("shift");
818     case VK_RSHIFT: return JvNewStringLatin1("shift");
819     case VK_LCONTROL: return JvNewStringLatin1("control");
820     case VK_RCONTROL: return JvNewStringLatin1("control");
821     }
822     return NULL;
823 }
824
825
826
827
828 ///////////////////////////////////////////////////////////////////////////////////////
829 ///////////////////////////////////////////////////////////////////////////////////////
830 ///////////////////////////////////////////////////////////////////////////////////////
831
832 #else /* COMPILE_DLL */
833
834
835 //
836 // A simple DLL to invoke xwt.exe and pass it any arguments found in the <object/> tag
837 //
838
839 #include <windows.h>
840 #include <initguid.h>
841 #include <objbase.h>
842 #include <oaidl.h>
843 #include <oleauto.h>
844 #include <olectl.h>
845
846
847 // Globals ////////////////////////////////////////////////////////////////////////
848
849 using namespace std;
850
851 #define CLSID_STRING_SIZE 39
852
853 const char XWT_friendlyName[] = "XWT ActiveX Control (build " BUILDID ")";
854 const char XWT_versionIndependantProgramID[] = "XWT.ActiveX";
855 const char XWT_programID[] = "XWT.ActiveX (build " BUILDID ")";
856 extern "C" const CLSID XWT_clsid = CLSID_STRUCT;
857 static HMODULE g_hModule = NULL;    //DLL handle
858
859
860
861
862 // Superclasses ////////////////////////////////////////////////////////////////////////
863
864 // Option bit definitions for IObjectSafety:
865 #define INTERFACESAFE_FOR_UNTRUSTED_CALLER  0x00000001  // Caller of interface may be untrusted
866 #define INTERFACESAFE_FOR_UNTRUSTED_DATA    0x00000002  // Data passed into interface may be untrusted
867
868 // {CB5BDC81-93C1-11cf-8F20-00805F2CD064}
869 DEFINE_GUID(IID_IObjectSafety, 0xcb5bdc81, 0x93c1, 0x11cf, 0x8f, 0x20, 0x0, 0x80, 0x5f, 0x2c, 0xd0, 0x64);
870
871 interface IObjectSafety : public IUnknown {
872  public:
873     virtual HRESULT __stdcall GetInterfaceSafetyOptions(REFIID riid, DWORD __RPC_FAR *pdwSupportedOptions, DWORD __RPC_FAR *pdwEnabledOptions) = 0;
874     virtual HRESULT __stdcall SetInterfaceSafetyOptions(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions) = 0;
875 };
876
877 interface IShoeHorn : IPersistPropertyBag, IObjectSafety { };
878
879
880
881 // Entry Points ////////////////////////////////////////////////////////////////////////
882
883 // to get mingw to stop nagging me
884 int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { }
885
886 // determines whether or not the DLL can be unloaded; always allow this since we don't do too much
887 STDAPI __declspec(dllexport) DllCanUnloadNow(void) { return S_OK; }
888
889 extern "C" __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID /*lpReserved*/) {
890     if (dwReason == DLL_PROCESS_ATTACH) g_hModule = (HINSTANCE)hModule;
891     return TRUE;
892 }
893
894
895
896 // Other ///////////////////////////////////////////////////////////////////////////////////
897
898 // simple assert() replacement that pops open a message box if there are errors
899 void check(int val, char* message) {
900     if (!val) {
901         MessageBox (NULL, message, "XWT Critical Abort", MB_OK | MB_ICONSTOP | MB_TASKMODAL | MB_SETFOREGROUND);
902         exit(-1);
903     }
904 }
905
906 void clsidToString(const CLSID& clsid, char* str, int length) {
907     check(length >= CLSID_STRING_SIZE, "clsidToString(): string too short");
908         LPOLESTR wide_str = NULL;
909         HRESULT hr = StringFromCLSID(clsid, &wide_str);
910         check(SUCCEEDED(hr), "StringFromCLSID() failed in clsidToString()");
911         wcstombs(str, wide_str, length);
912         CoTaskMemFree(wide_str);
913 }
914
915 void setRegistryKey(const char* key, const char* subkey, const char* value) {
916         HKEY hKey;
917         char keyBuf[1024];
918         strcpy(keyBuf, key);
919         if (subkey != NULL) {
920                 strcat(keyBuf, "\\");
921                 strcat(keyBuf, subkey );
922         }
923         long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, keyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL);
924         if (value != NULL)
925         check(RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)value, strlen(value) + 1) == ERROR_SUCCESS,
926               "RegSetValueEx() failed in setRegistryKey()");
927         RegCloseKey(hKey);
928 }
929
930 void deleteRegistryKey(HKEY parent, const char* target) {
931         HKEY hKeyChild;
932         RegOpenKeyEx(parent, target, 0, KEY_ALL_ACCESS, &hKeyChild);
933
934         // Iterate over children, deleting them
935         FILETIME time;
936         char szBuffer[256];
937         DWORD dwSize = 256;
938         while (RegEnumKeyEx(hKeyChild, 0, szBuffer, &dwSize, NULL, NULL, NULL, &time) == S_OK) {
939                 deleteRegistryKey(hKeyChild, szBuffer);
940                 dwSize = 256;
941         }
942
943         RegCloseKey(hKeyChild);
944         RegDeleteKey(parent, target);
945 }
946
947 STDAPI __declspec(dllexport) DllRegisterServer(void) {
948         char moduleName[512];
949     HRESULT result = GetModuleFileName(g_hModule, moduleName, sizeof(moduleName)/sizeof(char));
950         check(result, "GetModuleFileName() failed in RegisterServer()");
951
952         char clsidString[CLSID_STRING_SIZE];
953         clsidToString(XWT_clsid, clsidString, sizeof(clsidString));
954
955         // build the key
956         char key[64];
957         strcpy(key, "CLSID\\");
958         strcat(key, clsidString);
959   
960         setRegistryKey(key, NULL, XWT_friendlyName);
961         setRegistryKey(key, "InprocServer32", moduleName);
962         setRegistryKey(key, "ProgID", XWT_programID);
963         setRegistryKey(key, "VersionIndependentProgID", XWT_versionIndependantProgramID);
964         setRegistryKey(XWT_versionIndependantProgramID, NULL, XWT_friendlyName); 
965         setRegistryKey(XWT_versionIndependantProgramID, "CLSID", clsidString);
966         setRegistryKey(XWT_versionIndependantProgramID, "CurVer", XWT_programID);
967         setRegistryKey(XWT_programID, NULL, XWT_friendlyName); 
968         setRegistryKey(XWT_programID, "CLSID", clsidString);
969         return S_OK;
970 }
971
972 STDAPI __declspec(dllexport) DllUnregisterServer(void) {
973         char clsidString[CLSID_STRING_SIZE];
974         clsidToString(XWT_clsid, clsidString, sizeof(clsidString));
975
976         // build the key
977         char key[64];
978         strcpy(key, "CLSID\\");
979         strcat(key, clsidString);
980
981         deleteRegistryKey(HKEY_CLASSES_ROOT, key);
982         deleteRegistryKey(HKEY_CLASSES_ROOT, XWT_versionIndependantProgramID);
983         deleteRegistryKey(HKEY_CLASSES_ROOT, XWT_programID);
984         return S_OK;
985 }
986
987
988
989 // ShoeHorn //////////////////////////////////////////////////////////////////////////////////
990
991 class ShoeHorn : public IShoeHorn {
992     public:
993     virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) {
994         if(iid == IID_IUnknown) *ppv = static_cast<IShoeHorn*>(this);
995         else if(iid == XWT_clsid) *ppv = static_cast<IShoeHorn*>(this);
996         else if(iid == IID_IPersistPropertyBag) *ppv = static_cast<IPersistPropertyBag*>(this);
997         else if(iid == IID_IObjectSafety) *ppv = static_cast<IObjectSafety*>(this);
998         else { *ppv = NULL; return E_NOINTERFACE; }
999         reinterpret_cast<IUnknown*>(*ppv)->AddRef();
1000         return S_OK;
1001     }
1002     virtual ULONG __stdcall AddRef() { return InterlockedIncrement(&m_cRef); }
1003     virtual ULONG __stdcall Release() {
1004         if(InterlockedDecrement(&m_cRef) == 0) { delete this; return 0; }
1005         return m_cRef;
1006     }
1007     virtual HRESULT __stdcall GetClassID(CLSID*) { return S_OK; }
1008     virtual HRESULT __stdcall InitNew() { return S_OK; }
1009     virtual HRESULT __stdcall Save(IPropertyBag*, int, int) { return S_OK; }
1010     virtual HRESULT __stdcall SetInterfaceSafetyOptions(REFIID riid, DWORD pdwSupportedOptions, DWORD pdwEnabledOptions) { return S_OK; }
1011     virtual HRESULT __stdcall GetInterfaceSafetyOptions(REFIID riid, DWORD* pdwSupportedOptions, DWORD* pdwEnabledOptions) {
1012         if (pdwSupportedOptions != NULL) *pdwSupportedOptions |= INTERFACESAFE_FOR_UNTRUSTED_DATA;
1013         if (pdwEnabledOptions != NULL) *pdwSupportedOptions |= INTERFACESAFE_FOR_UNTRUSTED_DATA;
1014         return S_OK;
1015     }
1016
1017     virtual HRESULT __stdcall Load(IPropertyBag* pPropBag, IErrorLog* pErrorLog) {
1018         VARIANT v;
1019         v.vt = VT_BSTR;
1020         HRESULT hrRead;
1021         
1022         WCHAR wc[100];
1023         char url[100];
1024         
1025         MultiByteToWideChar(CP_ACP, 0, "initial-xwar-url", -1, wc, 100);
1026         pPropBag->Read(wc, &v, pErrorLog);
1027         check(WideCharToMultiByte(CP_ACP, 0, v.bstrVal, -1, url, 100, NULL, NULL),
1028               "WideCharToMultiByte() failed in ShoeHorn::Load()");
1029         
1030         HKEY hkey;
1031         LONG result = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\ActiveX Cache", &hkey);
1032         check(result == ERROR_SUCCESS, "RegOpenKey() failed in ShoeHorn::Load()");
1033         
1034         // iterate over all the activex cache locations until we find ourselves
1035         for(int i=0; i<9; i++) {
1036             DWORD buflen = 999;
1037             char buf[1000];
1038             VALENT valents[20];
1039             DWORD type;
1040             char which[2];
1041
1042             which[0] = '0' + i;
1043             which[1] = '\0';
1044             result = RegQueryValueEx(hkey, which, NULL, &type, (BYTE*)buf, &buflen);
1045             if (result != ERROR_SUCCESS)
1046                 if (i == 0) {
1047                     check(0, "RegQueryValueEx() failed in ShoeHorn::Load()");
1048                 } else {
1049                     break;
1050                 }
1051             buf[buflen] = '\0';
1052             
1053             char cmdline[200];
1054             for(int i=0; i<200; i++) cmdline[i] = '\0';
1055             strncpy(cmdline, buf, 200);
1056             strncpy(cmdline + strlen(cmdline), "\\xwt-" BUILDID ".exe", 200 - strlen(cmdline));
1057             strncpy(cmdline + strlen(cmdline), " ", 200 - strlen(cmdline));
1058             strncpy(cmdline + strlen(cmdline), url, 200 - strlen(cmdline));
1059             
1060             PROCESS_INFORMATION pInfo;
1061             STARTUPINFO sInfo;
1062             sInfo.cb = sizeof(STARTUPINFO);
1063             sInfo.lpReserved = NULL;
1064             sInfo.lpReserved2 = NULL;
1065             sInfo.cbReserved2 = 0;
1066             sInfo.lpDesktop = NULL;
1067             sInfo.lpTitle = NULL;
1068             sInfo.dwFlags = 0;
1069             sInfo.dwX = 0;
1070             sInfo.dwY = 0;
1071             sInfo.dwFillAttribute = 0;
1072             sInfo.wShowWindow = SW_SHOW;
1073             BOOL b = CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &sInfo, &pInfo);
1074             if (b) return S_OK;
1075         }
1076
1077         check(0, "unable to locate xwt-" BUILDID ".exe in ActiveX cache folders");
1078     }
1079
1080     ShoeHorn() : m_cRef(1) { };
1081     ~ShoeHorn() { };
1082     private: long m_cRef;
1083 };
1084
1085
1086
1087
1088 // ClassFactory //////////////////////////////////////////////////////////////////////////
1089
1090 class ClassFactory : public IClassFactory {
1091     public:
1092     virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) {
1093         if(iid == IID_IUnknown) *ppv = static_cast<IClassFactory*>(this);
1094         else if(iid == IID_IClassFactory) *ppv = static_cast<IClassFactory*>(this);
1095         else {
1096             *ppv = NULL;
1097             return E_NOINTERFACE;
1098         }
1099         reinterpret_cast<IUnknown*>(*ppv)->AddRef();
1100         return S_OK;
1101     }
1102
1103     ClassFactory() : m_cRef(1) { }
1104     ~ClassFactory() { }
1105     virtual HRESULT __stdcall LockServer(BOOL bLock) { return S_OK; }
1106     virtual ULONG __stdcall AddRef() { return InterlockedIncrement(&m_cRef); }
1107     virtual ULONG __stdcall Release() {
1108         if(InterlockedDecrement(&m_cRef) == 0) {
1109             delete this;
1110             return 0;
1111         }
1112         return m_cRef;
1113     }
1114
1115     virtual HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void** ppv) {
1116         if(pUnknownOuter != NULL) return CLASS_E_NOAGGREGATION;
1117         ShoeHorn* s = new ShoeHorn;
1118         if(s == NULL) return E_OUTOFMEMORY;
1119         HRESULT hr = s->QueryInterface(iid, ppv);
1120         s->Release();
1121         return hr;
1122     }
1123     
1124     private: long m_cRef;
1125 };
1126
1127
1128 extern "C" __stdcall HRESULT DllGetClassObject(const CLSID& clsid, const IID& iid, void** ppv) {
1129     if(clsid != XWT_clsid) return CLASS_E_CLASSNOTAVAILABLE;
1130     ClassFactory* pFactory = new ClassFactory; 
1131     if(pFactory == NULL) return E_OUTOFMEMORY;
1132     HRESULT hr = pFactory->QueryInterface(iid, ppv);
1133     pFactory->Release();
1134     return hr;
1135 }
1136
1137 #endif