1 // Copyright 2000-2005 the Contributors, as shown in the revision logs.
2 // Licensed under the GNU General Public License version 2 ("the License").
3 // You may not use this file except in compliance with the License.
7 // we have to do this because the jpeg libraries use the symbol 'INT32'
8 #define INT32 WIN32_INT32
10 // this has to precede the others so we don't get collisions on min/max
11 #include <org/ibex/js/JS.h>
12 #include <org/ibex/Box.h>
27 #include <java/lang/Integer.h>
28 #include <java/util/Hashtable.h>
29 #include <org/ibex/Box.h>
30 #include <org/ibex/Surface.h>
31 #include <org/ibex/PixelBuffer.h>
32 #include <org/ibex/Picture.h>
33 #include <org/ibex/Platform.h>
34 #include <org/ibex/plat/Win32.h>
35 #include <org/ibex/plat/Win32$Win32Surface.h>
36 #include <org/ibex/plat/Win32$Win32PixelBuffer.h>
37 #include <org/ibex/plat/Win32$Win32Picture.h>
38 #include <org/ibex/util/Log.h>
39 #include <org/ibex/util/Semaphore.h>
42 #include <java/lang/System.h>
43 #include <java/io/PrintStream.h>
45 #define WM_USER_SETCURSOR WM_USER
46 #define WM_USER_DISPOSE (WM_USER + 1)
47 #define WM_USER_CREATEWINDOW (WM_USER + 2)
48 #define WS_NORMAL (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
50 // FEATURE: there are lots of places where HANDLE's get casted to jint's -- this will break on Win64
51 // a clean way to do this would be to '#define jraw (gnu::gcj::RawData*)'
53 // Callbacks ////////////////////////////////////////////////////////////////////
55 LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
56 org::ibex::plat::Win32$Win32Surface* surface =
57 (org::ibex::plat::Win32$Win32Surface*)org::ibex::plat::Win32::hwndToWin32SurfaceMap->get(new java::lang::Integer((jint)hwnd));
59 if (surface != NULL) {
60 return (LRESULT)surface->WndProc((jint)hwnd, (jint)iMsg, (jint)wParam, (jint)lParam);
63 // this is really lame -- Win32 insists on being able to call your WndProc BEFORE CreateWindow returns...
64 return DefWindowProc(hwnd, iMsg, wParam, lParam);
69 // Initialization ////////////////////////////////////////////////////////////////////
71 static int window_class_counter = 0;
73 jstring org::ibex::plat::Win32::getTempPath() {
75 DWORD ret = GetTempPath(1024, buf);
76 if (ret == 0) criticalAbort(JvNewStringLatin1("GetTempPath() failed"));
77 return JvNewStringLatin1(buf);
80 // XOR mask for the hand cursor
81 static unsigned char hand_cursor_xor[32 * 4] = {
82 0x00, 0x00, 0x00, 0x00,
83 0x00, 0x0C, 0x00, 0x00,
84 0x00, 0x0C, 0x00, 0x00,
85 0x00, 0x0C, 0x00, 0x00,
86 0x00, 0x0C, 0x00, 0x00,
87 0x00, 0x0C, 0x00, 0x00,
88 0x00, 0x0D, 0x80, 0x00,
89 0x00, 0x0D, 0xB0, 0x00,
90 0x00, 0x0D, 0xB4, 0x00,
91 0x00, 0x0D, 0xB6, 0x00,
92 0x00, 0xCF, 0xF6, 0x00,
93 0x00, 0xEF, 0xFE, 0x00,
94 0x00, 0x6F, 0xFE, 0x00,
95 0x00, 0x2F, 0xFE, 0x00,
96 0x00, 0x3F, 0xFE, 0x00,
97 0x00, 0x1F, 0xFE, 0x00,
98 0x00, 0x1F, 0xFC, 0x00,
99 0x00, 0x0F, 0xFC, 0x00,
100 0x00, 0x0F, 0xFC, 0x00,
101 0x00, 0x07, 0xF8, 0x00,
102 0x00, 0x07, 0xF8, 0x00,
103 0x00, 0x00, 0x00, 0x00,
104 0x00, 0x00, 0x00, 0x00,
105 0x00, 0x00, 0x00, 0x00,
106 0x00, 0x00, 0x00, 0x00,
107 0x00, 0x00, 0x00, 0x00,
108 0x00, 0x00, 0x00, 0x00,
109 0x00, 0x00, 0x00, 0x00,
110 0x00, 0x00, 0x00, 0x00,
111 0x00, 0x00, 0x00, 0x00,
112 0x00, 0x00, 0x00, 0x00,
113 0x00, 0x00, 0x00, 0x00
116 // AND mask for the hand cursor
117 static unsigned char hand_cursor_and[32 * 4] = {
118 0xFF, 0xF3, 0xFF, 0xFF,
119 0xFF, 0xE1, 0xFF, 0xFF,
120 0xFF, 0xE1, 0xFF, 0xFF,
121 0xFF, 0xE1, 0xFF, 0xFF,
122 0xFF, 0xE1, 0xFF, 0xFF,
123 0xFF, 0xE0, 0x7F, 0xFF,
124 0xFF, 0xE0, 0x0F, 0xFF,
125 0xFF, 0xE0, 0x03, 0xFF,
126 0xFF, 0xE0, 0x01, 0xFF,
127 0xFF, 0x20, 0x00, 0xFF,
128 0xFE, 0x00, 0x00, 0xFF,
129 0xFE, 0x00, 0x00, 0xFF,
130 0xFF, 0x00, 0x00, 0xFF,
131 0xFF, 0x80, 0x00, 0xFF,
132 0xFF, 0x80, 0x00, 0xFF,
133 0xFF, 0xC0, 0x00, 0xFF,
134 0xFF, 0xC0, 0x01, 0xFF,
135 0xFF, 0xE0, 0x01, 0xFF,
136 0xFF, 0xE0, 0x01, 0xFF,
137 0xFF, 0xF0, 0x03, 0xFF,
138 0xFF, 0xF0, 0x03, 0xFF,
139 0xFF, 0xF0, 0x03, 0xFF,
140 0xFF, 0xFF, 0xFF, 0xFF,
141 0xFF, 0xFF, 0xFF, 0xFF,
142 0xFF, 0xFF, 0xFF, 0xFF,
143 0xFF, 0xFF, 0xFF, 0xFF,
144 0xFF, 0xFF, 0xFF, 0xFF,
145 0xFF, 0xFF, 0xFF, 0xFF,
146 0xFF, 0xFF, 0xFF, 0xFF,
147 0xFF, 0xFF, 0xFF, 0xFF,
148 0xFF, 0xFF, 0xFF, 0xFF,
149 0xFF, 0xFF, 0xFF, 0xFF
152 void org::ibex::plat::Win32::natPreInit() {
153 // Win32 throws stderr in the trash unless you designate your binary as a "console binary"
154 if (org::ibex::util::Log::logstream == java::lang::System::err) {
156 freopen("CONOUT$", "w+t", stderr);
160 void org::ibex::plat::Win32::natInit() {
162 // grab desktop dc/handle
163 desktop_handle = (jint)GetDesktopWindow();
164 desktop_dc = (jint)GetDC((HWND)desktop_handle);
167 org::ibex::plat::Win32::wait_cursor = (jint)LoadCursor(NULL, IDC_WAIT);
168 org::ibex::plat::Win32::default_cursor = (jint)LoadCursor(NULL, IDC_ARROW);
169 org::ibex::plat::Win32::crosshair_cursor = (jint)LoadCursor(NULL, IDC_CROSS);
170 org::ibex::plat::Win32::text_cursor = (jint)LoadCursor(NULL, IDC_IBEAM);
171 org::ibex::plat::Win32::move_cursor = (jint)LoadCursor(NULL, IDC_SIZEALL);
172 org::ibex::plat::Win32::sizenesw_cursor = (jint)LoadCursor(NULL, IDC_SIZENESW);
173 org::ibex::plat::Win32::sizens_cursor = (jint)LoadCursor(NULL, IDC_SIZENS);
174 org::ibex::plat::Win32::sizenwse_cursor = (jint)LoadCursor(NULL, IDC_SIZENWSE);
175 org::ibex::plat::Win32::sizewe_cursor = (jint)LoadCursor(NULL, IDC_SIZEWE);
176 org::ibex::plat::Win32::hand_cursor = (jint)CreateCursor(GetModuleHandle(NULL), 14, 1, 32, 32, hand_cursor_and, hand_cursor_xor);
178 messagePumpThread = (jint)GetCurrentThreadId();
179 messagePumpStarted->release();
182 while(GetMessage(&msg, (HWND)NULL, 0, 0) > 0) {
184 if (msg.message == WM_USER_CREATEWINDOW) {
185 org::ibex::plat::Win32$Win32Surface *surface = (org::ibex::plat::Win32$Win32Surface*)msg.lParam;
187 // we must create a unique window class name for each
188 // window so that minimization icons can be set independantly
190 sprintf(buf, "Ibex_WINDOW_CLASS_%i", window_class_counter++);
193 wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
194 wc.lpfnWndProc = WndProc;
196 wc.cbSize = sizeof(WNDCLASSEX);
198 wc.hInstance = GetModuleHandle(NULL);
199 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
200 wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL),
203 GetSystemMetrics(SM_CXSMICON),
204 GetSystemMetrics(SM_CYSMICON),
206 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
207 wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
208 wc.lpszMenuName = "menu";
209 wc.lpszClassName = buf;
210 RegisterClassEx(&wc);
212 surface->hwnd = (jint)CreateWindow(wc.lpszClassName, TEXT(""),
213 msg.wParam ? WS_NORMAL : WS_POPUP,
215 (HWND__*)NULL, (HMENU__*)NULL,
216 GetModuleHandle(NULL), (LPVOID)NULL);
218 SetFocus((HWND)surface->hwnd);
219 surface->hwndCreated->release();
222 TranslateMessage(&msg);
223 if (msg.message == WM_LBUTTONDOWN || msg.message == WM_RBUTTONDOWN || msg.message == WM_MBUTTONDOWN) SetFocus(msg.hwnd);
224 DispatchMessage(&msg);
228 java::lang::System::exit(-1);
232 // Platform Methods ///////////////////////////////////////////////////////////////////
234 jstring org::ibex::plat::Win32::_getEnv(jstring key) {
235 int len = JvGetStringUTFLength(key);
237 JvGetStringUTFRegion(key, 0, len, buf);
240 DWORD ret = GetEnvironmentVariable(buf, buf2, 1024);
241 if (ret > 0 && ret < 1024) return JvNewStringLatin1(buf2);
245 jstring org::ibex::plat::Win32::_fileDialog(jstring suggestedFileName, jboolean write) {
249 memset(buf, 0, 1024);
250 memset(&ofn, 0, sizeof(OPENFILENAME));
252 if (suggestedFileName != NULL)
253 JvGetStringUTFRegion(suggestedFileName, 0, min(1023, JvGetStringUTFLength(suggestedFileName)), buf);
255 ofn.lStructSize = sizeof(OPENFILENAME);
256 ofn.nMaxCustFilter = 0;
260 if (write) ofn.Flags |= OFN_OVERWRITEPROMPT;
261 ofn.Flags |= OFN_HIDEREADONLY;
263 int ret = write ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn);
264 return ret == 0 ? NULL : JvNewStringLatin1(buf);
267 void org::ibex::plat::Win32::__detectProxy(JArray<jstring>* container) {
273 LONG result = RegOpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", &hkey);
274 if (result != ERROR_SUCCESS) return;
279 result = RegQueryValueEx(hkey, "AutoConfigURL", NULL, &type, (LPBYTE)buf, &buflen);
281 if (result == ERROR_SUCCESS) elements(container)[2] = JvNewStringLatin1(buf);
285 RegQueryValueEx(hkey, "ProxyEnable", NULL, &type, (LPBYTE)buf, &buflen);
286 if (buf[0] != 1) return;
291 RegQueryValueEx(hkey, "ProxyServer", NULL, &type, (LPBYTE)buf, &buflen);
293 elements(container)[0] = JvNewStringLatin1(buf);
297 RegQueryValueEx(hkey, "ProxyOverride", NULL, &type, (LPBYTE)buf, &buflen);
299 elements(container)[1] = JvNewStringLatin1(buf);
302 jstring org::ibex::plat::Win32::_getClipBoard() {
303 OpenClipboard((HWND)desktop_handle);
304 HGLOBAL hmem = GetClipboardData(CF_TEXT);
305 if (hmem == NULL) return NULL;
306 char* buf = (char*)GlobalLock(hmem);
307 if (buf == NULL) return NULL;
308 jstring ret = JvNewStringLatin1(buf);
314 void org::ibex::plat::Win32::_setClipBoard(jstring s) {
315 OpenClipboard((HWND)desktop_handle);
316 HGLOBAL hmem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, JvGetStringUTFLength(s) + 1);
317 if (hmem == NULL) return;
318 char* buf = (char*)GlobalLock(hmem);
319 if (buf == NULL) return;
320 JvGetStringUTFRegion(s, 0, JvGetStringUTFLength(s), buf);
321 buf[JvGetStringUTFLength(s)] = '\0';
323 SetClipboardData(CF_TEXT, hmem);
327 void org::ibex::plat::Win32::_criticalAbort(jstring message) {
328 char buf[JvGetStringUTFLength(message) + 1];
329 buf[JvGetStringUTFLength(message)] = '\0';
330 JvGetStringUTFRegion(message, 0, JvGetStringUTFLength(message), buf);
331 MessageBox (NULL, buf, "Ibex Cannot Continue", MB_OK | MB_ICONSTOP | MB_TASKMODAL | MB_SETFOREGROUND);
332 java::lang::System::exit(-1);
335 jint org::ibex::plat::Win32::_getScreenWidth() {
337 SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
338 return rect.right - rect.left;
341 jint org::ibex::plat::Win32::_getScreenHeight() {
343 SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
344 return rect.bottom - rect.top;
347 jboolean org::ibex::plat::Win32::_newBrowserWindow_(jstring url) {
349 int len = min(2048, JvGetStringUTFLength(url));
351 JvGetStringUTFRegion(url, 0, len, buf);
355 memset(&ei, 0, sizeof(ei));
356 ei.cbSize = sizeof(ei);
359 ei.fMask = SEE_MASK_NOCLOSEPROCESS;
360 ei.nShow = SW_SHOWDEFAULT;
361 return (ShellExecuteEx(&ei) == 0);
366 // Win32PixelBuffer /////////////////////////////////////////////////////////////////////////
368 // This is a scratch area; when blitting a translucent image, we copy the underlying data here first.
369 // Since all drawing operations are single-threaded, it's safe to use a global here.
370 static HBITMAP scratch = NULL;
371 static HDC scratch_dc = NULL;
372 static jint* scratch_bits = NULL;
373 static jint scratch_w = 0;
374 static jint scratch_h = 0;
376 #define max(a,b) ((a)>(b)?(a):(b))
377 #define min(a,b) ((a)<(b)?(a):(b))
379 void org::ibex::plat::Win32$Win32PixelBuffer::drawPicture(org::ibex::graphics::Picture* source0,
381 jint cx1, jint cy1, jint cx2, jint cy2,
382 jint rgb, jboolean alphaOnly) {
383 org::ibex::plat::Win32$Win32Picture* source = (org::ibex::plat::Win32$Win32Picture*)source0;
387 cx2 = min(dx + source->getWidth(), cx2);
388 cy2 = min(dy + source->getHeight(), cy2);
389 if (cx1 >= cx2 || cy1 >= cy2) return;
391 if (source->hasalpha) {
393 if (scratch == NULL || scratch_w < cx2 - cx1 || scratch_h < cy2 - cy1) {
394 if (scratch_dc != NULL) DeleteDC(scratch_dc);
395 if (scratch != NULL) DeleteObject(scratch);
396 scratch_w = max(cx2 - cx1, scratch_w);
397 scratch_h = max(cy2 - cy1, scratch_h);
399 BITMAPINFO bitmapinfo;
400 memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
401 bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
402 bitmapinfo.bmiHeader.biWidth = scratch_w;
403 bitmapinfo.bmiHeader.biHeight = -1 * scratch_h;
404 bitmapinfo.bmiHeader.biPlanes = 1;
405 bitmapinfo.bmiHeader.biBitCount = 32;
406 bitmapinfo.bmiHeader.biCompression = BI_RGB;
408 // create section DIB
409 scratch = CreateDIBSection(NULL, &bitmapinfo, DIB_RGB_COLORS, (void**)&scratch_bits, NULL, 0);
410 scratch_dc = CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
411 SelectObject(scratch_dc, scratch);
414 // copy from screen to scratch
415 BitBlt((HDC)scratch_dc, 0, 0, cx2 - cx1, cy2 - cy1, (HDC)hdc, cx1, cy1, SRCCOPY);
417 // apply alpha-blending to scratch
418 jint* dat = elements(source->data);
420 for(int x = cx1; x < cx2; x++)
421 for(int y = cy1; y < cy2; y++) {
422 jint dst = scratch_bits[(y - dy) * scratch_w + (x - dx)];
424 // FEATURE: see if we can leverage GDI to do something more clever here with alphaOnly
425 jint src = alphaOnly ? rgb : dat[(y - dy) * source->getWidth() + x - dx];
426 jint alpha = (dat[(y - dy) * source->getWidth() + x - dx] & 0xFF000000) >> 24;
427 jint r = (((src & 0x00FF0000) >> 16) * alpha + ((dst & 0x00FF0000) >> 16) * (0xFF - alpha)) / 0xFF;
428 jint g = (((src & 0x0000FF00) >> 8) * alpha + ((dst & 0x0000FF00) >> 8) * (0xFF - alpha)) / 0xFF;
429 jint b = (((src & 0x000000FF)) * alpha + ((dst & 0x000000FF)) * (0xFF - alpha)) / 0xFF;
430 scratch_bits[(y - dy) * scratch_w + (x - dx)] = (r << 16) | (g << 8) | b;
433 // copy back from scratch to screen
434 BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)scratch_dc, 0, 0, SRCCOPY);
438 // FIXME: support alphaOnly case here
439 if (source->hasmask) {
440 BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->maskdc, cx1 - dx, cy1 - dy, SRCAND);
441 BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->hdc, cx1 - dx, cy1 - dy, SRCPAINT);
443 BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->hdc, cx1 - dx, cy1 - dy, SRCCOPY);
450 void org::ibex::plat::Win32$Win32PixelBuffer::fillRect(jint x, jint y, jint x2, jint y2, jint color) {
454 // sadly, the ability to change the color of a brush didn't arrive until Win2k...
455 HBRUSH brush = CreateSolidBrush(PALETTERGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
457 RECT rect = { x, y, x + w, y + h };
458 FillRect((HDC)hdc, &rect, brush);
462 void org::ibex::plat::Win32$Win32Surface::blit(org::ibex::graphics::PixelBuffer* s, jint sx, jint sy, jint dx, jint dy, jint dx2, jint dy2) {
463 // we create the DC lazily to get around some strange race condition in WinXP
464 if (hdc == 0) hdc = (jint)GetDC((HWND)hwnd);
465 BitBlt((HDC)hdc, dx, dy, dx2 - dx, dy2 - dy, (HDC)(((org::ibex::plat::Win32$Win32PixelBuffer*)s)->hdc), sx, sy, SRCCOPY);
468 void org::ibex::plat::Win32$Win32PixelBuffer::natInit() {
469 hbitmap = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, w, h);
470 hdc = (jint)CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
471 SetBkMode((HDC)hdc, TRANSPARENT);
472 SelectObject((HDC)hdc, (HBITMAP)hbitmap);
475 void org::ibex::plat::Win32$Win32PixelBuffer::finalize() {
476 DeleteObject((void*)hdc);
477 DeleteObject((void*)hbitmap);
482 // Win32Picture /////////////////////////////////////////////////////////////////////////
484 void org::ibex::plat::Win32$Win32Picture::natInit() {
486 BITMAPINFO bitmapinfo;
487 memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
488 bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
489 bitmapinfo.bmiHeader.biWidth = width;
490 bitmapinfo.bmiHeader.biHeight = -1 * height;
491 bitmapinfo.bmiHeader.biPlanes = 1;
492 bitmapinfo.bmiHeader.biBitCount = 32;
493 bitmapinfo.bmiHeader.biCompression = BI_RGB;
495 hbitmap = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, width, height);
496 hdc = (jint)CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
497 SelectObject((HDC)hdc, (HBITMAP)hbitmap);
498 uint32_t* dat = (uint32_t*)elements(data);
499 for(int i=0; i<data->length; i++) if ((dat[i] & 0xFF000000) == 0x00000000) dat[i] = 0x00000000;
500 StretchDIBits((HDC)hdc, 0, 0, width, height, 0, 0, width, height, elements(data), &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
502 jint _copy[min(1024, data->length)];
503 jint* copy = data->length > 1024 ? (jint*)malloc(data->length * 4) : _copy;
505 memcpy(copy, elements(data), data->length * 4);
506 for(int i=0; i<data->length; i++)
507 if ((copy[i] & 0xFF000000) == 0x00000000) {
509 copy[i] = 0x00FFFFFF;
510 } else if ((copy[i] & 0xFF000000) == 0xFF000000) {
511 copy[i] = 0x00000000;
515 if (data->length > 1024) free(copy);
520 if (data->length > 1024) free(copy);
524 // hmask = (jint)CreateBitmap(w, h, 1, 1, NULL);
525 hmask = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, width, height);
526 maskdc = (jint)CreateCompatibleDC(NULL);
527 SelectObject((HDC)maskdc, (HBITMAP)hmask);
528 StretchDIBits((HDC)maskdc, 0, 0, width, height, 0, 0, width, height, copy, &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
529 if (data->length > 1024) free(copy);
534 // Win32Surface /////////////////////////////////////////////////////////////////////////////
536 void org::ibex::plat::Win32$Win32Surface::natInit(jboolean framed) {
538 // Ask the message-handling thread to create a window for us
539 PostThreadMessage((DWORD)org::ibex::plat::Win32::messagePumpThread, WM_USER + 2, (WPARAM)framed, (LPARAM)this);
540 hwndCreated->block();
542 // turn on incremental GC now that we have a user-visible interface
543 // [[this is causing segfaults; enable it later...]]
544 // GC_enable_incremental();
546 ShowWindow ((HWND)hwnd, SW_SHOWDEFAULT);
549 void org::ibex::plat::Win32$Win32Surface::finalize() { /* DeleteObject((void*)hwnd); */ }
550 void org::ibex::plat::Win32$Win32Surface::toFront() { BringWindowToTop((HWND)hwnd); }
551 void org::ibex::plat::Win32$Win32Surface::toBack() { SetWindowPos((HWND)hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); }
552 void org::ibex::plat::Win32$Win32Surface::_dispose() { PostMessage((HWND)hwnd, WM_USER_DISPOSE, 0, 0); }
553 void org::ibex::plat::Win32$Win32Surface::setInvisible(jboolean h) { ShowWindow((HWND)hwnd, h ? SW_HIDE : SW_SHOWNORMAL); }
554 void org::ibex::plat::Win32$Win32Surface::_setMinimized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMINIMIZED : SW_SHOWNORMAL); }
555 void org::ibex::plat::Win32$Win32Surface::_setMaximized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); }
556 void org::ibex::plat::Win32$Win32Surface::postCursorChange() { PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0); }
558 void org::ibex::plat::Win32$Win32Surface::setLocation() {
563 ClientToScreen((HWND)hwnd, &point);
564 GetWindowRect((HWND)hwnd, &rect);
565 SetWindowPos((HWND)hwnd, NULL, root->x - (point.x - rect.left), root->y - (point.y - rect.top), 0, 0, SWP_NOZORDER | SWP_NOSIZE);
568 void org::ibex::plat::Win32$Win32Surface::_setSize(jint w, jint h) {
569 RECT client_rect, window_rect;
570 GetClientRect((HWND)hwnd, &client_rect);
571 GetWindowRect((HWND)hwnd, &window_rect);
572 int width = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left) + w;
573 int height = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top) + h;
574 SetWindowPos((HWND)hwnd, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE);
577 void org::ibex::plat::Win32$Win32Surface::setTitleBarText(java::lang::String* title) {
578 int len = min(1024, JvGetStringUTFLength(title));
581 JvGetStringUTFRegion(title, 0, len, buf);
582 SetWindowText((HWND)hwnd, buf);
585 void org::ibex::plat::Win32$Win32Surface::setIcon(org::ibex::graphics::Picture* p0) {
587 org::ibex::plat::Win32$Win32Picture* p = (org::ibex::plat::Win32$Win32Picture*)p0;
588 int icon_width = GetSystemMetrics(SM_CXSMICON);
589 int icon_height = GetSystemMetrics(SM_CYSMICON);
591 // we create the DC lazily to get around some strange race condition in WinXP
592 if (hdc == 0) hdc = (jint)GetDC((HWND)hwnd);
595 HBITMAP bit = CreateCompatibleBitmap((HDC)hdc, icon_width, icon_height);
596 HDC memdc = CreateCompatibleDC((HDC)hdc);
597 SelectObject(memdc, bit);
598 BitBlt((HDC)memdc, 0, 0, icon_width, icon_height, (HDC)(p->hdc), 0, 0, SRCCOPY);
601 jint* dat = elements(p->data);
602 HBITMAP bit_mask = CreateBitmap(icon_width, icon_height * 2, 1, 1, NULL);
603 SelectObject(memdc, bit_mask);
604 for(int x=0; x<icon_width; x++)
605 for(int y=0; y<icon_height; y++) {
606 int source = dat[(x * p->getWidth()) / icon_width + ((y * p->getHeight()) / icon_height) * (p->getWidth())];
607 if ((source & 0xFF000000) != 0x00000000) SetPixel(memdc, x, y, PALETTERGB(0, 0, 0));
608 else SetPixel(memdc, x, y, PALETTERGB(255, 255, 255));
611 // instantiate the icon and assign it to the window class
614 ici.hbmMask = bit_mask;
616 HICON hicon = CreateIconIndirect(&ici);
617 HICON oldicon = (HICON)SetClassLong((HWND)hwnd, GCL_HICONSM, (LONG)hicon);
618 if (oldicon != NULL) DestroyIcon(oldicon);
622 static jstring keyToString(WPARAM wParam);
623 jint org::ibex::plat::Win32$Win32Surface::WndProc(jint _hwnd, jint _iMsg, jint _wParam, jint _lParam) {
625 UINT iMsg = (UINT)_iMsg;
626 WPARAM wParam = (WPARAM)_wParam;
627 LPARAM lParam = (LPARAM)_lParam;
629 int oldmousex, oldmousey;
635 RECT client_rect, window_rect;
639 int addwidth, addheight;
642 case WM_DEVMODECHANGE: break; // FEATURE: color depth changed
643 case WM_DISPLAYCHANGE: break; // FEATURE: screen size changed
645 VScroll(((jfloat)((wParam & 0xffff0000) >> 16)) / (jfloat)120);
648 case WM_SYSKEYDOWN: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
650 KeyPressed(keyToString(wParam));
653 case WM_SYSKEYUP: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
655 KeyReleased(keyToString(wParam));
658 case WM_SETFOCUS: Focused(true); return 0;
659 case WM_KILLFOCUS: Focused(false); return 0;
660 case WM_LBUTTONDBLCLK: DoubleClick(1); return 0;
661 case WM_RBUTTONDBLCLK: DoubleClick(2); return 0;
662 case WM_MBUTTONDBLCLK: DoubleClick(3); return 0;
663 case WM_LBUTTONDOWN: Press(1); return 0;
664 case WM_RBUTTONDOWN: Press(2); return 0;
665 case WM_MBUTTONDOWN: Press(3); return 0;
666 case WM_CLOSE: Close(); return 0;
667 case WM_ERASEBKGND: return 0;
672 Release(iMsg == WM_LBUTTONUP ? 1 : (iMsg == WM_RBUTTONUP ? 2 : 3));
673 if (captured && !inside) {
675 SetCursor((HCURSOR)previous_cursor);
680 GetClientRect((HWND)hwnd, &rect);
681 PosChange(rect.left, rect.top);
685 if (wParam == SIZE_MINIMIZED) {
686 if (maximized) Maximized(false);
688 } else if (wParam == SIZE_MAXIMIZED) {
689 if (minimized) Minimized(false);
692 if (minimized) Minimized(false);
693 if (maximized) Maximized(false);
695 // deliberately fall through to WM_SIZING
698 GetClientRect((HWND)hwnd, &rect);
699 SizeChange(rect.right - rect.left, rect.bottom - rect.top);
703 if (mousex == lParam & 0xFFFF && mousey == (lParam & 0xFFFF0000) >> 16) return 0;
705 GetClientRect((HWND)hwnd, &rect);
706 point.x = mouse_x = lParam & 0xFFFF;
707 point.y = mouse_y = (lParam & 0xFFFF0000) >> 16;
708 ClientToScreen((HWND)hwnd, &point);
709 hwnd2 = WindowFromPoint(point);
711 newinside = hwnd2 == (HWND)hwnd && mouse_x > 0 && mouse_y > 0 && mouse_x < (rect.right - rect.left) && mouse_y < (rect.bottom - rect.top) ? 1 : 0;
713 Move(mouse_x, mouse_y);
715 if (newinside && !inside) {
717 SetCapture((HWND)hwnd);
719 previous_cursor = (jint)GetCursor();
720 PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0);
722 } else if (!newinside && inside) {
724 if (!button1 && !button2 && !button3) {
727 SetCursor((HCURSOR)previous_cursor);
733 case WM_USER_SETCURSOR:
734 // I can't find documentation of this anywhere, but in my experience, an event must be received between an invocation
735 // of SetCapture() and SetCursor(). Furthermore, it seems that after calling SetCursor(), the cursor will not change until a
736 // return from WndProc (ie the completion of the processing of some event). Hence, we use WM_USER to indicate that the screen
737 // cursor should be re-set to "current_cursor".
738 SetCursor((HCURSOR)current_cursor);
741 case WM_USER_DISPOSE:
742 // used to signal that we should destroy ourselves
743 DestroyWindow((HWND)hwnd);
746 case WM_GETMINMAXINFO:
747 GetClientRect((HWND)hwnd, &client_rect);
748 GetWindowRect((HWND)hwnd, &window_rect);
749 addwidth = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left);
750 addheight = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top);
751 mmi = (MINMAXINFO*)lParam;
752 mmi->ptMinTrackSize.x = ((uint32_t)root->minwidth) + addwidth;
753 mmi->ptMinTrackSize.y = ((uint32_t)root->minheight) + addheight;
754 resizable = !((root->minwidth == root->maxwidth) && (root->minheight == root->maxheight));
755 mmi->ptMaxTrackSize.x = resizable ? org::ibex::plat::Win32::getScreenWidth() : mmi->ptMinTrackSize.x;
756 mmi->ptMaxTrackSize.y = resizable ? org::ibex::plat::Win32::getScreenHeight() : mmi->ptMinTrackSize.y;
761 BeginPaint((HWND)org::ibex::plat::Win32$Win32Surface::hwnd, &ps);
762 Dirty(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top);
763 EndPaint((HWND)org::ibex::plat::Win32$Win32Surface::hwnd, &ps);
769 return DefWindowProc((HWND)hwnd, iMsg, wParam, lParam);
773 // Key Translator Helper /////////////////////////////////////////////////////////////////////
775 static char keyarr [256] = { 0 };
776 static jstring keyToString(WPARAM wParam) {
778 keyarr[VK_CAPITAL] = GetKeyState(VK_CAPITAL);
779 keyarr[VK_LSHIFT] = GetKeyState(VK_LSHIFT);
780 keyarr[VK_RSHIFT] = GetKeyState(VK_RSHIFT);
781 keyarr[VK_SHIFT] = GetKeyState(VK_SHIFT);
783 if (ToAsciiEx(wParam, 0, (BYTE*)keyarr, (WORD*)arr, 0, GetKeyboardLayout(0)) == 1) {
785 case '\t': return JvNewStringLatin1("tab");
786 case 0x1b: return JvNewStringLatin1("escape");
787 case '\n': return JvNewStringLatin1("enter");
788 case '\r': return JvNewStringLatin1("enter");
789 case 0x08: return JvNewStringLatin1("back_space");
790 default: return JvNewStringLatin1(arr, 1);
793 } else switch (wParam) {
794 case VK_CLEAR: return JvNewStringLatin1("clear");
795 case VK_SHIFT: return JvNewStringLatin1("shift");
796 case VK_CONTROL: return JvNewStringLatin1("control");
797 case VK_CAPITAL: return JvNewStringLatin1("caps_lock");
798 case VK_MENU: return JvNewStringLatin1("alt");
799 case VK_PAUSE: return JvNewStringLatin1("pause");
800 case VK_PRIOR: return JvNewStringLatin1("page_up");
801 case VK_NEXT: return JvNewStringLatin1("page_down");
802 case VK_END: return JvNewStringLatin1("end");
803 case VK_HOME: return JvNewStringLatin1("home");
804 case VK_LEFT: return JvNewStringLatin1("left");
805 case VK_UP: return JvNewStringLatin1("up");
806 case VK_RIGHT: return JvNewStringLatin1("right");
807 case VK_DOWN: return JvNewStringLatin1("down");
808 case VK_INSERT: return JvNewStringLatin1("insert");
809 case VK_DELETE: return JvNewStringLatin1("delete");
810 case VK_NUMPAD0: return JvNewStringLatin1("numpad0");
811 case VK_NUMPAD1: return JvNewStringLatin1("numpad1");
812 case VK_NUMPAD2: return JvNewStringLatin1("numpad2");
813 case VK_NUMPAD3: return JvNewStringLatin1("numpad3");
814 case VK_NUMPAD4: return JvNewStringLatin1("numpad4");
815 case VK_NUMPAD5: return JvNewStringLatin1("numpad5");
816 case VK_NUMPAD6: return JvNewStringLatin1("numpad6");
817 case VK_NUMPAD7: return JvNewStringLatin1("numpad7");
818 case VK_NUMPAD8: return JvNewStringLatin1("numpad8");
819 case VK_NUMPAD9: return JvNewStringLatin1("numpad9");
820 case VK_F1: return JvNewStringLatin1("f1");
821 case VK_F2: return JvNewStringLatin1("f2");
822 case VK_F3: return JvNewStringLatin1("f3");
823 case VK_F4: return JvNewStringLatin1("f4");
824 case VK_F5: return JvNewStringLatin1("f5");
825 case VK_F6: return JvNewStringLatin1("f6");
826 case VK_F7: return JvNewStringLatin1("f7");
827 case VK_F8: return JvNewStringLatin1("f8");
828 case VK_F9: return JvNewStringLatin1("f9");
829 case VK_F10: return JvNewStringLatin1("f10");
830 case VK_F11: return JvNewStringLatin1("f11");
831 case VK_F12: return JvNewStringLatin1("f12");
832 case VK_NUMLOCK: return JvNewStringLatin1("num_lock");
833 case VK_SCROLL: return JvNewStringLatin1("scroll_lock");
834 case VK_LSHIFT: return JvNewStringLatin1("shift");
835 case VK_RSHIFT: return JvNewStringLatin1("shift");
836 case VK_LCONTROL: return JvNewStringLatin1("control");
837 case VK_RCONTROL: return JvNewStringLatin1("control");