1 1.2 christos /* $OpenBSD$ */ 2 1.1 christos 3 1.1 christos /* 4 1.2 christos * Copyright (c) 2012 Nicholas Marriott <nicholas.marriott (at) gmail.com> 5 1.1 christos * Copyright (c) 2012 George Nachman <tmux (at) georgester.com> 6 1.1 christos * 7 1.1 christos * Permission to use, copy, modify, and distribute this software for any 8 1.1 christos * purpose with or without fee is hereby granted, provided that the above 9 1.1 christos * copyright notice and this permission notice appear in all copies. 10 1.1 christos * 11 1.1 christos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 1.1 christos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 1.1 christos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 1.1 christos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 1.1 christos * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 16 1.1 christos * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 17 1.1 christos * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 1.1 christos */ 19 1.1 christos 20 1.1 christos #include <sys/types.h> 21 1.1 christos 22 1.1 christos #include <stdlib.h> 23 1.1 christos #include <string.h> 24 1.1 christos #include <time.h> 25 1.2 christos #include <unistd.h> 26 1.1 christos 27 1.1 christos #include "tmux.h" 28 1.1 christos 29 1.2 christos /* 30 1.2 christos * Block of data to output. Each client has one "all" queue of blocks and 31 1.2 christos * another queue for each pane (in struct client_offset). %output blocks are 32 1.2 christos * added to both queues and other output lines (notifications) added only to 33 1.2 christos * the client queue. 34 1.2 christos * 35 1.2 christos * When a client becomes writeable, data from blocks on the pane queue are sent 36 1.2 christos * up to the maximum size (CLIENT_BUFFER_HIGH). If a block is entirely written, 37 1.2 christos * it is removed from both pane and client queues and if this means non-%output 38 1.2 christos * blocks are now at the head of the client queue, they are written. 39 1.2 christos * 40 1.2 christos * This means a %output block holds up any subsequent non-%output blocks until 41 1.2 christos * it is written which enforces ordering even if the client cannot accept the 42 1.2 christos * entire block in one go. 43 1.2 christos */ 44 1.2 christos struct control_block { 45 1.2 christos size_t size; 46 1.2 christos char *line; 47 1.2 christos uint64_t t; 48 1.2 christos 49 1.2 christos TAILQ_ENTRY(control_block) entry; 50 1.2 christos TAILQ_ENTRY(control_block) all_entry; 51 1.2 christos }; 52 1.2 christos 53 1.2 christos /* Control client pane. */ 54 1.2 christos struct control_pane { 55 1.2 christos u_int pane; 56 1.2 christos 57 1.2 christos /* 58 1.2 christos * Offsets into the pane data. The first (offset) is the data we have 59 1.2 christos * written; the second (queued) the data we have queued (pointed to by 60 1.2 christos * a block). 61 1.2 christos */ 62 1.2 christos struct window_pane_offset offset; 63 1.2 christos struct window_pane_offset queued; 64 1.2 christos 65 1.2 christos int flags; 66 1.2 christos #define CONTROL_PANE_OFF 0x1 67 1.2 christos #define CONTROL_PANE_PAUSED 0x2 68 1.2 christos 69 1.2 christos int pending_flag; 70 1.2 christos TAILQ_ENTRY(control_pane) pending_entry; 71 1.2 christos 72 1.2 christos TAILQ_HEAD(, control_block) blocks; 73 1.2 christos 74 1.2 christos RB_ENTRY(control_pane) entry; 75 1.2 christos }; 76 1.2 christos RB_HEAD(control_panes, control_pane); 77 1.2 christos 78 1.2 christos /* Subscription pane. */ 79 1.2 christos struct control_sub_pane { 80 1.2 christos u_int pane; 81 1.2 christos u_int idx; 82 1.2 christos char *last; 83 1.2 christos 84 1.2 christos RB_ENTRY(control_sub_pane) entry; 85 1.2 christos }; 86 1.2 christos RB_HEAD(control_sub_panes, control_sub_pane); 87 1.2 christos 88 1.2 christos /* Subscription window. */ 89 1.2 christos struct control_sub_window { 90 1.2 christos u_int window; 91 1.2 christos u_int idx; 92 1.2 christos char *last; 93 1.2 christos 94 1.2 christos RB_ENTRY(control_sub_window) entry; 95 1.2 christos }; 96 1.2 christos RB_HEAD(control_sub_windows, control_sub_window); 97 1.2 christos 98 1.2 christos /* Control client subscription. */ 99 1.2 christos struct control_sub { 100 1.2 christos char *name; 101 1.2 christos char *format; 102 1.2 christos 103 1.2 christos enum control_sub_type type; 104 1.2 christos u_int id; 105 1.2 christos 106 1.2 christos char *last; 107 1.2 christos struct control_sub_panes panes; 108 1.2 christos struct control_sub_windows windows; 109 1.2 christos 110 1.2 christos RB_ENTRY(control_sub) entry; 111 1.2 christos }; 112 1.2 christos RB_HEAD(control_subs, control_sub); 113 1.2 christos 114 1.2 christos /* Control client state. */ 115 1.2 christos struct control_state { 116 1.2 christos struct control_panes panes; 117 1.2 christos 118 1.2 christos TAILQ_HEAD(, control_pane) pending_list; 119 1.2 christos u_int pending_count; 120 1.2 christos 121 1.2 christos TAILQ_HEAD(, control_block) all_blocks; 122 1.2 christos 123 1.2 christos struct bufferevent *read_event; 124 1.2 christos struct bufferevent *write_event; 125 1.2 christos 126 1.2 christos struct control_subs subs; 127 1.2 christos struct event subs_timer; 128 1.2 christos }; 129 1.2 christos 130 1.2 christos /* Low and high watermarks. */ 131 1.2 christos #define CONTROL_BUFFER_LOW 512 132 1.2 christos #define CONTROL_BUFFER_HIGH 8192 133 1.2 christos 134 1.2 christos /* Minimum to write to each client. */ 135 1.2 christos #define CONTROL_WRITE_MINIMUM 32 136 1.2 christos 137 1.2 christos /* Maximum age for clients that are not using pause mode. */ 138 1.2 christos #define CONTROL_MAXIMUM_AGE 300000 139 1.2 christos 140 1.2 christos /* Flags to ignore client. */ 141 1.2 christos #define CONTROL_IGNORE_FLAGS \ 142 1.2 christos (CLIENT_CONTROL_NOOUTPUT| \ 143 1.2 christos CLIENT_UNATTACHEDFLAGS) 144 1.2 christos 145 1.2 christos /* Compare client panes. */ 146 1.2 christos static int 147 1.2 christos control_pane_cmp(struct control_pane *cp1, struct control_pane *cp2) 148 1.2 christos { 149 1.2 christos if (cp1->pane < cp2->pane) 150 1.2 christos return (-1); 151 1.2 christos if (cp1->pane > cp2->pane) 152 1.2 christos return (1); 153 1.2 christos return (0); 154 1.2 christos } 155 1.2 christos RB_GENERATE_STATIC(control_panes, control_pane, entry, control_pane_cmp); 156 1.2 christos 157 1.2 christos /* Compare client subs. */ 158 1.2 christos static int 159 1.2 christos control_sub_cmp(struct control_sub *csub1, struct control_sub *csub2) 160 1.2 christos { 161 1.2 christos return (strcmp(csub1->name, csub2->name)); 162 1.2 christos } 163 1.2 christos RB_GENERATE_STATIC(control_subs, control_sub, entry, control_sub_cmp); 164 1.2 christos 165 1.2 christos /* Compare client subscription panes. */ 166 1.2 christos static int 167 1.2 christos control_sub_pane_cmp(struct control_sub_pane *csp1, 168 1.2 christos struct control_sub_pane *csp2) 169 1.2 christos { 170 1.2 christos if (csp1->pane < csp2->pane) 171 1.2 christos return (-1); 172 1.2 christos if (csp1->pane > csp2->pane) 173 1.2 christos return (1); 174 1.2 christos if (csp1->idx < csp2->idx) 175 1.2 christos return (-1); 176 1.2 christos if (csp1->idx > csp2->idx) 177 1.2 christos return (1); 178 1.2 christos return (0); 179 1.2 christos } 180 1.2 christos RB_GENERATE_STATIC(control_sub_panes, control_sub_pane, entry, 181 1.2 christos control_sub_pane_cmp); 182 1.2 christos 183 1.2 christos /* Compare client subscription windows. */ 184 1.2 christos static int 185 1.2 christos control_sub_window_cmp(struct control_sub_window *csw1, 186 1.2 christos struct control_sub_window *csw2) 187 1.2 christos { 188 1.2 christos if (csw1->window < csw2->window) 189 1.2 christos return (-1); 190 1.2 christos if (csw1->window > csw2->window) 191 1.2 christos return (1); 192 1.2 christos if (csw1->idx < csw2->idx) 193 1.2 christos return (-1); 194 1.2 christos if (csw1->idx > csw2->idx) 195 1.2 christos return (1); 196 1.2 christos return (0); 197 1.2 christos } 198 1.2 christos RB_GENERATE_STATIC(control_sub_windows, control_sub_window, entry, 199 1.2 christos control_sub_window_cmp); 200 1.2 christos 201 1.2 christos /* Free a subscription. */ 202 1.2 christos static void 203 1.2 christos control_free_sub(struct control_state *cs, struct control_sub *csub) 204 1.2 christos { 205 1.2 christos struct control_sub_pane *csp, *csp1; 206 1.2 christos struct control_sub_window *csw, *csw1; 207 1.2 christos 208 1.2 christos RB_FOREACH_SAFE(csp, control_sub_panes, &csub->panes, csp1) { 209 1.2 christos RB_REMOVE(control_sub_panes, &csub->panes, csp); 210 1.2 christos free(csp); 211 1.2 christos } 212 1.2 christos RB_FOREACH_SAFE(csw, control_sub_windows, &csub->windows, csw1) { 213 1.2 christos RB_REMOVE(control_sub_windows, &csub->windows, csw); 214 1.2 christos free(csw); 215 1.2 christos } 216 1.2 christos free(csub->last); 217 1.2 christos 218 1.2 christos RB_REMOVE(control_subs, &cs->subs, csub); 219 1.2 christos free(csub->name); 220 1.2 christos free(csub->format); 221 1.2 christos free(csub); 222 1.2 christos } 223 1.2 christos 224 1.2 christos /* Free a block. */ 225 1.2 christos static void 226 1.2 christos control_free_block(struct control_state *cs, struct control_block *cb) 227 1.2 christos { 228 1.2 christos free(cb->line); 229 1.2 christos TAILQ_REMOVE(&cs->all_blocks, cb, all_entry); 230 1.2 christos free(cb); 231 1.2 christos } 232 1.2 christos 233 1.2 christos /* Get pane offsets for this client. */ 234 1.2 christos static struct control_pane * 235 1.2 christos control_get_pane(struct client *c, struct window_pane *wp) 236 1.2 christos { 237 1.2 christos struct control_state *cs = c->control_state; 238 1.2 christos struct control_pane cp = { .pane = wp->id }; 239 1.2 christos 240 1.2 christos return (RB_FIND(control_panes, &cs->panes, &cp)); 241 1.2 christos } 242 1.2 christos 243 1.2 christos /* Add pane offsets for this client. */ 244 1.2 christos static struct control_pane * 245 1.2 christos control_add_pane(struct client *c, struct window_pane *wp) 246 1.2 christos { 247 1.2 christos struct control_state *cs = c->control_state; 248 1.2 christos struct control_pane *cp; 249 1.2 christos 250 1.2 christos cp = control_get_pane(c, wp); 251 1.2 christos if (cp != NULL) 252 1.2 christos return (cp); 253 1.2 christos 254 1.2 christos cp = xcalloc(1, sizeof *cp); 255 1.2 christos cp->pane = wp->id; 256 1.2 christos RB_INSERT(control_panes, &cs->panes, cp); 257 1.2 christos 258 1.2 christos memcpy(&cp->offset, &wp->offset, sizeof cp->offset); 259 1.2 christos memcpy(&cp->queued, &wp->offset, sizeof cp->queued); 260 1.2 christos TAILQ_INIT(&cp->blocks); 261 1.2 christos 262 1.2 christos return (cp); 263 1.2 christos } 264 1.2 christos 265 1.2 christos /* Discard output for a pane. */ 266 1.2 christos static void 267 1.2 christos control_discard_pane(struct client *c, struct control_pane *cp) 268 1.2 christos { 269 1.2 christos struct control_state *cs = c->control_state; 270 1.2 christos struct control_block *cb, *cb1; 271 1.2 christos 272 1.2 christos TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) { 273 1.2 christos TAILQ_REMOVE(&cp->blocks, cb, entry); 274 1.2 christos control_free_block(cs, cb); 275 1.2 christos } 276 1.2 christos } 277 1.2 christos 278 1.2 christos /* Get actual pane for this client. */ 279 1.2 christos static struct window_pane * 280 1.2 christos control_window_pane(struct client *c, u_int pane) 281 1.2 christos { 282 1.2 christos struct window_pane *wp; 283 1.2 christos 284 1.2 christos if (c->session == NULL) 285 1.2 christos return (NULL); 286 1.2 christos if ((wp = window_pane_find_by_id(pane)) == NULL) 287 1.2 christos return (NULL); 288 1.2 christos if (winlink_find_by_window(&c->session->windows, wp->window) == NULL) 289 1.2 christos return (NULL); 290 1.2 christos return (wp); 291 1.2 christos } 292 1.2 christos 293 1.2 christos /* Reset control offsets. */ 294 1.2 christos void 295 1.2 christos control_reset_offsets(struct client *c) 296 1.2 christos { 297 1.2 christos struct control_state *cs = c->control_state; 298 1.2 christos struct control_pane *cp, *cp1; 299 1.2 christos 300 1.2 christos RB_FOREACH_SAFE(cp, control_panes, &cs->panes, cp1) { 301 1.2 christos RB_REMOVE(control_panes, &cs->panes, cp); 302 1.2 christos free(cp); 303 1.2 christos } 304 1.2 christos 305 1.2 christos TAILQ_INIT(&cs->pending_list); 306 1.2 christos cs->pending_count = 0; 307 1.2 christos } 308 1.2 christos 309 1.2 christos /* Get offsets for client. */ 310 1.2 christos struct window_pane_offset * 311 1.2 christos control_pane_offset(struct client *c, struct window_pane *wp, int *off) 312 1.2 christos { 313 1.2 christos struct control_state *cs = c->control_state; 314 1.2 christos struct control_pane *cp; 315 1.2 christos 316 1.2 christos if (c->flags & CLIENT_CONTROL_NOOUTPUT) { 317 1.2 christos *off = 0; 318 1.2 christos return (NULL); 319 1.2 christos } 320 1.2 christos 321 1.2 christos cp = control_get_pane(c, wp); 322 1.2 christos if (cp == NULL || (cp->flags & CONTROL_PANE_PAUSED)) { 323 1.2 christos *off = 0; 324 1.2 christos return (NULL); 325 1.2 christos } 326 1.2 christos if (cp->flags & CONTROL_PANE_OFF) { 327 1.2 christos *off = 1; 328 1.2 christos return (NULL); 329 1.2 christos } 330 1.2 christos *off = (EVBUFFER_LENGTH(cs->write_event->output) >= CONTROL_BUFFER_LOW); 331 1.2 christos return (&cp->offset); 332 1.2 christos } 333 1.2 christos 334 1.2 christos /* Set pane as on. */ 335 1.2 christos void 336 1.2 christos control_set_pane_on(struct client *c, struct window_pane *wp) 337 1.2 christos { 338 1.2 christos struct control_pane *cp; 339 1.2 christos 340 1.2 christos cp = control_get_pane(c, wp); 341 1.2 christos if (cp != NULL && (cp->flags & CONTROL_PANE_OFF)) { 342 1.2 christos cp->flags &= ~CONTROL_PANE_OFF; 343 1.2 christos memcpy(&cp->offset, &wp->offset, sizeof cp->offset); 344 1.2 christos memcpy(&cp->queued, &wp->offset, sizeof cp->queued); 345 1.2 christos } 346 1.2 christos } 347 1.2 christos 348 1.2 christos /* Set pane as off. */ 349 1.2 christos void 350 1.2 christos control_set_pane_off(struct client *c, struct window_pane *wp) 351 1.2 christos { 352 1.2 christos struct control_pane *cp; 353 1.2 christos 354 1.2 christos cp = control_add_pane(c, wp); 355 1.2 christos cp->flags |= CONTROL_PANE_OFF; 356 1.2 christos } 357 1.2 christos 358 1.2 christos /* Continue a paused pane. */ 359 1.2 christos void 360 1.2 christos control_continue_pane(struct client *c, struct window_pane *wp) 361 1.2 christos { 362 1.2 christos struct control_pane *cp; 363 1.2 christos 364 1.2 christos cp = control_get_pane(c, wp); 365 1.2 christos if (cp != NULL && (cp->flags & CONTROL_PANE_PAUSED)) { 366 1.2 christos cp->flags &= ~CONTROL_PANE_PAUSED; 367 1.2 christos memcpy(&cp->offset, &wp->offset, sizeof cp->offset); 368 1.2 christos memcpy(&cp->queued, &wp->offset, sizeof cp->queued); 369 1.2 christos control_write(c, "%%continue %%%u", wp->id); 370 1.2 christos } 371 1.2 christos } 372 1.2 christos 373 1.2 christos /* Pause a pane. */ 374 1.2 christos void 375 1.2 christos control_pause_pane(struct client *c, struct window_pane *wp) 376 1.2 christos { 377 1.2 christos struct control_pane *cp; 378 1.2 christos 379 1.2 christos cp = control_add_pane(c, wp); 380 1.2 christos if (~cp->flags & CONTROL_PANE_PAUSED) { 381 1.2 christos cp->flags |= CONTROL_PANE_PAUSED; 382 1.2 christos control_discard_pane(c, cp); 383 1.2 christos control_write(c, "%%pause %%%u", wp->id); 384 1.2 christos } 385 1.2 christos } 386 1.2 christos 387 1.2 christos /* Write a line. */ 388 1.4 wiz static void printflike(2, 0) 389 1.2 christos control_vwrite(struct client *c, const char *fmt, va_list ap) 390 1.2 christos { 391 1.2 christos struct control_state *cs = c->control_state; 392 1.2 christos char *s; 393 1.2 christos 394 1.2 christos xvasprintf(&s, fmt, ap); 395 1.2 christos log_debug("%s: %s: writing line: %s", __func__, c->name, s); 396 1.2 christos 397 1.2 christos bufferevent_write(cs->write_event, s, strlen(s)); 398 1.2 christos bufferevent_write(cs->write_event, "\n", 1); 399 1.2 christos 400 1.2 christos bufferevent_enable(cs->write_event, EV_WRITE); 401 1.2 christos free(s); 402 1.2 christos } 403 1.2 christos 404 1.1 christos /* Write a line. */ 405 1.2 christos void 406 1.1 christos control_write(struct client *c, const char *fmt, ...) 407 1.1 christos { 408 1.2 christos struct control_state *cs = c->control_state; 409 1.2 christos struct control_block *cb; 410 1.2 christos va_list ap; 411 1.1 christos 412 1.1 christos va_start(ap, fmt); 413 1.2 christos 414 1.2 christos if (TAILQ_EMPTY(&cs->all_blocks)) { 415 1.2 christos control_vwrite(c, fmt, ap); 416 1.2 christos va_end(ap); 417 1.2 christos return; 418 1.2 christos } 419 1.2 christos 420 1.2 christos cb = xcalloc(1, sizeof *cb); 421 1.2 christos xvasprintf(&cb->line, fmt, ap); 422 1.2 christos TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry); 423 1.2 christos cb->t = get_timer(); 424 1.2 christos 425 1.2 christos log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line); 426 1.2 christos bufferevent_enable(cs->write_event, EV_WRITE); 427 1.2 christos 428 1.1 christos va_end(ap); 429 1.2 christos } 430 1.2 christos 431 1.2 christos /* Check age for this pane. */ 432 1.2 christos static int 433 1.2 christos control_check_age(struct client *c, struct window_pane *wp, 434 1.2 christos struct control_pane *cp) 435 1.2 christos { 436 1.2 christos struct control_block *cb; 437 1.2 christos uint64_t t, age; 438 1.2 christos 439 1.2 christos cb = TAILQ_FIRST(&cp->blocks); 440 1.2 christos if (cb == NULL) 441 1.2 christos return (0); 442 1.2 christos t = get_timer(); 443 1.2 christos if (cb->t >= t) 444 1.2 christos return (0); 445 1.2 christos 446 1.2 christos age = t - cb->t; 447 1.2 christos log_debug("%s: %s: %%%u is %llu behind", __func__, c->name, wp->id, 448 1.2 christos (unsigned long long)age); 449 1.1 christos 450 1.2 christos if (c->flags & CLIENT_CONTROL_PAUSEAFTER) { 451 1.2 christos if (age < c->pause_age) 452 1.2 christos return (0); 453 1.2 christos cp->flags |= CONTROL_PANE_PAUSED; 454 1.2 christos control_discard_pane(c, cp); 455 1.2 christos control_write(c, "%%pause %%%u", wp->id); 456 1.2 christos } else { 457 1.2 christos if (age < CONTROL_MAXIMUM_AGE) 458 1.2 christos return (0); 459 1.2 christos c->exit_message = xstrdup("too far behind"); 460 1.2 christos c->flags |= CLIENT_EXIT; 461 1.2 christos control_discard(c); 462 1.2 christos } 463 1.2 christos return (1); 464 1.1 christos } 465 1.1 christos 466 1.2 christos /* Write output from a pane. */ 467 1.1 christos void 468 1.2 christos control_write_output(struct client *c, struct window_pane *wp) 469 1.1 christos { 470 1.2 christos struct control_state *cs = c->control_state; 471 1.2 christos struct control_pane *cp; 472 1.2 christos struct control_block *cb; 473 1.2 christos size_t new_size; 474 1.2 christos 475 1.2 christos if (winlink_find_by_window(&c->session->windows, wp->window) == NULL) 476 1.2 christos return; 477 1.2 christos 478 1.2 christos if (c->flags & CONTROL_IGNORE_FLAGS) { 479 1.2 christos cp = control_get_pane(c, wp); 480 1.2 christos if (cp != NULL) 481 1.2 christos goto ignore; 482 1.2 christos return; 483 1.2 christos } 484 1.2 christos cp = control_add_pane(c, wp); 485 1.2 christos if (cp->flags & (CONTROL_PANE_OFF|CONTROL_PANE_PAUSED)) 486 1.2 christos goto ignore; 487 1.2 christos if (control_check_age(c, wp, cp)) 488 1.2 christos return; 489 1.2 christos 490 1.2 christos window_pane_get_new_data(wp, &cp->queued, &new_size); 491 1.2 christos if (new_size == 0) 492 1.2 christos return; 493 1.2 christos window_pane_update_used_data(wp, &cp->queued, new_size); 494 1.2 christos 495 1.2 christos cb = xcalloc(1, sizeof *cb); 496 1.2 christos cb->size = new_size; 497 1.2 christos TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry); 498 1.2 christos cb->t = get_timer(); 499 1.2 christos 500 1.2 christos TAILQ_INSERT_TAIL(&cp->blocks, cb, entry); 501 1.2 christos log_debug("%s: %s: new output block of %zu for %%%u", __func__, c->name, 502 1.2 christos cb->size, wp->id); 503 1.2 christos 504 1.2 christos if (!cp->pending_flag) { 505 1.2 christos log_debug("%s: %s: %%%u now pending", __func__, c->name, 506 1.2 christos wp->id); 507 1.2 christos TAILQ_INSERT_TAIL(&cs->pending_list, cp, pending_entry); 508 1.2 christos cp->pending_flag = 1; 509 1.2 christos cs->pending_count++; 510 1.2 christos } 511 1.2 christos bufferevent_enable(cs->write_event, EV_WRITE); 512 1.2 christos return; 513 1.2 christos 514 1.2 christos ignore: 515 1.2 christos log_debug("%s: %s: ignoring pane %%%u", __func__, c->name, wp->id); 516 1.2 christos window_pane_update_used_data(wp, &cp->offset, SIZE_MAX); 517 1.2 christos window_pane_update_used_data(wp, &cp->queued, SIZE_MAX); 518 1.2 christos } 519 1.2 christos 520 1.2 christos /* Control client error callback. */ 521 1.2 christos static enum cmd_retval 522 1.2 christos control_error(struct cmdq_item *item, void *data) 523 1.2 christos { 524 1.2 christos struct client *c = cmdq_get_client(item); 525 1.2 christos char *error = data; 526 1.2 christos 527 1.2 christos cmdq_guard(item, "begin", 1); 528 1.2 christos control_write(c, "parse error: %s", error); 529 1.2 christos cmdq_guard(item, "error", 1); 530 1.2 christos 531 1.2 christos free(error); 532 1.2 christos return (CMD_RETURN_NORMAL); 533 1.1 christos } 534 1.1 christos 535 1.2 christos /* Control client error callback. */ 536 1.2 christos static void 537 1.2 christos control_error_callback(__unused struct bufferevent *bufev, 538 1.2 christos __unused short what, void *data) 539 1.1 christos { 540 1.2 christos struct client *c = data; 541 1.1 christos 542 1.2 christos c->flags |= CLIENT_EXIT; 543 1.2 christos } 544 1.2 christos 545 1.2 christos /* Control client input callback. Read lines and fire commands. */ 546 1.2 christos static void 547 1.2 christos control_read_callback(__unused struct bufferevent *bufev, void *data) 548 1.2 christos { 549 1.2 christos struct client *c = data; 550 1.2 christos struct control_state *cs = c->control_state; 551 1.2 christos struct evbuffer *buffer = cs->read_event->input; 552 1.2 christos char *line, *error; 553 1.2 christos struct cmdq_state *state; 554 1.2 christos enum cmd_parse_status status; 555 1.1 christos 556 1.1 christos for (;;) { 557 1.2 christos line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF); 558 1.1 christos if (line == NULL) 559 1.1 christos break; 560 1.2 christos log_debug("%s: %s: %s", __func__, c->name, line); 561 1.2 christos if (*line == '\0') { /* empty line detach */ 562 1.2 christos free(line); 563 1.1 christos c->flags |= CLIENT_EXIT; 564 1.1 christos break; 565 1.1 christos } 566 1.1 christos 567 1.2 christos state = cmdq_new_state(NULL, NULL, CMDQ_STATE_CONTROL); 568 1.2 christos status = cmd_parse_and_append(line, NULL, c, state, &error); 569 1.2 christos if (status == CMD_PARSE_ERROR) 570 1.2 christos cmdq_append(c, cmdq_get_callback(control_error, error)); 571 1.2 christos cmdq_free_state(state); 572 1.2 christos 573 1.2 christos free(line); 574 1.2 christos } 575 1.2 christos } 576 1.2 christos 577 1.2 christos /* Does this control client have outstanding data to write? */ 578 1.2 christos int 579 1.2 christos control_all_done(struct client *c) 580 1.2 christos { 581 1.2 christos struct control_state *cs = c->control_state; 582 1.2 christos 583 1.2 christos if (!TAILQ_EMPTY(&cs->all_blocks)) 584 1.2 christos return (0); 585 1.2 christos return (EVBUFFER_LENGTH(cs->write_event->output) == 0); 586 1.2 christos } 587 1.2 christos 588 1.2 christos /* Flush all blocks until output. */ 589 1.2 christos static void 590 1.2 christos control_flush_all_blocks(struct client *c) 591 1.2 christos { 592 1.2 christos struct control_state *cs = c->control_state; 593 1.2 christos struct control_block *cb, *cb1; 594 1.2 christos 595 1.2 christos TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1) { 596 1.2 christos if (cb->size != 0) 597 1.2 christos break; 598 1.2 christos log_debug("%s: %s: flushing line: %s", __func__, c->name, 599 1.2 christos cb->line); 600 1.2 christos 601 1.2 christos bufferevent_write(cs->write_event, cb->line, strlen(cb->line)); 602 1.2 christos bufferevent_write(cs->write_event, "\n", 1); 603 1.2 christos control_free_block(cs, cb); 604 1.2 christos } 605 1.2 christos } 606 1.2 christos 607 1.2 christos /* Append data to buffer. */ 608 1.2 christos static struct evbuffer * 609 1.2 christos control_append_data(struct client *c, struct control_pane *cp, uint64_t age, 610 1.2 christos struct evbuffer *message, struct window_pane *wp, size_t size) 611 1.2 christos { 612 1.2 christos u_char *new_data; 613 1.2 christos size_t new_size; 614 1.2 christos u_int i; 615 1.2 christos 616 1.2 christos if (message == NULL) { 617 1.2 christos message = evbuffer_new(); 618 1.2 christos if (message == NULL) 619 1.2 christos fatalx("out of memory"); 620 1.2 christos if (c->flags & CLIENT_CONTROL_PAUSEAFTER) { 621 1.2 christos evbuffer_add_printf(message, 622 1.2 christos "%%extended-output %%%u %llu : ", wp->id, 623 1.2 christos (unsigned long long)age); 624 1.2 christos } else 625 1.2 christos evbuffer_add_printf(message, "%%output %%%u ", wp->id); 626 1.2 christos } 627 1.2 christos 628 1.2 christos new_data = window_pane_get_new_data(wp, &cp->offset, &new_size); 629 1.2 christos if (new_size < size) 630 1.2 christos fatalx("not enough data: %zu < %zu", new_size, size); 631 1.2 christos for (i = 0; i < size; i++) { 632 1.2 christos if (new_data[i] < ' ' || new_data[i] == '\\') 633 1.2 christos evbuffer_add_printf(message, "\\%03o", new_data[i]); 634 1.2 christos else 635 1.2 christos evbuffer_add_printf(message, "%c", new_data[i]); 636 1.2 christos } 637 1.2 christos window_pane_update_used_data(wp, &cp->offset, size); 638 1.2 christos return (message); 639 1.2 christos } 640 1.2 christos 641 1.2 christos /* Write buffer. */ 642 1.2 christos static void 643 1.2 christos control_write_data(struct client *c, struct evbuffer *message) 644 1.2 christos { 645 1.2 christos struct control_state *cs = c->control_state; 646 1.2 christos 647 1.2 christos log_debug("%s: %s: %.*s", __func__, c->name, 648 1.2 christos (int)EVBUFFER_LENGTH(message), EVBUFFER_DATA(message)); 649 1.2 christos 650 1.2 christos evbuffer_add(message, "\n", 1); 651 1.2 christos bufferevent_write_buffer(cs->write_event, message); 652 1.2 christos evbuffer_free(message); 653 1.2 christos } 654 1.2 christos 655 1.2 christos /* Write output to client. */ 656 1.2 christos static int 657 1.2 christos control_write_pending(struct client *c, struct control_pane *cp, size_t limit) 658 1.2 christos { 659 1.2 christos struct control_state *cs = c->control_state; 660 1.2 christos struct window_pane *wp = NULL; 661 1.2 christos struct evbuffer *message = NULL; 662 1.2 christos size_t used = 0, size; 663 1.2 christos struct control_block *cb, *cb1; 664 1.2 christos uint64_t age, t = get_timer(); 665 1.2 christos 666 1.2 christos wp = control_window_pane(c, cp->pane); 667 1.4 wiz if (wp == NULL || wp->fd == -1) { 668 1.2 christos TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) { 669 1.2 christos TAILQ_REMOVE(&cp->blocks, cb, entry); 670 1.2 christos control_free_block(cs, cb); 671 1.2 christos } 672 1.2 christos control_flush_all_blocks(c); 673 1.2 christos return (0); 674 1.2 christos } 675 1.2 christos 676 1.2 christos while (used != limit && !TAILQ_EMPTY(&cp->blocks)) { 677 1.2 christos if (control_check_age(c, wp, cp)) { 678 1.2 christos if (message != NULL) 679 1.2 christos evbuffer_free(message); 680 1.2 christos message = NULL; 681 1.2 christos break; 682 1.2 christos } 683 1.2 christos 684 1.2 christos cb = TAILQ_FIRST(&cp->blocks); 685 1.2 christos if (cb->t < t) 686 1.2 christos age = t - cb->t; 687 1.2 christos else 688 1.2 christos age = 0; 689 1.2 christos log_debug("%s: %s: output block %zu (age %llu) for %%%u " 690 1.2 christos "(used %zu/%zu)", __func__, c->name, cb->size, 691 1.2 christos (unsigned long long)age, cp->pane, used, limit); 692 1.2 christos 693 1.2 christos size = cb->size; 694 1.2 christos if (size > limit - used) 695 1.2 christos size = limit - used; 696 1.2 christos used += size; 697 1.2 christos 698 1.2 christos message = control_append_data(c, cp, age, message, wp, size); 699 1.2 christos 700 1.2 christos cb->size -= size; 701 1.2 christos if (cb->size == 0) { 702 1.2 christos TAILQ_REMOVE(&cp->blocks, cb, entry); 703 1.2 christos control_free_block(cs, cb); 704 1.2 christos 705 1.2 christos cb = TAILQ_FIRST(&cs->all_blocks); 706 1.2 christos if (cb != NULL && cb->size == 0) { 707 1.2 christos if (wp != NULL && message != NULL) { 708 1.2 christos control_write_data(c, message); 709 1.2 christos message = NULL; 710 1.2 christos } 711 1.2 christos control_flush_all_blocks(c); 712 1.2 christos } 713 1.2 christos } 714 1.2 christos } 715 1.2 christos if (message != NULL) 716 1.2 christos control_write_data(c, message); 717 1.2 christos return (!TAILQ_EMPTY(&cp->blocks)); 718 1.2 christos } 719 1.2 christos 720 1.2 christos /* Control client write callback. */ 721 1.2 christos static void 722 1.2 christos control_write_callback(__unused struct bufferevent *bufev, void *data) 723 1.2 christos { 724 1.2 christos struct client *c = data; 725 1.2 christos struct control_state *cs = c->control_state; 726 1.2 christos struct control_pane *cp, *cp1; 727 1.2 christos struct evbuffer *evb = cs->write_event->output; 728 1.2 christos size_t space, limit; 729 1.2 christos 730 1.2 christos control_flush_all_blocks(c); 731 1.2 christos 732 1.2 christos while (EVBUFFER_LENGTH(evb) < CONTROL_BUFFER_HIGH) { 733 1.2 christos if (cs->pending_count == 0) 734 1.2 christos break; 735 1.2 christos space = CONTROL_BUFFER_HIGH - EVBUFFER_LENGTH(evb); 736 1.2 christos log_debug("%s: %s: %zu bytes available, %u panes", __func__, 737 1.2 christos c->name, space, cs->pending_count); 738 1.2 christos 739 1.2 christos limit = (space / cs->pending_count / 3); /* 3 bytes for \xxx */ 740 1.2 christos if (limit < CONTROL_WRITE_MINIMUM) 741 1.2 christos limit = CONTROL_WRITE_MINIMUM; 742 1.2 christos 743 1.2 christos TAILQ_FOREACH_SAFE(cp, &cs->pending_list, pending_entry, cp1) { 744 1.2 christos if (EVBUFFER_LENGTH(evb) >= CONTROL_BUFFER_HIGH) 745 1.2 christos break; 746 1.2 christos if (control_write_pending(c, cp, limit)) 747 1.2 christos continue; 748 1.2 christos TAILQ_REMOVE(&cs->pending_list, cp, pending_entry); 749 1.2 christos cp->pending_flag = 0; 750 1.2 christos cs->pending_count--; 751 1.2 christos } 752 1.2 christos } 753 1.2 christos if (EVBUFFER_LENGTH(evb) == 0) 754 1.2 christos bufferevent_disable(cs->write_event, EV_WRITE); 755 1.2 christos } 756 1.2 christos 757 1.2 christos /* Initialize for control mode. */ 758 1.2 christos void 759 1.2 christos control_start(struct client *c) 760 1.2 christos { 761 1.2 christos struct control_state *cs; 762 1.2 christos 763 1.2 christos if (c->flags & CLIENT_CONTROLCONTROL) { 764 1.2 christos close(c->out_fd); 765 1.2 christos c->out_fd = -1; 766 1.2 christos } else 767 1.2 christos setblocking(c->out_fd, 0); 768 1.2 christos setblocking(c->fd, 0); 769 1.2 christos 770 1.2 christos cs = c->control_state = xcalloc(1, sizeof *cs); 771 1.2 christos RB_INIT(&cs->panes); 772 1.2 christos TAILQ_INIT(&cs->pending_list); 773 1.2 christos TAILQ_INIT(&cs->all_blocks); 774 1.2 christos RB_INIT(&cs->subs); 775 1.2 christos 776 1.2 christos cs->read_event = bufferevent_new(c->fd, control_read_callback, 777 1.2 christos control_write_callback, control_error_callback, c); 778 1.5 wiz if (cs->read_event == NULL) 779 1.5 wiz fatalx("out of memory"); 780 1.2 christos 781 1.2 christos if (c->flags & CLIENT_CONTROLCONTROL) 782 1.2 christos cs->write_event = cs->read_event; 783 1.2 christos else { 784 1.2 christos cs->write_event = bufferevent_new(c->out_fd, NULL, 785 1.2 christos control_write_callback, control_error_callback, c); 786 1.5 wiz if (cs->write_event == NULL) 787 1.5 wiz fatalx("out of memory"); 788 1.2 christos } 789 1.2 christos bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW, 790 1.2 christos 0); 791 1.2 christos 792 1.2 christos if (c->flags & CLIENT_CONTROLCONTROL) { 793 1.2 christos bufferevent_write(cs->write_event, "\033P1000p", 7); 794 1.2 christos bufferevent_enable(cs->write_event, EV_WRITE); 795 1.2 christos } 796 1.2 christos } 797 1.2 christos 798 1.5 wiz /* Control client ready. */ 799 1.5 wiz void 800 1.5 wiz control_ready(struct client *c) 801 1.5 wiz { 802 1.5 wiz bufferevent_enable(c->control_state->read_event, EV_READ); 803 1.5 wiz } 804 1.5 wiz 805 1.2 christos /* Discard all output for a client. */ 806 1.2 christos void 807 1.2 christos control_discard(struct client *c) 808 1.2 christos { 809 1.2 christos struct control_state *cs = c->control_state; 810 1.2 christos struct control_pane *cp; 811 1.2 christos 812 1.2 christos RB_FOREACH(cp, control_panes, &cs->panes) 813 1.2 christos control_discard_pane(c, cp); 814 1.2 christos bufferevent_disable(cs->read_event, EV_READ); 815 1.2 christos } 816 1.2 christos 817 1.2 christos /* Stop control mode. */ 818 1.2 christos void 819 1.2 christos control_stop(struct client *c) 820 1.2 christos { 821 1.2 christos struct control_state *cs = c->control_state; 822 1.2 christos struct control_block *cb, *cb1; 823 1.2 christos struct control_sub *csub, *csub1; 824 1.2 christos 825 1.2 christos if (~c->flags & CLIENT_CONTROLCONTROL) 826 1.2 christos bufferevent_free(cs->write_event); 827 1.2 christos bufferevent_free(cs->read_event); 828 1.2 christos 829 1.2 christos RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) 830 1.2 christos control_free_sub(cs, csub); 831 1.2 christos if (evtimer_initialized(&cs->subs_timer)) 832 1.2 christos evtimer_del(&cs->subs_timer); 833 1.2 christos 834 1.2 christos TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1) 835 1.2 christos control_free_block(cs, cb); 836 1.2 christos control_reset_offsets(c); 837 1.2 christos 838 1.2 christos free(cs); 839 1.2 christos } 840 1.2 christos 841 1.2 christos /* Check session subscription. */ 842 1.2 christos static void 843 1.2 christos control_check_subs_session(struct client *c, struct control_sub *csub) 844 1.2 christos { 845 1.2 christos struct session *s = c->session; 846 1.2 christos struct format_tree *ft; 847 1.2 christos char *value; 848 1.2 christos 849 1.2 christos ft = format_create_defaults(NULL, c, s, NULL, NULL); 850 1.2 christos value = format_expand(ft, csub->format); 851 1.2 christos format_free(ft); 852 1.2 christos 853 1.2 christos if (csub->last != NULL && strcmp(value, csub->last) == 0) { 854 1.2 christos free(value); 855 1.2 christos return; 856 1.2 christos } 857 1.2 christos control_write(c, 858 1.2 christos "%%subscription-changed %s $%u - - - : %s", 859 1.2 christos csub->name, s->id, value); 860 1.2 christos free(csub->last); 861 1.2 christos csub->last = value; 862 1.2 christos } 863 1.2 christos 864 1.2 christos /* Check pane subscription. */ 865 1.2 christos static void 866 1.2 christos control_check_subs_pane(struct client *c, struct control_sub *csub) 867 1.2 christos { 868 1.2 christos struct session *s = c->session; 869 1.2 christos struct window_pane *wp; 870 1.2 christos struct window *w; 871 1.2 christos struct winlink *wl; 872 1.2 christos struct format_tree *ft; 873 1.2 christos char *value; 874 1.2 christos struct control_sub_pane *csp, find; 875 1.2 christos 876 1.2 christos wp = window_pane_find_by_id(csub->id); 877 1.4 wiz if (wp == NULL || wp->fd == -1) 878 1.2 christos return; 879 1.2 christos w = wp->window; 880 1.2 christos 881 1.2 christos TAILQ_FOREACH(wl, &w->winlinks, wentry) { 882 1.2 christos if (wl->session != s) 883 1.2 christos continue; 884 1.2 christos 885 1.2 christos ft = format_create_defaults(NULL, c, s, wl, wp); 886 1.2 christos value = format_expand(ft, csub->format); 887 1.2 christos format_free(ft); 888 1.2 christos 889 1.2 christos find.pane = wp->id; 890 1.2 christos find.idx = wl->idx; 891 1.2 christos 892 1.2 christos csp = RB_FIND(control_sub_panes, &csub->panes, &find); 893 1.2 christos if (csp == NULL) { 894 1.2 christos csp = xcalloc(1, sizeof *csp); 895 1.2 christos csp->pane = wp->id; 896 1.2 christos csp->idx = wl->idx; 897 1.2 christos RB_INSERT(control_sub_panes, &csub->panes, csp); 898 1.2 christos } 899 1.2 christos 900 1.2 christos if (csp->last != NULL && strcmp(value, csp->last) == 0) { 901 1.2 christos free(value); 902 1.2 christos continue; 903 1.2 christos } 904 1.2 christos control_write(c, 905 1.2 christos "%%subscription-changed %s $%u @%u %u %%%u : %s", 906 1.2 christos csub->name, s->id, w->id, wl->idx, wp->id, value); 907 1.2 christos free(csp->last); 908 1.2 christos csp->last = value; 909 1.2 christos } 910 1.2 christos } 911 1.2 christos 912 1.2 christos /* Check all panes subscription. */ 913 1.2 christos static void 914 1.2 christos control_check_subs_all_panes(struct client *c, struct control_sub *csub) 915 1.2 christos { 916 1.2 christos struct session *s = c->session; 917 1.2 christos struct window_pane *wp; 918 1.2 christos struct window *w; 919 1.2 christos struct winlink *wl; 920 1.2 christos struct format_tree *ft; 921 1.2 christos char *value; 922 1.2 christos struct control_sub_pane *csp, find; 923 1.2 christos 924 1.2 christos RB_FOREACH(wl, winlinks, &s->windows) { 925 1.2 christos w = wl->window; 926 1.2 christos TAILQ_FOREACH(wp, &w->panes, entry) { 927 1.2 christos ft = format_create_defaults(NULL, c, s, wl, wp); 928 1.2 christos value = format_expand(ft, csub->format); 929 1.2 christos format_free(ft); 930 1.2 christos 931 1.2 christos find.pane = wp->id; 932 1.2 christos find.idx = wl->idx; 933 1.2 christos 934 1.2 christos csp = RB_FIND(control_sub_panes, &csub->panes, &find); 935 1.2 christos if (csp == NULL) { 936 1.2 christos csp = xcalloc(1, sizeof *csp); 937 1.2 christos csp->pane = wp->id; 938 1.2 christos csp->idx = wl->idx; 939 1.2 christos RB_INSERT(control_sub_panes, &csub->panes, csp); 940 1.2 christos } 941 1.2 christos 942 1.2 christos if (csp->last != NULL && 943 1.2 christos strcmp(value, csp->last) == 0) { 944 1.2 christos free(value); 945 1.2 christos continue; 946 1.2 christos } 947 1.2 christos control_write(c, 948 1.2 christos "%%subscription-changed %s $%u @%u %u %%%u : %s", 949 1.2 christos csub->name, s->id, w->id, wl->idx, wp->id, value); 950 1.2 christos free(csp->last); 951 1.2 christos csp->last = value; 952 1.2 christos } 953 1.2 christos } 954 1.2 christos } 955 1.2 christos 956 1.2 christos /* Check window subscription. */ 957 1.2 christos static void 958 1.2 christos control_check_subs_window(struct client *c, struct control_sub *csub) 959 1.2 christos { 960 1.2 christos struct session *s = c->session; 961 1.2 christos struct window *w; 962 1.2 christos struct winlink *wl; 963 1.2 christos struct format_tree *ft; 964 1.2 christos char *value; 965 1.2 christos struct control_sub_window *csw, find; 966 1.2 christos 967 1.2 christos w = window_find_by_id(csub->id); 968 1.2 christos if (w == NULL) 969 1.2 christos return; 970 1.2 christos 971 1.2 christos TAILQ_FOREACH(wl, &w->winlinks, wentry) { 972 1.2 christos if (wl->session != s) 973 1.2 christos continue; 974 1.2 christos 975 1.2 christos ft = format_create_defaults(NULL, c, s, wl, NULL); 976 1.2 christos value = format_expand(ft, csub->format); 977 1.2 christos format_free(ft); 978 1.2 christos 979 1.2 christos find.window = w->id; 980 1.2 christos find.idx = wl->idx; 981 1.2 christos 982 1.2 christos csw = RB_FIND(control_sub_windows, &csub->windows, &find); 983 1.2 christos if (csw == NULL) { 984 1.2 christos csw = xcalloc(1, sizeof *csw); 985 1.2 christos csw->window = w->id; 986 1.2 christos csw->idx = wl->idx; 987 1.2 christos RB_INSERT(control_sub_windows, &csub->windows, csw); 988 1.2 christos } 989 1.2 christos 990 1.2 christos if (csw->last != NULL && strcmp(value, csw->last) == 0) { 991 1.2 christos free(value); 992 1.2 christos continue; 993 1.2 christos } 994 1.2 christos control_write(c, 995 1.2 christos "%%subscription-changed %s $%u @%u %u - : %s", 996 1.2 christos csub->name, s->id, w->id, wl->idx, value); 997 1.2 christos free(csw->last); 998 1.2 christos csw->last = value; 999 1.2 christos } 1000 1.2 christos } 1001 1.2 christos 1002 1.2 christos /* Check all windows subscription. */ 1003 1.2 christos static void 1004 1.2 christos control_check_subs_all_windows(struct client *c, struct control_sub *csub) 1005 1.2 christos { 1006 1.2 christos struct session *s = c->session; 1007 1.2 christos struct window *w; 1008 1.2 christos struct winlink *wl; 1009 1.2 christos struct format_tree *ft; 1010 1.2 christos char *value; 1011 1.2 christos struct control_sub_window *csw, find; 1012 1.2 christos 1013 1.2 christos RB_FOREACH(wl, winlinks, &s->windows) { 1014 1.2 christos w = wl->window; 1015 1.2 christos 1016 1.2 christos ft = format_create_defaults(NULL, c, s, wl, NULL); 1017 1.2 christos value = format_expand(ft, csub->format); 1018 1.2 christos format_free(ft); 1019 1.2 christos 1020 1.2 christos find.window = w->id; 1021 1.2 christos find.idx = wl->idx; 1022 1.2 christos 1023 1.2 christos csw = RB_FIND(control_sub_windows, &csub->windows, &find); 1024 1.2 christos if (csw == NULL) { 1025 1.2 christos csw = xcalloc(1, sizeof *csw); 1026 1.2 christos csw->window = w->id; 1027 1.2 christos csw->idx = wl->idx; 1028 1.2 christos RB_INSERT(control_sub_windows, &csub->windows, csw); 1029 1.1 christos } 1030 1.1 christos 1031 1.2 christos if (csw->last != NULL && strcmp(value, csw->last) == 0) { 1032 1.2 christos free(value); 1033 1.2 christos continue; 1034 1.2 christos } 1035 1.2 christos control_write(c, 1036 1.2 christos "%%subscription-changed %s $%u @%u %u - : %s", 1037 1.2 christos csub->name, s->id, w->id, wl->idx, value); 1038 1.2 christos free(csw->last); 1039 1.2 christos csw->last = value; 1040 1.2 christos } 1041 1.2 christos } 1042 1.2 christos 1043 1.2 christos /* Check subscriptions timer. */ 1044 1.2 christos static void 1045 1.2 christos control_check_subs_timer(__unused int fd, __unused short events, void *data) 1046 1.2 christos { 1047 1.2 christos struct client *c = data; 1048 1.2 christos struct control_state *cs = c->control_state; 1049 1.2 christos struct control_sub *csub, *csub1; 1050 1.2 christos struct timeval tv = { .tv_sec = 1 }; 1051 1.2 christos 1052 1.2 christos log_debug("%s: timer fired", __func__); 1053 1.2 christos evtimer_add(&cs->subs_timer, &tv); 1054 1.2 christos 1055 1.2 christos RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) { 1056 1.2 christos switch (csub->type) { 1057 1.2 christos case CONTROL_SUB_SESSION: 1058 1.2 christos control_check_subs_session(c, csub); 1059 1.2 christos break; 1060 1.2 christos case CONTROL_SUB_PANE: 1061 1.2 christos control_check_subs_pane(c, csub); 1062 1.2 christos break; 1063 1.2 christos case CONTROL_SUB_ALL_PANES: 1064 1.2 christos control_check_subs_all_panes(c, csub); 1065 1.2 christos break; 1066 1.2 christos case CONTROL_SUB_WINDOW: 1067 1.2 christos control_check_subs_window(c, csub); 1068 1.2 christos break; 1069 1.2 christos case CONTROL_SUB_ALL_WINDOWS: 1070 1.2 christos control_check_subs_all_windows(c, csub); 1071 1.2 christos break; 1072 1.2 christos } 1073 1.1 christos } 1074 1.1 christos } 1075 1.2 christos 1076 1.2 christos /* Add a subscription. */ 1077 1.2 christos void 1078 1.2 christos control_add_sub(struct client *c, const char *name, enum control_sub_type type, 1079 1.2 christos int id, const char *format) 1080 1.2 christos { 1081 1.2 christos struct control_state *cs = c->control_state; 1082 1.2 christos struct control_sub *csub, find; 1083 1.2 christos struct timeval tv = { .tv_sec = 1 }; 1084 1.2 christos 1085 1.2 christos find.name = __UNCONST(name); 1086 1.2 christos if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL) 1087 1.2 christos control_free_sub(cs, csub); 1088 1.2 christos 1089 1.2 christos csub = xcalloc(1, sizeof *csub); 1090 1.2 christos csub->name = xstrdup(name); 1091 1.2 christos csub->type = type; 1092 1.2 christos csub->id = id; 1093 1.2 christos csub->format = xstrdup(format); 1094 1.2 christos RB_INSERT(control_subs, &cs->subs, csub); 1095 1.2 christos 1096 1.2 christos RB_INIT(&csub->panes); 1097 1.2 christos RB_INIT(&csub->windows); 1098 1.2 christos 1099 1.2 christos if (!evtimer_initialized(&cs->subs_timer)) 1100 1.2 christos evtimer_set(&cs->subs_timer, control_check_subs_timer, c); 1101 1.2 christos if (!evtimer_pending(&cs->subs_timer, NULL)) 1102 1.2 christos evtimer_add(&cs->subs_timer, &tv); 1103 1.2 christos } 1104 1.2 christos 1105 1.2 christos /* Remove a subscription. */ 1106 1.2 christos void 1107 1.2 christos control_remove_sub(struct client *c, const char *name) 1108 1.2 christos { 1109 1.2 christos struct control_state *cs = c->control_state; 1110 1.2 christos struct control_sub *csub, find; 1111 1.2 christos 1112 1.2 christos find.name = __UNCONST(name); 1113 1.2 christos if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL) 1114 1.2 christos control_free_sub(cs, csub); 1115 1.2 christos if (RB_EMPTY(&cs->subs)) 1116 1.2 christos evtimer_del(&cs->subs_timer); 1117 1.2 christos } 1118