1*482624f4Snicm /* $OpenBSD: cmd-send-keys.c,v 1.76 2024/10/01 06:15:47 nicm Exp $ */ 2311827fbSnicm 3311827fbSnicm /* 498ca8272Snicm * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> 5311827fbSnicm * 6311827fbSnicm * Permission to use, copy, modify, and distribute this software for any 7311827fbSnicm * purpose with or without fee is hereby granted, provided that the above 8311827fbSnicm * copyright notice and this permission notice appear in all copies. 9311827fbSnicm * 10311827fbSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11311827fbSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12311827fbSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13311827fbSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14311827fbSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15311827fbSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16311827fbSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17311827fbSnicm */ 18311827fbSnicm 19311827fbSnicm #include <sys/types.h> 20311827fbSnicm 21311827fbSnicm #include <stdlib.h> 2252e27826Snicm #include <string.h> 23311827fbSnicm 24311827fbSnicm #include "tmux.h" 25311827fbSnicm 26311827fbSnicm /* 27311827fbSnicm * Send keys to client. 28311827fbSnicm */ 29311827fbSnicm 3068e0a7f2Snicm static enum cmd_retval cmd_send_keys_exec(struct cmd *, struct cmdq_item *); 31311827fbSnicm 32311827fbSnicm const struct cmd_entry cmd_send_keys_entry = { 33c057646bSnicm .name = "send-keys", 34c057646bSnicm .alias = "send", 35c057646bSnicm 3683a0a8d9Snicm .args = { "c:FHKlMN:Rt:X", 0, -1, NULL }, 3783a0a8d9Snicm .usage = "[-FHKlMRX] [-c target-client] [-N repeat-count] " 3883a0a8d9Snicm CMD_TARGET_PANE_USAGE " key ...", 39c057646bSnicm 40bf0d297eSnicm .target = { 't', CMD_FIND_PANE, 0 }, 418d471e80Snicm 428ed11439Snicm .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL, 43c057646bSnicm .exec = cmd_send_keys_exec 44311827fbSnicm }; 45311827fbSnicm 461311dc0cSnicm const struct cmd_entry cmd_send_prefix_entry = { 47c057646bSnicm .name = "send-prefix", 48c057646bSnicm .alias = NULL, 49c057646bSnicm 50a51dead1Snicm .args = { "2t:", 0, 0, NULL }, 51c057646bSnicm .usage = "[-2] " CMD_TARGET_PANE_USAGE, 52c057646bSnicm 53bf0d297eSnicm .target = { 't', CMD_FIND_PANE, 0 }, 548d471e80Snicm 557a61a8ddSnicm .flags = CMD_AFTERHOOK, 56c057646bSnicm .exec = cmd_send_keys_exec 571311dc0cSnicm }; 581311dc0cSnicm 59ad30ae1dSnicm static struct cmdq_item * 60035dc73dSnicm cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after, 6183a0a8d9Snicm struct args *args, key_code key) 62edafd27bSnicm { 63040343aeSnicm struct cmd_find_state *target = cmdq_get_target(item); 64035dc73dSnicm struct client *tc = cmdq_get_target_client(item); 65035dc73dSnicm struct session *s = target->s; 66035dc73dSnicm struct winlink *wl = target->wl; 67035dc73dSnicm struct window_pane *wp = target->wp; 682c8678f7Snicm struct window_mode_entry *wme; 6983a0a8d9Snicm struct key_table *table = NULL; 704e325abeSnicm struct key_binding *bd; 7183a0a8d9Snicm struct key_event *event; 7283a0a8d9Snicm 7383a0a8d9Snicm if (args_has(args, 'K')) { 748ed11439Snicm if (tc == NULL) 758ed11439Snicm return (item); 76*482624f4Snicm event = xcalloc(1, sizeof *event); 77c79c3b80Snicm event->key = key|KEYC_SENT; 7883a0a8d9Snicm memset(&event->m, 0, sizeof event->m); 79*482624f4Snicm if (server_client_handle_key(tc, event) == 0) { 80*482624f4Snicm free(event->buf); 8183a0a8d9Snicm free(event); 82*482624f4Snicm } 8383a0a8d9Snicm return (item); 8483a0a8d9Snicm } 85edafd27bSnicm 86035dc73dSnicm wme = TAILQ_FIRST(&wp->modes); 8730a94f45Snicm if (wme == NULL || wme->mode->key_table == NULL) { 885416581eSnicm if (window_pane_key(wp, tc, s, wl, key, NULL) != 0) 89de7a415fSnicm return (NULL); 90ad30ae1dSnicm return (item); 91edafd27bSnicm } 9230a94f45Snicm table = key_bindings_get_table(wme->mode->key_table(wme), 1); 93edafd27bSnicm 945416581eSnicm bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS); 95edafd27bSnicm if (bd != NULL) { 96edafd27bSnicm table->references++; 97035dc73dSnicm after = key_bindings_dispatch(bd, after, tc, NULL, target); 98edafd27bSnicm key_bindings_unref_table(table); 99edafd27bSnicm } 100035dc73dSnicm return (after); 101edafd27bSnicm } 102edafd27bSnicm 103a02c6cc0Snicm static struct cmdq_item * 104035dc73dSnicm cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after, 105035dc73dSnicm struct args *args, int i) 106a02c6cc0Snicm { 1071693b10bSnicm const char *s = args_string(args, i); 1086852c63bSnicm struct utf8_data *ud, *loop; 1096852c63bSnicm utf8_char uc; 110a02c6cc0Snicm key_code key; 111a02c6cc0Snicm char *endptr; 112a02c6cc0Snicm long n; 113a02c6cc0Snicm int literal; 114a02c6cc0Snicm 115a02c6cc0Snicm if (args_has(args, 'H')) { 116a02c6cc0Snicm n = strtol(s, &endptr, 16); 117a02c6cc0Snicm if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0') 118a02c6cc0Snicm return (item); 11983a0a8d9Snicm return (cmd_send_keys_inject_key(item, after, args, 12083a0a8d9Snicm KEYC_LITERAL|n)); 121a02c6cc0Snicm } 122a02c6cc0Snicm 123a02c6cc0Snicm literal = args_has(args, 'l'); 124a02c6cc0Snicm if (!literal) { 125a02c6cc0Snicm key = key_string_lookup_string(s); 126de7a415fSnicm if (key != KEYC_NONE && key != KEYC_UNKNOWN) { 12783a0a8d9Snicm after = cmd_send_keys_inject_key(item, after, args, 12883a0a8d9Snicm key); 129035dc73dSnicm if (after != NULL) 130035dc73dSnicm return (after); 131de7a415fSnicm } 132a02c6cc0Snicm literal = 1; 133a02c6cc0Snicm } 134a02c6cc0Snicm if (literal) { 135a02c6cc0Snicm ud = utf8_fromcstr(s); 1366852c63bSnicm for (loop = ud; loop->size != 0; loop++) { 1372d0bf746Snicm if (loop->size == 1 && loop->data[0] <= 0x7f) 1382d0bf746Snicm key = loop->data[0]; 1392d0bf746Snicm else { 1406852c63bSnicm if (utf8_from_data(loop, &uc) != UTF8_DONE) 141a02c6cc0Snicm continue; 1422d0bf746Snicm key = uc; 1432d0bf746Snicm } 14483a0a8d9Snicm after = cmd_send_keys_inject_key(item, after, args, 14583a0a8d9Snicm key); 146a02c6cc0Snicm } 147a02c6cc0Snicm free(ud); 148a02c6cc0Snicm } 149035dc73dSnicm return (after); 150a02c6cc0Snicm } 151a02c6cc0Snicm 152dc1f0f5fSnicm static enum cmd_retval 15368e0a7f2Snicm cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) 154311827fbSnicm { 15590d7ba38Snicm struct args *args = cmd_get_args(self); 156040343aeSnicm struct cmd_find_state *target = cmdq_get_target(item); 157035dc73dSnicm struct client *tc = cmdq_get_target_client(item); 158040343aeSnicm struct session *s = target->s; 159040343aeSnicm struct winlink *wl = target->wl; 160035dc73dSnicm struct window_pane *wp = target->wp; 161823b6d6dSnicm struct key_event *event = cmdq_get_event(item); 162823b6d6dSnicm struct mouse_event *m = &event->m; 1632c8678f7Snicm struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); 164035dc73dSnicm struct cmdq_item *after = item; 165885a4698Snicm key_code key; 1661693b10bSnicm u_int i, np = 1; 1671693b10bSnicm u_int count = args_count(args); 168576538d5Snicm char *cause = NULL; 169576538d5Snicm 170576538d5Snicm if (args_has(args, 'N')) { 171113211eaSnicm np = args_strtonum_and_expand(args, 'N', 1, UINT_MAX, item, 172113211eaSnicm &cause); 173576538d5Snicm if (cause != NULL) { 1746ea8b4b1Snicm cmdq_error(item, "repeat count %s", cause); 175576538d5Snicm free(cause); 176576538d5Snicm return (CMD_RETURN_ERROR); 177576538d5Snicm } 1781693b10bSnicm if (wme != NULL && (args_has(args, 'X') || count == 0)) { 179ba27d7a5Snicm if (wme->mode->command == NULL) { 1802c8678f7Snicm cmdq_error(item, "not in a mode"); 1812c8678f7Snicm return (CMD_RETURN_ERROR); 1822c8678f7Snicm } 18330a94f45Snicm wme->prefix = np; 184576538d5Snicm } 1852c8678f7Snicm } 186576538d5Snicm 187576538d5Snicm if (args_has(args, 'X')) { 18830a94f45Snicm if (wme == NULL || wme->mode->command == NULL) { 18968e0a7f2Snicm cmdq_error(item, "not in a mode"); 190576538d5Snicm return (CMD_RETURN_ERROR); 191576538d5Snicm } 192576538d5Snicm if (!m->valid) 19330a94f45Snicm m = NULL; 194035dc73dSnicm wme->mode->command(wme, tc, s, wl, args, m); 195576538d5Snicm return (CMD_RETURN_NORMAL); 196576538d5Snicm } 197576538d5Snicm 198e048bb79Snicm if (args_has(args, 'M')) { 199e048bb79Snicm wp = cmd_mouse_pane(m, &s, NULL); 200e048bb79Snicm if (wp == NULL) { 20168e0a7f2Snicm cmdq_error(item, "no mouse target"); 202e048bb79Snicm return (CMD_RETURN_ERROR); 203e048bb79Snicm } 204035dc73dSnicm window_pane_key(wp, tc, s, wl, m->key, m); 205e048bb79Snicm return (CMD_RETURN_NORMAL); 206e048bb79Snicm } 207e048bb79Snicm 20890d7ba38Snicm if (cmd_get_entry(self) == &cmd_send_prefix_entry) { 2091311dc0cSnicm if (args_has(args, '2')) 210d89252e5Snicm key = options_get_number(s->options, "prefix2"); 2111311dc0cSnicm else 212d89252e5Snicm key = options_get_number(s->options, "prefix"); 21383a0a8d9Snicm cmd_send_keys_inject_key(item, item, args, key); 2141311dc0cSnicm return (CMD_RETURN_NORMAL); 2151311dc0cSnicm } 2161311dc0cSnicm 217e16c1698Snicm if (args_has(args, 'R')) { 21833a1e283Snicm colour_palette_clear(&wp->palette); 21929ebed37Snicm input_reset(wp->ictx, 1); 22033a1e283Snicm wp->flags |= (PANE_STYLECHANGED|PANE_REDRAW); 221e16c1698Snicm } 22252e27826Snicm 223d1d26277Snicm if (count == 0) { 224fe510045Snicm if (args_has(args, 'N') || args_has(args, 'R')) 22505103773Snicm return (CMD_RETURN_NORMAL); 226d1d26277Snicm for (; np != 0; np--) 22783a0a8d9Snicm cmd_send_keys_inject_key(item, NULL, args, event->key); 228d1d26277Snicm return (CMD_RETURN_NORMAL); 229d1d26277Snicm } 230d1d26277Snicm 2316ea8b4b1Snicm for (; np != 0; np--) { 2321693b10bSnicm for (i = 0; i < count; i++) { 233035dc73dSnicm after = cmd_send_keys_inject_string(item, after, args, 234035dc73dSnicm i); 235040343aeSnicm } 2366ea8b4b1Snicm } 2376ea8b4b1Snicm 238a224d0d3Snicm return (CMD_RETURN_NORMAL); 239311827fbSnicm } 240