//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "platformWin32/platformWin32.h" #include "platform/platformInput.h" #include "platform/platformVideo.h" #include "platformWin32/winDirectInput.h" #include "platform/event.h" #include "console/console.h" #ifdef LOG_INPUT #include #include #endif // Static class variables: InputManager* Input::smManager; bool Input::smActive; bool Input::smLastKeyboardActivated; bool Input::smLastMouseActivated; bool Input::smLastJoystickActivated; #ifdef LOG_INPUT static HANDLE gInputLog; #endif static void fillAsciiTable(); //------------------------------------------------------------------------------ // // This function gets the standard ASCII code corresponding to our key code // and the existing modifier key state. // //------------------------------------------------------------------------------ struct AsciiData { struct KeyData { U16 ascii; bool isDeadChar; }; KeyData upper; KeyData lower; KeyData goofy; }; #define NUM_KEYS ( KEY_OEM_102 + 1 ) #define KEY_FIRST KEY_ESCAPE static AsciiData AsciiTable[NUM_KEYS]; //------------------------------------------------------------------------------ void Input::init() { Con::printf( "Input Init:" ); destroy(); #ifdef LOG_INPUT struct tm* newTime; time_t aclock; time( &aclock ); newTime = localtime( &aclock ); asctime( newTime ); gInputLog = CreateFile( L"input.log", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); log( "Input log opened at %s\n", asctime( newTime ) ); #endif smActive = false; smLastKeyboardActivated = true; smLastMouseActivated = true; smLastJoystickActivated = true; OSVERSIONINFO OSVersionInfo; dMemset( &OSVersionInfo, 0, sizeof( OSVERSIONINFO ) ); OSVersionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); if ( GetVersionEx( &OSVersionInfo ) ) { #ifdef LOG_INPUT log( "Operating System:\n" ); switch ( OSVersionInfo.dwPlatformId ) { case VER_PLATFORM_WIN32s: log( " Win32s on Windows 3.1 version %d.%d\n", OSVersionInfo.dwMajorVersion, OSVersionInfo.dwMinorVersion ); break; case VER_PLATFORM_WIN32_WINDOWS: log( " Windows 95 version %d.%d\n", OSVersionInfo.dwMajorVersion, OSVersionInfo.dwMinorVersion ); log( " Build number %d\n", LOWORD( OSVersionInfo.dwBuildNumber ) ); break; case VER_PLATFORM_WIN32_NT: log( " WinNT version %d.%d\n", OSVersionInfo.dwMajorVersion, OSVersionInfo.dwMinorVersion ); log( " Build number %d\n", OSVersionInfo.dwBuildNumber ); break; } if ( OSVersionInfo.szCSDVersion != NULL ) log( " %s\n", OSVersionInfo.szCSDVersion ); log( "\n" ); #endif if ( !( OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT && OSVersionInfo.dwMajorVersion < 5 ) ) { smManager = new DInputManager; if ( !smManager->enable() ) { Con::printf( " DirectInput not enabled." ); delete smManager; smManager = NULL; } else { DInputManager::init(); Con::printf( " DirectInput enabled." ); } } else Con::printf( " WinNT detected -- DirectInput not enabled." ); } fillAsciiTable(); Con::printf( "" ); } //------------------------------------------------------------------------------ ConsoleFunction( isJoystickDetected, bool, 1, 1, "isJoystickDetected()" ) { argc; argv; return( DInputDevice::joystickDetected() ); } //------------------------------------------------------------------------------ ConsoleFunction( getJoystickAxes, const char*, 2, 2, "getJoystickAxes( instance )" ) { argc; DInputManager* mgr = dynamic_cast( Input::getManager() ); if ( mgr ) return( mgr->getJoystickAxesString( dAtoi( argv[1] ) ) ); return( "" ); } //------------------------------------------------------------------------------ static void fillAsciiTable() { #ifdef LOG_INPUT char buf[256]; Input::log( "--- Filling the ASCII table! ---\n" ); #endif //HKL layout = GetKeyboardLayout( 0 ); U8 state[256]; U16 ascii[2]; U32 dikCode, vKeyCode, keyCode; S32 result; dMemset( &AsciiTable, 0, sizeof( AsciiTable ) ); dMemset( &state, 0, sizeof( state ) ); for ( keyCode = KEY_FIRST; keyCode < NUM_KEYS; keyCode++ ) { ascii[0] = ascii[1] = 0; dikCode = Key_to_DIK( keyCode ); if ( dikCode ) { //vKeyCode = MapVirtualKeyEx( dikCode, 1, layout ); vKeyCode = MapVirtualKey( dikCode, 1 ); #ifdef LOG_INPUT dSprintf( buf, sizeof( buf ), "KC: %#04X DK: %#04X VK: %#04X\n", keyCode, dikCode, vKeyCode ); Input::log( buf ); #endif // Lower case: ascii[0] = ascii[1] = 0; //result = ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout ); result = ToAscii( vKeyCode, dikCode, state, ascii, 0 ); #ifdef LOG_INPUT dSprintf( buf, sizeof( buf ), " LOWER- R: %d A[0]: %#06X A[1]: %#06X\n", result, ascii[0], ascii[1] ); Input::log( buf ); #endif if ( result == 2 ) AsciiTable[keyCode].lower.ascii = ascii[1] ? ascii[1] : ( ascii[0] >> 8 ); else if ( result == 1 ) AsciiTable[keyCode].lower.ascii = ascii[0]; else if ( result < 0 ) { AsciiTable[keyCode].lower.ascii = ascii[0]; AsciiTable[keyCode].lower.isDeadChar = true; // Need to clear the dead character from the keyboard layout: //ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout ); ToAscii( vKeyCode, dikCode, state, ascii, 0 ); } // Upper case: ascii[0] = ascii[1] = 0; state[VK_SHIFT] = 0x80; //result = ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout ); result = ToAscii( vKeyCode, dikCode, state, ascii, 0 ); #ifdef LOG_INPUT dSprintf( buf, sizeof( buf ), " UPPER- R: %d A[0]: %#06X A[1]: %#06X\n", result, ascii[0], ascii[1] ); Input::log( buf ); #endif if ( result == 2 ) AsciiTable[keyCode].upper.ascii = ascii[1] ? ascii[1] : ( ascii[0] >> 8 ); else if ( result == 1 ) AsciiTable[keyCode].upper.ascii = ascii[0]; else if ( result < 0 ) { AsciiTable[keyCode].upper.ascii = ascii[0]; AsciiTable[keyCode].upper.isDeadChar = true; // Need to clear the dead character from the keyboard layout: //ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout ); ToAscii( vKeyCode, dikCode, state, ascii, 0 ); } state[VK_SHIFT] = 0; // Foreign mod case: ascii[0] = ascii[1] = 0; state[VK_CONTROL] = 0x80; state[VK_MENU] = 0x80; //result = ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout ); result = ToAscii( vKeyCode, dikCode, state, ascii, 0 ); #ifdef LOG_INPUT dSprintf( buf, sizeof( buf ), " GOOFY- R: %d A[0]: %#06X A[1]: %#06X\n", result, ascii[0], ascii[1] ); Input::log( buf ); #endif if ( result == 2 ) AsciiTable[keyCode].goofy.ascii = ascii[1] ? ascii[1] : ( ascii[0] >> 8 ); else if ( result == 1 ) AsciiTable[keyCode].goofy.ascii = ascii[0]; else if ( result < 0 ) { AsciiTable[keyCode].goofy.ascii = ascii[0]; AsciiTable[keyCode].goofy.isDeadChar = true; // Need to clear the dead character from the keyboard layout: //ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout ); ToAscii( vKeyCode, dikCode, state, ascii, 0 ); } state[VK_CONTROL] = 0; state[VK_MENU] = 0; } } #ifdef LOG_INPUT Input::log( "--- Finished filling the ASCII table! ---\n\n" ); #endif } //------------------------------------------------------------------------------ U16 Input::getKeyCode( U16 asciiCode ) { U16 keyCode = 0; U16 i; // This is done three times so the lowerkey will always // be found first. Some foreign keyboards have duplicate // chars on some keys. for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ ) { if ( AsciiTable[i].lower.ascii == asciiCode ) { keyCode = i; break; }; } for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ ) { if ( AsciiTable[i].upper.ascii == asciiCode ) { keyCode = i; break; }; } for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ ) { if ( AsciiTable[i].goofy.ascii == asciiCode ) { keyCode = i; break; }; } return( keyCode ); } //------------------------------------------------------------------------------ U16 Input::getAscii( U16 keyCode, KEY_STATE keyState ) { if ( keyCode >= NUM_KEYS ) return 0; switch ( keyState ) { case STATE_LOWER: return AsciiTable[keyCode].lower.ascii; case STATE_UPPER: return AsciiTable[keyCode].upper.ascii; case STATE_GOOFY: return AsciiTable[keyCode].goofy.ascii; default: return(0); } } //------------------------------------------------------------------------------ void Input::destroy() { if ( smManager && smManager->isEnabled() ) { smManager->disable(); delete smManager; smManager = NULL; } #ifdef LOG_INPUT if ( gInputLog ) { log( "*** CLOSING LOG ***\n" ); CloseHandle( gInputLog ); gInputLog = NULL; } #endif } //------------------------------------------------------------------------------ bool Input::enable() { if ( smManager && !smManager->isEnabled() ) return( smManager->enable() ); return( false ); } //------------------------------------------------------------------------------ void Input::disable() { if ( smManager && smManager->isEnabled() ) smManager->disable(); } //------------------------------------------------------------------------------ void Input::activate() { #ifdef UNICODE winState.imeHandle = ImmGetContext( winState.appWindow ); ImmReleaseContext( winState.appWindow, winState.imeHandle ); #endif DInputDevice::resetModifierKeys(); if ( !Con::getBoolVariable( "$enableDirectInput" ) ) return; if ( smManager && smManager->isEnabled() && !smActive ) { Con::printf( "Activating DirectInput..." ); #ifdef LOG_INPUT Input::log( "Activating DirectInput...\n" ); #endif smActive = true; DInputManager* dInputManager = dynamic_cast( smManager ); if ( dInputManager ) { if ( dInputManager->isKeyboardEnabled() && smLastKeyboardActivated ) dInputManager->activateKeyboard(); if ( Video::isFullScreen() ) { // DirectInput Mouse Hook-Up: if ( dInputManager->isMouseEnabled() && smLastMouseActivated ) dInputManager->activateMouse(); } else dInputManager->deactivateMouse(); if ( dInputManager->isJoystickEnabled() && smLastJoystickActivated ) dInputManager->activateJoystick(); } } } //------------------------------------------------------------------------------ void Input::deactivate() { if ( smManager && smManager->isEnabled() && smActive ) { #ifdef LOG_INPUT Input::log( "Deactivating DirectInput...\n" ); #endif DInputManager* dInputManager = dynamic_cast( smManager ); if ( dInputManager ) { smLastKeyboardActivated = dInputManager->isKeyboardActive(); smLastMouseActivated = dInputManager->isMouseActive(); smLastJoystickActivated = dInputManager->isJoystickActive(); dInputManager->deactivateKeyboard(); dInputManager->deactivateMouse(); dInputManager->deactivateJoystick(); } smActive = false; Con::printf( "DirectInput deactivated." ); } } //------------------------------------------------------------------------------ void Input::reactivate() { // This is soo hacky... SetForegroundWindow( winState.appWindow ); PostMessage( winState.appWindow, WM_ACTIVATE, WA_ACTIVE, NULL ); } //------------------------------------------------------------------------------ bool Input::isEnabled() { if ( smManager ) return smManager->isEnabled(); return false; } //------------------------------------------------------------------------------ bool Input::isActive() { return smActive; } //------------------------------------------------------------------------------ void Input::process() { if ( smManager && smManager->isEnabled() && smActive ) smManager->process(); } //------------------------------------------------------------------------------ void Input::setCursorPos(S32 x, S32 y) { POINT pt; pt.x = x; pt.y = y; ClientToScreen(winState.appWindow, &pt); SetCursorPos(pt.x, pt.y); } //------------------------------------------------------------------------------ InputManager* Input::getManager() { return( smManager ); } #ifdef LOG_INPUT //------------------------------------------------------------------------------ void Input::log( const char* format, ... ) { if ( !gInputLog ) return; va_list argptr; va_start( argptr, format ); char buffer[512]; dVsprintf( buffer, 511, format, argptr ); DWORD bytes; WriteFile( gInputLog, buffer, dStrlen( buffer ), &bytes, NULL ); va_end( argptr ); } ConsoleFunction( inputLog, void, 2, 2, "inputLog( string )" ) { argc; Input::log( "%s\n", argv[1] ); } #endif // LOG_INPUT //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ static U8 VcodeRemap[256] = { 0, // 0x00 0, // 0x01 VK_LBUTTON 0, // 0x02 VK_RBUTTON 0, // 0x03 VK_CANCEL 0, // 0x04 VK_MBUTTON 0, // 0x05 0, // 0x06 0, // 0x07 KEY_BACKSPACE, // 0x08 VK_BACK KEY_TAB, // 0x09 VK_TAB 0, // 0x0A 0, // 0x0B 0, // 0x0C VK_CLEAR KEY_RETURN, // 0x0D VK_RETURN 0, // 0x0E 0, // 0x0F KEY_SHIFT, // 0x10 VK_SHIFT KEY_CONTROL, // 0x11 VK_CONTROL KEY_ALT, // 0x12 VK_MENU KEY_PAUSE, // 0x13 VK_PAUSE KEY_CAPSLOCK, // 0x14 VK_CAPITAL 0, // 0x15 VK_KANA, VK_HANGEUL, VK_HANGUL 0, // 0x16 0, // 0x17 VK_JUNJA 0, // 0x18 VK_FINAL 0, // 0x19 VK_HANJA, VK_KANJI 0, // 0x1A KEY_ESCAPE, // 0x1B VK_ESCAPE 0, // 0x1C VK_CONVERT 0, // 0x1D VK_NONCONVERT 0, // 0x1E VK_ACCEPT 0, // 0x1F VK_MODECHANGE KEY_SPACE, // 0x20 VK_SPACE KEY_PAGE_UP, // 0x21 VK_PRIOR KEY_PAGE_DOWN, // 0x22 VK_NEXT KEY_END, // 0x23 VK_END KEY_HOME, // 0x24 VK_HOME KEY_LEFT, // 0x25 VK_LEFT KEY_UP, // 0x26 VK_UP KEY_RIGHT, // 0x27 VK_RIGHT KEY_DOWN, // 0x28 VK_DOWN 0, // 0x29 VK_SELECT KEY_PRINT, // 0x2A VK_PRINT 0, // 0x2B VK_EXECUTE 0, // 0x2C VK_SNAPSHOT KEY_INSERT, // 0x2D VK_INSERT KEY_DELETE, // 0x2E VK_DELETE KEY_HELP, // 0x2F VK_HELP KEY_0, // 0x30 VK_0 VK_0 thru VK_9 are the same as ASCII '0' thru '9' (// 0x30 - // 0x39) KEY_1, // 0x31 VK_1 KEY_2, // 0x32 VK_2 KEY_3, // 0x33 VK_3 KEY_4, // 0x34 VK_4 KEY_5, // 0x35 VK_5 KEY_6, // 0x36 VK_6 KEY_7, // 0x37 VK_7 KEY_8, // 0x38 VK_8 KEY_9, // 0x39 VK_9 0, // 0x3A 0, // 0x3B 0, // 0x3C 0, // 0x3D 0, // 0x3E 0, // 0x3F 0, // 0x40 KEY_A, // 0x41 VK_A VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (// 0x41 - // 0x5A) KEY_B, // 0x42 VK_B KEY_C, // 0x43 VK_C KEY_D, // 0x44 VK_D KEY_E, // 0x45 VK_E KEY_F, // 0x46 VK_F KEY_G, // 0x47 VK_G KEY_H, // 0x48 VK_H KEY_I, // 0x49 VK_I KEY_J, // 0x4A VK_J KEY_K, // 0x4B VK_K KEY_L, // 0x4C VK_L KEY_M, // 0x4D VK_M KEY_N, // 0x4E VK_N KEY_O, // 0x4F VK_O KEY_P, // 0x50 VK_P KEY_Q, // 0x51 VK_Q KEY_R, // 0x52 VK_R KEY_S, // 0x53 VK_S KEY_T, // 0x54 VK_T KEY_U, // 0x55 VK_U KEY_V, // 0x56 VK_V KEY_W, // 0x57 VK_W KEY_X, // 0x58 VK_X KEY_Y, // 0x59 VK_Y KEY_Z, // 0x5A VK_Z KEY_WIN_LWINDOW, // 0x5B VK_LWIN KEY_WIN_RWINDOW, // 0x5C VK_RWIN KEY_WIN_APPS, // 0x5D VK_APPS 0, // 0x5E 0, // 0x5F KEY_NUMPAD0, // 0x60 VK_NUMPAD0 KEY_NUMPAD1, // 0x61 VK_NUMPAD1 KEY_NUMPAD2, // 0x62 VK_NUMPAD2 KEY_NUMPAD3, // 0x63 VK_NUMPAD3 KEY_NUMPAD4, // 0x64 VK_NUMPAD4 KEY_NUMPAD5, // 0x65 VK_NUMPAD5 KEY_NUMPAD6, // 0x66 VK_NUMPAD6 KEY_NUMPAD7, // 0x67 VK_NUMPAD7 KEY_NUMPAD8, // 0x68 VK_NUMPAD8 KEY_NUMPAD9, // 0x69 VK_NUMPAD9 KEY_MULTIPLY, // 0x6A VK_MULTIPLY KEY_ADD, // 0x6B VK_ADD KEY_SEPARATOR, // 0x6C VK_SEPARATOR KEY_SUBTRACT, // 0x6D VK_SUBTRACT KEY_DECIMAL, // 0x6E VK_DECIMAL KEY_DIVIDE, // 0x6F VK_DIVIDE KEY_F1, // 0x70 VK_F1 KEY_F2, // 0x71 VK_F2 KEY_F3, // 0x72 VK_F3 KEY_F4, // 0x73 VK_F4 KEY_F5, // 0x74 VK_F5 KEY_F6, // 0x75 VK_F6 KEY_F7, // 0x76 VK_F7 KEY_F8, // 0x77 VK_F8 KEY_F9, // 0x78 VK_F9 KEY_F10, // 0x79 VK_F10 KEY_F11, // 0x7A VK_F11 KEY_F12, // 0x7B VK_F12 KEY_F13, // 0x7C VK_F13 KEY_F14, // 0x7D VK_F14 KEY_F15, // 0x7E VK_F15 KEY_F16, // 0x7F VK_F16 KEY_F17, // 0x80 VK_F17 KEY_F18, // 0x81 VK_F18 KEY_F19, // 0x82 VK_F19 KEY_F20, // 0x83 VK_F20 KEY_F21, // 0x84 VK_F21 KEY_F22, // 0x85 VK_F22 KEY_F23, // 0x86 VK_F23 KEY_F24, // 0x87 VK_F24 0, // 0x88 0, // 0x89 0, // 0x8A 0, // 0x8B 0, // 0x8C 0, // 0x8D 0, // 0x8E 0, // 0x8F KEY_NUMLOCK, // 0x90 VK_NUMLOCK KEY_SCROLLLOCK, // 0x91 VK_OEM_SCROLL 0, // 0x92 0, // 0x93 0, // 0x94 0, // 0x95 0, // 0x96 0, // 0x97 0, // 0x98 0, // 0x99 0, // 0x9A 0, // 0x9B 0, // 0x9C 0, // 0x9D 0, // 0x9E 0, // 0x9F KEY_LSHIFT, // 0xA0 VK_LSHIFT KEY_RSHIFT, // 0xA1 VK_RSHIFT KEY_LCONTROL, // 0xA2 VK_LCONTROL KEY_RCONTROL, // 0xA3 VK_RCONTROL KEY_LALT, // 0xA4 VK_LMENU KEY_RALT, // 0xA5 VK_RMENU 0, // 0xA6 0, // 0xA7 0, // 0xA8 0, // 0xA9 0, // 0xAA 0, // 0xAB 0, // 0xAC 0, // 0xAD 0, // 0xAE 0, // 0xAF 0, // 0xB0 0, // 0xB1 0, // 0xB2 0, // 0xB3 0, // 0xB4 0, // 0xB5 0, // 0xB6 0, // 0xB7 0, // 0xB8 0, // 0xB9 KEY_SEMICOLON, // 0xBA VK_OEM_1 KEY_EQUALS, // 0xBB VK_OEM_PLUS KEY_COMMA, // 0xBC VK_OEM_COMMA KEY_MINUS, // 0xBD VK_OEM_MINUS KEY_PERIOD, // 0xBE VK_OEM_PERIOD KEY_SLASH, // 0xBF VK_OEM_2 KEY_TILDE, // 0xC0 VK_OEM_3 0, // 0xC1 0, // 0xC2 0, // 0xC3 0, // 0xC4 0, // 0xC5 0, // 0xC6 0, // 0xC7 0, // 0xC8 0, // 0xC9 0, // 0xCA 0, // 0xCB 0, // 0xCC 0, // 0xCD 0, // 0xCE 0, // 0xCF 0, // 0xD0 0, // 0xD1 0, // 0xD2 0, // 0xD3 0, // 0xD4 0, // 0xD5 0, // 0xD6 0, // 0xD7 0, // 0xD8 0, // 0xD9 0, // 0xDA KEY_LBRACKET, // 0xDB VK_OEM_4 KEY_BACKSLASH, // 0xDC VK_OEM_5 KEY_RBRACKET, // 0xDD VK_OEM_6 KEY_APOSTROPHE, // 0xDE VK_OEM_7 0, // 0xDF VK_OEM_8 0, // 0xE0 0, // 0xE1 VK_OEM_AX AX key on Japanese AX keyboard KEY_OEM_102, // 0xE2 VK_OEM_102 0, // 0xE3 0, // 0xE4 0, // 0xE5 VK_PROCESSKEY 0, // 0xE6 0, // 0xE7 0, // 0xE8 0, // 0xE9 0, // 0xEA 0, // 0xEB 0, // 0xEC 0, // 0xED 0, // 0xEE 0, // 0xEF 0, // 0xF0 0, // 0xF1 0, // 0xF2 0, // 0xF3 0, // 0xF4 0, // 0xF5 0, // 0xF6 VK_ATTN 0, // 0xF7 VK_CRSEL 0, // 0xF8 VK_EXSEL 0, // 0xF9 VK_EREOF 0, // 0xFA VK_PLAY 0, // 0xFB VK_ZOOM 0, // 0xFC VK_NONAME 0, // 0xFD VK_PA1 0, // 0xFE VK_OEM_CLEAR 0 // 0xFF }; //------------------------------------------------------------------------------ // // This function translates a virtual key code to our corresponding internal // key code using the preceding table. // //------------------------------------------------------------------------------ U8 TranslateOSKeyCode(U8 vcode) { return VcodeRemap[vcode]; } //----------------------------------------------------------------------------- // Clipboard functions const char* Platform::getClipboard() { HGLOBAL hGlobal; LPVOID pGlobal; //make sure we can access the clipboard if (!IsClipboardFormatAvailable(CF_TEXT)) return ""; if (!OpenClipboard(NULL)) return ""; hGlobal = GetClipboardData(CF_TEXT); pGlobal = GlobalLock(hGlobal); S32 cbLength = strlen((char *)pGlobal); char *returnBuf = Con::getReturnBuffer(cbLength + 1); strcpy(returnBuf, (char *)pGlobal); returnBuf[cbLength] = '\0'; GlobalUnlock(hGlobal); CloseClipboard(); //note - this function never returns NULL return returnBuf; } //----------------------------------------------------------------------------- bool Platform::setClipboard(const char *text) { if (!text) return false; //make sure we can access the clipboard if (!OpenClipboard(NULL)) return false; S32 cbLength = strlen(text); HGLOBAL hGlobal; LPVOID pGlobal; hGlobal = GlobalAlloc(GHND, cbLength + 1); pGlobal = GlobalLock (hGlobal); strcpy((char *)pGlobal, text); GlobalUnlock(hGlobal); EmptyClipboard(); SetClipboardData(CF_TEXT, hGlobal); CloseClipboard(); return true; }