Coverage Report

Created: 2026-07-16 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/status.c
Line
Count
Source
1
/* $OpenBSD: status.c,v 1.273 2026/07/06 14:29:10 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
#include <sys/time.h>
21
22
#include <errno.h>
23
#include <limits.h>
24
#include <stdarg.h>
25
#include <stdlib.h>
26
#include <string.h>
27
#include <time.h>
28
#include <unistd.h>
29
30
#include "tmux.h"
31
32
static void  status_message_area(struct client *, u_int *, u_int *);
33
static void  status_message_callback(int, short, void *);
34
static void  status_timer_callback(int, short, void *);
35
36
/* Status timer callback. */
37
static void
38
status_timer_callback(__unused int fd, __unused short events, void *arg)
39
0
{
40
0
  struct client *c = arg;
41
0
  struct session  *s = c->session;
42
0
  struct timeval   tv;
43
44
0
  evtimer_del(&c->status.timer);
45
46
0
  if (s == NULL)
47
0
    return;
48
49
0
  if (c->message_string == NULL && c->prompt == NULL)
50
0
    c->flags |= CLIENT_REDRAWSTATUS;
51
52
0
  timerclear(&tv);
53
0
  tv.tv_sec = options_get_number(s->options, "status-interval");
54
55
0
  if (tv.tv_sec != 0)
56
0
    evtimer_add(&c->status.timer, &tv);
57
0
  log_debug("client %p, status interval %d", c, (int)tv.tv_sec);
58
0
}
59
60
/* Start status timer for client. */
61
void
62
status_timer_start(struct client *c)
63
0
{
64
0
  struct session  *s = c->session;
65
66
0
  if (event_initialized(&c->status.timer))
67
0
    evtimer_del(&c->status.timer);
68
0
  else
69
0
    evtimer_set(&c->status.timer, status_timer_callback, c);
70
71
0
  if (s != NULL && options_get_number(s->options, "status"))
72
0
    status_timer_callback(-1, 0, c);
73
0
}
74
75
/* Start status timer for all clients. */
76
void
77
status_timer_start_all(void)
78
0
{
79
0
  struct client *c;
80
81
0
  TAILQ_FOREACH(c, &clients, entry)
82
0
    status_timer_start(c);
83
0
}
84
85
/* Update status cache. */
86
void
87
status_update_cache(struct session *s)
88
0
{
89
0
  s->statuslines = options_get_number(s->options, "status");
90
0
  if (s->statuslines == 0)
91
0
    s->statusat = -1;
92
0
  else if (options_get_number(s->options, "status-position") == 0)
93
0
    s->statusat = 0;
94
0
  else
95
0
    s->statusat = 1;
96
0
}
97
98
/* Get screen line of status line. -1 means off. */
99
int
100
status_at_line(struct client *c)
101
0
{
102
0
  struct session  *s = c->session;
103
104
0
  if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL))
105
0
    return (-1);
106
0
  if (s->statusat != 1)
107
0
    return (s->statusat);
108
0
  return (c->tty.sy - status_line_size(c));
109
0
}
110
111
/* Get size of status line for client's session. 0 means off. */
112
u_int
113
status_line_size(struct client *c)
114
0
{
115
0
  struct session  *s = c->session;
116
117
0
  if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL))
118
0
    return (0);
119
0
  if (s == NULL)
120
0
    return (options_get_number(global_s_options, "status"));
121
0
  return (s->statuslines);
122
0
}
123
124
/* Get the prompt line number for client's session. 1 means at the bottom. */
125
u_int
126
status_prompt_line_at(struct client *c)
127
0
{
128
0
  struct session  *s = c->session;
129
0
  u_int    line, lines;
130
131
0
  lines = status_line_size(c);
132
0
  if (lines == 0)
133
0
    return (0);
134
0
  line = options_get_number(s->options, "message-line");
135
0
  if (line >= lines)
136
0
    return (lines - 1);
137
0
  return (line);
138
0
}
139
140
/* Get window at window list position. */
141
struct style_range *
142
status_get_range(struct client *c, u_int x, u_int y)
143
0
{
144
0
  struct status_line  *sl = &c->status;
145
146
0
  if (y >= nitems(sl->entries))
147
0
    return (NULL);
148
0
  return (style_ranges_get_range(&sl->entries[y].ranges, x));
149
0
}
150
151
/* Save old status line. */
152
static void
153
status_push_screen(struct client *c)
154
0
{
155
0
  struct status_line *sl = &c->status;
156
157
0
  if (sl->active == &sl->screen) {
158
0
    sl->active = xmalloc(sizeof *sl->active);
159
0
    screen_init(sl->active, c->tty.sx, status_line_size(c), 0);
160
0
  }
161
0
  sl->references++;
162
0
}
163
164
/* Restore old status line. */
165
static void
166
status_pop_screen(struct client *c)
167
0
{
168
0
  struct status_line *sl = &c->status;
169
170
0
  if (--sl->references == 0) {
171
0
    screen_free(sl->active);
172
0
    free(sl->active);
173
0
    sl->active = &sl->screen;
174
0
  }
175
0
}
176
177
/* Initialize status line. */
178
void
179
status_init(struct client *c)
180
0
{
181
0
  struct status_line  *sl = &c->status;
182
0
  u_int      i;
183
184
0
  for (i = 0; i < nitems(sl->entries); i++)
185
0
    style_ranges_init(&sl->entries[i].ranges);
186
187
0
  screen_init(&sl->screen, c->tty.sx, 1, 0);
188
0
  sl->active = &sl->screen;
189
0
}
190
191
/* Free status line. */
192
void
193
status_free(struct client *c)
194
0
{
195
0
  struct status_line  *sl = &c->status;
196
0
  u_int      i;
197
198
0
  for (i = 0; i < nitems(sl->entries); i++) {
199
0
    style_ranges_free(&sl->entries[i].ranges);
200
0
    free((void *)sl->entries[i].expanded);
201
0
  }
202
203
0
  if (event_initialized(&sl->timer))
204
0
    evtimer_del(&sl->timer);
205
206
0
  if (sl->active != &sl->screen) {
207
0
    screen_free(sl->active);
208
0
    free(sl->active);
209
0
  }
210
0
  screen_free(&sl->screen);
211
0
}
212
213
/* Draw status line for client. */
214
int
215
status_redraw(struct client *c)
216
0
{
217
0
  struct status_line    *sl = &c->status;
218
0
  struct style_line_entry   *sle;
219
0
  struct session      *s = c->session;
220
0
  struct screen_write_ctx    ctx;
221
0
  struct grid_cell     gc;
222
0
  u_int        lines, i, n, width = c->tty.sx;
223
0
  int        flags, force = 0, changed = 0, fg, bg;
224
0
  struct options_entry    *o;
225
0
  union options_value   *ov;
226
0
  struct format_tree    *ft;
227
0
  char        *expanded;
228
229
0
  log_debug("%s enter", __func__);
230
231
  /* Shouldn't get here if not the active screen. */
232
0
  if (sl->active != &sl->screen)
233
0
    fatalx("not the active screen");
234
235
  /* No status line? */
236
0
  lines = status_line_size(c);
237
0
  if (c->tty.sy == 0 || lines == 0)
238
0
    return (1);
239
240
  /* Create format tree. */
241
0
  flags = FORMAT_STATUS;
242
0
  if (c->flags & CLIENT_STATUSFORCE)
243
0
    flags |= FORMAT_FORCE;
244
0
  ft = format_create(c, NULL, FORMAT_NONE, flags);
245
0
  format_defaults(ft, c, NULL, NULL, NULL);
246
247
  /* Set up default colour. */
248
0
  style_apply(&gc, s->options, "status-style", ft);
249
0
  fg = options_get_number(s->options, "status-fg");
250
0
  if (!COLOUR_DEFAULT(fg))
251
0
    gc.fg = fg;
252
0
  bg = options_get_number(s->options, "status-bg");
253
0
  if (!COLOUR_DEFAULT(bg))
254
0
    gc.bg = bg;
255
0
  if (!grid_cells_equal(&gc, &sl->style)) {
256
0
    force = 1;
257
0
    memcpy(&sl->style, &gc, sizeof sl->style);
258
0
  }
259
260
  /* Resize the target screen. */
261
0
  if (screen_size_x(&sl->screen) != width ||
262
0
      screen_size_y(&sl->screen) != lines) {
263
0
    screen_resize(&sl->screen, width, lines, 0);
264
0
    changed = force = 1;
265
0
  }
266
0
  screen_write_start(&ctx, &sl->screen);
267
268
  /* Write the status lines. */
269
0
  o = options_get(s->options, "status-format");
270
0
  if (o == NULL) {
271
0
    for (n = 0; n < width * lines; n++)
272
0
      screen_write_putc(&ctx, &gc, ' ');
273
0
  } else {
274
0
    for (i = 0; i < lines; i++) {
275
0
      screen_write_cursormove(&ctx, 0, i, 0);
276
277
0
      ov = options_array_getv(o, "%u", i);
278
0
      if (ov == NULL) {
279
0
        for (n = 0; n < width; n++)
280
0
          screen_write_putc(&ctx, &gc, ' ');
281
0
        continue;
282
0
      }
283
0
      sle = &sl->entries[i];
284
285
0
      expanded = format_expand_time(ft, ov->string);
286
0
      if (!force &&
287
0
          sle->expanded != NULL &&
288
0
          strcmp(expanded, sle->expanded) == 0) {
289
0
        free(expanded);
290
0
        continue;
291
0
      }
292
0
      changed = 1;
293
294
0
      for (n = 0; n < width; n++)
295
0
        screen_write_putc(&ctx, &gc, ' ');
296
0
      screen_write_cursormove(&ctx, 0, i, 0);
297
298
0
      style_ranges_free(&sle->ranges);
299
0
      format_draw(&ctx, &gc, width, expanded, &sle->ranges,
300
0
          0);
301
302
0
      free(sle->expanded);
303
0
      sle->expanded = expanded;
304
0
    }
305
0
  }
306
0
  screen_write_stop(&ctx);
307
308
  /* Free the format tree. */
309
0
  format_free(ft);
310
311
  /* Return if the status line has changed. */
312
0
  log_debug("%s exit: force=%d, changed=%d", __func__, force, changed);
313
0
  return (force || changed);
314
0
}
315
316
/* Escape # characters in a string so format_draw treats them as literal. */
317
static char *
318
status_message_escape(const char *s)
319
0
{
320
0
  const char  *cp;
321
0
  char    *out, *p;
322
0
  size_t     n = 0;
323
324
0
  for (cp = s; *cp != '\0'; cp++) {
325
0
    if (*cp == '#')
326
0
      n++;
327
0
  }
328
0
  p = out = xmalloc(strlen(s) + n + 1);
329
0
  for (cp = s; *cp != '\0'; cp++) {
330
0
    if (*cp == '#')
331
0
      *p++ = '#';
332
0
    *p++ = *cp;
333
0
  }
334
0
  *p = '\0';
335
0
  return (out);
336
0
}
337
338
/* Set a status line message. */
339
void
340
status_message_set(struct client *c, int delay, int ignore_styles,
341
    int ignore_keys, int no_freeze, const char *fmt, ...)
