/* The zlib/libpng License Copyright (c) 2006 Chris Snyder This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "mac/MacKeyboard.h" #include "mac/MacInputManager.h" #include "mac/MacHelpers.h" #include "OISException.h" #include "OISEvents.h" #include #include #include const EventTypeSpec DownSpec[] = {{kEventClassKeyboard, kEventRawKeyDown}, //non - repeats {kEventClassKeyboard, kEventRawKeyRepeat}}; //repeats const EventTypeSpec UpSpec = {kEventClassKeyboard, kEventRawKeyUp}, ModSpec = {kEventClassKeyboard, kEventRawKeyModifiersChanged}; const EventTypeSpec AllSpecs[] = {{kEventClassKeyboard, kEventRawKeyDown}, {kEventClassKeyboard, kEventRawKeyRepeat}, {kEventClassKeyboard, kEventRawKeyUp}, {kEventClassKeyboard, kEventRawKeyModifiersChanged}}; using namespace OIS; //-------------------------------------------------------------------// MacKeyboard::MacKeyboard( InputManager* creator, bool buffered, bool repeat ) : Keyboard(creator->inputSystemName(), buffered, 0, creator) { keyDownEventRef = NULL; keyUpEventRef = NULL; keyModEventRef = NULL; useRepeat = repeat; // Get a so-called "Univeral procedure pointer" for our callback keyDownUPP = NewEventHandlerUPP( KeyDownWrapper ); keyUpUPP = NewEventHandlerUPP( KeyUpWrapper ); keyModUPP = NewEventHandlerUPP( KeyModWrapper ); // populate the conversion map populateKeyConversion(); static_cast(mCreator)->_setKeyboardUsed(true); } //-------------------------------------------------------------------// MacKeyboard::~MacKeyboard() { // Remove our handlers so that this instance doesn't get called // after it is deleted if (keyDownEventRef != NULL) RemoveEventHandler(keyDownEventRef); if (keyUpEventRef != NULL) RemoveEventHandler(keyUpEventRef); if (keyModEventRef != NULL) RemoveEventHandler(keyModEventRef); // dispose of our UPPs DisposeEventHandlerUPP(keyDownUPP); DisposeEventHandlerUPP(keyUpUPP); DisposeEventHandlerUPP(keyModUPP); //Free the input managers keyboard static_cast(mCreator)->_setKeyboardUsed(false); } //-------------------------------------------------------------------// void MacKeyboard::_initialize() { EventTargetRef event = ((MacInputManager*)mCreator)->_getEventTarget(); memset( &KeyBuffer, 0, 256 ); mModifiers = 0; prevModMask = 0; // just in case this gets called after the first time.. better safe if (keyDownEventRef != NULL) RemoveEventHandler(keyDownEventRef); if (keyUpEventRef != NULL) RemoveEventHandler(keyUpEventRef); if (keyModEventRef != NULL) RemoveEventHandler(keyModEventRef); keyDownEventRef = NULL; keyUpEventRef = NULL; keyModEventRef = NULL; OSStatus status; // send both elements of downspec array... second index is for repeat events if ( useRepeat ) status = InstallEventHandler( event, keyDownUPP, 2, DownSpec, this, &keyDownEventRef ); else status = InstallEventHandler( event, keyDownUPP, 1, DownSpec, this, &keyDownEventRef ); if (status != noErr) OIS_EXCEPT( E_General, "MacKeyboard::_initialize >> Error loading KeyDown event handler" ); if (InstallEventHandler( event, keyUpUPP, 1, &UpSpec, this, &keyUpEventRef ) != noErr) OIS_EXCEPT( E_General, "MacKeyboard::_initialize >> Error loading KeyUp event handler" ); if (InstallEventHandler( event, keyModUPP, 1, &ModSpec, this, &keyModEventRef ) != noErr ) OIS_EXCEPT( E_General, "MacKeyboard::_initialize >> Error loading Keymods event handler" ); } //-------------------------------------------------------------------// bool MacKeyboard::isKeyDown( KeyCode key ) const { return (bool)KeyBuffer[key]; } //-------------------------------------------------------------------// void MacKeyboard::capture() { // if not buffered just return, we update the unbuffered automatically if ( !mBuffered || !mListener ) return; //If the mListener returns false, that means that we are probably deleted... //send no more events and just leave as the this pointer is invalid now... bool ret = true; // run through our event stack eventStack::iterator cur_it; for (cur_it = pendingEvents.begin(); cur_it != pendingEvents.end(); cur_it++) { if ( (*cur_it).Type == MAC_KEYDOWN || (*cur_it).Type == MAC_KEYREPEAT) mListener->keyPressed( (*cur_it).Event ); else if ( (*cur_it).Type == MAC_KEYUP ) mListener->keyReleased( (*cur_it).Event ); } pendingEvents.clear(); } //-------------------------------------------------------------------// std::string& MacKeyboard::getAsString( KeyCode key ) { getString = ""; return getString; } //-------------------------------------------------------------------// void MacKeyboard::setBuffered( bool buffered ) { mBuffered = buffered; } #include //-------------------------------------------------------------------// void MacKeyboard::_keyDownCallback( EventRef theEvent ) { UInt32 virtualKey; OSStatus status; unsigned int time = (unsigned int)GetEventTime(theEvent); status = GetEventParameter(theEvent, 'kcod', // get it in virtual keycode typeUInt32, NULL, // desired return type sizeof(UInt32), NULL, // bufsize &virtualKey ); KeyCode kc = keyConversion[virtualKey]; // record what kind of text we should pass the KeyEvent UniChar text[10]; char macChar; if (mTextMode == Unicode) { //get string size UInt32 stringsize; status = GetEventParameter( theEvent, 'kuni', typeUnicodeText, NULL, sizeof(UniChar) * 10, &stringsize, &text ); if(stringsize > 0) { // for each unicode char, send an event stringsize--; // no termination char for ( int i = 0; i < stringsize; i++ ) { injectEvent( kc, time, MAC_KEYDOWN, (unsigned int)text[i] ); } } } else if (mTextMode == Ascii) { status = GetEventParameter( theEvent, 'kchr', typeChar, NULL, sizeof(char), NULL, &macChar ); injectEvent( kc, time, MAC_KEYDOWN, (unsigned int)macChar ); } else { injectEvent( kc, time, MAC_KEYDOWN ); } } //-------------------------------------------------------------------// void MacKeyboard::_keyUpCallback( EventRef theEvent ) { UInt32 virtualKey; OSStatus status; status = GetEventParameter( theEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &virtualKey ); KeyCode kc = keyConversion[virtualKey]; injectEvent( kc, (int)GetEventTime(theEvent), MAC_KEYUP ); } //-------------------------------------------------------------------// void MacKeyboard::_modChangeCallback( EventRef theEvent ) { UInt32 mods; OSStatus status; status = GetEventParameter( theEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &mods ); // find the changed bit UInt32 change = prevModMask ^ mods; MacEventType newstate = ((change & prevModMask) > 0) ? MAC_KEYUP : MAC_KEYDOWN; unsigned int time = (int)GetEventTime( theEvent ); //cout << "preMask: " << hex << prevModMask << endl; //cout << "ModMask: " << hex << mods << endl; //cout << "Change: " << hex << (change & prevModMask) << endl << endl; // TODO test modifiers on a full keyboard to check if different mask for left/right switch (change) { case (shiftKey): // shift mModifiers &= (newstate == MAC_KEYDOWN) ? Shift : ~Shift; injectEvent( KC_LSHIFT, time, newstate ); //injectEvent( KC_RSHIFT, time, newstate ); break; case (optionKey): // option (alt) mModifiers &= (newstate == MAC_KEYDOWN) ? Alt : -Alt; //injectEvent( KC_RMENU, time, newstate ); injectEvent( KC_LMENU, time, newstate ); break; case (controlKey): // Ctrl mModifiers += (newstate == MAC_KEYDOWN) ? Ctrl : -Ctrl; //injectEvent( KC_RCONTROL, time, newstate ); injectEvent( KC_LCONTROL, time, newstate ); break; case (cmdKey): // apple //injectEvent( KC_RWIN, time, newstate ); injectEvent( KC_LWIN, time, newstate ); break; case (kEventKeyModifierFnMask): // fn key injectEvent( KC_APPS, time, newstate ); break; case (kEventKeyModifierNumLockMask): // numlock injectEvent( KC_NUMLOCK, time, newstate ); break; case (alphaLock): // caps lock injectEvent( KC_CAPITAL, time, newstate ); break; } prevModMask = mods; } //-------------------------------------------------------------------// void MacKeyboard::injectEvent( KeyCode kc, unsigned int time, MacEventType type, unsigned int txt ) { // set to 1 if this is either a keydown or repeat KeyBuffer[kc] = ( type == MAC_KEYUP ) ? 0 : 1; if ( mBuffered && mListener ) pendingEvents.push_back( MacKeyStackEvent( KeyEvent(this, kc, txt), type) ); } //-------------------------------------------------------------------// void MacKeyboard::copyKeyStates( char keys[256] ) const { memcpy( keys, KeyBuffer, 256 ); } //-------------------------------------------------------------------// void MacKeyboard::populateKeyConversion() { // TODO finish the key mapping // Virtual Key Map to KeyCode keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x12, KC_1)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x13, KC_2)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x14, KC_3)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x15, KC_4)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x17, KC_5)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x16, KC_6)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x1A, KC_7)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x1C, KC_8)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x19, KC_9)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x1D, KC_0)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x33, KC_BACK)); // might be wrong keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x1B, KC_MINUS)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x18, KC_EQUALS)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x31, KC_SPACE)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x2B, KC_COMMA)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x2F, KC_PERIOD)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x2A, KC_BACKSLASH)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x2C, KC_SLASH)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x21, KC_LBRACKET)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x1E, KC_RBRACKET)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x35, KC_ESCAPE)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x39, KC_CAPITAL)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x30, KC_TAB)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x24, KC_RETURN)); // double check return/enter //keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_colon, KC_COLON)); // no colon? keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x29, KC_SEMICOLON)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x27, KC_APOSTROPHE)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x32, KC_GRAVE)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x0B, KC_B)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x00, KC_A)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x08, KC_C)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x02, KC_D)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x0E, KC_E)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x03, KC_F)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x05, KC_G)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x04, KC_H)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x22, KC_I)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x26, KC_J)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x28, KC_K)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x25, KC_L)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x2E, KC_M)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x2D, KC_N)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x1F, KC_O)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x23, KC_P)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x0C, KC_Q)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x0F, KC_R)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x01, KC_S)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x11, KC_T)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x20, KC_U)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x09, KC_V)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x0D, KC_W)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x07, KC_X)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x10, KC_Y)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x06, KC_Z)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x7A, KC_F1)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x78, KC_F2)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x63, KC_F3)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x76, KC_F4)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x60, KC_F5)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x61, KC_F6)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x62, KC_F7)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x64, KC_F8)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x65, KC_F9)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x6D, KC_F10)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x67, KC_F11)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x6F, KC_F12)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x69, KC_F13)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x6B, KC_F14)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x71, KC_F15)); //Keypad keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x52, KC_NUMPAD0)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x53, KC_NUMPAD1)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x54, KC_NUMPAD2)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x55, KC_NUMPAD3)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x56, KC_NUMPAD4)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x57, KC_NUMPAD5)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x58, KC_NUMPAD6)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x59, KC_NUMPAD7)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x5B, KC_NUMPAD8)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x5C, KC_NUMPAD9)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x45, KC_ADD)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x4E, KC_SUBTRACT)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x41, KC_DECIMAL)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x51, KC_NUMPADEQUALS)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x4B, KC_DIVIDE)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x43, KC_MULTIPLY)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x4C, KC_NUMPADENTER)); //Keypad with numlock off //keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x73, KC_NUMPAD7)); // not sure of these //keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Up, KC_NUMPAD8)); // check on a non-laptop //keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Page_Up, KC_NUMPAD9)); //keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Left, KC_NUMPAD4)); //keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Begin, KC_NUMPAD5)); //keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Right, KC_NUMPAD6)); //keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_End, KC_NUMPAD1)); //keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Down, KC_NUMPAD2)); //keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Page_Down, KC_NUMPAD3)); //keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Insert, KC_NUMPAD0)); //keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Delete, KC_DECIMAL)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x7E, KC_UP)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x7D, KC_DOWN)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x7B, KC_LEFT)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x7C, KC_RIGHT)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x74, KC_PGUP)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x79, KC_PGDOWN)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x73, KC_HOME)); keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x77, KC_END)); //keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_Print, KC_SYSRQ)); // ?? //keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_Scroll_Lock, KC_SCROLL)); // ?? //keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_Pause, KC_PAUSE)); // ?? //keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_Insert, KC_INSERT)); // ?? keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x75, KC_DELETE)); // del under help key? }