mirror of
https://github.com/tio/tio.git
synced 2026-05-01 14:57:59 +02:00
Add user key-script mapping function (--keymap, Ctrl-t k)
User key-script mapping function:
You can specify the mappings as @<key-1>=<script-description-1>
@<key-2>=<script-description-2>... @<key-N>=<script-description-N>.
Script-description is script-filename or '!'script-commands.
After that,
When you press ctrl-t and <key-n>, tio executes <script-description-n>.
This user keymap takes precedence over the default settings (except for
ctrl-t q).
Example of startup option:
tio /dev/ttyUSB1 --keymap '@1=!print(tio.banner())
@2=!tio.write("test\r") @ctrl-a=!tio.write("ctrl-a\r")
@ctrl-j=test-script.tio'
Example of .tioconfig: (note: backslash escape needed.)
keymap = @1=!print(tio.banner()) @2=!tio.write("test\\r")
@ctrl-a=!tio.write("ctrl-a\\r") @ctrl-j=test-script.tio
This commit is contained in:
parent
8722b410a7
commit
60bc7e9cfe
6 changed files with 250 additions and 0 deletions
|
|
@ -250,6 +250,13 @@ static void config_parse_keys(GKeyFile *key_file, char *group)
|
||||||
g_free((void *)string);
|
g_free((void *)string);
|
||||||
string = NULL;
|
string = NULL;
|
||||||
}
|
}
|
||||||
|
config_get_string(key_file, group, "keymap", &string, NULL);
|
||||||
|
if (string != NULL)
|
||||||
|
{
|
||||||
|
option_parse_key_mappings(string);
|
||||||
|
g_free((void *)string);
|
||||||
|
string = NULL;
|
||||||
|
}
|
||||||
config_get_string(key_file, group, "color", &string, NULL);
|
config_get_string(key_file, group, "color", &string, NULL);
|
||||||
if (string != NULL)
|
if (string != NULL)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
10
src/misc.c
10
src/misc.c
|
|
@ -57,6 +57,16 @@ int ctrl_key_code(unsigned char key)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ctrl_key_char(int key_code)
|
||||||
|
{
|
||||||
|
if (key_code >= ('a' & ~0x60) && key_code <= ('z' & ~0x60))
|
||||||
|
{
|
||||||
|
return key_code | 0x60;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
bool regex_match(const char *string, const char *pattern)
|
bool regex_match(const char *string, const char *pattern)
|
||||||
{
|
{
|
||||||
regex_t regex;
|
regex_t regex;
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,15 @@
|
||||||
|
|
||||||
#define POLL_NOWAIT (0)
|
#define POLL_NOWAIT (0)
|
||||||
#define POLL_FOREVER (-1)
|
#define POLL_FOREVER (-1)
|
||||||
|
|
||||||
|
#define TOSTRING_(x) #x
|
||||||
|
#define TOSTR(x) TOSTRING_(x)
|
||||||
|
|
||||||
#define UNUSED(expr) do { (void)(expr); } while (0)
|
#define UNUSED(expr) do { (void)(expr); } while (0)
|
||||||
|
|
||||||
void delay(long ms);
|
void delay(long ms);
|
||||||
int ctrl_key_code(unsigned char key);
|
int ctrl_key_code(unsigned char key);
|
||||||
|
int ctrl_key_char(int key_code);
|
||||||
bool regex_match(const char *string, const char *pattern);
|
bool regex_match(const char *string, const char *pattern);
|
||||||
unsigned long djb2_hash(const unsigned char *str);
|
unsigned long djb2_hash(const unsigned char *str);
|
||||||
void *base62_encode(unsigned long num, char *output);
|
void *base62_encode(unsigned long num, char *output);
|
||||||
|
|
|
||||||
182
src/options.c
182
src/options.c
|
|
@ -63,6 +63,7 @@ enum opt_t
|
||||||
OPT_EXEC,
|
OPT_EXEC,
|
||||||
OPT_RAW,
|
OPT_RAW,
|
||||||
OPT_RAW_INTERACTIVE,
|
OPT_RAW_INTERACTIVE,
|
||||||
|
OPT_KEYMAP,
|
||||||
};
|
};
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|
@ -134,9 +135,12 @@ struct option_t option =
|
||||||
.map_o_ign_cr = false,
|
.map_o_ign_cr = false,
|
||||||
.raw = RAW_ON_DELAY,
|
.raw = RAW_ON_DELAY,
|
||||||
.raw_interactive = RAW_OFF,
|
.raw_interactive = RAW_OFF,
|
||||||
|
.keymap = NULL,
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
struct keymap_t keymaps[KEYMAP_MAX] = {0};
|
||||||
|
|
||||||
void option_print_help(char *argv[])
|
void option_print_help(char *argv[])
|
||||||
{
|
{
|
||||||
UNUSED(argv);
|
UNUSED(argv);
|
||||||
|
|
@ -174,6 +178,7 @@ void option_print_help(char *argv[])
|
||||||
printf(" --log-append Append to log file\n");
|
printf(" --log-append Append to log file\n");
|
||||||
printf(" --log-strip Strip control characters and escape sequences\n");
|
printf(" --log-strip Strip control characters and escape sequences\n");
|
||||||
printf(" -m, --map <flags> Map characters\n");
|
printf(" -m, --map <flags> Map characters\n");
|
||||||
|
printf(" --keymap <keymaps> Set key-script mappings\n");
|
||||||
printf(" -c, --color 0..255|bold|none|list Colorize tio text (default: bold)\n");
|
printf(" -c, --color 0..255|bold|none|list Colorize tio text (default: bold)\n");
|
||||||
printf(" -S, --socket <socket> Redirect I/O to socket\n");
|
printf(" -S, --socket <socket> Redirect I/O to socket\n");
|
||||||
printf(" --raw off|on|on-nodelay Select raw mode for non-interactive use (default: on)\n");
|
printf(" --raw off|on|on-nodelay Select raw mode for non-interactive use (default: on)\n");
|
||||||
|
|
@ -945,6 +950,11 @@ void options_print()
|
||||||
tio_printf(" Script file: %s", option.script_filename);
|
tio_printf(" Script file: %s", option.script_filename);
|
||||||
tio_printf(" Script run: %s", script_run_state_to_string(option.script_run));
|
tio_printf(" Script run: %s", script_run_state_to_string(option.script_run));
|
||||||
}
|
}
|
||||||
|
if (option.script != NULL)
|
||||||
|
{
|
||||||
|
tio_printf(" Script command: %s", option.script);
|
||||||
|
tio_printf(" Script run: %s", script_run_state_to_string(option.script_run));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void options_parse(int argc, char *argv[])
|
void options_parse(int argc, char *argv[])
|
||||||
|
|
@ -1003,6 +1013,7 @@ void options_parse(int argc, char *argv[])
|
||||||
{"log-strip", no_argument, 0, OPT_LOG_STRIP },
|
{"log-strip", no_argument, 0, OPT_LOG_STRIP },
|
||||||
{"socket", required_argument, 0, 'S' },
|
{"socket", required_argument, 0, 'S' },
|
||||||
{"map", required_argument, 0, 'm' },
|
{"map", required_argument, 0, 'm' },
|
||||||
|
{"keymap", required_argument, 0, OPT_KEYMAP },
|
||||||
{"color", required_argument, 0, 'c' },
|
{"color", required_argument, 0, 'c' },
|
||||||
{"input-mode", required_argument, 0, OPT_INPUT_MODE },
|
{"input-mode", required_argument, 0, OPT_INPUT_MODE },
|
||||||
{"output-mode", required_argument, 0, OPT_OUTPUT_MODE },
|
{"output-mode", required_argument, 0, OPT_OUTPUT_MODE },
|
||||||
|
|
@ -1215,6 +1226,11 @@ void options_parse(int argc, char *argv[])
|
||||||
option_parse_raw(optarg, &option.raw_interactive);
|
option_parse_raw(optarg, &option.raw_interactive);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OPT_KEYMAP:
|
||||||
|
option.keymap = optarg;
|
||||||
|
option_parse_key_mappings(optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'v':
|
case 'v':
|
||||||
printf("tio %s\n", VERSION);
|
printf("tio %s\n", VERSION);
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
|
|
@ -1299,3 +1315,169 @@ void options_parse_final(int argc, char *argv[])
|
||||||
// clang-format on
|
// clang-format on
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int keymap_set(char *key_str, int key_len, char *func_str, int func_len)
|
||||||
|
{
|
||||||
|
char func_str_r[KEYMAP_FUNC_STR_MAX + 1];
|
||||||
|
char *srcp;
|
||||||
|
int dst_ofs;
|
||||||
|
int key_ofs;
|
||||||
|
int empty_idx, matched_idx, idx;
|
||||||
|
bool found_empty = false;
|
||||||
|
bool found_matched = false;
|
||||||
|
bool unset_requested = false;
|
||||||
|
|
||||||
|
if (key_str[key_len] != '\0' || func_str[func_len] != '\0')
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* key_str should not include spaces */
|
||||||
|
key_ofs = 0;
|
||||||
|
for (key_ofs = 0; key_ofs < key_len; key_ofs++)
|
||||||
|
{
|
||||||
|
if (key_str[key_ofs] == ' ')
|
||||||
|
{
|
||||||
|
tio_error_printf("Key should not include space");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check disallowed key_str */
|
||||||
|
if (strcmp(key_str, "q") == 0)
|
||||||
|
{
|
||||||
|
tio_error_printf("Key %s is immutable", key_str);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove prefix spaces and postfix spaces from func_str */
|
||||||
|
for (srcp = func_str; *srcp != '\0'; srcp++)
|
||||||
|
{
|
||||||
|
if (*srcp != ' ')
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strncpy(func_str_r, srcp, KEYMAP_KEY_STR_MAX);
|
||||||
|
func_str_r[KEYMAP_KEY_STR_MAX] = '\0';
|
||||||
|
for (dst_ofs = strlen(func_str_r) - 1; dst_ofs >= 0; dst_ofs--)
|
||||||
|
{
|
||||||
|
if (func_str_r[dst_ofs] != ' ')
|
||||||
|
{
|
||||||
|
func_str_r[dst_ofs + 1] = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (strcmp(func_str_r, "nil") == 0 || func_str_r[0] == '\0')
|
||||||
|
{
|
||||||
|
unset_requested = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* search for entry which key matched or is empty */
|
||||||
|
for (idx = 0; idx < KEYMAP_MAX; idx++)
|
||||||
|
{
|
||||||
|
if (found_empty == false && keymaps[idx].key[0] == '\0')
|
||||||
|
{
|
||||||
|
empty_idx = idx;
|
||||||
|
found_empty = true;
|
||||||
|
}
|
||||||
|
if (found_matched == false && strcmp(keymaps[idx].key, key_str) == 0)
|
||||||
|
{
|
||||||
|
matched_idx = idx;
|
||||||
|
found_matched = true;
|
||||||
|
}
|
||||||
|
if (found_empty && found_matched)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update entry */
|
||||||
|
if (unset_requested)
|
||||||
|
{
|
||||||
|
if (found_matched)
|
||||||
|
{
|
||||||
|
keymaps[matched_idx].key[0] = '\0';
|
||||||
|
keymaps[matched_idx].func[0] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else /* set requested */
|
||||||
|
{
|
||||||
|
if (found_matched)
|
||||||
|
{
|
||||||
|
strcpy(keymaps[matched_idx].key, key_str);
|
||||||
|
strcpy(keymaps[matched_idx].func, func_str);
|
||||||
|
}
|
||||||
|
else if (found_empty)
|
||||||
|
{
|
||||||
|
strcpy(keymaps[empty_idx].key, key_str);
|
||||||
|
strcpy(keymaps[empty_idx].func, func_str);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tio_error_printf("Too many keymaps", key_str);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void keymaps_print(const char *title, int indent)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
bool keymap_title_done = false;
|
||||||
|
|
||||||
|
for (idx = 0; idx < KEYMAP_MAX; idx++)
|
||||||
|
{
|
||||||
|
if (keymaps[idx].key[0] == '\0')
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!keymap_title_done)
|
||||||
|
{
|
||||||
|
if (title[0] != '\0')
|
||||||
|
{
|
||||||
|
tio_printf("%s", title);
|
||||||
|
}
|
||||||
|
keymap_title_done = true;
|
||||||
|
}
|
||||||
|
tio_printf("%*sctrl-%c %s : %s", indent, " ", option.prefix_key, keymaps[idx].key, keymaps[idx].func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void option_parse_key_mappings(const char *keymap)
|
||||||
|
{
|
||||||
|
char key_str[KEYMAP_KEY_STR_MAX + 1];
|
||||||
|
char func_str[KEYMAP_FUNC_STR_MAX + 1];
|
||||||
|
int key_len, func_len;
|
||||||
|
char *buffer;
|
||||||
|
char *cp;
|
||||||
|
|
||||||
|
if (keymap == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse specified key mappings */
|
||||||
|
buffer = strdup(keymap);
|
||||||
|
cp = strchr(buffer, '@');
|
||||||
|
if (cp == NULL)
|
||||||
|
{
|
||||||
|
tio_error_print("Can't find keymap top character '@'");
|
||||||
|
goto parse_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (sscanf(cp, "@%" TOSTR(KEYMAP_KEY_STR_MAX) "[^=]=%" TOSTR(KEYMAP_FUNC_STR_MAX) "[^@]", key_str, func_str) == 2)
|
||||||
|
{
|
||||||
|
key_len = strlen(key_str);
|
||||||
|
func_len = strlen(func_str);
|
||||||
|
keymap_set(key_str, key_len, func_str, func_len);
|
||||||
|
cp = strchr(cp + key_len + func_len + 2, '@');
|
||||||
|
if (cp == NULL)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parse_end:
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,7 @@ struct option_t
|
||||||
int hex_n_value;
|
int hex_n_value;
|
||||||
bool vt100;
|
bool vt100;
|
||||||
char *exec;
|
char *exec;
|
||||||
|
char *keymap;
|
||||||
bool map_i_nl_cr;
|
bool map_i_nl_cr;
|
||||||
bool map_i_cr_nl;
|
bool map_i_cr_nl;
|
||||||
bool map_ign_cr;
|
bool map_ign_cr;
|
||||||
|
|
@ -120,7 +121,18 @@ struct option_t
|
||||||
bool map_o_ign_cr;
|
bool map_o_ign_cr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define KEYMAP_MAX 32
|
||||||
|
#define KEYMAP_KEY_STR_MAX 7
|
||||||
|
#define KEYMAP_FUNC_STR_MAX 127
|
||||||
|
|
||||||
|
struct keymap_t
|
||||||
|
{
|
||||||
|
char key[KEYMAP_KEY_STR_MAX + 1];
|
||||||
|
char func[KEYMAP_FUNC_STR_MAX + 1];
|
||||||
|
};
|
||||||
|
|
||||||
extern struct option_t option;
|
extern struct option_t option;
|
||||||
|
extern struct keymap_t keymaps[KEYMAP_MAX];
|
||||||
|
|
||||||
void options_print();
|
void options_print();
|
||||||
void options_parse(int argc, char *argv[]);
|
void options_parse(int argc, char *argv[]);
|
||||||
|
|
@ -147,5 +159,8 @@ void option_parse_timestamp(const char *arg, timestamp_t *timestamp);
|
||||||
const char* option_timestamp_format_to_string(timestamp_t timestamp);
|
const char* option_timestamp_format_to_string(timestamp_t timestamp);
|
||||||
|
|
||||||
void option_parse_mappings(const char *map);
|
void option_parse_mappings(const char *map);
|
||||||
|
void option_parse_key_mappings(const char *keymap);
|
||||||
|
|
||||||
const char* option_raw_to_string(raw_t raw);
|
const char* option_raw_to_string(raw_t raw);
|
||||||
|
|
||||||
|
void keymaps_print(const char *title, int indent);
|
||||||
|
|
|
||||||
31
src/tty.c
31
src/tty.c
|
|
@ -119,6 +119,7 @@
|
||||||
#define KEY_I 0x69
|
#define KEY_I 0x69
|
||||||
#define KEY_J 0x6A
|
#define KEY_J 0x6A
|
||||||
#define KEY_SHIFT_J 0x4A
|
#define KEY_SHIFT_J 0x4A
|
||||||
|
#define KEY_K 0x6B
|
||||||
#define KEY_L 0x6C
|
#define KEY_L 0x6C
|
||||||
#define KEY_SHIFT_L 0x4C
|
#define KEY_SHIFT_L 0x4C
|
||||||
#define KEY_M 0x6D
|
#define KEY_M 0x6D
|
||||||
|
|
@ -1099,6 +1100,26 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle user keymapped commands
|
||||||
|
for (int idx = 0; idx < KEYMAP_MAX; idx++)
|
||||||
|
{
|
||||||
|
if ((input_char >= 0x00) && (input_char <= 0x1f))
|
||||||
|
{
|
||||||
|
int ctrl_key_ch = ctrl_key_char(input_char);
|
||||||
|
if ((ctrl_key_ch >= 0) &&
|
||||||
|
(strncmp("ctrl-", keymaps[idx].key, 5) == 0) && (keymaps[idx].key[5] == ctrl_key_ch))
|
||||||
|
{
|
||||||
|
script_run(keymaps[idx].func);
|
||||||
|
goto handle_commands_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (input_char == keymaps[idx].key[0] && keymaps[idx].key[1] == '\0')
|
||||||
|
{
|
||||||
|
script_run(keymaps[idx].func);
|
||||||
|
goto handle_commands_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle commands
|
// Handle commands
|
||||||
switch (input_char)
|
switch (input_char)
|
||||||
{
|
{
|
||||||
|
|
@ -1128,6 +1149,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward)
|
||||||
tio_printf(" ctrl-%c x Send/Receive file via Xmodem", option.prefix_key);
|
tio_printf(" ctrl-%c x Send/Receive file via Xmodem", option.prefix_key);
|
||||||
tio_printf(" ctrl-%c y Send file via Ymodem", option.prefix_key);
|
tio_printf(" ctrl-%c y Send file via Ymodem", option.prefix_key);
|
||||||
tio_printf(" ctrl-%c ctrl-%c Send ctrl-%c character", option.prefix_key, option.prefix_key, option.prefix_key);
|
tio_printf(" ctrl-%c ctrl-%c Send ctrl-%c character", option.prefix_key, option.prefix_key, option.prefix_key);
|
||||||
|
keymaps_print("User key commands:", 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KEY_SHIFT_L:
|
case KEY_SHIFT_L:
|
||||||
|
|
@ -1203,6 +1225,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward)
|
||||||
rs485_print_config();
|
rs485_print_config();
|
||||||
}
|
}
|
||||||
mappings_print();
|
mappings_print();
|
||||||
|
keymaps_print(" Keymaps:", 4);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KEY_E:
|
case KEY_E:
|
||||||
|
|
@ -1290,6 +1313,12 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case KEY_K:
|
||||||
|
/* Set keymap */
|
||||||
|
tio_subcmd_readln("Enter keymap @<key>=<script-file>|!<script> :");
|
||||||
|
option_parse_key_mappings(line);
|
||||||
|
break;
|
||||||
|
|
||||||
case KEY_L:
|
case KEY_L:
|
||||||
/* Clear screen using ANSI/VT100 escape code */
|
/* Clear screen using ANSI/VT100 escape code */
|
||||||
printf("\033c");
|
printf("\033c");
|
||||||
|
|
@ -1443,6 +1472,8 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward)
|
||||||
/* Ignore unknown ctrl-t escaped keys */
|
/* Ignore unknown ctrl-t escaped keys */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handle_commands_end:
|
||||||
}
|
}
|
||||||
|
|
||||||
previous_char = input_char;
|
previous_char = input_char;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue