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