From 8636848e6348bb0e38cd6aaaadbe61e15181bc8f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Aug 2023 14:10:28 +0000 Subject: [PATCH] Add a session, pane and user mouse range types for the status line and add format variables for mouse_status_line and mouse_status_range so they can be associated with different commands in the key bindings. GitHub issue 3652. --- cmd.c | 12 ++++++--- format-draw.c | 50 +++++++++++++++++++++++++++++----- format.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ server-client.c | 41 ++++++++++++++++++++++------ style.c | 64 ++++++++++++++++++++++++++++++++++++++----- tmux.1 | 46 ++++++++++++++++++++++++++----- tmux.h | 7 ++++- 7 files changed, 260 insertions(+), 32 deletions(-) diff --git a/cmd.c b/cmd.c index cbdb7c15..c176edbd 100644 --- a/cmd.c +++ b/cmd.c @@ -813,10 +813,14 @@ cmd_mouse_pane(struct mouse_event *m, struct session **sp, if ((wl = cmd_mouse_window(m, sp)) == NULL) return (NULL); - if ((wp = window_pane_find_by_id(m->wp)) == NULL) - return (NULL); - if (!window_has_pane(wl->window, wp)) - return (NULL); + if (m->wp == -1) + wp = wl->window->active; + else { + if ((wp = window_pane_find_by_id(m->wp)) == NULL) + return (NULL); + if (!window_has_pane(wl->window, wp)) + return (NULL); + } if (wlp != NULL) *wlp = wl; diff --git a/format-draw.c b/format-draw.c index 3ea1f52f..a42dfe1d 100644 --- a/format-draw.c +++ b/format-draw.c @@ -33,6 +33,7 @@ struct format_range { enum style_range_type type; u_int argument; + char string[16]; TAILQ_ENTRY(format_range) entry; }; @@ -44,9 +45,18 @@ format_is_type(struct format_range *fr, struct style *sy) { if (fr->type != sy->range_type) return (0); - if (fr->type == STYLE_RANGE_WINDOW && - fr->argument != sy->range_argument) - return (0); + switch (fr->type) { + case STYLE_RANGE_NONE: + case STYLE_RANGE_LEFT: + case STYLE_RANGE_RIGHT: + return (1); + case STYLE_RANGE_PANE: + case STYLE_RANGE_WINDOW: + case STYLE_RANGE_SESSION: + return (fr->argument == sy->range_argument); + case STYLE_RANGE_USER: + return (strcmp(fr->string, sy->range_string) == 0); + } return (1); } @@ -942,6 +952,8 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, fr->type = sy.range_type; fr->argument = sy.range_argument; + strlcpy(fr->string, sy.range_string, + sizeof fr->string); } } @@ -1013,13 +1025,39 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, sr = xcalloc(1, sizeof *sr); sr->type = fr->type; sr->argument = fr->argument; + strlcpy(sr->string, fr->string, sizeof sr->string); sr->start = fr->start; sr->end = fr->end; TAILQ_INSERT_TAIL(srs, sr, entry); - log_debug("%s: range %d|%u at %u-%u", __func__, sr->type, - sr->argument, sr->start, sr->end); - + switch (sr->type) { + case STYLE_RANGE_NONE: + break; + case STYLE_RANGE_LEFT: + log_debug("%s: range left at %u-%u", __func__, + sr->start, sr->end); + break; + case STYLE_RANGE_RIGHT: + log_debug("%s: range right at %u-%u", __func__, + sr->start, sr->end); + break; + case STYLE_RANGE_PANE: + log_debug("%s: range pane|%%%u at %u-%u", __func__, + sr->argument, sr->start, sr->end); + break; + case STYLE_RANGE_WINDOW: + log_debug("%s: range window|%u at %u-%u", __func__, + sr->argument, sr->start, sr->end); + break; + case STYLE_RANGE_SESSION: + log_debug("%s: range session|$%u at %u-%u", __func__, + sr->argument, sr->start, sr->end); + break; + case STYLE_RANGE_USER: + log_debug("%s: range user|%u at %u-%u", __func__, + sr->argument, sr->start, sr->end); + break; + } format_free_range(&frs, fr); } diff --git a/format.c b/format.c index 0c3bd3ba..6e9094c2 100644 --- a/format.c +++ b/format.c @@ -1191,6 +1191,72 @@ format_cb_mouse_line(struct format_tree *ft) return (format_grid_line(gd, gd->hsize + y)); } +/* Callback for mouse_status_line. */ +static void * +format_cb_mouse_status_line(struct format_tree *ft) +{ + char *value; + u_int y; + + if (!ft->m.valid) + return (NULL); + if (ft->c == NULL || (~ft->c->tty.flags & TTY_STARTED)) + return (NULL); + + if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines) { + y = ft->m.y; + } else if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat) { + y = ft->m.y - ft->m.statusat; + } else + return (NULL); + xasprintf(&value, "%u", y); + return (value); + +} + +/* Callback for mouse_status_range. */ +static void * +format_cb_mouse_status_range(struct format_tree *ft) +{ + struct style_range *sr; + u_int x, y; + + if (!ft->m.valid) + return (NULL); + if (ft->c == NULL || (~ft->c->tty.flags & TTY_STARTED)) + return (NULL); + + if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines) { + x = ft->m.x; + y = ft->m.y; + } else if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat) { + x = ft->m.x; + y = ft->m.y - ft->m.statusat; + } else + return (NULL); + + sr = status_get_range(ft->c, x, y); + if (sr == NULL) + return (NULL); + switch (sr->type) { + case STYLE_RANGE_NONE: + return (NULL); + case STYLE_RANGE_LEFT: + return (xstrdup("left")); + case STYLE_RANGE_RIGHT: + return (xstrdup("right")); + case STYLE_RANGE_PANE: + return (xstrdup("pane")); + case STYLE_RANGE_WINDOW: + return (xstrdup("window")); + case STYLE_RANGE_SESSION: + return (xstrdup("session")); + case STYLE_RANGE_USER: + return (xstrdup(sr->string)); + } + return (NULL); +} + /* Callback for alternate_on. */ static void * format_cb_alternate_on(struct format_tree *ft) @@ -2848,6 +2914,12 @@ static const struct format_table_entry format_table[] = { { "mouse_standard_flag", FORMAT_TABLE_STRING, format_cb_mouse_standard_flag }, + { "mouse_status_line", FORMAT_TABLE_STRING, + format_cb_mouse_status_line + }, + { "mouse_status_range", FORMAT_TABLE_STRING, + format_cb_mouse_status_range + }, { "mouse_utf8_flag", FORMAT_TABLE_STRING, format_cb_mouse_utf8_flag }, diff --git a/server-client.c b/server-client.c index 1c927bb5..0f26ad37 100644 --- a/server-client.c +++ b/server-client.c @@ -564,9 +564,9 @@ static key_code server_client_check_mouse(struct client *c, struct key_event *event) { struct mouse_event *m = &event->m; - struct session *s = c->session; - struct winlink *wl; - struct window_pane *wp; + struct session *s = c->session, *fs; + struct winlink *fwl; + struct window_pane *wp, *fwp; u_int x, y, b, sx, sy, px, py; int ignore = 0; key_code key; @@ -672,6 +672,7 @@ have_event: /* Save the session. */ m->s = s->id; m->w = -1; + m->wp = -1; m->ignore = ignore; /* Is this on the status line? */ @@ -688,18 +689,42 @@ have_event: case STYLE_RANGE_NONE: return (KEYC_UNKNOWN); case STYLE_RANGE_LEFT: + log_debug("mouse range: left"); where = STATUS_LEFT; break; case STYLE_RANGE_RIGHT: + log_debug("mouse range: right"); where = STATUS_RIGHT; break; - case STYLE_RANGE_WINDOW: - wl = winlink_find_by_index(&s->windows, - sr->argument); - if (wl == NULL) + case STYLE_RANGE_PANE: + fwp = window_pane_find_by_id(sr->argument); + if (fwp == NULL) return (KEYC_UNKNOWN); - m->w = wl->window->id; + m->wp = sr->argument; + log_debug("mouse range: pane %%%u", m->wp); + where = STATUS; + break; + case STYLE_RANGE_WINDOW: + fwl = winlink_find_by_index(&s->windows, + sr->argument); + if (fwl == NULL) + return (KEYC_UNKNOWN); + m->w = fwl->window->id; + + log_debug("mouse range: window @%u", m->w); + where = STATUS; + break; + case STYLE_RANGE_SESSION: + fs = session_find_by_id(sr->argument); + if (fs == NULL) + return (KEYC_UNKNOWN); + m->s = sr->argument; + + log_debug("mouse range: session $%u", m->s); + where = STATUS; + break; + case STYLE_RANGE_USER: where = STATUS; break; } diff --git a/style.c b/style.c index 3d9d317d..0d7ebe86 100644 --- a/style.c +++ b/style.c @@ -37,11 +37,18 @@ static struct style style_default = { STYLE_ALIGN_DEFAULT, STYLE_LIST_OFF, - STYLE_RANGE_NONE, 0, + STYLE_RANGE_NONE, 0, "", STYLE_DEFAULT_BASE }; +/* Set range string. */ +static void +style_set_range_string(struct style *sy, const char *s) +{ + strlcpy(sy->range_string, s, sizeof sy->range_string); +} + /* * Parse an embedded style of the form "fg=colour,bg=colour,bright,...". Note * that this adds onto the given style, so it must have been initialized @@ -104,32 +111,67 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) } else if (strcasecmp(tmp, "norange") == 0) { sy->range_type = style_default.range_type; sy->range_argument = style_default.range_type; + strlcpy(sy->range_string, style_default.range_string, + sizeof sy->range_string); } else if (end > 6 && strncasecmp(tmp, "range=", 6) == 0) { found = strchr(tmp + 6, '|'); if (found != NULL) { *found++ = '\0'; if (*found == '\0') goto error; - for (cp = found; *cp != '\0'; cp++) { - if (!isdigit((u_char)*cp)) - goto error; - } } if (strcasecmp(tmp + 6, "left") == 0) { if (found != NULL) goto error; sy->range_type = STYLE_RANGE_LEFT; sy->range_argument = 0; + style_set_range_string(sy, ""); } else if (strcasecmp(tmp + 6, "right") == 0) { if (found != NULL) goto error; sy->range_type = STYLE_RANGE_RIGHT; sy->range_argument = 0; + style_set_range_string(sy, ""); + } else if (strcasecmp(tmp + 6, "pane") == 0) { + if (found == NULL) + goto error; + if (*found != '%' || found[1] == '\0') + goto error; + for (cp = found + 1; *cp != '\0'; cp++) { + if (!isdigit((u_char)*cp)) + goto error; + } + sy->range_type = STYLE_RANGE_PANE; + sy->range_argument = atoi(found + 1); + style_set_range_string(sy, ""); } else if (strcasecmp(tmp + 6, "window") == 0) { if (found == NULL) goto error; + for (cp = found; *cp != '\0'; cp++) { + if (!isdigit((u_char)*cp)) + goto error; + } sy->range_type = STYLE_RANGE_WINDOW; sy->range_argument = atoi(found); + style_set_range_string(sy, ""); + } else if (strcasecmp(tmp + 6, "session") == 0) { + if (found == NULL) + goto error; + if (*found != '$' || found[1] == '\0') + goto error; + for (cp = found + 1; *cp != '\0'; cp++) { + if (!isdigit((u_char)*cp)) + goto error; + } + sy->range_type = STYLE_RANGE_SESSION; + sy->range_argument = atoi(found + 1); + style_set_range_string(sy, ""); + } else if (strcasecmp(tmp + 6, "user") == 0) { + if (found == NULL) + goto error; + sy->range_type = STYLE_RANGE_USER; + sy->range_argument = 0; + style_set_range_string(sy, found); } } else if (strcasecmp(tmp, "noalign") == 0) sy->align = style_default.align; @@ -222,9 +264,19 @@ style_tostring(struct style *sy) tmp = "left"; else if (sy->range_type == STYLE_RANGE_RIGHT) tmp = "right"; - else if (sy->range_type == STYLE_RANGE_WINDOW) { + else if (sy->range_type == STYLE_RANGE_PANE) { + snprintf(b, sizeof b, "pane|%%%u", sy->range_argument); + tmp = b; + } else if (sy->range_type == STYLE_RANGE_WINDOW) { snprintf(b, sizeof b, "window|%u", sy->range_argument); tmp = b; + } else if (sy->range_type == STYLE_RANGE_SESSION) { + snprintf(b, sizeof b, "session|$%u", + sy->range_argument); + tmp = b; + } else if (sy->range_type == STYLE_RANGE_USER) { + snprintf(b, sizeof b, "user|%s", sy->range_string); + tmp = b; } off += xsnprintf(s + off, sizeof s - off, "%srange=%s", comma, tmp); diff --git a/tmux.1 b/tmux.1 index 8191b6dc..7836e5f5 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5443,6 +5443,8 @@ The following variables are available, where appropriate: .It Li "mouse_line" Ta "" Ta "Line under mouse, if any" .It Li "mouse_sgr_flag" Ta "" Ta "Pane mouse SGR flag" .It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag" +.It Li "mouse_status_line" Ta "" Ta "Status line on which mouse event took place" +.It Li "mouse_status_range" Ta "" Ta "Range type or argument of mouse event on status line" .It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag" .It Li "mouse_word" Ta "" Ta "Word under mouse, if any" .It Li "mouse_x" Ta "" Ta "Mouse X position, if any" @@ -5681,26 +5683,56 @@ Only one default may be pushed (each replaces the previous saved default). .It Xo Ic range=left , .Ic range=right , +.Ic range=session|X , .Ic range=window|X , +.Ic range=pane|X , +.Ic range=user|X , .Ic norange .Xc -Mark a range in the +Mark a range for mouse events in the .Ic status-format option. +When a mouse event occurs in the .Ic range=left -and +or .Ic range=right -are the text used for the +range, the .Ql StatusLeft and .Ql StatusRight -mouse keys. +key bindings are triggered. +.Pp +.Ic range=session|X , .Ic range=window|X -is the range for a window passed to the +and +.Ic range=pane|X +are ranges for a session, window or pane. +These trigger the .Ql Status -mouse key, where +mouse key with the target session, window or pane given by the .Ql X -is a window index. +argument. +.Ql X +is a session ID, window index in the current session or a pane ID. +For these, the +.Ic mouse_status_range +format variable will be set to +.Ql session , +.Ql window +or +.Ql pane . +.Pp +.Ic range=user|X +is a user-defined range; it triggers the +.Ql Status +mouse key. +The argument +.Ql X +will be available in the +.Ic mouse_status_range +format variable. +.Ql X +must be at most 15 bytes in length. .El .Pp Examples are: diff --git a/tmux.h b/tmux.h index 378871b7..d95ba597 100644 --- a/tmux.h +++ b/tmux.h @@ -795,11 +795,15 @@ enum style_range_type { STYLE_RANGE_NONE, STYLE_RANGE_LEFT, STYLE_RANGE_RIGHT, - STYLE_RANGE_WINDOW + STYLE_RANGE_PANE, + STYLE_RANGE_WINDOW, + STYLE_RANGE_SESSION, + STYLE_RANGE_USER }; struct style_range { enum style_range_type type; u_int argument; + char string[16]; u_int start; u_int end; /* not included */ @@ -826,6 +830,7 @@ struct style { enum style_range_type range_type; u_int range_argument; + char range_string[16]; enum style_default_type default_type; };