control.c revision 1.33
1/* $OpenBSD: control.c,v 1.33 2020/05/24 09:40:17 nicm Exp $ */ 2 3/* 4 * Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * Copyright (c) 2012 George Nachman <tmux@georgester.com> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 16 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 17 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20#include <sys/types.h> 21 22#include <event.h> 23#include <stdlib.h> 24#include <string.h> 25#include <time.h> 26 27#include "tmux.h" 28 29/* Control offsets. */ 30struct control_offset { 31 u_int pane; 32 33 struct window_pane_offset offset; 34 int flags; 35#define CONTROL_OFFSET_OFF 0x1 36 37 RB_ENTRY(control_offset) entry; 38}; 39RB_HEAD(control_offsets, control_offset); 40 41/* Control state. */ 42struct control_state { 43 struct control_offsets offsets; 44}; 45 46/* Compare client offsets. */ 47static int 48control_offset_cmp(struct control_offset *co1, struct control_offset *co2) 49{ 50 if (co1->pane < co2->pane) 51 return (-1); 52 if (co1->pane > co2->pane) 53 return (1); 54 return (0); 55} 56RB_GENERATE_STATIC(control_offsets, control_offset, entry, control_offset_cmp); 57 58/* Get pane offsets for this client. */ 59static struct control_offset * 60control_get_offset(struct client *c, struct window_pane *wp) 61{ 62 struct control_state *cs = c->control_state; 63 struct control_offset co = { .pane = wp->id }; 64 65 return (RB_FIND(control_offsets, &cs->offsets, &co)); 66} 67 68/* Add pane offsets for this client. */ 69static struct control_offset * 70control_add_offset(struct client *c, struct window_pane *wp) 71{ 72 struct control_state *cs = c->control_state; 73 struct control_offset *co; 74 75 co = control_get_offset(c, wp); 76 if (co != NULL) 77 return (co); 78 79 co = xcalloc(1, sizeof *co); 80 co->pane = wp->id; 81 RB_INSERT(control_offsets, &cs->offsets, co); 82 memcpy(&co->offset, &wp->offset, sizeof co->offset); 83 return (co); 84} 85 86/* Free control offsets. */ 87void 88control_free_offsets(struct client *c) 89{ 90 struct control_state *cs = c->control_state; 91 struct control_offset *co, *co1; 92 93 RB_FOREACH_SAFE(co, control_offsets, &cs->offsets, co1) { 94 RB_REMOVE(control_offsets, &cs->offsets, co); 95 free(co); 96 } 97} 98 99/* Get offsets for client. */ 100struct window_pane_offset * 101control_pane_offset(struct client *c, struct window_pane *wp, int *off) 102{ 103 struct control_offset *co; 104 105 if (c->flags & CLIENT_CONTROL_NOOUTPUT) { 106 *off = 0; 107 return (NULL); 108 } 109 110 co = control_get_offset(c, wp); 111 if (co == NULL) { 112 *off = 0; 113 return (NULL); 114 } 115 if (co->flags & CONTROL_OFFSET_OFF) { 116 *off = 1; 117 return (NULL); 118 } 119 return (&co->offset); 120} 121 122/* Set pane as on. */ 123void 124control_set_pane_on(struct client *c, struct window_pane *wp) 125{ 126 struct control_offset *co; 127 128 co = control_get_offset(c, wp); 129 if (co != NULL) { 130 co->flags &= ~CONTROL_OFFSET_OFF; 131 memcpy(&co->offset, &wp->offset, sizeof co->offset); 132 } 133} 134 135/* Set pane as off. */ 136void 137control_set_pane_off(struct client *c, struct window_pane *wp) 138{ 139 struct control_offset *co; 140 141 co = control_add_offset(c, wp); 142 co->flags |= CONTROL_OFFSET_OFF; 143} 144 145/* Write a line. */ 146void 147control_write(struct client *c, const char *fmt, ...) 148{ 149 va_list ap; 150 151 va_start(ap, fmt); 152 file_vprint(c, fmt, ap); 153 file_print(c, "\n"); 154 va_end(ap); 155} 156 157/* Write output from a pane. */ 158void 159control_write_output(struct client *c, struct window_pane *wp) 160{ 161 struct control_offset *co; 162 struct evbuffer *message; 163 u_char *new_data; 164 size_t new_size, i; 165 166 if (c->flags & CLIENT_CONTROL_NOOUTPUT) 167 return; 168 169 /* 170 * Only write input if the window pane is linked to a window belonging 171 * to the client's session. 172 */ 173 if (winlink_find_by_window(&c->session->windows, wp->window) == NULL) 174 return; 175 176 co = control_add_offset(c, wp); 177 if (co->flags & CONTROL_OFFSET_OFF) { 178 window_pane_update_used_data(wp, &co->offset, SIZE_MAX, 1); 179 return; 180 } 181 new_data = window_pane_get_new_data(wp, &co->offset, &new_size); 182 if (new_size == 0) 183 return; 184 185 message = evbuffer_new(); 186 if (message == NULL) 187 fatalx("out of memory"); 188 evbuffer_add_printf(message, "%%output %%%u ", wp->id); 189 190 for (i = 0; i < new_size; i++) { 191 if (new_data[i] < ' ' || new_data[i] == '\\') 192 evbuffer_add_printf(message, "\\%03o", new_data[i]); 193 else 194 evbuffer_add_printf(message, "%c", new_data[i]); 195 } 196 evbuffer_add(message, "", 1); 197 198 control_write(c, "%s", EVBUFFER_DATA(message)); 199 evbuffer_free(message); 200 201 window_pane_update_used_data(wp, &co->offset, new_size, 1); 202} 203 204/* Control error callback. */ 205static enum cmd_retval 206control_error(struct cmdq_item *item, void *data) 207{ 208 struct client *c = cmdq_get_client(item); 209 char *error = data; 210 211 cmdq_guard(item, "begin", 1); 212 control_write(c, "parse error: %s", error); 213 cmdq_guard(item, "error", 1); 214 215 free(error); 216 return (CMD_RETURN_NORMAL); 217} 218 219/* Control input callback. Read lines and fire commands. */ 220static void 221control_callback(__unused struct client *c, __unused const char *path, 222 int read_error, int closed, struct evbuffer *buffer, __unused void *data) 223{ 224 char *line, *error; 225 struct cmdq_state *state; 226 enum cmd_parse_status status; 227 228 if (closed || read_error != 0) 229 c->flags |= CLIENT_EXIT; 230 231 for (;;) { 232 line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF); 233 if (line == NULL) 234 break; 235 log_debug("%s: %s", __func__, line); 236 if (*line == '\0') { /* empty line exit */ 237 free(line); 238 c->flags |= CLIENT_EXIT; 239 break; 240 } 241 242 state = cmdq_new_state(NULL, NULL, CMDQ_STATE_CONTROL); 243 status = cmd_parse_and_append(line, NULL, c, state, &error); 244 if (status == CMD_PARSE_ERROR) 245 cmdq_append(c, cmdq_get_callback(control_error, error)); 246 cmdq_free_state(state); 247 248 free(line); 249 } 250} 251 252/* Initialize for control mode. */ 253void 254control_start(struct client *c) 255{ 256 struct control_state *cs; 257 258 cs = c->control_state = xcalloc(1, sizeof *cs); 259 RB_INIT(&cs->offsets); 260 261 file_read(c, "-", control_callback, c); 262 263 if (c->flags & CLIENT_CONTROLCONTROL) 264 file_print(c, "\033P1000p"); 265} 266 267/* Stop control mode. */ 268void 269control_stop(struct client *c) 270{ 271 struct control_state *cs = c->control_state; 272 273 control_free_offsets(c); 274 free(cs); 275} 276