1 // Copyright 2002 Adam Megacz, see the COPYING file for licensing [LGPL]
4 // we have to do this because the jpeg libraries use the symbol 'INT32'
5 #define INT32 WIN32_INT32
7 // this has to precede the others so we don't get collisions on min/max
8 #include <org/ibex/js/JS.h>
9 #include <org/ibex/Box.h>
24 #include <java/lang/Integer.h>
25 #include <java/util/Hashtable.h>
26 #include <org/ibex/Box.h>
27 #include <org/ibex/Surface.h>
28 #include <org/ibex/PixelBuffer.h>
29 #include <org/ibex/Picture.h>
30 #include <org/ibex/Platform.h>
31 #include <org/ibex/plat/Win32.h>
32 #include <org/ibex/plat/Win32$Win32Surface.h>
33 #include <org/ibex/plat/Win32$Win32PixelBuffer.h>
34 #include <org/ibex/plat/Win32$Win32Picture.h>
35 #include <org/ibex/util/Log.h>
36 #include <org/ibex/util/Semaphore.h>
39 #include <java/lang/System.h>
40 #include <java/io/PrintStream.h>
42 #define WM_USER_SETCURSOR WM_USER
43 #define WM_USER_DISPOSE (WM_USER + 1)
44 #define WM_USER_CREATEWINDOW (WM_USER + 2)
45 #define WS_NORMAL (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
47 // FEATURE: there are lots of places where HANDLE's get casted to jint's -- this will break on Win64
48 // a clean way to do this would be to '#define jraw (gnu::gcj::RawData*)'
50 // Callbacks ////////////////////////////////////////////////////////////////////
52 LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
53 org::ibex::plat::Win32$Win32Surface* surface =
54 (org::ibex::plat::Win32$Win32Surface*)org::ibex::plat::Win32::hwndToWin32SurfaceMap->get(new java::lang::Integer((jint)hwnd));
56 if (surface != NULL) {
57 return (LRESULT)surface->WndProc((jint)hwnd, (jint)iMsg, (jint)wParam, (jint)lParam);
60 // this is really lame -- Win32 insists on being able to call your WndProc BEFORE CreateWindow returns...
61 return DefWindowProc(hwnd, iMsg, wParam, lParam);
66 // Initialization ////////////////////////////////////////////////////////////////////
68 static int window_class_counter = 0;
70 jstring org::ibex::plat::Win32::getTempPath() {
72 DWORD ret = GetTempPath(1024, buf);
73 if (ret == 0) criticalAbort(JvNewStringLatin1("GetTempPath() failed"));
74 return JvNewStringLatin1(buf);
77 // XOR mask for the hand cursor
78 static unsigned char hand_cursor_xor[32 * 4] = {
79 0x00, 0x00, 0x00, 0x00,
80 0x00, 0x0C, 0x00, 0x00,
81 0x00, 0x0C, 0x00, 0x00,
82 0x00, 0x0C, 0x00, 0x00,
83 0x00, 0x0C, 0x00, 0x00,
84 0x00, 0x0C, 0x00, 0x00,
85 0x00, 0x0D, 0x80, 0x00,
86 0x00, 0x0D, 0xB0, 0x00,
87 0x00, 0x0D, 0xB4, 0x00,
88 0x00, 0x0D, 0xB6, 0x00,
89 0x00, 0xCF, 0xF6, 0x00,
90 0x00, 0xEF, 0xFE, 0x00,
91 0x00, 0x6F, 0xFE, 0x00,
92 0x00, 0x2F, 0xFE, 0x00,
93 0x00, 0x3F, 0xFE, 0x00,
94 0x00, 0x1F, 0xFE, 0x00,
95 0x00, 0x1F, 0xFC, 0x00,
96 0x00, 0x0F, 0xFC, 0x00,
97 0x00, 0x0F, 0xFC, 0x00,
98 0x00, 0x07, 0xF8, 0x00,
99 0x00, 0x07, 0xF8, 0x00,
100 0x00, 0x00, 0x00, 0x00,
101 0x00, 0x00, 0x00, 0x00,
102 0x00, 0x00, 0x00, 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
113 // AND mask for the hand cursor
114 static unsigned char hand_cursor_and[32 * 4] = {
115 0xFF, 0xF3, 0xFF, 0xFF,
116 0xFF, 0xE1, 0xFF, 0xFF,
117 0xFF, 0xE1, 0xFF, 0xFF,
118 0xFF, 0xE1, 0xFF, 0xFF,
119 0xFF, 0xE1, 0xFF, 0xFF,
120 0xFF, 0xE0, 0x7F, 0xFF,
121 0xFF, 0xE0, 0x0F, 0xFF,
122 0xFF, 0xE0, 0x03, 0xFF,
123 0xFF, 0xE0, 0x01, 0xFF,
124 0xFF, 0x20, 0x00, 0xFF,
125 0xFE, 0x00, 0x00, 0xFF,
126 0xFE, 0x00, 0x00, 0xFF,
127 0xFF, 0x00, 0x00, 0xFF,
128 0xFF, 0x80, 0x00, 0xFF,
129 0xFF, 0x80, 0x00, 0xFF,
130 0xFF, 0xC0, 0x00, 0xFF,
131 0xFF, 0xC0, 0x01, 0xFF,
132 0xFF, 0xE0, 0x01, 0xFF,
133 0xFF, 0xE0, 0x01, 0xFF,
134 0xFF, 0xF0, 0x03, 0xFF,
135 0xFF, 0xF0, 0x03, 0xFF,
136 0xFF, 0xF0, 0x03, 0xFF,
137 0xFF, 0xFF, 0xFF, 0xFF,
138 0xFF, 0xFF, 0xFF, 0xFF,
139 0xFF, 0xFF, 0xFF, 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
149 void org::ibex::plat::Win32::natPreInit() {
150 if (org::ibex::util::Log::verbose) {
152 freopen("CONOUT$", "w+t", stderr);
156 void org::ibex::plat::Win32::natInit() {
158 // grab desktop dc/handle
159 desktop_handle = (jint)GetDesktopWindow();
160 desktop_dc = (jint)GetDC((HWND)desktop_handle);
163 org::ibex::plat::Win32::wait_cursor = (jint)LoadCursor(NULL, IDC_WAIT);
164 org::ibex::plat::Win32::default_cursor = (jint)LoadCursor(NULL, IDC_ARROW);
165 org::ibex::plat::Win32::crosshair_cursor = (jint)LoadCursor(NULL, IDC_CROSS);
166 org::ibex::plat::Win32::text_cursor = (jint)LoadCursor(NULL, IDC_IBEAM);
167 org::ibex::plat::Win32::move_cursor = (jint)LoadCursor(NULL, IDC_SIZEALL);
168 org::ibex::plat::Win32::sizenesw_cursor = (jint)LoadCursor(NULL, IDC_SIZENESW);
169 org::ibex::plat::Win32::sizens_cursor = (jint)LoadCursor(NULL, IDC_SIZENS);
170 org::ibex::plat::Win32::sizenwse_cursor = (jint)LoadCursor(NULL, IDC_SIZENWSE);
171 org::ibex::plat::Win32::sizewe_cursor = (jint)LoadCursor(NULL, IDC_SIZEWE);
172 org::ibex::plat::Win32::hand_cursor = (jint)CreateCursor(GetModuleHandle(NULL), 14, 1, 32, 32, hand_cursor_and, hand_cursor_xor);
174 messagePumpThread = (jint)GetCurrentThreadId();
175 messagePumpStarted->release();
178 while(GetMessage(&msg, (HWND)NULL, 0, 0) > 0) {
180 if (msg.message == WM_USER_CREATEWINDOW) {
181 org::ibex::plat::Win32$Win32Surface *surface = (org::ibex::plat::Win32$Win32Surface*)msg.lParam;
183 // we must create a unique window class name for each
184 // window so that minimization icons can be set independantly
186 sprintf(buf, "Ibex_WINDOW_CLASS_%i", window_class_counter++);
189 wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
190 wc.lpfnWndProc = WndProc;
192 wc.cbSize = sizeof(WNDCLASSEX);
194 wc.hInstance = GetModuleHandle(NULL);
195 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
196 wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL),
199 GetSystemMetrics(SM_CXSMICON),
200 GetSystemMetrics(SM_CYSMICON),
202 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
203 wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
204 wc.lpszMenuName = "menu";
205 wc.lpszClassName = buf;
206 RegisterClassEx(&wc);
208 surface->hwnd = (jint)CreateWindow(wc.lpszClassName, TEXT(""),
209 msg.wParam ? WS_NORMAL : WS_POPUP,
211 (HWND__*)NULL, (HMENU__*)NULL,
212 GetModuleHandle(NULL), (LPVOID)NULL);
214 SetFocus((HWND)surface->hwnd);
215 surface->hwndCreated->release();
218 TranslateMessage(&msg);
219 if (msg.message == WM_LBUTTONDOWN || msg.message == WM_RBUTTONDOWN || msg.message == WM_MBUTTONDOWN) SetFocus(msg.hwnd);
220 DispatchMessage(&msg);
224 java::lang::System::exit(-1);
228 // Platform Methods ///////////////////////////////////////////////////////////////////
230 jstring org::ibex::plat::Win32::_getEnv(jstring key) {
231 int len = JvGetStringUTFLength(key);
233 JvGetStringUTFRegion(key, 0, len, buf);
236 DWORD ret = GetEnvironmentVariable(buf, buf2, 1024);
237 if (ret > 0 && ret < 1024) return JvNewStringLatin1(buf2);
241 jstring org::ibex::plat::Win32::_fileDialog(jstring suggestedFileName, jboolean write) {
245 memset(buf, 0, 1024);
246 memset(&ofn, 0, sizeof(OPENFILENAME));
248 if (suggestedFileName != NULL)
249 JvGetStringUTFRegion(suggestedFileName, 0, min(1023, JvGetStringUTFLength(suggestedFileName)), buf);
251 ofn.lStructSize = sizeof(OPENFILENAME);
252 ofn.nMaxCustFilter = 0;
256 if (write) ofn.Flags |= OFN_OVERWRITEPROMPT;
257 ofn.Flags |= OFN_HIDEREADONLY;
259 int ret = write ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn);
260 return ret == 0 ? NULL : JvNewStringLatin1(buf);
263 void org::ibex::plat::Win32::__detectProxy(JArray<jstring>* container) {
269 LONG result = RegOpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", &hkey);
270 if (result != ERROR_SUCCESS) return;
275 result = RegQueryValueEx(hkey, "AutoConfigURL", NULL, &type, (LPBYTE)buf, &buflen);
277 if (result == ERROR_SUCCESS) elements(container)[2] = JvNewStringLatin1(buf);
281 RegQueryValueEx(hkey, "ProxyEnable", NULL, &type, (LPBYTE)buf, &buflen);
282 if (buf[0] != 1) return;
287 RegQueryValueEx(hkey, "ProxyServer", NULL, &type, (LPBYTE)buf, &buflen);
289 elements(container)[0] = JvNewStringLatin1(buf);
293 RegQueryValueEx(hkey, "ProxyOverride", NULL, &type, (LPBYTE)buf, &buflen);
295 elements(container)[1] = JvNewStringLatin1(buf);
298 jstring org::ibex::plat::Win32::_getClipBoard() {
299 OpenClipboard((HWND)desktop_handle);
300 HGLOBAL hmem = GetClipboardData(CF_TEXT);
301 if (hmem == NULL) return NULL;
302 char* buf = (char*)GlobalLock(hmem);
303 if (buf == NULL) return NULL;
304 jstring ret = JvNewStringLatin1(buf);
310 void org::ibex::plat::Win32::_setClipBoard(jstring s) {
311 OpenClipboard((HWND)desktop_handle);
312 HGLOBAL hmem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, JvGetStringUTFLength(s) + 1);
313 if (hmem == NULL) return;
314 char* buf = (char*)GlobalLock(hmem);
315 if (buf == NULL) return;
316 JvGetStringUTFRegion(s, 0, JvGetStringUTFLength(s), buf);
317 buf[JvGetStringUTFLength(s)] = '\0';
319 SetClipboardData(CF_TEXT, hmem);
323 void org::ibex::plat::Win32::_criticalAbort(jstring message) {
324 char buf[JvGetStringUTFLength(message) + 1];
325 buf[JvGetStringUTFLength(message)] = '\0';
326 JvGetStringUTFRegion(message, 0, JvGetStringUTFLength(message), buf);
327 MessageBox (NULL, buf, "Ibex Cannot Continue", MB_OK | MB_ICONSTOP | MB_TASKMODAL | MB_SETFOREGROUND);
328 java::lang::System::exit(-1);
331 jint org::ibex::plat::Win32::_getScreenWidth() {
333 SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
334 return rect.right - rect.left;
337 jint org::ibex::plat::Win32::_getScreenHeight() {
339 SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
340 return rect.bottom - rect.top;
343 jboolean org::ibex::plat::Win32::_newBrowserWindow_(jstring url) {
345 int len = min(2048, JvGetStringUTFLength(url));
347 JvGetStringUTFRegion(url, 0, len, buf);
351 memset(&ei, 0, sizeof(ei));
352 ei.cbSize = sizeof(ei);
355 ei.fMask = SEE_MASK_NOCLOSEPROCESS;
356 ei.nShow = SW_SHOWDEFAULT;
357 return (ShellExecuteEx(&ei) == 0);
362 // Win32PixelBuffer /////////////////////////////////////////////////////////////////////////
364 // This is a scratch area; when blitting a translucent image, we copy the underlying data here first.
365 // Since all drawing operations are single-threaded, it's safe to use a global here.
366 static HBITMAP scratch = NULL;
367 static HDC scratch_dc = NULL;
368 static jint* scratch_bits = NULL;
369 static jint scratch_w = 0;
370 static jint scratch_h = 0;
372 #define max(a,b) ((a)>(b)?(a):(b))
373 #define min(a,b) ((a)<(b)?(a):(b))
375 void org::ibex::plat::Win32$Win32PixelBuffer::drawPicture(org::ibex::Picture* source0,
377 jint cx1, jint cy1, jint cx2, jint cy2,
378 jint rgb, jboolean alphaOnly) {
379 org::ibex::plat::Win32$Win32Picture* source = (org::ibex::plat::Win32$Win32Picture*)source0;
383 cx2 = min(dx + source->getWidth(), cx2);
384 cy2 = min(dy + source->getHeight(), cy2);
385 if (cx1 >= cx2 || cy1 >= cy2) return;
387 if (source->hasalpha) {
389 if (scratch == NULL || scratch_w < cx2 - cx1 || scratch_h < cy2 - cy1) {
390 if (scratch_dc != NULL) DeleteDC(scratch_dc);
391 if (scratch != NULL) DeleteObject(scratch);
392 scratch_w = max(cx2 - cx1, scratch_w);
393 scratch_h = max(cy2 - cy1, scratch_h);
395 BITMAPINFO bitmapinfo;
396 memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
397 bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
398 bitmapinfo.bmiHeader.biWidth = scratch_w;
399 bitmapinfo.bmiHeader.biHeight = -1 * scratch_h;
400 bitmapinfo.bmiHeader.biPlanes = 1;
401 bitmapinfo.bmiHeader.biBitCount = 32;
402 bitmapinfo.bmiHeader.biCompression = BI_RGB;
404 // create section DIB
405 scratch = CreateDIBSection(NULL, &bitmapinfo, DIB_RGB_COLORS, (void**)&scratch_bits, NULL, 0);
406 scratch_dc = CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
407 SelectObject(scratch_dc, scratch);
410 // copy from screen to scratch
411 BitBlt((HDC)scratch_dc, 0, 0, cx2 - cx1, cy2 - cy1, (HDC)hdc, cx1, cy1, SRCCOPY);
413 // apply alpha-blending to scratch
414 jint* dat = elements(source->data);
416 for(int x = cx1; x < cx2; x++)
417 for(int y = cy1; y < cy2; y++) {
418 jint dst = scratch_bits[(y - dy) * scratch_w + (x - dx)];
420 // FEATURE: see if we can leverage GDI to do something more clever here with alphaOnly
421 jint src = alphaOnly ? rgb : dat[(y - dy) * source->getWidth() + x - dx];
422 jint alpha = (dat[(y - dy) * source->getWidth() + x - dx] & 0xFF000000) >> 24;
423 jint r = (((src & 0x00FF0000) >> 16) * alpha + ((dst & 0x00FF0000) >> 16) * (0xFF - alpha)) / 0xFF;
424 jint g = (((src & 0x0000FF00) >> 8) * alpha + ((dst & 0x0000FF00) >> 8) * (0xFF - alpha)) / 0xFF;
425 jint b = (((src & 0x000000FF)) * alpha + ((dst & 0x000000FF)) * (0xFF - alpha)) / 0xFF;
426 scratch_bits[(y - dy) * scratch_w + (x - dx)] = (r << 16) | (g << 8) | b;
429 // copy back from scratch to screen
430 BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)scratch_dc, 0, 0, SRCCOPY);
434 // FIXME: support alphaOnly case here
435 if (source->hasmask) {
436 BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->maskdc, cx1 - dx, cy1 - dy, SRCAND);
437 BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->hdc, cx1 - dx, cy1 - dy, SRCPAINT);
439 BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->hdc, cx1 - dx, cy1 - dy, SRCCOPY);
446 void org::ibex::plat::Win32$Win32PixelBuffer::fillRect(jint x, jint y, jint x2, jint y2, jint color) {
450 // sadly, the ability to change the color of a brush didn't arrive until Win2k...
451 HBRUSH brush = CreateSolidBrush(PALETTERGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
453 RECT rect = { x, y, x + w, y + h };
454 FillRect((HDC)hdc, &rect, brush);
458 void org::ibex::plat::Win32$Win32Surface::blit(org::ibex::PixelBuffer* s, jint sx, jint sy, jint dx, jint dy, jint dx2, jint dy2) {
459 // we create the DC lazily to get around some strange race condition in WinXP
460 if (hdc == 0) hdc = (jint)GetDC((HWND)hwnd);
461 BitBlt((HDC)hdc, dx, dy, dx2 - dx, dy2 - dy, (HDC)(((org::ibex::plat::Win32$Win32PixelBuffer*)s)->hdc), sx, sy, SRCCOPY);
464 void org::ibex::plat::Win32$Win32PixelBuffer::natInit() {
465 hbitmap = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, w, h);
466 hdc = (jint)CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
467 SetBkMode((HDC)hdc, TRANSPARENT);
468 SelectObject((HDC)hdc, (HBITMAP)hbitmap);
471 void org::ibex::plat::Win32$Win32PixelBuffer::finalize() {
472 DeleteObject((void*)hdc);
473 DeleteObject((void*)hbitmap);
478 // Win32Picture /////////////////////////////////////////////////////////////////////////
480 void org::ibex::plat::Win32$Win32Picture::natInit() {
482 BITMAPINFO bitmapinfo;
483 memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
484 bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
485 bitmapinfo.bmiHeader.biWidth = width;
486 bitmapinfo.bmiHeader.biHeight = -1 * height;
487 bitmapinfo.bmiHeader.biPlanes = 1;
488 bitmapinfo.bmiHeader.biBitCount = 32;
489 bitmapinfo.bmiHeader.biCompression = BI_RGB;
491 hbitmap = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, width, height);
492 hdc = (jint)CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
493 SelectObject((HDC)hdc, (HBITMAP)hbitmap);
494 uint32_t* dat = (uint32_t*)elements(data);
495 for(int i=0; i<data->length; i++) if ((dat[i] & 0xFF000000) == 0x00000000) dat[i] = 0x00000000;
496 StretchDIBits((HDC)hdc, 0, 0, width, height, 0, 0, width, height, elements(data), &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
498 jint _copy[min(1024, data->length)];
499 jint* copy = data->length > 1024 ? (jint*)malloc(data->length * 4) : _copy;
501 memcpy(copy, elements(data), data->length * 4);
502 for(int i=0; i<data->length; i++)
503 if ((copy[i] & 0xFF000000) == 0x00000000) {
505 copy[i] = 0x00FFFFFF;
506 } else if ((copy[i] & 0xFF000000) == 0xFF000000) {
507 copy[i] = 0x00000000;
511 if (data->length > 1024) free(copy);
516 if (data->length > 1024) free(copy);
520 // hmask = (jint)CreateBitmap(w, h, 1, 1, NULL);
521 hmask = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, width, height);
522 maskdc = (jint)CreateCompatibleDC(NULL);
523 SelectObject((HDC)maskdc, (HBITMAP)hmask);
524 StretchDIBits((HDC)maskdc, 0, 0, width, height, 0, 0, width, height, copy, &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
525 if (data->length > 1024) free(copy);
530 // Win32Surface /////////////////////////////////////////////////////////////////////////////
532 void org::ibex::plat::Win32$Win32Surface::natInit(jboolean framed) {
534 // Ask the message-handling thread to create a window for us
535 PostThreadMessage((DWORD)org::ibex::plat::Win32::messagePumpThread, WM_USER + 2, (WPARAM)framed, (LPARAM)this);
536 hwndCreated->block();
538 // turn on incremental GC now that we have a user-visible interface
539 // [[this is causing segfaults; enable it later...]]
540 // GC_enable_incremental();
542 ShowWindow ((HWND)hwnd, SW_SHOWDEFAULT);
545 void org::ibex::plat::Win32$Win32Surface::finalize() { /* DeleteObject((void*)hwnd); */ }
546 void org::ibex::plat::Win32$Win32Surface::toFront() { BringWindowToTop((HWND)hwnd); }
547 void org::ibex::plat::Win32$Win32Surface::toBack() { SetWindowPos((HWND)hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); }
548 void org::ibex::plat::Win32$Win32Surface::_dispose() { PostMessage((HWND)hwnd, WM_USER_DISPOSE, 0, 0); }
549 void org::ibex::plat::Win32$Win32Surface::setInvisible(jboolean h) { ShowWindow((HWND)hwnd, h ? SW_HIDE : SW_SHOWNORMAL); }
550 void org::ibex::plat::Win32$Win32Surface::_setMinimized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMINIMIZED : SW_SHOWNORMAL); }
551 void org::ibex::plat::Win32$Win32Surface::_setMaximized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); }
552 void org::ibex::plat::Win32$Win32Surface::postCursorChange() { PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0); }
554 void org::ibex::plat::Win32$Win32Surface::setLocation() {
559 ClientToScreen((HWND)hwnd, &point);
560 GetWindowRect((HWND)hwnd, &rect);
561 SetWindowPos((HWND)hwnd, NULL, root->x - (point.x - rect.left), root->y - (point.y - rect.top), 0, 0, SWP_NOZORDER | SWP_NOSIZE);
564 void org::ibex::plat::Win32$Win32Surface::_setSize(jint w, jint h) {
565 RECT client_rect, window_rect;
566 GetClientRect((HWND)hwnd, &client_rect);
567 GetWindowRect((HWND)hwnd, &window_rect);
568 int width = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left) + w;
569 int height = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top) + h;
570 SetWindowPos((HWND)hwnd, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE);
573 void org::ibex::plat::Win32$Win32Surface::setTitleBarText(java::lang::String* title) {
574 int len = min(1024, JvGetStringUTFLength(title));
577 JvGetStringUTFRegion(title, 0, len, buf);
578 SetWindowText((HWND)hwnd, buf);
581 void org::ibex::plat::Win32$Win32Surface::setIcon(org::ibex::Picture* p0) {
583 org::ibex::plat::Win32$Win32Picture* p = (org::ibex::plat::Win32$Win32Picture*)p0;
584 int icon_width = GetSystemMetrics(SM_CXSMICON);
585 int icon_height = GetSystemMetrics(SM_CYSMICON);
587 // we create the DC lazily to get around some strange race condition in WinXP
588 if (hdc == 0) hdc = (jint)GetDC((HWND)hwnd);
591 HBITMAP bit = CreateCompatibleBitmap((HDC)hdc, icon_width, icon_height);
592 HDC memdc = CreateCompatibleDC((HDC)hdc);
593 SelectObject(memdc, bit);
594 BitBlt((HDC)memdc, 0, 0, icon_width, icon_height, (HDC)(p->hdc), 0, 0, SRCCOPY);
597 jint* dat = elements(p->data);
598 HBITMAP bit_mask = CreateBitmap(icon_width, icon_height * 2, 1, 1, NULL);
599 SelectObject(memdc, bit_mask);
600 for(int x=0; x<icon_width; x++)
601 for(int y=0; y<icon_height; y++) {
602 int source = dat[(x * p->getWidth()) / icon_width + ((y * p->getHeight()) / icon_height) * (p->getWidth())];
603 if ((source & 0xFF000000) != 0x00000000) SetPixel(memdc, x, y, PALETTERGB(0, 0, 0));
604 else SetPixel(memdc, x, y, PALETTERGB(255, 255, 255));
607 // instantiate the icon and assign it to the window class
610 ici.hbmMask = bit_mask;
612 HICON hicon = CreateIconIndirect(&ici);
613 HICON oldicon = (HICON)SetClassLong((HWND)hwnd, GCL_HICONSM, (LONG)hicon);
614 if (oldicon != NULL) DestroyIcon(oldicon);
618 static jstring keyToString(WPARAM wParam);
619 jint org::ibex::plat::Win32$Win32Surface::WndProc(jint _hwnd, jint _iMsg, jint _wParam, jint _lParam) {
621 UINT iMsg = (UINT)_iMsg;
622 WPARAM wParam = (WPARAM)_wParam;
623 LPARAM lParam = (LPARAM)_lParam;
625 int oldmousex, oldmousey;
631 RECT client_rect, window_rect;
635 int addwidth, addheight;
638 case WM_DEVMODECHANGE: break; // FEATURE: color depth changed
639 case WM_DISPLAYCHANGE: break; // FEATURE: screen size changed
640 case WM_MOUSEWHEEL: break; // FEATURE: Mouse Wheel
642 case WM_SYSKEYDOWN: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
644 KeyPressed(keyToString(wParam));
647 case WM_SYSKEYUP: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
649 KeyReleased(keyToString(wParam));
652 case WM_SETFOCUS: Focused(true); return 0;
653 case WM_KILLFOCUS: Focused(false); return 0;
654 case WM_LBUTTONDBLCLK: DoubleClick(1); return 0;
655 case WM_RBUTTONDBLCLK: DoubleClick(2); return 0;
656 case WM_MBUTTONDBLCLK: DoubleClick(3); return 0;
657 case WM_LBUTTONDOWN: Press(1); return 0;
658 case WM_RBUTTONDOWN: Press(2); return 0;
659 case WM_MBUTTONDOWN: Press(3); return 0;
660 case WM_CLOSE: Close(); return 0;
661 case WM_ERASEBKGND: return 0;
666 Release(iMsg == WM_LBUTTONUP ? 1 : (iMsg == WM_RBUTTONUP ? 2 : 3));
667 if (captured && !inside) {
669 SetCursor((HCURSOR)previous_cursor);
674 GetClientRect((HWND)hwnd, &rect);
675 PosChange(rect.left, rect.top);
679 if (wParam == SIZE_MINIMIZED) {
680 if (maximized) Maximized(false);
682 } else if (wParam == SIZE_MAXIMIZED) {
683 if (minimized) Minimized(false);
686 if (minimized) Minimized(false);
687 if (maximized) Maximized(false);
689 // deliberately fall through to WM_SIZING
692 GetClientRect((HWND)hwnd, &rect);
693 SizeChange(rect.right - rect.left, rect.bottom - rect.top);
697 if (mousex == lParam & 0xFFFF && mousey == (lParam & 0xFFFF0000) >> 16) return 0;
699 GetClientRect((HWND)hwnd, &rect);
700 point.x = mouse_x = lParam & 0xFFFF;
701 point.y = mouse_y = (lParam & 0xFFFF0000) >> 16;
702 ClientToScreen((HWND)hwnd, &point);
703 hwnd2 = WindowFromPoint(point);
705 newinside = hwnd2 == (HWND)hwnd && mouse_x > 0 && mouse_y > 0 && mouse_x < (rect.right - rect.left) && mouse_y < (rect.bottom - rect.top) ? 1 : 0;
707 Move(mouse_x, mouse_y);
709 if (newinside && !inside) {
711 SetCapture((HWND)hwnd);
713 previous_cursor = (jint)GetCursor();
714 PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0);
716 } else if (!newinside && inside) {
718 if (!button1 && !button2 && !button3) {
721 SetCursor((HCURSOR)previous_cursor);
727 case WM_USER_SETCURSOR:
728 // I can't find documentation of this anywhere, but in my experience, an event must be received between an invocation
729 // of SetCapture() and SetCursor(). Furthermore, it seems that after calling SetCursor(), the cursor will not change until a
730 // return from WndProc (ie the completion of the processing of some event). Hence, we use WM_USER to indicate that the screen
731 // cursor should be re-set to "current_cursor".
732 SetCursor((HCURSOR)current_cursor);
735 case WM_USER_DISPOSE:
736 // used to signal that we should destroy ourselves
737 DestroyWindow((HWND)hwnd);
740 case WM_GETMINMAXINFO:
741 GetClientRect((HWND)hwnd, &client_rect);
742 GetWindowRect((HWND)hwnd, &window_rect);
743 addwidth = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left);
744 addheight = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top);
745 mmi = (MINMAXINFO*)lParam;
746 mmi->ptMinTrackSize.x = ((uint32_t)root->minwidth) + addwidth;
747 mmi->ptMinTrackSize.y = ((uint32_t)root->minheight) + addheight;
748 resizable = !((root->minwidth == root->maxwidth) && (root->minheight == root->maxheight));
749 mmi->ptMaxTrackSize.x = resizable ? org::ibex::plat::Win32::getScreenWidth() : mmi->ptMinTrackSize.x;
750 mmi->ptMaxTrackSize.y = resizable ? org::ibex::plat::Win32::getScreenHeight() : mmi->ptMinTrackSize.y;
755 BeginPaint((HWND)org::ibex::plat::Win32$Win32Surface::hwnd, &ps);
756 Dirty(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top);
757 EndPaint((HWND)org::ibex::plat::Win32$Win32Surface::hwnd, &ps);
763 return DefWindowProc((HWND)hwnd, iMsg, wParam, lParam);
767 // Key Translator Helper /////////////////////////////////////////////////////////////////////
769 static char keyarr [256] = { 0 };
770 static jstring keyToString(WPARAM wParam) {
772 keyarr[VK_CAPITAL] = GetKeyState(VK_CAPITAL);
773 keyarr[VK_LSHIFT] = GetKeyState(VK_LSHIFT);
774 keyarr[VK_RSHIFT] = GetKeyState(VK_RSHIFT);
775 keyarr[VK_SHIFT] = GetKeyState(VK_SHIFT);
777 if (ToAsciiEx(wParam, 0, (BYTE*)keyarr, (WORD*)arr, 0, GetKeyboardLayout(0)) == 1) {
779 case '\t': return JvNewStringLatin1("tab");
780 case 0x1b: return JvNewStringLatin1("escape");
781 case '\n': return JvNewStringLatin1("enter");
782 case '\r': return JvNewStringLatin1("enter");
783 case 0x08: return JvNewStringLatin1("back_space");
784 default: return JvNewStringLatin1(arr, 1);
787 } else switch (wParam) {
788 case VK_CLEAR: return JvNewStringLatin1("clear");
789 case VK_SHIFT: return JvNewStringLatin1("shift");
790 case VK_CONTROL: return JvNewStringLatin1("control");
791 case VK_CAPITAL: return JvNewStringLatin1("caps_lock");
792 case VK_MENU: return JvNewStringLatin1("alt");
793 case VK_PAUSE: return JvNewStringLatin1("pause");
794 case VK_PRIOR: return JvNewStringLatin1("page_up");
795 case VK_NEXT: return JvNewStringLatin1("page_down");
796 case VK_END: return JvNewStringLatin1("end");
797 case VK_HOME: return JvNewStringLatin1("home");
798 case VK_LEFT: return JvNewStringLatin1("left");
799 case VK_UP: return JvNewStringLatin1("up");
800 case VK_RIGHT: return JvNewStringLatin1("right");
801 case VK_DOWN: return JvNewStringLatin1("down");
802 case VK_INSERT: return JvNewStringLatin1("insert");
803 case VK_DELETE: return JvNewStringLatin1("delete");
804 case VK_NUMPAD0: return JvNewStringLatin1("numpad0");
805 case VK_NUMPAD1: return JvNewStringLatin1("numpad1");
806 case VK_NUMPAD2: return JvNewStringLatin1("numpad2");
807 case VK_NUMPAD3: return JvNewStringLatin1("numpad3");
808 case VK_NUMPAD4: return JvNewStringLatin1("numpad4");
809 case VK_NUMPAD5: return JvNewStringLatin1("numpad5");
810 case VK_NUMPAD6: return JvNewStringLatin1("numpad6");
811 case VK_NUMPAD7: return JvNewStringLatin1("numpad7");
812 case VK_NUMPAD8: return JvNewStringLatin1("numpad8");
813 case VK_NUMPAD9: return JvNewStringLatin1("numpad9");
814 case VK_F1: return JvNewStringLatin1("f1");
815 case VK_F2: return JvNewStringLatin1("f2");
816 case VK_F3: return JvNewStringLatin1("f3");
817 case VK_F4: return JvNewStringLatin1("f4");
818 case VK_F5: return JvNewStringLatin1("f5");
819 case VK_F6: return JvNewStringLatin1("f6");
820 case VK_F7: return JvNewStringLatin1("f7");
821 case VK_F8: return JvNewStringLatin1("f8");
822 case VK_F9: return JvNewStringLatin1("f9");
823 case VK_F10: return JvNewStringLatin1("f10");
824 case VK_F11: return JvNewStringLatin1("f11");
825 case VK_F12: return JvNewStringLatin1("f12");
826 case VK_NUMLOCK: return JvNewStringLatin1("num_lock");
827 case VK_SCROLL: return JvNewStringLatin1("scroll_lock");
828 case VK_LSHIFT: return JvNewStringLatin1("shift");
829 case VK_RSHIFT: return JvNewStringLatin1("shift");
830 case VK_LCONTROL: return JvNewStringLatin1("control");
831 case VK_RCONTROL: return JvNewStringLatin1("control");