/* * Copyright 2011-2014 Branimir Karadzic. All rights reserved. * License: http://www.opensource.org/licenses/BSD-2-Clause */ #include "entry_p.h" #if ENTRY_CONFIG_USE_NATIVE && BX_PLATFORM_WINDOWS #include #include #include #include #include #include #define WM_USER_CREATE_WINDOW (WM_USER+0) #define WM_USER_SET_WINDOW_SIZE (WM_USER+1) #define WM_USER_TOGGLE_WINDOW_FRAME (WM_USER+2) #define WM_USER_MOUSE_LOCK (WM_USER+3) namespace entry { struct TranslateKeyModifiers { int m_vk; Modifier::Enum m_modifier; }; static const TranslateKeyModifiers s_translateKeyModifiers[8] = { { VK_LMENU, Modifier::LeftAlt }, { VK_RMENU, Modifier::RightAlt }, { VK_LCONTROL, Modifier::LeftCtrl }, { VK_RCONTROL, Modifier::RightCtrl }, { VK_LSHIFT, Modifier::LeftShift }, { VK_RSHIFT, Modifier::RightShift }, { VK_LWIN, Modifier::LeftMeta }, { VK_RWIN, Modifier::RightMeta }, }; static uint8_t translateKeyModifiers() { uint8_t modifiers = 0; for (uint32_t ii = 0; ii < BX_COUNTOF(s_translateKeyModifiers); ++ii) { const TranslateKeyModifiers& tkm = s_translateKeyModifiers[ii]; modifiers |= 0 > GetKeyState(tkm.m_vk) ? tkm.m_modifier : Modifier::None; } return modifiers; } static uint8_t s_translateKey[256]; static Key::Enum translateKey(WPARAM _wparam) { return (Key::Enum)s_translateKey[_wparam&0xff]; } struct MainThreadEntry { int m_argc; char** m_argv; static int32_t threadFunc(void* _userData); }; struct Context { Context() : m_mz(0) , m_frame(true) , m_mouseLock(NULL) , m_init(false) , m_exit(false) { memset(s_translateKey, 0, sizeof(s_translateKey) ); s_translateKey[VK_ESCAPE] = Key::Esc; s_translateKey[VK_RETURN] = Key::Return; s_translateKey[VK_TAB] = Key::Tab; s_translateKey[VK_BACK] = Key::Backspace; s_translateKey[VK_SPACE] = Key::Space; s_translateKey[VK_UP] = Key::Up; s_translateKey[VK_DOWN] = Key::Down; s_translateKey[VK_LEFT] = Key::Left; s_translateKey[VK_RIGHT] = Key::Right; s_translateKey[VK_PRIOR] = Key::PageUp; s_translateKey[VK_NEXT] = Key::PageUp; s_translateKey[VK_HOME] = Key::Home; s_translateKey[VK_END] = Key::End; s_translateKey[VK_SNAPSHOT] = Key::Print; s_translateKey[VK_OEM_PLUS] = Key::Plus; s_translateKey[VK_OEM_MINUS] = Key::Minus; s_translateKey[VK_F1] = Key::F1; s_translateKey[VK_F2] = Key::F2; s_translateKey[VK_F3] = Key::F3; s_translateKey[VK_F4] = Key::F4; s_translateKey[VK_F5] = Key::F5; s_translateKey[VK_F6] = Key::F6; s_translateKey[VK_F7] = Key::F7; s_translateKey[VK_F8] = Key::F8; s_translateKey[VK_F9] = Key::F9; s_translateKey[VK_F10] = Key::F10; s_translateKey[VK_F11] = Key::F11; s_translateKey[VK_F12] = Key::F12; s_translateKey[VK_NUMPAD0] = Key::NumPad0; s_translateKey[VK_NUMPAD1] = Key::NumPad1; s_translateKey[VK_NUMPAD2] = Key::NumPad2; s_translateKey[VK_NUMPAD3] = Key::NumPad3; s_translateKey[VK_NUMPAD4] = Key::NumPad4; s_translateKey[VK_NUMPAD5] = Key::NumPad5; s_translateKey[VK_NUMPAD6] = Key::NumPad6; s_translateKey[VK_NUMPAD7] = Key::NumPad7; s_translateKey[VK_NUMPAD8] = Key::NumPad8; s_translateKey[VK_NUMPAD9] = Key::NumPad9; s_translateKey['0'] = Key::Key0; s_translateKey['1'] = Key::Key1; s_translateKey['2'] = Key::Key2; s_translateKey['3'] = Key::Key3; s_translateKey['4'] = Key::Key4; s_translateKey['5'] = Key::Key5; s_translateKey['6'] = Key::Key6; s_translateKey['7'] = Key::Key7; s_translateKey['8'] = Key::Key8; s_translateKey['9'] = Key::Key9; s_translateKey['A'] = Key::KeyA; s_translateKey['B'] = Key::KeyB; s_translateKey['C'] = Key::KeyC; s_translateKey['D'] = Key::KeyD; s_translateKey['E'] = Key::KeyE; s_translateKey['F'] = Key::KeyF; s_translateKey['G'] = Key::KeyG; s_translateKey['H'] = Key::KeyH; s_translateKey['I'] = Key::KeyI; s_translateKey['J'] = Key::KeyJ; s_translateKey['K'] = Key::KeyK; s_translateKey['L'] = Key::KeyL; s_translateKey['M'] = Key::KeyM; s_translateKey['N'] = Key::KeyN; s_translateKey['O'] = Key::KeyO; s_translateKey['P'] = Key::KeyP; s_translateKey['Q'] = Key::KeyQ; s_translateKey['R'] = Key::KeyR; s_translateKey['S'] = Key::KeyS; s_translateKey['T'] = Key::KeyT; s_translateKey['U'] = Key::KeyU; s_translateKey['V'] = Key::KeyV; s_translateKey['W'] = Key::KeyW; s_translateKey['X'] = Key::KeyX; s_translateKey['Y'] = Key::KeyY; s_translateKey['Z'] = Key::KeyZ; } int32_t run(int _argc, char** _argv) { SetDllDirectory("."); HINSTANCE instance = (HINSTANCE)GetModuleHandle(NULL); WNDCLASSEX wnd; memset(&wnd, 0, sizeof(wnd) ); wnd.cbSize = sizeof(wnd); wnd.style = CS_HREDRAW | CS_VREDRAW; wnd.lpfnWndProc = wndProc; wnd.hInstance = instance; wnd.hIcon = LoadIcon(NULL, IDI_APPLICATION); wnd.hCursor = LoadCursor(NULL, IDC_ARROW); wnd.lpszClassName = "bgfx"; wnd.hIconSm = LoadIcon(NULL, IDI_APPLICATION); RegisterClassExA(&wnd); m_windowAlloc.alloc(); m_hwnd[0] = CreateWindowA("bgfx" , "BGFX" , WS_OVERLAPPEDWINDOW|WS_VISIBLE , 0 , 0 , ENTRY_DEFAULT_WIDTH , ENTRY_DEFAULT_HEIGHT , NULL , NULL , instance , 0 ); bgfx::winSetHwnd(m_hwnd[0]); adjust(m_hwnd[0], ENTRY_DEFAULT_WIDTH, ENTRY_DEFAULT_HEIGHT, true); m_width = ENTRY_DEFAULT_WIDTH; m_height = ENTRY_DEFAULT_HEIGHT; m_oldWidth = ENTRY_DEFAULT_WIDTH; m_oldHeight = ENTRY_DEFAULT_HEIGHT; MainThreadEntry mte; mte.m_argc = _argc; mte.m_argv = _argv; bx::Thread thread; thread.init(mte.threadFunc, &mte); m_init = true; m_eventQueue.postSizeEvent(findHandle(m_hwnd[0]), m_width, m_height); MSG msg; msg.message = WM_NULL; while (!m_exit) { WaitMessage(); while (0 != PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE) ) { TranslateMessage(&msg); DispatchMessage(&msg); } } thread.shutdown(); DestroyWindow(m_hwnd[0]); return 0; } LRESULT process(HWND _hwnd, UINT _id, WPARAM _wparam, LPARAM _lparam) { if (m_init) { switch (_id) { case WM_USER_CREATE_WINDOW: { uint32_t width = GET_X_LPARAM(_lparam); uint32_t height = GET_Y_LPARAM(_lparam); HWND hwnd = CreateWindowA("bgfx" , "" , WS_OVERLAPPEDWINDOW|WS_VISIBLE , 0 , 0 , width , height , NULL , NULL , (HINSTANCE)GetModuleHandle(NULL) , 0 ); m_hwnd[_wparam] = hwnd; WindowHandle handle = { (uint16_t)_wparam }; m_eventQueue.postWindowEvent(handle, hwnd); } break; case WM_USER_SET_WINDOW_SIZE: { uint32_t width = GET_X_LPARAM(_lparam); uint32_t height = GET_Y_LPARAM(_lparam); adjust(m_hwnd[_wparam], width, height, true); } break; case WM_USER_TOGGLE_WINDOW_FRAME: { if (m_frame) { m_oldWidth = m_width; m_oldHeight = m_height; } adjust(m_hwnd[_wparam], m_oldWidth, m_oldHeight, !m_frame); } break; case WM_USER_MOUSE_LOCK: setMouseLock(_hwnd, !!_lparam); break; case WM_DESTROY: break; case WM_QUIT: case WM_CLOSE: m_eventQueue.postWindowEvent(findHandle(_hwnd) ); if (_hwnd == m_hwnd[0]) { m_exit = true; m_eventQueue.postExitEvent(); } break; case WM_SIZING: { RECT& rect = *(RECT*)_lparam; uint32_t width = rect.right - rect.left - m_frameWidth; uint32_t height = rect.bottom - rect.top - m_frameHeight; // Recalculate size according to aspect ratio switch (_wparam) { case WMSZ_LEFT: case WMSZ_RIGHT: { float aspectRatio = 1.0f/m_aspectRatio; width = bx::uint32_max(ENTRY_DEFAULT_WIDTH/4, width); height = uint32_t(float(width)*aspectRatio); } break; default: { float aspectRatio = m_aspectRatio; height = bx::uint32_max(ENTRY_DEFAULT_HEIGHT/4, height); width = uint32_t(float(height)*aspectRatio); } break; } // Recalculate position using different anchor points switch(_wparam) { case WMSZ_LEFT: case WMSZ_TOPLEFT: case WMSZ_BOTTOMLEFT: rect.left = rect.right - width - m_frameWidth; rect.bottom = rect.top + height + m_frameHeight; break; default: rect.right = rect.left + width + m_frameWidth; rect.bottom = rect.top + height + m_frameHeight; break; } m_eventQueue.postSizeEvent(findHandle(_hwnd), m_width, m_height); } return 0; case WM_SIZE: { uint32_t width = GET_X_LPARAM(_lparam); uint32_t height = GET_Y_LPARAM(_lparam); m_width = width; m_height = height; m_eventQueue.postSizeEvent(findHandle(_hwnd), m_width, m_height); } break; case WM_SYSCOMMAND: switch (_wparam) { case SC_MINIMIZE: case SC_RESTORE: { HWND parent = GetWindow(_hwnd, GW_OWNER); if (NULL != parent) { PostMessage(parent, _id, _wparam, _lparam); } } } break; case WM_MOUSEMOVE: { int32_t mx = GET_X_LPARAM(_lparam); int32_t my = GET_Y_LPARAM(_lparam); if (_hwnd == m_mouseLock) { mx -= m_mx; my -= m_my; if (0 == mx && 0 == my) { break; } setMousePos(_hwnd, m_mx, m_my); } m_eventQueue.postMouseEvent(findHandle(_hwnd), mx, my, m_mz); } break; case WM_MOUSEWHEEL: { POINT pt = { GET_X_LPARAM(_lparam), GET_Y_LPARAM(_lparam) }; ScreenToClient(_hwnd, &pt); int32_t mx = pt.x; int32_t my = pt.y; m_mz += GET_WHEEL_DELTA_WPARAM(_wparam); m_eventQueue.postMouseEvent(findHandle(_hwnd), mx, my, m_mz); } break; case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_LBUTTONDBLCLK: { int32_t mx = GET_X_LPARAM(_lparam); int32_t my = GET_Y_LPARAM(_lparam); m_eventQueue.postMouseEvent(findHandle(_hwnd), mx, my, m_mz, MouseButton::Left, _id == WM_LBUTTONDOWN); } break; case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDBLCLK: { int32_t mx = GET_X_LPARAM(_lparam); int32_t my = GET_Y_LPARAM(_lparam); m_eventQueue.postMouseEvent(findHandle(_hwnd), mx, my, m_mz, MouseButton::Middle, _id == WM_MBUTTONDOWN); } break; case WM_RBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: { int32_t mx = GET_X_LPARAM(_lparam); int32_t my = GET_Y_LPARAM(_lparam); m_eventQueue.postMouseEvent(findHandle(_hwnd), mx, my, m_mz, MouseButton::Right, _id == WM_RBUTTONDOWN); } break; case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_KEYUP: case WM_SYSKEYUP: { uint8_t modifiers = translateKeyModifiers(); Key::Enum key = translateKey(_wparam); WindowHandle handle = findHandle(_hwnd); if (Key::Print == key && 0x3 == ( (uint32_t)(_lparam)>>30) ) { // VK_SNAPSHOT doesn't generate keydown event. Fire on down event when previous // key state bit is set to 1 and transition state bit is set to 1. // // http://msdn.microsoft.com/en-us/library/windows/desktop/ms646280%28v=vs.85%29.aspx m_eventQueue.postKeyEvent(handle, key, modifiers, true); } m_eventQueue.postKeyEvent(handle, key, modifiers, _id == WM_KEYDOWN || _id == WM_SYSKEYDOWN); } break; default: break; } } return DefWindowProc(_hwnd, _id, _wparam, _lparam); } WindowHandle findHandle(HWND _hwnd) { bx::LwMutexScope scope(m_lock); for (uint32_t ii = 0, num = m_windowAlloc.getNumHandles(); ii < num; ++ii) { uint16_t idx = m_windowAlloc.getHandleAt(ii); if (_hwnd == m_hwnd[idx]) { WindowHandle handle = { idx }; return handle; } } WindowHandle invalid = { UINT16_MAX }; return invalid; } void adjust(HWND _hwnd, uint32_t _width, uint32_t _height, bool _windowFrame) { m_width = _width; m_height = _height; m_aspectRatio = float(_width)/float(_height); ShowWindow(_hwnd, SW_SHOWNORMAL); RECT rect; RECT newrect = {0, 0, (LONG)_width, (LONG)_height}; DWORD style = WS_POPUP|WS_SYSMENU; if (m_frame) { GetWindowRect(_hwnd, &m_rect); m_style = GetWindowLong(_hwnd, GWL_STYLE); } if (_windowFrame) { rect = m_rect; style = m_style; } else { #if defined(__MINGW32__) rect = m_rect; style = m_style; #else HMONITOR monitor = MonitorFromWindow(_hwnd, MONITOR_DEFAULTTONEAREST); MONITORINFO mi; mi.cbSize = sizeof(mi); GetMonitorInfo(monitor, &mi); newrect = mi.rcMonitor; rect = mi.rcMonitor; #endif // !defined(__MINGW__) } SetWindowLong(_hwnd, GWL_STYLE, style); uint32_t prewidth = newrect.right - newrect.left; uint32_t preheight = newrect.bottom - newrect.top; AdjustWindowRect(&newrect, style, FALSE); m_frameWidth = (newrect.right - newrect.left) - prewidth; m_frameHeight = (newrect.bottom - newrect.top) - preheight; UpdateWindow(_hwnd); if (rect.left == -32000 || rect.top == -32000) { rect.left = 0; rect.top = 0; } int32_t left = rect.left; int32_t top = rect.top; int32_t width = (newrect.right-newrect.left); int32_t height = (newrect.bottom-newrect.top); if (!_windowFrame) { float aspectRatio = 1.0f/m_aspectRatio; width = bx::uint32_max(ENTRY_DEFAULT_WIDTH/4, width); height = uint32_t(float(width)*aspectRatio); left = newrect.left+(newrect.right-newrect.left-width)/2; top = newrect.top+(newrect.bottom-newrect.top-height)/2; } HWND parent = GetWindow(_hwnd, GW_OWNER); if (NULL != parent) { if (_windowFrame) { SetWindowPos(parent , HWND_TOP , -32000 , -32000 , 0 , 0 , SWP_SHOWWINDOW ); } else { SetWindowPos(parent , HWND_TOP , newrect.left , newrect.top , newrect.right-newrect.left , newrect.bottom-newrect.top , SWP_SHOWWINDOW ); } } SetWindowPos(_hwnd , HWND_TOP , left , top , width , height , SWP_SHOWWINDOW ); ShowWindow(_hwnd, SW_RESTORE); m_frame = _windowFrame; } void setMousePos(HWND _hwnd, int32_t _mx, int32_t _my) { POINT pt = { _mx, _my }; ClientToScreen(_hwnd, &pt); SetCursorPos(pt.x, pt.y); } void setMouseLock(HWND _hwnd, bool _lock) { if (_hwnd != m_mouseLock) { if (_lock) { m_mx = m_width/2; m_my = m_height/2; ShowCursor(false); setMousePos(_hwnd, m_mx, m_my); } else { setMousePos(_hwnd, m_mx, m_my); ShowCursor(true); } m_mouseLock = _hwnd; } } static LRESULT CALLBACK wndProc(HWND _hwnd, UINT _id, WPARAM _wparam, LPARAM _lparam); EventQueue m_eventQueue; bx::LwMutex m_lock; bx::HandleAllocT m_windowAlloc; HWND m_hwnd[ENTRY_CONFIG_MAX_WINDOWS]; RECT m_rect; DWORD m_style; uint32_t m_width; uint32_t m_height; uint32_t m_oldWidth; uint32_t m_oldHeight; uint32_t m_frameWidth; uint32_t m_frameHeight; float m_aspectRatio; int32_t m_mx; int32_t m_my; int32_t m_mz; bool m_frame; HWND m_mouseLock; bool m_init; bool m_exit; }; static Context s_ctx; LRESULT CALLBACK Context::wndProc(HWND _hwnd, UINT _id, WPARAM _wparam, LPARAM _lparam) { return s_ctx.process(_hwnd, _id, _wparam, _lparam); } const Event* poll() { return s_ctx.m_eventQueue.poll(); } void release(const Event* _event) { s_ctx.m_eventQueue.release(_event); } WindowHandle createWindow(uint32_t _width, uint32_t _height, const char* /*_title*/) { bx::LwMutexScope scope(s_ctx.m_lock); WindowHandle handle = { s_ctx.m_windowAlloc.alloc() }; if (UINT16_MAX != handle.idx) { PostMessage(s_ctx.m_hwnd[0], WM_USER_CREATE_WINDOW, handle.idx, (_height<<16) | (_width&0xffff) ); } return handle; } void destroyWindow(WindowHandle _handle) { PostMessage(s_ctx.m_hwnd[_handle.idx], WM_CLOSE, _handle.idx, 0); } void setWindowSize(WindowHandle _handle, uint32_t _width, uint32_t _height) { PostMessage(s_ctx.m_hwnd[_handle.idx], WM_USER_SET_WINDOW_SIZE, _handle.idx, (_height<<16) | (_width&0xffff) ); } void setWindowTitle(WindowHandle _handle, const char* _title) { SetWindowTextA(s_ctx.m_hwnd[_handle.idx], _title); // SetWindowTextA(GetWindow(s_ctx.m_hwnd[_handle.idx], GW_HWNDNEXT), _title); } void toggleWindowFrame(WindowHandle _handle) { PostMessage(s_ctx.m_hwnd[_handle.idx], WM_USER_TOGGLE_WINDOW_FRAME, _handle.idx, 0); } void setMouseLock(WindowHandle _handle, bool _lock) { PostMessage(s_ctx.m_hwnd[_handle.idx], WM_USER_MOUSE_LOCK, _handle.idx, _lock); } int32_t MainThreadEntry::threadFunc(void* _userData) { MainThreadEntry* self = (MainThreadEntry*)_userData; int32_t result = main(self->m_argc, self->m_argv); PostMessage(s_ctx.m_hwnd[0], WM_QUIT, 0, 0); return result; } } // namespace entry int main(int _argc, char** _argv) { using namespace entry; return s_ctx.run(_argc, _argv); } #endif // BX_PLATFORM_WINDOWS