[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.35

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