diff options
-rw-r--r-- | common_features.mk | 5 | ||||
-rw-r--r-- | docs/_summary.md | 1 | ||||
-rw-r--r-- | docs/feature_terminal.md | 80 | ||||
-rw-r--r-- | docs/macros.md | 136 | ||||
-rw-r--r-- | keyboards/planck/keymaps/default/keymap.c | 2 | ||||
-rw-r--r-- | keyboards/planck/rules.mk | 2 | ||||
-rw-r--r-- | quantum/audio/song_list.h | 3 | ||||
-rw-r--r-- | quantum/process_keycode/process_terminal.c | 252 | ||||
-rw-r--r-- | quantum/process_keycode/process_terminal.h | 27 | ||||
-rw-r--r-- | quantum/process_keycode/process_terminal_nop.h | 25 | ||||
-rw-r--r-- | quantum/quantum.c | 73 | ||||
-rw-r--r-- | quantum/quantum.h | 25 | ||||
-rw-r--r-- | quantum/quantum_keycodes.h | 5 | ||||
-rw-r--r-- | quantum/send_string_keycodes.h | 168 |
14 files changed, 748 insertions, 56 deletions
diff --git a/common_features.mk b/common_features.mk index f405d5c07b..d499d1f0b7 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 77d208fc3c..ce02220fbf 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 0000000000..2c5f2c486b --- /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 <layer> <row> <col>` + +Prints out the keycode value of a certain layer, row, and column: + +``` +> keycode 0 1 0 +0x29 (41) +``` + +### `keymap <layer>` + +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 c7a9b2e7a6..66d2bc090a 100644 --- a/docs/macros.md +++ b/docs/macros.md @@ -1,72 +1,126 @@ # 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` -By default QMK assumes you don't have any macros. To define your macros you create an `action_get_macro()` function. For example: +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 -const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) { +enum custom_keycodes { + PRINT_TRUTH = SAFE_RANGE +}; + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { if (record->event.pressed) { - switch(id) { - case 0: - return MACRO(D(LSFT), T(H), U(LSFT), T(I), D(LSFT), T(1), U(LSFT), END); - case 1: - return MACRO(D(LSFT), T(B), U(LSFT), T(Y), T(E), D(LSFT), T(1), U(LSFT), END); + switch(keycode) { + case PRINT_TRUTH: + SEND_STRING("QMK is the best thing ever!"); + return false; break; } } - return MACRO_NONE; + return true; }; ``` -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: +### 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 <sendstring_colemak.h> + +### 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 - if (!record->event.pressed) { +char my_str[4] = "ok."; +send_string(my_str); ``` -## Macro Commands - -A macro can include the following commands: +The shortcuts defined above won't work with `send_string()`, but you can separate things out to different lines if needed: -* I() change interval of stroke in milliseconds. -* D() press key. -* U() release key. -* T() type key(press and release). -* W() wait (milliseconds). -* END end mark. +```c +char my_str[4] = "ok."; +SEND_STRING("I said: "); +send_string(my_str); +SEND_STRING(".."SS_TAP(X_END)); +``` -## Sending strings +## The old way: `MACRO()` & `action_get_macro` -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()`. +{% 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 %} -For example: +By default QMK assumes you don't have any macros. To define your macros you create an `action_get_macro()` function. 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(D(LSFT), T(H), U(LSFT), T(I), D(LSFT), T(1), U(LSFT), END); + case 1: + return MACRO(D(LSFT), T(B), U(LSFT), T(Y), T(E), D(LSFT), T(1), U(LSFT), END); } } 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: +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: -``` -#include <sendstring_colemak.h> -``` + if (!record->event.pressed) { + +### Macro Commands + +A macro can include the following commands: + +* I() change interval of stroke in milliseconds. +* D() press key. +* U() release key. +* T() type key(press and release). +* W() wait (milliseconds). +* END end mark. -## 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(<kc>);` +### `register_code(<kc>);` This sends the `<kc>` 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(<kc>);` +### `unregister_code(<kc>);` Parallel to `register_code` function, this sends the `<kc>` 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 5449517801..48b02de38e 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 32a9a26c75..c4091f0117 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 5ad543ca76..25e66e67c1 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 0000000000..deb1543e3d --- /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 <http://www.gnu.org/licenses/>. + */ + +#include "process_terminal.h" +#include <string.h> +#include "version.h" +#include <stdio.h> +#include <math.h> + +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 <layer> <row> <col>\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 <layer>\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 0000000000..d945949a41 --- /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 <http://www.gnu.org/licenses/>. + */ + +#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 0000000000..56895b33c3 --- /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 <http://www.gnu.org/licenses/>. + */ + +#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 285e1e81ee..1fccaa7d5a 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -238,6 +238,9 @@ bool process_record_quantum(keyrecord_t *record) { #ifdef UNICODEMAP_ENABLE process_unicode_map(keycode, record) && #endif + #ifdef TERMINAL_ENABLE + process_terminal(keycode, record) && + #endif true)) { return false; } @@ -600,21 +603,55 @@ 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); + 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); } - else { - register_code(keycode); - unregister_code(keycode); + ++str; + // interval + { uint8_t ms = interval; while (ms--) wait_ms(1); } + } +} + +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 @@ -622,6 +659,20 @@ void send_string_with_delay(const char *str, uint8_t interval) { } } +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 9a6d691a15..f3333a002a 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -40,7 +40,7 @@ #include "action_util.h" #include <stdlib.h> #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 ccd4565f53..26c3c41a73 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 0000000000..0e308be508 --- /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 |