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);
|
||||
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);
|
||||
if (string != NULL)
|
||||
{
|
||||
|
|
|
|||
10
src/misc.c
10
src/misc.c
|
|
@ -57,6 +57,16 @@ int ctrl_key_code(unsigned char key)
|
|||
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)
|
||||
{
|
||||
regex_t regex;
|
||||
|
|
|
|||
|
|
@ -26,10 +26,15 @@
|
|||
|
||||
#define POLL_NOWAIT (0)
|
||||
#define POLL_FOREVER (-1)
|
||||
|
||||
#define TOSTRING_(x) #x
|
||||
#define TOSTR(x) TOSTRING_(x)
|
||||
|
||||
#define UNUSED(expr) do { (void)(expr); } while (0)
|
||||
|
||||
void delay(long ms);
|
||||
int ctrl_key_code(unsigned char key);
|
||||
int ctrl_key_char(int key_code);
|
||||
bool regex_match(const char *string, const char *pattern);
|
||||
unsigned long djb2_hash(const unsigned char *str);
|
||||
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_RAW,
|
||||
OPT_RAW_INTERACTIVE,
|
||||
OPT_KEYMAP,
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
|
|
@ -134,9 +135,12 @@ struct option_t option =
|
|||
.map_o_ign_cr = false,
|
||||
.raw = RAW_ON_DELAY,
|
||||
.raw_interactive = RAW_OFF,
|
||||
.keymap = NULL,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
struct keymap_t keymaps[KEYMAP_MAX] = {0};
|
||||
|
||||
void option_print_help(char *argv[])
|
||||
{
|
||||
UNUSED(argv);
|
||||
|
|
@ -174,6 +178,7 @@ void option_print_help(char *argv[])
|
|||
printf(" --log-append Append to log file\n");
|
||||
printf(" --log-strip Strip control characters and escape sequences\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(" -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");
|
||||
|
|
@ -945,6 +950,11 @@ void options_print()
|
|||
tio_printf(" Script file: %s", option.script_filename);
|
||||
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[])
|
||||
|
|
@ -1003,6 +1013,7 @@ void options_parse(int argc, char *argv[])
|
|||
{"log-strip", no_argument, 0, OPT_LOG_STRIP },
|
||||
{"socket", required_argument, 0, 'S' },
|
||||
{"map", required_argument, 0, 'm' },
|
||||
{"keymap", required_argument, 0, OPT_KEYMAP },
|
||||
{"color", required_argument, 0, 'c' },
|
||||
{"input-mode", required_argument, 0, OPT_INPUT_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);
|
||||
break;
|
||||
|
||||
case OPT_KEYMAP:
|
||||
option.keymap = optarg;
|
||||
option_parse_key_mappings(optarg);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
printf("tio %s\n", VERSION);
|
||||
exit(EXIT_SUCCESS);
|
||||
|
|
@ -1299,3 +1315,169 @@ void options_parse_final(int argc, char *argv[])
|
|||
// clang-format on
|
||||
#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;
|
||||
bool vt100;
|
||||
char *exec;
|
||||
char *keymap;
|
||||
bool map_i_nl_cr;
|
||||
bool map_i_cr_nl;
|
||||
bool map_ign_cr;
|
||||
|
|
@ -120,7 +121,18 @@ struct option_t
|
|||
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 keymap_t keymaps[KEYMAP_MAX];
|
||||
|
||||
void options_print();
|
||||
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);
|
||||
|
||||
void option_parse_mappings(const char *map);
|
||||
void option_parse_key_mappings(const char *keymap);
|
||||
|
||||
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_J 0x6A
|
||||
#define KEY_SHIFT_J 0x4A
|
||||
#define KEY_K 0x6B
|
||||
#define KEY_L 0x6C
|
||||
#define KEY_SHIFT_L 0x4C
|
||||
#define KEY_M 0x6D
|
||||
|
|
@ -1099,6 +1100,26 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward)
|
|||
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
|
||||
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 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);
|
||||
keymaps_print("User key commands:", 1);
|
||||
break;
|
||||
|
||||
case KEY_SHIFT_L:
|
||||
|
|
@ -1203,6 +1225,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward)
|
|||
rs485_print_config();
|
||||
}
|
||||
mappings_print();
|
||||
keymaps_print(" Keymaps:", 4);
|
||||
break;
|
||||
|
||||
case KEY_E:
|
||||
|
|
@ -1290,6 +1313,12 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward)
|
|||
}
|
||||
break;
|
||||
|
||||
case KEY_K:
|
||||
/* Set keymap */
|
||||
tio_subcmd_readln("Enter keymap @<key>=<script-file>|!<script> :");
|
||||
option_parse_key_mappings(line);
|
||||
break;
|
||||
|
||||
case KEY_L:
|
||||
/* Clear screen using ANSI/VT100 escape code */
|
||||
printf("\033c");
|
||||
|
|
@ -1443,6 +1472,8 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward)
|
|||
/* Ignore unknown ctrl-t escaped keys */
|
||||
break;
|
||||
}
|
||||
|
||||
handle_commands_end:
|
||||
}
|
||||
|
||||
previous_char = input_char;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue