1 /* $OpenBSD: cmd-send-keys.c,v 1.75 2023/01/16 11:26:14 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 21 #include <stdlib.h> 22 #include <string.h> 23 24 #include "tmux.h" 25 26 /* 27 * Send keys to client. 28 */ 29 30 static enum cmd_retval cmd_send_keys_exec(struct cmd *, struct cmdq_item *); 31 32 const struct cmd_entry cmd_send_keys_entry = { 33 .name = "send-keys", 34 .alias = "send", 35 36 .args = { "c:FHKlMN:Rt:X", 0, -1, NULL }, 37 .usage = "[-FHKlMRX] [-c target-client] [-N repeat-count] " 38 CMD_TARGET_PANE_USAGE " key ...", 39 40 .target = { 't', CMD_FIND_PANE, 0 }, 41 42 .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL, 43 .exec = cmd_send_keys_exec 44 }; 45 46 const struct cmd_entry cmd_send_prefix_entry = { 47 .name = "send-prefix", 48 .alias = NULL, 49 50 .args = { "2t:", 0, 0, NULL }, 51 .usage = "[-2] " CMD_TARGET_PANE_USAGE, 52 53 .target = { 't', CMD_FIND_PANE, 0 }, 54 55 .flags = CMD_AFTERHOOK, 56 .exec = cmd_send_keys_exec 57 }; 58 59 static struct cmdq_item * 60 cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after, 61 struct args *args, key_code key) 62 { 63 struct cmd_find_state *target = cmdq_get_target(item); 64 struct client *tc = cmdq_get_target_client(item); 65 struct session *s = target->s; 66 struct winlink *wl = target->wl; 67 struct window_pane *wp = target->wp; 68 struct window_mode_entry *wme; 69 struct key_table *table = NULL; 70 struct key_binding *bd; 71 struct key_event *event; 72 73 if (args_has(args, 'K')) { 74 if (tc == NULL) 75 return (item); 76 event = xmalloc(sizeof *event); 77 event->key = key|KEYC_SENT; 78 memset(&event->m, 0, sizeof event->m); 79 if (server_client_handle_key(tc, event) == 0) 80 free(event); 81 return (item); 82 } 83 84 wme = TAILQ_FIRST(&wp->modes); 85 if (wme == NULL || wme->mode->key_table == NULL) { 86 if (window_pane_key(wp, tc, s, wl, key, NULL) != 0) 87 return (NULL); 88 return (item); 89 } 90 table = key_bindings_get_table(wme->mode->key_table(wme), 1); 91 92 bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS); 93 if (bd != NULL) { 94 table->references++; 95 after = key_bindings_dispatch(bd, after, tc, NULL, target); 96 key_bindings_unref_table(table); 97 } 98 return (after); 99 } 100 101 static struct cmdq_item * 102 cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after, 103 struct args *args, int i) 104 { 105 const char *s = args_string(args, i); 106 struct utf8_data *ud, *loop; 107 utf8_char uc; 108 key_code key; 109 char *endptr; 110 long n; 111 int literal; 112 113 if (args_has(args, 'H')) { 114 n = strtol(s, &endptr, 16); 115 if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0') 116 return (item); 117 return (cmd_send_keys_inject_key(item, after, args, 118 KEYC_LITERAL|n)); 119 } 120 121 literal = args_has(args, 'l'); 122 if (!literal) { 123 key = key_string_lookup_string(s); 124 if (key != KEYC_NONE && key != KEYC_UNKNOWN) { 125 after = cmd_send_keys_inject_key(item, after, args, 126 key); 127 if (after != NULL) 128 return (after); 129 } 130 literal = 1; 131 } 132 if (literal) { 133 ud = utf8_fromcstr(s); 134 for (loop = ud; loop->size != 0; loop++) { 135 if (loop->size == 1 && loop->data[0] <= 0x7f) 136 key = loop->data[0]; 137 else { 138 if (utf8_from_data(loop, &uc) != UTF8_DONE) 139 continue; 140 key = uc; 141 } 142 after = cmd_send_keys_inject_key(item, after, args, 143 key); 144 } 145 free(ud); 146 } 147 return (after); 148 } 149 150 static enum cmd_retval 151 cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) 152 { 153 struct args *args = cmd_get_args(self); 154 struct cmd_find_state *target = cmdq_get_target(item); 155 struct client *tc = cmdq_get_target_client(item); 156 struct session *s = target->s; 157 struct winlink *wl = target->wl; 158 struct window_pane *wp = target->wp; 159 struct key_event *event = cmdq_get_event(item); 160 struct mouse_event *m = &event->m; 161 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); 162 struct cmdq_item *after = item; 163 key_code key; 164 u_int i, np = 1; 165 u_int count = args_count(args); 166 char *cause = NULL; 167 168 if (args_has(args, 'N')) { 169 np = args_strtonum_and_expand(args, 'N', 1, UINT_MAX, item, 170 &cause); 171 if (cause != NULL) { 172 cmdq_error(item, "repeat count %s", cause); 173 free(cause); 174 return (CMD_RETURN_ERROR); 175 } 176 if (wme != NULL && (args_has(args, 'X') || count == 0)) { 177 if (wme->mode->command == NULL) { 178 cmdq_error(item, "not in a mode"); 179 return (CMD_RETURN_ERROR); 180 } 181 wme->prefix = np; 182 } 183 } 184 185 if (args_has(args, 'X')) { 186 if (wme == NULL || wme->mode->command == NULL) { 187 cmdq_error(item, "not in a mode"); 188 return (CMD_RETURN_ERROR); 189 } 190 if (!m->valid) 191 m = NULL; 192 wme->mode->command(wme, tc, s, wl, args, m); 193 return (CMD_RETURN_NORMAL); 194 } 195 196 if (args_has(args, 'M')) { 197 wp = cmd_mouse_pane(m, &s, NULL); 198 if (wp == NULL) { 199 cmdq_error(item, "no mouse target"); 200 return (CMD_RETURN_ERROR); 201 } 202 window_pane_key(wp, tc, s, wl, m->key, m); 203 return (CMD_RETURN_NORMAL); 204 } 205 206 if (cmd_get_entry(self) == &cmd_send_prefix_entry) { 207 if (args_has(args, '2')) 208 key = options_get_number(s->options, "prefix2"); 209 else 210 key = options_get_number(s->options, "prefix"); 211 cmd_send_keys_inject_key(item, item, args, key); 212 return (CMD_RETURN_NORMAL); 213 } 214 215 if (args_has(args, 'R')) { 216 colour_palette_clear(&wp->palette); 217 input_reset(wp->ictx, 1); 218 wp->flags |= (PANE_STYLECHANGED|PANE_REDRAW); 219 } 220 221 if (count == 0) { 222 if (args_has(args, 'N') || args_has(args, 'R')) 223 return (CMD_RETURN_NORMAL); 224 for (; np != 0; np--) 225 cmd_send_keys_inject_key(item, NULL, args, event->key); 226 return (CMD_RETURN_NORMAL); 227 } 228 229 for (; np != 0; np--) { 230 for (i = 0; i < count; i++) { 231 after = cmd_send_keys_inject_string(item, after, args, 232 i); 233 } 234 } 235 236 return (CMD_RETURN_NORMAL); 237 } 238