#include "precompiled.h" #include "Hotkey.h" #include "lib/input.h" #include "ConfigDB.h" #include "CLogger.h" #include "CConsole.h" #include "CStr.h" #include "ps/Globals.h" extern CConsole* g_Console; static bool unified[5]; /* SDL-type */ struct SHotkeyMapping { int mapsTo; bool negation; std::vector requires; SHotkeyMapping() : mapsTo(-1) {} }; typedef std::vector KeyMapping; // 'Keycodes' for the mouse buttons const int MOUSE_LEFT = SDLK_LAST + SDL_BUTTON_LEFT; const int MOUSE_RIGHT = SDLK_LAST + SDL_BUTTON_RIGHT; const int MOUSE_MIDDLE = SDLK_LAST + SDL_BUTTON_MIDDLE; const int MOUSE_WHEELUP = SDLK_LAST + SDL_BUTTON_WHEELUP; const int MOUSE_WHEELDOWN = SDLK_LAST + SDL_BUTTON_WHEELDOWN; // 'Keycodes' for the unified modifier keys const int UNIFIED_SHIFT = MOUSE_WHEELDOWN + 1; const int UNIFIED_CTRL = MOUSE_WHEELDOWN + 2; const int UNIFIED_ALT = MOUSE_WHEELDOWN + 3; const int UNIFIED_META = MOUSE_WHEELDOWN + 4; const int UNIFIED_SUPER = MOUSE_WHEELDOWN + 5; /** * HK_MAX_KEYCODES: Global maximum number of keycodes, including our "fake" keycodes for * mouse buttons and unified modifiers. */ const int HK_MAX_KEYCODES = UNIFIED_SUPER + 1; // A mapping of keycodes onto sets of SDL event codes static KeyMapping hotkeyMap[HK_MAX_KEYCODES]; // An array of the status of virtual keys bool hotkeys[HOTKEY_LAST]; struct SHotkeyInfo { int code; const char* name; int defaultmapping1, defaultmapping2; }; // Will phase out the default shortcuts at sometime in the near future // (or, failing that, will update them so they can do the tricky stuff // the config file can.) static SHotkeyInfo hotkeyInfo[] = { { HOTKEY_EXIT, "exit", SDLK_ESCAPE, 0 }, { HOTKEY_SCREENSHOT, "screenshot", SDLK_PRINT, 0 }, { HOTKEY_BIGSCREENSHOT, "bigscreenshot", 0, 0 }, { HOTKEY_WIREFRAME, "wireframe", SDLK_w, 0 }, { HOTKEY_CAMERA_RESET, "camera.reset", 0, 0 }, { HOTKEY_CAMERA_RESET_ORIGIN, "camera.reset.origin", SDLK_h, 0 }, { HOTKEY_CAMERA_ZOOM_IN, "camera.zoom.in", SDLK_PLUS, SDLK_KP_PLUS }, { HOTKEY_CAMERA_ZOOM_OUT, "camera.zoom.out", SDLK_MINUS, SDLK_KP_MINUS }, { HOTKEY_CAMERA_ZOOM_WHEEL_IN, "camera.zoom.wheel.in", MOUSE_WHEELUP, 0 }, { HOTKEY_CAMERA_ZOOM_WHEEL_OUT, "camera.zoom.wheel.out", MOUSE_WHEELDOWN, 0 }, { HOTKEY_CAMERA_ROTATE, "camera.rotate", 0, 0 }, { HOTKEY_CAMERA_ROTATE_KEYBOARD, "camera.rotate.keyboard", 0, 0 }, { HOTKEY_CAMERA_ROTATE_ABOUT_TARGET, "camera.rotate.abouttarget", 0, 0 }, { HOTKEY_CAMERA_ROTATE_ABOUT_TARGET_KEYBOARD, "camera.rotate.abouttarget.keyboard", 0, 0 }, { HOTKEY_CAMERA_PAN, "camera.pan", MOUSE_MIDDLE, 0 }, { HOTKEY_CAMERA_PAN_KEYBOARD, "camera.pan.keyboard", 0, 0 }, { HOTKEY_CAMERA_LEFT, "camera.left", SDLK_LEFT, 0 }, { HOTKEY_CAMERA_RIGHT, "camera.right", SDLK_RIGHT, 0 }, { HOTKEY_CAMERA_UP, "camera.up", SDLK_UP, 0 }, { HOTKEY_CAMERA_DOWN, "camera.down", SDLK_DOWN, 0 }, { HOTKEY_CAMERA_UNIT_VIEW, "camera.unit.view", SDLK_u, 0 }, { HOTKEY_CAMERA_UNIT_ATTACH, "camera.unit.attach", SDLK_l, 0 }, { HOTKEY_CAMERA_BOOKMARK_0, "camera.bookmark.0", SDLK_F5, 0, }, { HOTKEY_CAMERA_BOOKMARK_1, "camera.bookmark.1", SDLK_F6, 0, }, { HOTKEY_CAMERA_BOOKMARK_2, "camera.bookmark.2", SDLK_F7, 0, }, { HOTKEY_CAMERA_BOOKMARK_3, "camera.bookmark.3", SDLK_F8, 0, }, { HOTKEY_CAMERA_BOOKMARK_4, "camera.bookmark.4", 0, 0, }, { HOTKEY_CAMERA_BOOKMARK_5, "camera.bookmark.5", 0, 0, }, { HOTKEY_CAMERA_BOOKMARK_6, "camera.bookmark.6", 0, 0, }, { HOTKEY_CAMERA_BOOKMARK_7, "camera.bookmark.7", 0, 0, }, { HOTKEY_CAMERA_BOOKMARK_8, "camera.bookmark.8", 0, 0, }, { HOTKEY_CAMERA_BOOKMARK_9, "camera.bookmark.9", 0, 0, }, { HOTKEY_CAMERA_BOOKMARK_SAVE, "camera.bookmark.save", 0, 0 }, { HOTKEY_CAMERA_BOOKMARK_SNAP, "camera.bookmark.snap", 0, 0 }, { HOTKEY_CAMERA_CINEMA_ADD, "camera.cinema.add", SDLK_l, 0 }, { HOTKEY_CAMERA_CINEMA_DELETE, "camera.cinema.delete", SDLK_u, 0 }, { HOTKEY_CAMERA_CINEMA_DELETE_ALL, "camera.cinema.delete.all", SDLK_r, 0 }, { HOTKEY_CAMERA_CINEMA_QUEUE, "camera.cinema.write", SDLK_i, 0}, { HOTKEY_CONSOLE_TOGGLE, "console.toggle", SDLK_F1, 0 }, { HOTKEY_CONSOLE_COPY, "console.copy", 0, 0 }, { HOTKEY_CONSOLE_PASTE, "console.paste", 0, 0 }, { HOTKEY_SELECTION_ADD, "selection.add", SDLK_LSHIFT, SDLK_RSHIFT }, { HOTKEY_SELECTION_REMOVE, "selection.remove", SDLK_LCTRL, SDLK_RCTRL }, { HOTKEY_SELECTION_GROUP_0, "selection.group.0", SDLK_0, 0, }, { HOTKEY_SELECTION_GROUP_1, "selection.group.1", SDLK_1, 0, }, { HOTKEY_SELECTION_GROUP_2, "selection.group.2", SDLK_2, 0, }, { HOTKEY_SELECTION_GROUP_3, "selection.group.3", SDLK_3, 0, }, { HOTKEY_SELECTION_GROUP_4, "selection.group.4", SDLK_4, 0, }, { HOTKEY_SELECTION_GROUP_5, "selection.group.5", SDLK_5, 0, }, { HOTKEY_SELECTION_GROUP_6, "selection.group.6", SDLK_6, 0, }, { HOTKEY_SELECTION_GROUP_7, "selection.group.7", SDLK_7, 0, }, { HOTKEY_SELECTION_GROUP_8, "selection.group.8", SDLK_8, 0, }, { HOTKEY_SELECTION_GROUP_9, "selection.group.9", SDLK_9, 0, }, { HOTKEY_SELECTION_GROUP_10, "selection.group.10", 0, 0, }, { HOTKEY_SELECTION_GROUP_11, "selection.group.11", 0, 0, }, { HOTKEY_SELECTION_GROUP_12, "selection.group.12", 0, 0, }, { HOTKEY_SELECTION_GROUP_13, "selection.group.13", 0, 0, }, { HOTKEY_SELECTION_GROUP_14, "selection.group.14", 0, 0, }, { HOTKEY_SELECTION_GROUP_15, "selection.group.15", 0, 0, }, { HOTKEY_SELECTION_GROUP_16, "selection.group.16", 0, 0, }, { HOTKEY_SELECTION_GROUP_17, "selection.group.17", 0, 0, }, { HOTKEY_SELECTION_GROUP_18, "selection.group.18", 0, 0, }, { HOTKEY_SELECTION_GROUP_19, "selection.group.19", 0, 0, }, { HOTKEY_SELECTION_GROUP_ADD, "selection.group.add", SDLK_LSHIFT, SDLK_RSHIFT }, { HOTKEY_SELECTION_GROUP_SAVE, "selection.group.save", SDLK_LCTRL, SDLK_RCTRL }, { HOTKEY_SELECTION_GROUP_SNAP, "selection.group.snap", SDLK_LALT, SDLK_RALT }, { HOTKEY_SELECTION_SNAP, "selection.snap", SDLK_HOME, 0 }, { HOTKEY_ORDER_QUEUE, "order.queue", SDLK_LSHIFT, SDLK_RSHIFT }, { HOTKEY_CONTEXTORDER_NEXT, "contextorder.next", SDLK_RIGHTBRACKET, 0 }, { HOTKEY_CONTEXTORDER_PREVIOUS, "contextorder.previous", SDLK_LEFTBRACKET, 0 }, { HOTKEY_HIGHLIGHTALL, "highlightall", SDLK_o, 0 }, { HOTKEY_PROFILE_TOGGLE, "profile.toggle", SDLK_F11, 0 }, { HOTKEY_PROFILE_SAVE, "profile.save", 0, 0 }, { HOTKEY_PLAYMUSIC, "playmusic", SDLK_p, 0 }, { HOTKEY_WATER_TOGGLE, "water.toggle", SDLK_q, 0 }, { HOTKEY_WATER_RAISE, "water.toggle", SDLK_a, 0 }, { HOTKEY_WATER_LOWER, "water.toggle", SDLK_z, 0 }, { HOTKEY_PAUSE, "pause", SDLK_PAUSE, 0 }, { HOTKEY_SPEED_INCREASE, "speed.increase", 0, 0 }, { HOTKEY_SPEED_DECREASE, "speed.decrease", 0, 0 } }; /* SDL-type ends */ /* GUI-type */ struct SHotkeyMappingGUI { CStr mapsTo; bool negation; std::vector requires; SHotkeyMappingGUI() : mapsTo(-1) {} }; typedef std::vector GuiMapping; // A mapping of keycodes onto sets of hotkey name strings (e.g. '[hotkey.]camera.reset') static GuiMapping hotkeyMapGUI[HK_MAX_KEYCODES]; typedef std::vector GUIObjectList; // A list of GUI objects typedef std::map GUIHotkeyMap; // A mapping of name strings to lists of GUI objects that they trigger static GUIHotkeyMap guiHotkeyMap; // Look up a key binding in the config file and set the mappings for // all key combinations that trigger it. void setBindings( const CStr& hotkeyName, int integerMapping = -1 ) { CConfigValueSet* binding = g_ConfigDB.GetValues( CFG_USER, CStr( "hotkey." ) + hotkeyName ); if( binding ) { int mapping; CConfigValueSet::iterator it; CParser multikeyParser; multikeyParser.InputTaskType( "multikey", "<[!$arg(_negate)][~$arg(_negate)]$value_+_>_[!$arg(_negate)][~$arg(_negate)]$value" ); // Iterate through the bindings for this event... for( it = binding->begin(); it != binding->end(); it++ ) { std::string hotkey; if( it->GetString( hotkey ) ) { std::vector keyCombination; CParserLine multikeyIdentifier; multikeyIdentifier.ParseString( multikeyParser, hotkey ); // Iterate through multiple-key bindings (e.g. Ctrl+I) bool negateNext = false; for( size_t t = 0; t < multikeyIdentifier.GetArgCount(); t++ ) { if( multikeyIdentifier.GetArgString( (int)t, hotkey ) ) { if( hotkey == "_negate" ) { negateNext = true; continue; } // Attempt decode as key name mapping = getKeyCode( hotkey ); // Attempt to decode as a negation of a keyname // Yes, it's going a bit far, perhaps. // Too powerful for most uses, probably. // However, it got some hardcoding out of the engine. // Thus it makes me happy. if( !mapping ) if( !it->GetInt( mapping ) ) // Attempt decode as key code { LOG( WARNING, "hotkey", "Couldn't map '%s'", hotkey.c_str() ); continue; } if( negateNext ) mapping |= HOTKEY_NEGATION_FLAG; negateNext = false; keyCombination.push_back( mapping ); } } std::vector::iterator itKey, itKey2; SHotkeyMapping bindCode; SHotkeyMappingGUI bindName; for( itKey = keyCombination.begin(); itKey != keyCombination.end(); itKey++ ) { bindName.mapsTo = hotkeyName; bindName.negation = ( ( *itKey & HOTKEY_NEGATION_FLAG ) ? true : false ); bindName.requires.clear(); if( integerMapping != -1 ) { bindCode.mapsTo = integerMapping; bindCode.negation = ( ( *itKey & HOTKEY_NEGATION_FLAG ) ? true : false ); bindCode.requires.clear(); } for( itKey2 = keyCombination.begin(); itKey2 != keyCombination.end(); itKey2++ ) { // Push any auxiliary keys. if( itKey != itKey2 ) { bindName.requires.push_back( *itKey2 ); if( integerMapping != -1 ) bindCode.requires.push_back( *itKey2 ); } } hotkeyMapGUI[*itKey & ~HOTKEY_NEGATION_FLAG].push_back( bindName ); if( integerMapping != -1 ) hotkeyMap[*itKey & ~HOTKEY_NEGATION_FLAG].push_back( bindCode ); } } } } else if( integerMapping != -1 ) { SHotkeyMapping bind[2]; bind[0].mapsTo = integerMapping; bind[1].mapsTo = integerMapping; bind[0].requires.clear(); bind[1].requires.clear(); bind[0].negation = false; bind[1].negation = false; hotkeyMap[ hotkeyInfo[integerMapping].defaultmapping1 ].push_back( bind[0] ); if( hotkeyInfo[integerMapping].defaultmapping2 ) hotkeyMap[ hotkeyInfo[integerMapping].defaultmapping2 ].push_back( bind[1] ); } } void loadHotkeys() { initKeyNameMap(); int i; for( i = 0; i < HOTKEY_LAST; i++ ) setBindings( hotkeyInfo[i].name, i ); // Set up the state of the hotkeys given no key is down. // i.e. find those hotkeys triggered by all negations. std::vector::iterator it; std::vector::iterator j; bool allNegated; for( i = 1; i < HK_MAX_KEYCODES; i++ ) { for( it = hotkeyMap[i].begin(); it != hotkeyMap[i].end(); it++ ) { if( !it->negation ) continue; allNegated = true; for( j = it->requires.begin(); j != it->requires.end(); j++ ) if( !( *j & HOTKEY_NEGATION_FLAG ) ) allNegated = false; debug_assert(it->mapsTo < ARRAY_SIZE(hotkeys)); if( allNegated ) hotkeys[it->mapsTo] = true; } } } void hotkeyRegisterGUIObject( const CStr& objName, const CStr& hotkeyName ) { GUIObjectList& boundTo = guiHotkeyMap[hotkeyName]; if( boundTo.empty() ) { // Load keybindings from the config file setBindings( hotkeyName ); } boundTo.push_back( objName ); } InReaction hotkeyInputHandler( const SDL_Event* ev ) { int keycode = 0; switch( ev->type ) { case SDL_KEYDOWN: case SDL_KEYUP: keycode = (int)ev->key.keysym.sym; break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: keycode = SDLK_LAST + (int)ev->button.button; break; default: return( IN_PASS ); } // Somewhat hackish: // Create phantom 'unified-modifier' events when left- or right- modifier keys are pressed // Just send them to this handler; don't let the imaginary event codes leak back to real SDL. SDL_Event phantom; phantom.type = ( ( ev->type == SDL_KEYDOWN ) || ( ev->type == SDL_MOUSEBUTTONDOWN ) ) ? SDL_KEYDOWN : SDL_KEYUP; if( ( keycode == SDLK_LSHIFT ) || ( keycode == SDLK_RSHIFT ) ) { (int&)phantom.key.keysym.sym = UNIFIED_SHIFT; unified[0] = ( phantom.type == SDL_KEYDOWN ); hotkeyInputHandler( &phantom ); } else if( ( keycode == SDLK_LCTRL ) || ( keycode == SDLK_RCTRL ) ) { (int&)phantom.key.keysym.sym = UNIFIED_CTRL; unified[1] = ( phantom.type == SDL_KEYDOWN ); hotkeyInputHandler( &phantom ); } else if( ( keycode == SDLK_LALT ) || ( keycode == SDLK_RALT ) ) { (int&)phantom.key.keysym.sym = UNIFIED_ALT; unified[2] = ( phantom.type == SDL_KEYDOWN ); hotkeyInputHandler( &phantom ); } else if( ( keycode == SDLK_LMETA ) || ( keycode == SDLK_RMETA ) ) { (int&)phantom.key.keysym.sym = UNIFIED_META; unified[3] = ( phantom.type == SDL_KEYDOWN ); hotkeyInputHandler( &phantom ); } else if( ( keycode == SDLK_LSUPER ) || ( keycode == SDLK_RSUPER ) ) { (int&)phantom.key.keysym.sym = UNIFIED_SUPER; unified[4] = ( phantom.type == SDL_KEYDOWN ); hotkeyInputHandler( &phantom ); } // Inhibit the dispatch of hotkey events caused by printable or control keys // while the console is up. (But allow multiple-key - 'Ctrl+F' events, and whatever // key toggles the console.) bool consoleCapture = false, isCapturable; if( g_Console->IsActive() && ( ( keycode == 8 ) || ( keycode == 9 ) || ( keycode == 13 ) || /* Editing */ ( ( keycode >= 32 ) && ( keycode < 273 ) ) || /* Printable (<128), 'World' (<256) */ ( ( keycode >= 273 ) && ( keycode < 282 ) && /* Numeric keypad (<273), navigation */ ( keycode != SDLK_INSERT ) ) ) ) /* keys (<282) except insert */ consoleCapture = true; std::vector::iterator it; std::vector::iterator itGUI; SDL_Event hotkeyNotification; // Here's an interesting bit: // If you have an event bound to, say, 'F', and another to, say, 'Ctrl+F', pressing // 'F' while control is down would normally fire off both. // To avoid this, set the modifier keys for /all/ events this key would trigger // (Ctrl, for example, is both group-save and bookmark-save) // but only send a HotkeyDown event for the event with bindings most precisely // matching the conditions (i.e. the event with the highest number of auxiliary // keys, providing they're all down) bool typeKeyDown = ( ev->type == SDL_KEYDOWN ) || ( ev->type == SDL_MOUSEBUTTONDOWN ); // -- KEYDOWN SECTION -- // SDL-events bit uint closestMap = 0; // avoid "uninitialized" warning size_t closestMapMatch = 0; for( it = hotkeyMap[keycode].begin(); it < hotkeyMap[keycode].end(); it++ ) { // If a key has been pressed, and this event triggers on it's release, skip it. // Similarly, if the key's been released and the event triggers on a keypress, skip it. if( it->negation == typeKeyDown ) continue; // Check to see if all auxiliary keys are down std::vector::iterator itKey; bool accept = true; isCapturable = true; for( itKey = it->requires.begin(); itKey != it->requires.end(); itKey++ ) { int keyCode = *itKey & ~HOTKEY_NEGATION_FLAG; // Clear the negation-modifier bit bool rqdState = ( *itKey & HOTKEY_NEGATION_FLAG ) == 0; // debug_assert( !rqdState ); if( keyCode < SDLK_LAST ) { if( g_keys[keyCode] != rqdState ) accept = false; } else if( keyCode < UNIFIED_SHIFT ) { if( g_mouse_buttons[keyCode-SDLK_LAST] != rqdState ) accept = false; } else if( (uint)(keyCode-UNIFIED_SHIFT) < ARRAY_SIZE(unified) ) { if( unified[keyCode-UNIFIED_SHIFT] != rqdState ) accept = false; } else { debug_printf("keyCode = %i\n", keyCode); debug_warn("keyCode out of range in GUI hotkey requirements"); } // If this event requires a multiple keypress (with the exception // of shift+key combinations) the console won't inhibit it. if( rqdState && ( *itKey != SDLK_RSHIFT ) && ( *itKey != SDLK_LSHIFT ) ) isCapturable = false; } if( it->mapsTo == HOTKEY_CONSOLE_TOGGLE ) isCapturable = false; // Because that would be silly. debug_assert(it->mapsTo < ARRAY_SIZE(hotkeys)); if( accept && !( isCapturable && consoleCapture ) ) { hotkeys[it->mapsTo] = true; if( it->requires.size() >= closestMapMatch ) { // Only if it's a more precise match, and it either isn't capturable or the console won't capture it. closestMap = it->mapsTo; closestMapMatch = it->requires.size() + 1; } } } if( closestMapMatch ) { hotkeyNotification.type = SDL_HOTKEYDOWN; hotkeyNotification.user.code = closestMap; SDL_PushEvent( &hotkeyNotification ); } // GUI bit... could do with some optimization later. CStr closestMapName = -1; closestMapMatch = 0; for( itGUI = hotkeyMapGUI[keycode].begin(); itGUI != hotkeyMapGUI[keycode].end(); itGUI++ ) { // If a key has been pressed, and this event triggers on it's release, skip it. // Similarly, if the key's been released and the event triggers on a keypress, skip it. if( itGUI->negation == typeKeyDown ) continue; // Check to see if all auxiliary keys are down std::vector::iterator itKey; bool accept = true; isCapturable = true; for( itKey = itGUI->requires.begin(); itKey != itGUI->requires.end(); itKey++ ) { int keyCode = *itKey & ~HOTKEY_NEGATION_FLAG; // Clear the negation-modifier bit bool rqdState = ( *itKey & HOTKEY_NEGATION_FLAG ) == 0; if( keyCode < SDLK_LAST ) { if( g_keys[keyCode] != rqdState ) accept = false; } else if( keyCode < UNIFIED_SHIFT ) { if( g_mouse_buttons[keyCode-SDLK_LAST] != rqdState ) accept = false; } else if( (uint)(keyCode-UNIFIED_SHIFT) < ARRAY_SIZE(unified) ) { if( unified[keyCode-UNIFIED_SHIFT] != rqdState ) accept = false; } else { debug_printf("keyCode = %i\n", keyCode); debug_warn("keyCode out of range in GUI hotkey requirements"); } // If this event requires a multiple keypress (with the exception // of shift+key combinations) the console won't inhibit it. if( rqdState && ( *itKey != SDLK_RSHIFT ) && ( *itKey != SDLK_LSHIFT ) ) isCapturable = false; } if( accept && !( isCapturable && consoleCapture ) ) { if( itGUI->requires.size() >= closestMapMatch ) { closestMapName = itGUI->mapsTo; closestMapMatch = itGUI->requires.size() + 1; } } } // GUI-objects bit // This fragment is an obvious candidate for rewriting when speed becomes an issue. if( closestMapMatch ) { GUIHotkeyMap::iterator map_it; GUIObjectList::iterator obj_it; map_it = guiHotkeyMap.find( closestMapName ); if( map_it != guiHotkeyMap.end() ) { GUIObjectList& targets = map_it->second; for( obj_it = targets.begin(); obj_it != targets.end(); obj_it++ ) { hotkeyNotification.type = SDL_GUIHOTKEYPRESS; hotkeyNotification.user.data1 = &(*obj_it); SDL_PushEvent( &hotkeyNotification ); } } } // -- KEYUP SECTION -- for( it = hotkeyMap[keycode].begin(); it < hotkeyMap[keycode].end(); it++ ) { // If it's a keydown event, won't cause HotKeyUps in anything that doesn't // use this key negated => skip them // If it's a keyup event, won't cause HotKeyUps in anything that does use // this key negated => skip them too. if( it->negation != typeKeyDown ) continue; // Check to see if all auxiliary keys are down std::vector::iterator itKey; bool accept = true; for( itKey = it->requires.begin(); itKey != it->requires.end(); itKey++ ) { if( *itKey < SDLK_LAST ) { if( !g_keys[*itKey] ) accept = false; } else if( *itKey < UNIFIED_SHIFT ) { if( !g_mouse_buttons[(*itKey)-SDLK_LAST] ) accept = false; } else if( *itKey < HOTKEY_NEGATION_FLAG ) { if( !unified[(*itKey)-UNIFIED_SHIFT] ) accept = false; } } debug_assert(it->mapsTo < ARRAY_SIZE(hotkeys)); if( accept ) { hotkeys[it->mapsTo] = false; hotkeyNotification.type = SDL_HOTKEYUP; hotkeyNotification.user.code = it->mapsTo; SDL_PushEvent( &hotkeyNotification ); } } return( IN_PASS ); } // Returns true if the specified HOTKEY_* responds to the specified SDLK_* // (mainly for the screenshot system to know whether it needs to override // the printscreen screen). Ignores modifier keys. bool keyRespondsTo( int hotkey, int sdlkey ) { for (KeyMapping::iterator it = hotkeyMap[sdlkey].begin(); it != hotkeyMap[sdlkey].end(); ++it) if (it->mapsTo == hotkey) return true; return false; } // Returns true if one of the key combinations for the given hotkey is pressed bool isKeyDown( const CStr& keyname ) { return hotkeys[getKeyCode(keyname)]; }