342
0
{
343
0
  struct timeval   tv;
344
0
  va_list    ap;
345
0
  char    *s;
346
347
0
  va_start(ap, fmt);
348
0
  xvasprintf(&s, fmt, ap);
349
0
  va_end(ap);
350
351
0
  log_debug("%s: %s", __func__, s);
352
353
0
  if (c == NULL) {
354
0
    server_add_message("message: %s", s);
355
0
    free(s);
356
0
    return;
357
0
  }
358
359
0
  status_message_clear(c);
360
0
  status_push_screen(c);
361
0
  c->message_string = s;
362
0
  server_add_message("%s message: %s", c->name, s);
363
364
  /*
365
   * With delay -1, the display-time option is used; zero means wait for
366
   * key press; more than zero is the actual delay time in milliseconds.
367
   */
368
0
  if (delay == -1)
369
0
    delay = options_get_number(c->session->options, "display-time");
370
0
  if (delay > 0) {
371
0
    tv.tv_sec = delay / 1000;
372
0
    tv.tv_usec = (delay % 1000) * 1000L;
373
374
0
    if (event_initialized(&c->message_timer))
375
0
      evtimer_del(&c->message_timer);
376
0
    evtimer_set(&c->message_timer, status_message_callback, c);
377
378
0
    evtimer_add(&c->message_timer, &tv);
379
0
  }
380
381
0
  if (delay != 0)
382
0
    c->message_ignore_keys = ignore_keys;
383
0
  c->message_ignore_styles = ignore_styles;
384
385
0
  if (!no_freeze)
386
0
    c->tty.flags |= TTY_FREEZE;
387
0
  c->tty.flags |= TTY_NOCURSOR;
388
0
  c->flags |= CLIENT_REDRAWSTATUS;
389
0
}
390
391
/* Clear status line message. */
392
void
393
status_message_clear(struct client *c)
394
0
{
395
0
  if (c->message_string == NULL)
396
0
    return;
397
398
0
  free(c->message_string);
399
0
  c->message_string = NULL;
400
401
0
  if (c->prompt == NULL)
402
0
    c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
403
0
  c->flags |= CLIENT_ALLREDRAWFLAGS; /* was frozen and may have changed */
404
405
0
  status_pop_screen(c);
406
0
}
407
408
/*
409
 * Calculate prompt/message area geometry from the style's width and align
410
 * directives: x offset and available width within the status line.
411
 */
412
static void
413
status_message_area(struct client *c, u_int *area_x, u_int *area_w)
414
0
{
415
0
  struct session    *s = c->session;
416
0
  struct style    *sy;
417
0
  u_int      w;
418
419
  /* Get width from message-style's width directive. */
420
0
  sy = options_string_to_style(s->options, "message-style", NULL);
421
0
  if (sy != NULL && sy->width >= 0) {
422
0
    if (sy->width_percentage)
423
0
      w = (c->tty.sx * (u_int)sy->width) / 100;
424
0
    else
425
0
      w = (u_int)sy->width;
426
0
  } else
427
0
    w = c->tty.sx;
428
0
  if (w == 0 || w > c->tty.sx)
429
0
    w = c->tty.sx;
430
431
  /* Get horizontal position from message-style's align directive. */
432
0
  if (sy != NULL) {
433
0
    switch (sy->align) {
434
0
    case STYLE_ALIGN_CENTRE:
435
0
    case STYLE_ALIGN_ABSOLUTE_CENTRE:
436
0
      *area_x = (c->tty.sx - w) / 2;
437
0
      break;
438
0
    case STYLE_ALIGN_RIGHT:
439
0
      *area_x = c->tty.sx - w;
440
0
      break;
441
0
    default:
442
0
      *area_x = 0;
443
0
      break;
444
0
    }
445
0
  } else
446
0
    *area_x = 0;
447
448
0
  *area_w = w;
449
0
}
450
451
/* Clear status line message after timer expires. */
452
static void
453
status_message_callback(__unused int fd, __unused short event, void *data)
454
0
{
455
0
  struct client *c = data;
456
457
0
  status_message_clear(c);
458
0
}
459
460
/* Draw client message on status line of present else on last line. */
461
int
462
status_message_redraw(struct client *c)
463
0
{
464
0
  struct status_line  *sl = &c->status;
465
0
  struct screen_write_ctx  ctx;
466
0
  struct session    *s = c->session;
467
0
  struct screen    old_screen;
468
0
  u_int      lines, messageline;
469
0
  u_int      ax, aw;
470
0
  struct grid_cell   gc;
471
0
  struct format_tree  *ft;
472
0
  const char    *msgfmt;
473
0
  char      *expanded, *msg;
474
475
0
  if (c->tty.sx == 0 || c->tty.sy == 0)
476
0
    return (0);
477
0
  memcpy(&old_screen, sl->active, sizeof old_screen);
478
479
0
  lines = status_line_size(c);
480
0
  if (lines <= 1)
481
0
    lines = 1;
482
0
  screen_init(sl->active, c->tty.sx, lines, 0);
483
484
0
  messageline = status_prompt_line_at(c);
485
0
  if (messageline > lines - 1)
486
0
    messageline = lines - 1;
487
488
0
  status_message_area(c, &ax, &aw);
489
490
0
  ft = format_create_defaults(NULL, c, NULL, NULL, NULL);
491
0
  style_apply(&gc, s->options, "message-style", ft);
492
493
  /*
494
   * Set #{message} in the format tree. If styles should be ignored in
495
   * the message content, escape # characters so format_draw treats them
496
   * as literal text.
497
   */
498
0
  if (c->message_ignore_styles) {
499
0
    msg = status_message_escape(c->message_string);
500
0
    format_add(ft, "message", "%s", msg);
501
0
    free(msg);
502
0
  } else
503
0
    format_add(ft, "message", "%s", c->message_string);
504
0
  format_add(ft, "command_prompt", "%d", 0);
505
506
0
  msgfmt = options_get_string(s->options, "message-format");
507
0
  expanded = format_expand_time(ft, msgfmt);
508
0
  format_free(ft);
509
510
0
  screen_write_start(&ctx, sl->active);
511
0
  screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines);
