Coverage Report

Created: 2023-09-25 07:17

/src/neomutt/gui/curs_lib.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 * GUI miscellaneous curses (window drawing) routines
4
 *
5
 * @authors
6
 * Copyright (C) 1996-2002,2010,2012-2013 Michael R. Elkins <me@mutt.org>
7
 * Copyright (C) 2004 g10 Code GmbH
8
 *
9
 * @copyright
10
 * This program is free software: you can redistribute it and/or modify it under
11
 * the terms of the GNU General Public License as published by the Free Software
12
 * Foundation, either version 2 of the License, or (at your option) any later
13
 * version.
14
 *
15
 * This program is distributed in the hope that it will be useful, but WITHOUT
16
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18
 * details.
19
 *
20
 * You should have received a copy of the GNU General Public License along with
21
 * this program.  If not, see <http://www.gnu.org/licenses/>.
22
 */
23
24
/**
25
 * @page gui_curs_lib Window drawing code
26
 *
27
 * GUI miscellaneous curses (window drawing) routines
28
 */
29
30
#include "config.h"
31
#include <errno.h>
32
#include <fcntl.h>
33
#include <limits.h>
34
#include <stdbool.h>
35
#include <stdio.h>
36
#include <stdlib.h>
37
#include <string.h>
38
#include <termios.h>
39
#include <unistd.h>
40
#include <wchar.h>
41
#include "mutt/lib.h"
42
#include "config/lib.h"
43
#include "core/lib.h"
44
#include "mutt.h"
45
#include "curs_lib.h"
46
#include "browser/lib.h"
47
#include "color/lib.h"
48
#include "editor/lib.h"
49
#include "history/lib.h"
50
#include "key/lib.h"
51
#include "question/lib.h"
52
#include "globals.h"
53
#include "msgcont.h"
54
#include "msgwin.h"
55
#include "mutt_curses.h"
56
#include "mutt_logging.h"
57
#include "mutt_thread.h"
58
#include "mutt_window.h"
59
#include "opcodes.h"
60
#include "protos.h"
61
#ifdef HAVE_ISWBLANK
62
#include <wctype.h>
63
#endif
64
65
/**
66
 * mutt_beep - Irritate the user
67
 * @param force If true, ignore the "$beep" config variable
68
 */
69
void mutt_beep(bool force)
70
0
{
71
0
  const bool c_beep = cs_subset_bool(NeoMutt->sub, "beep");
72
0
  if (force || c_beep)
73
0
    beep();
74
0
}
75
76
/**
77
 * mutt_refresh - Force a refresh of the screen
78
 */
79
void mutt_refresh(void)
80
0
{
81
  /* don't refresh when we are waiting for a child. */
82
0
  if (OptKeepQuiet)
83
0
    return;
84
85
  /* don't refresh in the middle of macros unless necessary */
86
0
  if (!ARRAY_EMPTY(&MacroEvents) && !OptForceRefresh)
87
0
    return;
88
89
  /* else */
90
0
  refresh();
91
0
}
92
93
/**
94
 * mutt_need_hard_redraw - Force a hard refresh
95
 *
96
 * Make sure that the next refresh does a full refresh.  This could be
97
 * optimized by not doing it at all if DISPLAY is set as this might indicate
98
 * that a GUI based pinentry was used.  Having an option to customize this is
99
 * of course the NeoMutt way.
100
 */
101
void mutt_need_hard_redraw(void)
102
0
{
103
  // Forcibly switch to the alternate screen.
104
  // Using encryption can leave ncurses confused about which mode it's in.
105
0
  fputs("\033[?1049h", stdout);
106
0
  fflush(stdout);
107
0
  keypad(stdscr, true);
108
0
  clearok(stdscr, true);
109
0
  window_redraw(NULL);
110
0
}
111
112
/**
113
 * mutt_edit_file - Let the user edit a file
114
 * @param editor User's editor config
115
 * @param file   File to edit
116
 */
117
void mutt_edit_file(const char *editor, const char *file)
118
0
{
119
0
  struct Buffer *cmd = buf_pool_get();
120
121
0
  mutt_endwin();
122
0
  buf_file_expand_fmt_quote(cmd, editor, file);
123
0
  if (mutt_system(buf_string(cmd)) != 0)
124
0
  {
125
0
    mutt_error(_("Error running \"%s\""), buf_string(cmd));
126
0
  }
127
  /* the terminal may have been resized while the editor owned it */
128
0
  mutt_resize_screen();
129
0
  keypad(stdscr, true);
130
0
  clearok(stdscr, true);
131
132
0
  buf_pool_release(&cmd);
133
0
}
134
135
/**
136
 * mutt_query_exit - Ask the user if they want to leave NeoMutt
137
 *
138
 * This function is called when the user presses the abort key.
139
 */
140
void mutt_query_exit(void)
141
0
{
142
0
  mutt_flushinp();
143
0
  if (query_yesorno(_("Exit NeoMutt without saving?"), MUTT_YES) == MUTT_YES)
144
0
  {
145
0
    mutt_exit(0); /* This call never returns */
146
0
  }
147
0
  mutt_clear_error();
148
0
  SigInt = false;
149
0
}
150
151
/**
152
 * mutt_endwin - Shutdown curses
153
 */
154
void mutt_endwin(void)
155
0
{
156
0
  if (OptNoCurses)
157
0
    return;
158
159
0
  int e = errno;
160
161
  /* at least in some situations (screen + xterm under SuSE11/12) endwin()
162
   * doesn't properly flush the screen without an explicit call.  */
163
0
  mutt_refresh();
164
0
  endwin();
165
0
  SigWinch = true;
166
167
0
  errno = e;
168
0
}
169
170
/**
171
 * mutt_perror_debug - Show the user an 'errno' message
172
 * @param s Additional text to show
173
 */
174
void mutt_perror_debug(const char *s)
175
0
{
176
0
  char *p = strerror(errno);
177
178
0
  mutt_debug(LL_DEBUG1, "%s: %s (errno = %d)\n", s, p ? p : "unknown error", errno);
179
0
  mutt_error("%s: %s (errno = %d)", s, p ? p : _("unknown error"), errno);
180
0
}
181
182
/**
183
 * mutt_any_key_to_continue - Prompt the user to 'press any key' and wait
184
 * @param s Message prompt
185
 * @retval num Key pressed
186
 * @retval EOF Error, or prompt aborted
187
 */
188
int mutt_any_key_to_continue(const char *s)
189
0
{
190
0
  struct termios term = { 0 };
191
0
  struct termios old = { 0 };
192
193
0
  int fd = open("/dev/tty", O_RDONLY);
194
0
  if (fd < 0)
195
0
    return EOF;
196
197
0
  tcgetattr(fd, &old); // Save the current tty settings
198
199
0
  term = old;
200
0
  term.c_lflag &= ~(ICANON | ECHO); // Canonical (not line-buffered), don't echo the characters
201
0
  term.c_cc[VMIN] = 1;    // Wait for at least one character
202
0
  term.c_cc[VTIME] = 255; // Wait for 25.5s
203
0
  tcsetattr(fd, TCSANOW, &term);
204
205
0
  if (s)
206
0
    fputs(s, stdout);
207
0
  else
208
0
    fputs(_("Press any key to continue..."), stdout);
209
0
  fflush(stdout);
210
211
0
  char ch = '\0';
212
  // Wait for a character.  This might timeout, so loop.
213
0
  while (read(fd, &ch, 1) == 0)
214
0
    ; // do nothing
215
216
  // Change the tty settings to be non-blocking
217
0
  term.c_cc[VMIN] = 0;  // Returning with zero characters is acceptable
218
0
  term.c_cc[VTIME] = 0; // Don't wait
219
0
  tcsetattr(fd, TCSANOW, &term);
220
221
0
  char buf[64] = { 0 };
222
0
  while (read(fd, buf, sizeof(buf)) > 0)
223
0
    ; // Mop up any remaining chars
224
225
0
  tcsetattr(fd, TCSANOW, &old); // Restore the previous tty settings
226
0
  close(fd);
227
228
0
  fputs("\r\n", stdout);
229
0
  mutt_clear_error();
230
0
  return (ch >= 0) ? ch : EOF;
231
0
}
232
233
/**
234
 * mw_enter_fname - Ask the user to select a file - @ingroup gui_mw
235
 * @param[in]  prompt   Prompt
236
 * @param[in]  fname    Buffer for the result
237
 * @param[in]  mailbox  If true, select mailboxes
238
 * @param[in]  multiple Allow multiple selections
239
 * @param[in]  m        Mailbox
240
 * @param[out] files    List of files selected
241
 * @param[out] numfiles Number of files selected
242
 * @param[in]  flags    Flags, see #SelectFileFlags
243
 * @retval  0 Success
244
 * @retval -1 Error
245
 *
246
 * This function uses the message window.
247
 *
248
 * Allow the user to enter a filename.
249
 * If they hit '?' then the browser will be started.  See: dlg_browser()
250
 */
