Annotation of src/usr.bin/tmux/control.c, Revision 1.32
1.32 ! nicm 1: /* $OpenBSD: control.c,v 1.31 2020/05/21 07:24:13 nicm Exp $ */
1.1 nicm 2:
3: /*
1.17 nicm 4: * Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1 nicm 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>
1.2 nicm 23: #include <stdlib.h>
1.1 nicm 24: #include <string.h>
1.10 nicm 25: #include <time.h>
1.1 nicm 26:
27: #include "tmux.h"
28:
1.32 ! nicm 29: /* Control offsets. */
! 30: struct 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: };
! 39: RB_HEAD(control_offsets, control_offset);
! 40:
! 41: /* Compare client offsets. */
! 42: static int
! 43: control_offset_cmp(struct control_offset *co1, struct control_offset *co2)
! 44: {
! 45: if (co1->pane < co2->pane)
! 46: return (-1);
! 47: if (co1->pane > co2->pane)
! 48: return (1);
! 49: return (0);
! 50: }
! 51: RB_GENERATE_STATIC(control_offsets, control_offset, entry, control_offset_cmp);
! 52:
! 53: /* Get pane offsets for this client. */
! 54: static struct control_offset *
! 55: control_get_offset(struct client *c, struct window_pane *wp)
! 56: {
! 57: struct control_offset co = { .pane = wp->id };
! 58:
! 59: if (c->offsets == NULL)
! 60: return (NULL);
! 61: return (RB_FIND(control_offsets, c->offsets, &co));
! 62: }
! 63:
! 64: /* Add pane offsets for this client. */
! 65: static struct control_offset *
! 66: control_add_offset(struct client *c, struct window_pane *wp)
! 67: {
! 68: struct control_offset *co;
! 69:
! 70: co = control_get_offset(c, wp);
! 71: if (co != NULL)
! 72: return (co);
! 73:
! 74: if (c->offsets == NULL) {
! 75: c->offsets = xmalloc(sizeof *c->offsets);
! 76: RB_INIT(c->offsets);
! 77: }
! 78:
! 79: co = xcalloc(1, sizeof *co);
! 80: co->pane = wp->id;
! 81: RB_INSERT(control_offsets, c->offsets, co);
! 82: memcpy(&co->offset, &wp->offset, sizeof co->offset);
! 83: return (co);
! 84: }
! 85:
! 86: /* Free control offsets. */
! 87: void
! 88: control_free_offsets(struct client *c)
! 89: {
! 90: struct control_offset *co, *co1;
! 91:
! 92: if (c->offsets == NULL)
! 93: return;
! 94: RB_FOREACH_SAFE(co, control_offsets, c->offsets, co1) {
! 95: RB_REMOVE(control_offsets, c->offsets, co);
! 96: free(co);
! 97: }
! 98: free(c->offsets);
! 99: }
! 100:
! 101: /* Get offsets for client. */
! 102: struct window_pane_offset *
! 103: control_pane_offset(struct client *c, struct window_pane *wp, int *off)
! 104: {
! 105: struct control_offset *co;
! 106:
! 107: if (c->flags & CLIENT_CONTROL_NOOUTPUT) {
! 108: *off = 0;
! 109: return (NULL);
! 110: }
! 111:
! 112: co = control_get_offset(c, wp);
! 113: if (co == NULL) {
! 114: *off = 0;
! 115: return (NULL);
! 116: }
! 117: if (co->flags & CONTROL_OFFSET_OFF) {
! 118: *off = 1;
! 119: return (NULL);
! 120: }
! 121: return (&co->offset);
! 122: }
! 123:
! 124: /* Set pane as on. */
! 125: void
! 126: control_set_pane_on(struct client *c, struct window_pane *wp)
! 127: {
! 128: struct control_offset *co;
! 129:
! 130: co = control_get_offset(c, wp);
! 131: if (co != NULL) {
! 132: co->flags &= ~CONTROL_OFFSET_OFF;
! 133: memcpy(&co->offset, &wp->offset, sizeof co->offset);
! 134: }
! 135: }
! 136:
! 137: /* Set pane as off. */
! 138: void
! 139: control_set_pane_off(struct client *c, struct window_pane *wp)
! 140: {
! 141: struct control_offset *co;
! 142:
! 143: co = control_add_offset(c, wp);
! 144: co->flags |= CONTROL_OFFSET_OFF;
! 145: }
! 146:
1.1 nicm 147: /* Write a line. */
1.13 nicm 148: void
1.1 nicm 149: control_write(struct client *c, const char *fmt, ...)
150: {
1.25 nicm 151: va_list ap;
1.1 nicm 152:
153: va_start(ap, fmt);
1.25 nicm 154: file_vprint(c, fmt, ap);
155: file_print(c, "\n");
1.1 nicm 156: va_end(ap);
1.31 nicm 157: }
158:
159: /* Write output from a pane. */
160: void
161: control_write_output(struct client *c, struct window_pane *wp)
162: {
1.32 ! nicm 163: struct control_offset *co;
1.31 nicm 164: struct evbuffer *message;
165: u_char *new_data;
166: size_t new_size, i;
167:
168: if (c->flags & CLIENT_CONTROL_NOOUTPUT)
169: return;
170:
171: /*
172: * Only write input if the window pane is linked to a window belonging
173: * to the client's session.
174: */
175: if (winlink_find_by_window(&c->session->windows, wp->window) == NULL)
176: return;
177:
1.32 ! nicm 178: co = control_add_offset(c, wp);
! 179: if (co->flags & CONTROL_OFFSET_OFF) {
1.31 nicm 180: window_pane_update_used_data(wp, &co->offset, SIZE_MAX, 1);
181: return;
182: }
183: new_data = window_pane_get_new_data(wp, &co->offset, &new_size);
184: if (new_size == 0)
185: return;
186:
187: message = evbuffer_new();
188: if (message == NULL)
189: fatalx("out of memory");
190: evbuffer_add_printf(message, "%%output %%%u ", wp->id);
191:
192: for (i = 0; i < new_size; i++) {
193: if (new_data[i] < ' ' || new_data[i] == '\\')
194: evbuffer_add_printf(message, "\\%03o", new_data[i]);
195: else
196: evbuffer_add_printf(message, "%c", new_data[i]);
197: }
198: evbuffer_add(message, "", 1);
199:
200: control_write(c, "%s", EVBUFFER_DATA(message));
201: evbuffer_free(message);
202:
203: window_pane_update_used_data(wp, &co->offset, new_size, 1);
1.1 nicm 204: }
205:
1.18 nicm 206: /* Control error callback. */
207: static enum cmd_retval
1.19 nicm 208: control_error(struct cmdq_item *item, void *data)
1.18 nicm 209: {
1.26 nicm 210: struct client *c = cmdq_get_client(item);
1.18 nicm 211: char *error = data;
212:
1.19 nicm 213: cmdq_guard(item, "begin", 1);
1.18 nicm 214: control_write(c, "parse error: %s", error);
1.19 nicm 215: cmdq_guard(item, "error", 1);
1.18 nicm 216:
217: free(error);
218: return (CMD_RETURN_NORMAL);
219: }
220:
1.1 nicm 221: /* Control input callback. Read lines and fire commands. */
1.25 nicm 222: static void
223: control_callback(__unused struct client *c, __unused const char *path,
1.30 nicm 224: int read_error, int closed, struct evbuffer *buffer, __unused void *data)
1.1 nicm 225: {
1.30 nicm 226: char *line, *error;
1.29 nicm 227: struct cmdq_state *state;
1.30 nicm 228: enum cmd_parse_status status;
1.1 nicm 229:
1.30 nicm 230: if (closed || read_error != 0)
1.1 nicm 231: c->flags |= CLIENT_EXIT;
232:
233: for (;;) {
1.25 nicm 234: line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF);
1.1 nicm 235: if (line == NULL)
236: break;
1.25 nicm 237: log_debug("%s: %s", __func__, line);
1.1 nicm 238: if (*line == '\0') { /* empty line exit */
1.24 nicm 239: free(line);
1.1 nicm 240: c->flags |= CLIENT_EXIT;
241: break;
242: }
243:
1.30 nicm 244: state = cmdq_new_state(NULL, NULL, CMDQ_STATE_CONTROL);
245: status = cmd_parse_and_append(line, NULL, c, state, &error);
246: if (status == CMD_PARSE_ERROR)
247: cmdq_append(c, cmdq_get_callback(control_error, error));
248: cmdq_free_state(state);
1.1 nicm 249:
1.2 nicm 250: free(line);
1.1 nicm 251: }
1.25 nicm 252: }
253:
1.32 ! nicm 254: /* Initialize for control mode. */
1.25 nicm 255: void
256: control_start(struct client *c)
257: {
258: file_read(c, "-", control_callback, c);
259:
260: if (c->flags & CLIENT_CONTROLCONTROL)
261: file_print(c, "\033P1000p");
1.1 nicm 262: }