512
0
  screen_write_cursormove(&ctx, ax, messageline, 0);
513
0
  format_draw(&ctx, &gc, aw, expanded, NULL, 0);
514
0
  screen_write_stop(&ctx);
515
516
0
  free(expanded);
517
518
0
  if (grid_compare(sl->active->grid, old_screen.grid) == 0) {
519
0
    screen_free(&old_screen);
520
0
    return (0);
521
0
  }
522
0
  screen_free(&old_screen);
523
0
  return (1);
524
0
}
525
526
527
struct status_prompt_data {
528
  struct client   *c;
529
  status_prompt_input_cb   inputcb;
530
  prompt_free_cb     freecb;
531
  void      *data;
532
};
533
534
static enum prompt_result
535
status_prompt_input_callback(void *data, const char *s,
536
    enum prompt_key_result key)
537
0
{
538
0
  struct status_prompt_data *spd = data;
539
0
  struct client     *c = spd->c;
540
0
  status_prompt_input_cb     inputcb = spd->inputcb;
541
0
  void        *arg = spd->data;
542
543
0
  if (inputcb != NULL)
544
0
    return (inputcb(c, arg, s, key));
545
0
  return (PROMPT_CLOSE);
546
0
}
547
548
static void
549
status_prompt_free_callback(void *data)
550
0
{
551
0
  struct status_prompt_data *spd = data;
552
0
  prompt_free_cb       freecb = spd->freecb;
553
0
  void        *arg = spd->data;
554
555
0
  if (freecb != NULL)
556
0
    freecb(arg);
557
0
  free(spd);
558
0
}
559
560
/* Accept prompt immediately. */
561
static enum cmd_retval
562
status_prompt_accept(__unused struct cmdq_item *item, void *data)
563
0
{
564
0
  struct client *c = data;
565
566
0
  if (c->prompt != NULL)
567
0
    status_prompt_key(c, 'y', NULL);
568
0
  return (CMD_RETURN_NORMAL);
569
0
}
570
571
/* Enable status line prompt. */
572
void
573
status_prompt_set(struct client *c, struct cmd_find_state *fs,
574
    const char *msg, const char *input, status_prompt_input_cb inputcb,
575
    prompt_free_cb freecb, void *data, int flags, enum prompt_type prompt_type)