251
int mw_enter_fname(const char *prompt, struct Buffer *fname, bool mailbox,
252
                   struct Mailbox *m, bool multiple, char ***files,
253
                   int *numfiles, SelectFileFlags flags)
254
0
{
255
0
  struct MuttWindow *win = msgwin_new(true);
256
0
  if (!win)
257
0
    return -1;
258
259
0
  int rc = -1;
260
261
0
  struct Buffer *text = buf_pool_get();
262
0
  const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
263
0
  const struct AttrColor *ac_prompt = simple_color_get(MT_COLOR_PROMPT);
264
265
0
  msgwin_add_text(win, prompt, ac_prompt);
266
0
  msgwin_add_text(win, _(" ('?' for list): "), ac_prompt);
267
0
  if (!buf_is_empty(fname))
268
0
    msgwin_add_text(win, buf_string(fname), ac_normal);
269
270
0
  msgcont_push_window(win);
271
0
  struct MuttWindow *old_focus = window_set_focus(win);
272
273
0
  struct KeyEvent event = { 0, OP_NULL };
274
0
  do
275
0
  {
276
0
    window_redraw(NULL);
277
0
    event = mutt_getch(GETCH_NO_FLAGS);
278
0
  } while ((event.op == OP_TIMEOUT) || (event.op == OP_REPAINT));
279
280
0
  mutt_refresh();
281
0
  win = msgcont_pop_window();
282
0
  window_set_focus(old_focus);
283
0
  mutt_window_free(&win);
284
285
0
  if (event.ch < 0)
286
0
    goto done;
287
288
0
  if (event.ch == '?')
289
0
  {
290
0
    buf_reset(fname);
291
292
0
    if (flags == MUTT_SEL_NO_FLAGS)
293
0
      flags = MUTT_SEL_FOLDER;
294
0
    if (multiple)
295
0
      flags |= MUTT_SEL_MULTI;
296
0
    if (mailbox)
297
0
      flags |= MUTT_SEL_MAILBOX;
298
0
    dlg_browser(fname, flags, m, files, numfiles);
299
0
  }
300
0
  else
301
0
  {
302
0
    char *pc = NULL;
303
0
    mutt_str_asprintf(&pc, "%s: ", prompt);
304
0
    if (event.op == OP_NULL)
305
0
      mutt_unget_ch(event.ch);
306
0
    else
307
0
      mutt_unget_op(event.op);
308
309
0
    buf_alloc(fname, 1024);
310
0
    struct FileCompletionData cdata = { multiple, m, files, numfiles };
311
0
    enum HistoryClass hclass = mailbox ? HC_MBOX : HC_FILE;
312
0
    if (mw_get_field(pc, fname, MUTT_COMP_CLEAR, hclass, &CompleteMailboxOps, &cdata) != 0)
313
0
    {
314
0
      buf_reset(fname);
315
0
    }
316
0
    FREE(&pc);
317
0
  }
318
319
0
  rc = 0;
320
321
0
done:
322
0
  buf_pool_release(&text);
323
0
  return rc;
324
0
}
325
326
/**
327
 * mutt_addwch - Addwch would be provided by an up-to-date curses library
328
 * @param win Window
329
 * @param wc  Wide char to display
330
 * @retval  0 Success
331
 * @retval -1 Error
332
 */
