diff --git a/format.c b/format.c index 3d498b34..1f6b58e7 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) @@ -3443,6 +3455,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 58a1b091..98961f03 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); } @@ -1901,6 +1903,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; @@ -1999,6 +2006,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 fd58c480..d3328dc8 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 a2755d35..105a8afb 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) @@ -1782,6 +1828,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); @@ -2080,7 +2129,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 d069059c..ca846d7d 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6264,6 +6264,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 60795e09..ee3f03ac 100644 --- a/tmux.h +++ b/tmux.h @@ -652,6 +652,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) @@ -1222,6 +1223,7 @@ struct window_pane { struct window_pane_resizes resize_queue; struct event resize_timer; + struct event sync_timer; struct input_ctx *ictx; @@ -3144,6 +3146,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 68634c81..b62d1921 100644 --- a/window.c +++ b/window.c @@ -1001,6 +1001,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); @@ -1080,6 +1082,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;