diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 9c4b1508..a5582e46 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -99,7 +99,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) w = wp->window = window_create(w->sx, w->sy, w->xpixel, w->ypixel); options_set_parent(wp->options, w->options); - wp->flags |= PANE_STYLECHANGED; + wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); TAILQ_INSERT_HEAD(&w->panes, wp, entry); w->active = wp; w->latest = tc; diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 627424ec..3300498f 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -150,7 +150,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) src_wp->window = dst_w; options_set_parent(src_wp->options, dst_w->options); - src_wp->flags |= PANE_STYLECHANGED; + src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); if (flags & SPAWN_BEFORE) TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry); else diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 135729f5..3cabe07e 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -149,12 +149,14 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) markedwp = marked_pane.wp; if (lastwp != NULL) { - lastwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); + lastwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED| + PANE_THEMECHANGED); server_redraw_window_borders(lastwp->window); server_status_window(lastwp->window); } if (markedwp != NULL) { - markedwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); + markedwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED| + PANE_THEMECHANGED); server_redraw_window_borders(markedwp->window); server_status_window(markedwp->window); } @@ -169,7 +171,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } options_set_string(oo, "window-active-style", 0, "%s", style); - wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); + wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|PANE_THEMECHANGED); } if (args_has(args, 'g')) { cmdq_print(item, "%s", options_get_string(oo, "window-style")); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index c270fbd1..35b3f140 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -217,7 +217,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'R')) { colour_palette_clear(&wp->palette); input_reset(wp->ictx, 1); - wp->flags |= (PANE_STYLECHANGED|PANE_REDRAW); + wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED|PANE_REDRAW); } if (count == 0) { diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 6931bd16..4680f598 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -101,10 +101,10 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) src_wp->window = dst_w; options_set_parent(src_wp->options, dst_w->options); - src_wp->flags |= PANE_STYLECHANGED; + src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); dst_wp->window = src_w; options_set_parent(dst_wp->options, src_w->options); - dst_wp->flags |= PANE_STYLECHANGED; + dst_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); sx = src_wp->sx; sy = src_wp->sy; xoff = src_wp->xoff; yoff = src_wp->yoff; diff --git a/colour.c b/colour.c index 5fd91c9c..903090d3 100644 --- a/colour.c +++ b/colour.c @@ -182,6 +182,46 @@ colour_tostring(int c) return ("invalid"); } +/* Convert background colour to theme. */ +enum client_theme +colour_totheme(int c) +{ + int r, g, b, brightness; + + if (c == -1) + return (THEME_UNKNOWN); + + if (c & COLOUR_FLAG_RGB) { + r = (c >> 16) & 0xff; + g = (c >> 8) & 0xff; + b = (c >> 0) & 0xff; + + brightness = r + g + b; + if (brightness > 382) + return (THEME_LIGHT); + return (THEME_DARK); + } + + if (c & COLOUR_FLAG_256) + return (colour_totheme(colour_256toRGB(c))); + + switch (c) { + case 0: + case 90: + return (THEME_DARK); + case 7: + case 97: + return (THEME_LIGHT); + default: + if (c >= 0 && c <= 7) + return (colour_totheme(colour_256toRGB(c))); + if (c >= 90 && c <= 97) + return (colour_totheme(colour_256toRGB(8 + c - 90))); + break; + } + return (THEME_UNKNOWN); +} + /* Convert colour from string. */ int colour_fromstring(const char *s) diff --git a/format.c b/format.c index 7cd5b2a8..39a4a9a7 100644 --- a/format.c +++ b/format.c @@ -1544,6 +1544,23 @@ format_cb_client_written(struct format_tree *ft) return (NULL); } +/* Callback for client_theme. */ +static void * +format_cb_client_theme(struct format_tree *ft) +{ + if (ft->c != NULL) { + switch (ft->c->theme) { + case THEME_DARK: + return (xstrdup("dark")); + case THEME_LIGHT: + return (xstrdup("light")); + case THEME_UNKNOWN: + return (NULL); + } + } + return (NULL); +} + /* Callback for config_files. */ static void * format_cb_config_files(__unused struct format_tree *ft) @@ -2877,6 +2894,9 @@ static const struct format_table_entry format_table[] = { { "client_termtype", FORMAT_TABLE_STRING, format_cb_client_termtype }, + { "client_theme", FORMAT_TABLE_STRING, + format_cb_client_theme + }, { "client_tty", FORMAT_TABLE_STRING, format_cb_client_tty }, diff --git a/input-keys.c b/input-keys.c index 900dea07..91415064 100644 --- a/input-keys.c +++ b/input-keys.c @@ -314,6 +314,12 @@ static struct input_key_entry input_key_defaults[] = { { .key = KEYC_DC|KEYC_BUILD_MODIFIERS, .data = "\033[3;_~" }, + { .key = KEYC_REPORT_DARK_THEME, + .data = "\033[?997;1n" + }, + { .key = KEYC_REPORT_LIGHT_THEME, + .data = "\033[?997;2n" + }, }; static const key_code input_key_modifiers[] = { 0, diff --git a/input.c b/input.c index b6eef671..02bd5458 100644 --- a/input.c +++ b/input.c @@ -133,7 +133,7 @@ static void printflike(2, 3) input_reply(struct input_ctx *, const char *, ...); static void input_set_state(struct input_ctx *, const struct input_transition *); static void input_reset_cell(struct input_ctx *); - +static void input_report_current_theme(struct input_ctx *); static void input_osc_4(struct input_ctx *, const char *); static void input_osc_8(struct input_ctx *, const char *); static void input_osc_10(struct input_ctx *, const char *); @@ -243,6 +243,7 @@ enum input_csi_type { INPUT_CSI_DECSTBM, INPUT_CSI_DL, INPUT_CSI_DSR, + INPUT_CSI_DSR_PRIVATE, INPUT_CSI_ECH, INPUT_CSI_ED, INPUT_CSI_EL, @@ -251,6 +252,7 @@ enum input_csi_type { INPUT_CSI_IL, INPUT_CSI_MODOFF, INPUT_CSI_MODSET, + INPUT_CSI_QUERY_PRIVATE, INPUT_CSI_RCP, INPUT_CSI_REP, INPUT_CSI_RM, @@ -259,8 +261,8 @@ enum input_csi_type { INPUT_CSI_SD, INPUT_CSI_SGR, INPUT_CSI_SM, - INPUT_CSI_SM_PRIVATE, INPUT_CSI_SM_GRAPHICS, + INPUT_CSI_SM_PRIVATE, INPUT_CSI_SU, INPUT_CSI_TBC, INPUT_CSI_VPA, @@ -304,6 +306,8 @@ static const struct input_table_entry input_csi_table[] = { { 'm', ">", INPUT_CSI_MODSET }, { 'n', "", INPUT_CSI_DSR }, { 'n', ">", INPUT_CSI_MODOFF }, + { 'n', "?", INPUT_CSI_DSR_PRIVATE }, + { 'p', "?$", INPUT_CSI_QUERY_PRIVATE }, { 'q', " ", INPUT_CSI_DECSCUSR }, { 'q', ">", INPUT_CSI_XDA }, { 'r', "", INPUT_CSI_DECSTBM }, @@ -1527,6 +1531,20 @@ input_csi_dispatch(struct input_ctx *ictx) if (n != -1) screen_write_deleteline(sctx, n, bg); break; + case INPUT_CSI_DSR_PRIVATE: + switch (input_get(ictx, 0, 0, 0)) { + case 996: + input_report_current_theme(ictx); + break; + } + break; + case INPUT_CSI_QUERY_PRIVATE: + switch (input_get(ictx, 0, 0, 0)) { + case 2031: + input_reply(ictx, "\033[?2031;2$y"); + break; + } + break; case INPUT_CSI_DSR: switch (input_get(ictx, 0, 0, 0)) { case -1: @@ -1777,6 +1795,9 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) case 2004: screen_write_mode_clear(sctx, MODE_BRACKETPASTE); break; + case 2031: + screen_write_mode_clear(sctx, MODE_THEME_UPDATES); + break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; @@ -1872,6 +1893,9 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) case 2004: screen_write_mode_set(sctx, MODE_BRACKETPASTE); break; + case 2031: + screen_write_mode_set(sctx, MODE_THEME_UPDATES); + break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; @@ -2660,84 +2684,6 @@ bad: free(id); } -/* - * Get a client with a foreground for the pane. There isn't much to choose - * between them so just use the first. - */ -static int -input_get_fg_client(struct window_pane *wp) -{ - struct window *w = wp->window; - struct client *loop; - - TAILQ_FOREACH(loop, &clients, entry) { - if (loop->flags & CLIENT_UNATTACHEDFLAGS) - continue; - if (loop->session == NULL || !session_has(loop->session, w)) - continue; - if (loop->tty.fg == -1) - continue; - return (loop->tty.fg); - } - return (-1); -} - -/* Get a client with a background for the pane. */ -static int -input_get_bg_client(struct window_pane *wp) -{ - struct window *w = wp->window; - struct client *loop; - - TAILQ_FOREACH(loop, &clients, entry) { - if (loop->flags & CLIENT_UNATTACHEDFLAGS) - continue; - if (loop->session == NULL || !session_has(loop->session, w)) - continue; - if (loop->tty.bg == -1) - continue; - return (loop->tty.bg); - } - return (-1); -} - -/* - * If any control mode client exists that has provided a bg color, return it. - * Otherwise, return -1. - */ -static int -input_get_bg_control_client(struct window_pane *wp) -{ - struct client *c; - - if (wp->control_bg == -1) - return (-1); - - TAILQ_FOREACH(c, &clients, entry) { - if (c->flags & CLIENT_CONTROL) - return (wp->control_bg); - } - return (-1); -} - -/* - * If any control mode client exists that has provided a fg color, return it. - * Otherwise, return -1. - */ -static int -input_get_fg_control_client(struct window_pane *wp) -{ - struct client *c; - - if (wp->control_fg == -1) - return (-1); - - TAILQ_FOREACH(c, &clients, entry) { - if (c->flags & CLIENT_CONTROL) - return (wp->control_fg); - } - return (-1); -} /* Handle the OSC 10 sequence for setting and querying foreground colour. */ static void @@ -2750,11 +2696,11 @@ input_osc_10(struct input_ctx *ictx, const char *p) if (strcmp(p, "?") == 0) { if (wp == NULL) return; - c = input_get_fg_control_client(wp); + c = window_pane_get_fg_control_client(wp); if (c == -1) { tty_default_colours(&defaults, wp); if (COLOUR_DEFAULT(defaults.fg)) - c = input_get_fg_client(wp); + c = window_pane_get_fg(wp); else c = defaults.fg; } @@ -2795,20 +2741,12 @@ static void input_osc_11(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; - struct grid_cell defaults; int c; if (strcmp(p, "?") == 0) { if (wp == NULL) return; - c = input_get_bg_control_client(wp); - if (c == -1) { - tty_default_colours(&defaults, wp); - if (COLOUR_DEFAULT(defaults.bg)) - c = input_get_bg_client(wp); - else - c = defaults.bg; - } + c = window_pane_get_bg(wp); input_osc_colour_reply(ictx, 11, c); return; } @@ -2820,7 +2758,7 @@ input_osc_11(struct input_ctx *ictx, const char *p) if (ictx->palette != NULL) { ictx->palette->bg = c; if (wp != NULL) - wp->flags |= PANE_STYLECHANGED; + wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); screen_write_fullredraw(&ictx->ctx); } } @@ -2836,7 +2774,7 @@ input_osc_111(struct input_ctx *ictx, const char *p) if (ictx->palette != NULL) { ictx->palette->bg = 8; if (wp != NULL) - wp->flags |= PANE_STYLECHANGED; + wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); screen_write_fullredraw(&ictx->ctx); } } @@ -3027,3 +2965,18 @@ input_set_buffer_size(size_t buffer_size) log_debug("%s: %lu -> %lu", __func__, input_buffer_size, buffer_size); input_buffer_size = buffer_size; } + +static void +input_report_current_theme(struct input_ctx *ictx) +{ + switch (window_pane_get_theme(ictx->wp)) { + case THEME_DARK: + input_reply(ictx, "\033[?997;1n"); + break; + case THEME_LIGHT: + input_reply(ictx, "\033[?997;2n"); + break; + case THEME_UNKNOWN: + break; + } +} diff --git a/options-table.c b/options-table.c index bea0bc4c..6de2fb02 100644 --- a/options-table.c +++ b/options-table.c @@ -1455,6 +1455,8 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_HOOK("client-focus-out", ""), OPTIONS_TABLE_HOOK("client-resized", ""), OPTIONS_TABLE_HOOK("client-session-changed", ""), + OPTIONS_TABLE_HOOK("client-light-theme", ""), + OPTIONS_TABLE_HOOK("client-dark-theme", ""), OPTIONS_TABLE_HOOK("command-error", ""), OPTIONS_TABLE_PANE_HOOK("pane-died", ""), OPTIONS_TABLE_PANE_HOOK("pane-exited", ""), diff --git a/options.c b/options.c index 4beb9898..5541a376 100644 --- a/options.c +++ b/options.c @@ -1165,7 +1165,7 @@ options_push_changes(const char *name) if (strcmp(name, "window-style") == 0 || strcmp(name, "window-active-style") == 0) { RB_FOREACH(wp, window_pane_tree, &all_window_panes) - wp->flags |= PANE_STYLECHANGED; + wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); } if (strcmp(name, "pane-colours") == 0) { RB_FOREACH(wp, window_pane_tree, &all_window_panes) diff --git a/server-client.c b/server-client.c index 7b017697..8ab00fbf 100644 --- a/server-client.c +++ b/server-client.c @@ -60,11 +60,11 @@ static void server_client_set_title(struct client *); static void server_client_set_path(struct client *); static void server_client_reset_state(struct client *); static void server_client_update_latest(struct client *); - static void server_client_dispatch(struct imsg *, void *); static void server_client_dispatch_command(struct client *, struct imsg *); static void server_client_dispatch_identify(struct client *, struct imsg *); static void server_client_dispatch_shell(struct client *); +static void server_client_report_theme(struct client *, enum client_theme); /* Compare client windows. */ static int @@ -304,6 +304,7 @@ server_client_create(int fd) c->tty.sx = 80; c->tty.sy = 24; + c->theme = THEME_UNKNOWN; status_init(c); c->flags |= CLIENT_FOCUSED; @@ -405,6 +406,7 @@ server_client_set_session(struct client *c, struct session *s) recalculate_sizes(); window_update_focus(s->curw->window); session_update_activity(s, NULL); + session_theme_changed(s); gettimeofday(&s->last_attached_time, NULL); s->curw->flags &= ~WINLINK_ALERTFLAGS; s->curw->window->latest = c; @@ -2388,6 +2390,16 @@ server_client_key_callback(struct cmdq_item *item, void *data) event->key = key; } + /* Handle theme reporting keys. */ + if (key == KEYC_REPORT_LIGHT_THEME) { + server_client_report_theme(c, THEME_LIGHT); + goto out; + } + if (key == KEYC_REPORT_DARK_THEME) { + server_client_report_theme(c, THEME_DARK); + goto out; + } + /* Find affected pane. */ if (!KEYC_IS_MOUSE(key) || cmd_find_from_mouse(&fs, m, 0) != 0) cmd_find_from_client(&fs, c, 0); @@ -2678,6 +2690,12 @@ server_client_loop(void) } check_window_name(w); } + + /* Send theme updates. */ + RB_FOREACH(w, windows, &windows) { + TAILQ_FOREACH(wp, &w->panes, entry) + window_pane_send_theme_update(wp); + } } /* Check if window needs to be resized. */ @@ -3909,3 +3927,21 @@ out: if (!parse) free(msg); } + +static void +server_client_report_theme(struct client *c, enum client_theme theme) +{ + if (theme == THEME_LIGHT) { + c->theme = THEME_LIGHT; + notify_client("client-light-theme", c); + } else { + c->theme = THEME_DARK; + notify_client("client-dark-theme", c); + } + + /* + * Request foreground and background colour again. Don't forward 2031 to + * panes until a response is received. + */ + tty_puts(&c->tty, "\033]10;?\033\\\033]11;?\033\\"); +} diff --git a/session.c b/session.c index a3adafdd..e9664183 100644 --- a/session.c +++ b/session.c @@ -753,3 +753,16 @@ session_renumber_windows(struct session *s) RB_FOREACH_SAFE(wl, winlinks, &old_wins, wl1) winlink_remove(&old_wins, wl); } + +/* Set the PANE_THEMECHANGED flag for every pane in this session. */ +void +session_theme_changed(struct session *s) +{ + struct window_pane *wp; + struct winlink *wl; + + RB_FOREACH(wl, winlinks, &s->windows) { + TAILQ_FOREACH(wp, &wl->window->panes, entry) + wp->flags |= PANE_THEMECHANGED; + } +} diff --git a/tmux.h b/tmux.h index 52b85cb1..9d239510 100644 --- a/tmux.h +++ b/tmux.h @@ -369,6 +369,10 @@ enum { KEYC_KP_ZERO, KEYC_KP_PERIOD, + /* Theme reporting. */ + KEYC_REPORT_DARK_THEME, + KEYC_REPORT_LIGHT_THEME, + /* End of special keys. */ KEYC_BASE_END }; @@ -636,6 +640,7 @@ enum tty_code_code { #define MODE_CURSOR_VERY_VISIBLE 0x10000 #define MODE_CURSOR_BLINKING_SET 0x20000 #define MODE_KEYS_EXTENDED_2 0x40000 +#define MODE_THEME_UPDATES 0x80000 #define ALL_MODES 0xffffff #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) @@ -1125,8 +1130,9 @@ struct window_pane { #define PANE_STATUSDRAWN 0x400 #define PANE_EMPTY 0x800 #define PANE_STYLECHANGED 0x1000 -#define PANE_UNSEENCHANGES 0x2000 -#define PANE_REDRAWSCROLLBAR 0x4000 +#define PANE_THEMECHANGED 0x2000 +#define PANE_UNSEENCHANGES 0x4000 +#define PANE_REDRAWSCROLLBAR 0x8000 u_int sb_slider_y; u_int sb_slider_h; @@ -1831,6 +1837,16 @@ struct overlay_ranges { u_int nx[OVERLAY_MAX_RANGES]; }; +/* + * Client theme, this is worked out from the background colour if not reported + * by terminal. + */ +enum client_theme { + THEME_UNKNOWN, + THEME_LIGHT, + THEME_DARK +}; + /* Client connection. */ typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef void (*prompt_free_cb)(void *); @@ -1890,6 +1906,7 @@ struct client { struct mouse_event click_event; struct status_line status; + enum client_theme theme; #define CLIENT_TERMINAL 0x1 #define CLIENT_LOGIN 0x2 @@ -2887,6 +2904,8 @@ void input_parse_screen(struct input_ctx *, struct screen *, void input_reply_clipboard(struct bufferevent *, const char *, size_t, const char *); void input_set_buffer_size(size_t); +int input_get_bg_client(struct window_pane *); +int input_get_bg_control_client(struct window_pane *); /* input-key.c */ void input_key_build(void); @@ -2901,7 +2920,8 @@ int colour_join_rgb(u_char, u_char, u_char); void colour_split_rgb(int, u_char *, u_char *, u_char *); int colour_force_rgb(int); const char *colour_tostring(int); -int colour_fromstring(const char *s); +enum client_theme colour_totheme(int); +int colour_fromstring(const char *); int colour_256toRGB(int); int colour_256to16(int); int colour_byname(const char *); @@ -3198,6 +3218,13 @@ void window_set_fill_character(struct window *); void window_pane_default_cursor(struct window_pane *); int window_pane_mode(struct window_pane *); int window_pane_show_scrollbar(struct window_pane *, int); +int window_pane_get_bg(struct window_pane *); +int window_pane_get_fg(struct window_pane *); +int window_pane_get_fg_control_client(struct window_pane *); +int window_pane_get_bg_control_client(struct window_pane *); +int window_get_bg_client(struct window_pane *); +enum client_theme window_pane_get_theme(struct window_pane *); +void window_pane_send_theme_update(struct window_pane *); /* layout.c */ u_int layout_count_cells(struct layout_cell *); @@ -3395,6 +3422,7 @@ void session_group_synchronize_from(struct session *); u_int session_group_count(struct session_group *); u_int session_group_attached_count(struct session_group *); void session_renumber_windows(struct session *); +void session_theme_changed(struct session *); /* utf8.c */ enum utf8_state utf8_towc (const struct utf8_data *, wchar_t *); diff --git a/tty-keys.c b/tty-keys.c index 77ab4ae1..45175171 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -213,6 +213,10 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = { /* Extended keys. */ { "\033[1;5Z", '\011'|KEYC_CTRL|KEYC_SHIFT }, + + /* Theme reporting. */ + { "\033[?997;1n", KEYC_REPORT_DARK_THEME }, + { "\033[?997;2n", KEYC_REPORT_LIGHT_THEME }, }; /* Default xterm keys. */ @@ -791,10 +795,12 @@ tty_keys_next(struct tty *tty) switch (tty_keys_colours(tty, buf, len, &size, &tty->fg, &tty->bg)) { case 0: /* yes */ key = KEYC_UNKNOWN; + session_theme_changed(tty->client->session); goto complete_key; case -1: /* no, or not valid */ break; case 1: /* partial */ + session_theme_changed(tty->client->session); goto partial_key; } diff --git a/tty.c b/tty.c index 0080b7c6..fc74a164 100644 --- a/tty.c +++ b/tty.c @@ -351,6 +351,11 @@ tty_start_tty(struct tty *tty) if (tty_term_has(tty->term, TTYC_ENBP)) tty_putcode(tty, TTYC_ENBP); + if (tty->term->flags & TERM_VT100LIKE) { + /* Subscribe to theme changes and request theme now. */ + tty_puts(tty, "\033[?2031h\033[?996n"); + } + evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); evtimer_add(&tty->start_timer, &tv); @@ -463,6 +468,9 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG)); tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); + if (tty->term->flags & TERM_VT100LIKE) + tty_raw(tty, "\033[?2031l"); + setblocking(c->fd, 1); } diff --git a/window.c b/window.c index dc220462..3bb67f00 100644 --- a/window.c +++ b/window.c @@ -943,7 +943,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp = xcalloc(1, sizeof *wp); wp->window = w; wp->options = options_create(w->options); - wp->flags = PANE_STYLECHANGED; + wp->flags = (PANE_STYLECHANGED|PANE_THEMECHANGED); wp->id = next_window_pane_id++; RB_INSERT(window_pane_tree, &all_window_panes, wp); @@ -1794,3 +1794,162 @@ window_pane_show_scrollbar(struct window_pane *wp, int sb_option) return (1); return (0); } + +int +window_pane_get_bg(struct window_pane *wp) +{ + int c; + struct grid_cell defaults; + + c = window_pane_get_bg_control_client(wp); + if (c == -1) { + tty_default_colours(&defaults, wp); + if (COLOUR_DEFAULT(defaults.bg)) + c = window_get_bg_client(wp); + else + c = defaults.bg; + } + return (c); +} + +/* Get a client with a background for the pane. */ +int +window_get_bg_client(struct window_pane *wp) +{ + struct window *w = wp->window; + struct client *loop; + + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->flags & CLIENT_UNATTACHEDFLAGS) + continue; + if (loop->session == NULL || !session_has(loop->session, w)) + continue; + if (loop->tty.bg == -1) + continue; + return (loop->tty.bg); + } + return (-1); +} + +/* + * If any control mode client exists that has provided a bg color, return it. + * Otherwise, return -1. + */ +int +window_pane_get_bg_control_client(struct window_pane *wp) +{ + struct client *c; + + if (wp->control_bg == -1) + return (-1); + + TAILQ_FOREACH(c, &clients, entry) { + if (c->flags & CLIENT_CONTROL) + return (wp->control_bg); + } + return (-1); +} + +/* + * Get a client with a foreground for the pane. There isn't much to choose + * between them so just use the first. + */ +int +window_pane_get_fg(struct window_pane *wp) +{ + struct window *w = wp->window; + struct client *loop; + + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->flags & CLIENT_UNATTACHEDFLAGS) + continue; + if (loop->session == NULL || !session_has(loop->session, w)) + continue; + if (loop->tty.fg == -1) + continue; + return (loop->tty.fg); + } + return (-1); +} + +/* + * If any control mode client exists that has provided a fg color, return it. + * Otherwise, return -1. + */ +int +window_pane_get_fg_control_client(struct window_pane *wp) +{ + struct client *c; + + if (wp->control_fg == -1) + return (-1); + + TAILQ_FOREACH(c, &clients, entry) { + if (c->flags & CLIENT_CONTROL) + return (wp->control_fg); + } + return (-1); +} + +enum client_theme +window_pane_get_theme(struct window_pane *wp) +{ + struct window *w = wp->window; + struct client *loop; + enum client_theme theme; + int found_light = 0, found_dark = 0; + + /* + * Derive theme from pane background color, if it's not the default + * colour. + */ + theme = colour_totheme(window_pane_get_bg(wp)); + if (theme != THEME_UNKNOWN) + return (theme); + + /* Try to find a client that has a theme. */ + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->flags & CLIENT_UNATTACHEDFLAGS) + continue; + if (loop->session == NULL || !session_has(loop->session, w)) + continue; + switch (loop->theme) { + case THEME_LIGHT: + found_light = 1; + break; + case THEME_DARK: + found_dark = 1; + break; + case THEME_UNKNOWN: + break; + } + } + + if (found_dark && !found_light) + return (THEME_DARK); + if (found_light && !found_dark) + return (THEME_LIGHT); + return (THEME_UNKNOWN); +} + +void +window_pane_send_theme_update(struct window_pane *wp) +{ + if (~wp->flags & PANE_THEMECHANGED) + return; + if (~wp->screen->mode & MODE_THEME_UPDATES) + return; + + switch (window_pane_get_theme(wp)) { + case THEME_LIGHT: + input_key_pane(wp, KEYC_REPORT_LIGHT_THEME, NULL); + break; + case THEME_DARK: + input_key_pane(wp, KEYC_REPORT_DARK_THEME, NULL); + break; + case THEME_UNKNOWN: + break; + } + + wp->flags &= ~PANE_THEMECHANGED; +}