333
int mutt_addwch(struct MuttWindow *win, wchar_t wc)
334
0
{
335
0
  char buf[MB_LEN_MAX * 2];
336
0
  mbstate_t mbstate = { 0 };
337
0
  size_t n1, n2;
338
339
0
  if (((n1 = wcrtomb(buf, wc, &mbstate)) == ICONV_ILLEGAL_SEQ) ||
340
0
      ((n2 = wcrtomb(buf + n1, 0, &mbstate)) == ICONV_ILLEGAL_SEQ))
341
0
  {
342
0
    return -1; /* ERR */
343
0
  }
344
0
  else
345
0
  {
346
0
    return mutt_window_addstr(win, buf);
347
0
  }
348
0
}
349
350
/**
351
 * mutt_simple_format - Format a string, like snprintf()
352
 * @param[out] buf       Buffer in which to save string
353
 * @param[in]  buflen    Buffer length
354
 * @param[in]  min_width Minimum width
355
 * @param[in]  max_width Maximum width
356
 * @param[in]  justify   Justification, e.g. #JUSTIFY_RIGHT
357
 * @param[in]  pad_char  Padding character
358
 * @param[in]  s         String to format
359
 * @param[in]  n         Number of bytes of string to format
360
 * @param[in]  arboreal  If true, string contains graphical tree characters
361
 *
362
 * This formats a string, a bit like snprintf(buf, buflen, "%-*.*s",
363
 * min_width, max_width, s), except that the widths refer to the number of
364
 * character cells when printed.
365
 */
366
void mutt_simple_format(char *buf, size_t buflen, int min_width, int max_width,
367
                        enum FormatJustify justify, char pad_char,
368
                        const char *s, size_t n, bool arboreal)
369
0
{
370
0
  wchar_t wc = 0;
371
0
  int w;
372
0
  size_t k;
373
0
  char scratch[MB_LEN_MAX] = { 0 };
374
0
  mbstate_t mbstate1 = { 0 };
375
0
  mbstate_t mbstate2 = { 0 };
376
0
  bool escaped = false;
377
378
0
  buflen--;
379
0
  char *p = buf;
380
0
  for (; n && (k = mbrtowc(&wc, s, n, &mbstate1)); s += k, n -= k)
381
0
  {
382
0
    if ((k == ICONV_ILLEGAL_SEQ) || (k == ICONV_BUF_TOO_SMALL))
383
0
    {
384
0
      if ((k == ICONV_ILLEGAL_SEQ) && (errno == EILSEQ))
385
0
        memset(&mbstate1, 0, sizeof(mbstate1));
386
387
0
      k = (k == ICONV_ILLEGAL_SEQ) ? 1 : n;
388
0
      wc = ReplacementChar;
389
0
    }
390
0
    if (escaped)
391
0
    {
392
0
      escaped = false;
393
0
      w = 0;
394
0
    }
395
0
    else if (arboreal && (wc == MUTT_SPECIAL_INDEX))
396
0
    {
397
0
      escaped = true;
398
0
      w = 0;
399
0
    }
400
0
    else if (arboreal && (wc < MUTT_TREE_MAX))
401
0
    {
402
0
      w = 1; /* hack */
403
0
    }
404
0
    else
405
0
    {
406
0
#ifdef HAVE_ISWBLANK
407
0
      if (iswblank(wc))
408
0
        wc = ' ';
409
0
      else
410
0
#endif
411
0
          if (!IsWPrint(wc))
412
0
        wc = '?';
413
0
      w = wcwidth(wc);
414
0
    }
415
0
    if (w >= 0)
416
0
    {
417
0
      if (w > max_width)
418
0
        continue;
419
0
      size_t k2 = wcrtomb(scratch, wc, &mbstate2);
420
0
      if ((k2 == ICONV_ILLEGAL_SEQ) || (k2 > buflen))
421
0
        continue;
422
423
0
      min_width -= w;
424
0
      max_width -= w;
425
0
      strncpy(p, scratch, k2);
426
0
      p += k2;
427
0
      buflen -= k2;
428
0
    }
429
0
  }
430
0
  w = ((int) buflen < min_width) ? buflen : min_width;
431
0
  if (w <= 0)
432
0
  {
433
0
    *p = '\0';
434
0
  }
435
0
  else if (justify == JUSTIFY_RIGHT) /* right justify */
436
0
  {
437
0
    p[w] = '\0';
438
0
    while (--p >= buf)
439
0
      p[w] = *p;
440
0
    while (--w >= 0)
441
0
      buf[w] = pad_char;
442
0
  }
443
0
  else if (justify == JUSTIFY_CENTER) /* center */
444
0
  {
445
0
    char *savedp = p;
446
0
    int half = (w + 1) / 2; /* half of cushion space */
447
448
0
    p[w] = '\0';
449
450
    /* move str to center of buffer */
451
0
    while (--p >= buf)
452
0
      p[half] = *p;
453
454
    /* fill rhs */
455
0
    p = savedp + half;
456
0
    while (--w >= half)
457
0
      *p++ = pad_char;
458
459
    /* fill lhs */
460
0
    while (half--)
461
0
      buf[half] = pad_char;
462
0
  }
463
0
  else /* left justify */
464
0
  {
465
0
    while (--w >= 0)
466
0
      *p++ = pad_char;
467
0
    *p = '\0';
468
0
  }
469
0
}
470
471
/**
472
 * mutt_format_s_x - Format a string like snprintf()
473
 * @param[out] buf      Buffer in which to save string
474
 * @param[in]  buflen   Buffer length
475
 * @param[in]  prec     Field precision, e.g. "-3.4"
476
 * @param[in]  s        String to format
477
 * @param[in]  arboreal  If true, string contains graphical tree characters
478
 *
479
 * This formats a string rather like:
480
 * - snprintf(fmt, sizeof(fmt), "%%%ss", prec);
481
 * - snprintf(buf, buflen, fmt, s);
482
 * except that the numbers in the conversion specification refer to
483
 * the number of character cells when printed.
484
 */
