1515#include < functional>
1616#include < unordered_map>
1717#include < regex>
18+ #include < cctype>
19+ #include < limits>
1820
1921#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
2022 #include < windows.h>
@@ -96,18 +98,144 @@ unsigned int hashesFailed = 0;
9698/* Will be incremented at each `hash` command */
9799unsigned int hashesTested = 0 ;
98100
99- struct coord2d { uint8_t x; uint8_t y; };
100101// Note: we could just store the string in a char*[8][8], then search for it and calculate its row/col at runtime, but meh.
101- static const std::unordered_map<std::string, coord2d > valid_keys = {
102- {" graph" , {0 , 1 }}, {" trace" , {1 ,1 }}, { " zoom" , {2 , 1 }}, {" window" , {3 , 1 }}, {" y=" , {4 , 1 }}, {" 2nd" , {5 , 1 }}, { " mode" , {6 , 1 }}, { " del" , {7 , 1 }},
103- { " on" , {0 , 2 }}, { " sto" , {1 , 2 }}, { " ln" , {2 ,2 }}, { " log" , {3 , 2 }}, {" ^2" , {4 , 2 }}, { " -1" , {5 , 2 }}, { " math" , {6 , 2 }}, {" alpha" , {7 , 2 }},
104- { " 0" , {0 , 3 }}, { " 1" , {1 , 3 }}, { " 4" , {2 , 3 }}, { " 7" , {3 ,3 }}, { " ," , {4 , 3 }}, {" sin" , {5 , 3 }}, { " apps" , {6 , 3 }}, { " xton" , {7 , 3 }},
105- { " ." , {0 , 4 }}, { " 2" , {1 , 4 }}, { " 5" , {2 , 4 }}, { " 8 " , {3 ,4 }}, { " ( " , {4 ,4 }}, {" cos" , {5 , 4 }}, { " prgm" , {6 , 4 }}, { " stat" , {7 , 4 }},
106- { " (-)" , {0 , 5 }}, { " 3" , {1 , 5 }}, { " 6" , {2 , 5 }}, { " 9" , {3 , 5 }}, { " )" , {4 , 5 }}, {" tan" , {5 ,5 }}, { " vars" , {6 , 5 }},
107- {" enter" , {0 , 6 }}, { " +" , {1 , 6 }}, { " -" , {2 , 6 }}, { " *" , {3 , 6 }}, { " /" , {4 , 6 }}, { " ^" , {5 , 6 }}, {" clear" , {6 ,6 }},
108- { " down" , {0 , 7 }}, { " left" , {1 , 7 }}, {" right" , {2 , 7 }}, { " up" , {3 , 7 }}
102+ static const std::unordered_map<std::string, key_coord_t > valid_keys = {
103+ {" graph" , {1 , 0 }}, {" trace" , {1 ,1 }}, { " zoom" , {1 , 2 }}, {" window" , {1 , 3 }}, { " wind " , { 1 , 3 }}, { " y=" , {1 , 4 }}, { " yequ " , { 1 , 4 }}, { " 2nd" , {1 , 5 }}, { " mode" , {1 , 6 }}, { " del" , {1 , 7 }},
104+ { " on" , {2 , 0 }}, { " sto" , {2 , 1 }}, { " ln" , {2 ,2 }}, { " log" , {2 , 3 }}, { " ^2" , {2 , 4 }}, { " sq " , { 2 , 4 }}, { " -1" , {2 , 5 }}, { " inv " , { 2 , 5 }}, { " math" , {2 , 6 }}, {" alpha" , {2 , 7 }},
105+ { " 0" , {3 , 0 }}, { " 1" , {3 , 1 }}, { " 4" , {3 , 2 }}, { " 7" , {3 ,3 }}, { " ," , {3 , 4 }}, { " comma " , { 3 , 4 }}, { " sin" , {3 , 5 }}, { " apps" , {3 , 6 }}, {" xton" , {3 , 7 }}, { " xtn " , { 3 , 7 }},
106+ { " ." , {4 , 0 }}, { " dot " , { 4 , 0 }}, { " 2" , {4 , 1 }}, { " 5" , {4 , 2 }}, { " 8 " , { 4 , 3 }}, { " ( " , {4 ,4 }}, { " lpar " , {4 ,4 }}, { " cos" , {4 , 5 }}, { " prgm" , {4 , 6 }}, { " stat" , {4 , 7 }},
107+ { " (-)" , {5 , 0 }}, { " neg " , { 5 , 0 }}, { " 3" , {5 , 1 }}, { " 6" , {5 , 2 }}, { " 9" , {5 , 3 }}, { " )" , {5 , 4 }}, { " rpar " , { 5 , 4 }}, { " tan" , {5 ,5 }}, { " vars" , {5 , 6 }},
108+ {" enter" , {6 , 0 }}, { " +" , {6 , 1 }}, { " add " , { 6 , 1 }}, { " -" , {6 , 2 }}, { " sub " , { 6 , 2 }}, { " *" , {6 , 3 }}, { " mul " , { 6 , 3 }}, { " /" , {6 , 4 }}, { " div " , { 6 , 4 }}, { " ^" , {6 , 5 }}, { " pow " , { 6 , 5 }}, {" clear" , { 6 , 6 }}, { " clr " , {6 ,6 }},
109+ { " down" , {7 , 0 }}, { " left" , {7 , 1 }}, {" right" , {7 , 2 }}, { " up" , {7 , 3 }}
109110};
110111
112+ static bool parseMilliseconds (const std::string& str, bool allowZero, unsigned int & ms)
113+ {
114+ if (str.empty ()) {
115+ return false ;
116+ }
117+
118+ unsigned long value = 0 ;
119+ for (const char c : str) {
120+ if (!std::isdigit (static_cast <unsigned char >(c))) {
121+ return false ;
122+ }
123+ value = value * 10 + static_cast <unsigned long >(c - ' 0' );
124+ if (value > std::numeric_limits<unsigned int >::max ()) {
125+ return false ;
126+ }
127+ }
128+
129+ if (!allowZero && value == 0 ) {
130+ return false ;
131+ }
132+
133+ ms = static_cast <unsigned int >(value);
134+ return true ;
135+ }
136+
137+ static void reportSequenceError (const key_sequence_handlers_t & handlers, const std::string& error)
138+ {
139+ if (handlers.error ) {
140+ handlers.error (error);
141+ }
142+ }
143+
144+ bool keyCoordForName (const std::string& name, key_coord_t & coord)
145+ {
146+ const auto tmp = valid_keys.find (name);
147+ if (tmp == valid_keys.end ()) {
148+ return false ;
149+ }
150+
151+ coord = tmp->second ;
152+ return true ;
153+ }
154+
155+ bool runKeySequence (const std::string& sequence, const key_sequence_handlers_t & handlers)
156+ {
157+ if (!handlers.keyEvent || !handlers.delay ) {
158+ reportSequenceError (handlers, " Bad 'keys' sequence handler" );
159+ return false ;
160+ }
161+
162+ bool ok = true ;
163+ std::stringstream ss (sequence);
164+ std::string rawStep;
165+ while (std::getline (ss, rawStep, ' ,' )) {
166+ const std::string& step = rawStep;
167+ if (step.find (" delay:" ) == 0 ) {
168+ const size_t separator = step.find (' :' );
169+ unsigned int delayMs = 0 ;
170+ if (!parseMilliseconds (step.substr (separator + 1 ), true , delayMs)) {
171+ reportSequenceError (handlers, " Bad delay in 'keys' sequence: " + step);
172+ ok = false ;
173+ continue ;
174+ }
175+ handlers.delay (delayMs);
176+ continue ;
177+ }
178+
179+ if (step.find (" hold:" ) == 0 ) {
180+ const size_t firstSeparator = step.find (' :' );
181+ const size_t secondSeparator = step.find (' :' , firstSeparator + 1 );
182+ if (secondSeparator == std::string::npos || step.find (' :' , secondSeparator + 1 ) != std::string::npos) {
183+ reportSequenceError (handlers, " Bad hold step in 'keys' sequence: " + step);
184+ ok = false ;
185+ continue ;
186+ }
187+
188+ unsigned int holdMs = 0 ;
189+ if (!parseMilliseconds (step.substr (secondSeparator + 1 ), false , holdMs)) {
190+ reportSequenceError (handlers, " Bad hold duration in 'keys' sequence: " + step);
191+ ok = false ;
192+ continue ;
193+ }
194+
195+ key_coord_t coord{};
196+ if (!keyCoordForName (step.substr (firstSeparator + 1 , secondSeparator - firstSeparator - 1 ), coord)) {
197+ reportSequenceError (handlers, " Unknown key in 'keys' sequence: " + step);
198+ ok = false ;
199+ continue ;
200+ }
201+
202+ handlers.keyEvent (coord.y , coord.x , true );
203+ handlers.delay (holdMs);
204+ handlers.keyEvent (coord.y , coord.x , false );
205+ handlers.delay (handlers.delay_after_step_ms );
206+ continue ;
207+ }
208+
209+ if (step.find (" down:" ) == 0 || step.find (" up:" ) == 0 ) {
210+ const size_t separator = step.find (' :' );
211+ key_coord_t coord{};
212+ if (!keyCoordForName (step.substr (separator + 1 ), coord)) {
213+ reportSequenceError (handlers, " Unknown key in 'keys' sequence: " + step);
214+ ok = false ;
215+ continue ;
216+ }
217+ handlers.keyEvent (coord.y , coord.x , step.find (" down:" ) == 0 );
218+ handlers.delay (handlers.delay_after_step_ms );
219+ continue ;
220+ }
221+
222+ const std::string keyName = step.find (" press:" ) == 0 ? step.substr (6 ) : step;
223+ key_coord_t coord{};
224+ if (!keyCoordForName (keyName, coord)) {
225+ reportSequenceError (handlers, " Unknown key in 'keys' sequence: " + keyName);
226+ ok = false ;
227+ continue ;
228+ }
229+
230+ handlers.keyEvent (coord.y , coord.x , true );
231+ handlers.delay (handlers.default_hold_ms );
232+ handlers.keyEvent (coord.y , coord.x , false );
233+ handlers.delay (handlers.delay_after_step_ms );
234+ }
235+
236+ return ok;
237+ }
238+
111239void sendCSC (uint8_t csc)
112240{
113241 int retry = 200 ;
@@ -278,10 +406,9 @@ static const std::unordered_map<std::string, seq_cmd_func_t> valid_seq_commands
278406 },
279407 {
280408 " key" , [](const std::string& which_key) {
281- const auto & tmp = valid_keys. find (which_key) ;
282- if (tmp != valid_keys. end ( ))
409+ key_coord_t key_coords{} ;
410+ if (keyCoordForName (which_key, key_coords ))
283411 {
284- const coord2d& key_coords = tmp->second ;
285412 cemucore::emu_keypad_event (key_coords.y , key_coords.x , true );
286413 cemucore::emu_run (80 );
287414 cemucore::emu_keypad_event (key_coords.y , key_coords.x , false );
@@ -294,6 +421,23 @@ static const std::unordered_map<std::string, seq_cmd_func_t> valid_seq_commands
294421 };
295422 }
296423 },
424+ {
425+ " keys" , [](const std::string& sequence) {
426+ key_sequence_handlers_t handlers;
427+ handlers.keyEvent = [](uint8_t row, uint8_t col, bool pressed) {
428+ cemucore::emu_keypad_event (row, col, pressed);
429+ };
430+ handlers.delay = [](unsigned int ms) {
431+ cemucore::emu_run (ms);
432+ };
433+ handlers.error = [](const std::string& error) {
434+ std::cerr << " \t [Error] " << error << std::endl;
435+ };
436+ handlers.delay_after_step_ms = config.delay_after_key ;
437+
438+ runKeySequence (sequence, handlers);
439+ }
440+ },
297441 {
298442 " sendCSC" , [](const std::string& str) {
299443 std::vector<std::string> parts;
@@ -309,10 +453,9 @@ static const std::unordered_map<std::string, seq_cmd_func_t> valid_seq_commands
309453 }
310454 for (const auto & part : parts)
311455 {
312- const auto & tmp = valid_keys. find (part) ;
313- if (tmp != valid_keys. end ( ))
456+ key_coord_t key_coords{} ;
457+ if (keyCoordForName (part, key_coords ))
314458 {
315- const coord2d& key_coords = tmp->second ;
316459 sendCSC ((7 -key_coords.y )*8 + key_coords.x + 1 );
317460 if (config.delay_after_key > 0 )
318461 {
@@ -326,10 +469,9 @@ static const std::unordered_map<std::string, seq_cmd_func_t> valid_seq_commands
326469 },
327470 {
328471 " hold" , [](const std::string& which_key) {
329- const auto & tmp = valid_keys. find (which_key) ;
330- if (tmp != valid_keys. end ( ))
472+ key_coord_t key_coords{} ;
473+ if (keyCoordForName (which_key, key_coords ))
331474 {
332- const coord2d& key_coords = tmp->second ;
333475 cemucore::emu_keypad_event (key_coords.y , key_coords.x , true );
334476 } else {
335477 std::cerr << " \t [Error] unknown key \" " << which_key << " \" was not hold." << std::endl;
@@ -338,10 +480,9 @@ static const std::unordered_map<std::string, seq_cmd_func_t> valid_seq_commands
338480 },
339481 {
340482 " release" , [](const std::string& which_key) {
341- const auto & tmp = valid_keys. find (which_key) ;
342- if (tmp != valid_keys. end ( ))
483+ key_coord_t key_coords{} ;
484+ if (keyCoordForName (which_key, key_coords ))
343485 {
344- const coord2d& key_coords = tmp->second ;
345486 cemucore::emu_keypad_event (key_coords.y , key_coords.x , false );
346487 } else {
347488 std::cerr << " \t [Error] unknown key \" " << which_key << " \" was not released." << std::endl;
0 commit comments