576
0
{
577
0
  struct prompt_create_data pd;
578
0
  struct status_prompt_data *spd;
579
580
0
  server_client_clear_overlay(c);
581
582
0
  status_message_clear(c);
583
0
  status_prompt_clear(c);
584
0
  status_push_screen(c);
585
586
0
  spd = xcalloc(1, sizeof *spd);
587
0
  spd->c = c;
588
0
  spd->inputcb = inputcb;
589
0
  spd->freecb = freecb;
590
0
  spd->data = data;
591
592
0
  memset(&pd, 0, sizeof pd);
593
0
  prompt_set_options(&pd, c->session);
594
0
  pd.fs = fs;
595
0
  pd.prompt = msg;
596
0
  pd.input = input;
597
0
  pd.type = prompt_type;
598
0
  pd.flags = flags;
599
0
  pd.inputcb = status_prompt_input_callback;
600
0
  pd.freecb = status_prompt_free_callback;
601
0
  pd.data = spd;
602
0
  c->prompt = prompt_create(&pd);
603
604
0
  if ((~flags & PROMPT_INCREMENTAL) && (~flags & PROMPT_NOFREEZE))
605
0
    c->tty.flags |= TTY_FREEZE;
606
0
  c->flags |= CLIENT_REDRAWSTATUS;
607
608
0
  prompt_incremental_start(c->prompt);
609
610
0
  if ((flags & PROMPT_SINGLE) && (flags & PROMPT_ACCEPT))
611
0
    cmdq_append(c, cmdq_get_callback(status_prompt_accept, c));
612
0
}
613
614
/* Remove status line prompt. */
615
void
616
status_prompt_clear(struct client *c)
617
0
{
618
0
  if (c->prompt == NULL)
619
0
    return;
620
621
0
  prompt_free(c->prompt);
622
0
  c->prompt = NULL;
623
624
0
  c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
625
0
  c->flags |= CLIENT_ALLREDRAWFLAGS; /* was frozen and may have changed */
626
627
0
  status_pop_screen(c);
628
0
}
629
630
/* Update status line prompt with a new prompt string. */
631
void
632
status_prompt_update(struct client *c, const char *msg, const char *input)
633
0
{
634
0
  if (c->prompt == NULL)
635
0
    return;
636
0
  prompt_update(c->prompt, msg, input);
637
0
  c->flags |= CLIENT_REDRAWSTATUS;
638
0
}
639
640
/* Get the screen line on which the prompt is drawn. */
641
static u_int
642
status_prompt_screen_line(struct client *c)
643
0
{
644
0
  struct tty  *tty = &c->tty;
645
0
  u_int    n;
646
647
0
  if (options_get_number(c->session->options, "status-position") == 0)
648
0
    return (status_prompt_line_at(c));
649
0
  n = status_line_size(c) - status_prompt_line_at(c);
650
0
  if (n <= tty->sy)
651
0
    return (tty->sy - n);
652
0
  return (tty->sy - 1);
653
0
}
654
655
/* Draw client prompt on status line of present else on last line. */
656
int
657
status_prompt_redraw(struct client *c)
658
0
{
659
0
  struct status_line  *sl = &c->status;
660
0
  struct screen_write_ctx  ctx;
661
0
  struct screen    old_screen;
662
0
  struct prompt_draw_data  pdd;
663
0
  u_int      lines, ax, aw, promptline;
664
665
0
  if (c->tty.sx == 0 || c->tty.sy == 0)
666
0
    return (0);
667
0
  memcpy(&old_screen, sl->active, sizeof old_screen);
668
669
0
  lines = status_line_size(c);
670
0
  if (lines <= 1)
671
0
    lines = 1;
672
0
  screen_init(sl->active, c->tty.sx, lines, 0);
673
674
0
  promptline = status_prompt_line_at(c);
675
0
  if (promptline > lines - 1)
676
0
    promptline = lines - 1;
677
678
0
  status_message_area(c, &ax, &aw);
679
680
0
  screen_write_start(&ctx, sl->active);
681
0
  screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines);
682
683
0
  pdd.ctx = &ctx;
684
0
  pdd.area_x = ax;
685
0
  pdd.area_width = aw;
686
0
  pdd.prompt_line = promptline;
687
0
  pdd.cursor_x = &sl->prompt_cx;
688
0
  prompt_draw(c->prompt, &pdd);
689
690
0
  screen_write_stop(&ctx);
691
692
0
  if (grid_compare(sl->active->grid, old_screen.grid) == 0) {
693
0
    screen_free(&old_screen);
694
0
    return (0);
695
0
  }
696
0
  screen_free(&old_screen);
697
0
  return (1);
698
0
}
699
700
/* Work out the tty cursor position for the prompt. */
701
void
702
status_prompt_cursor(struct client *c, u_int *cx, u_int *cy)
703
0
{
704
0
  *cy = status_prompt_screen_line(c);
705
0
  *cx = c->status.prompt_cx;
706
0
}
707
708
/* Handle keys in prompt. */
709
enum prompt_key_result
710
status_prompt_key(struct client *c, key_code key, struct mouse_event *m)
711
0
{
712
0
  enum prompt_key_result  result;
713
0
  u_int     ax, aw;
714
0
  int     redraw = 0;
715
716
0
  if (KEYC_IS_MOUSE(key)) {
717
0
    if (m == NULL || MOUSE_BUTTONS(m->b) != MOUSE_BUTTON_1 ||
718
0
        MOUSE_DRAG(m->b) || MOUSE_RELEASE(m->b) ||
719
0
        m->y != status_prompt_screen_line(c))
720
0
      return (PROMPT_KEY_NOT_HANDLED);
721
0
    status_message_area(c, &ax, &aw);
722
0
    result = prompt_mouse(c->prompt, m->x, ax, aw, &redraw);
723
0
  } else
724
0
    result = prompt_key(c->prompt, key, &redraw);
725
0
  if (redraw && c->prompt != NULL)
726
0
    c->flags |= CLIENT_REDRAWSTATUS;
727
0
  if (c->prompt != NULL && prompt_closed(c->prompt))
728
0
    status_prompt_clear(c);
729
0
  return (result);
730
0
}