485
void mutt_format_s_x(char *buf, size_t buflen, const char *prec, const char *s, bool arboreal)
486
0
{
487
0
  enum FormatJustify justify = JUSTIFY_RIGHT;
488
0
  char *p = NULL;
489
0
  int min_width;
490
0
  int max_width = INT_MAX;
491
492
0
  if (*prec == '-')
493
0
  {
494
0
    prec++;
495
0
    justify = JUSTIFY_LEFT;
496
0
  }
497
0
  else if (*prec == '=')
498
0
  {
499
0
    prec++;
500
0
    justify = JUSTIFY_CENTER;
501
0
  }
502
0
  min_width = strtol(prec, &p, 10);
503
0
  if (*p == '.')
504
0
  {
505
0
    prec = p + 1;
506
0
    max_width = strtol(prec, &p, 10);
507
0
    if (p <= prec)
508
0
      max_width = INT_MAX;
509
0
  }
510
511
0
  mutt_simple_format(buf, buflen, min_width, max_width, justify, ' ', s,
512
0
                     mutt_str_len(s), arboreal);
513
0
}
514
515
/**
516
 * mutt_format_s - Format a simple string
517
 * @param[out] buf      Buffer in which to save string
518
 * @param[in]  buflen   Buffer length
519
 * @param[in]  prec     Field precision, e.g. "-3.4"
520
 * @param[in]  s        String to format
521
 */
522
void mutt_format_s(char *buf, size_t buflen, const char *prec, const char *s)
523
0
{
524
0
  mutt_format_s_x(buf, buflen, prec, s, false);
525
0
}
526
527
/**
528
 * mutt_format_s_tree - Format a simple string with tree characters
529
 * @param[out] buf      Buffer in which to save string
530
 * @param[in]  buflen   Buffer length
531
 * @param[in]  prec     Field precision, e.g. "-3.4"
532
 * @param[in]  s        String to format
533
 */
534
void mutt_format_s_tree(char *buf, size_t buflen, const char *prec, const char *s)
535
0
{
536
0
  mutt_format_s_x(buf, buflen, prec, s, true);
537
0
}
538
539
/**
540
 * mutt_paddstr - Display a string on screen, padded if necessary
541
 * @param win Window
542
 * @param n   Final width of field
543
 * @param s   String to display
544
 */
