NeoMutt  2023-11-03-85-g512e01
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 <unistd.h>
32#include "mutt/lib.h"
33#include "config/lib.h"
34#include "core/lib.h"
35#include "gui/lib.h"
36#include "key/lib.h"
37#include "menu/lib.h"
38#include "globals.h"
39#ifdef USE_INOTIFY
40#include "monitor.h"
41#endif
42
43// It's not possible to unget more than one char under some curses libs,
44// so roll our own input buffering routines.
45
48struct KeyEventArray MacroEvents = ARRAY_HEAD_INITIALIZER;
49
52static struct KeyEventArray UngetKeyEvents = ARRAY_HEAD_INITIALIZER;
53
57void mutt_flushinp(void)
58{
61 flushinp();
62}
63
69static struct KeyEvent *array_pop(struct KeyEventArray *a)
70{
71 if (ARRAY_EMPTY(a))
72 {
73 return NULL;
74 }
75
76 struct KeyEvent *event = ARRAY_LAST(a);
77 ARRAY_SHRINK(a, 1);
78 return event;
79}
80
87static void array_add(struct KeyEventArray *a, int ch, int op)
88{
89 struct KeyEvent event = { ch, op };
90 ARRAY_ADD(a, event);
91}
92
97static void array_to_endcond(struct KeyEventArray *a)
98{
99 while (!ARRAY_EMPTY(a))
100 {
101 if (array_pop(a)->op == OP_END_COND)
102 {
103 return;
104 }
105 }
106}
107
115{
116 array_add(&UngetKeyEvents, ch, OP_NULL);
117}
118
126{
128}
129
136void mutt_unget_string(const char *s)
137{
138 const char *p = s + mutt_str_len(s) - 1;
139
140 while (p >= s)
141 {
142 mutt_unget_ch((unsigned char) *p--);
143 }
144}
145
155{
157}
158
166{
168}
169
178{
180}
181
182#ifdef USE_INOTIFY
188static int mutt_monitor_getch(void)
189{
190 /* ncurses has its own internal buffer, so before we perform a poll,
191 * we need to make sure there isn't a character waiting */
192 timeout(0);
193 int ch = getch();
194 timeout(1000); // 1 second
195 if (ch == ERR)
196 {
197 if (mutt_monitor_poll() != 0)
198 ch = ERR;
199 else
200 ch = getch();
201 }
202 return ch;
203}
204#endif /* USE_INOTIFY */
205
222{
223 static const struct KeyEvent event_abort = { 0, OP_ABORT };
224 static const struct KeyEvent event_repaint = { 0, OP_REPAINT };
225 static const struct KeyEvent event_timeout = { 0, OP_TIMEOUT };
226
227 if (OptNoCurses)
228 return event_abort;
229
230 struct KeyEvent *event_key = array_pop(&UngetKeyEvents);
231 if (event_key)
232 return *event_key;
233
234 if (!(flags & GETCH_IGNORE_MACRO))
235 {
236 event_key = array_pop(&MacroEvents);
237 if (event_key)
238 return *event_key;
239 }
240
241 int ch;
242 SigInt = false;
244 timeout(1000); // 1 second
245#ifdef USE_INOTIFY
247#else
248 ch = getch();
249#endif
251
252 if (SigInt)
253 {
255 return event_abort;
256 }
257
258 if (ch == KEY_RESIZE)
259 {
260 timeout(0);
261 while ((ch = getch()) == KEY_RESIZE)
262 {
263 // do nothing
264 }
265 }
266
267 if (ch == ERR)
268 {
269 if (!isatty(0)) // terminal was lost
270 mutt_exit(1);
271
272 if (SigWinch)
273 {
274 SigWinch = false;
276 return event_repaint;
277 }
278
280 return event_timeout;
281 }
282
283 if (ch == AbortKey)
284 return event_abort;
285
286 if (ch & 0x80)
287 {
288 const bool c_meta_key = cs_subset_bool(NeoMutt->sub, "meta_key");
289 if (c_meta_key)
290 {
291 /* send ALT-x as ESC-x */
292 ch &= ~0x80;
294 return (struct KeyEvent){ '\033', OP_NULL }; // Escape
295 }
296 }
297
298 return (struct KeyEvent){ ch, OP_NULL };
299}
300
305void km_error_key(enum MenuType mtype)
306{
307 struct Keymap *key = km_find_func(mtype, OP_HELP);
308 if (!key && (mtype != MENU_EDITOR) && (mtype != MENU_PAGER))
309 key = km_find_func(MENU_GENERIC, OP_HELP);
310 if (!key)
311 {
312 mutt_error(_("Key is not bound"));
313 return;
314 }
315
316 char buf[128] = { 0 };
317 km_expand_key(buf, sizeof(buf), key);
318 mutt_error(_("Key is not bound. Press '%s' for help."), buf);
319}
320
330static struct KeyEvent retry_generic(enum MenuType mtype, keycode_t *keys,
331 int keyslen, int lastkey, GetChFlags flags)
332{
333 if (lastkey)
334 mutt_unget_ch(lastkey);
335 for (; keyslen; keyslen--)
336 mutt_unget_ch(keys[keyslen - 1]);
337
338 if ((mtype != MENU_EDITOR) && (mtype != MENU_GENERIC) && (mtype != MENU_PAGER))
339 {
340 return km_dokey_event(MENU_GENERIC, flags);
341 }
342 if ((mtype != MENU_EDITOR) && (mtype != MENU_GENERIC))
343 {
344 /* probably a good idea to flush input here so we can abort macros */
346 }
347
348 return (struct KeyEvent){ mutt_getch(flags).ch, OP_NULL };
349}
350
358{
359 struct KeyEvent event = { 0, OP_NULL };
360 struct Keymap *map = STAILQ_FIRST(&Keymaps[mtype]);
361 int pos = 0;
362 int n = 0;
363
364 if (!map && (mtype != MENU_EDITOR))
365 return retry_generic(mtype, NULL, 0, 0, flags);
366
367 while (true)
368 {
369 event = mutt_getch(flags);
370
371 // abort, timeout, repaint
372 if (event.op < OP_NULL)
373 return event;
374
375 /* do we have an op already? */
376 if (event.op != OP_NULL)
377 {
378 const char *func = NULL;
379 const struct MenuFuncOp *funcs = NULL;
380
381 /* is this a valid op for this menu type? */
382 if ((funcs = km_get_table(mtype)) && (func = mutt_get_func(funcs, event.op)))
383 return event;
384
385 if ((mtype != MENU_EDITOR) && (mtype != MENU_PAGER) && (mtype != MENU_GENERIC))
386 {
387 /* check generic menu type */
388 funcs = OpGeneric;
389 func = mutt_get_func(funcs, event.op);
390 if (func)
391 return event;
392 }
393
394 /* Sigh. Valid function but not in this context.
395 * Find the literal string and push it back */
396 for (int i = 0; MenuNames[i].name; i++)
397 {
398 funcs = km_get_table(MenuNames[i].value);
399 if (funcs)
400 {
401 func = mutt_get_func(funcs, event.op);
402 if (func)
403 {
404 mutt_unget_ch('>');
405 mutt_unget_string(func);
406 mutt_unget_ch('<');
407 break;
408 }
409 }
410 }
411 /* continue to chew */
412 if (func)
413 continue;
414 }
415
416 if (!map)
417 return event;
418
419 /* Nope. Business as usual */
420 while (event.ch > map->keys[pos])
421 {
422 if ((pos > map->eq) || !STAILQ_NEXT(map, entries))
423 return retry_generic(mtype, map->keys, pos, event.ch, flags);
424 map = STAILQ_NEXT(map, entries);
425 }
426
427 if (event.ch != map->keys[pos])
428 return retry_generic(mtype, map->keys, pos, event.ch, flags);
429
430 if (++pos == map->len)
431 {
432 if (map->op != OP_MACRO)
433 return (struct KeyEvent){ event.ch, map->op };
434
435 /* #GETCH_IGNORE_MACRO turns off processing the MacroEvents buffer
436 * in mutt_getch(). Generating new macro events during that time would
437 * result in undesired behavior once the option is turned off.
438 *
439 * Originally this returned -1, however that results in an unbuffered
440 * username or password prompt being aborted. Returning OP_NULL allows
441 * mutt_enter_string_full() to display the keybinding pressed instead.
442 *
443 * It may be unexpected for a macro's keybinding to be returned,
444 * but less so than aborting the prompt. */
445 if (flags & GETCH_IGNORE_MACRO)
446 {
447 return (struct KeyEvent){ event.ch, OP_NULL };
448 }
449
450 if (n++ == 10)
451 {
453 mutt_error(_("Macro loop detected"));
454 return (struct KeyEvent){ '\0', OP_ABORT };
455 }
456
458 map = STAILQ_FIRST(&Keymaps[mtype]);
459 pos = 0;
460 }
461 }
462
463 /* not reached */
464}
465
475int km_dokey(enum MenuType mtype, GetChFlags flags)
476{
477 return km_dokey_event(mtype, flags).op;
478}
#define ARRAY_SHRINK(head, num)
Mark a number of slots at the end of the array as unused.
Definition: array.h:171
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition: array.h:155
#define ARRAY_LAST(head)
Convenience method to get the last element.
Definition: array.h:143
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition: array.h:73
#define ARRAY_SIZE(head)
The number of elements stored.
Definition: array.h:86
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:57
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_query_exit(void)
Ask the user if they want to leave NeoMutt.
Definition: curs_lib.c:136
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: get.c:57
void mutt_unget_string(const char *s)
Return a string to the input buffer.
Definition: get.c:136
struct KeyEvent mutt_getch(GetChFlags flags)
Read a character from the input buffer.
Definition: get.c:221
void mutt_unget_ch(int ch)
Return a keystroke to the input buffer.
Definition: get.c:114
struct KeyEvent km_dokey_event(enum MenuType mtype, GetChFlags flags)
Determine what a keypress should do.
Definition: get.c:357
struct KeyEventArray MacroEvents
These are used for macros and exec/push commands.
Definition: get.c:48
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: get.c:57
static struct KeyEventArray UngetKeyEvents
These are used in all other "normal" situations, and are not ignored when passing GETCH_IGNORE_MACRO.
Definition: get.c:52
int km_dokey(enum MenuType mtype, GetChFlags flags)
Determine what a keypress should do.
Definition: get.c:475
static void array_add(struct KeyEventArray *a, int ch, int op)
Add an event to the end of the array.
Definition: get.c:87
static void array_to_endcond(struct KeyEventArray *a)
Clear the array until an OP_END_COND.
Definition: get.c:97
void mutt_unget_string(const char *s)
Return a string to the input buffer.
Definition: get.c:136
void mutt_push_macro_event(int ch, int op)
Add the character/operation to the macro buffer.
Definition: get.c:154
struct KeyEvent mutt_getch(GetChFlags flags)
Read a character from the input buffer.
Definition: get.c:221
static struct KeyEvent * array_pop(struct KeyEventArray *a)
Remove an event from the array.
Definition: get.c:69
void mutt_unget_op(int op)
Return an operation to the input buffer.
Definition: get.c:125
void mutt_flush_unget_to_endcond(void)
Clear entries from UngetKeyEvents.
Definition: get.c:177
void mutt_flush_macro_to_endcond(void)
Drop a macro from the input buffer.
Definition: get.c:165
static int mutt_monitor_getch(void)
Get a character and poll the filesystem monitor.
Definition: get.c:188
void mutt_unget_ch(int ch)
Return a keystroke to the input buffer.
Definition: get.c:114
void km_error_key(enum MenuType mtype)
Handle an unbound key sequence.
Definition: get.c:305
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:330
bool OptNoCurses
(pseudo) when sending in batch mode
Definition: globals.c:77
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
#define mutt_error(...)
Definition: logging2.h:92
const struct MenuFuncOp OpGeneric[]
Functions for the Generic Menu.
Definition: functions.c:67
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:513
int km_expand_key(char *s, size_t len, struct Keymap *map)
Get the key string bound to a Keymap.
Definition: lib.c:461
Manage keymappings.
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:48
void generic_tokenize_push_string(char *s)
Parse and queue a 'push' command.
Definition: lib.c:342
keycode_t AbortKey
key to abort edits etc, normally Ctrl-G
Definition: lib.c:126
#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:325
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:529
struct KeymapList Keymaps[]
Array of Keymap keybindings, one for each Menu.
Definition: lib.c:129
void mutt_exit(int code)
Leave NeoMutt NOW.
Definition: main.c:228
GUI present the user with a selectable list.
int mutt_monitor_poll(void)
Check for filesystem changes.
Definition: monitor.c:397
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:568
@ 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:34
#define OP_REPAINT
Repaint is needed.
Definition: opcodes.h:32
#define OP_ABORT
$abort_key pressed (Ctrl-G)
Definition: opcodes.h:35
#define STAILQ_FIRST(head)
Definition: queue.h:350
#define STAILQ_NEXT(elm, field)
Definition: queue.h:400
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition: signal.c:251
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 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:33
Mapping between a function and an operation.
Definition: lib.h:102
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
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:55
@ MENU_EDITOR
Text entry area.
Definition: type.h:44