small typo
[midnight-commander.git] / src / key.c
blob23ae9f3031b586edeaf39c7ef7a55921de334ab1
1 /* Keyboard support routines.
3 Copyright (C) 1994,1995 the Free Software Foundation.
5 Written by: 1994, 1995 Miguel de Icaza.
6 1994, 1995 Janne Kukonlehto.
7 1995 Jakub Jelinek.
8 1997 Norbert Warmuth
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 #include <config.h>
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <string.h>
28 #ifdef HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31 #include <ctype.h>
32 #include <errno.h>
34 #include "global.h"
35 #include "tty.h"
36 #include "mouse.h"
37 #include "key.h"
38 #include "main.h"
39 #include "win.h"
40 #include "cons.saver.h"
41 #include "../vfs/vfs.h"
43 #ifdef HAVE_TEXTMODE_X11_SUPPORT
44 #include <X11/Xlib.h>
45 #endif
47 #ifdef __linux__
48 # if defined(__GLIBC__) && (__GLIBC__ < 2)
49 # include <linux/termios.h> /* This is needed for TIOCLINUX */
50 # else
51 # include <termios.h>
52 # endif
53 # include <sys/ioctl.h>
54 #endif
56 #define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *)NULL))
57 #define DIF_TIME(t1,t2) ((t2.tv_sec -t1.tv_sec) *1000+ \
58 (t2.tv_usec-t1.tv_usec)/1000)
60 /* timeout for old_esc_mode in usec */
61 #define ESCMODE_TIMEOUT 1000000
63 int mou_auto_repeat = 100;
64 int double_click_speed = 250;
65 int old_esc_mode = 0;
67 int use_8th_bit_as_meta = 1;
69 typedef struct key_def {
70 char ch; /* Holds the matching char code */
71 int code; /* The code returned, valid if child == NULL */
72 struct key_def *next;
73 struct key_def *child; /* sequence continuation */
74 int action; /* optional action to be done. Now used only
75 to mark that we are just after the first
76 Escape */
77 } key_def;
79 /* This holds all the key definitions */
80 static key_def *keys = 0;
82 static int input_fd;
83 static int disabled_channels = 0; /* Disable channels checking */
84 static int xgetch_second (void);
86 /* File descriptor monitoring add/remove routines */
87 typedef struct SelectList {
88 int fd;
89 select_fn callback;
90 void *info;
91 struct SelectList *next;
92 } SelectList;
94 static SelectList *select_list = 0;
96 void add_select_channel (int fd, select_fn callback, void *info)
98 SelectList *new;
100 new = g_new (SelectList, 1);
101 new->fd = fd;
102 new->callback = callback;
103 new->info = info;
104 new->next = select_list;
105 select_list = new;
108 void delete_select_channel (int fd)
110 SelectList *p = select_list;
111 SelectList *p_prev = 0;
112 SelectList *p_next;
114 while (p) {
115 if (p->fd == fd) {
116 p_next = p->next;
118 if (p_prev)
119 p_prev->next = p_next;
120 else
121 select_list = p_next;
123 g_free (p);
124 p = p_next;
125 continue;
128 p_prev = p;
129 p = p->next;
133 inline static int add_selects (fd_set *select_set)
135 SelectList *p;
136 int top_fd = 0;
138 if (disabled_channels)
139 return 0;
141 for (p = select_list; p; p = p->next){
142 FD_SET (p->fd, select_set);
143 if (p->fd > top_fd)
144 top_fd = p->fd;
146 return top_fd;
149 static void check_selects (fd_set *select_set)
151 SelectList *p;
153 if (disabled_channels)
154 return;
156 for (p = select_list; p; p = p->next)
157 if (FD_ISSET (p->fd, select_set))
158 (*p->callback)(p->fd, p->info);
161 void channels_down (void)
163 disabled_channels ++;
166 void channels_up (void)
168 if (!disabled_channels)
169 fprintf (stderr,
170 "Error: channels_up called with disabled_channels = 0\n");
171 disabled_channels--;
174 typedef const struct {
175 int code;
176 char *seq;
177 int action;
178 } key_define_t;
180 static key_define_t mc_bindings [] = {
181 { KEY_END, ESC_STR ">", MCKEY_NOACTION },
182 { KEY_HOME, ESC_STR "<", MCKEY_NOACTION },
183 { 0, 0, MCKEY_NOACTION },
186 /* Broken terminfo and termcap databases on xterminals */
187 static key_define_t xterm_key_defines [] = {
188 { KEY_F(1), ESC_STR "OP", MCKEY_NOACTION },
189 { KEY_F(2), ESC_STR "OQ", MCKEY_NOACTION },
190 { KEY_F(3), ESC_STR "OR", MCKEY_NOACTION },
191 { KEY_F(4), ESC_STR "OS", MCKEY_NOACTION },
192 { KEY_F(1), ESC_STR "[11~", MCKEY_NOACTION },
193 { KEY_F(2), ESC_STR "[12~", MCKEY_NOACTION },
194 { KEY_F(3), ESC_STR "[13~", MCKEY_NOACTION },
195 { KEY_F(4), ESC_STR "[14~", MCKEY_NOACTION },
196 { KEY_F(5), ESC_STR "[15~", MCKEY_NOACTION },
197 { KEY_F(6), ESC_STR "[17~", MCKEY_NOACTION },
198 { KEY_F(7), ESC_STR "[18~", MCKEY_NOACTION },
199 { KEY_F(8), ESC_STR "[19~", MCKEY_NOACTION },
200 { KEY_F(9), ESC_STR "[20~", MCKEY_NOACTION },
201 { KEY_F(10), ESC_STR "[21~", MCKEY_NOACTION },
202 { 0, 0, MCKEY_NOACTION },
205 static key_define_t mc_default_keys [] = {
206 { ESC_CHAR, ESC_STR, MCKEY_ESCAPE },
207 { ESC_CHAR, ESC_STR ESC_STR, MCKEY_NOACTION },
208 { 0, 0, MCKEY_NOACTION },
211 static void
212 define_sequences (key_define_t *kd)
214 int i;
216 for (i = 0; kd [i].code; i++)
217 define_sequence(kd [i].code, kd [i].seq, kd [i].action);
220 #ifdef HAVE_TEXTMODE_X11_SUPPORT
221 static Display *x11_display;
222 static Window x11_window;
223 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
225 /* This has to be called before slang_init or whatever routine
226 calls any define_sequence */
227 void init_key (void)
229 char *term = (char *) getenv ("TERM");
231 /* This has to be the first define_sequence */
232 /* So, we can assume that the first keys member has ESC */
233 define_sequences (mc_default_keys);
235 /* Terminfo on irix does not have some keys */
236 if ((!strncmp (term, "iris-ansi", 9)) || (!strncmp (term, "xterm", 5)))
237 define_sequences (xterm_key_defines);
239 define_sequences (mc_bindings);
241 /* load some additional keys (e.g. direct Alt-? support) */
242 load_xtra_key_defines();
244 #ifdef __QNX__
245 if (strncmp(term, "qnx", 3) == 0){
246 /* Modify the default value of use_8th_bit_as_meta: we would
247 * like to provide a working mc for a newbie who knows nothing
248 * about [Options|Display bits|Full 8 bits input]...
250 * Don't use 'meta'-bit, when we are dealing with a
251 * 'qnx*'-type terminal: clear the default value!
252 * These terminal types use 0xFF as an escape character,
253 * so use_8th_bit_as_meta==1 must not be enabled!
255 * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
256 * is not used now (doesn't even depend on use_8th_bit_as_meta
257 * as in mc-3.1.2)...GREAT!...no additional code is required!]
259 use_8th_bit_as_meta = 0;
261 #endif /* __QNX__ */
263 #ifdef HAVE_TEXTMODE_X11_SUPPORT
264 x11_display = XOpenDisplay (0);
265 if (x11_display)
266 x11_window = DefaultRootWindow (x11_display);
267 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
270 /* This has to be called after SLang_init_tty/slint_init */
271 void init_key_input_fd (void)
273 #ifdef HAVE_SLANG
274 input_fd = SLang_TT_Read_FD;
275 #endif
279 static void
280 xmouse_get_event (Gpm_Event *ev)
282 int btn;
283 static struct timeval tv1 = { 0, 0 }; /* Force first click as single */
284 static struct timeval tv2;
285 static int clicks;
286 static int last_btn = 0;
288 /* Decode Xterm mouse information to a GPM style event */
290 /* Variable btn has following meaning: */
291 /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
292 btn = getch () - 32;
294 /* There seems to be no way of knowing which button was released */
295 /* So we assume all the buttons were released */
297 if (btn == 3){
298 if (last_btn) {
299 ev->type = GPM_UP | (GPM_SINGLE << clicks);
300 ev->buttons = 0;
301 last_btn = 0;
302 GET_TIME (tv1);
303 clicks = 0;
304 } else {
305 /* Bogus event, maybe mouse wheel */
306 ev->type = 0;
308 } else {
309 ev->type = GPM_DOWN;
310 GET_TIME (tv2);
311 if (tv1.tv_sec && (DIF_TIME (tv1,tv2) < double_click_speed)){
312 clicks++;
313 clicks %= 3;
314 } else
315 clicks = 0;
317 switch (btn) {
318 case 0:
319 ev->buttons = GPM_B_LEFT;
320 break;
321 case 1:
322 ev->buttons = GPM_B_MIDDLE;
323 break;
324 case 2:
325 ev->buttons = GPM_B_RIGHT;
326 break;
327 case 64:
328 ev->buttons = GPM_B_UP;
329 break;
330 case 65:
331 ev->buttons = GPM_B_DOWN;
332 break;
333 default:
334 /* Nothing */
335 ev->type = 0;
336 ev->buttons = 0;
337 break;
339 last_btn = ev->buttons;
341 /* Coordinates are 33-based */
342 /* Transform them to 1-based */
343 ev->x = getch () - 32;
344 ev->y = getch () - 32;
347 static key_def *create_sequence (char *seq, int code, int action)
349 key_def *base, *p, *attach;
351 for (base = attach = NULL; *seq; seq++){
352 p = g_new (key_def, 1);
353 if (!base) base = p;
354 if (attach) attach->child = p;
356 p->ch = *seq;
357 p->code = code;
358 p->child = p->next = NULL;
359 if (!seq[1])
360 p->action = action;
361 else
362 p->action = MCKEY_NOACTION;
363 attach = p;
365 return base;
368 /* The maximum sequence length (32 + null terminator) */
369 #define SEQ_BUFFER_LEN 33
370 static int seq_buffer [SEQ_BUFFER_LEN];
371 static int *seq_append = 0;
373 static int push_char (int c)
375 if (!seq_append)
376 seq_append = seq_buffer;
378 if (seq_append == &(seq_buffer [SEQ_BUFFER_LEN-2]))
379 return 0;
380 *(seq_append++) = c;
381 *seq_append = 0;
382 return 1;
386 * Return 1 on success, 0 on error.
387 * An error happens if SEQ is a beginning of an existing longer sequence.
389 int define_sequence (int code, char *seq, int action)
391 key_def *base;
393 if (strlen (seq) > SEQ_BUFFER_LEN-1)
394 return 0;
396 for (base = keys; (base != 0) && *seq; ){
397 if (*seq == base->ch){
398 if (base->child == 0){
399 if (*(seq+1)){
400 base->child = create_sequence (seq+1, code, action);
401 return 1;
402 } else {
403 /* The sequence matches an existing one. */
404 base->code = code;
405 base->action = action;
406 return 1;
408 } else {
409 base = base->child;
410 seq++;
412 } else {
413 if (base->next)
414 base = base->next;
415 else {
416 base->next = create_sequence (seq, code, action);
417 return 1;
422 if (!*seq) {
423 /* Attempt to redefine a sequence with a shorter sequence. */
424 return 0;
427 keys = create_sequence (seq, code, action);
428 return 1;
431 static int *pending_keys;
433 static int
434 correct_key_code (int c)
436 /* This is needed on some OS that do not support ncurses and */
437 /* do some magic after read()ing the data */
438 if (c == '\r')
439 return '\n';
441 #ifdef IS_AIX
442 if (c == KEY_SCANCEL)
443 return '\t';
444 #endif
446 if (c == KEY_F(0))
447 return KEY_F(10);
449 if (!alternate_plus_minus)
450 switch (c) {
451 case KEY_KP_ADD: c = '+'; break;
452 case KEY_KP_SUBTRACT: c = '-'; break;
453 case KEY_KP_MULTIPLY: c = '*'; break;
456 return c;
459 int get_key_code (int no_delay)
461 int c;
462 static key_def *this = NULL, *parent;
463 static struct timeval esctime = { -1, -1 };
464 static int lastnodelay = -1;
466 if (no_delay != lastnodelay) {
467 this = NULL;
468 lastnodelay = no_delay;
471 pend_send:
472 if (pending_keys){
473 int d = *pending_keys++;
474 check_pend:
475 if (!*pending_keys){
476 pending_keys = 0;
477 seq_append = 0;
479 if (d == ESC_CHAR && pending_keys){
480 d = ALT(*pending_keys++);
481 goto check_pend;
483 if ((d & 0x80) && use_8th_bit_as_meta)
484 d = ALT(d & 0x7f);
485 this = NULL;
486 return correct_key_code (d);
489 nodelay_try_again:
490 if (no_delay) {
491 nodelay (stdscr, TRUE);
493 c = getch ();
494 #if defined(USE_NCURSES) && defined(KEY_RESIZE)
495 if (c == KEY_RESIZE)
496 goto nodelay_try_again;
497 #endif
498 if (no_delay) {
499 nodelay (stdscr, FALSE);
500 if (c == ERR) {
501 if (this != NULL && parent != NULL &&
502 parent->action == MCKEY_ESCAPE && old_esc_mode) {
503 struct timeval current, timeout;
505 if (esctime.tv_sec == -1)
506 return ERR;
507 GET_TIME (current);
508 timeout.tv_sec = ESCMODE_TIMEOUT / 1000000 + esctime.tv_sec;
509 timeout.tv_usec = ESCMODE_TIMEOUT % 1000000 + esctime.tv_usec;
510 if (timeout.tv_usec > 1000000) {
511 timeout.tv_usec -= 1000000;
512 timeout.tv_sec++;
514 if (current.tv_sec < timeout.tv_sec)
515 return ERR;
516 if (current.tv_sec == timeout.tv_sec &&
517 current.tv_usec < timeout.tv_usec)
518 return ERR;
519 this = NULL;
520 pending_keys = seq_append = NULL;
521 return ESC_CHAR;
523 return ERR;
525 } else if (c == ERR){
526 /* Maybe we got an incomplete match.
527 This we do only in delay mode, since otherwise
528 getch can return ERR at any time. */
529 if (seq_append) {
530 pending_keys = seq_buffer;
531 goto pend_send;
533 this = NULL;
534 return ERR;
537 /* Search the key on the root */
538 if (!no_delay || this == NULL) {
539 this = keys;
540 parent = NULL;
542 if ((c & 0x80) && use_8th_bit_as_meta) {
543 c &= 0x7f;
545 /* The first sequence defined starts with esc */
546 parent = keys;
547 this = keys->child;
550 while (this){
551 if (c == this->ch){
552 if (this->child){
553 if (!push_char (c)){
554 pending_keys = seq_buffer;
555 goto pend_send;
557 parent = this;
558 this = this->child;
559 if (parent->action == MCKEY_ESCAPE && old_esc_mode) {
560 if (no_delay) {
561 GET_TIME (esctime);
562 if (this == NULL) {
563 /* Shouldn't happen */
564 fprintf (stderr, "Internal error\n");
565 exit (1);
567 goto nodelay_try_again;
569 esctime.tv_sec = -1;
570 c = xgetch_second ();
571 if (c == ERR) {
572 pending_keys = seq_append = NULL;
573 this = NULL;
574 return ESC_CHAR;
576 } else {
577 if (no_delay)
578 goto nodelay_try_again;
579 c = getch ();
581 } else {
582 /* We got a complete match, return and reset search */
583 int code;
585 pending_keys = seq_append = NULL;
586 code = this->code;
587 this = NULL;
588 return correct_key_code (code);
590 } else {
591 if (this->next)
592 this = this->next;
593 else {
594 if (parent != NULL && parent->action == MCKEY_ESCAPE) {
595 /* This is just to save a lot of define_sequences */
596 if (isalpha(c)
597 || (c == '\n') || (c == '\t') || (c == XCTRL('h'))
598 || (c == KEY_BACKSPACE) || (c == '!') || (c == '\r')
599 || c == 127 || c == '+' || c == '-' || c == '\\'
600 || c == '?')
601 c = ALT(c);
602 else if (isdigit(c))
603 c = KEY_F (c-'0');
604 else if (c == ' ')
605 c = ESC_CHAR;
606 pending_keys = seq_append = NULL;
607 this = NULL;
608 return correct_key_code (c);
610 /* Did not find a match or {c} was changed in the if above,
611 so we have to return everything we had skipped
613 push_char (c);
614 pending_keys = seq_buffer;
615 goto pend_send;
619 this = NULL;
620 return correct_key_code (c);
623 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
624 static void
625 try_channels (int set_timeout)
627 struct timeval timeout;
628 static fd_set select_set;
629 struct timeval *timeptr;
630 int v;
631 int maxfdp;
633 while (1){
634 FD_ZERO (&select_set);
635 FD_SET (input_fd, &select_set); /* Add stdin */
636 maxfdp = max (add_selects (&select_set), input_fd);
638 if (set_timeout){
639 timeout.tv_sec = 0;
640 timeout.tv_usec = 100000;
641 timeptr = &timeout;
642 } else
643 timeptr = 0;
645 v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr);
646 if (v > 0){
647 check_selects (&select_set);
648 if (FD_ISSET (input_fd, &select_set))
649 return;
654 /* Workaround for System V Curses vt100 bug */
655 static int getch_with_delay (void)
657 int c;
659 /* This routine could be used on systems without mouse support,
660 so we need to do the select check :-( */
661 while (1){
662 if (!pending_keys)
663 try_channels (0);
665 /* Try to get a character */
666 c = get_key_code (0);
667 if (c != ERR)
668 break;
669 /* Failed -> wait 0.1 secs and try again */
670 try_channels (1);
672 /* Success -> return the character */
673 return c;
676 extern int max_dirt_limit;
678 /* Returns a character read from stdin with appropriate interpretation */
679 /* Also takes care of generated mouse events */
680 /* Returns EV_MOUSE if it is a mouse event */
681 /* Returns EV_NONE if non-blocking or interrupt set and nothing was done */
683 get_event (Gpm_Event * event, int redo_event, int block)
685 int c;
686 static int flag; /* Return value from select */
687 #ifdef HAVE_LIBGPM
688 static Gpm_Event ev; /* Mouse event */
689 #endif
690 struct timeval timeout;
691 struct timeval *time_addr = NULL;
692 static int dirty = 3;
694 if ((dirty == 3) || is_idle ()) {
695 mc_refresh ();
696 doupdate ();
697 dirty = 1;
698 } else
699 dirty++;
701 vfs_timeout_handler ();
703 /* Ok, we use (event->x < 0) to signal that the event does not contain
704 a suitable position for the mouse, so we can't use show_mouse_pointer
705 on it.
707 if (event->x > 0) {
708 show_mouse_pointer (event->x, event->y);
709 if (!redo_event)
710 event->x = -1;
713 /* Repeat if using mouse */
714 while (mouse_enabled && !pending_keys) {
715 int maxfdp;
716 fd_set select_set;
718 FD_ZERO (&select_set);
719 FD_SET (input_fd, &select_set);
720 maxfdp = max (add_selects (&select_set), input_fd);
722 #ifdef HAVE_LIBGPM
723 if (use_mouse_p == MOUSE_GPM) {
724 if (gpm_fd == -1) {
725 /* Connection to gpm broken, possibly gpm has died */
726 mouse_enabled = 0;
727 use_mouse_p = MOUSE_NONE;
728 break;
730 FD_SET (gpm_fd, &select_set);
731 maxfdp = max (maxfdp, gpm_fd);
733 #endif
735 if (redo_event) {
736 timeout.tv_usec = mou_auto_repeat * 1000;
737 timeout.tv_sec = 0;
739 time_addr = &timeout;
740 } else {
741 int seconds;
743 if ((seconds = vfs_timeouts ())) {
744 /* the timeout could be improved and actually be
745 * the number of seconds until the next vfs entry
746 * timeouts in the stamp list.
749 timeout.tv_sec = seconds;
750 timeout.tv_usec = 0;
751 time_addr = &timeout;
752 } else
753 time_addr = NULL;
756 if (!block) {
757 time_addr = &timeout;
758 timeout.tv_sec = 0;
759 timeout.tv_usec = 0;
761 enable_interrupt_key ();
762 flag = select (maxfdp + 1, &select_set, NULL, NULL, time_addr);
763 disable_interrupt_key ();
765 /* select timed out: it could be for any of the following reasons:
766 * redo_event -> it was because of the MOU_REPEAT handler
767 * !block -> we did not block in the select call
768 * else -> 10 second timeout to check the vfs status.
770 if (flag == 0) {
771 if (redo_event)
772 return EV_MOUSE;
773 if (!block)
774 return EV_NONE;
775 vfs_timeout_handler ();
777 if (flag == -1 && errno == EINTR)
778 return EV_NONE;
780 check_selects (&select_set);
782 if (FD_ISSET (input_fd, &select_set))
783 break;
784 #ifdef HAVE_LIBGPM
785 if (use_mouse_p == MOUSE_GPM && FD_ISSET (gpm_fd, &select_set)) {
786 Gpm_GetEvent (&ev);
787 Gpm_FitEvent (&ev);
788 *event = ev;
789 return EV_MOUSE;
791 #endif /* !HAVE_LIBGPM */
793 #ifndef HAVE_SLANG
794 flag = is_wintouched (stdscr);
795 untouchwin (stdscr);
796 #endif /* !HAVE_SLANG */
797 c = block ? getch_with_delay () : get_key_code (1);
799 #ifndef HAVE_SLANG
800 if (flag)
801 touchwin (stdscr);
802 #endif /* !HAVE_SLANG */
804 if (c == MCKEY_MOUSE
805 #ifdef KEY_MOUSE
806 || c == KEY_MOUSE
807 #endif /* KEY_MOUSE */
809 /* Mouse event */
810 xmouse_get_event (event);
811 if (event->type)
812 return EV_MOUSE;
813 else
814 return EV_NONE;
817 return c;
820 /* Returns a key press, mouse events are discarded */
821 int mi_getch ()
823 Gpm_Event ev;
824 int key;
826 ev.x = -1;
827 while ((key = get_event (&ev, 0, 1)) == EV_NONE)
829 return key;
832 static int xgetch_second (void)
834 fd_set Read_FD_Set;
835 int c;
836 struct timeval timeout;
838 timeout.tv_sec = ESCMODE_TIMEOUT / 1000000;
839 timeout.tv_usec = ESCMODE_TIMEOUT % 1000000;
840 nodelay (stdscr, TRUE);
841 FD_ZERO (&Read_FD_Set);
842 FD_SET (input_fd, &Read_FD_Set);
843 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &timeout);
844 c = getch ();
845 nodelay (stdscr, FALSE);
846 return c;
849 static void
850 learn_store_key (char *buffer, char **p, int c)
852 if (*p - buffer > 253)
853 return;
854 if (c == ESC_CHAR) {
855 *(*p)++ = '\\';
856 *(*p)++ = 'e';
857 } else if (c < ' ') {
858 *(*p)++ = '^';
859 *(*p)++ = c + 'a' - 1;
860 } else if (c == '^') {
861 *(*p)++ = '^';
862 *(*p)++ = '^';
863 } else
864 *(*p)++ = (char) c;
867 char *learn_key (void)
869 /* LEARN_TIMEOUT in usec */
870 #define LEARN_TIMEOUT 200000
872 fd_set Read_FD_Set;
873 struct timeval endtime;
874 struct timeval timeout;
875 int c;
876 char buffer [256];
877 char *p = buffer;
879 keypad(stdscr, FALSE); /* disable intepreting keys by ncurses */
880 c = getch ();
881 while (c == ERR)
882 c = getch (); /* Sanity check, should be unnecessary */
883 learn_store_key (buffer, &p, c);
884 GET_TIME (endtime);
885 endtime.tv_usec += LEARN_TIMEOUT;
886 if (endtime.tv_usec > 1000000) {
887 endtime.tv_usec -= 1000000;
888 endtime.tv_sec++;
890 nodelay (stdscr, TRUE);
891 for (;;) {
892 while ((c = getch ()) == ERR) {
893 GET_TIME (timeout);
894 timeout.tv_usec = endtime.tv_usec - timeout.tv_usec;
895 if (timeout.tv_usec < 0)
896 timeout.tv_sec++;
897 timeout.tv_sec = endtime.tv_sec - timeout.tv_sec;
898 if (timeout.tv_sec >= 0 && timeout.tv_usec > 0) {
899 FD_ZERO (&Read_FD_Set);
900 FD_SET (input_fd, &Read_FD_Set);
901 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &timeout);
902 } else
903 break;
905 if (c == ERR)
906 break;
907 learn_store_key (buffer, &p, c);
909 keypad(stdscr, TRUE);
910 nodelay (stdscr, FALSE);
911 *p = 0;
912 return g_strdup (buffer);
915 /* xterm and linux console only: set keypad to numeric or application
916 mode. Only in application keypad mode it's possible to distinguish
917 the '+' key and the '+' on the keypad ('*' and '-' ditto)*/
918 void
919 numeric_keypad_mode (void)
921 if (console_flag || xterm_flag) {
922 fprintf (stdout, "\033>");
923 fflush (stdout);
927 void
928 application_keypad_mode (void)
930 if (console_flag || xterm_flag) {
931 fprintf (stdout, "\033=");
932 fflush (stdout);
937 /* A function to check if we're idle.
938 Currently checks only for key presses.
939 We could also check the mouse. */
940 int is_idle (void)
942 /* Check for incoming key presses *
943 * If there are any we say we're busy */
945 fd_set select_set;
946 struct timeval timeout;
947 FD_ZERO (&select_set);
948 FD_SET (0, &select_set);
949 timeout.tv_sec = 0;
950 timeout.tv_usec = 0;
951 select (1, &select_set, 0, 0, &timeout);
952 return ! FD_ISSET (0, &select_set);
957 get_modifier (void)
959 #ifdef HAVE_TEXTMODE_X11_SUPPORT
960 if (x11_display) {
961 Window root, child;
962 int root_x, root_y;
963 int win_x, win_y;
964 unsigned int mask;
965 Bool b;
966 int result = 0;
968 b = XQueryPointer(x11_display, x11_window, &root, &child,
969 &root_x, &root_y,
970 &win_x, &win_y,
971 &mask);
973 if (mask & ShiftMask)
974 result |= SHIFT_PRESSED;
975 if (mask & ControlMask)
976 result |= CONTROL_PRESSED;
977 return result;
978 } else
979 #endif
980 #ifdef __linux__
982 unsigned char modifiers;
984 modifiers = 6;
986 if (ioctl (0, TIOCLINUX, &modifiers) < 0)
987 return 0;
989 return (int) modifiers;
991 #else
992 return 0;
993 #endif
997 ctrl_pressed (void)
999 if (get_modifier () & CONTROL_PRESSED)
1000 return 1;
1001 else
1002 return 0;
1005 static void k_dispose (key_def *k)
1007 if (!k)
1008 return;
1009 k_dispose (k->child);
1010 k_dispose (k->next);
1011 g_free (k);
1014 static void s_dispose (SelectList *sel)
1016 if (!sel)
1017 return;
1019 s_dispose (sel->next);
1020 g_free (sel);
1023 void done_key ()
1025 k_dispose (keys);
1026 s_dispose (select_list);
1028 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1029 if (x11_display)
1030 XCloseDisplay (x11_display);
1031 #endif /* HAVE_TEXTMODE_X11_SUPPORT */