545
void mutt_paddstr(struct MuttWindow *win, int n, const char *s)
546
0
{
547
0
  wchar_t wc = 0;
548
0
  size_t k;
549
0
  size_t len = mutt_str_len(s);
550
0
  mbstate_t mbstate = { 0 };
551
552
0
  for (; len && (k = mbrtowc(&wc, s, len, &mbstate)); s += k, len -= k)
553
0
  {
554
0
    if ((k == ICONV_ILLEGAL_SEQ) || (k == ICONV_BUF_TOO_SMALL))
555
0
    {
556
0
      if (k == ICONV_ILLEGAL_SEQ)
557
0
        memset(&mbstate, 0, sizeof(mbstate));
558
0
      k = (k == ICONV_ILLEGAL_SEQ) ? 1 : len;
559
0
      wc = ReplacementChar;
560
0
    }
561
0
    if (!IsWPrint(wc))
562
0
      wc = '?';
563
0
    const int w = wcwidth(wc);
564
0
    if (w >= 0)
565
0
    {
566
0
      if (w > n)
567
0
        break;
568
0
      mutt_window_addnstr(win, (char *) s, k);
569
0
      n -= w;
570
0
    }
571
0
  }
572
0
  while (n-- > 0)
573
0
    mutt_window_addch(win, ' ');
574
0
}
575
576
/**
577
 * mutt_wstr_trunc - Work out how to truncate a widechar string
578
 * @param[in]  src    String to measure
579
 * @param[in]  maxlen Maximum length of string in bytes
580
 * @param[in]  maxwid Maximum width in screen columns
581
 * @param[out] width  Save the truncated screen column width
582
 * @retval num Bytes to use
583
 *
584
 * See how many bytes to copy from string so it's at most maxlen bytes long and
585
 * maxwid columns wide
586
 */
587
size_t mutt_wstr_trunc(const char *src, size_t maxlen, size_t maxwid, size_t *width)
588
0
{
589
0
  wchar_t wc = 0;
590
0
  size_t n, w = 0, l = 0, cl;
591
0
  int cw;
592
0
  mbstate_t mbstate = { 0 };
593
594
0
  if (!src)
595
0
    goto out;
596
597
0
  n = mutt_str_len(src);
598
599
0
  for (w = 0; n && (cl = mbrtowc(&wc, src, n, &mbstate)); src += cl, n -= cl)
600
0
  {
601
0
    if (cl == ICONV_ILLEGAL_SEQ)
602
0
    {
603
0
      memset(&mbstate, 0, sizeof(mbstate));
604
0
      cl = 1;
605
0
      wc = ReplacementChar;
606
0
    }
607
0
    else if (cl == ICONV_BUF_TOO_SMALL)
608
0
    {
609
0
      cl = n;
610
0
      wc = ReplacementChar;
611
0
    }
612
613
0
    cw = wcwidth(wc);
614
    /* hack because MUTT_TREE symbols aren't turned into characters
615
     * until rendered by print_enriched_string() */
616
0
    if ((cw < 0) && (src[0] == MUTT_SPECIAL_INDEX))
617
0
    {
618
0
      cl = 2; /* skip the index coloring sequence */
619
0
      cw = 0;
620
0
    }
621
0
    else if ((cw < 0) && (cl == 1) && (src[0] != '\0') && (src[0] < MUTT_TREE_MAX))
622
0
    {
623
0
      cw = 1;
624
0
    }
625
0
    else if (cw < 0)
626
0
    {
627
0
      cw = 0; /* unprintable wchar */
628
0
    }
629
0
    if (wc == '\n')
630
0
      break;
631
0
    if (((cl + l) > maxlen) || ((cw + w) > maxwid))
632
0
      break;
633
0
    l += cl;
634
0
    w += cw;
635
0
  }
636
0
out:
637
0
  if (width)
638
0
    *width = w;
639
0
  return l;
640
0
}
641
642
/**
643
 * mutt_strwidth - Measure a string's width in screen cells
644
 * @param s String to be measured
645
 * @retval num Screen cells string would use
646
 */
647
size_t mutt_strwidth(const char *s)
648
0
{
649
0
  if (!s)
650
0
    return 0;
651
0
  return mutt_strnwidth(s, mutt_str_len(s));
652
0
}
653
654
/**
655
 * mutt_strnwidth - Measure a string's width in screen cells
656
 * @param s String to be measured
657
 * @param n Length of string to be measured
658
 * @retval num Screen cells string would use
659
 */
