From 7ad924bae5519e981c57495e481db62741aa4376 Mon Sep 17 00:00:00 2001 From: Jack Humbert Date: Tue, 12 Sep 2017 00:43:10 -0400 Subject: [PATCH] Updates send_string functionality, adds terminal feature (#1657) * implement basic terminal stuff * modify send_string to read normal strings too * add files bc yeah. working pgm detected * pgm detection apparently not working * adds send string keycodes, additional keycode support in send string * implement arguments * [terminal] add help command * [terminal] adds keycode and keymap functions * [terminal] adds nop.h, documentation * update macro docs --- common_features.mk | 5 + docs/_summary.md | 1 + docs/feature_terminal.md | 80 ++++++ docs/macros.md | 136 +++++++--- keyboards/planck/keymaps/default/keymap.c | 2 +- keyboards/planck/rules.mk | 2 +- quantum/audio/song_list.h | 3 + quantum/process_keycode/process_terminal.c | 252 ++++++++++++++++++ quantum/process_keycode/process_terminal.h | 27 ++ .../process_keycode/process_terminal_nop.h | 25 ++ quantum/quantum.c | 75 +++++- quantum/quantum.h | 25 +- quantum/quantum_keycodes.h | 5 + quantum/send_string_keycodes.h | 168 ++++++++++++ 14 files changed, 749 insertions(+), 57 deletions(-) create mode 100644 docs/feature_terminal.md create mode 100644 quantum/process_keycode/process_terminal.c create mode 100644 quantum/process_keycode/process_terminal.h create mode 100644 quantum/process_keycode/process_terminal_nop.h create mode 100644 quantum/send_string_keycodes.h diff --git a/common_features.mk b/common_features.mk index f405d5c07b9..d499d1f0b70 100644 --- a/common_features.mk +++ b/common_features.mk @@ -153,6 +153,11 @@ ifeq ($(strip $(LED_TABLES)), yes) SRC += $(QUANTUM_DIR)/led_tables.c endif +ifeq ($(strip $(TERMINAL_ENABLE)), yes) + SRC += $(QUANTUM_DIR)/process_keycode/process_terminal.c + OPT_DEFS += -DTERMINAL_ENABLE +endif + QUANTUM_SRC:= \ $(QUANTUM_DIR)/quantum.c \ $(QUANTUM_DIR)/keymap_common.c \ diff --git a/docs/_summary.md b/docs/_summary.md index 77d208fc3cd..ce02220fbf2 100644 --- a/docs/_summary.md +++ b/docs/_summary.md @@ -28,6 +28,7 @@ * [Thermal Printer](feature_thermal_printer.md) * [Stenography](stenography.md) * [Unicode](unicode.md) + * [Terminal](feature_terminal.md) * Reference * [Glossary](glossary.md) diff --git a/docs/feature_terminal.md b/docs/feature_terminal.md new file mode 100644 index 00000000000..2c5f2c486bd --- /dev/null +++ b/docs/feature_terminal.md @@ -0,0 +1,80 @@ +# Terminal + +> This feature is currently *huge* at 4400 bytes, and should probably only be put on boards with a lot of memory, or for fun. + +The terminal feature is a command-line-like interface designed to communicate through a text editor with keystrokes. It's beneficial to turn off auto-indent features in your editor. + +To enable, stick this in your `rules.mk` or `Makefile`: + + TERMINAL_ENABLE = yes + +And use the `TERM_ON` and `TERM_OFF` keycodes to turn it on or off. + +When enabled, a `> ` prompt will appear, where you'll be able to type, backspace (a bell will ding if you reach the beginning and audio is enabled), and hit enter to send the command. Arrow keys are currently disabled so it doesn't get confused. Moving your cursor around with the mouse is discouraged. + +`#define TERMINAL_HELP` enables some other output helpers that aren't really needed with this page. + +## Future ideas + +* Keyboard/user-extendable commands +* Smaller footprint +* Arrow key support +* Command history +* SD card support +* LCD support for buffer display +* Keycode -> name string LUT +* Layer status +* *Analog/digital port read/write* +* RGB mode stuff +* Macro definitions +* EEPROM read/write +* Audio control + +## Current commands + +### `about` + +Prints out the current version of QMK with a build date: + +``` +> about +QMK Firmware + v0.5.115-7-g80ed73-dirty + Built: 2017-08-29-20:24:44 +``` + +### `help` + +Prints out the available commands: + +``` +> help +commands available: + about help keycode keymap exit +``` + +### `keycode ` + +Prints out the keycode value of a certain layer, row, and column: + +``` +> keycode 0 1 0 +0x29 (41) +``` + +### `keymap ` + +Prints out the entire keymap for a certain layer + +``` +> keymap 0 +0x002b, 0x0014, 0x001a, 0x0008, 0x0015, 0x0017, 0x001c, 0x0018, 0x000c, 0x0012, 0x0013, 0x002a, +0x0029, 0x0004, 0x0016, 0x0007, 0x0009, 0x000a, 0x000b, 0x000d, 0x000e, 0x000f, 0x0033, 0x0034, +0x00e1, 0x001d, 0x001b, 0x0006, 0x0019, 0x0005, 0x0011, 0x0010, 0x0036, 0x0037, 0x0038, 0x0028, +0x5cd6, 0x00e0, 0x00e2, 0x00e3, 0x5cd4, 0x002c, 0x002c, 0x5cd5, 0x0050, 0x0051, 0x0052, 0x004f, +> +``` + +### `exit` + +Exits the terminal - same as `TERM_OFF`. \ No newline at end of file diff --git a/docs/macros.md b/docs/macros.md index c7a9b2e7a6f..66d2bc090a8 100644 --- a/docs/macros.md +++ b/docs/macros.md @@ -1,12 +1,93 @@ # Macros -Macros allow you to send multiple keystrokes when pressing just one key. QMK has a number of ways to define and use macros. These can do anything you want- type common phrases for you, copypasta, repetitive game movements, or even help you code. +Macros allow you to send multiple keystrokes when pressing just one key. QMK has a number of ways to define and use macros. These can do anything you want: type common phrases for you, copypasta, repetitive game movements, or even help you code. {% hint style='danger' %} **Security Note**: While it is possible to use macros to send passwords, credit card numbers, and other sensitive information it is a supremely bad idea to do so. Anyone who gets ahold of your keyboard will be able to access that information by opening a text editor. {% endhint %} -# Macro Definitions +## The new way: `SEND_STRING()` & `process_record_user` + +Sometimes you just want a key to type out words or phrases. For the most common situations we've provided `SEND_STRING()`, which will type out your string for you. All ascii that is easily translated to a keycode is supported (eg `\n\t`). + +For example: + +```c +enum custom_keycodes { + PRINT_TRUTH = SAFE_RANGE +}; + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + if (record->event.pressed) { + switch(keycode) { + case PRINT_TRUTH: + SEND_STRING("QMK is the best thing ever!"); + return false; break; + } + } + return true; +}; +``` + +### Tap/down/up + +You can send arbitary keycodes by wrapping them in: + +* `SS_TAP()` +* `SS_DOWN()` +* `SS_UP()` + +For example: + + SEND_STRING(SS_TAP(X_HOME)); + +Would tap `KC_HOME` - note how the prefix is now `X_`, and not `KC_`. You can also combine this with other strings, like this: + + SEND_STRING("VE"SS_TAP(X_HOME)"LO"); + +Which would send "VE" followed by a `KC_HOME` tap, and "LO" (spelling "LOVE" if on a newline). + +There's also a couple of mod shortcuts you can use: + +* `SS_LCTRL(string)` +* `SS_LGUI(string)` +* `SS_LALT(string)` + +That can be used like this: + + SEND_STRING(SS_LCTRL("a")); + +Which would send LCTRL+a (LTRL down, a, LTRL up) - notice that they take strings (eg `"k"`), and not the `X_K` keycodes. + +### Alternative keymaps + +By default, it assumes a US keymap with a QWERTY layout; if you want to change that (e.g. if your OS uses software Colemak), include this somewhere in your keymap: + + #include + +### Strings in memory + +If for some reason you're manipulating strings and need to print out something you just generated (instead of being a literal, constant string), you can use `send_string()`, like this: + +```c +char my_str[4] = "ok."; +send_string(my_str); +``` + +The shortcuts defined above won't work with `send_string()`, but you can separate things out to different lines if needed: + +```c +char my_str[4] = "ok."; +SEND_STRING("I said: "); +send_string(my_str); +SEND_STRING(".."SS_TAP(X_END)); +``` + +## The old way: `MACRO()` & `action_get_macro` + +{% hint style='info' %} +This is inherited from TMK, and hasn't been updated - it's recommend that you use `SEND_STRING` and `process_record_user` instead. +{% endhint %} By default QMK assumes you don't have any macros. To define your macros you create an `action_get_macro()` function. For example: @@ -26,11 +107,9 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) { This defines two macros which will be run when the key they are assigned to is pressed. If instead you'd like them to run when the key is released you can change the if statement: -```c if (!record->event.pressed) { -``` -## Macro Commands +### Macro Commands A macro can include the following commands: @@ -41,32 +120,7 @@ A macro can include the following commands: * W() wait (milliseconds). * END end mark. -## Sending strings - -Sometimes you just want a key to type out words or phrases. For the most common situations we've provided `SEND_STRING()`, which will type out your string for you instead of having to build a `MACRO()`. - -For example: - -```c -const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) { - if (record->event.pressed) { - switch(id) { - case 0: - SEND_STRING("QMK is the best thing ever!"); - return false; - } - } - return MACRO_NONE; -}; -``` - -By default, it assumes a US keymap with a QWERTY layout; if you want to change that (e.g. if your OS uses software Colemak), include this somewhere in your keymap: - -``` -#include -``` - -## Mapping a Macro to a key +### Mapping a Macro to a key Use the `M()` function within your `KEYMAP()` to call a macro. For example, here is the keymap for a 2-key keyboard: @@ -92,7 +146,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) { When you press the key on the left it will type "Hi!" and when you press the key on the right it will type "Bye!". -## Naming your macros +### Naming your macros If you have a bunch of macros you want to refer to from your keymap while keeping the keymap easily readable you can name them using `#define` at the top of your file. @@ -107,11 +161,11 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { }; ``` -# Advanced macro functions +## Advanced macro functions -While working within the `action_get_macro()` function block there are some functions you may find useful. Keep in mind that while you can write some fairly advanced code within a macro if your functionality gets too complex you may want to define a custom keycode instead. Macros are meant to be simple. +There are some functions you may find useful in macro-writing. Keep in mind that while you can write some fairly advanced code within a macro if your functionality gets too complex you may want to define a custom keycode instead. Macros are meant to be simple. -#### `record->event.pressed` +### `record->event.pressed` This is a boolean value that can be tested to see if the switch is being pressed or released. An example of this is @@ -123,27 +177,27 @@ This is a boolean value that can be tested to see if the switch is being pressed } ``` -#### `register_code();` +### `register_code();` This sends the `` keydown event to the computer. Some examples would be `KC_ESC`, `KC_C`, `KC_4`, and even modifiers such as `KC_LSFT` and `KC_LGUI`. -#### `unregister_code();` +### `unregister_code();` Parallel to `register_code` function, this sends the `` keyup event to the computer. If you don't use this, the key will be held down until it's sent. -#### `clear_keyboard();` +### `clear_keyboard();` This will clear all mods and keys currently pressed. -#### `clear_mods();` +### `clear_mods();` This will clear all mods currently pressed. -#### `clear_keyboard_but_mods();` +### `clear_keyboard_but_mods();` This will clear all keys besides the mods currently pressed. -# Advanced Example: Single-key copy/paste +## Advanced Example: Single-key copy/paste This example defines a macro which sends `Ctrl-C` when pressed down, and `Ctrl-V` when released. diff --git a/keyboards/planck/keymaps/default/keymap.c b/keyboards/planck/keymaps/default/keymap.c index 54495178012..48b02de38e3 100644 --- a/keyboards/planck/keymaps/default/keymap.c +++ b/keyboards/planck/keymaps/default/keymap.c @@ -163,7 +163,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { * `-----------------------------------------------------------------------------------' */ [_ADJUST] = { - {_______, RESET, DEBUG, _______, _______, _______, _______, _______, _______, _______, _______, KC_DEL }, + {_______, RESET, DEBUG, _______, _______, _______, _______, TERM_ON, TERM_OFF,_______, _______, KC_DEL }, {_______, _______, MU_MOD, AU_ON, AU_OFF, AG_NORM, AG_SWAP, QWERTY, COLEMAK, DVORAK, PLOVER, _______}, {_______, MUV_DE, MUV_IN, MU_ON, MU_OFF, MI_ON, MI_OFF, _______, _______, _______, _______, _______}, {_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______} diff --git a/keyboards/planck/rules.mk b/keyboards/planck/rules.mk index 32a9a26c75d..c4091f01179 100644 --- a/keyboards/planck/rules.mk +++ b/keyboards/planck/rules.mk @@ -57,7 +57,7 @@ CONSOLE_ENABLE = yes # Console for debug(+400) COMMAND_ENABLE = no # Commands for debug and configuration NKRO_ENABLE = no # Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality -MIDI_ENABLE = yes # MIDI controls +MIDI_ENABLE = no # MIDI controls AUDIO_ENABLE = yes # Audio output on port C6 UNICODE_ENABLE = no # Unicode BLUETOOTH_ENABLE = no # Enable Bluetooth with the Adafruit EZ-Key HID diff --git a/quantum/audio/song_list.h b/quantum/audio/song_list.h index 5ad543ca769..25e66e67c19 100644 --- a/quantum/audio/song_list.h +++ b/quantum/audio/song_list.h @@ -248,4 +248,7 @@ Q__NOTE(_GS5), \ HD_NOTE(_C6), +#define TERMINAL_SOUND \ + E__NOTE(_C5 ) + #endif diff --git a/quantum/process_keycode/process_terminal.c b/quantum/process_keycode/process_terminal.c new file mode 100644 index 00000000000..deb1543e3da --- /dev/null +++ b/quantum/process_keycode/process_terminal.c @@ -0,0 +1,252 @@ +/* Copyright 2017 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "process_terminal.h" +#include +#include "version.h" +#include +#include + +bool terminal_enabled = false; +char buffer[80] = ""; +char newline[2] = "\n"; +char arguments[6][20]; + +__attribute__ ((weak)) +const char terminal_prompt[8] = "> "; + +#ifdef AUDIO_ENABLE + #ifndef TERMINAL_SONG + #define TERMINAL_SONG SONG(TERMINAL_SOUND) + #endif + float terminal_song[][2] = TERMINAL_SONG; + #define TERMINAL_BELL() PLAY_SONG(terminal_song) +#else + #define TERMINAL_BELL() +#endif + +__attribute__ ((weak)) +const char keycode_to_ascii_lut[58] = { + 0, 0, 0, 0, + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 0, 0, 0, '\t', + ' ', '-', '=', '[', ']', '\\', 0, ';', '\'', '`', ',', '.', '/' +}; + +__attribute__ ((weak)) +const char shifted_keycode_to_ascii_lut[58] = { + 0, 0, 0, 0, + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', 0, 0, 0, '\t', + ' ', '_', '+', '{', '}', '|', 0, ':', '\'', '~', '<', '>', '?' +}; + +struct stringcase { + char* string; + void (*func)(void); +} typedef stringcase; + +void enable_terminal(void) { + terminal_enabled = true; + strcpy(buffer, ""); + for (int i = 0; i < 6; i++) + strcpy(arguments[i], ""); + // select all text to start over + // SEND_STRING(SS_LCTRL("a")); + send_string(terminal_prompt); +} + +void disable_terminal(void) { + terminal_enabled = false; +} + +void terminal_about(void) { + SEND_STRING("QMK Firmware\n"); + SEND_STRING(" v"); + SEND_STRING(QMK_VERSION); + SEND_STRING("\n"SS_TAP(X_HOME)" Built: "); + SEND_STRING(QMK_BUILDDATE); + send_string(newline); + #ifdef TERMINAL_HELP + if (strlen(arguments[1]) != 0) { + SEND_STRING("You entered: "); + send_string(arguments[1]); + send_string(newline); + } + #endif +} + +void terminal_help(void); + +extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; + +void terminal_keycode(void) { + if (strlen(arguments[1]) != 0 && strlen(arguments[2]) != 0 && strlen(arguments[3]) != 0) { + char keycode_dec[5]; + char keycode_hex[5]; + uint16_t layer = strtol(arguments[1], (char **)NULL, 10); + uint16_t row = strtol(arguments[2], (char **)NULL, 10); + uint16_t col = strtol(arguments[3], (char **)NULL, 10); + uint16_t keycode = pgm_read_word(&keymaps[layer][row][col]); + itoa(keycode, keycode_dec, 10); + itoa(keycode, keycode_hex, 16); + SEND_STRING("0x"); + send_string(keycode_hex); + SEND_STRING(" ("); + send_string(keycode_dec); + SEND_STRING(")\n"); + } else { + #ifdef TERMINAL_HELP + SEND_STRING("usage: keycode \n"); + #endif + } +} + +void terminal_keymap(void) { + if (strlen(arguments[1]) != 0) { + uint16_t layer = strtol(arguments[1], (char **)NULL, 10); + for (int r = 0; r < MATRIX_ROWS; r++) { + for (int c = 0; c < MATRIX_COLS; c++) { + uint16_t keycode = pgm_read_word(&keymaps[layer][r][c]); + char keycode_s[8]; + sprintf(keycode_s, "0x%04x, ", keycode); + send_string(keycode_s); + } + send_string(newline); + } + } else { + #ifdef TERMINAL_HELP + SEND_STRING("usage: keymap \n"); + #endif + } +} + +stringcase terminal_cases[] = { + { "about", terminal_about }, + { "help", terminal_help }, + { "keycode", terminal_keycode }, + { "keymap", terminal_keymap }, + { "exit", disable_terminal } +}; + +void terminal_help(void) { + SEND_STRING("commands available:\n "); + for( stringcase* case_p = terminal_cases; case_p != terminal_cases + sizeof( terminal_cases ) / sizeof( terminal_cases[0] ); case_p++ ) { + send_string(case_p->string); + SEND_STRING(" "); + } + send_string(newline); +} + +void command_not_found(void) { + SEND_STRING("command \""); + send_string(buffer); + SEND_STRING("\" not found\n"); +} + +void process_terminal_command(void) { + // we capture return bc of the order of events, so we need to manually send a newline + send_string(newline); + + char * pch; + uint8_t i = 0; + pch = strtok(buffer, " "); + while (pch != NULL) { + strcpy(arguments[i], pch); + pch = strtok(NULL, " "); + i++; + } + + bool command_found = false; + for( stringcase* case_p = terminal_cases; case_p != terminal_cases + sizeof( terminal_cases ) / sizeof( terminal_cases[0] ); case_p++ ) { + if( 0 == strcmp( case_p->string, buffer ) ) { + command_found = true; + (*case_p->func)(); + break; + } + } + + if (!command_found) + command_not_found(); + + if (terminal_enabled) { + strcpy(buffer, ""); + for (int i = 0; i < 6; i++) + strcpy(arguments[i], ""); + SEND_STRING(SS_TAP(X_HOME)); + send_string(terminal_prompt); + } +} + +bool process_terminal(uint16_t keycode, keyrecord_t *record) { + + if (keycode == TERM_ON && record->event.pressed) { + enable_terminal(); + return false; + } + + if (terminal_enabled && record->event.pressed) { + if (keycode == TERM_OFF && record->event.pressed) { + disable_terminal(); + return false; + } + if (keycode < 256) { + uint8_t str_len; + char char_to_add; + switch (keycode) { + case KC_ENTER: + process_terminal_command(); + return false; break; + case KC_ESC: + SEND_STRING("\n"); + enable_terminal(); + return false; break; + case KC_BSPC: + str_len = strlen(buffer); + if (str_len > 0) { + buffer[str_len-1] = 0; + return true; + } else { + TERMINAL_BELL(); + return false; + } break; + case KC_LEFT: + case KC_RIGHT: + case KC_UP: + case KC_DOWN: + return false; break; + default: + if (keycode <= 58) { + char_to_add = 0; + if (get_mods() & (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT))) { + char_to_add = shifted_keycode_to_ascii_lut[keycode]; + } else if (get_mods() == 0) { + char_to_add = keycode_to_ascii_lut[keycode]; + } + if (char_to_add != 0) { + strncat(buffer, &char_to_add, 1); + } + } break; + } + + + + } + } + return true; +} \ No newline at end of file diff --git a/quantum/process_keycode/process_terminal.h b/quantum/process_keycode/process_terminal.h new file mode 100644 index 00000000000..d945949a41d --- /dev/null +++ b/quantum/process_keycode/process_terminal.h @@ -0,0 +1,27 @@ +/* Copyright 2017 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PROCESS_TERMINAL_H +#define PROCESS_TERMINAL_H + +#include "quantum.h" + +extern const char keycode_to_ascii_lut[58]; +extern const char shifted_keycode_to_ascii_lut[58]; +extern const char terminal_prompt[8]; +bool process_terminal(uint16_t keycode, keyrecord_t *record); + +#endif \ No newline at end of file diff --git a/quantum/process_keycode/process_terminal_nop.h b/quantum/process_keycode/process_terminal_nop.h new file mode 100644 index 00000000000..56895b33c30 --- /dev/null +++ b/quantum/process_keycode/process_terminal_nop.h @@ -0,0 +1,25 @@ +/* Copyright 2017 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PROCESS_TERMINAL_H +#define PROCESS_TERMINAL_H + +#include "quantum.h" + +#define TERM_ON KC_NO +#define TERM_OFF KC_NO + +#endif \ No newline at end of file diff --git a/quantum/quantum.c b/quantum/quantum.c index 285e1e81ee3..1fccaa7d5a0 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -237,6 +237,9 @@ bool process_record_quantum(keyrecord_t *record) { #endif #ifdef UNICODEMAP_ENABLE process_unicode_map(keycode, record) && + #endif + #ifdef TERMINAL_ENABLE + process_terminal(keycode, record) && #endif true)) { return false; @@ -600,21 +603,29 @@ void send_string(const char *str) { send_string_with_delay(str, 0); } +void send_string_P(const char *str) { + send_string_with_delay_P(str, 0); +} + void send_string_with_delay(const char *str, uint8_t interval) { while (1) { - uint8_t keycode; - uint8_t ascii_code = pgm_read_byte(str); + char ascii_code = *str; if (!ascii_code) break; - keycode = pgm_read_byte(&ascii_to_keycode_lut[ascii_code]); - if (pgm_read_byte(&ascii_to_shift_lut[ascii_code])) { - register_code(KC_LSFT); - register_code(keycode); - unregister_code(keycode); - unregister_code(KC_LSFT); - } - else { - register_code(keycode); - unregister_code(keycode); + if (ascii_code == 1) { + // tap + uint8_t keycode = *(++str); + register_code(keycode); + unregister_code(keycode); + } else if (ascii_code == 2) { + // down + uint8_t keycode = *(++str); + register_code(keycode); + } else if (ascii_code == 3) { + // up + uint8_t keycode = *(++str); + unregister_code(keycode); + } else { + send_char(ascii_code); } ++str; // interval @@ -622,6 +633,46 @@ void send_string_with_delay(const char *str, uint8_t interval) { } } +void send_string_with_delay_P(const char *str, uint8_t interval) { + while (1) { + char ascii_code = pgm_read_byte(str); + if (!ascii_code) break; + if (ascii_code == 1) { + // tap + uint8_t keycode = pgm_read_byte(++str); + register_code(keycode); + unregister_code(keycode); + } else if (ascii_code == 2) { + // down + uint8_t keycode = pgm_read_byte(++str); + register_code(keycode); + } else if (ascii_code == 3) { + // up + uint8_t keycode = pgm_read_byte(++str); + unregister_code(keycode); + } else { + send_char(ascii_code); + } + ++str; + // interval + { uint8_t ms = interval; while (ms--) wait_ms(1); } + } +} + +void send_char(char ascii_code) { + uint8_t keycode; + keycode = pgm_read_byte(&ascii_to_keycode_lut[(uint8_t)ascii_code]); + if (pgm_read_byte(&ascii_to_shift_lut[(uint8_t)ascii_code])) { + register_code(KC_LSFT); + register_code(keycode); + unregister_code(keycode); + unregister_code(KC_LSFT); + } else { + register_code(keycode); + unregister_code(keycode); + } +} + void set_single_persistent_default_layer(uint8_t default_layer) { #if defined(AUDIO_ENABLE) && defined(DEFAULT_LAYER_SONGS) PLAY_SONG(default_layer_songs[default_layer]); diff --git a/quantum/quantum.h b/quantum/quantum.h index 9a6d691a155..f3333a002af 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -40,7 +40,7 @@ #include "action_util.h" #include #include "print.h" - +#include "send_string_keycodes.h" extern uint32_t default_layer_state; @@ -103,11 +103,32 @@ extern uint32_t default_layer_state; #include "process_key_lock.h" #endif -#define SEND_STRING(str) send_string(PSTR(str)) +#ifdef TERMINAL_ENABLE + #include "process_terminal.h" +#else + #include "process_terminal_nop.h" +#endif + +#define STRINGIZE(z) #z +#define ADD_SLASH_X(y) STRINGIZE(\x ## y) +#define SYMBOL_STR(x) ADD_SLASH_X(x) + +#define SS_TAP(keycode) "\1" SYMBOL_STR(keycode) +#define SS_DOWN(keycode) "\2" SYMBOL_STR(keycode) +#define SS_UP(keycode) "\3" SYMBOL_STR(keycode) + +#define SS_LCTRL(string) SS_DOWN(X_LCTRL) string SS_UP(X_LCTRL) +#define SS_LGUI(string) SS_DOWN(X_LGUI) string SS_UP(X_LGUI) +#define SS_LALT(string) SS_DOWN(X_LALT) string SS_UP(X_LALT) + +#define SEND_STRING(str) send_string_P(PSTR(str)) extern const bool ascii_to_shift_lut[0x80]; extern const uint8_t ascii_to_keycode_lut[0x80]; void send_string(const char *str); void send_string_with_delay(const char *str, uint8_t interval); +void send_string_P(const char *str); +void send_string_with_delay_P(const char *str, uint8_t interval); +void send_char(char ascii_code); // For tri-layer void update_tri_layer(uint8_t layer1, uint8_t layer2, uint8_t layer3); diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index ccd4565f530..26c3c41a73b 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -431,6 +431,11 @@ enum quantum_keycodes { KC_LOCK, #endif +#ifdef TERMINAL_ENABLE + TERM_ON, + TERM_OFF, +#endif + // always leave at the end SAFE_RANGE }; diff --git a/quantum/send_string_keycodes.h b/quantum/send_string_keycodes.h new file mode 100644 index 00000000000..0e308be5081 --- /dev/null +++ b/quantum/send_string_keycodes.h @@ -0,0 +1,168 @@ +#ifndef SEND_STRING_KEYCODES +#define SEND_STRING_KEYCODES + +#define X_NO 00 +#define X_ROLL_OVER 01 +#define X_POST_FAIL 02 +#define X_UNDEFINED 03 +#define X_A 04 +#define X_B 05 +#define X_C 06 +#define X_D 07 +#define X_E 08 +#define X_F 09 +#define X_G 0A +#define X_H 0B +#define X_I 0C +#define X_J 0D +#define X_K 0E +#define X_L 0F +#define X_M 10 +#define X_N 11 +#define X_O 12 +#define X_P 13 +#define X_Q 14 +#define X_R 15 +#define X_S 16 +#define X_T 17 +#define X_U 18 +#define X_V 19 +#define X_W 1A +#define X_X 1B +#define X_Y 1C +#define X_Z 1D +#define X_1 1E +#define X_2 1F +#define X_3 20 +#define X_4 21 +#define X_5 22 +#define X_6 23 +#define X_7 24 +#define X_8 25 +#define X_9 26 +#define X_0 27 +#define X_ENTER 28 +#define X_ESCAPE 29 +#define X_BSPACE 2A +#define X_TAB 2B +#define X_SPACE 2C +#define X_MINUS 2D +#define X_EQUAL 2E +#define X_LBRACKET 2F +#define X_RBRACKET 30 +#define X_BSLASH 31 +#define X_NONUS_HASH 32 +#define X_SCOLON 33 +#define X_QUOTE 34 +#define X_GRAVE 35 +#define X_COMMA 36 +#define X_DOT 37 +#define X_SLASH 38 +#define X_CAPSLOCK 39 +#define X_F1 3A +#define X_F2 3B +#define X_F3 3C +#define X_F4 3D +#define X_F5 3E +#define X_F6 3F +#define X_F7 40 +#define X_F8 41 +#define X_F9 42 +#define X_F10 43 +#define X_F11 44 +#define X_F12 45 +#define X_PSCREEN 46 +#define X_SCROLLLOCK 47 +#define X_PAUSE 48 +#define X_INSERT 49 +#define X_HOME 4A +#define X_PGUP 4B +#define X_DELETE 4C +#define X_END 4D +#define X_PGDOWN 4E +#define X_RIGHT 4F +#define X_LEFT 50 +#define X_DOWN 51 +#define X_UP 52 +#define X_NUMLOCK 53 +#define X_KP_SLASH 54 +#define X_KP_ASTERISK 55 +#define X_KP_MINUS 56 +#define X_KP_PLUS 57 +#define X_KP_ENTER 58 +#define X_KP_1 59 +#define X_KP_2 5A +#define X_KP_3 5B +#define X_KP_4 5C +#define X_KP_5 5D +#define X_KP_6 5E +#define X_KP_7 5F +#define X_KP_8 60 +#define X_KP_9 61 +#define X_KP_0 62 +#define X_KP_DOT 63 +#define X_NONUS_BSLASH 64 +#define X_APPLICATION 65 +#define X_POWER 66 +#define X_KP_EQUAL 67 +#define X_F13 68 +#define X_F14 69 +#define X_F15 6A +#define X_F16 6B +#define X_F17 6C +#define X_F18 6D +#define X_F19 6E +#define X_F20 6F +#define X_F21 70 +#define X_F22 71 +#define X_F23 72 +#define X_F24 73 +#define X_EXECUTE 74 +#define X_HELP 75 +#define X_MENU 76 +#define X_SELECT 77 +#define X_STOP 78 +#define X_AGAIN 79 +#define X_UNDO 7A +#define X_CUT 7B +#define X_COPY 7C +#define X_PASTE 7D +#define X_FIND 7E +#define X__MUTE 7F +#define X__VOLUP 80 +#define X__VOLDOWN 81 +#define X_LOCKING_CAPS 82 +#define X_LOCKING_NUM 83 +#define X_LOCKING_SCROLL 84 +#define X_KP_COMMA 85 +#define X_KP_EQUAL_AS400 86 +#define X_INT1 87 +#define X_INT2 88 +#define X_INT3 89 +#define X_INT4 8A +#define X_INT5 8B +#define X_INT6 8C +#define X_INT7 8D +#define X_INT8 8E +#define X_INT9 8F +#define X_LANG1 90 +#define X_LANG2 91 +#define X_LANG3 92 +#define X_LANG4 93 +#define X_LANG5 94 +#define X_LANG6 95 +#define X_LANG7 96 +#define X_LANG8 97 +#define X_LANG9 98 + +/* Modifiers */ +#define X_LCTRL e0 +#define X_LSHIFT e1 +#define X_LALT e2 +#define X_LGUI e3 +#define X_RCTRL e4 +#define X_RSHIFT e5 +#define X_RALT e6 +#define X_RGUI e7 + +#endif \ No newline at end of file