- Start rosapps rearrange and cleanup process.
[reactos.git] / rosapps / applications / mc / src / key.c
1 /* Keyboard support routines.
2
3 Copyright (C) 1994,1995 the Free Software Foundation.
4
5 Written by: 1994, 1995 Miguel de Icaza.
6 1994, 1995 Janne Kukonlehto.
7 1995 Jakub Jelinek.
8 1997 Norbert Warmuth
9
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.
14
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.
19
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., 675 Mass Ave, Cambridge, MA 02139, USA. */
23
24 #include <config.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <sys/types.h>
28 #include <string.h>
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32 #include <sys/types.h> /* FD_ZERO et al */
33 #ifndef SCO_FLAVOR
34 /* alex: sys/select.h defines struct timeval */
35 # include <sys/time.h> /* struct timeval */
36 #endif /* SCO_FLAVOR */
37 #if HAVE_SYS_SELECT_H
38 # include <sys/select.h>
39 #endif
40 #include "tty.h"
41 #include <ctype.h>
42 #include <errno.h>
43 #include <malloc.h>
44 #include "util.h" /* For xmalloc prototype */
45 #include "mad.h" /* The memory debugger */
46 #include "global.h"
47 #include "mouse.h"
48 #include "key.h"
49 #include "main.h"
50 #include "file.h"
51 #include "win.h"
52 #include "cons.saver.h"
53 #include "../vfs/vfs.h"
54
55 #ifdef __linux__
56 # if defined(__GLIBC__) && (__GLIBC__ < 2)
57 # include <linux/termios.h> /* This is needed for TIOCLINUX */
58 # else
59 # include <termios.h>
60 # endif
61 # include <sys/ioctl.h>
62 #endif
63
64 #include "x.h"
65
66 /* "$Id$" */
67
68 /* This macros were stolen from gpm 0.15 */
69 #define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *)NULL))
70 #define DIF_TIME(t1,t2) ((t2.tv_sec -t1.tv_sec) *1000+ \
71 (t2.tv_usec-t1.tv_usec)/1000)
72
73 /* timeout for old_esc_mode in usec */
74 #define ESCMODE_TIMEOUT 1000000
75
76 int mou_auto_repeat = 100;
77 int double_click_speed = 250;
78 int old_esc_mode = 0;
79
80 int use_8th_bit_as_meta = 1;
81
82 #ifndef HAVE_X
83 typedef struct key_def {
84 char ch; /* Holds the matching char code */
85 int code; /* The code returned, valid if child == NULL */
86 struct key_def *next;
87 struct key_def *child; /* sequence continuation */
88 int action; /* optional action to be done. Now used only
89 to mark that we are just after the first
90 Escape */
91 } key_def;
92
93 /* This holds all the key definitions */
94 static key_def *keys = 0;
95 #endif
96
97 static int input_fd;
98 static fd_set select_set;
99 static int disabled_channels = 0; /* Disable channels checking */
100 int xgetch_second (void);
101
102 #ifndef PORT_HAS_FILE_HANDLERS
103 /* File descriptor monitoring add/remove routines */
104 typedef struct SelectList {
105 int fd;
106 select_fn callback;
107 void *info;
108 struct SelectList *next;
109 } SelectList;
110
111 SelectList *select_list = 0;
112
113 void add_select_channel (int fd, select_fn callback, void *info)
114 {
115 SelectList *new;
116
117 new = xmalloc (sizeof (SelectList), "add_select_channel");
118 new->fd = fd;
119 new->callback = callback;
120 new->info = info;
121 new->next = select_list;
122 select_list = new;
123 }
124
125 void delete_select_channel (int fd)
126 {
127 SelectList *p = select_list;
128 SelectList *prev = 0;
129
130 while (p){
131 if (p->fd == fd){
132 if (prev)
133 prev->next = p->next;
134 else
135 select_list = p->next;
136 free (p);
137 }
138 prev = p;
139 p = p->next;
140 }
141 }
142
143 inline static int add_selects (fd_set *select_set)
144 {
145 SelectList *p;
146 int top_fd = 0;
147
148 if (disabled_channels)
149 return 0;
150
151 for (p = select_list; p; p = p->next){
152 FD_SET (p->fd, select_set);
153 if (p->fd > top_fd)
154 top_fd = p->fd;
155 }
156 return top_fd;
157 }
158
159 static void check_selects (fd_set *select_set)
160 {
161 SelectList *p;
162
163 if (disabled_channels)
164 return;
165
166 for (p = select_list; p; p = p->next)
167 if (FD_ISSET (p->fd, select_set))
168 (*p->callback)(p->fd, p->info);
169 }
170 #endif
171
172 void channels_down (void)
173 {
174 disabled_channels ++;
175 }
176
177 void channels_up (void)
178 {
179 if (!disabled_channels)
180 fprintf (stderr,
181 "Error: channels_up called with disabled_channels = 0\n");
182 disabled_channels--;
183 }
184
185 typedef struct {
186 int code;
187 char *seq;
188 int action;
189 } key_define_t;
190
191 #ifndef HAVE_X
192 key_define_t mc_bindings [] = {
193 { KEY_END, ESC_STR ">", MCKEY_NOACTION },
194 { KEY_HOME, ESC_STR "<", MCKEY_NOACTION },
195
196 #ifdef linux
197 /* Incredible, but many Linuxes still have old databases */
198 { KEY_IC, ESC_STR "[2~", MCKEY_NOACTION },
199 #endif
200 { 0, 0, MCKEY_NOACTION },
201 };
202
203 /* Broken terminfo and termcap databases on xterminals */
204 key_define_t xterm_key_defines [] = {
205 { KEY_F(1), ESC_STR "OP", MCKEY_NOACTION },
206 { KEY_F(2), ESC_STR "OQ", MCKEY_NOACTION },
207 { KEY_F(3), ESC_STR "OR", MCKEY_NOACTION },
208 { KEY_F(4), ESC_STR "OS", MCKEY_NOACTION },
209 { KEY_F(1), ESC_STR "[11~", MCKEY_NOACTION },
210 { KEY_F(2), ESC_STR "[12~", MCKEY_NOACTION },
211 { KEY_F(3), ESC_STR "[13~", MCKEY_NOACTION },
212 { KEY_F(4), ESC_STR "[14~", MCKEY_NOACTION },
213 { KEY_F(5), ESC_STR "[15~", MCKEY_NOACTION },
214 { KEY_F(6), ESC_STR "[17~", MCKEY_NOACTION },
215 { KEY_F(7), ESC_STR "[18~", MCKEY_NOACTION },
216 { KEY_F(8), ESC_STR "[19~", MCKEY_NOACTION },
217 { KEY_F(9), ESC_STR "[20~", MCKEY_NOACTION },
218 { KEY_F(10), ESC_STR "[21~", MCKEY_NOACTION },
219 { 0, 0, MCKEY_NOACTION },
220 };
221
222 key_define_t mc_default_keys [] = {
223 { ESC_CHAR, ESC_STR, MCKEY_ESCAPE },
224 { ESC_CHAR, ESC_STR ESC_STR, MCKEY_NOACTION },
225 { 0, 0, MCKEY_NOACTION },
226 };
227 #endif
228
229 void define_sequences (key_define_t *kd)
230 {
231 #ifndef HAVE_X
232 int i;
233
234 for (i = 0; kd [i].code; i++)
235 define_sequence(kd [i].code, kd [i].seq, kd [i].action);
236 #endif
237 }
238
239 /* This has to be called before slang_init or whatever routine
240 calls any define_sequence */
241 void init_key (void)
242 {
243 #ifndef HAVE_X
244 char *term = (char *) getenv ("TERM");
245
246 /* This has to be the first define_sequence */
247 /* So, we can assume that the first keys member has ESC */
248 define_sequences (mc_default_keys);
249
250 /* Terminfo on irix does not have some keys */
251 if ((!strncmp (term, "iris-ansi", 9)) || (!strncmp (term, "xterm", 5)))
252 define_sequences (xterm_key_defines);
253
254 define_sequences (mc_bindings);
255
256 /* load some additional keys (e.g. direct Alt-? support) */
257 load_xtra_key_defines();
258
259 #ifdef __QNX__
260 if (strncmp(term, "qnx", 3) == 0){
261 /* Modify the default value of use_8th_bit_as_meta: we would
262 * like to provide a working mc for a newbie who knows nothing
263 * about [Options|Display bits|Full 8 bits input]...
264 *
265 * Don't use 'meta'-bit, when we are dealing with a
266 * 'qnx*'-type terminal: clear the default value!
267 * These terminal types use 0xFF as an escape character,
268 * so use_8th_bit_as_meta==1 must not be enabled!
269 *
270 * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
271 * is not used now (doesn't even depend on use_8th_bit_as_meta
272 * as in mc-3.1.2)...GREAT!...no additional code is required!]
273 */
274 use_8th_bit_as_meta = 0;
275 }
276 #endif /* __QNX__ */
277 #endif /* !HAVE_X */
278 }
279
280 /* This has to be called after SLang_init_tty/slint_init */
281 void init_key_input_fd (void)
282 {
283 #ifndef HAVE_X
284 #ifdef HAVE_SLANG
285 input_fd = SLang_TT_Read_FD;
286 #endif
287 #endif /* !HAVE_X */
288 }
289
290
291 #ifndef HAVE_X
292 void xmouse_get_event (Gpm_Event *ev)
293 {
294 int btn;
295 static struct timeval tv1 = { 0, 0 }; /* Force first click as single */
296 static struct timeval tv2;
297 static int clicks;
298
299 /* Decode Xterm mouse information to a GPM style event */
300
301 /* Variable btn has following meaning: */
302 /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
303 btn = xgetch () - 32;
304
305 /* There seems to be no way of knowing which button was released */
306 /* So we assume all the buttons were released */
307
308 if (btn == 3){
309 ev->type = GPM_UP | (GPM_SINGLE << clicks);
310 ev->buttons = 0;
311 GET_TIME (tv1);
312 clicks = 0;
313 } else {
314 ev->type = GPM_DOWN;
315 GET_TIME (tv2);
316 if (tv1.tv_sec && (DIF_TIME (tv1,tv2) < double_click_speed)){
317 clicks++;
318 clicks %= 3;
319 } else
320 clicks = 0;
321
322 switch (btn) {
323 case 0:
324 ev->buttons = GPM_B_LEFT;
325 break;
326 case 1:
327 ev->buttons = GPM_B_MIDDLE;
328 break;
329 case 2:
330 ev->buttons = GPM_B_RIGHT;
331 break;
332 default:
333 /* Nothing */
334 break;
335 }
336 }
337 /* Coordinates are 33-based */
338 /* Transform them to 1-based */
339 ev->x = xgetch () - 32;
340 ev->y = xgetch () - 32;
341 }
342
343 static key_def *create_sequence (char *seq, int code, int action)
344 {
345 key_def *base, *p, *attach;
346
347 for (base = attach = NULL; *seq; seq++){
348 p = xmalloc (sizeof (key_def), "create_sequence");
349 if (!base) base = p;
350 if (attach) attach->child = p;
351
352 p->ch = *seq;
353 p->code = code;
354 p->child = p->next = NULL;
355 if (!seq[1])
356 p->action = action;
357 else
358 p->action = MCKEY_NOACTION;
359 attach = p;
360 }
361 return base;
362 }
363
364 /* The maximum sequence length (32 + null terminator) */
365 static int seq_buffer [33];
366 static int *seq_append = 0;
367
368 static int push_char (int c)
369 {
370 if (!seq_append)
371 seq_append = seq_buffer;
372
373 if (seq_append == &(seq_buffer [sizeof (seq_buffer)-2]))
374 return 0;
375 *(seq_append++) = c;
376 *seq_append = 0;
377 return 1;
378 }
379 #endif /* !HAVE_X */
380
381 void define_sequence (int code, char *seq, int action)
382 {
383 #ifndef HAVE_X
384 key_def *base;
385
386 if (strlen (seq) > sizeof (seq_buffer)-1)
387 return;
388
389 for (base = keys; (base != 0) && *seq; ){
390 if (*seq == base->ch){
391 if (base->child == 0){
392 if (*(seq+1)){
393 base->child = create_sequence (seq+1, code, action);
394 return;
395 } else {
396 /* The sequence clashes */
397 return;
398 }
399 } else {
400 base = base->child;
401 seq++;
402 }
403 } else {
404 if (base->next)
405 base = base->next;
406 else {
407 base->next = create_sequence (seq, code, action);
408 return;
409 }
410 }
411 }
412 keys = create_sequence (seq, code, action);
413 #endif
414 }
415
416 #ifndef HAVE_X
417 static int *pending_keys;
418 #endif
419
420 int correct_key_code (int c)
421 {
422 /* This is needed on some OS that do not support ncurses and */
423 /* do some magic after read()ing the data */
424 if (c == '\r')
425 return '\n';
426
427 #ifdef IS_AIX
428 if (c == KEY_SCANCEL)
429 return '\t';
430 #endif
431
432 if (c == KEY_F(0))
433 return KEY_F(10);
434
435 if (!alternate_plus_minus)
436 switch (c) {
437 case KEY_KP_ADD: c = '+'; break;
438 case KEY_KP_SUBTRACT: c = '-'; break;
439 case KEY_KP_MULTIPLY: c = '*'; break;
440 }
441
442 return c;
443 }
444
445 int get_key_code (int no_delay)
446 {
447 #ifndef HAVE_X
448 int c;
449 static key_def *this = NULL, *parent;
450 static struct timeval esctime = { -1, -1 };
451 static int lastnodelay = -1;
452
453 if (no_delay != lastnodelay) {
454 this = NULL;
455 lastnodelay = no_delay;
456 }
457
458 pend_send:
459 if (pending_keys){
460 int d = *pending_keys++;
461 check_pend:
462 if (!*pending_keys){
463 pending_keys = 0;
464 seq_append = 0;
465 }
466 if (d == ESC_CHAR && pending_keys){
467 d = ALT(*pending_keys++);
468 goto check_pend;
469 }
470 if ((d & 0x80) && use_8th_bit_as_meta)
471 d = ALT(d & 0x7f);
472 this = NULL;
473 return correct_key_code (d);
474 }
475
476 nodelay_try_again:
477 if (no_delay) {
478 #ifdef BUGGY_CURSES
479 wtimeout(stdscr, 500);
480 #else
481 nodelay (stdscr, TRUE);
482 #endif
483 }
484 c = xgetch ();
485 if (no_delay) {
486 #ifdef BUGGY_CURSES
487 notimeout (stdscr, TRUE);
488 #else
489 nodelay (stdscr, FALSE);
490 #endif
491 if (c == ERR) {
492 if (this != NULL && parent != NULL &&
493 parent->action == MCKEY_ESCAPE && old_esc_mode) {
494 struct timeval current, timeout;
495
496 if (esctime.tv_sec == -1)
497 return ERR;
498 GET_TIME (current);
499 timeout.tv_sec = ESCMODE_TIMEOUT / 1000000 + esctime.tv_sec;
500 timeout.tv_usec = ESCMODE_TIMEOUT % 1000000 + esctime.tv_usec;
501 if (timeout.tv_usec > 1000000) {
502 timeout.tv_usec -= 1000000;
503 timeout.tv_sec++;
504 }
505 if (current.tv_sec < timeout.tv_sec)
506 return ERR;
507 if (current.tv_sec == timeout.tv_sec &&
508 current.tv_usec < timeout.tv_usec)
509 return ERR;
510 this = NULL;
511 pending_keys = seq_append = NULL;
512 return ESC_CHAR;
513 }
514 return ERR;
515 }
516 } else if (c == ERR){
517 /* Maybe we got an incomplete match.
518 This we do only in delay mode, since otherwise
519 xgetch can return ERR at any time. */
520 if (seq_append) {
521 pending_keys = seq_buffer;
522 goto pend_send;
523 }
524 this = NULL;
525 return ERR;
526 }
527
528 /* Search the key on the root */
529 if (!no_delay || this == NULL) {
530 this = keys;
531 parent = NULL;
532
533 if ((c & 0x80) && use_8th_bit_as_meta) {
534 c &= ~0x7f;
535
536 /* The first sequence defined starts with esc */
537 parent = keys;
538 this = keys->child;
539 }
540 }
541 while (this){
542 if (c == this->ch){
543 if (this->child){
544 if (!push_char (c)){
545 pending_keys = seq_buffer;
546 goto pend_send;
547 }
548 parent = this;
549 this = this->child;
550 if (parent->action == MCKEY_ESCAPE && old_esc_mode) {
551 if (no_delay) {
552 GET_TIME (esctime);
553 if (this == NULL) {
554 /* Shouldn't happen */
555 fprintf (stderr, "Internal error\n");
556 exit (1);
557 }
558 goto nodelay_try_again;
559 }
560 esctime.tv_sec = -1;
561 c = xgetch_second ();
562 if (c == ERR) {
563 pending_keys = seq_append = NULL;
564 this = NULL;
565 return ESC_CHAR;
566 }
567 } else {
568 if (no_delay)
569 goto nodelay_try_again;
570 c = xgetch ();
571 }
572 } else {
573 /* We got a complete match, return and reset search */
574 int code;
575
576 pending_keys = seq_append = NULL;
577 code = this->code;
578 this = NULL;
579 return correct_key_code (code);
580 }
581 } else {
582 if (this->next)
583 this = this->next;
584 else {
585 if (parent != NULL && parent->action == MCKEY_ESCAPE) {
586 /* This is just to save a lot of define_sequences */
587 if (isalpha(c)
588 || (c == '\n') || (c == '\t') || (c == XCTRL('h'))
589 || (c == KEY_BACKSPACE) || (c == '!') || (c == '\r')
590 || c == 127 || c == '+' || c == '-' || c == '\\'
591 || c == '?')
592 c = ALT(c);
593 else if (isdigit(c))
594 c = KEY_F (c-'0');
595 else if (c == ' ')
596 c = ESC_CHAR;
597 pending_keys = seq_append = NULL;
598 this = NULL;
599 return correct_key_code (c);
600 }
601 /* Did not find a match or {c} was changed in the if above,
602 so we have to return everything we had skipped
603 */
604 push_char (c);
605 pending_keys = seq_buffer;
606 goto pend_send;
607 }
608 }
609 }
610 this = NULL;
611 return correct_key_code (c);
612 #else
613 return ERR;
614 #endif /* HAVE_X */
615 }
616
617 #ifndef PORT_HAS_FILE_HANDLERS
618 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
619 void try_channels (int set_timeout)
620 {
621 struct timeval timeout;
622 static fd_set select_set;
623 struct timeval *timeptr;
624 int v;
625
626 while (1){
627 FD_ZERO (&select_set);
628 FD_SET (input_fd, &select_set); /* Add stdin */
629 add_selects (&select_set);
630
631 if (set_timeout){
632 timeout.tv_sec = 0;
633 timeout.tv_usec = 100000;
634 timeptr = &timeout;
635 } else
636 timeptr = 0;
637
638 v = select (FD_SETSIZE, &select_set, NULL, NULL, timeptr);
639 if (v > 0){
640 check_selects (&select_set);
641 if (FD_ISSET (input_fd, &select_set))
642 return;
643 }
644 }
645 }
646
647 #ifndef HAVE_X
648 /* Workaround for System V Curses vt100 bug */
649 static int getch_with_delay (void)
650 {
651 int c;
652
653 /* This routine could be used on systems without mouse support,
654 so we need to do the select check :-( */
655 while (1){
656 if (!pending_keys)
657 try_channels (0);
658
659 /* Try to get a character */
660 c = get_key_code (0);
661 if (c != ERR)
662 break;
663 /* Failed -> wait 0.1 secs and try again */
664 try_channels (1);
665 }
666 /* Success -> return the character */
667 return c;
668 }
669 #endif /* !HAVE_X */
670
671 #ifndef HAVE_LIBGPM
672 #define gpm_flag 0
673 #endif
674 #endif /* !HAVE_FILE_HANDLERS */
675
676 extern int max_dirt_limit;
677
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 */
682 int get_event (Gpm_Event *event, int redo_event, int block)
683 {
684 #ifndef HAVE_X
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;
693
694 if ((dirty == 3) || is_idle ()){
695 mc_refresh ();
696 doupdate ();
697 dirty = 1;
698 } else
699 dirty++;
700
701 vfs_timeout_handler ();
702
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.
706 */
707 if (event->x > 0){
708 show_mouse_pointer (event->x, event->y);
709 if (!redo_event)
710 event->x = -1;
711 }
712
713 /* Repeat if using mouse */
714 while ((xmouse_flag || gpm_flag) && !pending_keys)
715 {
716 if (xmouse_flag || gpm_flag)
717 {
718 FD_ZERO (&select_set);
719 FD_SET (input_fd, &select_set);
720 add_selects (&select_set);
721
722 #ifdef HAVE_LIBGPM
723 if (gpm_flag) {
724 FD_SET (gpm_fd, &select_set);
725 }
726 #endif
727
728 if (redo_event){
729 timeout.tv_usec = mou_auto_repeat * 1000;
730 timeout.tv_sec = 0;
731
732 time_addr = &timeout;
733 } else {
734 int seconds;
735
736 if ((seconds = vfs_timeouts ())){
737 /* the timeout could be improved and actually be
738 * the number of seconds until the next vfs entry
739 * timeouts in the stamp list.
740 */
741
742 timeout.tv_sec = seconds;
743 timeout.tv_usec = 0;
744 time_addr = &timeout;
745 } else
746 time_addr = NULL;
747 }
748
749 if (!block){
750 time_addr = &timeout;
751 timeout.tv_sec = 0;
752 timeout.tv_usec = 0;
753 }
754 enable_interrupt_key ();
755 flag = select (FD_SETSIZE, &select_set, NULL, NULL, time_addr);
756 disable_interrupt_key ();
757
758 /* select timed out: it could be for any of the following reasons:
759 * redo_event -> it was because of the MOU_REPEAT handler
760 * !block -> we did not block in the select call
761 * else -> 10 second timeout to check the vfs status.
762 */
763 if (flag == 0){
764 if (redo_event)
765 return EV_MOUSE;
766 if (!block)
767 return EV_NONE;
768 vfs_timeout_handler ();
769 }
770 if (flag == -1 && errno == EINTR)
771 return EV_NONE;
772
773 check_selects (&select_set);
774
775 if (FD_ISSET (input_fd, &select_set))
776 break;
777 }
778 #ifdef HAVE_LIBGPM
779 if (gpm_flag && FD_ISSET (gpm_fd, &select_set)){
780 if (gpm_flag){
781 Gpm_GetEvent (&ev);
782 Gpm_FitEvent (&ev);
783 }
784 *event = ev;
785 return EV_MOUSE;
786 }
787 #endif
788 }
789 # ifndef HAVE_SLANG
790 flag = is_wintouched(stdscr);
791 untouchwin (stdscr);
792 # endif
793
794 c = block ? getch_with_delay () : get_key_code(1);
795
796 # ifndef HAVE_SLANG
797 if (flag)
798 touchwin (stdscr);
799 # endif
800
801 if (c == MCKEY_MOUSE) { /* Mouse event */
802 xmouse_get_event (event);
803 return EV_MOUSE;
804 }
805
806 return c;
807 #else
808 return EV_NONE;
809 #endif /* HAVE_X */
810 }
811
812 #ifndef PORT_HAS_GETCH
813 /* Returns a key press, mouse events are discarded */
814 int mi_getch ()
815 {
816 Gpm_Event ev;
817 int key;
818
819 ev.x = -1;
820 while ((key = get_event (&ev, 0, 1)) == 0)
821 ;
822 return key;
823 }
824 #endif
825
826 int xgetch_second (void)
827 {
828 fd_set Read_FD_Set;
829 int c;
830 struct timeval timeout;
831
832 timeout.tv_sec = ESCMODE_TIMEOUT / 1000000;
833 timeout.tv_usec = ESCMODE_TIMEOUT % 1000000;
834 #ifdef BUGGY_CURSES
835 wtimeout(stdscr, 500);
836 #else
837 nodelay (stdscr, TRUE);
838 #endif
839 FD_ZERO (&Read_FD_Set);
840 FD_SET (input_fd, &Read_FD_Set);
841 select (FD_SETSIZE, &Read_FD_Set, NULL, NULL, &timeout);
842 c = xgetch ();
843 #ifdef BUGGY_CURSES
844 notimeout (stdscr, TRUE);
845 #else
846 nodelay (stdscr, FALSE);
847 #endif
848 return c;
849 }
850
851 #ifndef HAVE_X
852 void learn_store_key (char *buffer, char **p, int c)
853 {
854 if (*p - buffer > 253)
855 return;
856 if (c == ESC_CHAR) {
857 *(*p)++ = '\\';
858 *(*p)++ = 'e';
859 } else if (c < ' ') {
860 *(*p)++ = '^';
861 *(*p)++ = c + 'a' - 1;
862 } else if (c == '^') {
863 *(*p)++ = '^';
864 *(*p)++ = '^';
865 } else
866 *(*p)++ = (char) c;
867 }
868
869 char *learn_key (void)
870 {
871 /* LEARN_TIMEOUT in usec */
872 #define LEARN_TIMEOUT 200000
873
874 fd_set Read_FD_Set;
875 struct timeval endtime;
876 struct timeval timeout;
877 int c = xgetch ();
878 char buffer [256];
879 char *p = buffer;
880
881 while (c == ERR)
882 c = xgetch (); /* 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++;
889 }
890 #ifdef BUGGY_CURSES
891 wtimeout(stdscr, 500);
892 #else
893 nodelay (stdscr, TRUE);
894 #endif
895 for (;;) {
896 while ((c = xgetch ()) == ERR) {
897 GET_TIME (timeout);
898 timeout.tv_usec = endtime.tv_usec - timeout.tv_usec;
899 if (timeout.tv_usec < 0)
900 timeout.tv_sec++;
901 timeout.tv_sec = endtime.tv_sec - timeout.tv_sec;
902 if (timeout.tv_sec >= 0 && timeout.tv_usec > 0) {
903 FD_ZERO (&Read_FD_Set);
904 FD_SET (input_fd, &Read_FD_Set);
905 select (FD_SETSIZE, &Read_FD_Set, NULL, NULL, &timeout);
906 } else
907 break;
908 }
909 if (c == ERR)
910 break;
911 learn_store_key (buffer, &p, c);
912 }
913 #ifdef BUGGY_CURSES
914 notimeout (stdscr, TRUE);
915 #else
916 nodelay (stdscr, FALSE);
917 #endif
918 *p = 0;
919 return strdup (buffer);
920 }
921
922 /* xterm and linux console only: set keypad to numeric or application
923 mode. Only in application keypad mode it's possible to distinguish
924 the '+' key and the '+' on the keypad ('*' and '-' ditto)*/
925 void
926 numeric_keypad_mode (void)
927 {
928 if (console_flag || xterm_flag) {
929 fprintf (stdout, "\033>");
930 fflush (stdout);
931 }
932 }
933
934 void
935 application_keypad_mode (void)
936 {
937 if (console_flag || xterm_flag) {
938 fprintf (stdout, "\033=");
939 fflush (stdout);
940 }
941 }
942
943 #endif /* !HAVE_X */
944
945 /* A function to check if we're idle.
946 Currently checks only for key presses.
947 We could also check the mouse. */
948 int is_idle (void)
949 {
950 /* Check for incoming key presses *
951 * If there are any we say we're busy */
952
953 fd_set select_set;
954 struct timeval timeout;
955 FD_ZERO (&select_set);
956 FD_SET (0, &select_set);
957 timeout.tv_sec = 0;
958 timeout.tv_usec = 0;
959 select (FD_SETSIZE, &select_set, 0, 0, &timeout);
960 return ! FD_ISSET (0, &select_set);
961 }
962
963
964 int get_modifier ()
965 {
966 #ifdef __linux__
967 unsigned char modifiers;
968
969 modifiers = 6;
970
971 if (ioctl (0, TIOCLINUX, &modifiers) < 0)
972 return 0;
973
974 return (int) modifiers;
975 #else
976 return 0;
977 #endif
978 }
979
980 int ctrl_pressed ()
981 {
982 #ifdef __linux__
983 if (get_modifier () & CONTROL_PRESSED)
984 return 1;
985 #endif
986 return 0;
987 }
988
989 #ifdef HAVE_MAD
990 #ifndef HAVE_X
991 void k_dispose (key_def *k)
992 {
993 if (!k)
994 return;
995 k_dispose (k->child);
996 k_dispose (k->next);
997 free (k);
998 }
999
1000 void s_dispose (SelectList *sel)
1001 {
1002 if (!sel)
1003 return;
1004
1005 s_dispose (sel->next);
1006 free (sel);
1007 }
1008
1009 void done_key ()
1010 {
1011 k_dispose (keys);
1012 s_dispose (select_list);
1013 }
1014
1015 #else
1016
1017 void done_key ()
1018 {
1019 }
1020
1021 #endif /* HAVE_X */
1022 #endif /* HAVE_MAD */