mirror of
https://github.com/taigrr/arduinolibs
synced 2025-01-18 04:33:12 -08:00
Shell and Terminal API
This commit is contained in:
304
gen/genkeymap.cpp
Normal file
304
gen/genkeymap.cpp
Normal file
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Southern Storm Software, Pty Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// Generates the keymap state machine table for the Terminal class.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
#include "../libraries/Terminal/USBKeysExtra.h"
|
||||
|
||||
typedef struct {
|
||||
const char *sequence;
|
||||
int code;
|
||||
} EscKey;
|
||||
static EscKey const escKeys[] = {
|
||||
// Based on the key sequence tables from:
|
||||
// http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-PC-Style-Function-Keys
|
||||
// http://aperiodic.net/phil/archives/Geekery/term-function-keys.html
|
||||
// Also some miscellaneous codes from other sources like the Linux console.
|
||||
|
||||
// Cursor control keys.
|
||||
{"[A", KEY_UP_ARROW},
|
||||
{"OA", KEY_UP_ARROW},
|
||||
{"A", KEY_UP_ARROW},
|
||||
{"[B", KEY_DOWN_ARROW},
|
||||
{"OB", KEY_DOWN_ARROW},
|
||||
{"B", KEY_DOWN_ARROW},
|
||||
{"[C", KEY_RIGHT_ARROW},
|
||||
{"OC", KEY_RIGHT_ARROW},
|
||||
{"C", KEY_RIGHT_ARROW},
|
||||
{"[D", KEY_LEFT_ARROW},
|
||||
{"OD", KEY_LEFT_ARROW},
|
||||
{"D", KEY_LEFT_ARROW},
|
||||
{"[H", KEY_HOME},
|
||||
{"OH", KEY_HOME},
|
||||
{"[1~", KEY_HOME},
|
||||
{"[F", KEY_END},
|
||||
{"OF", KEY_END},
|
||||
{"[4~", KEY_END},
|
||||
{"[2~", KEY_INSERT},
|
||||
{"[3~", KEY_DELETE},
|
||||
{"[5~", KEY_PAGE_UP},
|
||||
{"[6~", KEY_PAGE_DOWN},
|
||||
|
||||
// Numeric keypad. Mostly mapped back to ASCII.
|
||||
{"O ", ' '},
|
||||
{"? ", ' '},
|
||||
{"OI", KEY_TAB},
|
||||
{"?I", KEY_TAB},
|
||||
{"OM", KEY_RETURN},
|
||||
{"?M", KEY_RETURN},
|
||||
{"Oj", '*'},
|
||||
{"?j", '*'},
|
||||
{"Ok", '+'},
|
||||
{"?k", '+'},
|
||||
{"Ol", ','},
|
||||
{"?l", ','},
|
||||
{"Om", '-'},
|
||||
{"?m", '-'},
|
||||
{"On", '.'},
|
||||
{"?n", '.'},
|
||||
{"Oo", '/'},
|
||||
{"?o", '/'},
|
||||
{"Op", '0'},
|
||||
{"?p", '0'},
|
||||
{"Oq", '1'},
|
||||
{"?q", '1'},
|
||||
{"Or", '2'},
|
||||
{"?r", '2'},
|
||||
{"Os", '3'},
|
||||
{"?s", '3'},
|
||||
{"Ot", '4'},
|
||||
{"?t", '4'},
|
||||
{"Ou", '5'},
|
||||
{"?u", '5'},
|
||||
{"Ov", '6'},
|
||||
{"?v", '6'},
|
||||
{"Ow", '7'},
|
||||
{"?w", '7'},
|
||||
{"Ox", '8'},
|
||||
{"?x", '8'},
|
||||
{"Oy", '9'},
|
||||
{"?y", '9'},
|
||||
{"OX", '='},
|
||||
{"?X", '='},
|
||||
|
||||
// Function keys.
|
||||
{"[11~", KEY_F1},
|
||||
{"P", KEY_F1},
|
||||
{"OP", KEY_F1},
|
||||
{"[[A", KEY_F1},
|
||||
{"[12~", KEY_F2},
|
||||
{"Q", KEY_F2},
|
||||
{"OQ", KEY_F2},
|
||||
{"[[B", KEY_F2},
|
||||
{"[13~", KEY_F3},
|
||||
{"R", KEY_F3},
|
||||
{"OR", KEY_F3},
|
||||
{"[[C", KEY_F3},
|
||||
{"[14~", KEY_F4},
|
||||
{"S", KEY_F4},
|
||||
{"OS", KEY_F4},
|
||||
{"[[D", KEY_F4},
|
||||
{"[15~", KEY_F5},
|
||||
{"[[E", KEY_F5},
|
||||
{"[17~", KEY_F6},
|
||||
{"[18~", KEY_F7},
|
||||
{"[19~", KEY_F8},
|
||||
{"[20~", KEY_F9},
|
||||
{"[21~", KEY_F10},
|
||||
{"[23~", KEY_F11},
|
||||
{"[24~", KEY_F12},
|
||||
{"[25~", KEY_F13},
|
||||
{"[11;2~", KEY_F13},
|
||||
{"O2P", KEY_F13},
|
||||
{"[26~", KEY_F14},
|
||||
{"[12;2~", KEY_F14},
|
||||
{"O2Q", KEY_F14},
|
||||
{"[28~", KEY_F15},
|
||||
{"[13;2~", KEY_F15},
|
||||
{"O2R", KEY_F15},
|
||||
{"[29~", KEY_F16},
|
||||
{"[14;2~", KEY_F16},
|
||||
{"O2S", KEY_F16},
|
||||
{"[31~", KEY_F17},
|
||||
{"[15;2~", KEY_F17},
|
||||
{"[32~", KEY_F18},
|
||||
{"[17;2~", KEY_F18},
|
||||
{"[33~", KEY_F19},
|
||||
{"[18;2~", KEY_F19},
|
||||
{"[34~", KEY_F20},
|
||||
{"[19;2~", KEY_F20},
|
||||
{"[20;2~", KEY_F21},
|
||||
{"[23$", KEY_F21},
|
||||
{"[21;2~", KEY_F22},
|
||||
{"[24$", KEY_F22},
|
||||
{"[23;2~", KEY_F23},
|
||||
{"[11^", KEY_F23},
|
||||
{"[24;2~", KEY_F24},
|
||||
{"[12^", KEY_F24},
|
||||
|
||||
// Other keys.
|
||||
{"[Z", KEY_BACK_TAB},
|
||||
{"OZ", KEY_BACK_TAB},
|
||||
{"[P", KEY_PAUSE},
|
||||
{"[G", KEY_NUMPAD_5},
|
||||
};
|
||||
#define numEscKeys (sizeof(escKeys) / sizeof(escKeys[0]))
|
||||
|
||||
class Node
|
||||
{
|
||||
public:
|
||||
explicit Node(Node *parent = 0);
|
||||
~Node();
|
||||
|
||||
void add(const char *str, int code);
|
||||
void dumpRules(std::vector<uint8_t> *vec);
|
||||
|
||||
int ch;
|
||||
int code;
|
||||
int offset;
|
||||
Node *firstChild;
|
||||
Node *lastChild;
|
||||
Node *nextChild;
|
||||
};
|
||||
|
||||
Node::Node(Node *parent)
|
||||
: ch(0)
|
||||
, code(-1)
|
||||
, offset(0)
|
||||
, firstChild(0)
|
||||
, lastChild(0)
|
||||
, nextChild(0)
|
||||
{
|
||||
if (parent) {
|
||||
if (parent->lastChild)
|
||||
parent->lastChild->nextChild = this;
|
||||
else
|
||||
parent->firstChild = this;
|
||||
parent->lastChild = this;
|
||||
}
|
||||
}
|
||||
|
||||
Node::~Node()
|
||||
{
|
||||
Node *child = firstChild;
|
||||
Node *next;
|
||||
while (child != 0) {
|
||||
next = child->nextChild;
|
||||
delete child;
|
||||
child = next;
|
||||
}
|
||||
}
|
||||
|
||||
void Node::add(const char *str, int code)
|
||||
{
|
||||
int ch = str[0] & 0xFF;
|
||||
Node *child = firstChild;
|
||||
while (child != 0) {
|
||||
if (child->ch == ch)
|
||||
break;
|
||||
child = child->nextChild;
|
||||
}
|
||||
if (!child) {
|
||||
child = new Node(this);
|
||||
child->ch = ch;
|
||||
}
|
||||
if (str[1] == '\0') {
|
||||
// Leaf node at the end of a string.
|
||||
child->code = code;
|
||||
} else {
|
||||
// Interior node with more children.
|
||||
child->add(str + 1, code);
|
||||
}
|
||||
}
|
||||
|
||||
void Node::dumpRules(std::vector<uint8_t> *vec)
|
||||
{
|
||||
Node *child;
|
||||
|
||||
// First pass: Output the recognizers for this level.
|
||||
offset = vec->size();
|
||||
child = firstChild;
|
||||
while (child != 0) {
|
||||
if (child->firstChild) {
|
||||
// Interior nodes need 3 bytes for the character and offset.
|
||||
vec->push_back((uint8_t)(child->ch | 0x80));
|
||||
vec->push_back(0);
|
||||
vec->push_back(0);
|
||||
} else {
|
||||
// Leaf nodes need 2 bytes for the character and code.
|
||||
vec->push_back((uint8_t)(child->ch));
|
||||
vec->push_back((uint8_t)(child->code));
|
||||
if (child->code > 255)
|
||||
printf("Code 0x%X exceeds 255 - need to change table format\n", child->code);
|
||||
}
|
||||
child = child->nextChild;
|
||||
}
|
||||
vec->push_back(0); // Terminate this level.
|
||||
|
||||
// Second pass: Output the recognizers for the child levels.
|
||||
child = firstChild;
|
||||
while (child != 0) {
|
||||
if (child->firstChild)
|
||||
child->dumpRules(vec);
|
||||
child = child->nextChild;
|
||||
}
|
||||
|
||||
// Third pass: Back-patch the links to the child recognizers.
|
||||
int posn = offset;
|
||||
child = firstChild;
|
||||
while (child != 0) {
|
||||
if (child->firstChild) {
|
||||
int value = child->offset;
|
||||
(*vec)[posn + 1] = (uint8_t)value;
|
||||
(*vec)[posn + 2] = (uint8_t)(value >> 8);
|
||||
posn += 3;
|
||||
} else {
|
||||
posn += 2;
|
||||
}
|
||||
child = child->nextChild;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Node *root = new Node();
|
||||
for (unsigned index = 0; index < numEscKeys; ++index)
|
||||
root->add(escKeys[index].sequence, escKeys[index].code);
|
||||
std::vector<uint8_t> vec;
|
||||
root->dumpRules(&vec);
|
||||
printf("static uint8_t const keymap[%d] PROGMEM = {\n", (int)vec.size());
|
||||
for (unsigned index = 0; index < vec.size(); ++index) {
|
||||
if ((index % 12) == 0)
|
||||
printf(" ");
|
||||
printf("0x%02X", (int)(vec[index]));
|
||||
if ((index % 12) == 11)
|
||||
printf(",\n");
|
||||
else
|
||||
printf(", ");
|
||||
}
|
||||
printf("};\n");
|
||||
delete root;
|
||||
return 0;
|
||||
}
|
||||
235
gen/genwcwidth.c
Normal file
235
gen/genwcwidth.c
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Southern Storm Software, Pty Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// Generates the Terminal::isWideCharacter() function from the data at:
|
||||
// http://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt
|
||||
// http://www.unicode.org/reports/tr11/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_UNICODE 0x10FFFF
|
||||
#define NUM_UNICODE (MAX_UNICODE + 1)
|
||||
|
||||
static unsigned char *masks;
|
||||
|
||||
static void mark_range(long code1, long code2)
|
||||
{
|
||||
while (code1 <= code2) {
|
||||
if (code1 > MAX_UNICODE)
|
||||
break;
|
||||
masks[code1 / 8] |= (1 << (code1 % 8));
|
||||
++code1;
|
||||
}
|
||||
}
|
||||
|
||||
static void unmark_range(long code1, long code2)
|
||||
{
|
||||
while (code1 <= code2) {
|
||||
if (code1 > MAX_UNICODE)
|
||||
break;
|
||||
masks[code1 / 8] &= ~(1 << (code1 % 8));
|
||||
++code1;
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_ranges(void)
|
||||
{
|
||||
long code;
|
||||
int index, sum;
|
||||
unsigned char *prevptr = 0;
|
||||
unsigned char *ptr;
|
||||
int dotdot = 0;
|
||||
for (code = 0; code <= MAX_UNICODE; code += 0x100) {
|
||||
ptr = masks + (code / 8);
|
||||
sum = 0;
|
||||
for (index = 0; index < 32; ++index)
|
||||
sum += ptr[index];
|
||||
if (sum == 0 || sum == (0xFF * 32)) {
|
||||
if (prevptr && !memcmp(ptr, prevptr, 32)) {
|
||||
if (!dotdot) {
|
||||
dotdot = 1;
|
||||
printf("..\n");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
dotdot = 0;
|
||||
printf("%06lX: ", code);
|
||||
for (index = 0; index < 32; ++index)
|
||||
printf("%02X", ptr[index]);
|
||||
printf("\n");
|
||||
prevptr = ptr;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void print_lookup_table(const char *name, long first, long last)
|
||||
{
|
||||
long index, size;
|
||||
unsigned char *ptr = masks + first / 8;
|
||||
size = (last - first + 1) / 8;
|
||||
printf(" static unsigned char const %s[%ld] PROGMEM = {\n", name, size);
|
||||
for (index = 0; index < size; ++index) {
|
||||
if ((index % 8) == 0)
|
||||
printf(" ");
|
||||
printf("0x%02X", ptr[index]);
|
||||
if (index < (size - 1)) {
|
||||
if ((index % 8) == 7)
|
||||
printf(",\n");
|
||||
else
|
||||
printf(", ");
|
||||
} else {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf(" };\n");
|
||||
}
|
||||
|
||||
static void recognizer(void)
|
||||
{
|
||||
long code;
|
||||
int first = 1;
|
||||
|
||||
printf("bool Terminal::isWideCharacter(long code)\n{\n");
|
||||
printf(" // This function was automatically generated by genwcwidth.c\n");
|
||||
print_lookup_table("range3000", 0x3000, 0x30FF);
|
||||
print_lookup_table("rangeFE00", 0xFE00, 0xFFFF);
|
||||
printf(" unsigned c;\n");
|
||||
|
||||
// Bail out early for Latin character sets.
|
||||
printf(" if (code < 0x2300) {\n");
|
||||
printf(" return false;\n");
|
||||
|
||||
// Densely populated ranges.
|
||||
printf(" } else if (code >= 0x3000 && code <= 0x30FF) {\n");
|
||||
printf(" c = (unsigned)(code - 0x3000);\n");
|
||||
printf(" return (pgm_read_byte(range3000 + (c / 8)) & (1 << (c %% 8))) != 0;\n");
|
||||
printf(" } else if (code >= 0xFE00 && code <= 0xFFFF) {\n");
|
||||
printf(" c = (unsigned)(code - 0xFE00);\n");
|
||||
printf(" return (pgm_read_byte(rangeFE00 + (c / 8)) & (1 << (c %% 8))) != 0;\n");
|
||||
|
||||
// Deal with the main wide character ranges.
|
||||
printf(" } else if (code >= 0x3400 && code <= 0x4DBF) {\n");
|
||||
printf(" return true;\n");
|
||||
printf(" } else if (code >= 0x4E00 && code <= 0x9FFF) {\n");
|
||||
printf(" return true;\n");
|
||||
printf(" } else if (code >= 0xF900 && code <= 0xFAFF) {\n");
|
||||
printf(" return true;\n");
|
||||
printf(" } else if (code >= 0x20000 && code <= 0x2FFFD) {\n");
|
||||
printf(" return true;\n");
|
||||
printf(" } else if (code >= 0x30000 && code <= 0x3FFFD) {\n");
|
||||
printf(" return true;\n");
|
||||
printf(" } else if (");
|
||||
|
||||
// Deal with the left-overs.
|
||||
unmark_range(0x3000, 0x30FF);
|
||||
unmark_range(0xFE00, 0xFFFF);
|
||||
for (code = 0; code <= MAX_UNICODE; ++code) {
|
||||
if (masks[code / 8] & (1 << (code % 8))) {
|
||||
if (!first)
|
||||
printf(" ||\n ");
|
||||
else
|
||||
first = 0;
|
||||
printf("code == 0x%04lX", code);
|
||||
}
|
||||
}
|
||||
printf(") {\n");
|
||||
printf(" return true;\n");
|
||||
printf(" }\n");
|
||||
|
||||
printf(" return false;\n");
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
FILE *file;
|
||||
char buffer[BUFSIZ];
|
||||
|
||||
// Allocate memory for the "is this a wide character?" mask array.
|
||||
masks = calloc(NUM_UNICODE / 8, sizeof(unsigned char));
|
||||
if (!masks) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load the contents of "EastAsianWidth.txt".
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: %s EastAsianWidth.txt\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
if ((file = fopen(argv[1], "r")) == NULL) {
|
||||
perror(argv[1]);
|
||||
return 1;
|
||||
}
|
||||
while (fgets(buffer, sizeof(buffer), file)) {
|
||||
if ((buffer[0] >= '0' && buffer[0] <= '9') ||
|
||||
(buffer[0] >= 'A' && buffer[0] <= 'F')) {
|
||||
long code1 = 0;
|
||||
long code2 = 0;
|
||||
char *endptr = NULL;
|
||||
code1 = strtol(buffer, &endptr, 16);
|
||||
if (endptr[0] == '.' && endptr[1] == '.') {
|
||||
endptr += 2;
|
||||
code2 = strtol(buffer, &endptr, 16);
|
||||
} else {
|
||||
code2 = code1;
|
||||
}
|
||||
if (endptr[0] == ';') {
|
||||
// Recognise 'W' and 'F' as wide characters. It is possible
|
||||
// that 'A' (ambiguous) characters may also be wide but only
|
||||
// in East Asian contexts, which we assume we're not for now.
|
||||
if (endptr[1] == 'W' || endptr[1] == 'F') {
|
||||
mark_range(code1, code2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
// Some special ranges that are implicitly all-wide even if the
|
||||
// code points aren't currently allocated by the Unicode standard.
|
||||
mark_range(0x3400, 0x4DBF);
|
||||
mark_range(0x4E00, 0x9FFF);
|
||||
mark_range(0xF900, 0xFAFF);
|
||||
mark_range(0x20000, 0x2FFFD);
|
||||
mark_range(0x30000, 0x3FFFD);
|
||||
|
||||
// Dump the ranges.
|
||||
dump_ranges();
|
||||
|
||||
// Unmark the special ranges to make it easier to find the left-overs.
|
||||
unmark_range(0x3400, 0x4DBF);
|
||||
unmark_range(0x4E00, 0x9FFF);
|
||||
unmark_range(0xF900, 0xFAFF);
|
||||
unmark_range(0x20000, 0x2FFFD);
|
||||
unmark_range(0x30000, 0x3FFFD);
|
||||
|
||||
// Create the recognition tree.
|
||||
recognizer();
|
||||
|
||||
// Clean up and exit.
|
||||
free(masks);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user