NeoMutt  2025-01-09-117-gace867
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
get.c
Go to the documentation of this file.
1
29#include "config.h"
30#include <stdbool.h>
31#include <stddef.h>
32#include <unistd.h>
33#include "mutt/lib.h"
34#include "config/lib.h"
35#include "core/lib.h"
36#include "gui/lib.h"
37#include "lib.h"
38#include "menu/lib.h"
39#include "globals.h"
40#ifdef USE_INOTIFY
41#include "monitor.h"
42#endif
43
44// It's not possible to unget more than one char under some curses libs,
45// so roll our own input buffering routines.
46
49struct KeyEventArray MacroEvents = ARRAY_HEAD_INITIALIZER;
50
53static struct KeyEventArray UngetKeyEvents = ARRAY_HEAD_INITIALIZER;
54
58void mutt_flushinp(void)
59{
62 flushinp();
63}
64
70static struct KeyEvent *array_pop(struct KeyEventArray *a)
71{
72 if (ARRAY_EMPTY(a))
73 {
74 return NULL;
75 }
76
77 struct KeyEvent *event = ARRAY_LAST(a);
78 ARRAY_SHRINK(a, 1);
79 return event;
80}
81
88static void array_add(struct KeyEventArray *a, int ch, int op)
89{
90 struct KeyEvent event = { ch, op };
91 ARRAY_ADD(a, event);
92}
93
98static void array_to_endcond(struct KeyEventArray *a)
99{
100 while (!ARRAY_EMPTY(a))
101 {
102 if (array_pop(a)->op == OP_END_COND)
103 {
104 return;
105 }
106 }
107}
108
116{
117 array_add(&UngetKeyEvents, ch, OP_NULL);
118}
119
127{
129}
130
137void mutt_unget_string(const char *s)
138{
139 const char *p = s + mutt_str_len(s) - 1;
140
141 while (p >= s)
142 {
143 mutt_unget_ch((unsigned char) *p--);
144 }
145}
146
156{
158}
159
167{
169}
170
171#ifdef USE_INOTIFY
177static int mutt_monitor_getch(void)
178{
179 /* ncurses has its own internal buffer, so before we perform a poll,
180 * we need to make sure there isn't a character waiting */
181 timeout(0);
182 int ch = getch();
183 timeout(1000); // 1 second
184 if (ch == ERR)
185 {
186 if (mutt_monitor_poll() != 0)
187 ch = ERR;
188 else
189 ch = getch();
190 }
191 return ch;
192}
193#endif /* USE_INOTIFY */
194
211{
212 static const struct KeyEvent event_abort = { 0, OP_ABORT };
213 static const struct KeyEvent event_repaint = { 0, OP_REPAINT };
214 static const struct KeyEvent event_timeout = { 0, OP_TIMEOUT };
215
216 if (OptNoCurses)
217 return event_abort;
218
219 struct KeyEvent *event_key = array_pop(&UngetKeyEvents);
220 if (event_key)
221 return *event_key;
222
223 if (!(flags & GETCH_IGNORE_MACRO))
224 {
225 event_key = array_pop(&MacroEvents);
226 if (event_key)
227 return *event_key;
228 }
229
230 int ch;
231 SigInt = false;
233 timeout(1000); // 1 second
234#ifdef USE_INOTIFY
236#else
237 ch = getch();
238#endif
240
241 if (SigInt)
242 {
244 return event_abort;
245 }
246
247 if (ch == KEY_RESIZE)
248 {
249 timeout(0);
250 while ((ch = getch()) == KEY_RESIZE)
251 {
252 // do nothing
253 }
254 }
255
256 if (ch == ERR)
257 {
258 if (!isatty(STDIN_FILENO)) // terminal was lost
259 mutt_exit(1);
260
261 if (SigWinch)
262 {
263 SigWinch = false;
265 return event_repaint;
266 }
267
269 return event_timeout;
270 }
271
272 if (ch == AbortKey)
273 return event_abort;
274
275 if (ch & 0x80)
276 {
277 const bool c_meta_key = cs_subset_bool(NeoMutt->sub, "meta_key");
278 if (c_meta_key)
279 {
280 /* send ALT-x as ESC-x */
281 ch &= ~0x80;
283 return (struct KeyEvent) { '\033', OP_NULL }; // Escape
284 }
285 }
286
287 return (struct KeyEvent) { ch, OP_NULL };
288}
289
294void km_error_key(enum MenuType mtype)
295{
296 struct Keymap *key = km_find_func(mtype, OP_HELP);
297 if (!key && (mtype != MENU_EDITOR) && (mtype != MENU_PAGER))
298 key = km_find_func(MENU_GENERIC, OP_HELP);
299
300 if (!key)
301 {
302 mutt_error(_("Key is not bound"));
303 return;
304 }
305
306 struct Buffer *buf = buf_pool_get();
307 km_expand_key(key, buf);
308 mutt_error(_("Key is not bound. Press '%s' for help."), buf_string(buf));
309 buf_pool_release(&buf);
310}
311
321static struct KeyEvent retry_generic(enum MenuType mtype, keycode_t *keys,
322 int keyslen, int lastkey, GetChFlags flags)
323{
324 if (lastkey)
325 mutt_unget_ch(lastkey);
326 for (; keyslen; keyslen--)
327 mutt_unget_ch(keys[keyslen - 1]);
328
329 if ((mtype != MENU_EDITOR) && (mtype != MENU_GENERIC) && (mtype != MENU_PAGER))
330 {
331 return km_dokey_event(MENU_GENERIC, flags);
332 }
333 if ((mtype != MENU_EDITOR) && (mtype != MENU_GENERIC))
334 {
335 /* probably a good idea to flush input here so we can abort macros */
337 }
338
339 return (struct KeyEvent) { mutt_getch(flags).ch, OP_NULL };
340}
341
349{
350 char *pp = NULL;
351 char *p = s + mutt_str_len(s) - 1;
352 size_t l;
353 int i, op = OP_NULL;
354
355 while (p >= s)
356 {
357 /* if we see something like "<PageUp>", look to see if it is a real
358 * function name and return the corresponding value */
359 if (*p == '>')
360 {
361 for (pp = p - 1; pp >= s && *pp != '<'; pp--)
362 ; // do nothing
363
364 if (pp >= s)
365 {
366 i = parse_fkey(pp);
367 if (i > 0)
368 {
369 mutt_push_macro_event(KEY_F(i), 0);
370 p = pp - 1;
371 continue;
372 }
373
374 l = p - pp + 1;
375 for (i = 0; KeyNames[i].name; i++)
376 {
377 if (mutt_istrn_equal(pp, KeyNames[i].name, l))
378 break;
379 }
380 if (KeyNames[i].name)
381 {
382 /* found a match */
383 mutt_push_macro_event(KeyNames[i].value, 0);
384 p = pp - 1;
385 continue;
386 }
387
388 /* See if it is a valid command
389 * skip the '<' and the '>' when comparing */
390 for (enum MenuType j = 0; MenuNames[j].name; j++)
391 {
392 const struct MenuFuncOp *funcs = km_get_table(MenuNames[j].value);
393 if (funcs)
394 {
395 op = get_op(funcs, pp + 1, l - 2);
396 if (op != OP_NULL)
397 break;
398 }
399 }
400
401 if (op != OP_NULL)
402 {
404 p = pp - 1;
405 continue;
406 }
407 }
408 }
409 mutt_push_macro_event((unsigned char) *p--, 0); /* independent 8 bits chars */
410 }
411}
412
420{
421 struct KeyEvent event = { 0, OP_NULL };
422 struct Keymap *map = STAILQ_FIRST(&Keymaps[mtype]);
423 int pos = 0;
424 int n = 0;
425
426 if (!map && (mtype != MENU_EDITOR))
427 return retry_generic(mtype, NULL, 0, 0, flags);
428
429 while (true)
430 {
431 event = mutt_getch(flags);
432
433 // abort, timeout, repaint
434 if (event.op < OP_NULL)
435 return event;
436
437 /* do we have an op already? */
438 if (event.op != OP_NULL)
439 {
440 const char *func = NULL;
441 const struct MenuFuncOp *funcs = NULL;
442
443 /* is this a valid op for this menu type? */
444 if ((funcs = km_get_table(mtype)) && (func = mutt_get_func(funcs, event.op)))
445 return event;
446
447 if ((mtype != MENU_EDITOR) && (mtype != MENU_PAGER) && (mtype != MENU_GENERIC))
448 {
449 /* check generic menu type */
450 funcs = OpGeneric;
451 func = mutt_get_func(funcs, event.op);
452 if (func)
453 return event;
454 }
455
456 /* Sigh. Valid function but not in this context.
457 * Find the literal string and push it back */
458 for (int i = 0; MenuNames[i].name; i++)
459 {
460 funcs = km_get_table(MenuNames[i].value);
461 if (funcs)
462 {
463 func = mutt_get_func(funcs, event.op);
464 if (func)
465 {
466 mutt_unget_ch('>');
467 mutt_unget_string(func);
468 mutt_unget_ch('<');
469 break;
470 }
471 }
472 }
473 /* continue to chew */
474 if (func)
475 continue;
476 }
477
478 if (!map)
479 return event;
480
481 /* Nope. Business as usual */
482 while (event.ch > map->keys[pos])
483 {
484 if ((pos > map->eq) || !STAILQ_NEXT(map, entries))
485 return retry_generic(mtype, map->keys, pos, event.ch, flags);
486 map = STAILQ_NEXT(map, entries);
487 }
488
489 if (event.ch != map->keys[pos])
490 return retry_generic(mtype, map->keys, pos, event.ch, flags);
491
492 if (++pos == map->len)
493 {
494 if (map->op != OP_MACRO)
495 return (struct KeyEvent) { event.ch, map->op };
496
497 /* #GETCH_IGNORE_MACRO turns off processing the MacroEvents buffer
498 * in mutt_getch(). Generating new macro events during that time would
499 * result in undesired behavior once the option is turned off.
500 *
501 * Originally this returned -1, however that results in an unbuffered
502 * username or password prompt being aborted. Returning OP_NULL allows
503 * mw_get_field() to display the keybinding pressed instead.
504 *
505 * It may be unexpected for a macro's keybinding to be returned,
506 * but less so than aborting the prompt. */
507 if (flags & GETCH_IGNORE_MACRO)
508 {
509 return (struct KeyEvent) { event.ch, OP_NULL };
510 }
511
512 if (n++ == 10)
513 {
515 mutt_error(_("Macro loop detected"));
516 return (struct KeyEvent) { '\0', OP_ABORT };
517 }
518
520 map = STAILQ_FIRST(&Keymaps[mtype]);
521 pos = 0;
522 }
523 }
524
525 /* not reached */
526}
527
537int km_dokey(enum MenuType mtype, GetChFlags flags)
538{
539 return km_dokey_event(mtype, flags).op;
540}
#define ARRAY_SHRINK(head, num)
Mark a number of slots at the end of the array as unused.
Definition: array.h:172
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition: array.h:156
#define ARRAY_LAST(head)
Convenience method to get the last element.
Definition: array.h:144
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition: array.h:74
#define ARRAY_SIZE(head)
The number of elements stored.
Definition: array.h:87
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:58
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:47
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
void mutt_query_exit(void)
Ask the user if they want to leave NeoMutt.
Definition: curs_lib.c:138
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: get.c:58
void mutt_unget_string(const char *s)
Return a string to the input buffer.
Definition: get.c:137
struct KeyEvent mutt_getch(GetChFlags flags)
Read a character from the input buffer.
Definition: get.c:210
void mutt_unget_ch(int ch)
Return a keystroke to the input buffer.
Definition: get.c:115
void mutt_exit(int code)
Leave NeoMutt NOW.
Definition: exit.c:41
struct KeyEvent km_dokey_event(enum MenuType mtype, GetChFlags flags)
Determine what a keypress should do.
Definition: get.c:419
struct KeyEventArray MacroEvents
These are used for macros and exec/push commands.
Definition: get.c:49
void generic_tokenize_push_string(char *s)
Parse and queue a 'push' command.
Definition: get.c:348
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: get.c:58
static struct KeyEventArray UngetKeyEvents
These are used in all other "normal" situations, and are not ignored when passing GETCH_IGNORE_MACRO.
Definition: get.c:53
int km_dokey(enum MenuType mtype, GetChFlags flags)
Determine what a keypress should do.
Definition: get.c:537
static void array_add(struct KeyEventArray *a, int ch, int op)
Add an event to the end of the array.
Definition: get.c:88
static void array_to_endcond(struct KeyEventArray *a)
Clear the array until an OP_END_COND.
Definition: get.c:98
void mutt_unget_string(const char *s)
Return a string to the input buffer.
Definition: get.c:137
void mutt_push_macro_event(int ch, int op)
Add the character/operation to the macro buffer.
Definition: get.c:155
struct KeyEvent mutt_getch(GetChFlags flags)
Read a character from the input buffer.
Definition: get.c:210
static struct KeyEvent * array_pop(struct KeyEventArray *a)
Remove an event from the array.
Definition: get.c:70
void mutt_unget_op(int op)
Return an operation to the input buffer.
Definition: get.c:126
void mutt_flush_macro_to_endcond(void)
Drop a macro from the input buffer.
Definition: get.c:166
static int mutt_monitor_getch(void)
Get a character and poll the filesystem monitor.
Definition: get.c:177
void mutt_unget_ch(int ch)
Return a keystroke to the input buffer.
Definition: get.c:115
void km_error_key(enum MenuType mtype)
Handle an unbound key sequence.
Definition: get.c:294
static struct KeyEvent retry_generic(enum MenuType mtype, keycode_t *keys, int keyslen, int lastkey, GetChFlags flags)
Try to find the key in the generic menu bindings.
Definition: get.c:321
bool OptNoCurses
(pseudo) when sending in batch mode
Definition: globals.c:66
#define mutt_error(...)
Definition: logging2.h:93
const struct MenuFuncOp OpGeneric[]
Functions for the Generic Menu.
Definition: functions.c:69
Convenience wrapper for the gui headers.
struct Keymap * km_find_func(enum MenuType mtype, int func)
Find a function's mapping in a Menu.
Definition: lib.c:541
int parse_fkey(char *s)
Parse a function key string.
Definition: lib.c:165
const struct MenuFuncOp * km_get_table(enum MenuType mtype)
Lookup a Menu's functions.
Definition: lib.c:557
struct Mapping KeyNames[]
Key name lookup table.
Definition: lib.c:59
int get_op(const struct MenuFuncOp *funcs, const char *start, size_t len)
Get the function by its name.
Definition: lib.c:298
bool km_expand_key(struct Keymap *map, struct Buffer *buf)
Get the key string bound to a Keymap.
Definition: lib.c:509
uint8_t GetChFlags
Flags for mutt_getch(), e.g. GETCH_NO_FLAGS.
Definition: lib.h:51
struct KeyEventArray MacroEvents
These are used for macros and exec/push commands.
Definition: get.c:49
void generic_tokenize_push_string(char *s)
Parse and queue a 'push' command.
Definition: get.c:348
keycode_t AbortKey
key to abort edits etc, normally Ctrl-G
Definition: lib.c:121
#define GETCH_IGNORE_MACRO
Don't use MacroEvents.
Definition: lib.h:53
const char * mutt_get_func(const struct MenuFuncOp *bindings, int op)
Get the name of a function.
Definition: lib.c:320
short keycode_t
Type for key storage, the rest of neomutt works fine with int type.
Definition: lib.h:56
const struct MenuFuncOp * km_get_table(enum MenuType mtype)
Lookup a Menu's functions.
Definition: lib.c:557
struct KeymapList Keymaps[]
Array of Keymap keybindings, one for each Menu.
Definition: lib.c:124
GUI present the user with a selectable list.
int mutt_monitor_poll(void)
Check for filesystem changes.
Definition: monitor.c:401
Monitor files for changes.
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
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:497
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition: string.c:454
@ NT_TIMEOUT
Timeout has occurred.
Definition: notify_type.h:56
@ NT_RESIZE
Window has been resized.
Definition: notify_type.h:52
#define OP_TIMEOUT
1 second with no events
Definition: opcodes.h:36
#define OP_REPAINT
Repaint is needed.
Definition: opcodes.h:34
#define OP_ABORT
$abort_key pressed (Ctrl-G)
Definition: opcodes.h:37
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
#define STAILQ_FIRST(head)
Definition: queue.h:388
#define STAILQ_NEXT(elm, field)
Definition: queue.h:439
volatile sig_atomic_t SigWinch
true after SIGWINCH is received
Definition: signal.c:70
volatile sig_atomic_t SigInt
true after SIGINT is received
Definition: signal.c:69
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition: signal.c:316
Key value store.
String manipulation buffer.
Definition: buffer.h:36
An event such as a keypress.
Definition: lib.h:81
int op
Function opcode, e.g. OP_HELP.
Definition: lib.h:83
int ch
Raw key pressed.
Definition: lib.h:82
A keyboard mapping.
Definition: lib.h:66
keycode_t * keys
key sequence
Definition: lib.h:72
char * macro
Macro expansion (op == OP_MACRO)
Definition: lib.h:67
short eq
Number of leading keys equal to next entry.
Definition: lib.h:70
short len
Length of key sequence (unit: sizeof (keycode_t))
Definition: lib.h:71
short op
Operation to perform.
Definition: lib.h:69
const char * name
String value.
Definition: mapping.h:34
Mapping between a function and an operation.
Definition: lib.h:112
int op
Operation, e.g. OP_DELETE.
Definition: lib.h:114
Container for Accounts, Notifications.
Definition: neomutt.h:43
struct Notify * notify_timeout
Timeout notifications handler.
Definition: neomutt.h:46
struct Notify * notify_resize
Window resize notifications handler.
Definition: neomutt.h:45
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:47
const struct Mapping MenuNames[]
Menu name lookup table.
Definition: type.c:37
MenuType
Types of GUI selections.
Definition: type.h:36
@ MENU_GENERIC
Generic selection list.
Definition: type.h:46
@ MENU_PAGER
Pager pager (email viewer)
Definition: type.h:48
@ MENU_EDITOR
Text entry area.
Definition: type.h:44