process_terminal.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. /* Copyright 2017 Jack Humbert
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation, either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include "process_terminal.h"
  17. #include <string.h>
  18. #include "version.h"
  19. #include <stdio.h>
  20. #include <math.h>
  21. #ifndef CMD_BUFF_SIZE
  22. #define CMD_BUFF_SIZE 5
  23. #endif
  24. bool terminal_enabled = false;
  25. char buffer[80] = "";
  26. char cmd_buffer[CMD_BUFF_SIZE][80];
  27. bool cmd_buffer_enabled = true; //replace with ifdef?
  28. char newline[2] = "\n";
  29. char arguments[6][20];
  30. bool firstTime = true;
  31. short int current_cmd_buffer_pos = 0; //used for up/down arrows - keeps track of where you are in the command buffer
  32. __attribute__ ((weak))
  33. const char terminal_prompt[8] = "> ";
  34. #ifdef AUDIO_ENABLE
  35. #ifndef TERMINAL_SONG
  36. #define TERMINAL_SONG SONG(TERMINAL_SOUND)
  37. #endif
  38. float terminal_song[][2] = TERMINAL_SONG;
  39. #define TERMINAL_BELL() PLAY_SONG(terminal_song)
  40. #else
  41. #define TERMINAL_BELL()
  42. #endif
  43. __attribute__ ((weak))
  44. const char keycode_to_ascii_lut[58] = {
  45. 0, 0, 0, 0,
  46. 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
  47. 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  48. '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 0, 0, 0, '\t',
  49. ' ', '-', '=', '[', ']', '\\', 0, ';', '\'', '`', ',', '.', '/'
  50. };
  51. __attribute__ ((weak))
  52. const char shifted_keycode_to_ascii_lut[58] = {
  53. 0, 0, 0, 0,
  54. 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
  55. 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  56. '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', 0, 0, 0, '\t',
  57. ' ', '_', '+', '{', '}', '|', 0, ':', '\'', '~', '<', '>', '?'
  58. };
  59. struct stringcase {
  60. char* string;
  61. void (*func)(void);
  62. } typedef stringcase;
  63. void enable_terminal(void) {
  64. terminal_enabled = true;
  65. strcpy(buffer, "");
  66. memset(cmd_buffer,0,CMD_BUFF_SIZE * 80);
  67. for (int i = 0; i < 6; i++)
  68. strcpy(arguments[i], "");
  69. // select all text to start over
  70. // SEND_STRING(SS_LCTRL("a"));
  71. send_string(terminal_prompt);
  72. }
  73. void disable_terminal(void) {
  74. terminal_enabled = false;
  75. SEND_STRING("\n");
  76. }
  77. void push_to_cmd_buffer(void) {
  78. if (cmd_buffer_enabled) {
  79. if (cmd_buffer == NULL) {
  80. return;
  81. } else {
  82. if (firstTime) {
  83. firstTime = false;
  84. strcpy(cmd_buffer[0],buffer);
  85. return;
  86. }
  87. for (int i= CMD_BUFF_SIZE - 1;i > 0 ;--i) {
  88. strncpy(cmd_buffer[i],cmd_buffer[i-1],80);
  89. }
  90. strcpy(cmd_buffer[0],buffer);
  91. return;
  92. }
  93. }
  94. }
  95. void terminal_about(void) {
  96. SEND_STRING("QMK Firmware\n");
  97. SEND_STRING(" v");
  98. SEND_STRING(QMK_VERSION);
  99. SEND_STRING("\n"SS_TAP(X_HOME)" Built: ");
  100. SEND_STRING(QMK_BUILDDATE);
  101. send_string(newline);
  102. #ifdef TERMINAL_HELP
  103. if (strlen(arguments[1]) != 0) {
  104. SEND_STRING("You entered: ");
  105. send_string(arguments[1]);
  106. send_string(newline);
  107. }
  108. #endif
  109. }
  110. void terminal_help(void);
  111. extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS];
  112. void terminal_keycode(void) {
  113. if (strlen(arguments[1]) != 0 && strlen(arguments[2]) != 0 && strlen(arguments[3]) != 0) {
  114. char keycode_dec[5];
  115. char keycode_hex[5];
  116. uint16_t layer = strtol(arguments[1], (char **)NULL, 10);
  117. uint16_t row = strtol(arguments[2], (char **)NULL, 10);
  118. uint16_t col = strtol(arguments[3], (char **)NULL, 10);
  119. uint16_t keycode = pgm_read_word(&keymaps[layer][row][col]);
  120. itoa(keycode, keycode_dec, 10);
  121. itoa(keycode, keycode_hex, 16);
  122. SEND_STRING("0x");
  123. send_string(keycode_hex);
  124. SEND_STRING(" (");
  125. send_string(keycode_dec);
  126. SEND_STRING(")\n");
  127. } else {
  128. #ifdef TERMINAL_HELP
  129. SEND_STRING("usage: keycode <layer> <row> <col>\n");
  130. #endif
  131. }
  132. }
  133. void terminal_keymap(void) {
  134. if (strlen(arguments[1]) != 0) {
  135. uint16_t layer = strtol(arguments[1], (char **)NULL, 10);
  136. for (int r = 0; r < MATRIX_ROWS; r++) {
  137. for (int c = 0; c < MATRIX_COLS; c++) {
  138. uint16_t keycode = pgm_read_word(&keymaps[layer][r][c]);
  139. char keycode_s[8];
  140. sprintf(keycode_s, "0x%04x,", keycode);
  141. send_string(keycode_s);
  142. }
  143. send_string(newline);
  144. }
  145. } else {
  146. #ifdef TERMINAL_HELP
  147. SEND_STRING("usage: keymap <layer>\n");
  148. #endif
  149. }
  150. }
  151. void print_cmd_buff(void) {
  152. /* without the below wait, a race condition can occur wherein the
  153. buffer can be printed before it has been fully moved */
  154. wait_ms(250);
  155. for(int i=0;i<CMD_BUFF_SIZE;i++){
  156. char tmpChar = ' ';
  157. itoa(i ,&tmpChar,10);
  158. const char * tmpCnstCharStr = &tmpChar; //because sned_string wont take a normal char *
  159. send_string(tmpCnstCharStr);
  160. SEND_STRING(". ");
  161. send_string(cmd_buffer[i]);
  162. SEND_STRING("\n");
  163. }
  164. }
  165. void flush_cmd_buffer(void) {
  166. memset(cmd_buffer,0,CMD_BUFF_SIZE * 80);
  167. SEND_STRING("Buffer Cleared!\n");
  168. }
  169. stringcase terminal_cases[] = {
  170. { "about", terminal_about },
  171. { "help", terminal_help },
  172. { "keycode", terminal_keycode },
  173. { "keymap", terminal_keymap },
  174. { "flush-buffer" , flush_cmd_buffer},
  175. { "print-buffer" , print_cmd_buff},
  176. { "exit", disable_terminal }
  177. };
  178. void terminal_help(void) {
  179. SEND_STRING("commands available:\n ");
  180. for( stringcase* case_p = terminal_cases; case_p != terminal_cases + sizeof( terminal_cases ) / sizeof( terminal_cases[0] ); case_p++ ) {
  181. send_string(case_p->string);
  182. SEND_STRING(" ");
  183. }
  184. send_string(newline);
  185. }
  186. void command_not_found(void) {
  187. wait_ms(50); //sometimes buffer isnt grabbed quick enough
  188. SEND_STRING("command \"");
  189. send_string(buffer);
  190. SEND_STRING("\" not found\n");
  191. }
  192. void process_terminal_command(void) {
  193. // we capture return bc of the order of events, so we need to manually send a newline
  194. send_string(newline);
  195. char * pch;
  196. uint8_t i = 0;
  197. pch = strtok(buffer, " ");
  198. while (pch != NULL) {
  199. strcpy(arguments[i], pch);
  200. pch = strtok(NULL, " ");
  201. i++;
  202. }
  203. bool command_found = false;
  204. for( stringcase* case_p = terminal_cases; case_p != terminal_cases + sizeof( terminal_cases ) / sizeof( terminal_cases[0] ); case_p++ ) {
  205. if( 0 == strcmp( case_p->string, buffer ) ) {
  206. command_found = true;
  207. (*case_p->func)();
  208. break;
  209. }
  210. }
  211. if (!command_found)
  212. command_not_found();
  213. if (terminal_enabled) {
  214. strcpy(buffer, "");
  215. for (int i = 0; i < 6; i++)
  216. strcpy(arguments[i], "");
  217. SEND_STRING(SS_TAP(X_HOME));
  218. send_string(terminal_prompt);
  219. }
  220. }
  221. void check_pos(void) {
  222. if (current_cmd_buffer_pos >= CMD_BUFF_SIZE) { //if over the top, move it back down to the top of the buffer so you can climb back down...
  223. current_cmd_buffer_pos = CMD_BUFF_SIZE - 1;
  224. } else if (current_cmd_buffer_pos < 0) { //...and if you fall under the bottom of the buffer, reset back to 0 so you can climb back up
  225. current_cmd_buffer_pos = 0;
  226. }
  227. }
  228. bool process_terminal(uint16_t keycode, keyrecord_t *record) {
  229. if (keycode == TERM_ON && record->event.pressed) {
  230. enable_terminal();
  231. return false;
  232. }
  233. if (terminal_enabled && record->event.pressed) {
  234. if (keycode == TERM_OFF && record->event.pressed) {
  235. disable_terminal();
  236. return false;
  237. }
  238. if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX)) {
  239. keycode = keycode & 0xFF;
  240. }
  241. if (keycode < 256) {
  242. uint8_t str_len;
  243. char char_to_add;
  244. switch (keycode) {
  245. case KC_ENTER:
  246. case KC_KP_ENTER:
  247. push_to_cmd_buffer();
  248. current_cmd_buffer_pos = 0;
  249. process_terminal_command();
  250. return false; break;
  251. case KC_ESC:
  252. SEND_STRING("\n");
  253. enable_terminal();
  254. return false; break;
  255. case KC_BSPC:
  256. str_len = strlen(buffer);
  257. if (str_len > 0) {
  258. buffer[str_len-1] = 0;
  259. return true;
  260. } else {
  261. TERMINAL_BELL();
  262. return false;
  263. } break;
  264. case KC_LEFT:
  265. return false; break;
  266. case KC_RIGHT:
  267. return false; break;
  268. case KC_UP: // 0 = recent
  269. check_pos(); //check our current buffer position is valid
  270. if (current_cmd_buffer_pos <= CMD_BUFF_SIZE - 1) { //once we get to the top, dont do anything
  271. str_len = strlen(buffer);
  272. for(int i= 0;i < str_len ;++i) {
  273. send_string(SS_TAP(X_BSPACE)); //clear w/e is on the line already
  274. //process_terminal(KC_BSPC,record);
  275. }
  276. strncpy(buffer,cmd_buffer[current_cmd_buffer_pos],80);
  277. send_string(buffer);
  278. ++current_cmd_buffer_pos; //get ready to access the above cmd if up/down is pressed again
  279. }
  280. return false; break;
  281. case KC_DOWN:
  282. check_pos();
  283. if (current_cmd_buffer_pos >= 0) { //once we get to the bottom, dont do anything
  284. str_len = strlen(buffer);
  285. for(int i= 0;i < str_len ;++i) {
  286. send_string(SS_TAP(X_BSPACE)); //clear w/e is on the line already
  287. //process_terminal(KC_BSPC,record);
  288. }
  289. strncpy(buffer,cmd_buffer[current_cmd_buffer_pos],79);
  290. send_string(buffer);
  291. --current_cmd_buffer_pos; //get ready to access the above cmd if down/up is pressed again
  292. }
  293. return false; break;
  294. default:
  295. if (keycode <= 58) {
  296. char_to_add = 0;
  297. if (get_mods() & (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT))) {
  298. char_to_add = shifted_keycode_to_ascii_lut[keycode];
  299. } else if (get_mods() == 0) {
  300. char_to_add = keycode_to_ascii_lut[keycode];
  301. }
  302. if (char_to_add != 0) {
  303. strncat(buffer, &char_to_add, 1);
  304. }
  305. } break;
  306. }
  307. }
  308. }
  309. return true;
  310. }