aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Humbert <jack.humb@gmail.com>2017-09-12 00:43:10 -0400
committerGitHub <noreply@github.com>2017-09-12 00:43:10 -0400
commit7ad924bae5519e981c57495e481db62741aa4376 (patch)
treee07575ee363f68b70579cda8e54eebcbd55d4127
parenta4ff8b91f74df9fb1d87f52c0ded23935344d2eb (diff)
downloadqmk_firmware-7ad924bae5519e981c57495e481db62741aa4376.tar.gz
qmk_firmware-7ad924bae5519e981c57495e481db62741aa4376.zip
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
-rw-r--r--common_features.mk5
-rw-r--r--docs/_summary.md1
-rw-r--r--docs/feature_terminal.md80
-rw-r--r--docs/macros.md136
-rw-r--r--keyboards/planck/keymaps/default/keymap.c2
-rw-r--r--keyboards/planck/rules.mk2
-rw-r--r--quantum/audio/song_list.h3
-rw-r--r--quantum/process_keycode/process_terminal.c252
-rw-r--r--quantum/process_keycode/process_terminal.h27
-rw-r--r--quantum/process_keycode/process_terminal_nop.h25
-rw-r--r--quantum/quantum.c73
-rw-r--r--quantum/quantum.h25
-rw-r--r--quantum/quantum_keycodes.h5
-rw-r--r--quantum/send_string_keycodes.h168
14 files changed, 748 insertions, 56 deletions
diff --git a/common_features.mk b/common_features.mk
index f405d5c07..d499d1f0b 100644
--- a/common_features.mk
+++ b/common_features.mk
@@ -153,6 +153,11 @@ ifeq ($(strip $(LED_TABLES)), yes)
153 SRC += $(QUANTUM_DIR)/led_tables.c 153 SRC += $(QUANTUM_DIR)/led_tables.c
154endif 154endif
155 155
156ifeq ($(strip $(TERMINAL_ENABLE)), yes)
157 SRC += $(QUANTUM_DIR)/process_keycode/process_terminal.c
158 OPT_DEFS += -DTERMINAL_ENABLE
159endif
160
156QUANTUM_SRC:= \ 161QUANTUM_SRC:= \
157 $(QUANTUM_DIR)/quantum.c \ 162 $(QUANTUM_DIR)/quantum.c \
158 $(QUANTUM_DIR)/keymap_common.c \ 163 $(QUANTUM_DIR)/keymap_common.c \
diff --git a/docs/_summary.md b/docs/_summary.md
index 77d208fc3..ce02220fb 100644
--- a/docs/_summary.md
+++ b/docs/_summary.md
@@ -28,6 +28,7 @@
28 * [Thermal Printer](feature_thermal_printer.md) 28 * [Thermal Printer](feature_thermal_printer.md)
29 * [Stenography](stenography.md) 29 * [Stenography](stenography.md)
30 * [Unicode](unicode.md) 30 * [Unicode](unicode.md)
31 * [Terminal](feature_terminal.md)
31 32
32* Reference 33* Reference
33 * [Glossary](glossary.md) 34 * [Glossary](glossary.md)
diff --git a/docs/feature_terminal.md b/docs/feature_terminal.md
new file mode 100644
index 000000000..2c5f2c486
--- /dev/null
+++ b/docs/feature_terminal.md
@@ -0,0 +1,80 @@
1# Terminal
2
3> This feature is currently *huge* at 4400 bytes, and should probably only be put on boards with a lot of memory, or for fun.
4
5The 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.
6
7To enable, stick this in your `rules.mk` or `Makefile`:
8
9 TERMINAL_ENABLE = yes
10
11And use the `TERM_ON` and `TERM_OFF` keycodes to turn it on or off.
12
13When 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.
14
15`#define TERMINAL_HELP` enables some other output helpers that aren't really needed with this page.
16
17## Future ideas
18
19* Keyboard/user-extendable commands
20* Smaller footprint
21* Arrow key support
22* Command history
23* SD card support
24* LCD support for buffer display
25* Keycode -> name string LUT
26* Layer status
27* *Analog/digital port read/write*
28* RGB mode stuff
29* Macro definitions
30* EEPROM read/write
31* Audio control
32
33## Current commands
34
35### `about`
36
37Prints out the current version of QMK with a build date:
38
39```
40> about
41QMK Firmware
42 v0.5.115-7-g80ed73-dirty
43 Built: 2017-08-29-20:24:44
44```
45
46### `help`
47
48Prints out the available commands:
49
50```
51> help
52commands available:
53 about help keycode keymap exit
54```
55
56### `keycode <layer> <row> <col>`
57
58Prints out the keycode value of a certain layer, row, and column:
59
60```
61> keycode 0 1 0
620x29 (41)
63```
64
65### `keymap <layer>`
66
67Prints out the entire keymap for a certain layer
68
69```
70> keymap 0
710x002b, 0x0014, 0x001a, 0x0008, 0x0015, 0x0017, 0x001c, 0x0018, 0x000c, 0x0012, 0x0013, 0x002a,
720x0029, 0x0004, 0x0016, 0x0007, 0x0009, 0x000a, 0x000b, 0x000d, 0x000e, 0x000f, 0x0033, 0x0034,
730x00e1, 0x001d, 0x001b, 0x0006, 0x0019, 0x0005, 0x0011, 0x0010, 0x0036, 0x0037, 0x0038, 0x0028,
740x5cd6, 0x00e0, 0x00e2, 0x00e3, 0x5cd4, 0x002c, 0x002c, 0x5cd5, 0x0050, 0x0051, 0x0052, 0x004f,
75>
76```
77
78### `exit`
79
80Exits the terminal - same as `TERM_OFF`. \ No newline at end of file
diff --git a/docs/macros.md b/docs/macros.md
index c7a9b2e7a..66d2bc090 100644
--- a/docs/macros.md
+++ b/docs/macros.md
@@ -1,72 +1,126 @@
1# Macros 1# Macros
2 2
3Macros 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. 3Macros 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.
4 4
5{% hint style='danger' %} 5{% hint style='danger' %}
6**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. 6**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.
7{% endhint %} 7{% endhint %}
8 8
9# Macro Definitions 9## The new way: `SEND_STRING()` & `process_record_user`
10 10
11By default QMK assumes you don't have any macros. To define your macros you create an `action_get_macro()` function. For example: 11Sometimes 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`).
12
13For example:
12 14
13```c 15```c
14const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) { 16enum custom_keycodes {
17 PRINT_TRUTH = SAFE_RANGE
18};
19
20bool process_record_user(uint16_t keycode, keyrecord_t *record) {
15 if (record->event.pressed) { 21 if (record->event.pressed) {
16 switch(id) { 22 switch(keycode) {
17 case 0: 23 case PRINT_TRUTH:
18 return MACRO(D(LSFT), T(H), U(LSFT), T(I), D(LSFT), T(1), U(LSFT), END); 24 SEND_STRING("QMK is the best thing ever!");
19 case 1: 25 return false; break;
20 return MACRO(D(LSFT), T(B), U(LSFT), T(Y), T(E), D(LSFT), T(1), U(LSFT), END);
21 } 26 }
22 } 27 }
23 return MACRO_NONE; 28 return true;
24}; 29};
25``` 30```
26 31
27This 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: 32### Tap/down/up
33
34You can send arbitary keycodes by wrapping them in:
35
36* `SS_TAP()`
37* `SS_DOWN()`
38* `SS_UP()`
39
40For example:
41
42 SEND_STRING(SS_TAP(X_HOME));
43
44Would tap `KC_HOME` - note how the prefix is now `X_`, and not `KC_`. You can also combine this with other strings, like this:
45
46 SEND_STRING("VE"SS_TAP(X_HOME)"LO");
47
48Which would send "VE" followed by a `KC_HOME` tap, and "LO" (spelling "LOVE" if on a newline).
49
50There's also a couple of mod shortcuts you can use:
51
52* `SS_LCTRL(string)`
53* `SS_LGUI(string)`
54* `SS_LALT(string)`
55
56That can be used like this:
57
58 SEND_STRING(SS_LCTRL("a"));
59
60Which would send LCTRL+a (LTRL down, a, LTRL up) - notice that they take strings (eg `"k"`), and not the `X_K` keycodes.
61
62### Alternative keymaps
63
64By 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:
65
66 #include <sendstring_colemak.h>
67
68### Strings in memory
69
70If 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:
28 71
29```c 72```c
30 if (!record->event.pressed) { 73char my_str[4] = "ok.";
74send_string(my_str);
31``` 75```
32 76
33## Macro Commands 77The shortcuts defined above won't work with `send_string()`, but you can separate things out to different lines if needed:
34
35A macro can include the following commands:
36 78
37* I() change interval of stroke in milliseconds. 79```c
38* D() press key. 80char my_str[4] = "ok.";
39* U() release key. 81SEND_STRING("I said: ");
40* T() type key(press and release). 82send_string(my_str);
41* W() wait (milliseconds). 83SEND_STRING(".."SS_TAP(X_END));
42* END end mark. 84```
43 85
44## Sending strings 86## The old way: `MACRO()` & `action_get_macro`
45 87
46Sometimes 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()`. 88{% hint style='info' %}
89This is inherited from TMK, and hasn't been updated - it's recommend that you use `SEND_STRING` and `process_record_user` instead.
90{% endhint %}
47 91
48For example: 92By default QMK assumes you don't have any macros. To define your macros you create an `action_get_macro()` function. For example:
49 93
50```c 94```c
51const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) { 95const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) {
52 if (record->event.pressed) { 96 if (record->event.pressed) {
53 switch(id) { 97 switch(id) {
54 case 0: 98 case 0:
55 SEND_STRING("QMK is the best thing ever!"); 99 return MACRO(D(LSFT), T(H), U(LSFT), T(I), D(LSFT), T(1), U(LSFT), END);
56 return false; 100 case 1:
101 return MACRO(D(LSFT), T(B), U(LSFT), T(Y), T(E), D(LSFT), T(1), U(LSFT), END);
57 } 102 }
58 } 103 }
59 return MACRO_NONE; 104 return MACRO_NONE;
60}; 105};
61``` 106```
62 107
63By 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: 108This 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:
64 109
65``` 110 if (!record->event.pressed) {
66#include <sendstring_colemak.h> 111
67``` 112### Macro Commands
113
114A macro can include the following commands:
115
116* I() change interval of stroke in milliseconds.
117* D() press key.
118* U() release key.
119* T() type key(press and release).
120* W() wait (milliseconds).
121* END end mark.
68 122
69## Mapping a Macro to a key 123### Mapping a Macro to a key
70 124
71Use the `M()` function within your `KEYMAP()` to call a macro. For example, here is the keymap for a 2-key keyboard: 125Use the `M()` function within your `KEYMAP()` to call a macro. For example, here is the keymap for a 2-key keyboard:
72 126
@@ -92,7 +146,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) {
92 146
93When you press the key on the left it will type "Hi!" and when you press the key on the right it will type "Bye!". 147When you press the key on the left it will type "Hi!" and when you press the key on the right it will type "Bye!".
94 148
95## Naming your macros 149### Naming your macros
96 150
97If 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. 151If 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.
98 152
@@ -107,11 +161,11 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
107}; 161};
108``` 162```
109 163
110# Advanced macro functions 164## Advanced macro functions
111 165
112While 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. 166There 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.
113 167
114#### `record->event.pressed` 168### `record->event.pressed`
115 169
116This is a boolean value that can be tested to see if the switch is being pressed or released. An example of this is 170This is a boolean value that can be tested to see if the switch is being pressed or released. An example of this is
117 171
@@ -123,27 +177,27 @@ This is a boolean value that can be tested to see if the switch is being pressed
123 } 177 }
124``` 178```
125 179
126#### `register_code(<kc>);` 180### `register_code(<kc>);`
127 181
128This 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`. 182This 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`.
129 183
130#### `unregister_code(<kc>);` 184### `unregister_code(<kc>);`
131 185
132Parallel 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. 186Parallel 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.
133 187
134#### `clear_keyboard();` 188### `clear_keyboard();`
135 189
136This will clear all mods and keys currently pressed. 190This will clear all mods and keys currently pressed.
137 191
138#### `clear_mods();` 192### `clear_mods();`
139 193
140This will clear all mods currently pressed. 194This will clear all mods currently pressed.
141 195
142#### `clear_keyboard_but_mods();` 196### `clear_keyboard_but_mods();`
143 197
144This will clear all keys besides the mods currently pressed. 198This will clear all keys besides the mods currently pressed.
145 199
146# Advanced Example: Single-key copy/paste 200## Advanced Example: Single-key copy/paste
147 201
148This example defines a macro which sends `Ctrl-C` when pressed down, and `Ctrl-V` when released. 202This example defines a macro which sends `Ctrl-C` when pressed down, and `Ctrl-V` when released.
149 203
diff --git a/keyboards/planck/keymaps/default/keymap.c b/keyboards/planck/keymaps/default/keymap.c
index 544951780..48b02de38 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] = {
163 * `-----------------------------------------------------------------------------------' 163 * `-----------------------------------------------------------------------------------'
164 */ 164 */
165[_ADJUST] = { 165[_ADJUST] = {
166 {_______, RESET, DEBUG, _______, _______, _______, _______, _______, _______, _______, _______, KC_DEL }, 166 {_______, RESET, DEBUG, _______, _______, _______, _______, TERM_ON, TERM_OFF,_______, _______, KC_DEL },
167 {_______, _______, MU_MOD, AU_ON, AU_OFF, AG_NORM, AG_SWAP, QWERTY, COLEMAK, DVORAK, PLOVER, _______}, 167 {_______, _______, MU_MOD, AU_ON, AU_OFF, AG_NORM, AG_SWAP, QWERTY, COLEMAK, DVORAK, PLOVER, _______},
168 {_______, MUV_DE, MUV_IN, MU_ON, MU_OFF, MI_ON, MI_OFF, _______, _______, _______, _______, _______}, 168 {_______, MUV_DE, MUV_IN, MU_ON, MU_OFF, MI_ON, MI_OFF, _______, _______, _______, _______, _______},
169 {_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______} 169 {_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______}
diff --git a/keyboards/planck/rules.mk b/keyboards/planck/rules.mk
index 32a9a26c7..c4091f011 100644
--- a/keyboards/planck/rules.mk
+++ b/keyboards/planck/rules.mk
@@ -57,7 +57,7 @@ CONSOLE_ENABLE = yes # Console for debug(+400)
57COMMAND_ENABLE = no # Commands for debug and configuration 57COMMAND_ENABLE = no # Commands for debug and configuration
58NKRO_ENABLE = no # Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work 58NKRO_ENABLE = no # Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
59BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality 59BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality
60MIDI_ENABLE = yes # MIDI controls 60MIDI_ENABLE = no # MIDI controls
61AUDIO_ENABLE = yes # Audio output on port C6 61AUDIO_ENABLE = yes # Audio output on port C6
62UNICODE_ENABLE = no # Unicode 62UNICODE_ENABLE = no # Unicode
63BLUETOOTH_ENABLE = no # Enable Bluetooth with the Adafruit EZ-Key HID 63BLUETOOTH_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 5ad543ca7..25e66e67c 100644
--- a/quantum/audio/song_list.h
+++ b/quantum/audio/song_list.h
@@ -248,4 +248,7 @@
248 Q__NOTE(_GS5), \ 248 Q__NOTE(_GS5), \
249 HD_NOTE(_C6), 249 HD_NOTE(_C6),
250 250
251#define TERMINAL_SOUND \
252 E__NOTE(_C5 )
253
251#endif 254#endif
diff --git a/quantum/process_keycode/process_terminal.c b/quantum/process_keycode/process_terminal.c
new file mode 100644
index 000000000..deb1543e3
--- /dev/null
+++ b/quantum/process_keycode/process_terminal.c
@@ -0,0 +1,252 @@
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
17#include "process_terminal.h"
18#include <string.h>
19#include "version.h"
20#include <stdio.h>
21#include <math.h>
22
23bool terminal_enabled = false;
24char buffer[80] = "";
25char newline[2] = "\n";
26char arguments[6][20];
27
28__attribute__ ((weak))
29const char terminal_prompt[8] = "> ";
30
31#ifdef AUDIO_ENABLE
32 #ifndef TERMINAL_SONG
33 #define TERMINAL_SONG SONG(TERMINAL_SOUND)
34 #endif
35 float terminal_song[][2] = TERMINAL_SONG;
36 #define TERMINAL_BELL() PLAY_SONG(terminal_song)
37#else
38 #define TERMINAL_BELL()
39#endif
40
41__attribute__ ((weak))
42const char keycode_to_ascii_lut[58] = {
43 0, 0, 0, 0,
44 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
45 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
46 '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 0, 0, 0, '\t',
47 ' ', '-', '=', '[', ']', '\\', 0, ';', '\'', '`', ',', '.', '/'
48};
49
50__attribute__ ((weak))
51const char shifted_keycode_to_ascii_lut[58] = {
52 0, 0, 0, 0,
53 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
54 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
55 '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', 0, 0, 0, '\t',
56 ' ', '_', '+', '{', '}', '|', 0, ':', '\'', '~', '<', '>', '?'
57};
58
59struct stringcase {
60 char* string;
61 void (*func)(void);
62} typedef stringcase;
63
64void enable_terminal(void) {
65 terminal_enabled = true;
66 strcpy(buffer, "");
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
74void disable_terminal(void) {
75 terminal_enabled = false;
76}
77
78void terminal_about(void) {
79 SEND_STRING("QMK Firmware\n");
80 SEND_STRING(" v");
81 SEND_STRING(QMK_VERSION);
82 SEND_STRING("\n"SS_TAP(X_HOME)" Built: ");
83 SEND_STRING(QMK_BUILDDATE);
84 send_string(newline);
85 #ifdef TERMINAL_HELP
86 if (strlen(arguments[1]) != 0) {
87 SEND_STRING("You entered: ");
88 send_string(arguments[1]);
89 send_string(newline);
90 }
91 #endif
92}
93
94void terminal_help(void);
95
96extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS];
97
98void terminal_keycode(void) {
99 if (strlen(arguments[1]) != 0 && strlen(arguments[2]) != 0 && strlen(arguments[3]) != 0) {
100 char keycode_dec[5];
101 char keycode_hex[5];
102 uint16_t layer = strtol(arguments[1], (char **)NULL, 10);
103 uint16_t row = strtol(arguments[2], (char **)NULL, 10);
104 uint16_t col = strtol(arguments[3], (char **)NULL, 10);
105 uint16_t keycode = pgm_read_word(&keymaps[layer][row][col]);
106 itoa(keycode, keycode_dec, 10);
107 itoa(keycode, keycode_hex, 16);
108 SEND_STRING("0x");
109 send_string(keycode_hex);
110 SEND_STRING(" (");
111 send_string(keycode_dec);
112 SEND_STRING(")\n");
113 } else {
114 #ifdef TERMINAL_HELP
115 SEND_STRING("usage: keycode <layer> <row> <col>\n");
116 #endif
117 }
118}
119
120void terminal_keymap(void) {
121 if (strlen(arguments[1]) != 0) {
122 uint16_t layer = strtol(arguments[1], (char **)NULL, 10);
123 for (int r = 0; r < MATRIX_ROWS; r++) {
124 for (int c = 0; c < MATRIX_COLS; c++) {
125 uint16_t keycode = pgm_read_word(&keymaps[layer][r][c]);
126 char keycode_s[8];
127 sprintf(keycode_s, "0x%04x, ", keycode);
128 send_string(keycode_s);
129 }
130 send_string(newline);
131 }
132 } else {
133 #ifdef TERMINAL_HELP
134 SEND_STRING("usage: keymap <layer>\n");
135 #endif
136 }
137}
138
139stringcase terminal_cases[] = {
140 { "about", terminal_about },
141 { "help", terminal_help },
142 { "keycode", terminal_keycode },
143 { "keymap", terminal_keymap },
144 { "exit", disable_terminal }
145};
146
147void terminal_help(void) {
148 SEND_STRING("commands available:\n ");
149 for( stringcase* case_p = terminal_cases; case_p != terminal_cases + sizeof( terminal_cases ) / sizeof( terminal_cases[0] ); case_p++ ) {
150 send_string(case_p->string);
151 SEND_STRING(" ");
152 }
153 send_string(newline);
154}
155
156void command_not_found(void) {
157 SEND_STRING("command \"");
158 send_string(buffer);
159 SEND_STRING("\" not found\n");
160}
161
162void process_terminal_command(void) {
163 // we capture return bc of the order of events, so we need to manually send a newline
164 send_string(newline);
165
166 char * pch;
167 uint8_t i = 0;
168 pch = strtok(buffer, " ");
169 while (pch != NULL) {
170 strcpy(arguments[i], pch);
171 pch = strtok(NULL, " ");
172 i++;
173 }
174
175 bool command_found = false;
176 for( stringcase* case_p = terminal_cases; case_p != terminal_cases + sizeof( terminal_cases ) / sizeof( terminal_cases[0] ); case_p++ ) {
177 if( 0 == strcmp( case_p->string, buffer ) ) {
178 command_found = true;
179 (*case_p->func)();
180 break;
181 }
182 }
183
184 if (!command_found)
185 command_not_found();
186
187 if (terminal_enabled) {
188 strcpy(buffer, "");
189 for (int i = 0; i < 6; i++)
190 strcpy(arguments[i], "");
191 SEND_STRING(SS_TAP(X_HOME));
192 send_string(terminal_prompt);
193 }
194}
195
196bool process_terminal(uint16_t keycode, keyrecord_t *record) {
197
198 if (keycode == TERM_ON && record->event.pressed) {
199 enable_terminal();
200 return false;
201 }
202
203 if (terminal_enabled && record->event.pressed) {
204 if (keycode == TERM_OFF && record->event.pressed) {
205 disable_terminal();
206 return false;
207 }
208 if (keycode < 256) {
209 uint8_t str_len;
210 char char_to_add;
211 switch (keycode) {
212 case KC_ENTER:
213 process_terminal_command();
214 return false; break;
215 case KC_ESC:
216 SEND_STRING("\n");
217 enable_terminal();
218 return false; break;
219 case KC_BSPC:
220 str_len = strlen(buffer);
221 if (str_len > 0) {
222 buffer[str_len-1] = 0;
223 return true;
224 } else {
225 TERMINAL_BELL();
226 return false;
227 } break;
228 case KC_LEFT:
229 case KC_RIGHT:
230 case KC_UP:
231 case KC_DOWN:
232 return false; break;
233 default:
234 if (keycode <= 58) {
235 char_to_add = 0;
236 if (get_mods() & (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT))) {
237 char_to_add = shifted_keycode_to_ascii_lut[keycode];
238 } else if (get_mods() == 0) {
239 char_to_add = keycode_to_ascii_lut[keycode];
240 }
241 if (char_to_add != 0) {
242 strncat(buffer, &char_to_add, 1);
243 }
244 } break;
245 }
246
247
248
249 }
250 }
251 return true;
252} \ 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 000000000..d945949a4
--- /dev/null
+++ b/quantum/process_keycode/process_terminal.h
@@ -0,0 +1,27 @@
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
17#ifndef PROCESS_TERMINAL_H
18#define PROCESS_TERMINAL_H
19
20#include "quantum.h"
21
22extern const char keycode_to_ascii_lut[58];
23extern const char shifted_keycode_to_ascii_lut[58];
24extern const char terminal_prompt[8];
25bool process_terminal(uint16_t keycode, keyrecord_t *record);
26
27#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 000000000..56895b33c
--- /dev/null
+++ b/quantum/process_keycode/process_terminal_nop.h
@@ -0,0 +1,25 @@
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
17#ifndef PROCESS_TERMINAL_H
18#define PROCESS_TERMINAL_H
19
20#include "quantum.h"
21
22#define TERM_ON KC_NO
23#define TERM_OFF KC_NO
24
25#endif \ No newline at end of file
diff --git a/quantum/quantum.c b/quantum/quantum.c
index 285e1e81e..1fccaa7d5 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -238,6 +238,9 @@ bool process_record_quantum(keyrecord_t *record) {
238 #ifdef UNICODEMAP_ENABLE 238 #ifdef UNICODEMAP_ENABLE
239 process_unicode_map(keycode, record) && 239 process_unicode_map(keycode, record) &&
240 #endif 240 #endif
241 #ifdef TERMINAL_ENABLE
242 process_terminal(keycode, record) &&
243 #endif
241 true)) { 244 true)) {
242 return false; 245 return false;
243 } 246 }
@@ -600,21 +603,55 @@ void send_string(const char *str) {
600 send_string_with_delay(str, 0); 603 send_string_with_delay(str, 0);
601} 604}
602 605
606void send_string_P(const char *str) {
607 send_string_with_delay_P(str, 0);
608}
609
603void send_string_with_delay(const char *str, uint8_t interval) { 610void send_string_with_delay(const char *str, uint8_t interval) {
604 while (1) { 611 while (1) {
605 uint8_t keycode; 612 char ascii_code = *str;
606 uint8_t ascii_code = pgm_read_byte(str);
607 if (!ascii_code) break; 613 if (!ascii_code) break;
608 keycode = pgm_read_byte(&ascii_to_keycode_lut[ascii_code]); 614 if (ascii_code == 1) {
609 if (pgm_read_byte(&ascii_to_shift_lut[ascii_code])) { 615 // tap
610 register_code(KC_LSFT); 616 uint8_t keycode = *(++str);
611 register_code(keycode); 617 register_code(keycode);
612 unregister_code(keycode); 618 unregister_code(keycode);
613 unregister_code(KC_LSFT); 619 } else if (ascii_code == 2) {
620 // down
621 uint8_t keycode = *(++str);
622 register_code(keycode);
623 } else if (ascii_code == 3) {
624 // up
625 uint8_t keycode = *(++str);
626 unregister_code(keycode);
627 } else {
628 send_char(ascii_code);
614 } 629 }
615 else { 630 ++str;
616 register_code(keycode); 631 // interval
617 unregister_code(keycode); 632 { uint8_t ms = interval; while (ms--) wait_ms(1); }
633 }
634}
635
636void send_string_with_delay_P(const char *str, uint8_t interval) {
637 while (1) {
638 char ascii_code = pgm_read_byte(str);
639 if (!ascii_code) break;
640 if (ascii_code == 1) {
641 // tap
642 uint8_t keycode = pgm_read_byte(++str);
643 register_code(keycode);
644 unregister_code(keycode);
645 } else if (ascii_code == 2) {
646 // down
647 uint8_t keycode = pgm_read_byte(++str);
648 register_code(keycode);
649 } else if (ascii_code == 3) {
650 // up
651 uint8_t keycode = pgm_read_byte(++str);
652 unregister_code(keycode);
653 } else {
654 send_char(ascii_code);
618 } 655 }
619 ++str; 656 ++str;
620 // interval 657 // interval
@@ -622,6 +659,20 @@ void send_string_with_delay(const char *str, uint8_t interval) {
622 } 659 }
623} 660}
624 661
662void send_char(char ascii_code) {
663 uint8_t keycode;
664 keycode = pgm_read_byte(&ascii_to_keycode_lut[(uint8_t)ascii_code]);
665 if (pgm_read_byte(&ascii_to_shift_lut[(uint8_t)ascii_code])) {
666 register_code(KC_LSFT);
667 register_code(keycode);
668 unregister_code(keycode);
669 unregister_code(KC_LSFT);
670 } else {
671 register_code(keycode);
672 unregister_code(keycode);
673 }
674}
675
625void set_single_persistent_default_layer(uint8_t default_layer) { 676void set_single_persistent_default_layer(uint8_t default_layer) {
626 #if defined(AUDIO_ENABLE) && defined(DEFAULT_LAYER_SONGS) 677 #if defined(AUDIO_ENABLE) && defined(DEFAULT_LAYER_SONGS)
627 PLAY_SONG(default_layer_songs[default_layer]); 678 PLAY_SONG(default_layer_songs[default_layer]);
diff --git a/quantum/quantum.h b/quantum/quantum.h
index 9a6d691a1..f3333a002 100644
--- a/quantum/quantum.h
+++ b/quantum/quantum.h
@@ -40,7 +40,7 @@
40#include "action_util.h" 40#include "action_util.h"
41#include <stdlib.h> 41#include <stdlib.h>
42#include "print.h" 42#include "print.h"
43 43#include "send_string_keycodes.h"
44 44
45extern uint32_t default_layer_state; 45extern uint32_t default_layer_state;
46 46
@@ -103,11 +103,32 @@ extern uint32_t default_layer_state;
103 #include "process_key_lock.h" 103 #include "process_key_lock.h"
104#endif 104#endif
105 105
106#define SEND_STRING(str) send_string(PSTR(str)) 106#ifdef TERMINAL_ENABLE
107 #include "process_terminal.h"
108#else
109 #include "process_terminal_nop.h"
110#endif
111
112#define STRINGIZE(z) #z
113#define ADD_SLASH_X(y) STRINGIZE(\x ## y)
114#define SYMBOL_STR(x) ADD_SLASH_X(x)
115
116#define SS_TAP(keycode) "\1" SYMBOL_STR(keycode)
117#define SS_DOWN(keycode) "\2" SYMBOL_STR(keycode)
118#define SS_UP(keycode) "\3" SYMBOL_STR(keycode)
119
120#define SS_LCTRL(string) SS_DOWN(X_LCTRL) string SS_UP(X_LCTRL)
121#define SS_LGUI(string) SS_DOWN(X_LGUI) string SS_UP(X_LGUI)
122#define SS_LALT(string) SS_DOWN(X_LALT) string SS_UP(X_LALT)
123
124#define SEND_STRING(str) send_string_P(PSTR(str))
107extern const bool ascii_to_shift_lut[0x80]; 125extern const bool ascii_to_shift_lut[0x80];
108extern const uint8_t ascii_to_keycode_lut[0x80]; 126extern const uint8_t ascii_to_keycode_lut[0x80];
109void send_string(const char *str); 127void send_string(const char *str);
110void send_string_with_delay(const char *str, uint8_t interval); 128void send_string_with_delay(const char *str, uint8_t interval);
129void send_string_P(const char *str);
130void send_string_with_delay_P(const char *str, uint8_t interval);
131void send_char(char ascii_code);
111 132
112// For tri-layer 133// For tri-layer
113void update_tri_layer(uint8_t layer1, uint8_t layer2, uint8_t layer3); 134void 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 ccd4565f5..26c3c41a7 100644
--- a/quantum/quantum_keycodes.h
+++ b/quantum/quantum_keycodes.h
@@ -431,6 +431,11 @@ enum quantum_keycodes {
431 KC_LOCK, 431 KC_LOCK,
432#endif 432#endif
433 433
434#ifdef TERMINAL_ENABLE
435 TERM_ON,
436 TERM_OFF,
437#endif
438
434 // always leave at the end 439 // always leave at the end
435 SAFE_RANGE 440 SAFE_RANGE
436}; 441};
diff --git a/quantum/send_string_keycodes.h b/quantum/send_string_keycodes.h
new file mode 100644
index 000000000..0e308be50
--- /dev/null
+++ b/quantum/send_string_keycodes.h
@@ -0,0 +1,168 @@
1#ifndef SEND_STRING_KEYCODES
2#define SEND_STRING_KEYCODES
3
4#define X_NO 00
5#define X_ROLL_OVER 01
6#define X_POST_FAIL 02
7#define X_UNDEFINED 03
8#define X_A 04
9#define X_B 05
10#define X_C 06
11#define X_D 07
12#define X_E 08
13#define X_F 09
14#define X_G 0A
15#define X_H 0B
16#define X_I 0C
17#define X_J 0D
18#define X_K 0E
19#define X_L 0F
20#define X_M 10
21#define X_N 11
22#define X_O 12
23#define X_P 13
24#define X_Q 14
25#define X_R 15
26#define X_S 16
27#define X_T 17
28#define X_U 18
29#define X_V 19
30#define X_W 1A
31#define X_X 1B
32#define X_Y 1C
33#define X_Z 1D
34#define X_1 1E
35#define X_2 1F
36#define X_3 20
37#define X_4 21
38#define X_5 22
39#define X_6 23
40#define X_7 24
41#define X_8 25
42#define X_9 26
43#define X_0 27
44#define X_ENTER 28
45#define X_ESCAPE 29
46#define X_BSPACE 2A
47#define X_TAB 2B
48#define X_SPACE 2C
49#define X_MINUS 2D
50#define X_EQUAL 2E
51#define X_LBRACKET 2F
52#define X_RBRACKET 30
53#define X_BSLASH 31
54#define X_NONUS_HASH 32
55#define X_SCOLON 33
56#define X_QUOTE 34
57#define X_GRAVE 35
58#define X_COMMA 36
59#define X_DOT 37
60#define X_SLASH 38
61#define X_CAPSLOCK 39
62#define X_F1 3A
63#define X_F2 3B
64#define X_F3 3C
65#define X_F4 3D
66#define X_F5 3E
67#define X_F6 3F
68#define X_F7 40
69#define X_F8 41
70#define X_F9 42
71#define X_F10 43
72#define X_F11 44
73#define X_F12 45
74#define X_PSCREEN 46
75#define X_SCROLLLOCK 47
76#define X_PAUSE 48
77#define X_INSERT 49
78#define X_HOME 4A
79#define X_PGUP 4B
80#define X_DELETE 4C
81#define X_END 4D
82#define X_PGDOWN 4E
83#define X_RIGHT 4F
84#define X_LEFT 50
85#define X_DOWN 51
86#define X_UP 52
87#define X_NUMLOCK 53
88#define X_KP_SLASH 54
89#define X_KP_ASTERISK 55
90#define X_KP_MINUS 56
91#define X_KP_PLUS 57
92#define X_KP_ENTER 58
93#define X_KP_1 59
94#define X_KP_2 5A
95#define X_KP_3 5B
96#define X_KP_4 5C
97#define X_KP_5 5D
98#define X_KP_6 5E
99#define X_KP_7 5F
100#define X_KP_8 60
101#define X_KP_9 61
102#define X_KP_0 62
103#define X_KP_DOT 63
104#define X_NONUS_BSLASH 64
105#define X_APPLICATION 65
106#define X_POWER 66
107#define X_KP_EQUAL 67
108#define X_F13 68
109#define X_F14 69
110#define X_F15 6A
111#define X_F16 6B
112#define X_F17 6C
113#define X_F18 6D
114#define X_F19 6E
115#define X_F20 6F
116#define X_F21 70
117#define X_F22 71
118#define X_F23 72
119#define X_F24 73
120#define X_EXECUTE 74
121#define X_HELP 75
122#define X_MENU 76
123#define X_SELECT 77
124#define X_STOP 78
125#define X_AGAIN 79
126#define X_UNDO 7A
127#define X_CUT 7B
128#define X_COPY 7C
129#define X_PASTE 7D
130#define X_FIND 7E
131#define X__MUTE 7F
132#define X__VOLUP 80
133#define X__VOLDOWN 81
134#define X_LOCKING_CAPS 82
135#define X_LOCKING_NUM 83
136#define X_LOCKING_SCROLL 84
137#define X_KP_COMMA 85
138#define X_KP_EQUAL_AS400 86
139#define X_INT1 87
140#define X_INT2 88
141#define X_INT3 89
142#define X_INT4 8A
143#define X_INT5 8B
144#define X_INT6 8C
145#define X_INT7 8D
146#define X_INT8 8E
147#define X_INT9 8F
148#define X_LANG1 90
149#define X_LANG2 91
150#define X_LANG3 92
151#define X_LANG4 93
152#define X_LANG5 94
153#define X_LANG6 95
154#define X_LANG7 96
155#define X_LANG8 97
156#define X_LANG9 98
157
158/* Modifiers */
159#define X_LCTRL e0
160#define X_LSHIFT e1
161#define X_LALT e2
162#define X_LGUI e3
163#define X_RCTRL e4
164#define X_RSHIFT e5
165#define X_RALT e6
166#define X_RGUI e7
167
168#endif \ No newline at end of file