diff --git a/format.c b/format.c index afcf7535..ec55fc91 100644 --- a/format.c +++ b/format.c @@ -1947,6 +1947,18 @@ format_cb_origin_flag(struct format_tree *ft) return (NULL); } +/* Callback for synchronized_output_flag. */ +static void * +format_cb_synchronized_output_flag(struct format_tree *ft) +{ + if (ft->wp != NULL) { + if (ft->wp->base.mode & MODE_SYNC) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + /* Callback for pane_active. */ static void * format_cb_pane_active(struct format_tree *ft) @@ -3439,6 +3451,9 @@ static const struct format_table_entry format_table[] = { { "start_time", FORMAT_TABLE_TIME, format_cb_start_time }, + { "synchronized_output_flag", FORMAT_TABLE_STRING, + format_cb_synchronized_output_flag + }, { "tree_mode_format", FORMAT_TABLE_STRING, format_cb_tree_mode_format }, diff --git a/input.c b/input.c index ce888887..b3f67b23 100644 --- a/input.c +++ b/input.c @@ -898,6 +898,8 @@ input_free(struct input_ctx *ictx) evbuffer_free(ictx->since_ground); event_del(&ictx->ground_timer); + screen_write_stop_sync(ictx->wp); + free(ictx); } @@ -1897,6 +1899,11 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) case 2031: screen_write_mode_clear(sctx, MODE_THEME_UPDATES); break; + case 2026: /* synchronized output */ + screen_write_stop_sync(ictx->wp); + if (ictx->wp != NULL) + ictx->wp->flags |= PANE_REDRAW; + break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; @@ -1995,6 +2002,9 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) case 2031: screen_write_mode_set(sctx, MODE_THEME_UPDATES); break; + case 2026: /* synchronized output */ + screen_write_start_sync(ictx->wp); + break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; diff --git a/screen-redraw.c b/screen-redraw.c index 0dda2fea..84ab17a6 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -898,6 +898,9 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) struct grid_cell defaults; u_int i, j, top, x, y, width; + if (wp->base.mode & MODE_SYNC) + screen_write_stop_sync(wp); + log_debug("%s: %s @%u %%%u", __func__, c->name, w->id, wp->id); if (wp->xoff + wp->sx <= ctx->ox || wp->xoff >= ctx->ox + ctx->sx) diff --git a/screen-write.c b/screen-write.c index 39c15b39..86eb45c2 100644 --- a/screen-write.c +++ b/screen-write.c @@ -894,6 +894,52 @@ screen_write_mode_clear(struct screen_write_ctx *ctx, int mode) log_debug("%s: %s", __func__, screen_mode_to_string(mode)); } +/* Sync timeout callback. */ +static void +screen_write_sync_callback(__unused int fd, __unused short events, void *arg) +{ + struct window_pane *wp = arg; + + log_debug("%s: %%%u sync timer expired", __func__, wp->id); + evtimer_del(&wp->sync_timer); + + if (wp->base.mode & MODE_SYNC) { + wp->base.mode &= ~MODE_SYNC; + wp->flags |= PANE_REDRAW; + } +} + +/* Start sync mode. */ +void +screen_write_start_sync(struct window_pane *wp) +{ + struct timeval tv = { .tv_sec = 1, .tv_usec = 0 }; + + if (wp == NULL) + return; + + wp->base.mode |= MODE_SYNC; + if (!event_initialized(&wp->sync_timer)) + evtimer_set(&wp->sync_timer, screen_write_sync_callback, wp); + evtimer_add(&wp->sync_timer, &tv); + + log_debug("%s: %%%u started sync mode", __func__, wp->id); +} + +/* Stop sync mode. */ +void +screen_write_stop_sync(struct window_pane *wp) +{ + if (wp == NULL) + return; + + if (event_initialized(&wp->sync_timer)) + evtimer_del(&wp->sync_timer); + wp->base.mode &= ~MODE_SYNC; + + log_debug("%s: %%%u stopped sync mode", __func__, wp->id); +} + /* Cursor up by ny. */ void screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny) @@ -1692,6 +1738,9 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, u_int y, cx, cy, last, items = 0; struct tty_ctx ttyctx; + if (s->mode & MODE_SYNC) + return; + if (ctx->scrolled != 0) { log_debug("%s: scrolled %u (region %u-%u)", __func__, ctx->scrolled, s->rupper, s->rlower); @@ -1985,7 +2034,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) } /* Write to the screen. */ - if (!skip) { + if (!skip && !(s->mode & MODE_SYNC)) { if (selected) { screen_select_cell(s, &tmp_gc, gc); ttyctx.cell = &tmp_gc; diff --git a/tmux.1 b/tmux.1 index f032a8de..6e5b70f7 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6262,6 +6262,7 @@ The following variables are available, where appropriate: .It Li "socket_path" Ta "" Ta "Server socket path" .It Li "sixel_support" Ta "" Ta "1 if server has support for SIXEL" .It Li "start_time" Ta "" Ta "Server start time" +.It Li "synchronized_output_flag" Ta "" Ta "1 if pane has synchronized output enabled" .It Li "uid" Ta "" Ta "Server UID" .It Li "user" Ta "" Ta "Server user" .It Li "version" Ta "" Ta "Server version" diff --git a/tmux.h b/tmux.h index 9cd11609..180208e3 100644 --- a/tmux.h +++ b/tmux.h @@ -643,6 +643,7 @@ enum tty_code_code { #define MODE_CURSOR_BLINKING_SET 0x20000 #define MODE_KEYS_EXTENDED_2 0x40000 #define MODE_THEME_UPDATES 0x80000 +#define MODE_SYNC 0x100000 #define ALL_MODES 0xffffff #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) @@ -1190,6 +1191,7 @@ struct window_pane { struct window_pane_resizes resize_queue; struct event resize_timer; + struct event sync_timer; struct input_ctx *ictx; @@ -3099,6 +3101,8 @@ void screen_write_preview(struct screen_write_ctx *, struct screen *, u_int, void screen_write_backspace(struct screen_write_ctx *); void screen_write_mode_set(struct screen_write_ctx *, int); void screen_write_mode_clear(struct screen_write_ctx *, int); +void screen_write_start_sync(struct window_pane *); +void screen_write_stop_sync(struct window_pane *); void screen_write_cursorup(struct screen_write_ctx *, u_int); void screen_write_cursordown(struct screen_write_ctx *, u_int); void screen_write_cursorright(struct screen_write_ctx *, u_int); diff --git a/window.c b/window.c index 80bc57c2..24bc5936 100644 --- a/window.c +++ b/window.c @@ -990,6 +990,8 @@ window_pane_destroy(struct window_pane *wp) if (event_initialized(&wp->resize_timer)) event_del(&wp->resize_timer); + if (event_initialized(&wp->sync_timer)) + event_del(&wp->sync_timer); TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) { TAILQ_REMOVE(&wp->resize_queue, r, entry); free(r); @@ -1069,6 +1071,8 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) if (sx == wp->sx && sy == wp->sy) return; + screen_write_stop_sync(wp); + r = xmalloc(sizeof *r); r->sx = sx; r->sy = sy;