*** empty log message ***
[midnight-commander.git] / src / key.c
blob757fb195bd288ccade347e6dce345902b4289052
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 fd_set select_set;
84 static int disabled_channels = 0; /* Disable channels checking */
85 static int xgetch_second (void);
87 /* File descriptor monitoring add/remove routines */
88 typedef struct SelectList {
89 int fd;
90 select_fn callback;
91 void *info;
92 struct SelectList *next;
93 } SelectList;
95 static SelectList *select_list = 0;
97 void add_select_channel (int fd, select_fn callback, void *info)
99 SelectList *new;
101 new = g_new (SelectList, 1);
102 new->fd = fd;
103 new->callback = callback;
104 new->info = info;
105 new->next = select_list;
106 select_list = new;
109 void delete_select_channel (int fd)
111 SelectList *p = select_list;
112 SelectList *p_prev = 0;
113 SelectList *p_next;
115 while (p) {
116 if (p->fd == fd) {
117 p_next = p->next;
119 if (p_prev)
120 p_prev->next = p_next;
121 else
122 select_list = p_next;
124 g_free (p);
125 p = p_next;
126 continue;
129 p_prev = p;
130 p = p->next;
134 inline static int add_selects (fd_set *select_set)
136 SelectList *p;
137 int top_fd = 0;
139 if (disabled_channels)
140 return 0;
142 for (p = select_list; p; p = p->next){
143 FD_SET (p->fd, select_set);
144 if (p->fd > top_fd)
145 top_fd = p->fd;
147 return top_fd;
150 static void check_selects (fd_set *select_set)
152 SelectList *p;
154 if (disabled_channels)
155 return;
157 for (p = select_list; p; p = p->next)
158 if (FD_ISSET (p->fd, select_set))
159 (*p->callback)(p->fd, p->info);
162 void channels_down (void)
164 disabled_channels ++;
167 void channels_up (void)
169 if (!disabled_channels)
170 fprintf (stderr,
171 "Error: channels_up called with disabled_channels = 0\n");
172 disabled_channels--;
175 typedef const struct {
176 int code;
177 char *seq;
178 int action;
179 } key_define_t;
181 static key_define_t mc_bindings [] = {
182 { KEY_END, ESC_STR ">", MCKEY_NOACTION },
183 { KEY_HOME, ESC_STR "<", MCKEY_NOACTION },
184 { 0, 0, MCKEY_NOACTION },
187 /* Broken terminfo and termcap databases on xterminals */
188 static key_define_t xterm_key_defines [] = {
189 { KEY_F(1), ESC_STR "OP", MCKEY_NOACTION },
190 { KEY_F(2), ESC_STR "OQ", MCKEY_NOACTION },
191 { KEY_F(3), ESC_STR "OR", MCKEY_NOACTION },
192 { KEY_F(4), ESC_STR "OS", MCKEY_NOACTION },
193 { KEY_F(1), ESC_STR "[11~", MCKEY_NOACTION },
194 { KEY_F(2), ESC_STR "[12~", MCKEY_NOACTION },
195 { KEY_F(3), ESC_STR "[13~", MCKEY_NOACTION },
196 { KEY_F(4), ESC_STR "[14~", MCKEY_NOACTION },
197 { KEY_F(5), ESC_STR "[15~", MCKEY_NOACTION },
198 { KEY_F(6), ESC_STR "[17~", MCKEY_NOACTION },
199 { KEY_F(7), ESC_STR "[18~", MCKEY_NOACTION },
200 { KEY_F(8), ESC_STR "[19~", MCKEY_NOACTION },
201 { KEY_F(9), ESC_STR "[20~", MCKEY_NOACTION },
202 { KEY_F(10), ESC_STR "[21~", MCKEY_NOACTION },
203 { 0, 0, MCKEY_NOACTION },
206 static key_define_t mc_default_keys [] = {
207 { ESC_CHAR, ESC_STR, MCKEY_ESCAPE },
208 { ESC_CHAR, ESC_STR ESC_STR, MCKEY_NOACTION },
209 { 0, 0, MCKEY_NOACTION },
212 static void
213 define_sequences (key_define_t *kd)
215 int i;
217 for (i = 0; kd [i].code; i++)
218 define_sequence(kd [i].code, kd [i].seq, kd [i].action);
221 #ifdef HAVE_TEXTMODE_X11_SUPPORT
222 static Display *x11_display;
223 static Window x11_window;
224 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
226 /* This has to be called before slang_init or whatever routine
227 calls any define_sequence */
228 void init_key (void)
230 char *term = (char *) getenv ("TERM");
232 /* This has to be the first define_sequence */
233 /* So, we can assume that the first keys member has ESC */
234 define_sequences (mc_default_keys);
236 /* Terminfo on irix does not have some keys */
237 if ((!strncmp (term, "iris-ansi", 9)) || (!strncmp (term, "xterm", 5)))
238 define_sequences (xterm_key_defines);
240 define_sequences (mc_bindings);
242 /* load some additional keys (e.g. direct Alt-? support) */
243 load_xtra_key_defines();
245 #ifdef __QNX__
246 if (strncmp(term, "qnx", 3) == 0){
247 /* Modify the default value of use_8th_bit_as_meta: we would
248 * like to provide a working mc for a newbie who knows nothing
249 * about [Options|Display bits|Full 8 bits input]...
251 * Don't use 'meta'-bit, when we are dealing with a
252 * 'qnx*'-type terminal: clear the default value!
253 * These terminal types use 0xFF as an escape character,
254 * so use_8th_bit_as_meta==1 must not be enabled!
256 * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
257 * is not used now (doesn't even depend on use_8th_bit_as_meta
258 * as in mc-3.1.2)...GREAT!...no additional code is required!]
260 use_8th_bit_as_meta = 0;
262 #endif /* __QNX__ */
264 #ifdef HAVE_TEXTMODE_X11_SUPPORT
265 x11_display = XOpenDisplay (0);
266 if (x11_display)
267 x11_window = DefaultRootWindow (x11_display);
268 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
271 /* This has to be called after SLang_init_tty/slint_init */
272 void init_key_input_fd (void)
274 #ifdef HAVE_SLANG
275 input_fd = SLang_TT_Read_FD;
276 #endif
280 static void
281 xmouse_get_event (Gpm_Event *ev)
283 int btn;
284 static struct timeval tv1 = { 0, 0 }; /* Force first click as single */
285 static struct timeval tv2;
286 static int clicks;
287 static int last_btn = 0;
289 /* Decode Xterm mouse information to a GPM style event */
291 /* Variable btn has following meaning: */
292 /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
293 btn = xgetch () - 32;
295 /* There seems to be no way of knowing which button was released */
296 /* So we assume all the buttons were released */
298 if (btn == 3){
299 if (last_btn) {
300 ev->type = GPM_UP | (GPM_SINGLE << clicks);
301 ev->buttons = 0;
302 last_btn = 0;
303 GET_TIME (tv1);
304 clicks = 0;
305 } else {
306 /* Bogus event, maybe mouse wheel */
307 ev->type = 0;
309 } else {
310 ev->type = GPM_DOWN;
311 GET_TIME (tv2);
312 if (tv1.tv_sec && (DIF_TIME (tv1,tv2) < double_click_speed)){
313 clicks++;
314 clicks %= 3;
315 } else
316 clicks = 0;
318 switch (btn) {
319 case 0:
320 ev->buttons = GPM_B_LEFT;
321 break;
322 case 1:
323 ev->buttons = GPM_B_MIDDLE;
324 break;
325 case 2:
326 ev->buttons = GPM_B_RIGHT;
327 break;
328 default:
329 /* Nothing */
330 ev->type = 0;
331 ev->buttons = 0;
332 break;
334 last_btn = ev->buttons;
336 /* Coordinates are 33-based */
337 /* Transform them to 1-based */
338 ev->x = xgetch () - 32;
339 ev->y = xgetch () - 32;
342 static key_def *create_sequence (char *seq, int code, int action)
344 key_def *base, *p, *attach;
346 for (base = attach = NULL; *seq; seq++){
347 p = g_new (key_def, 1);
348 if (!base) base = p;
349 if (attach) attach->child = p;
351 p->ch = *seq;
352 p->code = code;
353 p->child = p->next = NULL;
354 if (!seq[1])
355 p->action = action;
356 else
357 p->action = MCKEY_NOACTION;
358 attach = p;
360 return base;
363 /* The maximum sequence length (32 + null terminator) */
364 #define SEQ_BUFFER_LEN 33
365 static int seq_buffer [SEQ_BUFFER_LEN];
366 static int *seq_append = 0;
368 static int push_char (int c)
370 if (!seq_append)
371 seq_append = seq_buffer;
373 if (seq_append == &(seq_buffer [SEQ_BUFFER_LEN-2]))
374 return 0;
375 *(seq_append++) = c;
376 *seq_append = 0;
377 return 1;
381 * Return 1 on success, 0 on error.
382 * An error happens if SEQ is a beginning of an existing longer sequence.
384 int define_sequence (int code, char *seq, int action)
386 key_def *base;
388 if (strlen (seq) > SEQ_BUFFER_LEN-1)
389 return 0;
391 for (base = keys; (base != 0) && *seq; ){
392 if (*seq == base->ch){
393 if (base->child == 0){
394 if (*(seq+1)){
395 base->child = create_sequence (seq+1, code, action);
396 return 1;
397 } else {
398 /* The sequence matches an existing one. */
399 base->code = code;
400 base->action = action;
401 return 1;
403 } else {
404 base = base->child;
405 seq++;
407 } else {
408 if (base->next)
409 base = base->next;
410 else {
411 base->next = create_sequence (seq, code, action);
412 return 1;
417 if (!*seq) {
418 /* Attempt to redefine a sequence with a shorter sequence. */
419 return 0;
422 keys = create_sequence (seq, code, action);
423 return 1;
426 static int *pending_keys;
428 static int
429 correct_key_code (int c)
431 /* This is needed on some OS that do not support ncurses and */
432 /* do some magic after read()ing the data */
433 if (c == '\r')
434 return '\n';
436 #ifdef IS_AIX
437 if (c == KEY_SCANCEL)
438 return '\t';
439 #endif
441 if (c == KEY_F(0))
442 return KEY_F(10);
444 if (!alternate_plus_minus)
445 switch (c) {
446 case KEY_KP_ADD: c = '+'; break;
447 case KEY_KP_SUBTRACT: c = '-'; break;
448 case KEY_KP_MULTIPLY: c = '*'; break;
451 return c;
454 int get_key_code (int no_delay)
456 int c;
457 static key_def *this = NULL, *parent;
458 static struct timeval esctime = { -1, -1 };
459 static int lastnodelay = -1;
461 if (no_delay != lastnodelay) {
462 this = NULL;
463 lastnodelay = no_delay;
466 pend_send:
467 if (pending_keys){
468 int d = *pending_keys++;
469 check_pend:
470 if (!*pending_keys){
471 pending_keys = 0;
472 seq_append = 0;
474 if (d == ESC_CHAR && pending_keys){
475 d = ALT(*pending_keys++);
476 goto check_pend;
478 if ((d & 0x80) && use_8th_bit_as_meta)
479 d = ALT(d & 0x7f);
480 this = NULL;
481 return correct_key_code (d);
484 nodelay_try_again:
485 if (no_delay) {
486 nodelay (stdscr, TRUE);
488 c = xgetch ();
489 #if defined(USE_NCURSES) && defined(KEY_RESIZE)
490 if (c == KEY_RESIZE)
491 goto nodelay_try_again;
492 #endif
493 if (no_delay) {
494 nodelay (stdscr, FALSE);
495 if (c == ERR) {
496 if (this != NULL && parent != NULL &&
497 parent->action == MCKEY_ESCAPE && old_esc_mode) {
498 struct timeval current, timeout;
500 if (esctime.tv_sec == -1)
501 return ERR;
502 GET_TIME (current);
503 timeout.tv_sec = ESCMODE_TIMEOUT / 1000000 + esctime.tv_sec;
504 timeout.tv_usec = ESCMODE_TIMEOUT % 1000000 + esctime.tv_usec;
505 if (timeout.tv_usec > 1000000) {
506 timeout.tv_usec -= 1000000;
507 timeout.tv_sec++;
509 if (current.tv_sec < timeout.tv_sec)
510 return ERR;
511 if (current.tv_sec == timeout.tv_sec &&
512 current.tv_usec < timeout.tv_usec)
513 return ERR;
514 this = NULL;
515 pending_keys = seq_append = NULL;
516 return ESC_CHAR;
518 return ERR;
520 } else if (c == ERR){
521 /* Maybe we got an incomplete match.
522 This we do only in delay mode, since otherwise
523 xgetch can return ERR at any time. */
524 if (seq_append) {
525 pending_keys = seq_buffer;
526 goto pend_send;
528 this = NULL;
529 return ERR;
532 /* Search the key on the root */
533 if (!no_delay || this == NULL) {
534 this = keys;
535 parent = NULL;
537 if ((c & 0x80) && use_8th_bit_as_meta) {
538 c &= 0x7f;
540 /* The first sequence defined starts with esc */
541 parent = keys;
542 this = keys->child;
545 while (this){
546 if (c == this->ch){
547 if (this->child){
548 if (!push_char (c)){
549 pending_keys = seq_buffer;
550 goto pend_send;
552 parent = this;
553 this = this->child;
554 if (parent->action == MCKEY_ESCAPE && old_esc_mode) {
555 if (no_delay) {
556 GET_TIME (esctime);
557 if (this == NULL) {
558 /* Shouldn't happen */
559 fprintf (stderr, "Internal error\n");
560 exit (1);
562 goto nodelay_try_again;
564 esctime.tv_sec = -1;
565 c = xgetch_second ();
566 if (c == ERR) {
567 pending_keys = seq_append = NULL;
568 this = NULL;
569 return ESC_CHAR;
571 } else {
572 if (no_delay)
573 goto nodelay_try_again;
574 c = xgetch ();
576 } else {
577 /* We got a complete match, return and reset search */
578 int code;
580 pending_keys = seq_append = NULL;
581 code = this->code;
582 this = NULL;
583 return correct_key_code (code);
585 } else {
586 if (this->next)
587 this = this->next;
588 else {
589 if (parent != NULL && parent->action == MCKEY_ESCAPE) {
590 /* This is just to save a lot of define_sequences */
591 if (isalpha(c)
592 || (c == '\n') || (c == '\t') || (c == XCTRL('h'))
593 || (c == KEY_BACKSPACE) || (c == '!') || (c == '\r')
594 || c == 127 || c == '+' || c == '-' || c == '\\'
595 || c == '?')
596 c = ALT(c);
597 else if (isdigit(c))
598 c = KEY_F (c-'0');
599 else if (c == ' ')
600 c = ESC_CHAR;
601 pending_keys = seq_append = NULL;
602 this = NULL;
603 return correct_key_code (c);
605 /* Did not find a match or {c} was changed in the if above,
606 so we have to return everything we had skipped
608 push_char (c);
609 pending_keys = seq_buffer;
610 goto pend_send;
614 this = NULL;
615 return correct_key_code (c);
618 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
619 static void
620 try_channels (int set_timeout)
622 struct timeval timeout;
623 static fd_set select_set;
624 struct timeval *timeptr;
625 int v;
626 int maxfdp;
628 while (1){
629 FD_ZERO (&select_set);
630 FD_SET (input_fd, &select_set); /* Add stdin */
631 maxfdp = max (add_selects (&select_set), input_fd);
633 if (set_timeout){
634 timeout.tv_sec = 0;
635 timeout.tv_usec = 100000;
636 timeptr = &timeout;
637 } else
638 timeptr = 0;
640 v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr);
641 if (v > 0){
642 check_selects (&select_set);
643 if (FD_ISSET (input_fd, &select_set))
644 return;
649 /* Workaround for System V Curses vt100 bug */
650 static int getch_with_delay (void)
652 int c;
654 /* This routine could be used on systems without mouse support,
655 so we need to do the select check :-( */
656 while (1){
657 if (!pending_keys)
658 try_channels (0);
660 /* Try to get a character */
661 c = get_key_code (0);
662 if (c != ERR)
663 break;
664 /* Failed -> wait 0.1 secs and try again */
665 try_channels (1);
667 /* Success -> return the character */
668 return c;
671 extern int max_dirt_limit;
673 /* Returns a character read from stdin with appropriate interpretation */
674 /* Also takes care of generated mouse events */
675 /* Returns EV_MOUSE if it is a mouse event */
676 /* Returns EV_NONE if non-blocking or interrupt set and nothing was done */
677 int get_event (Gpm_Event *event, int redo_event, int block)
679 int c;
680 static int flag; /* Return value from select */
681 #ifdef HAVE_LIBGPM
682 static Gpm_Event ev; /* Mouse event */
683 #endif
684 struct timeval timeout;
685 struct timeval *time_addr = NULL;
686 static int dirty = 3;
688 if ((dirty == 3) || is_idle ()){
689 mc_refresh ();
690 doupdate ();
691 dirty = 1;
692 } else
693 dirty++;
695 vfs_timeout_handler ();
697 /* Ok, we use (event->x < 0) to signal that the event does not contain
698 a suitable position for the mouse, so we can't use show_mouse_pointer
699 on it.
701 if (event->x > 0){
702 show_mouse_pointer (event->x, event->y);
703 if (!redo_event)
704 event->x = -1;
707 /* Repeat if using mouse */
708 while (mouse_enabled && !pending_keys) {
709 if (mouse_enabled) {
710 int maxfdp;
711 FD_ZERO (&select_set);
712 FD_SET (input_fd, &select_set);
713 maxfdp = max (add_selects (&select_set), input_fd);
715 #ifdef HAVE_LIBGPM
716 if (gpm_fd == -1) {
717 /* Connection to gpm broken, possibly gpm has died */
718 mouse_enabled = 0;
719 use_mouse_p = MOUSE_NONE;
721 if (mouse_enabled && use_mouse_p == MOUSE_GPM) {
722 FD_SET (gpm_fd, &select_set);
723 maxfdp = max (maxfdp, gpm_fd);
725 #endif
727 if (redo_event){
728 timeout.tv_usec = mou_auto_repeat * 1000;
729 timeout.tv_sec = 0;
731 time_addr = &timeout;
732 } else {
733 int seconds;
735 if ((seconds = vfs_timeouts ())){
736 /* the timeout could be improved and actually be
737 * the number of seconds until the next vfs entry
738 * timeouts in the stamp list.
741 timeout.tv_sec = seconds;
742 timeout.tv_usec = 0;
743 time_addr = &timeout;
744 } else
745 time_addr = NULL;
748 if (!block){
749 time_addr = &timeout;
750 timeout.tv_sec = 0;
751 timeout.tv_usec = 0;
753 enable_interrupt_key ();
754 flag = select (maxfdp + 1, &select_set, NULL, NULL, time_addr);
755 disable_interrupt_key ();
757 /* select timed out: it could be for any of the following reasons:
758 * redo_event -> it was because of the MOU_REPEAT handler
759 * !block -> we did not block in the select call
760 * else -> 10 second timeout to check the vfs status.
762 if (flag == 0){
763 if (redo_event)
764 return EV_MOUSE;
765 if (!block)
766 return EV_NONE;
767 vfs_timeout_handler ();
769 if (flag == -1 && errno == EINTR)
770 return EV_NONE;
772 check_selects (&select_set);
774 if (FD_ISSET (input_fd, &select_set))
775 break;
777 #ifdef HAVE_LIBGPM
778 if (mouse_enabled && use_mouse_p == MOUSE_GPM
779 && FD_ISSET (gpm_fd, &select_set)) {
780 Gpm_GetEvent (&ev);
781 Gpm_FitEvent (&ev);
782 *event = ev;
783 return EV_MOUSE;
785 #endif /* !HAVE_LIBGPM */
787 #ifndef HAVE_SLANG
788 flag = is_wintouched(stdscr);
789 untouchwin (stdscr);
790 #endif /* !HAVE_SLANG */
791 c = block ? getch_with_delay () : get_key_code(1);
793 #ifndef HAVE_SLANG
794 if (flag)
795 touchwin (stdscr);
796 #endif /* !HAVE_SLANG */
798 if (c == MCKEY_MOUSE
799 #ifdef KEY_MOUSE
800 || c == KEY_MOUSE
801 #endif /* KEY_MOUSE */
803 /* Mouse event */
804 xmouse_get_event (event);
805 if (event->type)
806 return EV_MOUSE;
807 else
808 return EV_NONE;
811 return c;
814 /* Returns a key press, mouse events are discarded */
815 int mi_getch ()
817 Gpm_Event ev;
818 int key;
820 ev.x = -1;
821 while ((key = get_event (&ev, 0, 1)) == EV_NONE)
823 return key;
826 static int xgetch_second (void)
828 fd_set Read_FD_Set;
829 int c;
830 struct timeval timeout;
832 timeout.tv_sec = ESCMODE_TIMEOUT / 1000000;
833 timeout.tv_usec = ESCMODE_TIMEOUT % 1000000;
834 nodelay (stdscr, TRUE);
835 FD_ZERO (&Read_FD_Set);
836 FD_SET (input_fd, &Read_FD_Set);
837 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &timeout);
838 c = xgetch ();
839 nodelay (stdscr, FALSE);
840 return c;
843 static void
844 learn_store_key (char *buffer, char **p, int c)
846 if (*p - buffer > 253)
847 return;
848 if (c == ESC_CHAR) {
849 *(*p)++ = '\\';
850 *(*p)++ = 'e';
851 } else if (c < ' ') {
852 *(*p)++ = '^';
853 *(*p)++ = c + 'a' - 1;
854 } else if (c == '^') {
855 *(*p)++ = '^';
856 *(*p)++ = '^';
857 } else
858 *(*p)++ = (char) c;
861 char *learn_key (void)
863 /* LEARN_TIMEOUT in usec */
864 #define LEARN_TIMEOUT 200000
866 fd_set Read_FD_Set;
867 struct timeval endtime;
868 struct timeval timeout;
869 int c;
870 char buffer [256];
871 char *p = buffer;
873 keypad(stdscr, FALSE); /* disable intepreting keys by ncurses */
874 c = xgetch ();
875 while (c == ERR)
876 c = xgetch (); /* Sanity check, should be unnecessary */
877 learn_store_key (buffer, &p, c);
878 GET_TIME (endtime);
879 endtime.tv_usec += LEARN_TIMEOUT;
880 if (endtime.tv_usec > 1000000) {
881 endtime.tv_usec -= 1000000;
882 endtime.tv_sec++;
884 nodelay (stdscr, TRUE);
885 for (;;) {
886 while ((c = xgetch ()) == ERR) {
887 GET_TIME (timeout);
888 timeout.tv_usec = endtime.tv_usec - timeout.tv_usec;
889 if (timeout.tv_usec < 0)
890 timeout.tv_sec++;
891 timeout.tv_sec = endtime.tv_sec - timeout.tv_sec;
892 if (timeout.tv_sec >= 0 && timeout.tv_usec > 0) {
893 FD_ZERO (&Read_FD_Set);
894 FD_SET (input_fd, &Read_FD_Set);
895 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &timeout);
896 } else
897 break;
899 if (c == ERR)
900 break;
901 learn_store_key (buffer, &p, c);
903 keypad(stdscr, TRUE);
904 nodelay (stdscr, FALSE);
905 *p = 0;
906 return g_strdup (buffer);
909 /* xterm and linux console only: set keypad to numeric or application
910 mode. Only in application keypad mode it's possible to distinguish
911 the '+' key and the '+' on the keypad ('*' and '-' ditto)*/
912 void
913 numeric_keypad_mode (void)
915 if (console_flag || xterm_flag) {
916 fprintf (stdout, "\033>");
917 fflush (stdout);
921 void
922 application_keypad_mode (void)
924 if (console_flag || xterm_flag) {
925 fprintf (stdout, "\033=");
926 fflush (stdout);
931 /* A function to check if we're idle.
932 Currently checks only for key presses.
933 We could also check the mouse. */
934 int is_idle (void)
936 /* Check for incoming key presses *
937 * If there are any we say we're busy */
939 fd_set select_set;
940 struct timeval timeout;
941 FD_ZERO (&select_set);
942 FD_SET (0, &select_set);
943 timeout.tv_sec = 0;
944 timeout.tv_usec = 0;
945 select (1, &select_set, 0, 0, &timeout);
946 return ! FD_ISSET (0, &select_set);
951 get_modifier (void)
953 #ifdef HAVE_TEXTMODE_X11_SUPPORT
954 if (x11_display) {
955 Window root, child;
956 int root_x, root_y;
957 int win_x, win_y;
958 unsigned int mask;
959 Bool b;
960 int result = 0;
962 b = XQueryPointer(x11_display, x11_window, &root, &child,
963 &root_x, &root_y,
964 &win_x, &win_y,
965 &mask);
967 if (mask & ShiftMask)
968 result |= SHIFT_PRESSED;
969 if (mask & ControlMask)
970 result |= CONTROL_PRESSED;
971 return result;
972 } else
973 #endif
974 #ifdef __linux__
976 unsigned char modifiers;
978 modifiers = 6;
980 if (ioctl (0, TIOCLINUX, &modifiers) < 0)
981 return 0;
983 return (int) modifiers;
985 #else
986 return 0;
987 #endif
991 ctrl_pressed (void)
993 if (get_modifier () & CONTROL_PRESSED)
994 return 1;
995 else
996 return 0;
999 static void k_dispose (key_def *k)
1001 if (!k)
1002 return;
1003 k_dispose (k->child);
1004 k_dispose (k->next);
1005 g_free (k);
1008 static void s_dispose (SelectList *sel)
1010 if (!sel)
1011 return;
1013 s_dispose (sel->next);
1014 g_free (sel);
1017 void done_key ()
1019 k_dispose (keys);
1020 s_dispose (select_list);
1022 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1023 if (x11_display)
1024 XCloseDisplay (x11_display);
1025 #endif /* HAVE_TEXTMODE_X11_SUPPORT */