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 // Win32 throws stderr in the trash unless you designate your binary as a "console binary"
151 if (org::ibex::util::Log::logstream == java::lang::System::err) {
153 freopen("CONOUT$", "w+t", stderr);
157 void org::ibex::plat::Win32::natInit() {
159 // grab desktop dc/handle
160 desktop_handle = (jint)GetDesktopWindow();
161 desktop_dc = (jint)GetDC((HWND)desktop_handle);
164 org::ibex::plat::Win32::wait_cursor = (jint)LoadCursor(NULL, IDC_WAIT);
165 org::ibex::plat::Win32::default_cursor = (jint)LoadCursor(NULL, IDC_ARROW);
166 org::ibex::plat::Win32::crosshair_cursor = (jint)LoadCursor(NULL, IDC_CROSS);
167 org::ibex::plat::Win32::text_cursor = (jint)LoadCursor(NULL, IDC_IBEAM);
168 org::ibex::plat::Win32::move_cursor = (jint)LoadCursor(NULL, IDC_SIZEALL);
169 org::ibex::plat::Win32::sizenesw_cursor = (jint)LoadCursor(NULL, IDC_SIZENESW);
170 org::ibex::plat::Win32::sizens_cursor = (jint)LoadCursor(NULL, IDC_SIZENS);
171 org::ibex::plat::Win32::sizenwse_cursor = (jint)LoadCursor(NULL, IDC_SIZENWSE);
172 org::ibex::plat::Win32::sizewe_cursor = (jint)LoadCursor(NULL, IDC_SIZEWE);
173 org::ibex::plat::Win32::hand_cursor = (jint)CreateCursor(GetModuleHandle(NULL), 14, 1, 32, 32, hand_cursor_and, hand_cursor_xor);
175 messagePumpThread = (jint)GetCurrentThreadId();
176 messagePumpStarted->release();
179 while(GetMessage(&msg, (HWND)NULL, 0, 0) > 0) {
181 if (msg.message == WM_USER_CREATEWINDOW) {
182 org::ibex::plat::Win32$Win32Surface *surface = (org::ibex::plat::Win32$Win32Surface*)msg.lParam;
184 // we must create a unique window class name for each
185 // window so that minimization icons can be set independantly
187 sprintf(buf, "Ibex_WINDOW_CLASS_%i", window_class_counter++);
190 wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
191 wc.lpfnWndProc = WndProc;
193 wc.cbSize = sizeof(WNDCLASSEX);
195 wc.hInstance = GetModuleHandle(NULL);
196 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
197 wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL),
200 GetSystemMetrics(SM_CXSMICON),
201 GetSystemMetrics(SM_CYSMICON),
203 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
204 wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
205 wc.lpszMenuName = "menu";
206 wc.lpszClassName = buf;
207 RegisterClassEx(&wc);
209 surface->hwnd = (jint)CreateWindow(wc.lpszClassName, TEXT(""),
210 msg.wParam ? WS_NORMAL : WS_POPUP,
212 (HWND__*)NULL, (HMENU__*)NULL,
213 GetModuleHandle(NULL), (LPVOID)NULL);
215 SetFocus((HWND)surface->hwnd);
216 surface->hwndCreated->release();
219 TranslateMessage(&msg);
220 if (msg.message == WM_LBUTTONDOWN || msg.message == WM_RBUTTONDOWN || msg.message == WM_MBUTTONDOWN) SetFocus(msg.hwnd);
221 DispatchMessage(&msg);
225 java::lang::System::exit(-1);
229 // Platform Methods ///////////////////////////////////////////////////////////////////
231 jstring org::ibex::plat::Win32::_getEnv(jstring key) {
232 int len = JvGetStringUTFLength(key);
234 JvGetStringUTFRegion(key, 0, len, buf);
237 DWORD ret = GetEnvironmentVariable(buf, buf2, 1024);
238 if (ret > 0 && ret < 1024) return JvNewStringLatin1(buf2);
242 jstring org::ibex::plat::Win32::_fileDialog(jstring suggestedFileName, jboolean write) {
246 memset(buf, 0, 1024);
247 memset(&ofn, 0, sizeof(OPENFILENAME));
249 if (suggestedFileName != NULL)
250 JvGetStringUTFRegion(suggestedFileName, 0, min(1023, JvGetStringUTFLength(suggestedFileName)), buf);
252 ofn.lStructSize = sizeof(OPENFILENAME);
253 ofn.nMaxCustFilter = 0;
257 if (write) ofn.Flags |= OFN_OVERWRITEPROMPT;
258 ofn.Flags |= OFN_HIDEREADONLY;
260 int ret = write ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn);
261 return ret == 0 ? NULL : JvNewStringLatin1(buf);
264 void org::ibex::plat::Win32::__detectProxy(JArray<jstring>* container) {
270 LONG result = RegOpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", &hkey);
271 if (result != ERROR_SUCCESS) return;
276 result = RegQueryValueEx(hkey, "AutoConfigURL", NULL, &type, (LPBYTE)buf, &buflen);
278 if (result == ERROR_SUCCESS) elements(container)[2] = JvNewStringLatin1(buf);
282 RegQueryValueEx(hkey, "ProxyEnable", NULL, &type, (LPBYTE)buf, &buflen);
283 if (buf[0] != 1) return;
288 RegQueryValueEx(hkey, "ProxyServer", NULL, &type, (LPBYTE)buf, &buflen);
290 elements(container)[0] = JvNewStringLatin1(buf);
294 RegQueryValueEx(hkey, "ProxyOverride", NULL, &type, (LPBYTE)buf, &buflen);
296 elements(container)[1] = JvNewStringLatin1(buf);
299 jstring org::ibex::plat::Win32::_getClipBoard() {
300 OpenClipboard((HWND)desktop_handle);
301 HGLOBAL hmem = GetClipboardData(CF_TEXT);
302 if (hmem == NULL) return NULL;
303 char* buf = (char*)GlobalLock(hmem);
304 if (buf == NULL) return NULL;
305 jstring ret = JvNewStringLatin1(buf);
311 void org::ibex::plat::Win32::_setClipBoard(jstring s) {
312 OpenClipboard((HWND)desktop_handle);
313 HGLOBAL hmem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, JvGetStringUTFLength(s) + 1);
314 if (hmem == NULL) return;
315 char* buf = (char*)GlobalLock(hmem);
316 if (buf == NULL) return;
317 JvGetStringUTFRegion(s, 0, JvGetStringUTFLength(s), buf);
318 buf[JvGetStringUTFLength(s)] = '\0';
320 SetClipboardData(CF_TEXT, hmem);
324 void org::ibex::plat::Win32::_criticalAbort(jstring message) {
325 char buf[JvGetStringUTFLength(message) + 1];
326 buf[JvGetStringUTFLength(message)] = '\0';
327 JvGetStringUTFRegion(message, 0, JvGetStringUTFLength(message), buf);
328 MessageBox (NULL, buf, "Ibex Cannot Continue", MB_OK | MB_ICONSTOP | MB_TASKMODAL | MB_SETFOREGROUND);
329 java::lang::System::exit(-1);
332 jint org::ibex::plat::Win32::_getScreenWidth() {
334 SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
335 return rect.right - rect.left;
338 jint org::ibex::plat::Win32::_getScreenHeight() {
340 SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
341 return rect.bottom - rect.top;
344 jboolean org::ibex::plat::Win32::_newBrowserWindow_(jstring url) {
346 int len = min(2048, JvGetStringUTFLength(url));
348 JvGetStringUTFRegion(url, 0, len, buf);
352 memset(&ei, 0, sizeof(ei));
353 ei.cbSize = sizeof(ei);
356 ei.fMask = SEE_MASK_NOCLOSEPROCESS;
357 ei.nShow = SW_SHOWDEFAULT;
358 return (ShellExecuteEx(&ei) == 0);
363 // Win32PixelBuffer /////////////////////////////////////////////////////////////////////////
365 // This is a scratch area; when blitting a translucent image, we copy the underlying data here first.
366 // Since all drawing operations are single-threaded, it's safe to use a global here.
367 static HBITMAP scratch = NULL;
368 static HDC scratch_dc = NULL;
369 static jint* scratch_bits = NULL;
370 static jint scratch_w = 0;
371 static jint scratch_h = 0;
373 #define max(a,b) ((a)>(b)?(a):(b))
374 #define min(a,b) ((a)<(b)?(a):(b))
376 void org::ibex::plat::Win32$Win32PixelBuffer::drawPicture(org::ibex::graphics::Picture* source0,
378 jint cx1, jint cy1, jint cx2, jint cy2,
379 jint rgb, jboolean alphaOnly) {
380 org::ibex::plat::Win32$Win32Picture* source = (org::ibex::plat::Win32$Win32Picture*)source0;
384 cx2 = min(dx + source->getWidth(), cx2);
385 cy2 = min(dy + source->getHeight(), cy2);
386 if (cx1 >= cx2 || cy1 >= cy2) return;
388 if (source->hasalpha) {
390 if (scratch == NULL || scratch_w < cx2 - cx1 || scratch_h < cy2 - cy1) {
391 if (scratch_dc != NULL) DeleteDC(scratch_dc);
392 if (scratch != NULL) DeleteObject(scratch);
393 scratch_w = max(cx2 - cx1, scratch_w);
394 scratch_h = max(cy2 - cy1, scratch_h);
396 BITMAPINFO bitmapinfo;
397 memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
398 bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
399 bitmapinfo.bmiHeader.biWidth = scratch_w;
400 bitmapinfo.bmiHeader.biHeight = -1 * scratch_h;
401 bitmapinfo.bmiHeader.biPlanes = 1;
402 bitmapinfo.bmiHeader.biBitCount = 32;
403 bitmapinfo.bmiHeader.biCompression = BI_RGB;
405 // create section DIB
406 scratch = CreateDIBSection(NULL, &bitmapinfo, DIB_RGB_COLORS, (void**)&scratch_bits, NULL, 0);
407 scratch_dc = CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
408 SelectObject(scratch_dc, scratch);
411 // copy from screen to scratch
412 BitBlt((HDC)scratch_dc, 0, 0, cx2 - cx1, cy2 - cy1, (HDC)hdc, cx1, cy1, SRCCOPY);
414 // apply alpha-blending to scratch
415 jint* dat = elements(source->data);
417 for(int x = cx1; x < cx2; x++)
418 for(int y = cy1; y < cy2; y++) {
419 jint dst = scratch_bits[(y - dy) * scratch_w + (x - dx)];
421 // FEATURE: see if we can leverage GDI to do something more clever here with alphaOnly
422 jint src = alphaOnly ? rgb : dat[(y - dy) * source->getWidth() + x - dx];
423 jint alpha = (dat[(y - dy) * source->getWidth() + x - dx] & 0xFF000000) >> 24;
424 jint r = (((src & 0x00FF0000) >> 16) * alpha + ((dst & 0x00FF0000) >> 16) * (0xFF - alpha)) / 0xFF;
425 jint g = (((src & 0x0000FF00) >> 8) * alpha + ((dst & 0x0000FF00) >> 8) * (0xFF - alpha)) / 0xFF;
426 jint b = (((src & 0x000000FF)) * alpha + ((dst & 0x000000FF)) * (0xFF - alpha)) / 0xFF;
427 scratch_bits[(y - dy) * scratch_w + (x - dx)] = (r << 16) | (g << 8) | b;
430 // copy back from scratch to screen
431 BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)scratch_dc, 0, 0, SRCCOPY);
435 // FIXME: support alphaOnly case here
436 if (source->hasmask) {
437 BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->maskdc, cx1 - dx, cy1 - dy, SRCAND);
438 BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->hdc, cx1 - dx, cy1 - dy, SRCPAINT);
440 BitBlt((HDC)hdc, cx1, cy1, cx2 - cx1, cy2 - cy1, (HDC)source->hdc, cx1 - dx, cy1 - dy, SRCCOPY);
447 void org::ibex::plat::Win32$Win32PixelBuffer::fillRect(jint x, jint y, jint x2, jint y2, jint color) {
451 // sadly, the ability to change the color of a brush didn't arrive until Win2k...
452 HBRUSH brush = CreateSolidBrush(PALETTERGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
454 RECT rect = { x, y, x + w, y + h };
455 FillRect((HDC)hdc, &rect, brush);
459 void org::ibex::plat::Win32$Win32Surface::blit(org::ibex::graphics::PixelBuffer* s, jint sx, jint sy, jint dx, jint dy, jint dx2, jint dy2) {
460 // we create the DC lazily to get around some strange race condition in WinXP
461 if (hdc == 0) hdc = (jint)GetDC((HWND)hwnd);
462 BitBlt((HDC)hdc, dx, dy, dx2 - dx, dy2 - dy, (HDC)(((org::ibex::plat::Win32$Win32PixelBuffer*)s)->hdc), sx, sy, SRCCOPY);
465 void org::ibex::plat::Win32$Win32PixelBuffer::natInit() {
466 hbitmap = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, w, h);
467 hdc = (jint)CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
468 SetBkMode((HDC)hdc, TRANSPARENT);
469 SelectObject((HDC)hdc, (HBITMAP)hbitmap);
472 void org::ibex::plat::Win32$Win32PixelBuffer::finalize() {
473 DeleteObject((void*)hdc);
474 DeleteObject((void*)hbitmap);
479 // Win32Picture /////////////////////////////////////////////////////////////////////////
481 void org::ibex::plat::Win32$Win32Picture::natInit() {
483 BITMAPINFO bitmapinfo;
484 memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
485 bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
486 bitmapinfo.bmiHeader.biWidth = width;
487 bitmapinfo.bmiHeader.biHeight = -1 * height;
488 bitmapinfo.bmiHeader.biPlanes = 1;
489 bitmapinfo.bmiHeader.biBitCount = 32;
490 bitmapinfo.bmiHeader.biCompression = BI_RGB;
492 hbitmap = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, width, height);
493 hdc = (jint)CreateCompatibleDC((HDC)org::ibex::plat::Win32::desktop_dc);
494 SelectObject((HDC)hdc, (HBITMAP)hbitmap);
495 uint32_t* dat = (uint32_t*)elements(data);
496 for(int i=0; i<data->length; i++) if ((dat[i] & 0xFF000000) == 0x00000000) dat[i] = 0x00000000;
497 StretchDIBits((HDC)hdc, 0, 0, width, height, 0, 0, width, height, elements(data), &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
499 jint _copy[min(1024, data->length)];
500 jint* copy = data->length > 1024 ? (jint*)malloc(data->length * 4) : _copy;
502 memcpy(copy, elements(data), data->length * 4);
503 for(int i=0; i<data->length; i++)
504 if ((copy[i] & 0xFF000000) == 0x00000000) {
506 copy[i] = 0x00FFFFFF;
507 } else if ((copy[i] & 0xFF000000) == 0xFF000000) {
508 copy[i] = 0x00000000;
512 if (data->length > 1024) free(copy);
517 if (data->length > 1024) free(copy);
521 // hmask = (jint)CreateBitmap(w, h, 1, 1, NULL);
522 hmask = (jint)CreateCompatibleBitmap((HDC)org::ibex::plat::Win32::desktop_dc, width, height);
523 maskdc = (jint)CreateCompatibleDC(NULL);
524 SelectObject((HDC)maskdc, (HBITMAP)hmask);
525 StretchDIBits((HDC)maskdc, 0, 0, width, height, 0, 0, width, height, copy, &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
526 if (data->length > 1024) free(copy);
531 // Win32Surface /////////////////////////////////////////////////////////////////////////////
533 void org::ibex::plat::Win32$Win32Surface::natInit(jboolean framed) {
535 // Ask the message-handling thread to create a window for us
536 PostThreadMessage((DWORD)org::ibex::plat::Win32::messagePumpThread, WM_USER + 2, (WPARAM)framed, (LPARAM)this);
537 hwndCreated->block();
539 // turn on incremental GC now that we have a user-visible interface
540 // [[this is causing segfaults; enable it later...]]
541 // GC_enable_incremental();
543 ShowWindow ((HWND)hwnd, SW_SHOWDEFAULT);
546 void org::ibex::plat::Win32$Win32Surface::finalize() { /* DeleteObject((void*)hwnd); */ }
547 void org::ibex::plat::Win32$Win32Surface::toFront() { BringWindowToTop((HWND)hwnd); }
548 void org::ibex::plat::Win32$Win32Surface::toBack() { SetWindowPos((HWND)hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); }
549 void org::ibex::plat::Win32$Win32Surface::_dispose() { PostMessage((HWND)hwnd, WM_USER_DISPOSE, 0, 0); }
550 void org::ibex::plat::Win32$Win32Surface::setInvisible(jboolean h) { ShowWindow((HWND)hwnd, h ? SW_HIDE : SW_SHOWNORMAL); }
551 void org::ibex::plat::Win32$Win32Surface::_setMinimized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMINIMIZED : SW_SHOWNORMAL); }
552 void org::ibex::plat::Win32$Win32Surface::_setMaximized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); }
553 void org::ibex::plat::Win32$Win32Surface::postCursorChange() { PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0); }
555 void org::ibex::plat::Win32$Win32Surface::setLocation() {
560 ClientToScreen((HWND)hwnd, &point);
561 GetWindowRect((HWND)hwnd, &rect);
562 SetWindowPos((HWND)hwnd, NULL, root->x - (point.x - rect.left), root->y - (point.y - rect.top), 0, 0, SWP_NOZORDER | SWP_NOSIZE);
565 void org::ibex::plat::Win32$Win32Surface::_setSize(jint w, jint h) {
566 RECT client_rect, window_rect;
567 GetClientRect((HWND)hwnd, &client_rect);
568 GetWindowRect((HWND)hwnd, &window_rect);
569 int width = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left) + w;
570 int height = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top) + h;
571 SetWindowPos((HWND)hwnd, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE);
574 void org::ibex::plat::Win32$Win32Surface::setTitleBarText(java::lang::String* title) {
575 int len = min(1024, JvGetStringUTFLength(title));
578 JvGetStringUTFRegion(title, 0, len, buf);
579 SetWindowText((HWND)hwnd, buf);
582 void org::ibex::plat::Win32$Win32Surface::setIcon(org::ibex::graphics::Picture* p0) {
584 org::ibex::plat::Win32$Win32Picture* p = (org::ibex::plat::Win32$Win32Picture*)p0;
585 int icon_width = GetSystemMetrics(SM_CXSMICON);
586 int icon_height = GetSystemMetrics(SM_CYSMICON);
588 // we create the DC lazily to get around some strange race condition in WinXP
589 if (hdc == 0) hdc = (jint)GetDC((HWND)hwnd);
592 HBITMAP bit = CreateCompatibleBitmap((HDC)hdc, icon_width, icon_height);
593 HDC memdc = CreateCompatibleDC((HDC)hdc);
594 SelectObject(memdc, bit);
595 BitBlt((HDC)memdc, 0, 0, icon_width, icon_height, (HDC)(p->hdc), 0, 0, SRCCOPY);
598 jint* dat = elements(p->data);
599 HBITMAP bit_mask = CreateBitmap(icon_width, icon_height * 2, 1, 1, NULL);
600 SelectObject(memdc, bit_mask);
601 for(int x=0; x<icon_width; x++)
602 for(int y=0; y<icon_height; y++) {
603 int source = dat[(x * p->getWidth()) / icon_width + ((y * p->getHeight()) / icon_height) * (p->getWidth())];
604 if ((source & 0xFF000000) != 0x00000000) SetPixel(memdc, x, y, PALETTERGB(0, 0, 0));
605 else SetPixel(memdc, x, y, PALETTERGB(255, 255, 255));
608 // instantiate the icon and assign it to the window class
611 ici.hbmMask = bit_mask;
613 HICON hicon = CreateIconIndirect(&ici);
614 HICON oldicon = (HICON)SetClassLong((HWND)hwnd, GCL_HICONSM, (LONG)hicon);
615 if (oldicon != NULL) DestroyIcon(oldicon);
619 static jstring keyToString(WPARAM wParam);
620 jint org::ibex::plat::Win32$Win32Surface::WndProc(jint _hwnd, jint _iMsg, jint _wParam, jint _lParam) {
622 UINT iMsg = (UINT)_iMsg;
623 WPARAM wParam = (WPARAM)_wParam;
624 LPARAM lParam = (LPARAM)_lParam;
626 int oldmousex, oldmousey;
632 RECT client_rect, window_rect;
636 int addwidth, addheight;
639 case WM_DEVMODECHANGE: break; // FEATURE: color depth changed
640 case WM_DISPLAYCHANGE: break; // FEATURE: screen size changed
642 VScroll(((jfloat)((wParam & 0xffff0000) >> 16)) / (jfloat)120);
645 case WM_SYSKEYDOWN: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
647 KeyPressed(keyToString(wParam));
650 case WM_SYSKEYUP: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
652 KeyReleased(keyToString(wParam));
655 case WM_SETFOCUS: Focused(true); return 0;
656 case WM_KILLFOCUS: Focused(false); return 0;
657 case WM_LBUTTONDBLCLK: DoubleClick(1); return 0;
658 case WM_RBUTTONDBLCLK: DoubleClick(2); return 0;
659 case WM_MBUTTONDBLCLK: DoubleClick(3); return 0;
660 case WM_LBUTTONDOWN: Press(1); return 0;
661 case WM_RBUTTONDOWN: Press(2); return 0;
662 case WM_MBUTTONDOWN: Press(3); return 0;
663 case WM_CLOSE: Close(); return 0;
664 case WM_ERASEBKGND: return 0;
669 Release(iMsg == WM_LBUTTONUP ? 1 : (iMsg == WM_RBUTTONUP ? 2 : 3));
670 if (captured && !inside) {
672 SetCursor((HCURSOR)previous_cursor);
677 GetClientRect((HWND)hwnd, &rect);
678 PosChange(rect.left, rect.top);
682 if (wParam == SIZE_MINIMIZED) {
683 if (maximized) Maximized(false);
685 } else if (wParam == SIZE_MAXIMIZED) {
686 if (minimized) Minimized(false);
689 if (minimized) Minimized(false);
690 if (maximized) Maximized(false);
692 // deliberately fall through to WM_SIZING
695 GetClientRect((HWND)hwnd, &rect);
696 SizeChange(rect.right - rect.left, rect.bottom - rect.top);
700 if (mousex == lParam & 0xFFFF && mousey == (lParam & 0xFFFF0000) >> 16) return 0;
702 GetClientRect((HWND)hwnd, &rect);
703 point.x = mouse_x = lParam & 0xFFFF;
704 point.y = mouse_y = (lParam & 0xFFFF0000) >> 16;
705 ClientToScreen((HWND)hwnd, &point);
706 hwnd2 = WindowFromPoint(point);
708 newinside = hwnd2 == (HWND)hwnd && mouse_x > 0 && mouse_y > 0 && mouse_x < (rect.right - rect.left) && mouse_y < (rect.bottom - rect.top) ? 1 : 0;
710 Move(mouse_x, mouse_y);
712 if (newinside && !inside) {
714 SetCapture((HWND)hwnd);
716 previous_cursor = (jint)GetCursor();
717 PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0);
719 } else if (!newinside && inside) {
721 if (!button1 && !button2 && !button3) {
724 SetCursor((HCURSOR)previous_cursor);
730 case WM_USER_SETCURSOR:
731 // I can't find documentation of this anywhere, but in my experience, an event must be received between an invocation
732 // of SetCapture() and SetCursor(). Furthermore, it seems that after calling SetCursor(), the cursor will not change until a
733 // return from WndProc (ie the completion of the processing of some event). Hence, we use WM_USER to indicate that the screen
734 // cursor should be re-set to "current_cursor".
735 SetCursor((HCURSOR)current_cursor);
738 case WM_USER_DISPOSE:
739 // used to signal that we should destroy ourselves
740 DestroyWindow((HWND)hwnd);
743 case WM_GETMINMAXINFO:
744 GetClientRect((HWND)hwnd, &client_rect);
745 GetWindowRect((HWND)hwnd, &window_rect);
746 addwidth = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left);
747 addheight = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top);
748 mmi = (MINMAXINFO*)lParam;
749 mmi->ptMinTrackSize.x = ((uint32_t)root->minwidth) + addwidth;
750 mmi->ptMinTrackSize.y = ((uint32_t)root->minheight) + addheight;
751 resizable = !((root->minwidth == root->maxwidth) && (root->minheight == root->maxheight));
752 mmi->ptMaxTrackSize.x = resizable ? org::ibex::plat::Win32::getScreenWidth() : mmi->ptMinTrackSize.x;
753 mmi->ptMaxTrackSize.y = resizable ? org::ibex::plat::Win32::getScreenHeight() : mmi->ptMinTrackSize.y;
758 BeginPaint((HWND)org::ibex::plat::Win32$Win32Surface::hwnd, &ps);
759 Dirty(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top);
760 EndPaint((HWND)org::ibex::plat::Win32$Win32Surface::hwnd, &ps);
766 return DefWindowProc((HWND)hwnd, iMsg, wParam, lParam);
770 // Key Translator Helper /////////////////////////////////////////////////////////////////////
772 static char keyarr [256] = { 0 };
773 static jstring keyToString(WPARAM wParam) {
775 keyarr[VK_CAPITAL] = GetKeyState(VK_CAPITAL);
776 keyarr[VK_LSHIFT] = GetKeyState(VK_LSHIFT);
777 keyarr[VK_RSHIFT] = GetKeyState(VK_RSHIFT);
778 keyarr[VK_SHIFT] = GetKeyState(VK_SHIFT);
780 if (ToAsciiEx(wParam, 0, (BYTE*)keyarr, (WORD*)arr, 0, GetKeyboardLayout(0)) == 1) {
782 case '\t': return JvNewStringLatin1("tab");
783 case 0x1b: return JvNewStringLatin1("escape");
784 case '\n': return JvNewStringLatin1("enter");
785 case '\r': return JvNewStringLatin1("enter");
786 case 0x08: return JvNewStringLatin1("back_space");
787 default: return JvNewStringLatin1(arr, 1);
790 } else switch (wParam) {
791 case VK_CLEAR: return JvNewStringLatin1("clear");
792 case VK_SHIFT: return JvNewStringLatin1("shift");
793 case VK_CONTROL: return JvNewStringLatin1("control");
794 case VK_CAPITAL: return JvNewStringLatin1("caps_lock");
795 case VK_MENU: return JvNewStringLatin1("alt");
796 case VK_PAUSE: return JvNewStringLatin1("pause");
797 case VK_PRIOR: return JvNewStringLatin1("page_up");
798 case VK_NEXT: return JvNewStringLatin1("page_down");
799 case VK_END: return JvNewStringLatin1("end");
800 case VK_HOME: return JvNewStringLatin1("home");
801 case VK_LEFT: return JvNewStringLatin1("left");
802 case VK_UP: return JvNewStringLatin1("up");
803 case VK_RIGHT: return JvNewStringLatin1("right");
804 case VK_DOWN: return JvNewStringLatin1("down");
805 case VK_INSERT: return JvNewStringLatin1("insert");
806 case VK_DELETE: return JvNewStringLatin1("delete");
807 case VK_NUMPAD0: return JvNewStringLatin1("numpad0");
808 case VK_NUMPAD1: return JvNewStringLatin1("numpad1");
809 case VK_NUMPAD2: return JvNewStringLatin1("numpad2");
810 case VK_NUMPAD3: return JvNewStringLatin1("numpad3");
811 case VK_NUMPAD4: return JvNewStringLatin1("numpad4");
812 case VK_NUMPAD5: return JvNewStringLatin1("numpad5");
813 case VK_NUMPAD6: return JvNewStringLatin1("numpad6");
814 case VK_NUMPAD7: return JvNewStringLatin1("numpad7");
815 case VK_NUMPAD8: return JvNewStringLatin1("numpad8");
816 case VK_NUMPAD9: return JvNewStringLatin1("numpad9");
817 case VK_F1: return JvNewStringLatin1("f1");
818 case VK_F2: return JvNewStringLatin1("f2");
819 case VK_F3: return JvNewStringLatin1("f3");
820 case VK_F4: return JvNewStringLatin1("f4");
821 case VK_F5: return JvNewStringLatin1("f5");
822 case VK_F6: return JvNewStringLatin1("f6");
823 case VK_F7: return JvNewStringLatin1("f7");
824 case VK_F8: return JvNewStringLatin1("f8");
825 case VK_F9: return JvNewStringLatin1("f9");
826 case VK_F10: return JvNewStringLatin1("f10");
827 case VK_F11: return JvNewStringLatin1("f11");
828 case VK_F12: return JvNewStringLatin1("f12");
829 case VK_NUMLOCK: return JvNewStringLatin1("num_lock");
830 case VK_SCROLL: return JvNewStringLatin1("scroll_lock");
831 case VK_LSHIFT: return JvNewStringLatin1("shift");
832 case VK_RSHIFT: return JvNewStringLatin1("shift");
833 case VK_LCONTROL: return JvNewStringLatin1("control");
834 case VK_RCONTROL: return JvNewStringLatin1("control");