[BACK]Return to control.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

Annotation of src/usr.bin/tmux/control.c, Revision 1.37

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