660
size_t mutt_strnwidth(const char *s, size_t n)
661
0
{
662
0
  if (!s)
663
0
    return 0;
664
665
0
  wchar_t wc = 0;
666
0
  int w;
667
0
  size_t k;
668
0
  mbstate_t mbstate = { 0 };
669
670
0
  for (w = 0; n && (k = mbrtowc(&wc, s, n, &mbstate)); s += k, n -= k)
671
0
  {
672
0
    if (*s == MUTT_SPECIAL_INDEX)
673
0
    {
674
0
      s += 2; /* skip the index coloring sequence */
675
0
      k = 0;
676
0
      continue;
677
0
    }
678
679
0
    if ((k == ICONV_ILLEGAL_SEQ) || (k == ICONV_BUF_TOO_SMALL))
680
0
    {
681
0
      if (k == ICONV_ILLEGAL_SEQ)
682
0
        memset(&mbstate, 0, sizeof(mbstate));
683
0
      k = (k == ICONV_ILLEGAL_SEQ) ? 1 : n;
684
0
      wc = ReplacementChar;
685
0
    }
686
0
    if (!IsWPrint(wc))
687
0
      wc = '?';
688
0
    w += wcwidth(wc);
689
0
  }
690
0
  return w;
691
0
}
692
693
/**
694
 * mw_what_key - Display the value of a key - @ingroup gui_mw
695
 *
696
 * This function uses the message window.
697
 *
698
 * Displays the octal value back to the user. e.g.
699
 * `Char = h, Octal = 150, Decimal = 104`
700
 *
701
 * Press the $abort_key (default Ctrl-G) to exit.
702
 */
703
void mw_what_key(void)
704
0
{
705
0
  struct MuttWindow *win = msgwin_new(true);
706
0
  if (!win)
707
0
    return;
708
709
0
  char prompt[256] = { 0 };
710
0
  snprintf(prompt, sizeof(prompt), _("Enter keys (%s to abort): "), km_keyname(AbortKey));
711
0
  msgwin_set_text(win, prompt, MT_COLOR_PROMPT);
712
713
0
  msgcont_push_window(win);
714
0
  struct MuttWindow *old_focus = window_set_focus(win);
715
0
  window_redraw(win);
716
717
0
  char keys[256] = { 0 };
718
0
  const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
719
0
  const struct AttrColor *ac_prompt = simple_color_get(MT_COLOR_PROMPT);
720
721
  // ---------------------------------------------------------------------------
722
  // Event Loop
723
0
  timeout(1000); // 1 second
724
0
  while (true)
725
0
  {
726
0
    int ch = getch();
727
0
    if (ch == AbortKey)
728
0
      break;
729
730
0
    if (ch == KEY_RESIZE)
731
0
    {
732
0
      timeout(0);
733
0
      while ((ch = getch()) == KEY_RESIZE)
734
0
      {
735
        // do nothing
736
0
      }
737
0
    }
738
739
0
    if (ch == ERR)
740
0
    {
741
0
      if (!isatty(0)) // terminal was lost
742
0
        mutt_exit(1);
743
744
0
      if (SigWinch)
745
0
      {
746
0
        SigWinch = false;
747
0
        notify_send(NeoMutt->notify_resize, NT_RESIZE, 0, NULL);
748
0
        window_redraw(NULL);
749
0
      }
750
0
      else
751
0
      {
752
0
        notify_send(NeoMutt->notify_timeout, NT_TIMEOUT, 0, NULL);
753
0
      }
754
755
0
      continue;
756
0
    }
757
758
0
    msgwin_clear_text(win);
759
0
    snprintf(keys, sizeof(keys), _("Char = %s, Octal = %o, Decimal = %d\n"),
760
0
             km_keyname(ch), ch, ch);
761
0
    msgwin_add_text(win, keys, ac_normal);
762
0
    msgwin_add_text(win, prompt, ac_prompt);
763
0
    msgwin_add_text(win, NULL, NULL);
764
0
    window_redraw(NULL);
765
0
  }
766
  // ---------------------------------------------------------------------------
767
768
0
  win = msgcont_pop_window();
769
0
  window_set_focus(old_focus);
770
0
  mutt_window_free(&win);
771
0
}