/* Copyright: © 2018 SIL International. Description: This is a test implementation of the keyboard processor API to enable testing API clients against a basic keyboard and give them something to link against and load. TODO: Add a mechanism to trigger output of PERSIST_OPT & RESET_OPT actions items, options support and context matching. Create Date: 17 Oct 2018 Authors: Tim Eves (TSE) History: 17 Oct 2018 - TSE - Initial implementation. */ #include "mock/mock_processor.hpp" #include "state.hpp" namespace { // An table of VKEY to character sequence mappings, one per shift state. // An empty string inidicates no mapping. constexpr char const table[2][256][4] = { { /*00-0F*/"","","","","","","","","","\t","","","","\015","","", /*10-1F*/"","","","","","","","","","","","\033","","","","", /*20-2F*/" ","","","","","","","","","","","","","","","", /*30-3F*/"0","1","2","3","4","5","6","7","8","9","","","","","","", /*40-4F*/"","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o", /*50-5F*/"p","q","r","s","t","u","v","w","x","y","z","","","","","", /*60-6F*/"0","1","2","3","4","5","6","7","8","9","*","+","","-",".","/", /*70-7F*/"f1","f2","f3","f4","f5","f6","f7","f8","f9","f10","f11","f12","f13","f14","f15","f16", /*80-8F*/"f17","f18","f19","f20","f24","","","","","","","","","","","", /*90-9F*/"","","","","","","","","","","","","","","","", /*A0-AF*/"","","","","","","","","","","","","","","","", /*B0-BF*/"","","","","","","","","","",";","=",",","-",".","/", /*C0-CF*/"`","","","","","","","","","","","","","","","", /*D0-DF*/"","","","","","","","","","","","[","\\","]","'","", /*E0-EF*/"","","","","","","","","","","","","","","","", /*F0-FF*/"","","","","","","","","","","","","","","","", }, { /*00-0F*/"","","","","","","","","","\t","","","","\015","","", /*10-1F*/"","","","","","","","","","","","\033","","","","", /*20-2F*/" ","","","","","","","","","","","","","","","", /*30-3F*/")","!","@","#","$","%","^","&","*","(","","","","","","", /*40-4F*/"","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O", /*50-5F*/"P","Q","R","S","T","U","V","W","X","Y","Z","","","","","", /*60-6F*/"","","","","","","","","","","","","","","","", /*70-7F*/"","","","","","","","","","","","","","","","", /*80-8F*/"","","","","","","","","","","","","","","","", /*90-9F*/"","","","","","","","","","","","","","","","", /*A0-AF*/"","","","","","","","","","","","","","","","", /*B0-BF*/"","","","","","","","","","",":","+","<","_",">","?", /*C0-CF*/"~","","","","","","","","","","","","","","","", /*D0-DF*/"","","","","","","","","","","","{","|","}","\"","", /*E0-EF*/"","","","","","","","","","","","","","","","", /*F0-FF*/"","","","","","","","","","","","","","","","", } }; constexpr km_core_attr const engine_attrs = { 256, KM_CORE_LIB_CURRENT, KM_CORE_LIB_AGE, KM_CORE_LIB_REVISION, KM_CORE_TECH_MOCK, "SIL International" }; } namespace km { namespace core { mock_processor::mock_processor(core::path const & path) : abstract_processor( keyboard_attributes(path.stem(), u"3.145", { option{KM_CORE_OPT_KEYBOARD, u"__test_point", u"not tiggered"}, })), _options({ {u"\x01__test_point", u"not tiggered"}, {u"\x02hello", u"-"} }) { } char16_t const * mock_processor::lookup_option(km_core_option_scope scope, std::u16string const & key) const { auto i = _options.find(char16_t(scope) + key); return i != _options.end() ? i->second.c_str() : nullptr; } option mock_processor::update_option(km_core_option_scope scope, std::u16string const & key, std::u16string const & value) { auto result = _options.emplace(char16_t(scope) + key, value); if ( !result.second && result.first == _options.end()) { return option(); } // key already existed so just update the value if(!result.second) { result.first->second = value; } persisted_store()[key] = value; return option(scope, key, result.first->second); } km_core_status mock_processor::process_queued_actions( km_core_state *state ) { assert(state); if (!state) return KM_CORE_STATUS_INVALID_ARGUMENT; // TODO Implement return KM_CORE_STATUS_OK; } bool mock_processor::queue_action( km_core_state * state, km_core_action_item const* action_item ) { assert(state); assert(action_item); if ((!state) || (!action_item)) return false; return false; } km_core_status mock_processor::process_event( km_core_state *state, km_core_virtual_key vk, uint16_t modifier_state, uint8_t is_key_down, uint16_t /* event_flags */ ) { assert(state); if (!state) return KM_CORE_STATUS_INVALID_ARGUMENT; if (!is_key_down) { // TODO: Implement caps lock handling return KM_CORE_STATUS_OK; } try { // At the start of every process_event always clear the action_items state->actions().clear(); switch (vk) { case KM_CORE_VKEY_BKSP: state->context().pop_back(); state->actions().push_backspace(KM_CORE_BT_UNKNOWN); // Assuming we don't know the character break; case KM_CORE_VKEY_F2: { state->actions().push_persist( update_option(KM_CORE_OPT_KEYBOARD, u"__test_point", u"F2 pressed test save.")); break; } case KM_CORE_VKEY_F4: state->context().push_marker(KM_CORE_VKEY_QUOTE); state->actions().push_marker(KM_CORE_VKEY_QUOTE); break; default: { auto shift_state = bool(modifier_state & KM_CORE_MODIFIER_SHIFT); // Only process further one of the shift states has something to output. if (table[0][vk][0] || table[1][vk][0]) { auto char_seq = table[shift_state][vk]; for (auto c = char_seq; *c; ++c) { km_core_usv usv = *c; state->context().push_character(usv); state->actions().push_character(usv); } state->actions().commit(); return KM_CORE_STATUS_OK; } // Both shift states output nothing, generate an alert. state->actions().push_alert(); break; } } state->actions().commit(); } catch (std::bad_alloc &) { state->actions().clear(); return KM_CORE_STATUS_NO_MEM; } return KM_CORE_STATUS_OK; } km_core_attr const & mock_processor::attributes() const { return engine_attrs; } km_core_keyboard_key * mock_processor::get_key_list() const { km_core_keyboard_key* key_list = new km_core_keyboard_key(KM_CORE_KEYBOARD_KEY_LIST_END); return key_list; } km_core_keyboard_imx * mock_processor::get_imx_list() const { km_core_keyboard_imx* imx_list = new km_core_keyboard_imx(KM_CORE_KEYBOARD_IMX_END); return imx_list; } km_core_context_item * mock_processor::get_intermediate_context() { km_core_context_item *citems = new km_core_context_item(KM_CORE_CONTEXT_ITEM_END); return citems; } km_core_status mock_processor::validate() const { return KM_CORE_STATUS_OK; } km_core_status null_processor::validate() const { return KM_CORE_STATUS_INVALID_ARGUMENT; } /** * Returns true if the data starts with 'MOCK'. * * @param data the keyboard blob * @return true if the processor can handle the keyboard, otherwise false. */ bool mock_processor::is_handled(const std::vector& data) { if (data.empty()) { return false; } if (data.size() < 4) { // a MOCK file is at least 4 bytes (MOCK) return false; } return ((char*)data.data())[0] == 'M' && ((char*)data.data())[1] == 'O' && ((char*)data.data())[2] == 'C' && ((char*)data.data())[3] == 'K'; } } // namespace core } // namespace km