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