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