1 /* $OpenBSD: cmd-send-keys.c,v 1.76 2024/10/01 06:15:47 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 = xcalloc(1, 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->buf); 81 free(event); 82 } 83 return (item); 84 } 85 86 wme = TAILQ_FIRST(&wp->modes); 87 if (wme == NULL || wme->mode->key_table == NULL) { 88 if (window_pane_key(wp, tc, s, wl, key, NULL) != 0) 89 return (NULL); 90 return (item); 91 } 92 table = key_bindings_get_table(wme->mode->key_table(wme), 1); 93 94 bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS); 95 if (bd != NULL) { 96 table->references++; 97 after = key_bindings_dispatch(bd, after, tc, NULL, target); 98 key_bindings_unref_table(table); 99 } 100 return (after); 101 } 102 103 static struct cmdq_item * 104 cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after, 105 struct args *args, int i) 106 { 107 const char *s = args_string(args, i); 108 struct utf8_data *ud, *loop; 109 utf8_char uc; 110 key_code key; 111 char *endptr; 112 long n; 113 int literal; 114 115 if (args_has(args, 'H')) { 116 n = strtol(s, &endptr, 16); 117 if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0') 118 return (item); 119 return (cmd_send_keys_inject_key(item, after, args, 120 KEYC_LITERAL|n)); 121 } 122 123 literal = args_has(args, 'l'); 124 if (!literal) { 125 key = key_string_lookup_string(s); 126 if (key != KEYC_NONE && key != KEYC_UNKNOWN) { 127 after = cmd_send_keys_inject_key(item, after, args, 128 key); 129 if (after != NULL) 130 return (after); 131 } 132 literal = 1; 133 } 134 if (literal) { 135 ud = utf8_fromcstr(s); 136 for (loop = ud; loop->size != 0; loop++) { 137 if (loop->size == 1 && loop->data[0] <= 0x7f) 138 key = loop->data[0]; 139 else { 140 if (utf8_from_data(loop, &uc) != UTF8_DONE) 141 continue; 142 key = uc; 143 } 144 after = cmd_send_keys_inject_key(item, after, args, 145 key); 146 } 147 free(ud); 148 } 149 return (after); 150 } 151 152 static enum cmd_retval 153 cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) 154 { 155 struct args *args = cmd_get_args(self); 156 struct cmd_find_state *target = cmdq_get_target(item); 157 struct client *tc = cmdq_get_target_client(item); 158 struct session *s = target->s; 159 struct winlink *wl = target->wl; 160 struct window_pane *wp = target->wp; 161 struct key_event *event = cmdq_get_event(item); 162 struct mouse_event *m = &event->m; 163 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); 164 struct cmdq_item *after = item; 165 key_code key; 166 u_int i, np = 1; 167 u_int count = args_count(args); 168 char *cause = NULL; 169 170 if (args_has(args, 'N')) { 171 np = args_strtonum_and_expand(args, 'N', 1, UINT_MAX, item, 172 &cause); 173 if (cause != NULL) { 174 cmdq_error(item, "repeat count %s", cause); 175 free(cause); 176 return (CMD_RETURN_ERROR); 177 } 178 if (wme != NULL && (args_has(args, 'X') || count == 0)) { 179 if (wme->mode->command == NULL) { 180 cmdq_error(item, "not in a mode"); 181 return (CMD_RETURN_ERROR); 182 } 183 wme->prefix = np; 184 } 185 } 186 187 if (args_has(args, 'X')) { 188 if (wme == NULL || wme->mode->command == NULL) { 189 cmdq_error(item, "not in a mode"); 190 return (CMD_RETURN_ERROR); 191 } 192 if (!m->valid) 193 m = NULL; 194 wme->mode->command(wme, tc, s, wl, args, m); 195 return (CMD_RETURN_NORMAL); 196 } 197 198 if (args_has(args, 'M')) { 199 wp = cmd_mouse_pane(m, &s, NULL); 200 if (wp == NULL) { 201 cmdq_error(item, "no mouse target"); 202 return (CMD_RETURN_ERROR); 203 } 204 window_pane_key(wp, tc, s, wl, m->key, m); 205 return (CMD_RETURN_NORMAL); 206 } 207 208 if (cmd_get_entry(self) == &cmd_send_prefix_entry) { 209 if (args_has(args, '2')) 210 key = options_get_number(s->options, "prefix2"); 211 else 212 key = options_get_number(s->options, "prefix"); 213 cmd_send_keys_inject_key(item, item, args, key); 214 return (CMD_RETURN_NORMAL); 215 } 216 217 if (args_has(args, 'R')) { 218 colour_palette_clear(&wp->palette); 219 input_reset(wp->ictx, 1); 220 wp->flags |= (PANE_STYLECHANGED|PANE_REDRAW); 221 } 222 223 if (count == 0) { 224 if (args_has(args, 'N') || args_has(args, 'R')) 225 return (CMD_RETURN_NORMAL); 226 for (; np != 0; np--) 227 cmd_send_keys_inject_key(item, NULL, args, event->key); 228 return (CMD_RETURN_NORMAL); 229 } 230 231 for (; np != 0; np--) { 232 for (i = 0; i < count; i++) { 233 after = cmd_send_keys_inject_string(item, after, args, 234 i); 235 } 236 } 237 238 return (CMD_RETURN_NORMAL); 239 } 240