Annotation of src/usr.bin/tmux/control.c, Revision 1.38
1.38 ! nicm 1: /* $OpenBSD: control.c,v 1.37 2020/06/02 08:17:27 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.35 nicm 30: /*
31: * Block of data to output. Each client has one "all" queue of blocks and
32: * another queue for each pane (in struct client_offset). %output blocks are
33: * added to both queues and other output lines (notifications) added only to
34: * the client queue.
35: *
36: * When a client becomes writeable, data from blocks on the pane queue are sent
37: * up to the maximum size (CLIENT_BUFFER_HIGH). If a block is entirely written,
38: * it is removed from both pane and client queues and if this means non-%output
39: * blocks are now at the head of the client queue, they are written.
40: *
41: * This means a %output block holds up any subsequent non-%output blocks until
42: * it is written which enforces ordering even if the client cannot accept the
43: * entire block in one go.
44: */
45: struct control_block {
46: size_t size;
47: char *line;
1.37 nicm 48: uint64_t t;
1.35 nicm 49:
50: TAILQ_ENTRY(control_block) entry;
51: TAILQ_ENTRY(control_block) all_entry;
1.36 nicm 52: };
1.35 nicm 53:
54: /* Control client pane. */
55: struct control_pane {
56: u_int pane;
57:
58: /*
59: * Offsets into the pane data. The first (offset) is the data we have
60: * written; the second (queued) the data we have queued (pointed to by
61: * a block).
62: */
63: struct window_pane_offset offset;
64: struct window_pane_offset queued;
65:
66: int flags;
67: #define CONTROL_PANE_OFF 0x1
1.38 ! nicm 68: #define CONTROL_PANE_PAUSED 0x2
1.32 nicm 69:
1.35 nicm 70: int pending_flag;
71: TAILQ_ENTRY(control_pane) pending_entry;
72:
73: TAILQ_HEAD(, control_block) blocks;
74:
75: RB_ENTRY(control_pane) entry;
1.32 nicm 76: };
1.35 nicm 77: RB_HEAD(control_panes, control_pane);
1.32 nicm 78:
1.34 nicm 79: /* Control client state. */
1.33 nicm 80: struct control_state {
1.35 nicm 81: struct control_panes panes;
82:
83: TAILQ_HEAD(, control_pane) pending_list;
84: u_int pending_count;
85:
86: TAILQ_HEAD(, control_block) all_blocks;
1.34 nicm 87:
1.35 nicm 88: struct bufferevent *read_event;
89: struct bufferevent *write_event;
1.33 nicm 90: };
91:
1.35 nicm 92: /* Low watermark. */
93: #define CONTROL_BUFFER_LOW 512
94: #define CONTROL_BUFFER_HIGH 8192
95:
96: /* Minimum to write to each client. */
97: #define CONTROL_WRITE_MINIMUM 32
98:
99: /* Flags to ignore client. */
100: #define CONTROL_IGNORE_FLAGS \
101: (CLIENT_CONTROL_NOOUTPUT| \
102: CLIENT_UNATTACHEDFLAGS)
103:
104: /* Compare client panes. */
1.32 nicm 105: static int
1.35 nicm 106: control_pane_cmp(struct control_pane *cp1, struct control_pane *cp2)
1.32 nicm 107: {
1.35 nicm 108: if (cp1->pane < cp2->pane)
1.32 nicm 109: return (-1);
1.35 nicm 110: if (cp1->pane > cp2->pane)
1.32 nicm 111: return (1);
112: return (0);
113: }
1.35 nicm 114: RB_GENERATE_STATIC(control_panes, control_pane, entry, control_pane_cmp);
115:
116: /* Free a block. */
117: static void
118: control_free_block(struct control_state *cs, struct control_block *cb)
119: {
120: free(cb->line);
121: TAILQ_REMOVE(&cs->all_blocks, cb, all_entry);
122: free(cb);
123: }
1.32 nicm 124:
125: /* Get pane offsets for this client. */
1.35 nicm 126: static struct control_pane *
127: control_get_pane(struct client *c, struct window_pane *wp)
1.32 nicm 128: {
1.33 nicm 129: struct control_state *cs = c->control_state;
1.35 nicm 130: struct control_pane cp = { .pane = wp->id };
1.32 nicm 131:
1.35 nicm 132: return (RB_FIND(control_panes, &cs->panes, &cp));
1.32 nicm 133: }
134:
135: /* Add pane offsets for this client. */
1.35 nicm 136: static struct control_pane *
137: control_add_pane(struct client *c, struct window_pane *wp)
1.32 nicm 138: {
1.33 nicm 139: struct control_state *cs = c->control_state;
1.35 nicm 140: struct control_pane *cp;
141:
142: cp = control_get_pane(c, wp);
143: if (cp != NULL)
144: return (cp);
1.32 nicm 145:
1.35 nicm 146: cp = xcalloc(1, sizeof *cp);
147: cp->pane = wp->id;
148: RB_INSERT(control_panes, &cs->panes, cp);
1.32 nicm 149:
1.35 nicm 150: memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
151: memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
152: TAILQ_INIT(&cp->blocks);
153:
154: return (cp);
1.32 nicm 155: }
156:
1.38 ! nicm 157: /* Discard output for a pane. */
! 158: static void
! 159: control_discard_pane(struct client *c, struct control_pane *cp)
! 160: {
! 161: struct control_state *cs = c->control_state;
! 162: struct control_block *cb, *cb1;
! 163:
! 164: TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) {
! 165: TAILQ_REMOVE(&cp->blocks, cb, entry);
! 166: control_free_block(cs, cb);
! 167: }
! 168: }
! 169:
1.37 nicm 170: /* Get actual pane for this client. */
171: static struct window_pane *
172: control_window_pane(struct client *c, u_int pane)
173: {
174: struct window_pane *wp;
175:
176: if (c->session == NULL)
177: return (NULL);
178: if ((wp = window_pane_find_by_id(pane)) == NULL)
179: return (NULL);
180: if (winlink_find_by_window(&c->session->windows, wp->window) == NULL)
181: return (NULL);
182: return (wp);
183: }
184:
1.35 nicm 185: /* Reset control offsets. */
1.32 nicm 186: void
1.35 nicm 187: control_reset_offsets(struct client *c)
1.32 nicm 188: {
1.33 nicm 189: struct control_state *cs = c->control_state;
1.35 nicm 190: struct control_pane *cp, *cp1;
1.32 nicm 191:
1.35 nicm 192: RB_FOREACH_SAFE(cp, control_panes, &cs->panes, cp1) {
193: RB_REMOVE(control_panes, &cs->panes, cp);
194: free(cp);
1.32 nicm 195: }
1.35 nicm 196:
197: TAILQ_INIT(&cs->pending_list);
198: cs->pending_count = 0;
1.32 nicm 199: }
200:
201: /* Get offsets for client. */
202: struct window_pane_offset *
203: control_pane_offset(struct client *c, struct window_pane *wp, int *off)
204: {
1.35 nicm 205: struct control_state *cs = c->control_state;
206: struct control_pane *cp;
1.32 nicm 207:
208: if (c->flags & CLIENT_CONTROL_NOOUTPUT) {
209: *off = 0;
210: return (NULL);
211: }
212:
1.35 nicm 213: cp = control_get_pane(c, wp);
1.38 ! nicm 214: if (cp == NULL || (cp->flags & CONTROL_PANE_PAUSED)) {
1.32 nicm 215: *off = 0;
216: return (NULL);
217: }
1.35 nicm 218: if (cp->flags & CONTROL_PANE_OFF) {
1.32 nicm 219: *off = 1;
220: return (NULL);
221: }
1.35 nicm 222: *off = (EVBUFFER_LENGTH(cs->write_event->output) >= CONTROL_BUFFER_LOW);
223: return (&cp->offset);
1.32 nicm 224: }
225:
226: /* Set pane as on. */
227: void
228: control_set_pane_on(struct client *c, struct window_pane *wp)
229: {
1.35 nicm 230: struct control_pane *cp;
1.32 nicm 231:
1.35 nicm 232: cp = control_get_pane(c, wp);
1.38 ! nicm 233: if (cp != NULL && (cp->flags & CONTROL_PANE_OFF)) {
1.35 nicm 234: cp->flags &= ~CONTROL_PANE_OFF;
235: memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
236: memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
1.32 nicm 237: }
238: }
239:
240: /* Set pane as off. */
241: void
242: control_set_pane_off(struct client *c, struct window_pane *wp)
243: {
1.35 nicm 244: struct control_pane *cp;
1.32 nicm 245:
1.35 nicm 246: cp = control_add_pane(c, wp);
247: cp->flags |= CONTROL_PANE_OFF;
1.32 nicm 248: }
249:
1.38 ! nicm 250: /* Continue a paused pane. */
! 251: void
! 252: control_continue_pane(struct client *c, struct window_pane *wp)
! 253: {
! 254: struct control_pane *cp;
! 255:
! 256: cp = control_get_pane(c, wp);
! 257: if (cp != NULL && (cp->flags & CONTROL_PANE_PAUSED)) {
! 258: cp->flags &= ~CONTROL_PANE_PAUSED;
! 259: memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
! 260: memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
! 261: control_write(c, "%%continue %%%u", wp->id);
! 262: }
! 263: }
! 264:
1.1 nicm 265: /* Write a line. */
1.35 nicm 266: static void
267: control_vwrite(struct client *c, const char *fmt, va_list ap)
1.1 nicm 268: {
1.34 nicm 269: struct control_state *cs = c->control_state;
270: char *s;
1.1 nicm 271:
1.34 nicm 272: xvasprintf(&s, fmt, ap);
1.35 nicm 273: log_debug("%s: %s: writing line: %s", __func__, c->name, s);
1.34 nicm 274:
275: bufferevent_write(cs->write_event, s, strlen(s));
276: bufferevent_write(cs->write_event, "\n", 1);
1.35 nicm 277:
278: bufferevent_enable(cs->write_event, EV_WRITE);
1.34 nicm 279: free(s);
1.31 nicm 280: }
281:
1.35 nicm 282: /* Write a line. */
283: void
284: control_write(struct client *c, const char *fmt, ...)
285: {
286: struct control_state *cs = c->control_state;
287: struct control_block *cb;
288: va_list ap;
289:
290: va_start(ap, fmt);
291:
292: if (TAILQ_EMPTY(&cs->all_blocks)) {
293: control_vwrite(c, fmt, ap);
294: va_end(ap);
295: return;
296: }
297:
298: cb = xcalloc(1, sizeof *cb);
299: xvasprintf(&cb->line, fmt, ap);
300: TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
1.37 nicm 301: cb->t = get_timer();
1.35 nicm 302:
303: log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line);
304: bufferevent_enable(cs->write_event, EV_WRITE);
305:
306: va_end(ap);
307: }
308:
1.31 nicm 309: /* Write output from a pane. */
310: void
311: control_write_output(struct client *c, struct window_pane *wp)
312: {
1.34 nicm 313: struct control_state *cs = c->control_state;
1.35 nicm 314: struct control_pane *cp;
315: struct control_block *cb;
316: size_t new_size;
1.38 ! nicm 317: uint64_t t;
1.31 nicm 318:
319: if (winlink_find_by_window(&c->session->windows, wp->window) == NULL)
320: return;
321:
1.35 nicm 322: if (c->flags & CONTROL_IGNORE_FLAGS) {
323: cp = control_get_pane(c, wp);
324: if (cp != NULL)
325: goto ignore;
1.31 nicm 326: return;
327: }
1.35 nicm 328: cp = control_add_pane(c, wp);
1.38 ! nicm 329: if (cp->flags & (CONTROL_PANE_OFF|CONTROL_PANE_PAUSED))
1.35 nicm 330: goto ignore;
1.38 ! nicm 331: if (c->flags & CLIENT_CONTROL_PAUSEAFTER) {
! 332: cb = TAILQ_FIRST(&cp->blocks);
! 333: if (cb != NULL) {
! 334: t = get_timer();
! 335: log_debug("%s: %s: %%%u is %lld behind", __func__,
! 336: c->name, wp->id, (long long)t - cb->t);
! 337: if (cb->t < t - c->pause_age) {
! 338: cp->flags |= CONTROL_PANE_PAUSED;
! 339: control_discard_pane(c, cp);
! 340: control_write(c, "%%pause %%%u", wp->id);
! 341: return;
! 342: }
! 343: }
! 344: }
1.35 nicm 345:
346: window_pane_get_new_data(wp, &cp->queued, &new_size);
1.31 nicm 347: if (new_size == 0)
348: return;
1.35 nicm 349: window_pane_update_used_data(wp, &cp->queued, new_size);
1.31 nicm 350:
1.35 nicm 351: cb = xcalloc(1, sizeof *cb);
352: cb->size = new_size;
353: TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
1.37 nicm 354: cb->t = get_timer();
1.35 nicm 355:
356: TAILQ_INSERT_TAIL(&cp->blocks, cb, entry);
357: log_debug("%s: %s: new output block of %zu for %%%u", __func__, c->name,
358: cb->size, wp->id);
359:
360: if (!cp->pending_flag) {
361: log_debug("%s: %s: %%%u now pending", __func__, c->name,
362: wp->id);
363: TAILQ_INSERT_TAIL(&cs->pending_list, cp, pending_entry);
364: cp->pending_flag = 1;
365: cs->pending_count++;
1.31 nicm 366: }
1.35 nicm 367: bufferevent_enable(cs->write_event, EV_WRITE);
368: return;
1.31 nicm 369:
1.35 nicm 370: ignore:
371: log_debug("%s: %s: ignoring pane %%%u", __func__, c->name, wp->id);
372: window_pane_update_used_data(wp, &cp->offset, SIZE_MAX);
373: window_pane_update_used_data(wp, &cp->queued, SIZE_MAX);
1.1 nicm 374: }
375:
1.34 nicm 376: /* Control client error callback. */
1.18 nicm 377: static enum cmd_retval
1.19 nicm 378: control_error(struct cmdq_item *item, void *data)
1.18 nicm 379: {
1.26 nicm 380: struct client *c = cmdq_get_client(item);
1.18 nicm 381: char *error = data;
382:
1.19 nicm 383: cmdq_guard(item, "begin", 1);
1.18 nicm 384: control_write(c, "parse error: %s", error);
1.19 nicm 385: cmdq_guard(item, "error", 1);
1.18 nicm 386:
387: free(error);
388: return (CMD_RETURN_NORMAL);
389: }
390:
1.34 nicm 391: /* Control client error callback. */
1.25 nicm 392: static void
1.34 nicm 393: control_error_callback(__unused struct bufferevent *bufev,
394: __unused short what, void *data)
1.1 nicm 395: {
1.34 nicm 396: struct client *c = data;
397:
398: c->flags |= CLIENT_EXIT;
399: }
400:
401: /* Control client input callback. Read lines and fire commands. */
402: static void
403: control_read_callback(__unused struct bufferevent *bufev, void *data)
404: {
405: struct client *c = data;
406: struct control_state *cs = c->control_state;
407: struct evbuffer *buffer = cs->read_event->input;
1.30 nicm 408: char *line, *error;
1.29 nicm 409: struct cmdq_state *state;
1.30 nicm 410: enum cmd_parse_status status;
1.1 nicm 411:
412: for (;;) {
1.25 nicm 413: line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF);
1.1 nicm 414: if (line == NULL)
415: break;
1.35 nicm 416: log_debug("%s: %s: %s", __func__, c->name, line);
417: if (*line == '\0') { /* empty line detach */
1.24 nicm 418: free(line);
1.1 nicm 419: c->flags |= CLIENT_EXIT;
420: break;
421: }
422:
1.30 nicm 423: state = cmdq_new_state(NULL, NULL, CMDQ_STATE_CONTROL);
424: status = cmd_parse_and_append(line, NULL, c, state, &error);
425: if (status == CMD_PARSE_ERROR)
426: cmdq_append(c, cmdq_get_callback(control_error, error));
427: cmdq_free_state(state);
1.1 nicm 428:
1.2 nicm 429: free(line);
1.1 nicm 430: }
1.25 nicm 431: }
432:
1.35 nicm 433: /* Does this control client have outstanding data to write? */
434: int
435: control_all_done(struct client *c)
436: {
437: struct control_state *cs = c->control_state;
438:
439: if (!TAILQ_EMPTY(&cs->all_blocks))
440: return (0);
441: return (EVBUFFER_LENGTH(cs->write_event->output) == 0);
442: }
443:
444: /* Flush all blocks until output. */
445: static void
446: control_flush_all_blocks(struct client *c)
447: {
448: struct control_state *cs = c->control_state;
449: struct control_block *cb, *cb1;
450:
451: TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1) {
452: if (cb->size != 0)
453: break;
454: log_debug("%s: %s: flushing line: %s", __func__, c->name,
455: cb->line);
456:
457: bufferevent_write(cs->write_event, cb->line, strlen(cb->line));
458: bufferevent_write(cs->write_event, "\n", 1);
459: control_free_block(cs, cb);
460: }
461: }
462:
463: /* Append data to buffer. */
464: static struct evbuffer *
465: control_append_data(struct control_pane *cp, struct evbuffer *message,
466: struct window_pane *wp, size_t size)
467: {
468: u_char *new_data;
469: size_t new_size;
470: u_int i;
471:
472: if (message == NULL) {
473: message = evbuffer_new();
474: if (message == NULL)
475: fatalx("out of memory");
476: evbuffer_add_printf(message, "%%output %%%u ", wp->id);
477: }
478:
479: new_data = window_pane_get_new_data(wp, &cp->offset, &new_size);
480: if (new_size < size)
481: fatalx("not enough data: %zu < %zu", new_size, size);
482: for (i = 0; i < size; i++) {
483: if (new_data[i] < ' ' || new_data[i] == '\\')
484: evbuffer_add_printf(message, "\\%03o", new_data[i]);
485: else
486: evbuffer_add_printf(message, "%c", new_data[i]);
487: }
488: window_pane_update_used_data(wp, &cp->offset, size);
489: return (message);
490: }
491:
492: /* Write buffer. */
493: static void
494: control_write_data(struct client *c, struct evbuffer *message)
495: {
496: struct control_state *cs = c->control_state;
497:
498: log_debug("%s: %s: %.*s", __func__, c->name,
499: (int)EVBUFFER_LENGTH(message), EVBUFFER_DATA(message));
500:
501: evbuffer_add(message, "\n", 1);
502: bufferevent_write_buffer(cs->write_event, message);
503: evbuffer_free(message);
504: }
505:
506: /* Write output to client. */
507: static int
508: control_write_pending(struct client *c, struct control_pane *cp, size_t limit)
509: {
510: struct control_state *cs = c->control_state;
511: struct window_pane *wp = NULL;
512: struct evbuffer *message = NULL;
513: size_t used = 0, size;
514: struct control_block *cb, *cb1;
515:
1.37 nicm 516: wp = control_window_pane(c, cp->pane);
517: if (wp == NULL) {
1.36 nicm 518: TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) {
519: TAILQ_REMOVE(&cp->blocks, cb, entry);
1.35 nicm 520: control_free_block(cs, cb);
1.36 nicm 521: }
1.35 nicm 522: control_flush_all_blocks(c);
523: return (0);
524: }
525:
526: while (used != limit && !TAILQ_EMPTY(&cp->blocks)) {
527: cb = TAILQ_FIRST(&cp->blocks);
528: log_debug("%s: %s: output block %zu for %%%u (used %zu/%zu)",
529: __func__, c->name, cb->size, cp->pane, used, limit);
530:
531: size = cb->size;
532: if (size > limit - used)
533: size = limit - used;
534: used += size;
535:
536: message = control_append_data(cp, message, wp, size);
537:
538: cb->size -= size;
539: if (cb->size == 0) {
540: TAILQ_REMOVE(&cp->blocks, cb, entry);
541: control_free_block(cs, cb);
542:
543: cb = TAILQ_FIRST(&cs->all_blocks);
544: if (cb != NULL && cb->size == 0) {
545: if (wp != NULL && message != NULL) {
546: control_write_data(c, message);
547: message = NULL;
548: }
549: control_flush_all_blocks(c);
550: }
551: }
552: }
553: if (message != NULL)
554: control_write_data(c, message);
555: return (!TAILQ_EMPTY(&cp->blocks));
556: }
557:
558: /* Control client write callback. */
559: static void
560: control_write_callback(__unused struct bufferevent *bufev, void *data)
561: {
562: struct client *c = data;
563: struct control_state *cs = c->control_state;
564: struct control_pane *cp, *cp1;
565: struct evbuffer *evb = cs->write_event->output;
566: size_t space, limit;
567:
568: control_flush_all_blocks(c);
569:
570: while (EVBUFFER_LENGTH(evb) < CONTROL_BUFFER_HIGH) {
571: if (cs->pending_count == 0)
572: break;
573: space = CONTROL_BUFFER_HIGH - EVBUFFER_LENGTH(evb);
574: log_debug("%s: %s: %zu bytes available, %u panes", __func__,
575: c->name, space, cs->pending_count);
576:
577: limit = (space / cs->pending_count / 3); /* 3 bytes for \xxx */
578: if (limit < CONTROL_WRITE_MINIMUM)
579: limit = CONTROL_WRITE_MINIMUM;
580:
581: TAILQ_FOREACH_SAFE(cp, &cs->pending_list, pending_entry, cp1) {
582: if (EVBUFFER_LENGTH(evb) >= CONTROL_BUFFER_HIGH)
583: break;
584: if (control_write_pending(c, cp, limit))
585: continue;
586: TAILQ_REMOVE(&cs->pending_list, cp, pending_entry);
587: cp->pending_flag = 0;
588: cs->pending_count--;
589: }
590: }
591: if (EVBUFFER_LENGTH(evb) == 0)
592: bufferevent_disable(cs->write_event, EV_WRITE);
593: }
594:
1.32 nicm 595: /* Initialize for control mode. */
1.25 nicm 596: void
597: control_start(struct client *c)
598: {
1.33 nicm 599: struct control_state *cs;
600:
1.34 nicm 601: if (c->flags & CLIENT_CONTROLCONTROL) {
602: close(c->out_fd);
603: c->out_fd = -1;
604: } else
605: setblocking(c->out_fd, 0);
606: setblocking(c->fd, 0);
607:
1.33 nicm 608: cs = c->control_state = xcalloc(1, sizeof *cs);
1.35 nicm 609: RB_INIT(&cs->panes);
610: TAILQ_INIT(&cs->pending_list);
611: TAILQ_INIT(&cs->all_blocks);
1.33 nicm 612:
1.35 nicm 613: cs->read_event = bufferevent_new(c->fd, control_read_callback,
614: control_write_callback, control_error_callback, c);
1.34 nicm 615: bufferevent_enable(cs->read_event, EV_READ);
1.25 nicm 616:
617: if (c->flags & CLIENT_CONTROLCONTROL)
1.34 nicm 618: cs->write_event = cs->read_event;
619: else {
1.35 nicm 620: cs->write_event = bufferevent_new(c->out_fd, NULL,
621: control_write_callback, control_error_callback, c);
622: }
623: bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW,
624: 0);
625:
626: if (c->flags & CLIENT_CONTROLCONTROL) {
627: bufferevent_write(cs->write_event, "\033P1000p", 7);
628: bufferevent_enable(cs->write_event, EV_WRITE);
1.34 nicm 629: }
1.35 nicm 630: }
631:
1.38 ! nicm 632: /* Discard all output for a client. */
1.35 nicm 633: void
1.38 ! nicm 634: control_discard(struct client *c)
1.35 nicm 635: {
636: struct control_state *cs = c->control_state;
637: struct control_pane *cp;
1.34 nicm 638:
1.38 ! nicm 639: RB_FOREACH(cp, control_panes, &cs->panes)
! 640: control_discard_pane(c, cp);
1.33 nicm 641: }
642:
643: /* Stop control mode. */
644: void
645: control_stop(struct client *c)
646: {
647: struct control_state *cs = c->control_state;
1.35 nicm 648: struct control_block *cb, *cb1;
1.34 nicm 649:
650: if (~c->flags & CLIENT_CONTROLCONTROL)
651: bufferevent_free(cs->write_event);
652: bufferevent_free(cs->read_event);
1.33 nicm 653:
1.35 nicm 654: TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
655: control_free_block(cs, cb);
656: control_reset_offsets(c);
657:
1.33 nicm 658: free(cs);
1.1 nicm 659: }