//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "platformWin32/platformWin32.h" #include "platform/platform.h" #include "platformWin32/platformGL.h" #include "platform/platformVideo.h" #include "platformWin32/winOGLVideo.h" #include "platformWin32/winD3DVideo.h" #include "platformWin32/winV2Video.h" #include "platform/event.h" #include "console/console.h" #include "platformWin32/winConsole.h" #include "platformWin32/winDirectInput.h" #include "platform/gameInterface.h" #include "math/mRandom.h" #include "core/fileStream.h" #include "game/resource.h" #include "core/unicode.h" #include "gui/core/guiCanvas.h" //-------------------------------------- Resource Includes #include "dgl/gBitmap.h" #include "dgl/gFont.h" extern void createFontInit(); extern void createFontShutdown(); extern void installRedBookDevices(); extern void handleRedBookCallback(U32, U32); #ifdef UNICODE static const UTF16 *windowClassName = L"Darkstar Window Class"; static UTF16 windowName[256] = L"Darkstar Window"; #else static const char *windowClassName = "Darkstar Window Class"; static char windowName[256] = "Darkstar Window"; #endif static bool gWindowCreated = false; static bool windowNotActive = false; static MRandomLCG sgPlatRandom; static bool sgQueueEvents; // is keyboard input a standard (non-changing) VK keycode #define dIsStandardVK(c) (((0x08 <= (c)) && ((c) <= 0x12)) || \ ((c) == 0x1b) || \ ((0x20 <= (c)) && ((c) <= 0x2e)) || \ ((0x30 <= (c)) && ((c) <= 0x39)) || \ ((0x41 <= (c)) && ((c) <= 0x5a)) || \ ((0x70 <= (c)) && ((c) <= 0x7B))) extern U16 DIK_to_Key( U8 dikCode ); Win32PlatState winState; //-------------------------------------- Win32PlatState::Win32PlatState() { log_fp = NULL; hinstOpenGL = NULL; hinstGLU = NULL; hinstOpenAL = NULL; appWindow = NULL; appDC = NULL; appInstance = NULL; currentTime = 0; processId = 0; } static bool windowLocked = false; static BYTE keyboardState[256]; static bool mouseButtonState[3]; static bool capsLockDown = false; static S32 modifierKeys = 0; static bool windowActive = true; static Point2I lastCursorPos(0,0); static Point2I windowSize; static HANDLE gMutexHandle = NULL; static bool sgDoubleByteEnabled = false; //-------------------------------------- static const char *getMessageName(S32 msg) { switch(msg) { case WM_KEYDOWN: return "WM_KEYDOWN"; case WM_KEYUP: return "WM_KEYUP"; case WM_SYSKEYUP: return "WM_SYSKEYUP"; case WM_SYSKEYDOWN: return "WM_SYSKEYDOWN"; default: return "Unknown!!"; } } //-------------------------------------- bool Platform::excludeOtherInstances(const char *mutexName) { #ifdef UNICODE UTF16 b[512]; convertUTF8toUTF16((UTF8 *)mutexName, b, sizeof(b)); gMutexHandle = CreateMutex(NULL, true, b); #else gMutexHandle = CreateMutex(NULL, true, mutexName); #endif if(!gMutexHandle) return false; if(GetLastError() == ERROR_ALREADY_EXISTS) { CloseHandle(gMutexHandle); gMutexHandle = NULL; return false; } return true; } void Platform::restartInstance() { if( Game->isRunning() ) { //Con::errorf( "Error restarting instance, Game is still running!"); return; } STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) ); char cen_buf[2048]; GetModuleFileNameA( NULL, cen_buf, 2047); #ifdef UNICODE UTF16 b[512]; convertUTF8toUTF16((UTF8 *)cen_buf, b, sizeof(b)); #else const char* b = cen_buf; #endif // Start the child process. if( CreateProcess( b, NULL, // Command line NULL, // Process handle not inheritable NULL, // Thread handle not inheritable FALSE, // Set handle inheritance to FALSE 0, // No creation flags NULL, // Use parent's environment block NULL, // Use parent's starting directory &si, // Pointer to STARTUPINFO structure &pi ) // Pointer to PROCESS_INFORMATION structure != false ) { WaitForInputIdle( pi.hProcess, 5000 ); CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); } } ///just check if the app's global mutex exists, and if so, ///return true - otherwise, false. Should be called before ExcludeOther /// at very start of app execution. bool Platform::checkOtherInstances(const char *mutexName) { #ifdef TORQUE_MULTITHREAD HANDLE pMutex = NULL; #ifdef UNICODE UTF16 b[512]; convertUTF8toUTF16((UTF8 *)mutexName, b, sizeof(b)); pMutex = CreateMutex(NULL, true, b); #else pMutex = CreateMutex(NULL, true, mutexName); #endif if(!pMutex) return false; if(GetLastError() == ERROR_ALREADY_EXISTS) { //another mutex of the same name exists //close ours CloseHandle(pMutex); pMutex = NULL; return true; } CloseHandle(pMutex); pMutex = NULL; #endif //we don;t care, always false return false; } //-------------------------------------- void Platform::AlertOK(const char *windowTitle, const char *message) { ShowCursor(true); #ifdef UNICODE UTF16 m[1024], t[512]; convertUTF8toUTF16((UTF8 *)windowTitle, t, sizeof(t)); convertUTF8toUTF16((UTF8 *)message, m, sizeof(m)); MessageBox(NULL, m, t, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OK); #else MessageBox(NULL, message, windowTitle, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OK); #endif } //-------------------------------------- bool Platform::AlertOKCancel(const char *windowTitle, const char *message) { ShowCursor(true); #ifdef UNICODE UTF16 m[1024], t[512]; convertUTF8toUTF16((UTF8 *)windowTitle, t, sizeof(t)); convertUTF8toUTF16((UTF8 *)message, m, sizeof(m)); return MessageBox(NULL, m, t, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OKCANCEL) == IDOK; #else return MessageBox(NULL, message, windowTitle, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OKCANCEL) == IDOK; #endif } //-------------------------------------- bool Platform::AlertRetry(const char *windowTitle, const char *message) { ShowCursor(true); #ifdef UNICODE UTF16 m[1024], t[512]; convertUTF8toUTF16((UTF8 *)windowTitle, t, sizeof(t)); convertUTF8toUTF16((UTF8 *)message, m, sizeof(m)); return (MessageBox(NULL, m, t, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_RETRYCANCEL) == IDRETRY); #else return (MessageBox(NULL, message, windowTitle, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_RETRYCANCEL) == IDRETRY); #endif } //-------------------------------------- HIMC gIMEContext; static void InitInput() { #ifndef TORQUE_LIB #ifdef UNICODE gIMEContext = ImmGetContext(winState.appWindow); ImmReleaseContext( winState.appWindow, gIMEContext ); #endif #endif dMemset( keyboardState, 0, 256 ); dMemset( mouseButtonState, 0, sizeof( mouseButtonState ) ); capsLockDown = (GetKeyState(VK_CAPITAL) & 0x01); if (capsLockDown) { keyboardState[VK_CAPITAL] |= 0x01; } } //-------------------------------------- static void setMouseClipping() { ClipCursor(NULL); if(windowActive) { ShowCursor(false); RECT r; GetWindowRect(winState.appWindow, &r); if(windowLocked) { POINT p; GetCursorPos(&p); lastCursorPos.set(p.x - r.left, p.y - r.top); ClipCursor(&r); S32 centerX = (r.right + r.left) >> 1; S32 centerY = (r.bottom + r.top) >> 1; if(!windowNotActive) SetCursorPos(centerX, centerY); } else { if(!windowNotActive && false) SetCursorPos(lastCursorPos.x + r.left, lastCursorPos.y + r.top); } } else ShowCursor(true); } //-------------------------------------- static bool sgTaskbarHidden = false; static HWND sgTaskbar = NULL; static void hideTheTaskbar() { // if ( !sgTaskbarHidden ) // { // sgTaskbar = FindWindow( "Shell_TrayWnd", NULL ); // if ( sgTaskbar ) // { // APPBARDATA abData; // dMemset( &abData, 0, sizeof( abData ) ); // abData.cbSize = sizeof( abData ); // abData.hWnd = sgTaskbar; // SHAppBarMessage( ABM_REMOVE, &abData ); // //ShowWindow( sgTaskbar, SW_HIDE ); // sgTaskbarHidden = true; // } // } } static void restoreTheTaskbar() { // if ( sgTaskbarHidden ) // { // APPBARDATA abData; // dMemset( &abData, 0, sizeof( abData ) ); // abData.cbSize = sizeof( abData ); // abData.hWnd = sgTaskbar; // SHAppBarMessage( ABM_ACTIVATE, &abData ); // //ShowWindow( sgTaskbar, SW_SHOW ); // sgTaskbarHidden = false; // } } //-------------------------------------- void Platform::enableKeyboardTranslation(void) { #ifdef UNICODE // Con::printf("translating..."); ImmAssociateContext( winState.appWindow, winState.imeHandle ); #endif } //-------------------------------------- void Platform::disableKeyboardTranslation(void) { #ifdef UNICODE // Con::printf("not translating..."); ImmAssociateContext( winState.appWindow, NULL ); #endif } //-------------------------------------- void Platform::setWindowLocked(bool locked) { windowLocked = locked; setMouseClipping(); } //-------------------------------------- void Platform::minimizeWindow() { ShowWindow(winState.appWindow, SW_MINIMIZE); restoreTheTaskbar(); } //-------------------------------------- static void processKeyMessage(UINT message, WPARAM wParam, LPARAM lParam) { S32 repeatCount = lParam & 0xffff; S32 scanCode = (lParam >> 16) & 0xff; S32 nVirtkey = dIsStandardVK(wParam) ? TranslateOSKeyCode(wParam) : DIK_to_Key(scanCode); S32 keyCode; if ( wParam == VK_PROCESSKEY && sgDoubleByteEnabled ) keyCode = MapVirtualKey( scanCode, 1 ); // This is the REAL virtual key... else keyCode = wParam; bool extended = (lParam >> 24) & 1; bool repeat = (lParam >> 30) & 1; bool make = (message == WM_KEYDOWN || message == WM_SYSKEYDOWN); S32 newVirtKey = nVirtkey; switch(nVirtkey) { case KEY_ALT: newVirtKey = extended ? KEY_RALT : KEY_LALT; break; case KEY_CONTROL: newVirtKey = extended ? KEY_RCONTROL : KEY_LCONTROL; break; case KEY_SHIFT: newVirtKey = ( scanCode == 54 ) ? KEY_RSHIFT : KEY_LSHIFT; break; case KEY_RETURN: if ( extended ) newVirtKey = KEY_NUMPADENTER; break; } S32 modKey = modifierKeys; if(make) { switch (newVirtKey) { case KEY_LSHIFT: modifierKeys |= SI_LSHIFT; modKey = 0; break; case KEY_RSHIFT: modifierKeys |= SI_RSHIFT; modKey = 0; break; case KEY_LCONTROL: modifierKeys |= SI_LCTRL; modKey = 0; break; case KEY_RCONTROL: modifierKeys |= SI_RCTRL; modKey = 0; break; case KEY_LALT: modifierKeys |= SI_LALT; modKey = 0; break; case KEY_RALT: modifierKeys |= SI_RALT; modKey = 0; break; } if(nVirtkey == KEY_CAPSLOCK) { capsLockDown = !capsLockDown; if(capsLockDown) keyboardState[keyCode] |= 0x01; else keyboardState[keyCode] &= 0xFE; } keyboardState[keyCode] |= 0x80; } else { switch (newVirtKey) { case KEY_LSHIFT: modifierKeys &= ~SI_LSHIFT; modKey = 0; break; case KEY_RSHIFT: modifierKeys &= ~SI_RSHIFT; modKey = 0; break; case KEY_LCONTROL: modifierKeys &= ~SI_LCTRL; modKey = 0; break; case KEY_RCONTROL: modifierKeys &= ~SI_RCTRL; modKey = 0; break; case KEY_LALT: modifierKeys &= ~SI_LALT; modKey = 0; break; case KEY_RALT: modifierKeys &= ~SI_RALT; modKey = 0; break; } keyboardState[keyCode] &= 0x7f; } U16 ascii[3]; WORD asciiCode = 0; dMemset( &ascii, 0, sizeof( ascii ) ); S32 res = ToUnicode( keyCode, scanCode, keyboardState, ascii, 3, 0 ); // This should only happen on Window 9x/ME systems if (res == 0) res = ToAscii( keyCode, scanCode, keyboardState, ascii, 0 ); if (res == 2) { asciiCode = ascii[1]; } else if ((res == 1) || (res < 0)) { asciiCode = ascii[0]; } InputEvent event; event.deviceInst = 0; event.deviceType = KeyboardDeviceType; event.objType = SI_KEY; event.objInst = newVirtKey; event.action = make ? (repeat ? SI_REPEAT : SI_MAKE ) : SI_BREAK; event.modifier = modKey; event.ascii = asciiCode; event.fValue = make ? 1.0 : 0.0; #ifdef LOG_INPUT char keyName[25]; GetKeyNameText( lParam, keyName, 24 ); if ( event.action == SI_MAKE ) Input::log( "EVENT (Win): %s key pressed (Repeat count: %d). MODS:%c%c%c\n", keyName, repeatCount, ( modKey & SI_SHIFT ? 'S' : '.' ), ( modKey & SI_CTRL ? 'C' : '.' ), ( modKey & SI_ALT ? 'A' : '.' ) ); else Input::log( "EVENT (Win): %s key released.\n", keyName ); #endif Game->postEvent(event); } static void processCharMessage( WPARAM wParam, LPARAM lParam ) { TCHAR charCode = wParam; UTF16 tmpString[2] = { charCode, 0 }; UTF8 out[256]; convertUTF16toUTF8(tmpString, out, 250); // Con::printf("Got IME char code %x (%s)", charCode, out); InputEvent event; event.deviceInst = 0; event.deviceType = KeyboardDeviceType; event.objType = SI_KEY; event.action = SI_MAKE; event.ascii = charCode; // Deal with some silly cases like . This might be indicative of a // missing MapVirtualKey step, not quite sure how to deal with this. if(event.ascii == 0x8) event.objInst = KEY_BACKSPACE; // Note we abuse objInst. Game->postEvent(event); // And put it back up as well, they can't hold it! event.action = SI_BREAK; Game->postEvent(event); } static S32 mouseX = 0xFFFFFFFF; static S32 mouseY = 0xFFFFFFFF; //-------------------------------------- static void CheckCursorPos() { if(windowLocked && windowActive) { POINT mousePos; GetCursorPos(&mousePos); RECT r; GetWindowRect(winState.appWindow, &r); S32 centerX = (r.right + r.left) >> 1; S32 centerY = (r.bottom + r.top) >> 1; if(mousePos.x != centerX) { InputEvent event; event.deviceInst = 0; event.deviceType = MouseDeviceType; event.objType = SI_XAXIS; event.objInst = 0; event.action = SI_MOVE; event.modifier = modifierKeys; event.ascii = 0; event.fValue = (mousePos.x - centerX); Game->postEvent(event); } if(mousePos.y != centerY) { InputEvent event; event.deviceInst = 0; event.deviceType = MouseDeviceType; event.objType = SI_YAXIS; event.objInst = 0; event.action = SI_MOVE; event.modifier = modifierKeys; event.ascii = 0; event.fValue = (mousePos.y - centerY); Game->postEvent(event); } SetCursorPos(centerX, centerY); } } //-------------------------------------- static void mouseButtonEvent(S32 action, S32 objInst) { CheckCursorPos(); if(!windowLocked) { if(action == SI_MAKE) SetCapture(winState.appWindow); else ReleaseCapture(); } U32 buttonId = objInst - KEY_BUTTON0; if ( buttonId < 3 ) mouseButtonState[buttonId] = ( action == SI_MAKE ) ? true : false; InputEvent event; event.deviceInst = 0; event.deviceType = MouseDeviceType; event.objType = SI_BUTTON; event.objInst = objInst; event.action = action; event.modifier = modifierKeys; event.ascii = 0; event.fValue = action == SI_MAKE ? 1.0 : 0.0; #ifdef LOG_INPUT if ( action == SI_MAKE ) Input::log( "EVENT (Win): mouse button%d pressed. MODS:%c%c%c\n", buttonId, ( modifierKeys & SI_SHIFT ? 'S' : '.' ), ( modifierKeys & SI_CTRL ? 'C' : '.' ), ( modifierKeys & SI_ALT ? 'A' : '.' ) ); else Input::log( "EVENT (Win): mouse button%d released.\n", buttonId ); #endif Game->postEvent(event); } //-------------------------------------- static void mouseWheelEvent( S32 delta ) { static S32 _delta = 0; _delta += delta; if ( abs( delta ) >= WHEEL_DELTA ) { _delta = 0; InputEvent event; event.deviceInst = 0; event.deviceType = MouseDeviceType; event.objType = SI_ZAXIS; event.objInst = 0; event.action = SI_MOVE; event.modifier = modifierKeys; event.ascii = 0; event.fValue = delta; #ifdef LOG_INPUT Input::log( "EVENT (Win): mouse wheel moved. delta = %d\n", delta ); #endif Game->postEvent( event ); } } struct WinMessage { UINT message; WPARAM wParam; LPARAM lParam; WinMessage(UINT m, WPARAM w, LPARAM l) : message(m), wParam(w), lParam(l) {} }; Vector sgWinMessages; #ifndef PBT_APMQUERYSUSPEND #define PBT_APMQUERYSUSPEND 0x0000 #endif bool sgKeyboardStateDirty = false; //-------------------------------------- static LRESULT PASCAL WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch ( message ) { case WM_POWERBROADCAST: { if(wParam == PBT_APMQUERYSUSPEND) return BROADCAST_QUERY_DENY; return true; } case WM_ACTIVATEAPP: if ((bool) wParam) { Video::reactivate(); ShowCursor(false); if ( Video::isFullScreen() ) hideTheTaskbar(); // HACK: Windows 98 (after switching from fullscreen to windowed mode) SetForegroundWindow( winState.appWindow ); // If our keyboard state is dirty, clear it if( sgKeyboardStateDirty == true ) { sgKeyboardStateDirty = false; InitInput(); } } else { // Window lost focus so set a dirty flag on the keyboard state if ( lParam == 0 ) sgKeyboardStateDirty = true; Video::deactivate(); restoreTheTaskbar(); } break; case WM_SYSCOMMAND: switch(wParam) { case SC_KEYMENU: case SC_SCREENSAVE: case SC_MONITORPOWER: if(GetForegroundWindow() == winState.appWindow) { return 0; } break; } break; case WM_ACTIVATE: windowActive = LOWORD(wParam) != WA_INACTIVE; if( Game->isRunning() ) { if (Con::isFunction("windowFocusChanged")) Con::executef( 2, "windowFocusChanged", windowActive ? "1" : "0" ); } if ( windowActive ) { setMouseClipping(); windowNotActive = false; Game->refreshWindow(); Input::activate(); } else { setMouseClipping(); windowNotActive = true; DInputManager* mgr = dynamic_cast( Input::getManager() ); if ( !mgr || !mgr->isMouseActive() ) { // Deactivate all the mouse triggers: for ( U32 i = 0; i < 3; i++ ) { if ( mouseButtonState[i] ) mouseButtonEvent( SI_BREAK, KEY_BUTTON0 + i ); } } Input::deactivate(); } break; case WM_MOVE: Game->refreshWindow(); break; case MM_MCINOTIFY: handleRedBookCallback(wParam, lParam); break; //case WM_DESTROY: case WM_CLOSE: PostQuitMessage(0); break; #ifdef UNICODE case WM_INPUTLANGCHANGE: // Con::printf("IME lang change"); break; case WM_IME_SETCONTEXT: // Con::printf("IME set context"); break; case WM_IME_COMPOSITION: // Con::printf("IME comp"); break; case WM_IME_STARTCOMPOSITION: // Con::printf("IME start comp"); break; #endif default: { if (sgQueueEvents) { WinMessage msg(message,wParam,lParam); sgWinMessages.push_front(msg); } } } return DefWindowProc(hWnd, message, wParam, lParam); } //-------------------------------------- static void OurDispatchMessages() { WinMessage msg(0,0,0); UINT message; WPARAM wParam; LPARAM lParam; DInputManager* mgr = dynamic_cast( Input::getManager() ); while (sgWinMessages.size()) { msg = sgWinMessages.front(); sgWinMessages.pop_front(); message = msg.message; wParam = msg.wParam; lParam = msg.lParam; if ( !mgr || !mgr->isMouseActive() ) { switch ( message ) { #ifdef UNICODE case WM_IME_COMPOSITION: { // Con::printf("OMG IME comp"); break; } case WM_IME_ENDCOMPOSITION: { // Con::printf("OMG IME end comp"); break; } case WM_IME_NOTIFY: { // Con::printf("OMG IME notify"); break; } case WM_IME_CHAR: processCharMessage( wParam, lParam ); break; #endif // We want to show the system cursor whenever we leave // our window, and it'd be simple, except for one problem: // showcursor isn't a toggle. so, keep hammering it until // the cursor is *actually* going to be shown. case WM_NCMOUSEMOVE: { while(ShowCursor(TRUE) < 0); break; } case WM_MOUSEMOVE: // keep trying until we actually show it while(ShowCursor(FALSE) >= 0); if ( !windowLocked ) { MouseMoveEvent event; S16 xPos = LOWORD(lParam); S16 yPos = HIWORD(lParam); event.xPos = xPos; // horizontal position of cursor event.yPos = yPos; // vertical position of cursor event.modifier = modifierKeys; #ifdef LOG_INPUT #ifdef LOG_MOUSEMOVE Input::log( "EVENT (Win): mouse move to (%d, %d).\n", event.xPos, event.yPos ); #endif #endif Game->postEvent(event); } break; case WM_LBUTTONDOWN: mouseButtonEvent(SI_MAKE, KEY_BUTTON0); break; case WM_MBUTTONDOWN: mouseButtonEvent(SI_MAKE, KEY_BUTTON2); break; case WM_RBUTTONDOWN: mouseButtonEvent(SI_MAKE, KEY_BUTTON1); break; case WM_LBUTTONUP: mouseButtonEvent(SI_BREAK, KEY_BUTTON0); break; case WM_MBUTTONUP: mouseButtonEvent(SI_BREAK, KEY_BUTTON2); break; case WM_RBUTTONUP: mouseButtonEvent(SI_BREAK, KEY_BUTTON1); break; case WM_MOUSEWHEEL: mouseWheelEvent( (S16) HIWORD( wParam ) ); break; } } if ( !mgr || !mgr->isKeyboardActive() ) { switch ( message ) { case WM_KEYUP: case WM_SYSKEYUP: case WM_KEYDOWN: case WM_SYSKEYDOWN: processKeyMessage(message, wParam, lParam); break; } } } } //-------------------------------------- static bool ProcessMessages() { MSG msg; while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if(msg.message == WM_QUIT) return false; TranslateMessage(&msg); DispatchMessage(&msg); OurDispatchMessages(); } return true; } //-------------------------------------- void Platform::process() { DInputManager* mgr = dynamic_cast( Input::getManager() ); if ( !mgr || !mgr->isMouseActive() ) CheckCursorPos(); WindowsConsole->process(); if(!ProcessMessages()) { // generate a quit event Event quitEvent; quitEvent.type = QuitEventType; Game->postEvent(quitEvent); } HWND window = GetForegroundWindow(); if (window && window != winState.appWindow && gWindowCreated) { // check to see if we are in the foreground or not // if not Sleep for a bit or until a Win32 message/input is recieved DWORD foregroundProcessId; GetWindowThreadProcessId(window, &foregroundProcessId); if (foregroundProcessId != winState.processId) MsgWaitForMultipleObjects(0, NULL, false, getBackgroundSleepTime(), QS_ALLINPUT); } // if there's no window, we sleep 1, otherwise we sleep 0 else if(!Game->isJournalReading() && gWindowCreated ) Sleep( 1 ); // give others some process time if necessary... Input::process(); } extern U32 calculateCRC(void * buffer, S32 len, U32 crcVal ); #if defined(TORQUE_DEBUG) || defined(INTERNAL_RELEASE) static U32 stubCRC = 0; #else static U32 stubCRC = 0xEA63F56C; #endif //-------------------------------------- static void InitWindowClass() { WNDCLASS wc; dMemset(&wc, 0, sizeof(wc)); wc.style = CS_OWNDC; wc.lpfnWndProc = WindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = winState.appInstance; wc.hIcon = LoadIcon(winState.appInstance, MAKEINTRESOURCE(IDI_ICON1)); wc.hCursor = LoadCursor (NULL,IDC_ARROW); wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH); wc.lpszMenuName = 0; wc.lpszClassName = windowClassName; RegisterClass( &wc ); // Curtain window class: wc.lpfnWndProc = DefWindowProc; wc.hCursor = NULL; wc.hbrBackground = (HBRUSH) GetStockObject(GRAY_BRUSH); wc.lpszClassName = dT("Curtain"); RegisterClass( &wc ); } //-------------------------------------- Resolution Video::getDesktopResolution() { Resolution Result; RECT clientRect; HWND hDesktop = GetDesktopWindow(); HDC hDeskDC = GetDC( hDesktop ); // Retrieve Resolution Information. Result.bpp = winState.desktopBitsPixel = GetDeviceCaps( hDeskDC, BITSPIXEL ); Result.w = winState.desktopWidth = GetDeviceCaps( hDeskDC, HORZRES ); Result.h = winState.desktopHeight = GetDeviceCaps( hDeskDC, VERTRES ); // Release Device Context. ReleaseDC( hDesktop, hDeskDC ); SystemParametersInfo(SPI_GETWORKAREA, 0, &clientRect, 0); winState.desktopClientWidth = clientRect.right; winState.desktopClientHeight = clientRect.bottom; // Return Result; return Result; } //-------------------------------------- HWND CreateOpenGLWindow( U32 width, U32 height, bool fullScreen ) { S32 windowStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS; S32 exWindowStyle = 0; if ( fullScreen ) windowStyle |= ( WS_POPUP | WS_MAXIMIZE ); else windowStyle |= ( WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX ); return CreateWindowEx( exWindowStyle, windowClassName, windowName, windowStyle, 0, 0, width, height, NULL, NULL, winState.appInstance, NULL); } //-------------------------------------- HWND CreateCurtain( U32 width, U32 height ) { return CreateWindow( dT("Curtain"), dT(""), ( WS_POPUP | WS_MAXIMIZE ), 0, 0, width, height, NULL, NULL, winState.appInstance, NULL ); } //-------------------------------------- void CreatePixelFormat( PIXELFORMATDESCRIPTOR *pPFD, S32 colorBits, S32 depthBits, S32 stencilBits, bool stereo ) { PIXELFORMATDESCRIPTOR src = { sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd 1, // version number PFD_DRAW_TO_WINDOW | // support window PFD_SUPPORT_OPENGL | // support OpenGL PFD_DOUBLEBUFFER, // double buffered PFD_TYPE_RGBA, // RGBA type colorBits, // color depth 0, 0, 0, 0, 0, 0, // color bits ignored 0, // no alpha buffer 0, // shift bit ignored 0, // no accumulation buffer 0, 0, 0, 0, // accum bits ignored depthBits, // z-buffer stencilBits, // stencil buffer 0, // no auxiliary buffer PFD_MAIN_PLANE, // main layer 0, // reserved 0, 0, 0 // layer masks ignored }; if ( stereo ) { //ri.Printf( PRINT_ALL, "...attempting to use stereo\n" ); src.dwFlags |= PFD_STEREO; //glConfig.stereoEnabled = true; } else { //glConfig.stereoEnabled = qfalse; } *pPFD = src; } //-------------------------------------- enum WinConstants { MAX_PFDS = 256 }; #ifndef PFD_GENERIC_ACCELERATED #define PFD_GENERIC_ACCELERATED 0x00001000 #endif S32 ChooseBestPixelFormat(HDC hDC, PIXELFORMATDESCRIPTOR *pPFD) { PIXELFORMATDESCRIPTOR pfds[MAX_PFDS+1]; S32 i; S32 bestMatch = 0; S32 maxPFD = dwglDescribePixelFormat(hDC, 1, sizeof(PIXELFORMATDESCRIPTOR), &pfds[0]); if(maxPFD > MAX_PFDS) maxPFD = MAX_PFDS; bool accelerated = false; for(i = 1; i <= maxPFD; i++) { dwglDescribePixelFormat(hDC, i, sizeof(PIXELFORMATDESCRIPTOR), &pfds[i]); // make sure this has hardware acceleration: if ( ( pfds[i].dwFlags & PFD_GENERIC_FORMAT ) != 0 ) continue; // verify pixel type if ( pfds[i].iPixelType != PFD_TYPE_RGBA ) continue; // verify proper flags if ( ( ( pfds[i].dwFlags & pPFD->dwFlags ) & pPFD->dwFlags ) != pPFD->dwFlags ) continue; accelerated = !(pfds[i].dwFlags & PFD_GENERIC_FORMAT); // // selection criteria (in order of priority): // // PFD_STEREO // colorBits // depthBits // stencilBits // if ( bestMatch ) { // check stereo if ( ( pfds[i].dwFlags & PFD_STEREO ) && ( !( pfds[bestMatch].dwFlags & PFD_STEREO ) ) && ( pPFD->dwFlags & PFD_STEREO ) ) { bestMatch = i; continue; } if ( !( pfds[i].dwFlags & PFD_STEREO ) && ( pfds[bestMatch].dwFlags & PFD_STEREO ) && ( pPFD->dwFlags & PFD_STEREO ) ) { bestMatch = i; continue; } // check color if ( pfds[bestMatch].cColorBits != pPFD->cColorBits ) { // prefer perfect match if ( pfds[i].cColorBits == pPFD->cColorBits ) { bestMatch = i; continue; } // otherwise if this PFD has more bits than our best, use it else if ( pfds[i].cColorBits > pfds[bestMatch].cColorBits ) { bestMatch = i; continue; } } // check depth if ( pfds[bestMatch].cDepthBits != pPFD->cDepthBits ) { // prefer perfect match if ( pfds[i].cDepthBits == pPFD->cDepthBits ) { bestMatch = i; continue; } // otherwise if this PFD has more bits than our best, use it else if ( pfds[i].cDepthBits > pfds[bestMatch].cDepthBits ) { bestMatch = i; continue; } } // check stencil if ( pfds[bestMatch].cStencilBits != pPFD->cStencilBits ) { // prefer perfect match if ( pfds[i].cStencilBits == pPFD->cStencilBits ) { bestMatch = i; continue; } // otherwise if this PFD has more bits than our best, use it else if ( ( pfds[i].cStencilBits > pfds[bestMatch].cStencilBits ) && ( pPFD->cStencilBits > 0 ) ) { bestMatch = i; continue; } } } else { bestMatch = i; } } if ( !bestMatch ) return 0; else if ( pfds[bestMatch].dwFlags & PFD_GENERIC_ACCELERATED ) { // MCD } else { // ICD } *pPFD = pfds[bestMatch]; return bestMatch; } //-------------------------------------- // // This function exists so DirectInput can communicate with the Windows mouse // in windowed mode. // //-------------------------------------- void setModifierKeys( S32 modKeys ) { modifierKeys = modKeys; } //-------------------------------------- const Point2I &Platform::getWindowSize() { return windowSize; } //-------------------------------------- void Platform::setWindowSize( U32 newWidth, U32 newHeight ) { windowSize.set( newWidth, newHeight ); } //-------------------------------------- static void InitWindow(const Point2I &initialSize) { windowSize = initialSize; // The window is created when the display device is activated. BH winState.processId = GetCurrentProcessId(); } //-------------------------------------- static void InitOpenGL() { // The OpenGL initialization stuff has been mostly moved to the display // devices' activate functions. BH DisplayDevice::init(); // Get the video settings from the prefs: const char* resString = Con::getVariable( "$pref::Video::resolution" ); char* tempBuf = new char[dStrlen( resString ) + 1]; dStrcpy( tempBuf, resString ); char* temp = dStrtok( tempBuf, " x\0" ); U32 width = ( temp ? dAtoi( temp ) : 800 ); temp = dStrtok( NULL, " x\0" ); U32 height = ( temp ? dAtoi( temp ) : 600 ); temp = dStrtok( NULL, "\0" ); U32 bpp = ( temp ? dAtoi( temp ) : 16 ); delete [] tempBuf; bool fullScreen = Con::getBoolVariable( "$pref::Video::fullScreen" ); // If no device is specified, see which ones we have... if ( !Video::setDevice( Con::getVariable( "$pref::Video::displayDevice" ), width, height, bpp, fullScreen ) ) { // First, try the default OpenGL device: if ( !Video::setDevice( "OpenGL", width, height, bpp, fullScreen ) ) { // Next, try the D3D device: if ( !Video::setDevice( "D3D", width, height, bpp, fullScreen ) ) { // Finally, try the Voodoo2 device: if ( !Video::setDevice( "Voodoo2", width, height, bpp, fullScreen ) ) { AssertFatal( false, "Could not find a compatible display device!" ); return; } } } } } //-------------------------------------- void Platform::init() { // Set the platform variable for the scripts Con::setVariable( "$platform", "windows" ); WinConsole::create(); if ( !WinConsole::isEnabled() ) Input::init(); InitInput(); // in case DirectInput falls through InitWindowClass(); Video::getDesktopResolution(); installRedBookDevices(); sgDoubleByteEnabled = GetSystemMetrics( SM_DBCSENABLED ); sgQueueEvents = true; } //-------------------------------------- void Platform::shutdown() { sgQueueEvents = false; if(gMutexHandle) CloseHandle(gMutexHandle); setWindowLocked( false ); Video::destroy(); Input::destroy(); WinConsole::destroy(); } class WinTimer { private: U32 mTickCountCurrent; U32 mTickCountNext; S64 mPerfCountCurrent; S64 mPerfCountNext; S64 mFrequency; F64 mPerfCountRemainderCurrent; F64 mPerfCountRemainderNext; bool mUsingPerfCounter; public: WinTimer() { SetProcessAffinityMask( GetCurrentProcess(), 1 ); mPerfCountRemainderCurrent = 0.0f; mUsingPerfCounter = QueryPerformanceFrequency((LARGE_INTEGER *) &mFrequency); if(mUsingPerfCounter) mUsingPerfCounter = QueryPerformanceCounter((LARGE_INTEGER *) &mPerfCountCurrent); if(!mUsingPerfCounter) mTickCountCurrent = GetTickCount(); } U32 getElapsedMS() { if(mUsingPerfCounter) { QueryPerformanceCounter( (LARGE_INTEGER *) &mPerfCountNext); F64 elapsedF64 = (1000.0 * F64(mPerfCountNext - mPerfCountCurrent) / F64(mFrequency)); elapsedF64 += mPerfCountRemainderCurrent; U32 elapsed = mFloor(elapsedF64); mPerfCountRemainderNext = elapsedF64 - F64(elapsed); return elapsed; } else { mTickCountNext = GetTickCount(); return mTickCountNext - mTickCountCurrent; } } void advance() { mTickCountCurrent = mTickCountNext; mPerfCountCurrent = mPerfCountNext; mPerfCountRemainderCurrent = mPerfCountRemainderNext; } }; static WinTimer gTimer; extern bool LinkConsoleFunctions; //-------------------------------------- static S32 run(S32 argc, const char **argv) { // Console hack to ensure consolefunctions get linked in LinkConsoleFunctions=true; createFontInit(); windowSize.set(0,0); S32 ret = Game->main(argc, argv); createFontShutdown(); return ret; } //-------------------------------------- void Platform::initWindow(const Point2I &initialSize, const char *name) { MSG msg; Con::printf( "Video Init:" ); Video::init(); PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); if ( Video::installDevice( OpenGLDevice::create() ) ) Con::printf( " Accelerated OpenGL display device detected." ); else Con::printf( " Accelerated OpenGL display device not detected." ); PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); if ( Video::installDevice( D3DDevice::create() ) ) Con::printf( " Accelerated D3D device detected." ); else Con::printf( " Accelerated D3D device not detected." ); PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); if ( Video::installDevice( Voodoo2Device::create() ) ) Con::printf( " Voodoo 2 display device detected." ); else Con::printf( " Voodoo 2 display device not detected." ); Con::printf( "" ); gWindowCreated = true; #ifdef UNICODE convertUTF8toUTF16((UTF8 *)name, windowName, sizeof(windowName)); #else dStrcpy(windowName, name); #endif PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); InitWindow(initialSize); PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); InitOpenGL(); PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); } void Platform::setWindowTitle( const char* title ) { if( !title || !title[0] ) return; #ifdef UNICODE convertUTF8toUTF16((UTF8 *)title, windowName, sizeof(windowName)); #else dStrcpy(windowName, title); #endif if( winState.appWindow ) #ifdef UNICODE SetWindowTextW( winState.appWindow, windowName ); #else SetWindowTextA( winState.appWindow, windowName ); #endif } //-------------------------------------- S32 main(S32 argc, const char **argv) { winState.appInstance = GetModuleHandle(NULL); return run(argc, argv); } //-------------------------------------- S32 PASCAL WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR lpszCmdLine, S32) { Vector argv; char moduleName[256]; GetModuleFileNameA(NULL, moduleName, sizeof(moduleName)); argv.push_back(moduleName); for (const char* word,*ptr = lpszCmdLine; *ptr; ) { // Eat white space for (; dIsspace(*ptr) && *ptr; ptr++) ; // Pick out the next word for (word = ptr; !dIsspace(*ptr) && *ptr; ptr++) ; // Add the word to the argument list. if (*word) { int len = ptr - word; char *arg = (char *) dMalloc(len + 1); dStrncpy(arg, word, len); arg[len] = 0; argv.push_back(arg); } } winState.appInstance = hInstance; S32 retVal = run(argv.size(), (const char **) argv.address()); for(U32 j = 1; j < argv.size(); j++) dFree(argv[j]); return retVal; } //-------------------------------------- void TimeManager::process() { TimeEvent event; event.elapsedTime = gTimer.getElapsedMS(); if(event.elapsedTime > sgTimeManagerProcessInterval) { gTimer.advance(); Game->postEvent(event); } } /* GLimp_Init GLW_LoadOpenGL QGL_Init(driver); GLW_StartDriverAndSetMode GLW_SetMode ChangeDisplaySettings GLW_CreateWindow GLW_InitDriver GLW_CreatePFD GLW_MakeContext GLW_ChoosePFD DescribePixelFormat SetPixelFormat GLW_InitExtensions WG_CheckHardwareGamma */ F32 Platform::getRandom() { return sgPlatRandom.randF(); } ////-------------------------------------- /// Spawn the default Operating System web browser with a URL /// @param webAddress URL to pass to browser /// @return true if browser successfully spawned bool Platform::openWebBrowser( const char* webAddress ) { static bool sHaveKey = false; static wchar_t sWebKey[512]; char utf8WebKey[512]; { HKEY regKey; DWORD size = sizeof( sWebKey ); if ( RegOpenKeyEx( HKEY_CLASSES_ROOT, dT("\\http\\shell\\open\\command"), 0, KEY_QUERY_VALUE, ®Key ) != ERROR_SUCCESS ) { Con::errorf( ConsoleLogEntry::General, "Platform::openWebBrowser - Failed to open the HKCR\\http registry key!!!"); return( false ); } if ( RegQueryValueEx( regKey, dT(""), NULL, NULL, (unsigned char *)sWebKey, &size ) != ERROR_SUCCESS ) { Con::errorf( ConsoleLogEntry::General, "Platform::openWebBrowser - Failed to query the open command registry key!!!" ); return( false ); } RegCloseKey( regKey ); sHaveKey = true; convertUTF16toUTF8(sWebKey,utf8WebKey,512); #ifdef UNICODE char *p = dStrstr((const char *)utf8WebKey, "%1"); #else char *p = strstr( (const char *) sWebKey , "%1"); #endif if (p) *p = 0; } STARTUPINFO si; dMemset( &si, 0, sizeof( si ) ); si.cb = sizeof( si ); char buf[1024]; #ifdef UNICODE dSprintf( buf, sizeof( buf ), "%s %s", utf8WebKey, webAddress ); UTF16 b[1024]; convertUTF8toUTF16((UTF8 *)buf, b, sizeof(b)); #else dSprintf( buf, sizeof( buf ), "%s %s", sWebKey, webAddress ); #endif //Con::errorf( ConsoleLogEntry::General, "** Web browser command = %s **", buf ); PROCESS_INFORMATION pi; dMemset( &pi, 0, sizeof( pi ) ); CreateProcess( NULL, #ifdef UNICODE b, #else buf, #endif NULL, NULL, false, CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi ); return( true ); } //-------------------------------------- // Login password routines: //-------------------------------------- #ifdef UNICODE static const UTF16* TorqueRegKey = dT("SOFTWARE\\GarageGames\\Torque"); #else static const char* TorqueRegKey = "SOFTWARE\\GarageGames\\Torque"; #endif const char* Platform::getLoginPassword() { HKEY regKey; char* returnString = NULL; if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, TorqueRegKey, 0, KEY_QUERY_VALUE, ®Key ) == ERROR_SUCCESS ) { U8 buf[32]; DWORD size = sizeof( buf ); if ( RegQueryValueEx( regKey, dT("LoginPassword"), NULL, NULL, buf, &size ) == ERROR_SUCCESS ) { returnString = Con::getReturnBuffer( size + 1 ); dStrcpy( returnString, (const char*) buf ); } RegCloseKey( regKey ); } if ( returnString ) return( returnString ); else return( "" ); } //-------------------------------------- bool Platform::setLoginPassword( const char* password ) { HKEY regKey; if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, TorqueRegKey, 0, KEY_WRITE, ®Key ) == ERROR_SUCCESS ) { if ( RegSetValueEx( regKey, dT("LoginPassword"), 0, REG_SZ, (const U8*) password, dStrlen( password ) + 1 ) != ERROR_SUCCESS ) Con::errorf( ConsoleLogEntry::General, "setLoginPassword - Failed to set the subkey value!" ); RegCloseKey( regKey ); return( true ); } else Con::errorf( ConsoleLogEntry::General, "setLoginPassword - Failed to open the Torque registry key!" ); return( false ); } //-------------------------------------- // Silly Korean registry key checker: //-------------------------------------- ConsoleFunction( isKoreanBuild, bool, 1, 1, "isKoreanBuild()" ) { argc; argv; HKEY regKey; bool result = false; if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, TorqueRegKey, 0, KEY_QUERY_VALUE, ®Key ) == ERROR_SUCCESS ) { DWORD val; DWORD size = sizeof( val ); if ( RegQueryValueEx( regKey, dT("Korean"), NULL, NULL, (U8*) &val, &size ) == ERROR_SUCCESS ) result = ( val > 0 ); RegCloseKey( regKey ); } return( result ); }