Annotation of src/usr.bin/tmux/control.c, Revision 1.34
1.34 ! nicm 1: /* $OpenBSD: control.c,v 1.33 2020/05/24 09:40:17 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.34 ! nicm 26: #include <unistd.h>
1.1 nicm 27:
28: #include "tmux.h"
29:
1.34 ! nicm 30: /* Control client offset. */
1.32 nicm 31: struct control_offset {
32: u_int pane;
33:
34: struct window_pane_offset offset;
35: int flags;
36: #define CONTROL_OFFSET_OFF 0x1
37:
1.34 ! nicm 38: RB_ENTRY(control_offset) entry;
1.32 nicm 39: };
40: RB_HEAD(control_offsets, control_offset);
41:
1.34 ! nicm 42: /* Control client state. */
1.33 nicm 43: struct control_state {
1.34 ! nicm 44: struct control_offsets offsets;
! 45:
! 46: struct bufferevent *read_event;
! 47: struct bufferevent *write_event;
1.33 nicm 48: };
49:
1.32 nicm 50: /* Compare client offsets. */
51: static int
52: control_offset_cmp(struct control_offset *co1, struct control_offset *co2)
53: {
54: if (co1->pane < co2->pane)
55: return (-1);
56: if (co1->pane > co2->pane)
57: return (1);
58: return (0);
59: }
60: RB_GENERATE_STATIC(control_offsets, control_offset, entry, control_offset_cmp);
61:
62: /* Get pane offsets for this client. */
63: static struct control_offset *
64: control_get_offset(struct client *c, struct window_pane *wp)
65: {
1.33 nicm 66: struct control_state *cs = c->control_state;
67: struct control_offset co = { .pane = wp->id };
1.32 nicm 68:
1.33 nicm 69: return (RB_FIND(control_offsets, &cs->offsets, &co));
1.32 nicm 70: }
71:
72: /* Add pane offsets for this client. */
73: static struct control_offset *
74: control_add_offset(struct client *c, struct window_pane *wp)
75: {
1.33 nicm 76: struct control_state *cs = c->control_state;
1.32 nicm 77: struct control_offset *co;
78:
79: co = control_get_offset(c, wp);
80: if (co != NULL)
81: return (co);
82:
83: co = xcalloc(1, sizeof *co);
84: co->pane = wp->id;
1.33 nicm 85: RB_INSERT(control_offsets, &cs->offsets, co);
1.32 nicm 86: memcpy(&co->offset, &wp->offset, sizeof co->offset);
87: return (co);
88: }
89:
90: /* Free control offsets. */
91: void
92: control_free_offsets(struct client *c)
93: {
1.33 nicm 94: struct control_state *cs = c->control_state;
1.32 nicm 95: struct control_offset *co, *co1;
96:
1.33 nicm 97: RB_FOREACH_SAFE(co, control_offsets, &cs->offsets, co1) {
98: RB_REMOVE(control_offsets, &cs->offsets, co);
1.32 nicm 99: free(co);
100: }
101: }
102:
103: /* Get offsets for client. */
104: struct window_pane_offset *
105: control_pane_offset(struct client *c, struct window_pane *wp, int *off)
106: {
107: struct control_offset *co;
108:
109: if (c->flags & CLIENT_CONTROL_NOOUTPUT) {
110: *off = 0;
111: return (NULL);
112: }
113:
114: co = control_get_offset(c, wp);
115: if (co == NULL) {
116: *off = 0;
117: return (NULL);
118: }
119: if (co->flags & CONTROL_OFFSET_OFF) {
120: *off = 1;
121: return (NULL);
122: }
123: return (&co->offset);
124: }
125:
126: /* Set pane as on. */
127: void
128: control_set_pane_on(struct client *c, struct window_pane *wp)
129: {
130: struct control_offset *co;
131:
132: co = control_get_offset(c, wp);
133: if (co != NULL) {
134: co->flags &= ~CONTROL_OFFSET_OFF;
135: memcpy(&co->offset, &wp->offset, sizeof co->offset);
136: }
137: }
138:
139: /* Set pane as off. */
140: void
141: control_set_pane_off(struct client *c, struct window_pane *wp)
142: {
143: struct control_offset *co;
144:
145: co = control_add_offset(c, wp);
146: co->flags |= CONTROL_OFFSET_OFF;
147: }
148:
1.1 nicm 149: /* Write a line. */
1.13 nicm 150: void
1.1 nicm 151: control_write(struct client *c, const char *fmt, ...)
152: {
1.34 ! nicm 153: struct control_state *cs = c->control_state;
! 154: va_list ap;
! 155: char *s;
1.1 nicm 156:
157: va_start(ap, fmt);
1.34 ! nicm 158: xvasprintf(&s, fmt, ap);
1.1 nicm 159: va_end(ap);
1.34 ! nicm 160:
! 161: bufferevent_write(cs->write_event, s, strlen(s));
! 162: bufferevent_write(cs->write_event, "\n", 1);
! 163: free(s);
1.31 nicm 164: }
165:
166: /* Write output from a pane. */
167: void
168: control_write_output(struct client *c, struct window_pane *wp)
169: {
1.34 ! nicm 170: struct control_state *cs = c->control_state;
1.32 nicm 171: struct control_offset *co;
1.31 nicm 172: struct evbuffer *message;
173: u_char *new_data;
174: size_t new_size, i;
175:
176: if (c->flags & CLIENT_CONTROL_NOOUTPUT)
177: return;
178: if (winlink_find_by_window(&c->session->windows, wp->window) == NULL)
179: return;
180:
1.32 nicm 181: co = control_add_offset(c, wp);
182: if (co->flags & CONTROL_OFFSET_OFF) {
1.31 nicm 183: window_pane_update_used_data(wp, &co->offset, SIZE_MAX, 1);
184: return;
185: }
186: new_data = window_pane_get_new_data(wp, &co->offset, &new_size);
187: if (new_size == 0)
188: return;
189:
190: message = evbuffer_new();
191: if (message == NULL)
192: fatalx("out of memory");
193: evbuffer_add_printf(message, "%%output %%%u ", wp->id);
194:
195: for (i = 0; i < new_size; i++) {
196: if (new_data[i] < ' ' || new_data[i] == '\\')
197: evbuffer_add_printf(message, "\\%03o", new_data[i]);
198: else
199: evbuffer_add_printf(message, "%c", new_data[i]);
200: }
1.34 ! nicm 201: evbuffer_add(message, "\n", 1);
1.31 nicm 202:
1.34 ! nicm 203: bufferevent_write_buffer(cs->write_event, message);
1.31 nicm 204: evbuffer_free(message);
205:
206: window_pane_update_used_data(wp, &co->offset, new_size, 1);
1.1 nicm 207: }
208:
1.34 ! nicm 209: /* Control client error callback. */
1.18 nicm 210: static enum cmd_retval
1.19 nicm 211: control_error(struct cmdq_item *item, void *data)
1.18 nicm 212: {
1.26 nicm 213: struct client *c = cmdq_get_client(item);
1.18 nicm 214: char *error = data;
215:
1.19 nicm 216: cmdq_guard(item, "begin", 1);
1.18 nicm 217: control_write(c, "parse error: %s", error);
1.19 nicm 218: cmdq_guard(item, "error", 1);
1.18 nicm 219:
220: free(error);
221: return (CMD_RETURN_NORMAL);
222: }
223:
1.34 ! nicm 224: /* Control client error callback. */
1.25 nicm 225: static void
1.34 ! nicm 226: control_error_callback(__unused struct bufferevent *bufev,
! 227: __unused short what, void *data)
1.1 nicm 228: {
1.34 ! nicm 229: struct client *c = data;
! 230:
! 231: c->flags |= CLIENT_EXIT;
! 232: }
! 233:
! 234: /* Control client input callback. Read lines and fire commands. */
! 235: static void
! 236: control_read_callback(__unused struct bufferevent *bufev, void *data)
! 237: {
! 238: struct client *c = data;
! 239: struct control_state *cs = c->control_state;
! 240: struct evbuffer *buffer = cs->read_event->input;
1.30 nicm 241: char *line, *error;
1.29 nicm 242: struct cmdq_state *state;
1.30 nicm 243: enum cmd_parse_status status;
1.1 nicm 244:
245: for (;;) {
1.25 nicm 246: line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF);
1.1 nicm 247: if (line == NULL)
248: break;
1.25 nicm 249: log_debug("%s: %s", __func__, line);
1.1 nicm 250: if (*line == '\0') { /* empty line exit */
1.24 nicm 251: free(line);
1.1 nicm 252: c->flags |= CLIENT_EXIT;
253: break;
254: }
255:
1.30 nicm 256: state = cmdq_new_state(NULL, NULL, CMDQ_STATE_CONTROL);
257: status = cmd_parse_and_append(line, NULL, c, state, &error);
258: if (status == CMD_PARSE_ERROR)
259: cmdq_append(c, cmdq_get_callback(control_error, error));
260: cmdq_free_state(state);
1.1 nicm 261:
1.2 nicm 262: free(line);
1.1 nicm 263: }
1.25 nicm 264: }
265:
1.32 nicm 266: /* Initialize for control mode. */
1.25 nicm 267: void
268: control_start(struct client *c)
269: {
1.33 nicm 270: struct control_state *cs;
271:
1.34 ! nicm 272: if (c->flags & CLIENT_CONTROLCONTROL) {
! 273: close(c->out_fd);
! 274: c->out_fd = -1;
! 275: } else
! 276: setblocking(c->out_fd, 0);
! 277: setblocking(c->fd, 0);
! 278:
1.33 nicm 279: cs = c->control_state = xcalloc(1, sizeof *cs);
280: RB_INIT(&cs->offsets);
281:
1.34 ! nicm 282: cs->read_event = bufferevent_new(c->fd, control_read_callback, NULL,
! 283: control_error_callback, c);
! 284: bufferevent_enable(cs->read_event, EV_READ);
1.25 nicm 285:
286: if (c->flags & CLIENT_CONTROLCONTROL)
1.34 ! nicm 287: cs->write_event = cs->read_event;
! 288: else {
! 289: cs->write_event = bufferevent_new(c->out_fd, NULL, NULL,
! 290: control_error_callback, c);
! 291: }
! 292: bufferevent_enable(cs->write_event, EV_WRITE);
! 293:
! 294: if (c->flags & CLIENT_CONTROLCONTROL)
! 295: control_write(c, "\033P1000p");
1.33 nicm 296: }
297:
298: /* Stop control mode. */
299: void
300: control_stop(struct client *c)
301: {
302: struct control_state *cs = c->control_state;
1.34 ! nicm 303:
! 304: if (~c->flags & CLIENT_CONTROLCONTROL)
! 305: bufferevent_free(cs->write_event);
! 306: bufferevent_free(cs->read_event);
1.33 nicm 307:
308: control_free_offsets(c);
309: free(cs);
1.1 nicm 310: }