NeoMutt  2023-11-03-85-g512e01
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
curs_lib.c
Go to the documentation of this file.
1
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 <string.h>
37#include <termios.h>
38#include <unistd.h>
39#include <wchar.h>
40#include "mutt/lib.h"
41#include "config/lib.h"
42#include "core/lib.h"
43#include "mutt.h"
44#include "curs_lib.h"
45#include "browser/lib.h"
46#include "color/lib.h"
47#include "editor/lib.h"
48#include "history/lib.h"
49#include "key/lib.h"
50#include "question/lib.h"
51#include "globals.h"
52#include "msgcont.h"
53#include "msgwin.h"
54#include "mutt_curses.h"
55#include "mutt_logging.h"
56#include "mutt_thread.h"
57#include "mutt_window.h"
58#include "opcodes.h"
59#include "protos.h"
60
65void mutt_beep(bool force)
66{
67 const bool c_beep = cs_subset_bool(NeoMutt->sub, "beep");
68 if (force || c_beep)
69 beep();
70}
71
75void mutt_refresh(void)
76{
77 /* don't refresh when we are waiting for a child. */
78 if (OptKeepQuiet)
79 return;
80
81 /* don't refresh in the middle of macros unless necessary */
83 return;
84
85 /* else */
86 refresh();
87}
88
98{
99 // Forcibly switch to the alternate screen.
100 // Using encryption can leave ncurses confused about which mode it's in.
101 fputs("\033[?1049h", stdout);
102 fflush(stdout);
103 keypad(stdscr, true);
104 clearok(stdscr, true);
105 window_redraw(NULL);
106}
107
113void mutt_edit_file(const char *editor, const char *file)
114{
115 struct Buffer *cmd = buf_pool_get();
116
117 mutt_endwin();
118 buf_file_expand_fmt_quote(cmd, editor, file);
119 if (mutt_system(buf_string(cmd)) != 0)
120 {
121 mutt_error(_("Error running \"%s\""), buf_string(cmd));
122 }
123 /* the terminal may have been resized while the editor owned it */
125 keypad(stdscr, true);
126 clearok(stdscr, true);
127
128 buf_pool_release(&cmd);
129}
130
137{
139 if (query_yesorno(_("Exit NeoMutt without saving?"), MUTT_YES) == MUTT_YES)
140 {
141 mutt_exit(0); /* This call never returns */
142 }
144 SigInt = false;
145}
146
150void mutt_endwin(void)
151{
152 if (OptNoCurses)
153 return;
154
155 int e = errno;
156
157 /* at least in some situations (screen + xterm under SuSE11/12) endwin()
158 * doesn't properly flush the screen without an explicit call. */
159 mutt_refresh();
160 endwin();
161 SigWinch = true;
162
163 errno = e;
164}
165
170void mutt_perror_debug(const char *s)
171{
172 char *p = strerror(errno);
173
174 mutt_debug(LL_DEBUG1, "%s: %s (errno = %d)\n", s, p ? p : "unknown error", errno);
175 mutt_error("%s: %s (errno = %d)", s, p ? p : _("unknown error"), errno);
176}
177
184int mutt_any_key_to_continue(const char *s)
185{
186 struct termios term = { 0 };
187 struct termios old = { 0 };
188
189 int fd = open("/dev/tty", O_RDONLY);
190 if (fd < 0)
191 return EOF;
192
193 tcgetattr(fd, &old); // Save the current tty settings
194
195 term = old;
196 term.c_lflag &= ~(ICANON | ECHO); // Canonical (not line-buffered), don't echo the characters
197 term.c_cc[VMIN] = 1; // Wait for at least one character
198 term.c_cc[VTIME] = 255; // Wait for 25.5s
199 tcsetattr(fd, TCSANOW, &term);
200
201 if (s)
202 fputs(s, stdout);
203 else
204 fputs(_("Press any key to continue..."), stdout);
205 fflush(stdout);
206
207 char ch = '\0';
208 // Wait for a character. This might timeout, so loop.
209 while (read(fd, &ch, 1) == 0)
210 ; // do nothing
211
212 // Change the tty settings to be non-blocking
213 term.c_cc[VMIN] = 0; // Returning with zero characters is acceptable
214 term.c_cc[VTIME] = 0; // Don't wait
215 tcsetattr(fd, TCSANOW, &term);
216
217 char buf[64] = { 0 };
218 while (read(fd, buf, sizeof(buf)) > 0)
219 ; // Mop up any remaining chars
220
221 tcsetattr(fd, TCSANOW, &old); // Restore the previous tty settings
222 close(fd);
223
224 fputs("\r\n", stdout);
226 return (ch >= 0) ? ch : EOF;
227}
228
247int mw_enter_fname(const char *prompt, struct Buffer *fname, bool mailbox,
248 struct Mailbox *m, bool multiple, char ***files,
249 int *numfiles, SelectFileFlags flags)
250{
251 struct MuttWindow *win = msgwin_new(true);
252 if (!win)
253 return -1;
254
255 int rc = -1;
256
257 struct Buffer *text = buf_pool_get();
258 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
259 const struct AttrColor *ac_prompt = simple_color_get(MT_COLOR_PROMPT);
260
261 msgwin_add_text(win, prompt, ac_prompt);
262 msgwin_add_text(win, _(" ('?' for list): "), ac_prompt);
263 if (!buf_is_empty(fname))
264 msgwin_add_text(win, buf_string(fname), ac_normal);
265
267 struct MuttWindow *old_focus = window_set_focus(win);
268
269 struct KeyEvent event = { 0, OP_NULL };
270 do
271 {
272 window_redraw(NULL);
273 event = mutt_getch(GETCH_NO_FLAGS);
274 } while ((event.op == OP_TIMEOUT) || (event.op == OP_REPAINT));
275
276 mutt_refresh();
277 win = msgcont_pop_window();
278 window_set_focus(old_focus);
279 mutt_window_free(&win);
280
281 if (event.ch < 0)
282 goto done;
283
284 if (event.ch == '?')
285 {
286 buf_reset(fname);
287
288 if (flags == MUTT_SEL_NO_FLAGS)
289 flags = MUTT_SEL_FOLDER;
290 if (multiple)
291 flags |= MUTT_SEL_MULTI;
292 if (mailbox)
293 flags |= MUTT_SEL_MAILBOX;
294 dlg_browser(fname, flags, m, files, numfiles);
295 }
296 else
297 {
298 char *pc = NULL;
299 mutt_str_asprintf(&pc, "%s: ", prompt);
300 if (event.op == OP_NULL)
301 mutt_unget_ch(event.ch);
302 else
303 mutt_unget_op(event.op);
304
305 buf_alloc(fname, 1024);
306 struct FileCompletionData cdata = { multiple, m, files, numfiles };
307 enum HistoryClass hclass = mailbox ? HC_MAILBOX : HC_FILE;
308 if (mw_get_field(pc, fname, MUTT_COMP_CLEAR, hclass, &CompleteMailboxOps, &cdata) != 0)
309 {
310 buf_reset(fname);
311 }
312 FREE(&pc);
313 }
314
315 rc = 0;
316
317done:
318 buf_pool_release(&text);
319 return rc;
320}
321
329int mutt_addwch(struct MuttWindow *win, wchar_t wc)
330{
331 char buf[MB_LEN_MAX * 2];
332 mbstate_t mbstate = { 0 };
333 size_t n1, n2;
334
335 if (((n1 = wcrtomb(buf, wc, &mbstate)) == ICONV_ILLEGAL_SEQ) ||
336 ((n2 = wcrtomb(buf + n1, 0, &mbstate)) == ICONV_ILLEGAL_SEQ))
337 {
338 return -1; /* ERR */
339 }
340 else
341 {
342 return mutt_window_addstr(win, buf);
343 }
344}
345
352void mutt_paddstr(struct MuttWindow *win, int n, const char *s)
353{
354 wchar_t wc = 0;
355 size_t k;
356 size_t len = mutt_str_len(s);
357 mbstate_t mbstate = { 0 };
358
359 for (; len && (k = mbrtowc(&wc, s, len, &mbstate)); s += k, len -= k)
360 {
361 if ((k == ICONV_ILLEGAL_SEQ) || (k == ICONV_BUF_TOO_SMALL))
362 {
363 if (k == ICONV_ILLEGAL_SEQ)
364 memset(&mbstate, 0, sizeof(mbstate));
365 k = (k == ICONV_ILLEGAL_SEQ) ? 1 : len;
366 wc = ReplacementChar;
367 }
368 if (!IsWPrint(wc))
369 wc = '?';
370 const int w = wcwidth(wc);
371 if (w >= 0)
372 {
373 if (w > n)
374 break;
375 mutt_window_addnstr(win, (char *) s, k);
376 n -= w;
377 }
378 }
379 while (n-- > 0)
380 mutt_window_addch(win, ' ');
381}
382
394size_t mutt_wstr_trunc(const char *src, size_t maxlen, size_t maxwid, size_t *width)
395{
396 wchar_t wc = 0;
397 size_t n, w = 0, l = 0, cl;
398 int cw;
399 mbstate_t mbstate = { 0 };
400
401 if (!src)
402 goto out;
403
404 n = mutt_str_len(src);
405
406 for (w = 0; n && (cl = mbrtowc(&wc, src, n, &mbstate)); src += cl, n -= cl)
407 {
408 if (cl == ICONV_ILLEGAL_SEQ)
409 {
410 memset(&mbstate, 0, sizeof(mbstate));
411 cl = 1;
412 wc = ReplacementChar;
413 }
414 else if (cl == ICONV_BUF_TOO_SMALL)
415 {
416 cl = n;
417 wc = ReplacementChar;
418 }
419
420 cw = wcwidth(wc);
421 /* hack because MUTT_TREE symbols aren't turned into characters
422 * until rendered by print_enriched_string() */
423 if ((cw < 0) && (src[0] == MUTT_SPECIAL_INDEX))
424 {
425 cl = 2; /* skip the index coloring sequence */
426 cw = 0;
427 }
428 else if ((cw < 0) && (cl == 1) && (src[0] != '\0') && (src[0] < MUTT_TREE_MAX))
429 {
430 cw = 1;
431 }
432 else if (cw < 0)
433 {
434 cw = 0; /* unprintable wchar */
435 }
436 if (wc == '\n')
437 break;
438 if (((cl + l) > maxlen) || ((cw + w) > maxwid))
439 break;
440 l += cl;
441 w += cw;
442 }
443out:
444 if (width)
445 *width = w;
446 return l;
447}
448
454size_t mutt_strwidth(const char *s)
455{
456 if (!s)
457 return 0;
458 return mutt_strnwidth(s, mutt_str_len(s));
459}
460
467size_t mutt_strnwidth(const char *s, size_t n)
468{
469 if (!s)
470 return 0;
471
472 wchar_t wc = 0;
473 int w;
474 size_t k;
475 mbstate_t mbstate = { 0 };
476
477 for (w = 0; n && (k = mbrtowc(&wc, s, n, &mbstate)); s += k, n -= k)
478 {
479 if (*s == MUTT_SPECIAL_INDEX)
480 {
481 s += 2; /* skip the index coloring sequence */
482 k = 0;
483 continue;
484 }
485
486 if ((k == ICONV_ILLEGAL_SEQ) || (k == ICONV_BUF_TOO_SMALL))
487 {
488 if (k == ICONV_ILLEGAL_SEQ)
489 memset(&mbstate, 0, sizeof(mbstate));
490 k = (k == ICONV_ILLEGAL_SEQ) ? 1 : n;
491 wc = ReplacementChar;
492 }
493 if (!IsWPrint(wc))
494 wc = '?';
495 w += wcwidth(wc);
496 }
497 return w;
498}
499
510void mw_what_key(void)
511{
512 struct MuttWindow *win = msgwin_new(true);
513 if (!win)
514 return;
515
516 char prompt[256] = { 0 };
517 snprintf(prompt, sizeof(prompt), _("Enter keys (%s to abort): "), km_keyname(AbortKey));
518 msgwin_set_text(win, prompt, MT_COLOR_PROMPT);
519
521 struct MuttWindow *old_focus = window_set_focus(win);
522 window_redraw(win);
523
524 char keys[256] = { 0 };
525 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
526 const struct AttrColor *ac_prompt = simple_color_get(MT_COLOR_PROMPT);
527
528 // ---------------------------------------------------------------------------
529 // Event Loop
530 timeout(1000); // 1 second
531 while (true)
532 {
533 int ch = getch();
534 if (ch == AbortKey)
535 break;
536
537 if (ch == KEY_RESIZE)
538 {
539 timeout(0);
540 while ((ch = getch()) == KEY_RESIZE)
541 {
542 // do nothing
543 }
544 }
545
546 if (ch == ERR)
547 {
548 if (!isatty(0)) // terminal was lost
549 mutt_exit(1);
550
551 if (SigWinch)
552 {
553 SigWinch = false;
555 window_redraw(NULL);
556 }
557 else
558 {
560 }
561
562 continue;
563 }
564
566 snprintf(keys, sizeof(keys), _("Char = %s, Octal = %o, Decimal = %d\n"),
567 km_keyname(ch), ch, ch);
568 msgwin_add_text(win, keys, ac_normal);
569 msgwin_add_text(win, prompt, ac_prompt);
570 msgwin_add_text(win, NULL, NULL);
571 window_redraw(NULL);
572 }
573 // ---------------------------------------------------------------------------
574
575 win = msgcont_pop_window();
576 window_set_focus(old_focus);
577 mutt_window_free(&win);
578}
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition: array.h:73
const struct CompleteOps CompleteMailboxOps
Auto-Completion of Files / Mailboxes.
Definition: complete.c:160
Select a Mailbox from a list.
#define MUTT_SEL_MAILBOX
Select a mailbox.
Definition: lib.h:58
#define MUTT_SEL_FOLDER
Select a local directory.
Definition: lib.h:60
#define MUTT_SEL_MULTI
Multi-selection is enabled.
Definition: lib.h:59
#define MUTT_SEL_NO_FLAGS
No flags are set.
Definition: lib.h:57
uint8_t SelectFileFlags
Flags for mutt_select_file(), e.g. MUTT_SEL_MAILBOX.
Definition: lib.h:56
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:88
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:303
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:349
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
Color and attribute parsing.
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition: simple.c:88
@ MT_COLOR_NORMAL
Plain text.
Definition: color.h:58
@ MT_COLOR_PROMPT
Question/user input.
Definition: color.h:61
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition: curs_lib.c:113
size_t mutt_strnwidth(const char *s, size_t n)
Measure a string's width in screen cells.
Definition: curs_lib.c:467
size_t mutt_wstr_trunc(const char *src, size_t maxlen, size_t maxwid, size_t *width)
Work out how to truncate a widechar string.
Definition: curs_lib.c:394
void mutt_paddstr(struct MuttWindow *win, int n, const char *s)
Display a string on screen, padded if necessary.
Definition: curs_lib.c:352
void mutt_refresh(void)
Force a refresh of the screen.
Definition: curs_lib.c:75
int mutt_addwch(struct MuttWindow *win, wchar_t wc)
Addwch would be provided by an up-to-date curses library.
Definition: curs_lib.c:329
int mutt_any_key_to_continue(const char *s)
Prompt the user to 'press any key' and wait.
Definition: curs_lib.c:184
void mutt_need_hard_redraw(void)
Force a hard refresh.
Definition: curs_lib.c:97
void mutt_query_exit(void)
Ask the user if they want to leave NeoMutt.
Definition: curs_lib.c:136
void mutt_endwin(void)
Shutdown curses.
Definition: curs_lib.c:150
void mutt_beep(bool force)
Irritate the user.
Definition: curs_lib.c:65
size_t mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition: curs_lib.c:454
void mutt_perror_debug(const char *s)
Show the user an 'errno' message.
Definition: curs_lib.c:170
GUI miscellaneous curses (window drawing) routines.
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: get.c:57
struct KeyEvent mutt_getch(GetChFlags flags)
Read a character from the input buffer.
Definition: get.c:221
void mutt_unget_op(int op)
Return an operation to the input buffer.
Definition: get.c:125
void mutt_unget_ch(int ch)
Return a keystroke to the input buffer.
Definition: get.c:114
Edit a string.
void buf_file_expand_fmt_quote(struct Buffer *dest, const char *fmt, const char *src)
Replace s in a string with a filename.
Definition: file.c:1497
struct KeyEventArray MacroEvents
These are used for macros and exec/push commands.
Definition: get.c:48
bool OptKeepQuiet
(pseudo) shut up the message and refresh functions while we are executing an external program
Definition: globals.c:71
bool OptNoCurses
(pseudo) when sending in batch mode
Definition: globals.c:77
bool OptForceRefresh
(pseudo) refresh even during macros
Definition: globals.c:70
SIG_ATOMIC_VOLATILE_T SigWinch
true after SIGWINCH is received
Definition: globals.c:60
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: globals.c:59
void dlg_browser(struct Buffer *file, SelectFileFlags flags, struct Mailbox *m, char ***files, int *numfiles)
Let the user select a file -.
Definition: dlg_browser.c:1226
void mw_what_key(void)
Display the value of a key -.
Definition: curs_lib.c:510
int mw_enter_fname(const char *prompt, struct Buffer *fname, bool mailbox, struct Mailbox *m, bool multiple, char ***files, int *numfiles, SelectFileFlags flags)
Ask the user to select a file -.
Definition: curs_lib.c:247
int mw_get_field(const char *prompt, struct Buffer *buf, CompletionFlags complete, enum HistoryClass hclass, const struct CompleteOps *comp_api, void *cdata)
Ask the user for a string -.
Definition: window.c:275
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
Read/write command history from/to a file.
HistoryClass
Type to differentiate different histories.
Definition: lib.h:50
@ HC_FILE
Files.
Definition: lib.h:54
@ HC_MAILBOX
Mailboxes.
Definition: lib.h:57
const char * km_keyname(int c)
Get the human name for a key.
Definition: lib.c:414
keycode_t AbortKey
code of key to abort prompts, normally Ctrl-G
Definition: lib.c:126
Manage keymappings.
#define GETCH_NO_FLAGS
No flags are set.
Definition: lib.h:52
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
void mutt_exit(int code)
Leave NeoMutt NOW.
Definition: main.c:228
#define IsWPrint(wc)
Definition: mbyte.h:41
#define FREE(x)
Definition: memory.h:45
void msgcont_push_window(struct MuttWindow *win)
Add a window to the Container Stack.
Definition: msgcont.c:93
struct MuttWindow * msgcont_pop_window(void)
Remove the last Window from the Container Stack.
Definition: msgcont.c:57
Message Window.
struct MuttWindow * msgwin_new(bool interactive)
Create the Message Window.
Definition: msgwin.c:371
void msgwin_clear_text(struct MuttWindow *win)
Clear the text in the Message Window.
Definition: msgwin.c:516
void msgwin_add_text(struct MuttWindow *win, const char *text, const struct AttrColor *ac_color)
Add text to the Message Window.
Definition: msgwin.c:419
void msgwin_set_text(struct MuttWindow *win, const char *text, enum ColorId color)
Set the text for the Message Window.
Definition: msgwin.c:484
Message Window.
wchar_t ReplacementChar
When a Unicode character can't be displayed, use this instead.
Definition: charset.c:58
#define ICONV_BUF_TOO_SMALL
Error value for iconv() - Buffer too small.
Definition: charset.h:105
#define ICONV_ILLEGAL_SEQ
Error value for iconv() - Illegal sequence.
Definition: charset.h:103
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
bool notify_send(struct Notify *notify, enum NotifyType event_type, int event_subtype, void *event_data)
Send out a notification message.
Definition: notify.c:173
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1022
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
Many unsorted constants and some structs.
#define MUTT_COMP_CLEAR
Clear input if printable character is pressed.
Definition: mutt.h:56
Define wrapper functions around Curses.
void mutt_resize_screen(void)
Update NeoMutt's opinion about the window size.
Definition: resize.c:74
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:73
NeoMutt Logging.
Create/manipulate threading in emails.
@ MUTT_TREE_MAX
Definition: mutt_thread.h:69
@ MUTT_SPECIAL_INDEX
Colour indicator.
Definition: mutt_thread.h:71
void window_redraw(struct MuttWindow *win)
Reflow, recalc and repaint a tree of Windows.
Definition: mutt_window.c:634
void mutt_window_free(struct MuttWindow **ptr)
Free a Window and its children.
Definition: mutt_window.c:202
struct MuttWindow * window_set_focus(struct MuttWindow *win)
Set the Window focus.
Definition: mutt_window.c:684
int mutt_window_addstr(struct MuttWindow *win, const char *str)
Write a string to a Window.
Definition: mutt_window.c:416
int mutt_window_addnstr(struct MuttWindow *win, const char *str, int num)
Write a partial string to a Window.
Definition: mutt_window.c:401
int mutt_window_addch(struct MuttWindow *win, int ch)
Write one character to a Window.
Definition: mutt_window.c:388
Window management.
@ NT_TIMEOUT
Timeout has occurred.
Definition: notify_type.h:56
@ NT_RESIZE
Window has been resized.
Definition: notify_type.h:52
All user-callable functions.
#define OP_TIMEOUT
1 second with no events
Definition: opcodes.h:34
#define OP_REPAINT
Repaint is needed.
Definition: opcodes.h:32
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
Prototypes for many functions.
int mutt_system(const char *cmd)
Run an external command.
Definition: system.c:50
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:39
Ask the user a question.
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:330
A curses colour and its attributes.
Definition: attr.h:66
String manipulation buffer.
Definition: buffer.h:34
Input for the file completion function.
Definition: curs_lib.h:39
char *** files
List of files selected.
Definition: curs_lib.h:42
struct Mailbox * mailbox
Mailbox.
Definition: curs_lib.h:41
bool multiple
Allow multiple selections.
Definition: curs_lib.h:40
int * numfiles
Number of files selected.
Definition: curs_lib.h:43
An event such as a keypress.
Definition: lib.h:82
int op
Function opcode, e.g. OP_HELP.
Definition: lib.h:84
int ch
Raw key pressed.
Definition: lib.h:83
A mailbox.
Definition: mailbox.h:79
Container for Accounts, Notifications.
Definition: neomutt.h:41
struct Notify * notify_timeout
Timeout notifications handler.
Definition: neomutt.h:44
struct Notify * notify_resize
Window resize notifications handler.
Definition: neomutt.h:43
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45